diff options
Diffstat (limited to 'third_party/rust/object/src/read/elf/note.rs')
-rw-r--r-- | third_party/rust/object/src/read/elf/note.rs | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/third_party/rust/object/src/read/elf/note.rs b/third_party/rust/object/src/read/elf/note.rs new file mode 100644 index 0000000000..84d4179de2 --- /dev/null +++ b/third_party/rust/object/src/read/elf/note.rs @@ -0,0 +1,266 @@ +use core::fmt::Debug; +use core::mem; + +use crate::elf; +use crate::endian::{self, U32}; +use crate::pod::Pod; +use crate::read::util; +use crate::read::{self, Bytes, Error, ReadError}; + +use super::FileHeader; + +/// An iterator over the notes in an ELF section or segment. +#[derive(Debug)] +pub struct NoteIterator<'data, Elf> +where + Elf: FileHeader, +{ + endian: Elf::Endian, + align: usize, + data: Bytes<'data>, +} + +impl<'data, Elf> NoteIterator<'data, Elf> +where + Elf: FileHeader, +{ + /// An iterator over the notes in an ELF section or segment. + /// + /// `align` should be from the `p_align` field of the segment, + /// or the `sh_addralign` field of the section. Supported values are + /// either 4 or 8, but values less than 4 are treated as 4. + /// This matches the behaviour of binutils. + /// + /// Returns `Err` if `align` is invalid. + pub fn new(endian: Elf::Endian, align: Elf::Word, data: &'data [u8]) -> read::Result<Self> { + let align = match align.into() { + 0u64..=4 => 4, + 8 => 8, + _ => return Err(Error("Invalid ELF note alignment")), + }; + // TODO: check data alignment? + Ok(NoteIterator { + endian, + align, + data: Bytes(data), + }) + } + + /// Returns the next note. + pub fn next(&mut self) -> read::Result<Option<Note<'data, Elf>>> { + let mut data = self.data; + if data.is_empty() { + return Ok(None); + } + + let header = data + .read_at::<Elf::NoteHeader>(0) + .read_error("ELF note is too short")?; + + // The name has no alignment requirement. + let offset = mem::size_of::<Elf::NoteHeader>(); + let namesz = header.n_namesz(self.endian) as usize; + let name = data + .read_bytes_at(offset, namesz) + .read_error("Invalid ELF note namesz")? + .0; + + // The descriptor must be aligned. + let offset = util::align(offset + namesz, self.align); + let descsz = header.n_descsz(self.endian) as usize; + let desc = data + .read_bytes_at(offset, descsz) + .read_error("Invalid ELF note descsz")? + .0; + + // The next note (if any) must be aligned. + let offset = util::align(offset + descsz, self.align); + if data.skip(offset).is_err() { + data = Bytes(&[]); + } + self.data = data; + + Ok(Some(Note { header, name, desc })) + } +} + +/// A parsed `NoteHeader`. +#[derive(Debug)] +pub struct Note<'data, Elf> +where + Elf: FileHeader, +{ + header: &'data Elf::NoteHeader, + name: &'data [u8], + desc: &'data [u8], +} + +impl<'data, Elf: FileHeader> Note<'data, Elf> { + /// Return the `n_type` field of the `NoteHeader`. + /// + /// The meaning of this field is determined by `name`. + pub fn n_type(&self, endian: Elf::Endian) -> u32 { + self.header.n_type(endian) + } + + /// Return the `n_namesz` field of the `NoteHeader`. + pub fn n_namesz(&self, endian: Elf::Endian) -> u32 { + self.header.n_namesz(endian) + } + + /// Return the `n_descsz` field of the `NoteHeader`. + pub fn n_descsz(&self, endian: Elf::Endian) -> u32 { + self.header.n_descsz(endian) + } + + /// Return the bytes for the name field following the `NoteHeader`. + /// + /// This field is usually a string including one or more trailing null bytes + /// (but it is not required to be). + /// + /// The length of this field is given by `n_namesz`. + pub fn name_bytes(&self) -> &'data [u8] { + self.name + } + + /// Return the bytes for the name field following the `NoteHeader`, + /// excluding all trailing null bytes. + pub fn name(&self) -> &'data [u8] { + let mut name = self.name; + while let [rest @ .., 0] = name { + name = rest; + } + name + } + + /// Return the bytes for the desc field following the `NoteHeader`. + /// + /// The length of this field is given by `n_descsz`. The meaning + /// of this field is determined by `name` and `n_type`. + pub fn desc(&self) -> &'data [u8] { + self.desc + } + + /// Return an iterator for properties if this note's type is `NT_GNU_PROPERTY_TYPE_0`. + pub fn gnu_properties( + &self, + endian: Elf::Endian, + ) -> Option<GnuPropertyIterator<'data, Elf::Endian>> { + if self.name() != elf::ELF_NOTE_GNU || self.n_type(endian) != elf::NT_GNU_PROPERTY_TYPE_0 { + return None; + } + // Use the ELF class instead of the section alignment. + // This matches what other parsers do. + let align = if Elf::is_type_64_sized() { 8 } else { 4 }; + Some(GnuPropertyIterator { + endian, + align, + data: Bytes(self.desc), + }) + } +} + +/// A trait for generic access to `NoteHeader32` and `NoteHeader64`. +#[allow(missing_docs)] +pub trait NoteHeader: Debug + Pod { + type Endian: endian::Endian; + + fn n_namesz(&self, endian: Self::Endian) -> u32; + fn n_descsz(&self, endian: Self::Endian) -> u32; + fn n_type(&self, endian: Self::Endian) -> u32; +} + +impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader32<Endian> { + type Endian = Endian; + + #[inline] + fn n_namesz(&self, endian: Self::Endian) -> u32 { + self.n_namesz.get(endian) + } + + #[inline] + fn n_descsz(&self, endian: Self::Endian) -> u32 { + self.n_descsz.get(endian) + } + + #[inline] + fn n_type(&self, endian: Self::Endian) -> u32 { + self.n_type.get(endian) + } +} + +impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader64<Endian> { + type Endian = Endian; + + #[inline] + fn n_namesz(&self, endian: Self::Endian) -> u32 { + self.n_namesz.get(endian) + } + + #[inline] + fn n_descsz(&self, endian: Self::Endian) -> u32 { + self.n_descsz.get(endian) + } + + #[inline] + fn n_type(&self, endian: Self::Endian) -> u32 { + self.n_type.get(endian) + } +} + +/// An iterator over the properties in a `NT_GNU_PROPERTY_TYPE_0` note. +#[derive(Debug)] +pub struct GnuPropertyIterator<'data, Endian: endian::Endian> { + endian: Endian, + align: usize, + data: Bytes<'data>, +} + +impl<'data, Endian: endian::Endian> GnuPropertyIterator<'data, Endian> { + /// Returns the next property. + pub fn next(&mut self) -> read::Result<Option<GnuProperty<'data>>> { + let mut data = self.data; + if data.is_empty() { + return Ok(None); + } + + (|| -> Result<_, ()> { + let pr_type = data.read_at::<U32<Endian>>(0)?.get(self.endian); + let pr_datasz = data.read_at::<U32<Endian>>(4)?.get(self.endian) as usize; + let pr_data = data.read_bytes_at(8, pr_datasz)?.0; + data.skip(util::align(8 + pr_datasz, self.align))?; + self.data = data; + Ok(Some(GnuProperty { pr_type, pr_data })) + })() + .read_error("Invalid ELF GNU property") + } +} + +/// A property in a `NT_GNU_PROPERTY_TYPE_0` note. +#[derive(Debug)] +pub struct GnuProperty<'data> { + pr_type: u32, + pr_data: &'data [u8], +} + +impl<'data> GnuProperty<'data> { + /// Return the property type. + /// + /// This is one of the `GNU_PROPERTY_*` constants. + pub fn pr_type(&self) -> u32 { + self.pr_type + } + + /// Return the property data. + pub fn pr_data(&self) -> &'data [u8] { + self.pr_data + } + + /// Parse the property data as an unsigned 32-bit integer. + pub fn data_u32<E: endian::Endian>(&self, endian: E) -> read::Result<u32> { + Bytes(self.pr_data) + .read_at::<U32<E>>(0) + .read_error("Invalid ELF GNU property data") + .map(|val| val.get(endian)) + } +} |