1use core::{marker::PhantomData, ops::Range};
3
4use crate::endian::EndianParse;
5use crate::file::Class;
6
7#[derive(Debug)]
8pub enum ParseError {
9 BadMagic([u8; 4]),
12 UnsupportedElfClass(u8),
15 UnsupportedElfEndianness(u8),
18 UnsupportedVersion((u64, u64)),
21 BadOffset(u64),
24 StringTableMissingNul(u64),
27 BadEntsize((u64, u64)),
31 UnexpectedSectionType((u32, u32)),
34 UnexpectedSegmentType((u32, u32)),
37 UnexpectedAlignment(usize),
40 SliceReadError((usize, usize)),
44 IntegerOverflow,
46 Utf8Error(core::str::Utf8Error),
49 TryFromSliceError(core::array::TryFromSliceError),
52 TryFromIntError(core::num::TryFromIntError),
56 #[cfg(feature = "std")]
57 IOError(std::io::Error),
60}
61
62#[cfg(feature = "std")]
63impl std::error::Error for ParseError {
64 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
65 match *self {
66 ParseError::BadMagic(_) => None,
67 ParseError::UnsupportedElfClass(_) => None,
68 ParseError::UnsupportedElfEndianness(_) => None,
69 ParseError::UnsupportedVersion(_) => None,
70 ParseError::BadOffset(_) => None,
71 ParseError::StringTableMissingNul(_) => None,
72 ParseError::BadEntsize(_) => None,
73 ParseError::UnexpectedSectionType(_) => None,
74 ParseError::UnexpectedSegmentType(_) => None,
75 ParseError::UnexpectedAlignment(_) => None,
76 ParseError::SliceReadError(_) => None,
77 ParseError::IntegerOverflow => None,
78 ParseError::Utf8Error(ref err) => Some(err),
79 ParseError::TryFromSliceError(ref err) => Some(err),
80 ParseError::TryFromIntError(ref err) => Some(err),
81 ParseError::IOError(ref err) => Some(err),
82 }
83 }
84}
85
86#[cfg(all(feature = "nightly", not(feature = "std")))]
87impl core::error::Error for ParseError {
88 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
89 match *self {
90 ParseError::BadMagic(_) => None,
91 ParseError::UnsupportedElfClass(_) => None,
92 ParseError::UnsupportedElfEndianness(_) => None,
93 ParseError::UnsupportedVersion(_) => None,
94 ParseError::BadOffset(_) => None,
95 ParseError::StringTableMissingNul(_) => None,
96 ParseError::BadEntsize(_) => None,
97 ParseError::UnexpectedSectionType(_) => None,
98 ParseError::UnexpectedSegmentType(_) => None,
99 ParseError::UnexpectedAlignment(_) => None,
100 ParseError::SliceReadError(_) => None,
101 ParseError::IntegerOverflow => None,
102 ParseError::Utf8Error(ref err) => Some(err),
103 ParseError::TryFromSliceError(ref err) => Some(err),
104 ParseError::TryFromIntError(ref err) => Some(err),
105 }
106 }
107}
108
109impl core::fmt::Display for ParseError {
110 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
111 match *self {
112 ParseError::BadMagic(ref magic) => {
113 write!(f, "Invalid Magic Bytes: {magic:X?}")
114 }
115 ParseError::UnsupportedElfClass(class) => {
116 write!(f, "Unsupported ELF Class: {class}")
117 }
118 ParseError::UnsupportedElfEndianness(endianness) => {
119 write!(f, "Unsupported ELF Endianness: {endianness}")
120 }
121 ParseError::UnsupportedVersion((found, expected)) => {
122 write!(
123 f,
124 "Unsupported ELF Version field found: {found} expected: {expected}"
125 )
126 }
127 ParseError::BadOffset(offset) => {
128 write!(f, "Bad offset: {offset:#X}")
129 }
130 ParseError::StringTableMissingNul(offset) => {
131 write!(
132 f,
133 "Could not find terminating NUL byte starting at offset: {offset:#X}"
134 )
135 }
136 ParseError::BadEntsize((found, expected)) => {
137 write!(
138 f,
139 "Invalid entsize. Expected: {expected:#X}, Found: {found:#X}"
140 )
141 }
142 ParseError::UnexpectedSectionType((found, expected)) => {
143 write!(
144 f,
145 "Could not interpret section of type {found} as type {expected}"
146 )
147 }
148 ParseError::UnexpectedSegmentType((found, expected)) => {
149 write!(
150 f,
151 "Could not interpret section of type {found} as type {expected}"
152 )
153 }
154 ParseError::UnexpectedAlignment(align) => {
155 write!(
156 f,
157 "Could not interpret section with unexpected alignment of {align}"
158 )
159 }
160 ParseError::SliceReadError((start, end)) => {
161 write!(f, "Could not read bytes in range [{start:#X}, {end:#X})")
162 }
163 ParseError::IntegerOverflow => {
164 write!(f, "Integer overflow detected")
165 }
166 ParseError::Utf8Error(ref err) => err.fmt(f),
167 ParseError::TryFromSliceError(ref err) => err.fmt(f),
168 ParseError::TryFromIntError(ref err) => err.fmt(f),
169 #[cfg(feature = "std")]
170 ParseError::IOError(ref err) => err.fmt(f),
171 }
172 }
173}
174
175impl From<core::str::Utf8Error> for ParseError {
176 fn from(err: core::str::Utf8Error) -> Self {
177 ParseError::Utf8Error(err)
178 }
179}
180
181impl From<core::array::TryFromSliceError> for ParseError {
182 fn from(err: core::array::TryFromSliceError) -> Self {
183 ParseError::TryFromSliceError(err)
184 }
185}
186
187impl From<core::num::TryFromIntError> for ParseError {
188 fn from(err: core::num::TryFromIntError) -> Self {
189 ParseError::TryFromIntError(err)
190 }
191}
192
193#[cfg(feature = "std")]
194impl From<std::io::Error> for ParseError {
195 fn from(err: std::io::Error) -> ParseError {
196 ParseError::IOError(err)
197 }
198}
199
200pub trait ParseAt: Sized {
207 fn parse_at<E: EndianParse>(
211 endian: E,
212 class: Class,
213 offset: &mut usize,
214 data: &[u8],
215 ) -> Result<Self, ParseError>;
216
217 fn size_for(class: Class) -> usize;
219
220 fn validate_entsize(class: Class, entsize: usize) -> Result<usize, ParseError> {
224 let expected = Self::size_for(class);
225 match entsize == expected {
226 true => Ok(entsize),
227 false => Err(ParseError::BadEntsize((entsize as u64, expected as u64))),
228 }
229 }
230}
231
232#[derive(Debug)]
234pub struct ParsingIterator<'data, E: EndianParse, P: ParseAt> {
235 endian: E,
236 class: Class,
237 data: &'data [u8],
238 offset: usize,
239 pd: PhantomData<&'data P>,
242}
243
244impl<'data, E: EndianParse, P: ParseAt> ParsingIterator<'data, E, P> {
245 pub fn new(endian: E, class: Class, data: &'data [u8]) -> Self {
246 ParsingIterator {
247 endian,
248 class,
249 data,
250 offset: 0,
251 pd: PhantomData,
252 }
253 }
254}
255
256impl<'data, E: EndianParse, P: ParseAt> Iterator for ParsingIterator<'data, E, P> {
257 type Item = P;
258 fn next(&mut self) -> Option<Self::Item> {
259 if self.data.is_empty() {
260 return None;
261 }
262
263 Self::Item::parse_at(self.endian, self.class, &mut self.offset, self.data).ok()
264 }
265}
266
267#[derive(Debug, Clone, Copy)]
270pub struct ParsingTable<'data, E: EndianParse, P: ParseAt> {
271 endian: E,
272 class: Class,
273 data: &'data [u8],
274 pd: PhantomData<&'data P>,
276}
277
278impl<'data, E: EndianParse, P: ParseAt> ParsingTable<'data, E, P> {
279 pub fn new(endian: E, class: Class, data: &'data [u8]) -> Self {
280 ParsingTable {
281 endian,
282 class,
283 data,
284 pd: PhantomData,
285 }
286 }
287
288 pub fn iter(&self) -> ParsingIterator<'data, E, P> {
290 ParsingIterator::new(self.endian, self.class, self.data)
291 }
292
293 pub fn len(&self) -> usize {
295 self.data.len() / P::size_for(self.class)
296 }
297
298 pub fn is_empty(&self) -> bool {
300 self.len() == 0
301 }
302
303 pub fn get(&self, index: usize) -> Result<P, ParseError> {
305 if self.data.is_empty() {
306 return Err(ParseError::BadOffset(index as u64));
307 }
308
309 let entsize = P::size_for(self.class);
310 let mut start = index
311 .checked_mul(entsize)
312 .ok_or(ParseError::IntegerOverflow)?;
313 if start > self.data.len() {
314 return Err(ParseError::BadOffset(index as u64));
315 }
316
317 P::parse_at(self.endian, self.class, &mut start, self.data)
318 }
319}
320
321impl<'data, E: EndianParse, P: ParseAt> IntoIterator for ParsingTable<'data, E, P> {
322 type IntoIter = ParsingIterator<'data, E, P>;
323 type Item = P;
324
325 fn into_iter(self) -> Self::IntoIter {
326 ParsingIterator::new(self.endian, self.class, self.data)
327 }
328}
329
330pub(crate) trait ReadBytesExt<'data> {
332 fn get_bytes(self, range: Range<usize>) -> Result<&'data [u8], ParseError>;
333}
334
335impl<'data> ReadBytesExt<'data> for &'data [u8] {
336 fn get_bytes(self, range: Range<usize>) -> Result<&'data [u8], ParseError> {
337 let start = range.start;
338 let end = range.end;
339 self.get(range)
340 .ok_or(ParseError::SliceReadError((start, end)))
341 }
342}
343
344#[cfg(test)]
345pub(crate) fn test_parse_for<E: EndianParse, P: ParseAt + core::fmt::Debug + PartialEq>(
346 endian: E,
347 class: Class,
348 expected: P,
349) {
350 let size = P::size_for(class);
351 let mut data = vec![0u8; size];
352 for (n, elem) in data.iter_mut().enumerate().take(size) {
353 *elem = n as u8;
354 }
355
356 let mut offset = 0;
357 let entry = P::parse_at(endian, class, &mut offset, data.as_ref()).expect("Failed to parse");
358
359 assert_eq!(entry, expected);
360 assert_eq!(offset, size);
361}
362
363#[cfg(test)]
364pub(crate) fn test_parse_fuzz_too_short<E: EndianParse, P: ParseAt + core::fmt::Debug>(
365 endian: E,
366 class: Class,
367) {
368 let size = P::size_for(class);
369 let data = vec![0u8; size];
370 for n in 0..size {
371 let buf = data.split_at(n).0;
372 let mut offset: usize = 0;
373 let error = P::parse_at(endian, class, &mut offset, buf).expect_err("Expected an error");
374 assert!(
375 matches!(error, ParseError::SliceReadError(_)),
376 "Unexpected Error type found: {error}"
377 );
378 }
379}
380
381#[cfg(test)]
382mod read_bytes_tests {
383 use super::ParseError;
384 use super::ReadBytesExt;
385
386 #[test]
387 fn get_bytes_works() {
388 let data = &[0u8, 1, 2, 3];
389 let subslice = data.get_bytes(1..3).expect("should be within range");
390 assert_eq!(subslice, [1, 2]);
391 }
392
393 #[test]
394 fn get_bytes_out_of_range_errors() {
395 let data = &[0u8, 1, 2, 3];
396 let err = data.get_bytes(3..9).expect_err("should be out of range");
397 assert!(
398 matches!(err, ParseError::SliceReadError((3, 9))),
399 "Unexpected Error type found: {err}"
400 );
401 }
402}
403
404#[cfg(test)]
405mod parsing_table_tests {
406 use crate::endian::{AnyEndian, BigEndian, LittleEndian};
407
408 use super::*;
409
410 type U32Table<'data, E> = ParsingTable<'data, E, u32>;
411
412 #[test]
413 fn test_u32_validate_entsize() {
414 assert!(matches!(u32::validate_entsize(Class::ELF32, 4), Ok(4)));
415 assert!(matches!(
416 u32::validate_entsize(Class::ELF32, 8),
417 Err(ParseError::BadEntsize((8, 4)))
418 ));
419 }
420
421 #[test]
422 fn test_u32_parse_at() {
423 let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
424 let mut offset = 2;
425 let result = u32::parse_at(LittleEndian, Class::ELF32, &mut offset, data.as_ref())
426 .expect("Expected to parse but:");
427 assert_eq!(result, 0x05040302);
428 }
429
430 #[test]
431 fn test_u32_table_len() {
432 let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
433 let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref());
434 assert_eq!(table.len(), 2);
435 }
436
437 #[test]
438 fn test_u32_table_is_empty() {
439 let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
440 let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref());
441 assert!(!table.is_empty());
442
443 let table = U32Table::new(LittleEndian, Class::ELF32, &[]);
444 assert!(table.is_empty());
445
446 let table = U32Table::new(LittleEndian, Class::ELF32, data.get(0..1).unwrap());
447 assert!(table.is_empty());
448 }
449
450 #[test]
451 fn test_u32_table_get_parse_failure() {
452 let data = vec![0u8, 1];
453 let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref());
454 assert!(matches!(
455 table.get(0),
456 Err(ParseError::SliceReadError((0, 4)))
457 ));
458 }
459
460 #[test]
461 fn test_lsb_u32_table_get() {
462 let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
463 let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref());
464 assert!(matches!(table.get(0), Ok(0x03020100)));
465 assert!(matches!(table.get(1), Ok(0x07060504)));
466 assert!(matches!(table.get(7), Err(ParseError::BadOffset(7))));
467 }
468
469 #[test]
470 fn test_any_lsb_u32_table_get() {
471 let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
472 let table = U32Table::new(AnyEndian::Little, Class::ELF32, data.as_ref());
473 assert!(matches!(table.get(0), Ok(0x03020100)));
474 assert!(matches!(table.get(1), Ok(0x07060504)));
475 assert!(matches!(table.get(7), Err(ParseError::BadOffset(7))));
476 }
477
478 #[test]
479 fn test_msb_u32_table_get() {
480 let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
481 let table = U32Table::new(BigEndian, Class::ELF32, data.as_ref());
482 assert!(matches!(table.get(0), Ok(0x00010203)));
483 assert!(matches!(table.get(1), Ok(0x04050607)));
484 assert!(matches!(table.get(7), Err(ParseError::BadOffset(7))));
485 }
486
487 #[test]
488 fn test_any_msb_u32_table_get() {
489 let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
490 let table = U32Table::new(AnyEndian::Big, Class::ELF32, data.as_ref());
491 assert!(matches!(table.get(0), Ok(0x00010203)));
492 assert!(matches!(table.get(1), Ok(0x04050607)));
493 assert!(matches!(table.get(7), Err(ParseError::BadOffset(7))));
494 }
495
496 #[test]
497 fn test_u32_table_get_unaligned() {
498 let data = [0u8, 1, 2, 3, 4, 5, 6, 7];
499 let table = U32Table::new(LittleEndian, Class::ELF32, data.get(1..).unwrap());
500 assert!(matches!(table.get(0), Ok(0x04030201)));
501 }
502}