1use crate::abi;
35use crate::endian::EndianParse;
36use crate::file::Class;
37use crate::parse::{ParseAt, ParseError, ReadBytesExt};
38use core::mem::size_of;
39use core::str::from_utf8;
40
41#[derive(Debug, PartialEq, Eq)]
43pub enum Note<'data> {
44 GnuAbiTag(NoteGnuAbiTag),
46 GnuBuildId(NoteGnuBuildId<'data>),
48 Unknown(NoteAny<'data>),
50}
51
52impl<'data> Note<'data> {
53 fn parse_at<E: EndianParse>(
54 endian: E,
55 _class: Class,
56 align: usize,
57 offset: &mut usize,
58 data: &'data [u8],
59 ) -> Result<Self, ParseError> {
60 if align == 0 {
63 return Err(ParseError::UnexpectedAlignment(align));
64 }
65
66 let nhdr = NoteHeader::parse_at(endian, Class::ELF32, offset, data)?;
69
70 let name_start = *offset;
71 let name_buf_size: usize = nhdr.n_namesz.saturating_sub(1).try_into()?;
72 let name_buf_end = name_start
73 .checked_add(name_buf_size)
74 .ok_or(ParseError::IntegerOverflow)?;
75 let name_buf = data.get_bytes(name_start..name_buf_end)?;
76 let name = from_utf8(name_buf)?;
77
78 *offset = (*offset)
80 .checked_add(nhdr.n_namesz.try_into()?)
81 .ok_or(ParseError::IntegerOverflow)?;
82
83 if *offset % align > 0 {
85 *offset = (*offset)
86 .checked_add(align - *offset % align)
87 .ok_or(ParseError::IntegerOverflow)?;
88 }
89
90 let desc_start = *offset;
91 let desc_size: usize = nhdr.n_descsz.try_into()?;
92 let desc_end = desc_start
93 .checked_add(desc_size)
94 .ok_or(ParseError::IntegerOverflow)?;
95 let raw_desc = data.get_bytes(desc_start..desc_end)?;
96 *offset = desc_end;
97
98 if *offset % align > 0 {
100 *offset = (*offset)
101 .checked_add(align - *offset % align)
102 .ok_or(ParseError::IntegerOverflow)?;
103 }
104
105 match name {
107 abi::ELF_NOTE_GNU => match nhdr.n_type {
108 abi::NT_GNU_ABI_TAG => {
109 let mut offset = 0;
110 Ok(Note::GnuAbiTag(NoteGnuAbiTag::parse_at(
111 endian,
112 _class,
113 &mut offset,
114 raw_desc,
115 )?))
116 }
117 abi::NT_GNU_BUILD_ID => Ok(Note::GnuBuildId(NoteGnuBuildId(raw_desc))),
118 _ => Ok(Note::Unknown(NoteAny {
119 n_type: nhdr.n_type,
120 name,
121 desc: raw_desc,
122 })),
123 },
124 _ => Ok(Note::Unknown(NoteAny {
125 n_type: nhdr.n_type,
126 name,
127 desc: raw_desc,
128 })),
129 }
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140pub struct NoteGnuAbiTag {
141 pub os: u32,
142 pub major: u32,
143 pub minor: u32,
144 pub subminor: u32,
145}
146
147impl ParseAt for NoteGnuAbiTag {
148 fn parse_at<E: EndianParse>(
149 endian: E,
150 _class: Class,
151 offset: &mut usize,
152 data: &[u8],
153 ) -> Result<Self, ParseError> {
154 Ok(NoteGnuAbiTag {
155 os: endian.parse_u32_at(offset, data)?,
156 major: endian.parse_u32_at(offset, data)?,
157 minor: endian.parse_u32_at(offset, data)?,
158 subminor: endian.parse_u32_at(offset, data)?,
159 })
160 }
161
162 fn size_for(_class: Class) -> usize {
163 size_of::<u32>() * 4
164 }
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub struct NoteGnuBuildId<'data>(pub &'data [u8]);
174
175#[derive(Debug, PartialEq, Eq)]
178pub struct NoteAny<'data> {
179 pub n_type: u64,
180 pub name: &'data str,
181 pub desc: &'data [u8],
182}
183
184#[derive(Debug)]
185pub struct NoteIterator<'data, E: EndianParse> {
186 endian: E,
187 class: Class,
188 align: usize,
189 data: &'data [u8],
190 offset: usize,
191}
192
193impl<'data, E: EndianParse> NoteIterator<'data, E> {
194 pub fn new(endian: E, class: Class, align: usize, data: &'data [u8]) -> Self {
195 NoteIterator {
196 endian,
197 class,
198 align,
199 data,
200 offset: 0,
201 }
202 }
203}
204
205impl<'data, E: EndianParse> Iterator for NoteIterator<'data, E> {
206 type Item = Note<'data>;
207 fn next(&mut self) -> Option<Self::Item> {
208 if self.data.is_empty() {
209 return None;
210 }
211
212 Note::parse_at(
213 self.endian,
214 self.class,
215 self.align,
216 &mut self.offset,
217 self.data,
218 )
219 .ok()
220 }
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
224struct NoteHeader {
225 pub n_namesz: u64,
226 pub n_descsz: u64,
227 pub n_type: u64,
228}
229
230impl ParseAt for NoteHeader {
231 fn parse_at<E: EndianParse>(
232 endian: E,
233 class: Class,
234 offset: &mut usize,
235 data: &[u8],
236 ) -> Result<Self, ParseError> {
237 match class {
238 Class::ELF32 => Ok(NoteHeader {
239 n_namesz: endian.parse_u32_at(offset, data)? as u64,
240 n_descsz: endian.parse_u32_at(offset, data)? as u64,
241 n_type: endian.parse_u32_at(offset, data)? as u64,
242 }),
243 Class::ELF64 => Ok(NoteHeader {
244 n_namesz: endian.parse_u64_at(offset, data)?,
245 n_descsz: endian.parse_u64_at(offset, data)?,
246 n_type: endian.parse_u64_at(offset, data)?,
247 }),
248 }
249 }
250
251 #[inline]
252 fn size_for(class: Class) -> usize {
253 match class {
254 Class::ELF32 => 12,
255 Class::ELF64 => 24,
256 }
257 }
258}
259
260#[cfg(test)]
261mod parse_tests {
262 use super::*;
263 use crate::abi;
264 use crate::endian::{BigEndian, LittleEndian};
265
266 #[test]
267 fn parse_nt_gnu_abi_tag() {
268 #[rustfmt::skip]
269 let data = [
270 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
271 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
272 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
273 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
274 ];
275
276 let mut offset = 0;
277 let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data)
278 .expect("Failed to parse");
279
280 assert_eq!(
281 note,
282 Note::GnuAbiTag(NoteGnuAbiTag {
283 os: abi::ELF_NOTE_GNU_ABI_TAG_OS_LINUX,
284 major: 2,
285 minor: 6,
286 subminor: 32
287 })
288 );
289 }
290
291 #[test]
292 fn parse_desc_gnu_build_id() {
293 let data = [
294 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e,
295 0x55, 0x00, 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc,
296 0xb0, 0xee, 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3,
297 ];
298
299 let mut offset = 0;
300 let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data)
301 .expect("Failed to parse");
302
303 assert_eq!(
304 note,
305 Note::GnuBuildId(NoteGnuBuildId(&[
306 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc, 0xb0, 0xee,
307 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3,
308 ]))
309 );
310 }
311
312 #[test]
313 fn parse_note_errors_with_zero_alignment() {
314 #[rustfmt::skip]
316 let data = [
317 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
318 0x05, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
319 0x02, 0x00, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00,
320 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321 ];
322
323 let mut offset = 0;
324 Note::parse_at(LittleEndian, Class::ELF64, 0, &mut offset, &data)
325 .expect_err("Should have gotten an alignment error");
326 }
327 #[test]
328 fn parse_note_with_8_byte_alignment() {
329 #[rustfmt::skip]
331 let data = [
332 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
333 0x05, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
334 0x02, 0x00, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00,
335 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
336 ];
337
338 let mut offset = 0;
352 let note = Note::parse_at(LittleEndian, Class::ELF64, 8, &mut offset, &data)
353 .expect("Failed to parse");
354 assert_eq!(
355 note,
356 Note::Unknown(NoteAny {
357 n_type: 5,
358 name: abi::ELF_NOTE_GNU,
359 desc: &[
360 0x2, 0x0, 0x0, 0xc0, 0x4, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
361 ]
362 })
363 );
364 }
365
366 #[test]
367 fn parse_note_with_8_byte_alignment_unaligned_namesz() {
368 let data = [
369 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
374
375 let mut offset = 0;
376 let note = Note::parse_at(LittleEndian, Class::ELF32, 8, &mut offset, &data)
377 .expect("Failed to parse");
378 assert_eq!(
379 note,
380 Note::Unknown(NoteAny {
381 n_type: 0x42,
382 name: &"GNUU",
383 desc: &[0x01, 0x02],
384 })
385 );
386 assert_eq!(offset, 32);
387 }
388
389 #[test]
390 fn parse_note_for_elf64_expects_nhdr32() {
391 let data = [
392 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e,
393 0x55, 0x00, 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc,
394 0xb0, 0xee, 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3,
395 ];
396
397 let mut offset = 0;
398 let note = Note::parse_at(LittleEndian, Class::ELF64, 4, &mut offset, &data)
401 .expect("Failed to parse");
402 assert_eq!(
403 note,
404 Note::GnuBuildId(NoteGnuBuildId(&[
405 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c, 0x57, 0xa7, 0xc8, 0xcc, 0xb0, 0xee,
406 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3,
407 ]))
408 );
409 }
410
411 #[test]
412 fn parse_note_32_lsb() {
413 let data = [
414 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00,
415 0x00, 0x00,
416 ];
417
418 let mut offset = 0;
419 let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data)
420 .expect("Failed to parse");
421 assert_eq!(
422 note,
423 Note::Unknown(NoteAny {
424 n_type: 6,
425 name: "",
426 desc: &[0x20, 0x0],
427 })
428 );
429 assert_eq!(offset, 16);
430 }
431
432 #[test]
433 fn parse_note_32_lsb_with_name_padding() {
434 let data = [
435 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
440 ]; let mut offset = 0;
443 let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data)
444 .expect("Failed to parse");
445 assert_eq!(
446 note,
447 Note::Unknown(NoteAny {
448 n_type: 1,
449 name: "GN",
450 desc: &[0x01, 0x02, 0x03, 0x04],
451 })
452 );
453 assert_eq!(offset, 20);
454 }
455
456 #[test]
457 fn parse_note_32_lsb_with_desc_padding() {
458 let data = [
459 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x01, 0x02, 0x00, 0x00, ];
465
466 let mut offset = 0;
467 let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data)
468 .expect("Failed to parse");
469 assert_eq!(
470 note,
471 Note::Unknown(NoteAny {
472 n_type: 0x42,
473 name: abi::ELF_NOTE_GNU,
474 desc: &[0x01, 0x02],
475 })
476 );
477 assert_eq!(offset, 20);
478 }
479
480 #[test]
481 fn parse_note_32_lsb_with_no_name() {
482 let data = [
483 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, ];
488
489 let mut offset = 0;
490 let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data)
491 .expect("Failed to parse");
492 assert_eq!(
493 note,
494 Note::Unknown(NoteAny {
495 n_type: 0x42,
496 name: "",
497 desc: &[0x20, 0x0],
498 })
499 );
500 assert_eq!(offset, 16);
501 }
502
503 #[test]
504 fn parse_note_32_lsb_with_no_desc() {
505 let data = [
506 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, ];
511
512 let mut offset = 0;
513 let note = Note::parse_at(LittleEndian, Class::ELF32, 4, &mut offset, &data)
514 .expect("Failed to parse");
515 assert_eq!(
516 note,
517 Note::Unknown(NoteAny {
518 n_type: 0x42,
519 name: abi::ELF_NOTE_GNU,
520 desc: &[],
521 })
522 );
523 assert_eq!(offset, 16);
524 }
525
526 use crate::parse::{test_parse_for, test_parse_fuzz_too_short};
527
528 #[test]
529 fn parse_nhdr32_lsb() {
530 test_parse_for(
531 LittleEndian,
532 Class::ELF32,
533 NoteHeader {
534 n_namesz: 0x03020100,
535 n_descsz: 0x07060504,
536 n_type: 0x0B0A0908,
537 },
538 );
539 }
540
541 #[test]
542 fn parse_nhdr32_msb() {
543 test_parse_for(
544 BigEndian,
545 Class::ELF32,
546 NoteHeader {
547 n_namesz: 0x00010203,
548 n_descsz: 0x04050607,
549 n_type: 0x08090A0B,
550 },
551 );
552 }
553
554 #[test]
555 fn parse_nhdr64_lsb() {
556 test_parse_for(
557 LittleEndian,
558 Class::ELF64,
559 NoteHeader {
560 n_namesz: 0x0706050403020100,
561 n_descsz: 0x0F0E0D0C0B0A0908,
562 n_type: 0x1716151413121110,
563 },
564 );
565 }
566
567 #[test]
568 fn parse_nhdr64_msb() {
569 test_parse_for(
570 BigEndian,
571 Class::ELF64,
572 NoteHeader {
573 n_namesz: 0x0001020304050607,
574 n_descsz: 0x08090A0B0C0D0E0F,
575 n_type: 0x1011121314151617,
576 },
577 );
578 }
579
580 #[test]
581 fn parse_nhdr32_lsb_fuzz_too_short() {
582 test_parse_fuzz_too_short::<_, NoteHeader>(LittleEndian, Class::ELF32);
583 }
584
585 #[test]
586 fn parse_nhdr32_msb_fuzz_too_short() {
587 test_parse_fuzz_too_short::<_, NoteHeader>(BigEndian, Class::ELF32);
588 }
589
590 #[test]
591 fn parse_nhdr64_lsb_fuzz_too_short() {
592 test_parse_fuzz_too_short::<_, NoteHeader>(LittleEndian, Class::ELF64);
593 }
594
595 #[test]
596 fn parse_nhdr64_msb_fuzz_too_short() {
597 test_parse_fuzz_too_short::<_, NoteHeader>(BigEndian, Class::ELF64);
598 }
599}