1use std::fmt;
2use std::ops::Range;
3
4use zerocopy::byteorder::{BigEndian, LittleEndian, U32, U64};
5use zerocopy::{FromBytes, Immutable, KnownLayout};
6
7use crate::endian::{Cursor, Endian};
8use crate::error::{Error, Result};
9use crate::format::function::{self, EncodedFunction, InfoRecord};
10use crate::model::{AddressRange, FileIndex, Function};
11
12use super::Gsym;
13
14#[derive(Clone, Copy, Debug)]
15pub(super) struct RawFunction<'data> {
16 pub(super) range: AddressRange,
17 pub(super) name: u64,
18 pub(super) data: &'data [u8],
19 pub(super) records: &'data [u8],
20}
21
22impl<'data> RawFunction<'data> {
23 pub(super) const fn records(self, endian: Endian) -> InfoRecords<'data> {
24 InfoRecords {
25 cursor: Cursor::new(self.records, endian),
26 done: false,
27 }
28 }
29}
30
31pub(super) struct InfoRecords<'data> {
32 cursor: Cursor<'data>,
33 done: bool,
34}
35
36impl<'data> Iterator for InfoRecords<'data> {
37 type Item = Result<InfoRecord<'data>>;
38
39 fn next(&mut self) -> Option<Self::Item> {
40 if self.done {
41 return None;
42 }
43 let result = function::next_record(&mut self.cursor, &mut self.done);
44 match result {
45 Ok(Some(record)) => Some(Ok(record)),
46 Ok(None) => None,
47 Err(error) => {
48 self.done = true;
49 Some(Err(error))
50 }
51 }
52 }
53}
54
55pub struct FunctionRef<'data> {
65 pub(super) index: usize,
66 pub(super) name: &'data [u8],
67 pub(super) all_data: &'data [u8],
68 pub(super) raw: RawFunction<'data>,
69 pub(super) endian: Endian,
70 pub(super) string_offset_size: u8,
71 pub(super) string_table: Range<usize>,
72 pub(super) file_table: Range<usize>,
73 pub(super) file_count: u32,
74}
75
76impl<'data> FunctionRef<'data> {
77 #[must_use]
79 pub const fn index(&self) -> usize {
80 self.index
81 }
82
83 #[must_use]
85 pub const fn range(&self) -> AddressRange {
86 self.raw.range
87 }
88
89 #[must_use]
91 pub const fn name(&self) -> &'data [u8] {
92 self.name
93 }
94
95 #[must_use]
97 pub const fn start(&self) -> u64 {
98 self.raw.range.start
99 }
100
101 pub fn decode(&self) -> Result<Function> {
108 super::owned::decode(self, self.decode_encoded()?)
109 }
110
111 pub(super) fn decode_encoded(&self) -> Result<EncodedFunction> {
112 function::decode(
113 self.raw.data,
114 self.endian,
115 self.string_offset_size,
116 self.raw.range.start,
117 )
118 }
119
120 pub(super) fn string(&self, offset: u64) -> Result<&'data [u8]> {
121 string_at(self.all_data, &self.string_table, offset)
122 }
123
124 pub(super) fn file(&self, index: FileIndex) -> Result<(&'data [u8], &'data [u8])> {
125 file_at(
126 self.all_data,
127 self.endian,
128 self.string_offset_size,
129 &self.file_table,
130 self.file_count,
131 &self.string_table,
132 index,
133 )
134 }
135}
136
137impl fmt::Debug for FunctionRef<'_> {
138 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
139 formatter
140 .debug_struct("FunctionRef")
141 .field("index", &self.index)
142 .field("range", &self.raw.range)
143 .field("name", &String::from_utf8_lossy(self.name))
144 .finish()
145 }
146}
147
148pub(super) fn string_at<'data>(
149 data: &'data [u8],
150 table: &Range<usize>,
151 offset: u64,
152) -> Result<&'data [u8]> {
153 let offset = usize::try_from(offset).map_err(|_| Error::Overflow("string offset"))?;
154 let start = table
155 .start
156 .checked_add(offset)
157 .ok_or(Error::Overflow("string address"))?;
158 if start >= table.end {
159 return Err(Error::InvalidOffset {
160 offset: start as u64,
161 input_len: data.len(),
162 });
163 }
164 let tail = data
165 .get(start..table.end)
166 .ok_or(Error::InvalidFormat("string table is outside the file"))?;
167 let end = tail
168 .iter()
169 .position(|byte| *byte == 0)
170 .ok_or(Error::InvalidFormat("unterminated string"))?;
171 tail.get(..end)
172 .ok_or(Error::InvalidFormat("unterminated string"))
173}
174
175pub(super) fn file_at<'data>(
176 data: &'data [u8],
177 endian: Endian,
178 string_offset_size: u8,
179 file_table: &Range<usize>,
180 file_count: u32,
181 string_table: &Range<usize>,
182 index: FileIndex,
183) -> Result<(&'data [u8], &'data [u8])> {
184 if index.get() >= file_count {
185 return Err(Error::OutOfRange {
186 field: "file index",
187 value: u64::from(index),
188 max: u64::from(file_count.saturating_sub(1)),
189 });
190 }
191 let entry_size = usize::from(string_offset_size)
192 .checked_mul(2)
193 .ok_or(Error::Overflow("file entry size"))?;
194 let entry_offset = usize::try_from(index.get())
195 .map_err(|_| Error::Overflow("file index conversion"))?
196 .checked_mul(entry_size)
197 .ok_or(Error::Overflow("file entry offset"))?;
198 let offset = file_table
199 .start
200 .checked_add(4)
201 .and_then(|start| start.checked_add(entry_offset))
202 .ok_or(Error::Overflow("file entry offset"))?;
203 let end = offset
204 .checked_add(entry_size)
205 .ok_or(Error::Overflow("file entry end"))?;
206 let bytes = data.get(offset..end).ok_or_else(|| Error::UnexpectedEof {
207 offset,
208 needed: entry_size,
209 remaining: data.len().saturating_sub(offset),
210 })?;
211 let (directory, basename) = match (string_offset_size, endian) {
212 (4, Endian::Little) => {
213 decode_pair::<U32<LittleEndian>>(bytes, |value| u64::from(value.get()))?
214 }
215 (4, Endian::Big) => decode_pair::<U32<BigEndian>>(bytes, |value| u64::from(value.get()))?,
216 (8, Endian::Little) => decode_pair::<U64<LittleEndian>>(bytes, |value| value.get())?,
217 (8, Endian::Big) => decode_pair::<U64<BigEndian>>(bytes, |value| value.get())?,
218 _ => {
219 return Err(Error::OutOfRange {
220 field: "string offset size",
221 value: u64::from(string_offset_size),
222 max: 8,
223 });
224 }
225 };
226 Ok((
227 string_at(data, string_table, directory)?,
228 string_at(data, string_table, basename)?,
229 ))
230}
231
232fn decode_pair<T>(bytes: &[u8], decode: impl Fn(&T) -> u64) -> Result<(u64, u64)>
233where
234 [T; 2]: FromBytes + KnownLayout + Immutable,
235{
236 let pair = <[T; 2]>::ref_from_bytes(bytes)
237 .map_err(|_| Error::InvalidFormat("file entry has an invalid typed layout"))?;
238 let [directory, basename] = pair;
239 Ok((decode(directory), decode(basename)))
240}
241
242pub struct Functions<'gsym, D> {
265 pub(super) gsym: &'gsym Gsym<D>,
266 pub(super) next: usize,
267}
268
269impl<D> fmt::Debug for Functions<'_, D> {
270 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
271 formatter
272 .debug_struct("Functions")
273 .field("next", &self.next)
274 .finish_non_exhaustive()
275 }
276}
277
278impl<'gsym, D: AsRef<[u8]>> Iterator for Functions<'gsym, D> {
279 type Item = Result<FunctionRef<'gsym>>;
280
281 fn next(&mut self) -> Option<Self::Item> {
282 if self.next >= self.gsym.layout.address_count as usize {
283 return None;
284 }
285 let index = self.next;
286 self.next = self.next.saturating_add(1);
287 Some(self.gsym.function(index))
288 }
289
290 fn size_hint(&self) -> (usize, Option<usize>) {
291 let remaining = (self.gsym.layout.address_count as usize).saturating_sub(self.next);
292 (remaining, Some(remaining))
293 }
294}
295
296impl<D: AsRef<[u8]>> ExactSizeIterator for Functions<'_, D> {}
297
298impl<D: AsRef<[u8]>> std::iter::FusedIterator for Functions<'_, D> {}