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 let mut data = self.data;
55 if data.is_empty() {
56 return Ok(None);
57 }
58
59 let header = data
60 .read_at::<Elf::NoteHeader>(0)
61 .read_error("ELF note is too short")?;
62
63 // The name has no alignment requirement.
64 let offset = mem::size_of::<Elf::NoteHeader>();
65 let namesz = header.n_namesz(self.endian) as usize;
66 let name = data
67 .read_bytes_at(offset, namesz)
68 .read_error("Invalid ELF note namesz")?
69 .0;
70
71 // The descriptor must be aligned.
72 let offset = util::align(offset + namesz, self.align);
73 let descsz = header.n_descsz(self.endian) as usize;
74 let desc = data
75 .read_bytes_at(offset, descsz)
76 .read_error("Invalid ELF note descsz")?
77 .0;
78
79 // The next note (if any) must be aligned.
80 let offset = util::align(offset + descsz, self.align);
81 if data.skip(offset).is_err() {
82 data = Bytes(&[]);
83 }
84 self.data = data;
85
86 Ok(Some(Note { header, name, desc }))
87 }
88}
89
90/// A parsed [`NoteHeader`].
91#[derive(Debug)]
92pub struct Note<'data, Elf>
93where
94 Elf: FileHeader,
95{
96 header: &'data Elf::NoteHeader,
97 name: &'data [u8],
98 desc: &'data [u8],
99}
100
101impl<'data, Elf: FileHeader> Note<'data, Elf> {
102 /// Return the `n_type` field of the `NoteHeader`.
103 ///
104 /// The meaning of this field is determined by `name`.
105 pub fn n_type(&self, endian: Elf::Endian) -> u32 {
106 self.header.n_type(endian)
107 }
108
109 /// Return the `n_namesz` field of the `NoteHeader`.
110 pub fn n_namesz(&self, endian: Elf::Endian) -> u32 {
111 self.header.n_namesz(endian)
112 }
113
114 /// Return the `n_descsz` field of the `NoteHeader`.
115 pub fn n_descsz(&self, endian: Elf::Endian) -> u32 {
116 self.header.n_descsz(endian)
117 }
118
119 /// Return the bytes for the name field following the `NoteHeader`.
120 ///
121 /// This field is usually a string including one or more trailing null bytes
122 /// (but it is not required to be).
123 ///
124 /// The length of this field is given by `n_namesz`.
125 pub fn name_bytes(&self) -> &'data [u8] {
126 self.name
127 }
128
129 /// Return the bytes for the name field following the `NoteHeader`,
130 /// excluding all trailing null bytes.
131 pub fn name(&self) -> &'data [u8] {
132 let mut name = self.name;
133 while let [rest @ .., 0] = name {
134 name = rest;
135 }
136 name
137 }
138
139 /// Return the bytes for the desc field following the `NoteHeader`.
140 ///
141 /// The length of this field is given by `n_descsz`. The meaning
142 /// of this field is determined by `name` and `n_type`.
143 pub fn desc(&self) -> &'data [u8] {
144 self.desc
145 }
146
147 /// Return an iterator for properties if this note's type is [`elf::NT_GNU_PROPERTY_TYPE_0`].
148 pub fn gnu_properties(
149 &self,
150 endian: Elf::Endian,
151 ) -> Option<GnuPropertyIterator<'data, Elf::Endian>> {
152 if self.name() != elf::ELF_NOTE_GNU || self.n_type(endian) != elf::NT_GNU_PROPERTY_TYPE_0 {
153 return None;
154 }
155 // Use the ELF class instead of the section alignment.
156 // This matches what other parsers do.
157 let align = if Elf::is_type_64_sized() { 8 } else { 4 };
158 Some(GnuPropertyIterator {
159 endian,
160 align,
161 data: Bytes(self.desc),
162 })
163 }
164}
165
166/// A trait for generic access to [`elf::NoteHeader32`] and [`elf::NoteHeader64`].
167#[allow(missing_docs)]
168pub trait NoteHeader: Debug + Pod {
169 type Endian: endian::Endian;
170
171 fn n_namesz(&self, endian: Self::Endian) -> u32;
172 fn n_descsz(&self, endian: Self::Endian) -> u32;
173 fn n_type(&self, endian: Self::Endian) -> u32;
174}
175
176impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader32<Endian> {
177 type Endian = Endian;
178
179 #[inline]
180 fn n_namesz(&self, endian: Self::Endian) -> u32 {
181 self.n_namesz.get(endian)
182 }
183
184 #[inline]
185 fn n_descsz(&self, endian: Self::Endian) -> u32 {
186 self.n_descsz.get(endian)
187 }
188
189 #[inline]
190 fn n_type(&self, endian: Self::Endian) -> u32 {
191 self.n_type.get(endian)
192 }
193}
194
195impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader64<Endian> {
196 type Endian = Endian;
197
198 #[inline]
199 fn n_namesz(&self, endian: Self::Endian) -> u32 {
200 self.n_namesz.get(endian)
201 }
202
203 #[inline]
204 fn n_descsz(&self, endian: Self::Endian) -> u32 {
205 self.n_descsz.get(endian)
206 }
207
208 #[inline]
209 fn n_type(&self, endian: Self::Endian) -> u32 {
210 self.n_type.get(endian)
211 }
212}
213
214/// An iterator for the properties in a [`elf::NT_GNU_PROPERTY_TYPE_0`] note.
215///
216/// Returned by [`Note::gnu_properties`].
217#[derive(Debug)]
218pub struct GnuPropertyIterator<'data, Endian: endian::Endian> {
219 endian: Endian,
220 align: usize,
221 data: Bytes<'data>,
222}
223
224impl<'data, Endian: endian::Endian> GnuPropertyIterator<'data, Endian> {
225 /// Returns the next property.
226 pub fn next(&mut self) -> read::Result<Option<GnuProperty<'data>>> {
227 let mut data: Bytes<'_> = self.data;
228 if data.is_empty() {
229 return Ok(None);
230 }
231
232 (|| -> Result<_, ()> {
233 let pr_type: u32 = data.read_at::<U32<Endian>>(offset:0)?.get(self.endian);
234 let pr_datasz: usize = data.read_at::<U32<Endian>>(offset:4)?.get(self.endian) as usize;
235 let pr_data: &[u8] = data.read_bytes_at(offset:8, count:pr_datasz)?.0;
236 data.skip(offset:util::align(offset:8 + pr_datasz, self.align))?;
237 self.data = data;
238 Ok(Some(GnuProperty { pr_type, pr_data }))
239 })()
240 .read_error("Invalid ELF GNU property")
241 }
242}
243
244/// A property in a [`elf::NT_GNU_PROPERTY_TYPE_0`] note.
245#[derive(Debug)]
246pub struct GnuProperty<'data> {
247 pr_type: u32,
248 pr_data: &'data [u8],
249}
250
251impl<'data> GnuProperty<'data> {
252 /// Return the property type.
253 ///
254 /// This is one of the `GNU_PROPERTY_*` constants.
255 pub fn pr_type(&self) -> u32 {
256 self.pr_type
257 }
258
259 /// Return the property data.
260 pub fn pr_data(&self) -> &'data [u8] {
261 self.pr_data
262 }
263
264 /// Parse the property data as an unsigned 32-bit integer.
265 pub fn data_u32<E: endian::Endian>(&self, endian: E) -> read::Result<u32> {
266 Bytes(self.pr_data)
267 .read_at::<U32<E>>(0)
268 .read_error("Invalid ELF GNU property data")
269 .map(|val: &U32Bytes| val.get(endian))
270 }
271}
272