// Defined note types for GNU systems. #[cfg(feature = "log")] use log::debug; #[cfg(feature = "alloc")] use scroll::{IOread, IOwrite, Pread, Pwrite, SizeWith}; /// ABI information. /// /// The descriptor consists of words: /// * word 0: OS descriptor /// * word 1: major version of the ABI /// * word 2: minor version of the ABI /// * word 3: subminor version of the ABI pub const NT_GNU_ABI_TAG: u32 = 1; /// Old name pub const ELF_NOTE_ABI: u32 = NT_GNU_ABI_TAG; // Known OSes. These values can appear in word 0 of an // `NT_GNU_ABI_TAG` note section entry. pub const ELF_NOTE_OS_LINUX: u32 = 0; pub const ELF_NOTE_OS_GNU: u32 = 1; pub const ELF_NOTE_OS_SOLARIS2: u32 = 2; pub const ELF_NOTE_OS_FREEBSD: u32 = 3; /// Synthetic `hwcap` information. /// /// The descriptor begins with two words: /// * word 0: number of entries /// * word 1: bitmask of enabled entries /// /// Then follow variable-length entries, one byte followed by a '\0'-terminated /// `hwcap` name string. The byte gives the bit number to test if enabled, /// `(1U << bit) & bitmask`. pub const NT_GNU_HWCAP: u32 = 2; /// Build ID bits as generated by ld --build-id. /// /// The descriptor consists of any nonzero number of bytes. pub const NT_GNU_BUILD_ID: u32 = 3; /// Version note generated by GNU gold containing a version string. pub const NT_GNU_GOLD_VERSION: u32 = 4; ///Contains copy of prstatus struct. pub const NT_PRSTATUS: u32 = 1; ///Contains copy of prpsinfo struct. pub const NT_PRPSINFO: u32 = 3; ///Fields of siginfo_t. pub const NT_SIGINFO: u32 = 0x5349_4749; ///Description of mapped files. pub const NT_FILE: u32 = 0x4649_4c45; #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))] #[repr(C)] /// Note section contents. Each entry in the note section begins with a header /// of a fixed form. pub struct Nhdr32 { /// Length of the note's name (includes the terminator) pub n_namesz: u32, /// Length of the note's descriptor pub n_descsz: u32, /// Type of the note pub n_type: u32, } // Declare that this is a plain type. unsafe impl plain::Plain for Nhdr32 {} #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))] #[repr(C)] /// Note section contents. Each entry in the note section begins with a header /// of a fixed form. pub struct Nhdr64 { /// Length of the note's name (includes the terminator) pub n_namesz: u64, /// Length of the note's descriptor. pub n_descsz: u64, /// Type of the note. pub n_type: u64, } // Declare that this is a plain type. unsafe impl plain::Plain for Nhdr64 {} if_alloc! { use crate::error; use crate::container; use scroll::ctx; use alloc::vec::Vec; /// An iterator over ELF binary notes in a note section or segment pub struct NoteDataIterator<'a> { pub data: &'a [u8], pub size: usize, pub offset: usize, pub ctx: (usize, container::Ctx), // (alignment, ctx) } impl<'a> Iterator for NoteDataIterator<'a> { type Item = error::Result>; fn next(&mut self) -> Option { if self.offset >= self.size { None } else { debug!("NoteIterator - {:#x}", self.offset); match self.data.gread_with(&mut self.offset, self.ctx) { Ok(res) => Some(Ok(res)), Err(e) => Some(Err(e)) } } } } /// An iterator over ELF binary notes pub struct NoteIterator<'a> { pub iters: Vec>, pub index: usize, } impl<'a> Iterator for NoteIterator<'a> { type Item = error::Result>; fn next(&mut self) -> Option { while self.index < self.iters.len() { if let Some(note_result) = self.iters[self.index].next() { return Some(note_result); } self.index += 1; } None } } #[derive(Debug)] struct NoteHeader { n_namesz: usize, n_descsz: usize, n_type: u32, } impl From for NoteHeader { fn from(header: Nhdr32) -> Self { NoteHeader { n_namesz: header.n_namesz as usize, n_descsz: header.n_descsz as usize, n_type: header.n_type, } } } impl From for NoteHeader { fn from(header: Nhdr64) -> Self { NoteHeader { n_namesz: header.n_namesz as usize, n_descsz: header.n_descsz as usize, n_type: header.n_type as u32, } } } fn align(alignment: usize, offset: &mut usize) { let diff = *offset % alignment; if diff != 0 { *offset += alignment - diff; } } /// A 32/64 bit Note struct, with the name and desc pre-parsed #[derive(Debug)] pub struct Note<'a> { /// The type of this note pub n_type: u32, /// NUL terminated string, where `namesz` includes the terminator pub name: &'a str, // needs padding such that namesz + padding % {wordsize} == 0 /// arbitrary data of length `descsz` pub desc: &'a [u8], // needs padding such that descsz + padding % {wordsize} == 0 } impl<'a> Note<'a> { pub fn type_to_str(&self) -> &'static str { match self.n_type { NT_GNU_ABI_TAG => "NT_GNU_ABI_TAG", NT_GNU_HWCAP => "NT_GNU_HWCAP", NT_GNU_BUILD_ID => "NT_GNU_BUILD_ID", NT_GNU_GOLD_VERSION => "NT_GNU_GOLD_VERSION", _ => "NT_UNKNOWN" } } } impl<'a> ctx::TryFromCtx<'a, (usize, container::Ctx)> for Note<'a> { type Error = error::Error; fn try_from_ctx(bytes: &'a [u8], (alignment, ctx): (usize, container::Ctx)) -> Result<(Self, usize), Self::Error> { let offset = &mut 0; let mut alignment = alignment; if alignment < 4 { alignment = 4; } let header: NoteHeader = { match alignment { 4|8 => bytes.gread_with::(offset, ctx.le)?.into(), _ => return Err(error::Error::Malformed(format!("Notes has unimplemented alignment requirement: {:#x}", alignment))) } }; debug!("{:?} - {:#x}", header, *offset); // -1 because includes \0 terminator let name = bytes.gread_with::<&'a str>(offset, ctx::StrCtx::Length(header.n_namesz.saturating_sub(1)))?; if header.n_namesz > 0 { *offset += 1; } align(alignment, offset); debug!("note name {} - {:#x}", name, *offset); let desc = bytes.gread_with::<&'a [u8]>(offset, header.n_descsz)?; align(alignment, offset); debug!("desc {:?} - {:#x}", desc, *offset); Ok((Note { name, desc, n_type: header.n_type, }, *offset)) } } #[cfg(test)] mod tests { use super::*; static NOTE_DATA: [u8; 68] = [0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0xbc, 0xfc, 0x66, 0xcd, 0xc7, 0xd5, 0x14, 0x7b, 0x53, 0xb1, 0x10, 0x11, 0x94, 0x86, 0x8e, 0xf9, 0x4f, 0xe8, 0xdd, 0xdb]; static CONTEXT: (usize, container::Ctx) = (4, container::Ctx { container: container::Container::Big, le: ::scroll::Endian::Little, }); fn make_note_iter(start: usize, end: usize) -> NoteDataIterator<'static> { NoteDataIterator { data: &NOTE_DATA, size: end, offset: start, ctx: CONTEXT, } } #[test] fn iter_single_section() { let mut notes = NoteIterator { iters: vec![make_note_iter(0, 68)], index: 0, }; assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG); assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID); assert!(notes.next().is_none()); } #[test] fn iter_multiple_sections() { let mut notes = NoteIterator { iters: vec![make_note_iter(0, 32), make_note_iter(32, 68)], index: 0, }; assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG); assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID); assert!(notes.next().is_none()); } #[test] fn skip_empty_sections() { let mut notes = NoteIterator { iters: vec![ make_note_iter(0, 32), make_note_iter(0, 0), make_note_iter(32, 68), ], index: 0, }; assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG); assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID); assert!(notes.next().is_none()); } #[test] fn ignore_no_sections() { let mut notes = NoteIterator { iters: vec![], index: 0 }; assert!(notes.next().is_none()); } } }