multiboot2/
elf_sections.rs

1//! Module for [`ElfSectionsTag`].
2
3use crate::{TagHeader, TagType};
4use core::fmt::{Debug, Formatter};
5use core::marker::PhantomData;
6use core::mem;
7use core::str::Utf8Error;
8use multiboot2_common::{MaybeDynSized, Tag};
9#[cfg(feature = "builder")]
10use {alloc::boxed::Box, multiboot2_common::new_boxed};
11
12/// This tag contains the section header table from an ELF binary.
13// The sections iterator is provided via the [`ElfSectionsTag::sections`]
14// method.
15#[derive(ptr_meta::Pointee, PartialEq, Eq)]
16#[repr(C, align(8))]
17pub struct ElfSectionsTag {
18    header: TagHeader,
19    number_of_sections: u32,
20    entry_size: u32,
21    shndx: u32,
22    sections: [u8],
23}
24
25impl ElfSectionsTag {
26    /// Create a new ElfSectionsTag with the given data.
27    #[cfg(feature = "builder")]
28    #[must_use]
29    pub fn new(number_of_sections: u32, entry_size: u32, shndx: u32, sections: &[u8]) -> Box<Self> {
30        let header = TagHeader::new(Self::ID, 0);
31        let number_of_sections = number_of_sections.to_ne_bytes();
32        let entry_size = entry_size.to_ne_bytes();
33        let shndx = shndx.to_ne_bytes();
34        new_boxed(
35            header,
36            &[&number_of_sections, &entry_size, &shndx, sections],
37        )
38    }
39
40    /// Get an iterator over the ELF sections.
41    #[must_use]
42    pub const fn sections(&self) -> ElfSectionIter {
43        let string_section_offset = (self.shndx * self.entry_size) as isize;
44        let string_section_ptr =
45            unsafe { self.sections.as_ptr().offset(string_section_offset) as *const _ };
46        ElfSectionIter {
47            current_section: self.sections.as_ptr(),
48            remaining_sections: self.number_of_sections,
49            entry_size: self.entry_size,
50            string_section: string_section_ptr,
51            _phantom_data: PhantomData,
52        }
53    }
54
55    /// Returns the amount of sections.
56    #[must_use]
57    pub const fn number_of_sections(&self) -> u32 {
58        self.number_of_sections
59    }
60
61    /// Returns the size of each entry.
62    #[must_use]
63    pub const fn entry_size(&self) -> u32 {
64        self.entry_size
65    }
66
67    /// Returns the index of the section header string table.
68    #[must_use]
69    pub const fn shndx(&self) -> u32 {
70        self.shndx
71    }
72}
73
74impl MaybeDynSized for ElfSectionsTag {
75    type Header = TagHeader;
76
77    const BASE_SIZE: usize = mem::size_of::<TagHeader>() + 3 * mem::size_of::<u32>();
78
79    fn dst_len(header: &TagHeader) -> usize {
80        assert!(header.size as usize >= Self::BASE_SIZE);
81        header.size as usize - Self::BASE_SIZE
82    }
83}
84
85impl Tag for ElfSectionsTag {
86    type IDType = TagType;
87
88    const ID: TagType = TagType::ElfSections;
89}
90
91impl Debug for ElfSectionsTag {
92    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
93        f.debug_struct("ElfSectionsTag")
94            .field("typ", &self.header.typ)
95            .field("size", &self.header.size)
96            .field("number_of_sections", &self.number_of_sections)
97            .field("entry_size", &self.entry_size)
98            .field("shndx", &self.shndx)
99            .field("sections", &self.sections())
100            .finish()
101    }
102}
103
104/// An iterator over [`ElfSection`]s.
105#[derive(Clone)]
106pub struct ElfSectionIter<'a> {
107    current_section: *const u8,
108    remaining_sections: u32,
109    entry_size: u32,
110    string_section: *const u8,
111    _phantom_data: PhantomData<&'a ()>,
112}
113
114impl<'a> Iterator for ElfSectionIter<'a> {
115    type Item = ElfSection<'a>;
116
117    fn next(&mut self) -> Option<ElfSection<'a>> {
118        while self.remaining_sections != 0 {
119            let section = ElfSection {
120                inner: self.current_section,
121                string_section: self.string_section,
122                entry_size: self.entry_size,
123                _phantom: PhantomData,
124            };
125
126            self.current_section = unsafe { self.current_section.offset(self.entry_size as isize) };
127            self.remaining_sections -= 1;
128
129            if section.section_type() != ElfSectionType::Unused {
130                return Some(section);
131            }
132        }
133        None
134    }
135
136    fn size_hint(&self) -> (usize, Option<usize>) {
137        (
138            self.remaining_sections as usize,
139            Some(self.remaining_sections as usize),
140        )
141    }
142}
143
144impl ExactSizeIterator for ElfSectionIter<'_> {
145    fn len(&self) -> usize {
146        self.remaining_sections as usize
147    }
148}
149
150impl Debug for ElfSectionIter<'_> {
151    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
152        /// Limit how many Elf-Sections should be debug-formatted.
153        /// Can be thousands of sections for a Rust binary => this is useless output.
154        /// If the user really wants this, they should debug-format the field directly.
155        const ELF_SECTIONS_LIMIT: usize = 7;
156
157        let mut debug = f.debug_list();
158
159        self.clone().take(ELF_SECTIONS_LIMIT).for_each(|ref e| {
160            debug.entry(e);
161        });
162
163        if self.clone().len() > ELF_SECTIONS_LIMIT {
164            debug.entry(&"...");
165        }
166
167        debug.finish()
168    }
169}
170
171/// A single generic ELF Section.
172// TODO Shouldn't this be called ElfSectionPtrs, ElfSectionWrapper or so?
173#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
174pub struct ElfSection<'a> {
175    inner: *const u8,
176    string_section: *const u8,
177    entry_size: u32,
178    _phantom: PhantomData<&'a ()>,
179}
180
181impl Debug for ElfSection<'_> {
182    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
183        let inner = self.get();
184        f.debug_struct("ElfSection")
185            .field("inner", &inner)
186            .field("string_section_ptr", &self.string_section)
187            .finish()
188    }
189}
190
191#[derive(Clone, Copy, Debug)]
192#[repr(C, packed)]
193struct ElfSectionInner32 {
194    name_index: u32,
195    typ: u32,
196    flags: u32,
197    addr: u32,
198    offset: u32,
199    size: u32,
200    link: u32,
201    info: u32,
202    addralign: u32,
203    entry_size: u32,
204}
205
206#[derive(Clone, Copy, Debug)]
207#[repr(C, packed)]
208struct ElfSectionInner64 {
209    name_index: u32,
210    typ: u32,
211    flags: u64,
212    addr: u64,
213    offset: u64,
214    size: u64,
215    link: u32,
216    info: u32,
217    addralign: u64,
218    entry_size: u64,
219}
220
221impl ElfSection<'_> {
222    /// Get the section type as an `ElfSectionType` enum variant.
223    #[must_use]
224    pub fn section_type(&self) -> ElfSectionType {
225        match self.get().typ() {
226            0 => ElfSectionType::Unused,
227            1 => ElfSectionType::ProgramSection,
228            2 => ElfSectionType::LinkerSymbolTable,
229            3 => ElfSectionType::StringTable,
230            4 => ElfSectionType::RelaRelocation,
231            5 => ElfSectionType::SymbolHashTable,
232            6 => ElfSectionType::DynamicLinkingTable,
233            7 => ElfSectionType::Note,
234            8 => ElfSectionType::Uninitialized,
235            9 => ElfSectionType::RelRelocation,
236            10 => ElfSectionType::Reserved,
237            11 => ElfSectionType::DynamicLoaderSymbolTable,
238            0x6000_0000..=0x6FFF_FFFF => ElfSectionType::EnvironmentSpecific,
239            0x7000_0000..=0x7FFF_FFFF => ElfSectionType::ProcessorSpecific,
240            e => {
241                log::warn!("Unknown section type {e:x}. Treating as ElfSectionType::Unused");
242                ElfSectionType::Unused
243            }
244        }
245    }
246
247    /// Get the "raw" section type as a `u32`
248    #[must_use]
249    pub fn section_type_raw(&self) -> u32 {
250        self.get().typ()
251    }
252
253    /// Read the name of the section.
254    pub fn name(&self) -> Result<&str, Utf8Error> {
255        use core::{slice, str};
256
257        let name_ptr = unsafe { self.string_table().offset(self.get().name_index() as isize) };
258
259        // strlen without null byte
260        let strlen = {
261            let mut len = 0;
262            while unsafe { *name_ptr.offset(len) } != 0 {
263                len += 1;
264            }
265            len as usize
266        };
267
268        str::from_utf8(unsafe { slice::from_raw_parts(name_ptr, strlen) })
269    }
270
271    /// Get the physical start address of the section.
272    #[must_use]
273    pub fn start_address(&self) -> u64 {
274        self.get().addr()
275    }
276
277    /// Get the physical end address of the section.
278    ///
279    /// This is the same as doing `section.start_address() + section.size()`
280    #[must_use]
281    pub fn end_address(&self) -> u64 {
282        self.get().addr() + self.get().size()
283    }
284
285    /// Get the section's size in bytes.
286    #[must_use]
287    pub fn size(&self) -> u64 {
288        self.get().size()
289    }
290
291    /// Get the section's address alignment constraints.
292    ///
293    /// That is, the value of `start_address` must be congruent to 0,
294    /// modulo the value of `addrlign`. Currently, only 0 and positive
295    /// integral powers of two are allowed. Values 0 and 1 mean the section has no
296    /// alignment constraints.
297    #[must_use]
298    pub fn addralign(&self) -> u64 {
299        self.get().addralign()
300    }
301
302    /// Get the section's flags.
303    #[must_use]
304    pub fn flags(&self) -> ElfSectionFlags {
305        ElfSectionFlags::from_bits_truncate(self.get().flags())
306    }
307
308    /// Check if the `ALLOCATED` flag is set in the section flags.
309    #[must_use]
310    pub fn is_allocated(&self) -> bool {
311        self.flags().contains(ElfSectionFlags::ALLOCATED)
312    }
313
314    fn get(&self) -> &dyn ElfSectionInner {
315        match self.entry_size {
316            40 => unsafe { &*(self.inner as *const ElfSectionInner32) },
317            64 => unsafe { &*(self.inner as *const ElfSectionInner64) },
318            s => panic!("Unexpected entry size: {s}"),
319        }
320    }
321
322    unsafe fn string_table(&self) -> *const u8 {
323        match self.entry_size {
324            40 => {
325                let ptr = self.string_section.cast::<ElfSectionInner32>();
326                let reference = unsafe { ptr.as_ref().unwrap() };
327                reference.addr() as *const u8
328            }
329            64 => {
330                let ptr = self.string_section.cast::<ElfSectionInner64>();
331                let reference = unsafe { ptr.as_ref().unwrap() };
332                reference.addr() as *const u8
333            }
334            s => panic!("Unexpected entry size: {s}"),
335        }
336    }
337}
338
339trait ElfSectionInner: Debug {
340    fn name_index(&self) -> u32;
341
342    fn typ(&self) -> u32;
343
344    fn flags(&self) -> u64;
345
346    fn addr(&self) -> u64;
347
348    fn size(&self) -> u64;
349
350    fn addralign(&self) -> u64;
351}
352
353impl ElfSectionInner for ElfSectionInner32 {
354    fn name_index(&self) -> u32 {
355        self.name_index
356    }
357
358    fn typ(&self) -> u32 {
359        self.typ
360    }
361
362    fn flags(&self) -> u64 {
363        self.flags.into()
364    }
365
366    fn addr(&self) -> u64 {
367        self.addr.into()
368    }
369
370    fn size(&self) -> u64 {
371        self.size.into()
372    }
373
374    fn addralign(&self) -> u64 {
375        self.addralign.into()
376    }
377}
378
379impl ElfSectionInner for ElfSectionInner64 {
380    fn name_index(&self) -> u32 {
381        self.name_index
382    }
383
384    fn typ(&self) -> u32 {
385        self.typ
386    }
387
388    fn flags(&self) -> u64 {
389        self.flags
390    }
391
392    fn addr(&self) -> u64 {
393        self.addr
394    }
395
396    fn size(&self) -> u64 {
397        self.size
398    }
399
400    fn addralign(&self) -> u64 {
401        self.addralign
402    }
403}
404
405/// An enum abstraction over raw ELF section types.
406#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
407#[repr(u32)]
408pub enum ElfSectionType {
409    /// This value marks the section header as inactive; it does not have an
410    /// associated section. Other members of the section header have undefined
411    /// values.
412    Unused = 0,
413
414    /// The section holds information defined by the program, whose format and
415    /// meaning are determined solely by the program.
416    ProgramSection = 1,
417
418    /// This section holds a linker symbol table.
419    LinkerSymbolTable = 2,
420
421    /// The section holds a string table.
422    StringTable = 3,
423
424    /// The section holds relocation entries with explicit addends, such as type
425    /// Elf32_Rela for the 32-bit class of object files. An object file may have
426    /// multiple relocation sections.
427    RelaRelocation = 4,
428
429    /// The section holds a symbol hash table.
430    SymbolHashTable = 5,
431
432    /// The section holds dynamic linking tables.
433    DynamicLinkingTable = 6,
434
435    /// This section holds information that marks the file in some way.
436    Note = 7,
437
438    /// A section of this type occupies no space in the file but otherwise resembles
439    /// `ProgramSection`. Although this section contains no bytes, the
440    /// sh_offset member contains the conceptual file offset.
441    Uninitialized = 8,
442
443    /// The section holds relocation entries without explicit addends, such as type
444    /// Elf32_Rel for the 32-bit class of object files. An object file may have
445    /// multiple relocation sections.
446    RelRelocation = 9,
447
448    /// This section type is reserved but has unspecified semantics.
449    Reserved = 10,
450
451    /// This section holds a dynamic loader symbol table.
452    DynamicLoaderSymbolTable = 11,
453
454    /// Values in this inclusive range (`[0x6000_0000, 0x6FFF_FFFF)`) are
455    /// reserved for environment-specific semantics.
456    EnvironmentSpecific = 0x6000_0000,
457
458    /// Values in this inclusive range (`[0x7000_0000, 0x7FFF_FFFF)`) are
459    /// reserved for processor-specific semantics.
460    ProcessorSpecific = 0x7000_0000,
461}
462
463bitflags! {
464    /// ELF Section bitflags.
465    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
466    #[repr(transparent)]
467    pub struct ElfSectionFlags: u64 {
468        /// The section contains data that should be writable during program execution.
469        const WRITABLE = 0x1;
470
471        /// The section occupies memory during the process execution.
472        const ALLOCATED = 0x2;
473
474        /// The section contains executable machine instructions.
475        const EXECUTABLE = 0x4;
476        // plus environment-specific use at 0x0F000000
477        // plus processor-specific use at 0xF0000000
478    }
479}