object/read/elf/
note.rs

1use core::fmt::Debug;
2use core::mem;
3
4use crate::elf;
5use crate::endian::{self, U32};
6use crate::pod::Pod;
7use crate::read::util;
8use crate::read::{self, Bytes, Error, ReadError};
9
10use super::FileHeader;
11
12/// An iterator over the notes in an ELF section or segment.
13///
14/// Returned [`ProgramHeader::notes`](super::ProgramHeader::notes)
15/// and [`SectionHeader::notes`](super::SectionHeader::notes).
16#[derive(Debug)]
17pub struct NoteIterator<'data, Elf>
18where
19    Elf: FileHeader,
20{
21    endian: Elf::Endian,
22    align: usize,
23    data: Bytes<'data>,
24}
25
26impl<'data, Elf> NoteIterator<'data, Elf>
27where
28    Elf: FileHeader,
29{
30    /// An iterator over the notes in an ELF section or segment.
31    ///
32    /// `align` should be from the `p_align` field of the segment,
33    /// or the `sh_addralign` field of the section. Supported values are
34    /// either 4 or 8, but values less than 4 are treated as 4.
35    /// This matches the behaviour of binutils.
36    ///
37    /// Returns `Err` if `align` is invalid.
38    pub fn new(endian: Elf::Endian, align: Elf::Word, data: &'data [u8]) -> read::Result<Self> {
39        let align = match align.into() {
40            0u64..=4 => 4,
41            8 => 8,
42            _ => return Err(Error("Invalid ELF note alignment")),
43        };
44        // TODO: check data alignment?
45        Ok(NoteIterator {
46            endian,
47            align,
48            data: Bytes(data),
49        })
50    }
51
52    /// Returns the next note.
53    pub fn next(&mut self) -> read::Result<Option<Note<'data, Elf>>> {
54        if self.data.is_empty() {
55            return Ok(None);
56        }
57
58        let result = self.parse().map(Some);
59        if result.is_err() {
60            self.data = Bytes(&[]);
61        }
62        result
63    }
64
65    fn parse(&mut self) -> read::Result<Note<'data, Elf>> {
66        let header = self
67            .data
68            .read_at::<Elf::NoteHeader>(0)
69            .read_error("ELF note is too short")?;
70
71        // The name has no alignment requirement.
72        let offset = mem::size_of::<Elf::NoteHeader>();
73        let namesz = header.n_namesz(self.endian) as usize;
74        let name = self
75            .data
76            .read_bytes_at(offset, namesz)
77            .read_error("Invalid ELF note namesz")?
78            .0;
79
80        // The descriptor must be aligned.
81        let offset = util::align(offset + namesz, self.align);
82        let descsz = header.n_descsz(self.endian) as usize;
83        let desc = self
84            .data
85            .read_bytes_at(offset, descsz)
86            .read_error("Invalid ELF note descsz")?
87            .0;
88
89        // The next note (if any) must be aligned.
90        let offset = util::align(offset + descsz, self.align);
91        if self.data.skip(offset).is_err() {
92            self.data = Bytes(&[]);
93        }
94
95        Ok(Note { header, name, desc })
96    }
97}
98
99impl<'data, Elf: FileHeader> Iterator for NoteIterator<'data, Elf> {
100    type Item = read::Result<Note<'data, Elf>>;
101
102    fn next(&mut self) -> Option<Self::Item> {
103        self.next().transpose()
104    }
105}
106
107/// A parsed [`NoteHeader`].
108#[derive(Debug)]
109pub struct Note<'data, Elf>
110where
111    Elf: FileHeader,
112{
113    header: &'data Elf::NoteHeader,
114    name: &'data [u8],
115    desc: &'data [u8],
116}
117
118impl<'data, Elf: FileHeader> Note<'data, Elf> {
119    /// Return the `n_type` field of the `NoteHeader`.
120    ///
121    /// The meaning of this field is determined by `name`.
122    pub fn n_type(&self, endian: Elf::Endian) -> u32 {
123        self.header.n_type(endian)
124    }
125
126    /// Return the `n_namesz` field of the `NoteHeader`.
127    pub fn n_namesz(&self, endian: Elf::Endian) -> u32 {
128        self.header.n_namesz(endian)
129    }
130
131    /// Return the `n_descsz` field of the `NoteHeader`.
132    pub fn n_descsz(&self, endian: Elf::Endian) -> u32 {
133        self.header.n_descsz(endian)
134    }
135
136    /// Return the bytes for the name field following the `NoteHeader`.
137    ///
138    /// This field is usually a string including one or more trailing null bytes
139    /// (but it is not required to be).
140    ///
141    /// The length of this field is given by `n_namesz`.
142    pub fn name_bytes(&self) -> &'data [u8] {
143        self.name
144    }
145
146    /// Return the bytes for the name field following the `NoteHeader`,
147    /// excluding all trailing null bytes.
148    pub fn name(&self) -> &'data [u8] {
149        let mut name = self.name;
150        while let [rest @ .., 0] = name {
151            name = rest;
152        }
153        name
154    }
155
156    /// Return the bytes for the desc field following the `NoteHeader`.
157    ///
158    /// The length of this field is given by `n_descsz`. The meaning
159    /// of this field is determined by `name` and `n_type`.
160    pub fn desc(&self) -> &'data [u8] {
161        self.desc
162    }
163
164    /// Return an iterator for properties if this note's type is [`elf::NT_GNU_PROPERTY_TYPE_0`].
165    pub fn gnu_properties(
166        &self,
167        endian: Elf::Endian,
168    ) -> Option<GnuPropertyIterator<'data, Elf::Endian>> {
169        if self.name() != elf::ELF_NOTE_GNU || self.n_type(endian) != elf::NT_GNU_PROPERTY_TYPE_0 {
170            return None;
171        }
172        // Use the ELF class instead of the section alignment.
173        // This matches what other parsers do.
174        let align = if Elf::is_type_64_sized() { 8 } else { 4 };
175        Some(GnuPropertyIterator {
176            endian,
177            align,
178            data: Bytes(self.desc),
179        })
180    }
181}
182
183/// A trait for generic access to [`elf::NoteHeader32`] and [`elf::NoteHeader64`].
184#[allow(missing_docs)]
185pub trait NoteHeader: Debug + Pod {
186    type Endian: endian::Endian;
187
188    fn n_namesz(&self, endian: Self::Endian) -> u32;
189    fn n_descsz(&self, endian: Self::Endian) -> u32;
190    fn n_type(&self, endian: Self::Endian) -> u32;
191}
192
193impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader32<Endian> {
194    type Endian = Endian;
195
196    #[inline]
197    fn n_namesz(&self, endian: Self::Endian) -> u32 {
198        self.n_namesz.get(endian)
199    }
200
201    #[inline]
202    fn n_descsz(&self, endian: Self::Endian) -> u32 {
203        self.n_descsz.get(endian)
204    }
205
206    #[inline]
207    fn n_type(&self, endian: Self::Endian) -> u32 {
208        self.n_type.get(endian)
209    }
210}
211
212impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader64<Endian> {
213    type Endian = Endian;
214
215    #[inline]
216    fn n_namesz(&self, endian: Self::Endian) -> u32 {
217        self.n_namesz.get(endian)
218    }
219
220    #[inline]
221    fn n_descsz(&self, endian: Self::Endian) -> u32 {
222        self.n_descsz.get(endian)
223    }
224
225    #[inline]
226    fn n_type(&self, endian: Self::Endian) -> u32 {
227        self.n_type.get(endian)
228    }
229}
230
231/// An iterator for the properties in a [`elf::NT_GNU_PROPERTY_TYPE_0`] note.
232///
233/// Returned by [`Note::gnu_properties`].
234#[derive(Debug)]
235pub struct GnuPropertyIterator<'data, Endian: endian::Endian> {
236    endian: Endian,
237    align: usize,
238    data: Bytes<'data>,
239}
240
241impl<'data, Endian: endian::Endian> GnuPropertyIterator<'data, Endian> {
242    /// Returns the next property.
243    pub fn next(&mut self) -> read::Result<Option<GnuProperty<'data>>> {
244        if self.data.is_empty() {
245            return Ok(None);
246        }
247
248        let result = self.parse().map(Some);
249        if result.is_err() {
250            self.data = Bytes(&[]);
251        }
252        result
253    }
254
255    fn parse(&mut self) -> read::Result<GnuProperty<'data>> {
256        (|| -> Result<_, ()> {
257            let pr_type = self.data.read_at::<U32<Endian>>(0)?.get(self.endian);
258            let pr_datasz = self.data.read_at::<U32<Endian>>(4)?.get(self.endian) as usize;
259            let pr_data = self.data.read_bytes_at(8, pr_datasz)?.0;
260            self.data.skip(util::align(8 + pr_datasz, self.align))?;
261            Ok(GnuProperty { pr_type, pr_data })
262        })()
263        .read_error("Invalid ELF GNU property")
264    }
265}
266
267impl<'data, Endian: endian::Endian> Iterator for GnuPropertyIterator<'data, Endian> {
268    type Item = read::Result<GnuProperty<'data>>;
269
270    fn next(&mut self) -> Option<Self::Item> {
271        self.next().transpose()
272    }
273}
274
275/// A property in a [`elf::NT_GNU_PROPERTY_TYPE_0`] note.
276#[derive(Debug)]
277pub struct GnuProperty<'data> {
278    pr_type: u32,
279    pr_data: &'data [u8],
280}
281
282impl<'data> GnuProperty<'data> {
283    /// Return the property type.
284    ///
285    /// This is one of the `GNU_PROPERTY_*` constants.
286    pub fn pr_type(&self) -> u32 {
287        self.pr_type
288    }
289
290    /// Return the property data.
291    pub fn pr_data(&self) -> &'data [u8] {
292        self.pr_data
293    }
294
295    /// Parse the property data as an unsigned 32-bit integer.
296    pub fn data_u32<E: endian::Endian>(&self, endian: E) -> read::Result<u32> {
297        Bytes(self.pr_data)
298            .read_at::<U32<E>>(0)
299            .read_error("Invalid ELF GNU property data")
300            .map(|val| val.get(endian))
301    }
302}