1 // Defined note types for GNU systems.
2 
3 #[cfg(feature = "log")]
4 use log::debug;
5 #[cfg(feature = "alloc")]
6 use scroll::{Pread, Pwrite, IOread, IOwrite, SizeWith};
7 
8 /// ABI information.
9 ///
10 /// The descriptor consists of words:
11 ///  * word 0: OS descriptor
12 ///  * word 1: major version of the ABI
13 ///  * word 2: minor version of the ABI
14 ///  * word 3: subminor version of the ABI
15 pub const NT_GNU_ABI_TAG: u32 = 1;
16 
17 /// Old name
18 pub const ELF_NOTE_ABI: u32 = NT_GNU_ABI_TAG;
19 // Known OSes.  These values can appear in word 0 of an
20 // `NT_GNU_ABI_TAG` note section entry.
21 pub const ELF_NOTE_OS_LINUX: u32 = 0;
22 pub const ELF_NOTE_OS_GNU: u32 = 1;
23 pub const ELF_NOTE_OS_SOLARIS2: u32 = 2;
24 pub const ELF_NOTE_OS_FREEBSD: u32 = 3;
25 
26 /// Synthetic `hwcap` information.
27 ///
28 /// The descriptor begins with two words:
29 ///  * word 0: number of entries
30 ///  * word 1: bitmask of enabled entries
31 ///
32 /// Then follow variable-length entries, one byte followed by a '\0'-terminated
33 /// `hwcap` name string.  The byte gives the bit number to test if enabled,
34 /// `(1U << bit) & bitmask`.
35 pub const NT_GNU_HWCAP: u32 = 2;
36 
37 /// Build ID bits as generated by ld --build-id.
38 ///
39 /// The descriptor consists of any nonzero number of bytes.
40 pub const NT_GNU_BUILD_ID: u32 = 3;
41 
42 /// Version note generated by GNU gold containing a version string.
43 pub const NT_GNU_GOLD_VERSION: u32 = 4;
44 
45 #[derive(Clone, Copy, Debug)]
46 #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))]
47 #[repr(C)]
48 /// Note section contents. Each entry in the note section begins with a header
49 /// of a fixed form.
50 pub struct Nhdr32 {
51     /// Length of the note's name (includes the terminator)
52     pub n_namesz: u32,
53     /// Length of the note's descriptor
54     pub n_descsz: u32,
55     /// Type of the note
56     pub n_type: u32,
57 }
58 
59 #[derive(Clone, Copy, Debug)]
60 #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))]
61 #[repr(C)]
62 /// Note section contents. Each entry in the note section begins with a header
63 /// of a fixed form.
64 pub struct Nhdr64 {
65     /// Length of the note's name (includes the terminator)
66     pub n_namesz: u64,
67     /// Length of the note's descriptor.
68     pub n_descsz: u64,
69     /// Type of the note.
70     pub n_type: u64,
71 }
72 
73 if_alloc! {
74     use crate::error;
75     use crate::container;
76     use scroll::ctx;
77     use crate::alloc::vec::Vec;
78 
79     /// An iterator over ELF binary notes in a note section or segment
80     pub struct NoteDataIterator<'a> {
81         pub data: &'a [u8],
82         pub size: usize,
83         pub offset: usize,
84         pub ctx: (usize, container::Ctx), // (alignment, ctx)
85     }
86 
87     impl<'a> Iterator for NoteDataIterator<'a> {
88         type Item = error::Result<Note<'a>>;
89         fn next(&mut self) -> Option<Self::Item> {
90             if self.offset >= self.size {
91                 None
92             } else {
93                 debug!("NoteIterator - {:#x}", self.offset);
94                 match self.data.gread_with(&mut self.offset, self.ctx) {
95                     Ok(res) => Some(Ok(res)),
96                     Err(e) => Some(Err(e))
97                 }
98             }
99         }
100     }
101 
102     /// An iterator over ELF binary notes
103     pub struct NoteIterator<'a> {
104         pub iters: Vec<NoteDataIterator<'a>>,
105         pub index: usize,
106     }
107 
108     impl<'a> Iterator for NoteIterator<'a> {
109         type Item = error::Result<Note<'a>>;
110         fn next(&mut self) -> Option<Self::Item> {
111             while self.index < self.iters.len() {
112                 if let Some(note_result) = self.iters[self.index].next() {
113                     return Some(note_result);
114                 }
115 
116                 self.index += 1;
117             }
118 
119             None
120         }
121     }
122 
123     #[derive(Debug)]
124     struct NoteHeader {
125         n_namesz: usize,
126         n_descsz: usize,
127         n_type: u32,
128     }
129 
130     impl From<Nhdr32> for NoteHeader {
131         fn from(header: Nhdr32) -> Self {
132             NoteHeader {
133                 n_namesz: header.n_namesz as usize,
134                 n_descsz: header.n_descsz as usize,
135                 n_type: header.n_type,
136             }
137         }
138     }
139 
140     impl From<Nhdr64> for NoteHeader {
141         fn from(header: Nhdr64) -> Self {
142             NoteHeader {
143                 n_namesz: header.n_namesz as usize,
144                 n_descsz: header.n_descsz as usize,
145                 n_type: header.n_type as u32,
146             }
147         }
148     }
149 
150     fn align(alignment: usize, offset: &mut usize) {
151         let diff = *offset % alignment;
152         if diff != 0 {
153             *offset += alignment - diff;
154         }
155     }
156 
157     /// A 32/64 bit Note struct, with the name and desc pre-parsed
158     #[derive(Debug)]
159     pub struct Note<'a> {
160         /// The type of this note
161         pub n_type: u32,
162         /// NUL terminated string, where `namesz` includes the terminator
163         pub name: &'a str, // needs padding such that namesz + padding % {wordsize} == 0
164         /// arbitrary data of length `descsz`
165         pub desc: &'a [u8], // needs padding such that descsz + padding % {wordsize} == 0
166     }
167 
168     impl<'a> Note<'a> {
169         pub fn type_to_str(&self) -> &'static str {
170             match self.n_type {
171                 NT_GNU_ABI_TAG => "NT_GNU_ABI_TAG",
172                 NT_GNU_HWCAP => "NT_GNU_HWCAP",
173                 NT_GNU_BUILD_ID => "NT_GNU_BUILD_ID",
174                 NT_GNU_GOLD_VERSION => "NT_GNU_GOLD_VERSION",
175                 _ => "NT_UNKNOWN"
176             }
177         }
178     }
179 
180     impl<'a> ctx::TryFromCtx<'a, (usize, container::Ctx)> for Note<'a> {
181         type Error = error::Error;
182         type Size = usize;
183         fn try_from_ctx(bytes: &'a [u8], (alignment, ctx): (usize, container::Ctx)) -> Result<(Self, Self::Size), Self::Error> {
184             let offset = &mut 0;
185             let mut alignment = alignment;
186             if alignment < 4 {
187                 alignment = 4;
188             }
189             let header: NoteHeader = {
190                 match alignment {
191                     4|8 => bytes.gread_with::<Nhdr32>(offset, ctx.le)?.into(),
192                     _ => return Err(error::Error::Malformed(format!("Notes has unimplemented alignment requirement: {:#x}", alignment)))
193                 }
194             };
195             debug!("{:?} - {:#x}", header, *offset);
196             // -1 because includes \0 terminator
197             let name = bytes.gread_with::<&'a str>(offset, ctx::StrCtx::Length(header.n_namesz - 1))?;
198             *offset += 1;
199             align(alignment, offset);
200             debug!("note name {} - {:#x}", name, *offset);
201             let desc = bytes.gread_with::<&'a [u8]>(offset, header.n_descsz)?;
202             align(alignment, offset);
203             debug!("desc {:?} - {:#x}", desc, *offset);
204             Ok((Note {
205                 name,
206                 desc,
207                 n_type: header.n_type,
208             }, *offset))
209         }
210     }
211 
212     #[cfg(test)]
213     mod tests {
214         use super::*;
215 
216         static NOTE_DATA: [u8; 68] = [0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
217                                      0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
218                                      0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
219                                      0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
220                                      0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
221                                      0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
222                                      0xbc, 0xfc, 0x66, 0xcd, 0xc7, 0xd5, 0x14, 0x7b,
223                                      0x53, 0xb1, 0x10, 0x11, 0x94, 0x86, 0x8e, 0xf9,
224                                      0x4f, 0xe8, 0xdd, 0xdb];
225 
226         static CONTEXT: (usize, container::Ctx) = (4, container::Ctx {
227             container: container::Container::Big,
228             le: ::scroll::Endian::Little,
229         });
230 
231         fn make_note_iter(start: usize, end: usize) -> NoteDataIterator<'static> {
232             NoteDataIterator {
233                 data: &NOTE_DATA,
234                 size: end,
235                 offset: start,
236                 ctx: CONTEXT,
237             }
238         }
239 
240         #[test]
241         fn iter_single_section() {
242             let mut notes = NoteIterator {
243                 iters: vec![make_note_iter(0, 68)],
244                 index: 0,
245             };
246 
247             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
248             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
249             assert!(notes.next().is_none());
250         }
251 
252         #[test]
253         fn iter_multiple_sections() {
254             let mut notes = NoteIterator {
255                 iters: vec![make_note_iter(0, 32), make_note_iter(32, 68)],
256                 index: 0,
257             };
258 
259             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
260             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
261             assert!(notes.next().is_none());
262         }
263 
264         #[test]
265         fn skip_empty_sections() {
266             let mut notes = NoteIterator {
267                 iters: vec![
268                     make_note_iter(0, 32),
269                     make_note_iter(0, 0),
270                     make_note_iter(32, 68),
271                 ],
272                 index: 0,
273             };
274 
275             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
276             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
277             assert!(notes.next().is_none());
278         }
279 
280         #[test]
281         fn ignore_no_sections() {
282             let mut notes = NoteIterator { iters: vec![], index: 0 };
283             assert!(notes.next().is_none());
284         }
285     }
286 }
287