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 | 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)] |
92 | pub struct Note<'data, Elf> |
93 | where |
94 | Elf: FileHeader, |
95 | { |
96 | header: &'data Elf::NoteHeader, |
97 | name: &'data [u8], |
98 | desc: &'data [u8], |
99 | } |
100 | |
101 | impl<'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)] |
168 | pub 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 | |
176 | impl<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 | |
195 | impl<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)] |
218 | pub struct GnuPropertyIterator<'data, Endian: endian::Endian> { |
219 | endian: Endian, |
220 | align: usize, |
221 | data: Bytes<'data>, |
222 | } |
223 | |
224 | impl<'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)] |
246 | pub struct GnuProperty<'data> { |
247 | pr_type: u32, |
248 | pr_data: &'data [u8], |
249 | } |
250 | |
251 | impl<'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 | |