kernel_elf_parser/
info.rs

1//! ELF information parsed from the ELF file
2//!
3
4extern crate alloc;
5use alloc::vec::Vec;
6
7use memory_addr::VirtAddr;
8use page_table_entry::MappingFlags;
9
10use crate::auxv::{AuxvEntry, AuxvType};
11
12/// ELF Program Header applied to the kernel
13///
14/// Details can be seen in the [ELF Program Header](https://refspecs.linuxbase.org/elf/gabi4+/ch5.pheader.html)
15pub struct ELFPH {
16    /// The start offset of the segment in the ELF file
17    pub offset: usize,
18    /// The destination virtual address of the segment in the kernel memory
19    pub vaddr: VirtAddr,
20    /// Memory size of the segment
21    pub memsz: u64,
22    /// File size of the segment
23    pub filesz: u64,
24    /// [`MappingFlags`] of the segment which is used to set the page table entry
25    pub flags: MappingFlags,
26}
27
28/// A wrapper for the ELF file data with some useful methods.
29pub struct ELFParser<'a> {
30    elf: &'a xmas_elf::ElfFile<'a>,
31    /// Base address of the ELF file loaded into the memory.
32    base: usize,
33}
34
35impl<'a> ELFParser<'a> {
36    fn elf_base_addr(elf: &xmas_elf::ElfFile, interp_base: usize) -> Result<usize, &'static str> {
37        match elf.header.pt2.type_().as_type() {
38            // static
39            xmas_elf::header::Type::Executable => Ok(0),
40            // dynamic
41            xmas_elf::header::Type::SharedObject => {
42                match elf
43                    .program_iter()
44                    .filter(|ph| ph.get_type() == Ok(xmas_elf::program::Type::Interp))
45                    .count()
46                {
47                    // Interpreter invoked by the ELF file.
48                    0 => Ok(interp_base),
49                    // Dynamic ELF file
50                    1 => Ok(0),
51                    _ => Err("Multiple interpreters found"),
52                }
53            }
54            _ => Err("Unsupported ELF type"),
55        }
56    }
57
58    /// Create a new `ELFInfo` instance.
59    /// # Arguments
60    /// * `elf` - The ELF file data
61    /// * `interp_base` - Address of the interpreter if the ELF file is a dynamic executable
62    /// * `bias` - Bias for the base address of the PIE executable.
63    /// * `uspace_base` - The lowest address of the user space
64    ///
65    /// # Note
66    /// If the ELF file is a dynamic executable, the `interp_base` should be the address of the interpreter, and the address of the ELF file will be `elf.base_addr() + bias`.
67    pub fn new(
68        elf: &'a xmas_elf::ElfFile,
69        interp_base: usize,
70        bias: Option<isize>,
71        uspace_base: usize,
72    ) -> Result<Self, &'static str> {
73        if elf.header.pt1.magic.as_slice() != b"\x7fELF" {
74            return Err("invalid elf!");
75        }
76
77        // Check if the ELF file is a Position Independent Executable (PIE)
78        let is_pie = elf.header.pt2.type_().as_type() == xmas_elf::header::Type::SharedObject
79            || (elf.header.pt2.type_().as_type() == xmas_elf::header::Type::Executable
80                && elf
81                    .program_iter()
82                    .any(|ph| ph.get_type() == Ok(xmas_elf::program::Type::Interp)));
83
84        // If it is not PIE, and the lowest address is less than user space base, it is invalid.
85        if !is_pie
86            && elf.program_iter().any(|ph| {
87                ph.get_type() == Ok(xmas_elf::program::Type::Load)
88                    && ph.virtual_addr() < uspace_base as u64
89            })
90        {
91            return Err("Invalid ELF base address");
92        }
93
94        let mut base = Self::elf_base_addr(elf, interp_base)?;
95        if is_pie {
96            base = base.wrapping_add(bias.unwrap_or(0) as usize);
97        }
98        Ok(Self { elf, base })
99    }
100
101    /// The entry point of the ELF file.
102    pub fn entry(&self) -> usize {
103        self.elf.header.pt2.entry_point() as usize + self.base
104    }
105
106    /// The number of program headers in the ELF file.
107    pub fn phnum(&self) -> usize {
108        self.elf.header.pt2.ph_count() as usize
109    }
110
111    /// The size of the program header table entry in the ELF file.
112    pub fn phent(&self) -> usize {
113        self.elf.header.pt2.ph_entry_size() as usize
114    }
115
116    /// The offset of the program header table in the ELF file.
117    pub fn phdr(&self) -> usize {
118        self.elf.header.pt2.ph_offset() as usize
119            + self.base
120            + self
121                .elf
122                .program_iter()
123                .find(|ph| ph.get_type() == Ok(xmas_elf::program::Type::Load))
124                .map(|ph| ph.virtual_addr() as usize)
125                .unwrap_or(0)
126    }
127
128    /// The base address of the ELF file loaded into the memory.
129    pub fn base(&self) -> usize {
130        self.base
131    }
132
133    /// The ref of the ELF file data.
134    pub fn elf(&self) -> &xmas_elf::ElfFile {
135        self.elf
136    }
137
138    /// Part of auxiliary vectors from the ELF file.
139    ///
140    /// # Arguments
141    ///
142    /// * `pagesz` - The page size of the system
143    ///
144    /// Details about auxiliary vectors are described in <https://articles.manugarg.com/aboutelfauxiliaryvectors.html>
145    pub fn auxv_vector(&self, pagesz: usize) -> [AuxvEntry; 17] {
146        [
147            AuxvEntry::new(AuxvType::PHDR, self.phdr()),
148            AuxvEntry::new(AuxvType::PHENT, self.phent()),
149            AuxvEntry::new(AuxvType::PHNUM, self.phnum()),
150            AuxvEntry::new(AuxvType::PAGESZ, pagesz),
151            AuxvEntry::new(AuxvType::BASE, self.base()),
152            AuxvEntry::new(AuxvType::FLAGS, 0),
153            AuxvEntry::new(AuxvType::ENTRY, self.entry()),
154            AuxvEntry::new(AuxvType::HWCAP, 0),
155            AuxvEntry::new(AuxvType::CLKTCK, 100),
156            AuxvEntry::new(AuxvType::PLATFORM, 0),
157            AuxvEntry::new(AuxvType::UID, 0),
158            AuxvEntry::new(AuxvType::EUID, 0),
159            AuxvEntry::new(AuxvType::GID, 0),
160            AuxvEntry::new(AuxvType::EGID, 0),
161            AuxvEntry::new(AuxvType::RANDOM, 0),
162            AuxvEntry::new(AuxvType::EXECFN, 0),
163            AuxvEntry::new(AuxvType::NULL, 0),
164        ]
165    }
166
167    /// Read all [`self::ELFPH`] with `LOAD` type of the elf file.
168    pub fn ph_load(&self) -> Vec<ELFPH> {
169        let mut segments = Vec::new();
170        // Load Elf "LOAD" segments at base_addr.
171        self.elf
172            .program_iter()
173            .filter(|ph| ph.get_type() == Ok(xmas_elf::program::Type::Load))
174            .for_each(|ph| {
175                let start_va = ph.virtual_addr() as usize + self.base;
176                let start_offset = ph.offset() as usize;
177                let mut flags = MappingFlags::USER;
178                if ph.flags().is_read() {
179                    flags |= MappingFlags::READ;
180                }
181                if ph.flags().is_write() {
182                    flags |= MappingFlags::WRITE;
183                }
184                if ph.flags().is_execute() {
185                    flags |= MappingFlags::EXECUTE;
186                }
187                segments.push(ELFPH {
188                    offset: start_offset,
189                    vaddr: VirtAddr::from(start_va),
190                    memsz: ph.mem_size(),
191                    filesz: ph.file_size(),
192                    flags,
193                });
194            });
195        segments
196    }
197}