1 | use core::fmt::Debug; |
2 | use core::mem; |
3 | |
4 | use crate::elf; |
5 | use crate::endian::{self, U32}; |
6 | use crate::pod::Pod; |
7 | use crate::read::util; |
8 | use crate::read::{self, Bytes, Error, ReadError}; |
9 | |
10 | use 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)] |
17 | pub struct NoteIterator<'data, Elf> |
18 | where |
19 | Elf: FileHeader, |
20 | { |
21 | endian: Elf::Endian, |
22 | align: usize, |
23 | data: Bytes<'data>, |
24 | } |
25 | |
26 | impl<'data, Elf> NoteIterator<'data, Elf> |
27 | where |
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 | |
99 | impl<'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)] |
109 | pub struct Note<'data, Elf> |
110 | where |
111 | Elf: FileHeader, |
112 | { |
113 | header: &'data Elf::NoteHeader, |
114 | name: &'data [u8], |
115 | desc: &'data [u8], |
116 | } |
117 | |
118 | impl<'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)] |
185 | pub 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 | |
193 | impl<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 | |
212 | impl<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)] |
235 | pub struct GnuPropertyIterator<'data, Endian: endian::Endian> { |
236 | endian: Endian, |
237 | align: usize, |
238 | data: Bytes<'data>, |
239 | } |
240 | |
241 | impl<'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 | |
267 | impl<'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)] |
277 | pub struct GnuProperty<'data> { |
278 | pr_type: u32, |
279 | pr_data: &'data [u8], |
280 | } |
281 | |
282 | impl<'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: &U32Bytes| val.get(endian)) |
301 | } |
302 | } |
303 | |