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 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         fn try_from_ctx(bytes: &'a [u8], (alignment, ctx): (usize, container::Ctx)) -> Result<(Self, usize), Self::Error> {
183             let offset = &mut 0;
184             let mut alignment = alignment;
185             if alignment < 4 {
186                 alignment = 4;
187             }
188             let header: NoteHeader = {
189                 match alignment {
190                     4|8 => bytes.gread_with::<Nhdr32>(offset, ctx.le)?.into(),
191                     _ => return Err(error::Error::Malformed(format!("Notes has unimplemented alignment requirement: {:#x}", alignment)))
192                 }
193             };
194             debug!("{:?} - {:#x}", header, *offset);
195             // -1 because includes \0 terminator
196             let name = bytes.gread_with::<&'a str>(offset, ctx::StrCtx::Length(header.n_namesz - 1))?;
197             *offset += 1;
198             align(alignment, offset);
199             debug!("note name {} - {:#x}", name, *offset);
200             let desc = bytes.gread_with::<&'a [u8]>(offset, header.n_descsz)?;
201             align(alignment, offset);
202             debug!("desc {:?} - {:#x}", desc, *offset);
203             Ok((Note {
204                 name,
205                 desc,
206                 n_type: header.n_type,
207             }, *offset))
208         }
209     }
210 
211     #[cfg(test)]
212     mod tests {
213         use super::*;
214 
215         static NOTE_DATA: [u8; 68] = [0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
216                                      0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
217                                      0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
218                                      0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
219                                      0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
220                                      0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
221                                      0xbc, 0xfc, 0x66, 0xcd, 0xc7, 0xd5, 0x14, 0x7b,
222                                      0x53, 0xb1, 0x10, 0x11, 0x94, 0x86, 0x8e, 0xf9,
223                                      0x4f, 0xe8, 0xdd, 0xdb];
224 
225         static CONTEXT: (usize, container::Ctx) = (4, container::Ctx {
226             container: container::Container::Big,
227             le: ::scroll::Endian::Little,
228         });
229 
230         fn make_note_iter(start: usize, end: usize) -> NoteDataIterator<'static> {
231             NoteDataIterator {
232                 data: &NOTE_DATA,
233                 size: end,
234                 offset: start,
235                 ctx: CONTEXT,
236             }
237         }
238 
239         #[test]
240         fn iter_single_section() {
241             let mut notes = NoteIterator {
242                 iters: vec![make_note_iter(0, 68)],
243                 index: 0,
244             };
245 
246             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
247             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
248             assert!(notes.next().is_none());
249         }
250 
251         #[test]
252         fn iter_multiple_sections() {
253             let mut notes = NoteIterator {
254                 iters: vec![make_note_iter(0, 32), make_note_iter(32, 68)],
255                 index: 0,
256             };
257 
258             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
259             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
260             assert!(notes.next().is_none());
261         }
262 
263         #[test]
264         fn skip_empty_sections() {
265             let mut notes = NoteIterator {
266                 iters: vec![
267                     make_note_iter(0, 32),
268                     make_note_iter(0, 0),
269                     make_note_iter(32, 68),
270                 ],
271                 index: 0,
272             };
273 
274             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
275             assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
276             assert!(notes.next().is_none());
277         }
278 
279         #[test]
280         fn ignore_no_sections() {
281             let mut notes = NoteIterator { iters: vec![], index: 0 };
282             assert!(notes.next().is_none());
283         }
284     }
285 }
286