goblin_experimental/elf/
note.rs

1// Defined note types for GNU systems.
2
3#[cfg(feature = "log")]
4use log::debug;
5#[cfg(feature = "alloc")]
6use scroll::{IOread, IOwrite, Pread, Pwrite, 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
15pub const NT_GNU_ABI_TAG: u32 = 1;
16
17/// Old name
18pub 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.
21pub const ELF_NOTE_OS_LINUX: u32 = 0;
22pub const ELF_NOTE_OS_GNU: u32 = 1;
23pub const ELF_NOTE_OS_SOLARIS2: u32 = 2;
24pub 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`.
35pub 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.
40pub const NT_GNU_BUILD_ID: u32 = 3;
41
42/// Version note generated by GNU gold containing a version string.
43pub const NT_GNU_GOLD_VERSION: u32 = 4;
44
45/// Program property note
46pub const NT_GNU_PROPERTY_TYPE_0: u32 = 5;
47
48///Contains copy of prstatus struct.
49pub const NT_PRSTATUS: u32 = 1;
50
51///Contains copy of prpsinfo struct.
52pub const NT_PRPSINFO: u32 = 3;
53
54///Fields of siginfo_t.
55pub const NT_SIGINFO: u32 = 0x5349_4749;
56
57///Description of mapped files.
58pub const NT_FILE: u32 = 0x4649_4c45;
59
60#[derive(Clone, Copy, Debug)]
61#[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))]
62#[repr(C)]
63/// Note section contents. Each entry in the note section begins with a header
64/// of a fixed form.
65pub struct Nhdr32 {
66    /// Length of the note's name (includes the terminator)
67    pub n_namesz: u32,
68    /// Length of the note's descriptor
69    pub n_descsz: u32,
70    /// Type of the note
71    pub n_type: u32,
72}
73
74// Declare that this is a plain type.
75unsafe impl plain::Plain for Nhdr32 {}
76
77#[derive(Clone, Copy, Debug)]
78#[cfg_attr(feature = "alloc", derive(Pread, Pwrite, IOread, IOwrite, SizeWith))]
79#[repr(C)]
80/// Note section contents. Each entry in the note section begins with a header
81/// of a fixed form.
82pub struct Nhdr64 {
83    /// Length of the note's name (includes the terminator)
84    pub n_namesz: u64,
85    /// Length of the note's descriptor.
86    pub n_descsz: u64,
87    /// Type of the note.
88    pub n_type: u64,
89}
90
91// Declare that this is a plain type.
92unsafe impl plain::Plain for Nhdr64 {}
93
94if_alloc! {
95    use crate::error;
96    use crate::container;
97    use scroll::ctx;
98    use alloc::vec::Vec;
99
100    /// An iterator over ELF binary notes in a note section or segment
101    pub struct NoteDataIterator<'a> {
102        pub data: &'a [u8],
103        pub size: usize,
104        pub offset: usize,
105        pub ctx: (usize, container::Ctx), // (alignment, ctx)
106    }
107
108    impl<'a> Iterator for NoteDataIterator<'a> {
109        type Item = error::Result<Note<'a>>;
110        fn next(&mut self) -> Option<Self::Item> {
111            if self.offset >= self.size {
112                None
113            } else {
114                debug!("NoteIterator - {:#x}", self.offset);
115                match self.data.gread_with(&mut self.offset, self.ctx) {
116                    Ok(res) => Some(Ok(res)),
117                    Err(e) => Some(Err(e))
118                }
119            }
120        }
121    }
122
123    /// An iterator over ELF binary notes
124    pub struct NoteIterator<'a> {
125        pub iters: Vec<NoteDataIterator<'a>>,
126        pub index: usize,
127    }
128
129    impl<'a> Iterator for NoteIterator<'a> {
130        type Item = error::Result<Note<'a>>;
131        fn next(&mut self) -> Option<Self::Item> {
132            while self.index < self.iters.len() {
133                if let Some(note_result) = self.iters[self.index].next() {
134                    return Some(note_result);
135                }
136
137                self.index += 1;
138            }
139
140            None
141        }
142    }
143
144    #[derive(Debug)]
145    struct NoteHeader {
146        n_namesz: usize,
147        n_descsz: usize,
148        n_type: u32,
149    }
150
151    impl From<Nhdr32> for NoteHeader {
152        fn from(header: Nhdr32) -> Self {
153            NoteHeader {
154                n_namesz: header.n_namesz as usize,
155                n_descsz: header.n_descsz as usize,
156                n_type: header.n_type,
157            }
158        }
159    }
160
161    impl From<Nhdr64> for NoteHeader {
162        fn from(header: Nhdr64) -> Self {
163            NoteHeader {
164                n_namesz: header.n_namesz as usize,
165                n_descsz: header.n_descsz as usize,
166                n_type: header.n_type as u32,
167            }
168        }
169    }
170
171    fn align(alignment: usize, offset: &mut usize) {
172        let diff = *offset % alignment;
173        if diff != 0 {
174            *offset += alignment - diff;
175        }
176    }
177
178    /// A 32/64 bit Note struct, with the name and desc pre-parsed
179    #[derive(Debug)]
180    pub struct Note<'a> {
181        /// The type of this note
182        pub n_type: u32,
183        /// NUL terminated string, where `namesz` includes the terminator
184        pub name: &'a str, // needs padding such that namesz + padding % {wordsize} == 0
185        /// arbitrary data of length `descsz`
186        pub desc: &'a [u8], // needs padding such that descsz + padding % {wordsize} == 0
187    }
188
189    impl<'a> Note<'a> {
190        pub fn type_to_str(&self) -> &'static str {
191            match self.n_type {
192                NT_GNU_ABI_TAG => "NT_GNU_ABI_TAG",
193                NT_GNU_HWCAP => "NT_GNU_HWCAP",
194                NT_GNU_BUILD_ID => "NT_GNU_BUILD_ID",
195                NT_GNU_GOLD_VERSION => "NT_GNU_GOLD_VERSION",
196                NT_GNU_PROPERTY_TYPE_0 => "NT_GNU_PROPERTY_0",
197                _ => "NT_UNKNOWN"
198            }
199        }
200    }
201
202    impl<'a> ctx::TryFromCtx<'a, (usize, container::Ctx)> for Note<'a> {
203        type Error = error::Error;
204        fn try_from_ctx(bytes: &'a [u8], (alignment, ctx): (usize, container::Ctx)) -> Result<(Self, usize), Self::Error> {
205            let offset = &mut 0;
206            let mut alignment = alignment;
207            if alignment < 4 {
208                alignment = 4;
209            }
210            let header: NoteHeader = {
211                match alignment {
212                    4|8 => bytes.gread_with::<Nhdr32>(offset, ctx.le)?.into(),
213                    _ => return Err(error::Error::Malformed(format!("Notes has unimplemented alignment requirement: {:#x}", alignment)))
214                }
215            };
216            debug!("{:?} - {:#x}", header, *offset);
217            // -1 because includes \0 terminator
218            let name = bytes.gread_with::<&'a str>(offset, ctx::StrCtx::Length(header.n_namesz.saturating_sub(1)))?;
219            if header.n_namesz > 0 {
220                *offset += 1;
221            }
222            align(alignment, offset);
223            debug!("note name {} - {:#x}", name, *offset);
224            let desc = bytes.gread_with::<&'a [u8]>(offset, header.n_descsz)?;
225            align(alignment, offset);
226            debug!("desc {:?} - {:#x}", desc, *offset);
227            Ok((Note {
228                name,
229                desc,
230                n_type: header.n_type,
231            }, *offset))
232        }
233    }
234
235    #[cfg(test)]
236    mod tests {
237        use super::*;
238
239        static NOTE_DATA: [u8; 132] = [0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
240                                      0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
241                                      0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
242                                      0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
243                                      0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
244                                      0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00,
245                                      0xbc, 0xfc, 0x66, 0xcd, 0xc7, 0xd5, 0x14, 0x7b,
246                                      0x53, 0xb1, 0x10, 0x11, 0x94, 0x86, 0x8e, 0xf9,
247                                      0x4f, 0xe8, 0xdd, 0xdb, 0x04, 0x00, 0x00, 0x00,
248                                      0x30, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
249                                      0x47, 0x4E, 0x55, 0x00, 0x02, 0x80, 0x00, 0xC0,
250                                      0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
251                                      0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0,
252                                      0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
253                                      0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0xC0,
254                                      0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
255                                      0x00, 0x00, 0x00, 0x00];
256
257        static CONTEXT: (usize, container::Ctx) = (4, container::Ctx {
258            container: container::Container::Big,
259            le: ::scroll::Endian::Little,
260        });
261
262        fn make_note_iter(start: usize, end: usize) -> NoteDataIterator<'static> {
263            NoteDataIterator {
264                data: &NOTE_DATA,
265                size: end,
266                offset: start,
267                ctx: CONTEXT,
268            }
269        }
270
271        #[test]
272        fn iter_single_section() {
273            let mut notes = NoteIterator {
274                iters: vec![make_note_iter(0, 132)],
275                index: 0,
276            };
277
278            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
279            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
280            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_PROPERTY_TYPE_0);
281            assert!(notes.next().is_none());
282        }
283
284        #[test]
285        fn iter_multiple_sections() {
286            let mut notes = NoteIterator {
287                iters: vec![make_note_iter(0, 32), make_note_iter(32, 68), make_note_iter(68, 132)],
288                index: 0,
289            };
290
291            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
292            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
293            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_PROPERTY_TYPE_0);
294            assert!(notes.next().is_none());
295        }
296
297        #[test]
298        fn skip_empty_sections() {
299            let mut notes = NoteIterator {
300                iters: vec![
301                    make_note_iter(0, 32),
302                    make_note_iter(0, 0),
303                    make_note_iter(32, 68),
304                ],
305                index: 0,
306            };
307
308            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_ABI_TAG);
309            assert_eq!(notes.next().unwrap().unwrap().n_type, NT_GNU_BUILD_ID);
310            assert!(notes.next().is_none());
311        }
312
313        #[test]
314        fn ignore_no_sections() {
315            let mut notes = NoteIterator { iters: vec![], index: 0 };
316            assert!(notes.next().is_none());
317        }
318    }
319}