kernel/executable/
elf.rs

1use core::{ffi::CStr, fmt, mem, ops::Deref};
2
3use alloc::{string::String, vec, vec::Vec};
4
5use crate::{fs, memory_management::virtual_memory_mapper};
6
7#[derive(Debug)]
8pub enum ElfLoadError {
9    InvalidMagic,
10    #[allow(dead_code)]
11    FileSystemError(fs::FileSystemError),
12    InvalidElfOrNotSupported,
13    UnexpectedEndOfFile,
14}
15
16impl From<fs::FileSystemError> for ElfLoadError {
17    fn from(e: fs::FileSystemError) -> Self {
18        Self::FileSystemError(e)
19    }
20}
21
22#[allow(dead_code)]
23mod consts {
24    pub const ELF_MAGIC: &[u8; 4] = b"\x7fELF";
25    pub const ABI_SYSV: u8 = 0;
26    pub const BITS_32: u8 = 1;
27    pub const BITS_64: u8 = 2;
28    pub const ENDIANNESS_LITTLE: u8 = 1;
29    pub const ENDIANNESS_BIG: u8 = 2;
30
31    pub const ELF_TYPE_RELOCATABLE: u16 = 1;
32    pub const ELF_TYPE_EXECUTABLE: u16 = 2;
33    pub const ELF_TYPE_SHARED: u16 = 3;
34
35    pub const ELF_MACHINE_X86: u16 = 3;
36    pub const ELF_MACHINE_X86_64: u16 = 62;
37
38    pub const PROG_FLAG_EXE: u32 = 0x1;
39    pub const PROG_FLAG_WRITE: u32 = 0x2;
40    pub const PROG_FLAG_READ: u32 = 0x4;
41}
42
43pub fn to_virtual_memory_flags(flags: u32) -> u64 {
44    // 0 means read-only
45    let mut vm_flags = 0;
46
47    if flags & consts::PROG_FLAG_WRITE != 0 {
48        vm_flags |= virtual_memory_mapper::flags::PTE_WRITABLE;
49    }
50    if flags & consts::PROG_FLAG_EXE != 0 {
51        // TODO: add support for executable pages
52    }
53    vm_flags
54}
55
56#[repr(C, packed)]
57#[derive(Copy, Clone, Debug)]
58struct ElfHeaderBase {
59    magic: [u8; 4],
60    bits: u8,
61    endianness: u8,
62    version: u8,
63    abi: u8,
64    abi_version: u8,
65    _pad: [u8; 7],
66    elf_type: u16,
67    machine: u16,
68    elf_version: u32,
69}
70
71#[repr(C, packed)]
72#[derive(Copy, Clone, Debug)]
73struct ElfHeader32 {
74    entry: u32,
75    program_header_offset: u32,
76    section_header_offset: u32,
77    flags: u32,
78    header_size: u16,
79    program_header_entry_size: u16,
80    program_header_entry_count: u16,
81    section_header_entry_size: u16,
82    section_header_entry_count: u16,
83    section_header_string_table_index: u16,
84}
85#[repr(C, packed)]
86#[derive(Copy, Clone, Debug)]
87struct ElfHeader64 {
88    entry: u64,
89    program_header_offset: u64,
90    section_header_offset: u64,
91    flags: u32,
92    header_size: u16,
93    program_header_entry_size: u16,
94    program_header_entry_count: u16,
95    section_header_entry_size: u16,
96    section_header_entry_count: u16,
97    section_header_string_table_index: u16,
98}
99
100#[derive(Copy, Clone)]
101union ElfHeaderUnion {
102    header32: ElfHeader32,
103    header64: ElfHeader64,
104}
105
106#[repr(C, packed)]
107#[derive(Copy, Clone)]
108struct ElfHeader {
109    base: ElfHeaderBase,
110    header: ElfHeaderUnion,
111}
112
113#[allow(dead_code)]
114impl ElfHeader {
115    fn is_valid_and_supported(&self) -> bool {
116        if (self.base.bits != consts::BITS_32 && self.base.bits != consts::BITS_64)
117            || self.base.endianness != consts::ENDIANNESS_LITTLE
118            || self.base.version != 1
119            || self.base.abi != consts::ABI_SYSV
120            || self.base.abi_version != 0
121            || (self.base.elf_type != consts::ELF_TYPE_EXECUTABLE
122                && self.base.elf_type != consts::ELF_TYPE_SHARED)
123            || self.base.machine != consts::ELF_MACHINE_X86_64
124            || self.base.elf_version != 1
125        {
126            return false;
127        }
128        true
129    }
130
131    fn is_elf64(&self) -> bool {
132        self.base.bits == consts::BITS_64
133    }
134
135    fn is_little_endian(&self) -> bool {
136        self.base.endianness == consts::ENDIANNESS_LITTLE
137    }
138
139    fn entry(&self) -> u64 {
140        if self.is_elf64() {
141            unsafe { self.header.header64.entry }
142        } else {
143            unsafe { self.header.header32.entry as u64 }
144        }
145    }
146
147    fn program_header_offset(&self) -> u64 {
148        if self.is_elf64() {
149            unsafe { self.header.header64.program_header_offset }
150        } else {
151            unsafe { self.header.header32.program_header_offset as u64 }
152        }
153    }
154
155    fn program_header_entry_size(&self) -> u64 {
156        if self.is_elf64() {
157            unsafe { self.header.header64.program_header_entry_size as u64 }
158        } else {
159            unsafe { self.header.header32.program_header_entry_size as u64 }
160        }
161    }
162
163    fn program_header_entry_count(&self) -> u64 {
164        if self.is_elf64() {
165            unsafe { self.header.header64.program_header_entry_count as u64 }
166        } else {
167            unsafe { self.header.header32.program_header_entry_count as u64 }
168        }
169    }
170
171    fn section_header_offset(&self) -> u64 {
172        if self.is_elf64() {
173            unsafe { self.header.header64.section_header_offset }
174        } else {
175            unsafe { self.header.header32.section_header_offset as u64 }
176        }
177    }
178
179    fn section_header_entry_size(&self) -> u64 {
180        if self.is_elf64() {
181            unsafe { self.header.header64.section_header_entry_size as u64 }
182        } else {
183            unsafe { self.header.header32.section_header_entry_size as u64 }
184        }
185    }
186
187    fn section_header_entry_count(&self) -> u64 {
188        if self.is_elf64() {
189            unsafe { self.header.header64.section_header_entry_count as u64 }
190        } else {
191            unsafe { self.header.header32.section_header_entry_count as u64 }
192        }
193    }
194
195    fn section_header_string_table_index(&self) -> u64 {
196        if self.is_elf64() {
197            unsafe { self.header.header64.section_header_string_table_index as u64 }
198        } else {
199            unsafe { self.header.header32.section_header_string_table_index as u64 }
200        }
201    }
202
203    fn size_of_header(&self) -> u64 {
204        if self.is_elf64() {
205            unsafe { self.header.header64.header_size as u64 }
206        } else {
207            unsafe { self.header.header32.header_size as u64 }
208        }
209    }
210}
211
212impl fmt::Debug for ElfHeader {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        let mut s = f.debug_struct("ElfHeader");
215        s.field("base", &self.base);
216        if self.is_elf64() {
217            s.field("header64", unsafe { &self.header.header64 });
218        } else {
219            s.field("header32", unsafe { &self.header.header32 });
220        }
221        s.finish()
222    }
223}
224
225#[derive(Copy, Clone, Debug)]
226#[allow(dead_code)]
227pub enum ElfProgramType {
228    // Unused
229    Null,
230    // Loadable segment
231    Load,
232    // Dynamic linking information
233    Dynamic,
234    // Program interpreter path
235    Interpreter,
236    // Auxiliary information
237    Note,
238    // Reserved
239    Shlib,
240    // Entry for header table itself
241    ProgramHeader,
242    // Thread-local storage template
243    ThreadLocalStorage,
244    OsSpecific(u32),
245    ProcessorSpecific(u32),
246}
247
248impl ElfProgramType {
249    pub fn from_u32(ty: u32) -> Self {
250        match ty {
251            0 => Self::Null,
252            1 => Self::Load,
253            2 => Self::Dynamic,
254            3 => Self::Interpreter,
255            4 => Self::Note,
256            5 => Self::Shlib,
257            6 => Self::ProgramHeader,
258            7 => Self::ThreadLocalStorage,
259            0x60000000..=0x6fffffff => Self::OsSpecific(ty),
260            0x70000000..=0x7fffffff => Self::ProcessorSpecific(ty),
261            _ => Self::Null,
262        }
263    }
264}
265
266#[repr(C, packed)]
267#[derive(Copy, Clone, Debug)]
268pub struct ElfProgram32 {
269    // Type of segment
270    ty: u32,
271    // File offset where segment is located, in bytes
272    offset: u32,
273    // Virtual address of beginning of segment
274    virtual_address: u32,
275    // Physical address of beginning of segment (OS-specific)
276    physical_address: u32,
277    // Num. of bytes in file image of segment (can be zero)
278    file_size: u32,
279    // Num. of bytes in mem image of segment (can be zero)
280    mem_size: u32,
281    // Segment flags
282    flags: u32,
283    // Segment alignment constraint
284    alignment: u32,
285}
286
287#[repr(C, packed)]
288#[derive(Copy, Clone, Debug)]
289pub struct ElfProgram64 {
290    // Type of segment
291    ty: u32,
292    // Segment flags
293    flags: u32,
294    // File offset where segment is located, in bytes
295    offset: u64,
296    // Virtual address of beginning of segment
297    virtual_address: u64,
298    // Physical address of beginning of segment (OS-specific)
299    physical_address: u64,
300    // Num. of bytes in file image of segment (can be zero)
301    file_size: u64,
302    // Num. of bytes in mem image of segment (can be zero)
303    mem_size: u64,
304    // Segment alignment constraint
305    alignment: u64,
306}
307
308#[derive(Clone, Copy)]
309pub enum ElfProgram {
310    Program32(ElfProgram32),
311    Program64(ElfProgram64),
312}
313
314impl ElfProgram {
315    pub fn load(
316        file: &mut fs::File,
317        is_elf64: bool,
318        entry_size: u64,
319    ) -> Result<Self, ElfLoadError> {
320        if is_elf64 {
321            if entry_size != mem::size_of::<ElfProgram64>() as u64 {
322                return Err(ElfLoadError::InvalidElfOrNotSupported);
323            }
324            let mut header_bytes = [0u8; mem::size_of::<ElfProgram64>()];
325            if file.read(&mut header_bytes)? != header_bytes.len() as u64 {
326                return Err(ElfLoadError::UnexpectedEndOfFile);
327            }
328            let program = unsafe { &*(header_bytes.as_ptr() as *const ElfProgram64) };
329            Ok(Self::Program64(*program))
330        } else {
331            if entry_size != mem::size_of::<ElfProgram32>() as u64 {
332                return Err(ElfLoadError::InvalidElfOrNotSupported);
333            }
334            let mut header_bytes = [0u8; mem::size_of::<ElfProgram32>()];
335            if file.read(&mut header_bytes)? != header_bytes.len() as u64 {
336                return Err(ElfLoadError::UnexpectedEndOfFile);
337            }
338            let program = unsafe { &*(header_bytes.as_ptr() as *const ElfProgram32) };
339            Ok(Self::Program32(*program))
340        }
341    }
342
343    pub fn ty(&self) -> ElfProgramType {
344        let ty_u32 = match self {
345            Self::Program32(p) => p.ty,
346            Self::Program64(p) => p.ty,
347        };
348
349        ElfProgramType::from_u32(ty_u32)
350    }
351
352    pub fn offset(&self) -> u64 {
353        match self {
354            Self::Program32(p) => p.offset as u64,
355            Self::Program64(p) => p.offset,
356        }
357    }
358
359    pub fn virtual_address(&self) -> u64 {
360        match self {
361            Self::Program32(p) => p.virtual_address as u64,
362            Self::Program64(p) => p.virtual_address,
363        }
364    }
365
366    pub fn physical_address(&self) -> u64 {
367        match self {
368            Self::Program32(p) => p.physical_address as u64,
369            Self::Program64(p) => p.physical_address,
370        }
371    }
372
373    pub fn file_size(&self) -> u64 {
374        match self {
375            Self::Program32(p) => p.file_size as u64,
376            Self::Program64(p) => p.file_size,
377        }
378    }
379
380    pub fn mem_size(&self) -> u64 {
381        match self {
382            Self::Program32(p) => p.mem_size as u64,
383            Self::Program64(p) => p.mem_size,
384        }
385    }
386
387    pub fn flags(&self) -> u32 {
388        match self {
389            Self::Program32(p) => p.flags,
390            Self::Program64(p) => p.flags,
391        }
392    }
393
394    pub fn alignment(&self) -> u64 {
395        match self {
396            Self::Program32(p) => p.alignment as u64,
397            Self::Program64(p) => p.alignment,
398        }
399    }
400}
401
402impl fmt::Debug for ElfProgram {
403    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404        f.debug_struct("ElfProgram")
405            .field("ty", &self.ty())
406            .field("flags", &self.flags())
407            .field("offset", &self.offset())
408            .field("virtual_address", &self.virtual_address())
409            .field("physical_address", &self.physical_address())
410            .field("file_size", &self.file_size())
411            .field("mem_size", &self.mem_size())
412            .field("alignment", &self.alignment())
413            .finish()
414    }
415}
416
417#[allow(dead_code)]
418#[derive(Copy, Clone, Debug)]
419pub enum ElfSectionType {
420    /// Unused
421    Null,
422    /// Program data
423    ProgramBits,
424    /// Symbol table
425    SymbolTable,
426    /// String table
427    StringTable,
428    /// Relocation entries with addends
429    Rela,
430    /// Symbol hash table
431    Hash,
432    /// Dynamic linking information
433    Dynamic,
434    /// Note section
435    Note,
436    /// Uninitialized space
437    NoBits,
438    /// Relocation entries, no addends
439    Rel,
440    /// Reserved
441    Shlib,
442    /// Dynamic loader symbol table
443    DynamicSymbols,
444    /// Array of constructors
445    InitArray,
446    /// Array of destructors
447    FiniArray,
448    /// Array of pre-constructors
449    PreInitArray,
450    /// Section group
451    Group,
452    /// Extended section indices
453    ExtendedSymbolTableIndices,
454    /// Number of defined types
455    Num,
456    /// Start OS-specific
457    Other(u32),
458}
459
460impl ElfSectionType {
461    pub fn from_u32(ty: u32) -> Self {
462        match ty {
463            0 => Self::Null,
464            1 => Self::ProgramBits,
465            2 => Self::SymbolTable,
466            3 => Self::StringTable,
467            4 => Self::Rela,
468            5 => Self::Hash,
469            6 => Self::Dynamic,
470            7 => Self::Note,
471            8 => Self::NoBits,
472            9 => Self::Rel,
473            10 => Self::Shlib,
474            11 => Self::DynamicSymbols,
475            14 => Self::InitArray,
476            15 => Self::FiniArray,
477            16 => Self::PreInitArray,
478            17 => Self::Group,
479            18 => Self::ExtendedSymbolTableIndices,
480            19 => Self::Num,
481            0x60000000..=0x6fffffff => Self::Other(ty),
482            _ => Self::Null,
483        }
484    }
485}
486
487#[repr(C, packed)]
488#[derive(Copy, Clone, Debug)]
489pub struct ElfSection32 {
490    /// Section name (string tbl index)
491    name: u32,
492    /// Section type
493    ty: u32,
494    /// Section flags
495    flags: u32,
496    /// Address in memory image
497    address: u32,
498    /// Offset in file
499    offset: u32,
500    /// Size of section
501    size: u32,
502    /// Link to other section
503    link: u32,
504    /// Misc info
505    info: u32,
506    /// Alignment
507    alignment: u32,
508    /// Entry size if section holds table (such as symbol table)
509    entry_size: u32,
510}
511
512#[repr(C, packed)]
513#[derive(Copy, Clone, Debug)]
514pub struct ElfSection64 {
515    /// Section name (string tbl index)
516    name: u32,
517    /// Section type
518    ty: u32,
519    /// Section flags
520    flags: u64,
521    /// Address in memory image
522    address: u64,
523    /// Offset in file
524    offset: u64,
525    /// Size of section
526    size: u64,
527    /// Link to other section
528    link: u32,
529    /// Misc info
530    info: u32,
531    /// Alignment
532    alignment: u64,
533    /// Entry size if section holds table (such as symbol table)
534    entry_size: u64,
535}
536
537#[derive(Clone, Copy)]
538pub enum ElfSectionInner {
539    Section32(ElfSection32),
540    Section64(ElfSection64),
541}
542
543impl ElfSectionInner {
544    pub fn load(
545        file: &mut fs::File,
546        is_elf64: bool,
547        entry_size: u64,
548    ) -> Result<Self, ElfLoadError> {
549        if is_elf64 {
550            if entry_size != mem::size_of::<ElfSection64>() as u64 {
551                return Err(ElfLoadError::InvalidElfOrNotSupported);
552            }
553            let mut header_bytes = [0u8; mem::size_of::<ElfSection64>()];
554            if file.read(&mut header_bytes)? != header_bytes.len() as u64 {
555                return Err(ElfLoadError::UnexpectedEndOfFile);
556            }
557            let section = unsafe { *(header_bytes.as_ptr() as *const ElfSection64) };
558            Ok(Self::Section64(section))
559        } else {
560            if entry_size != mem::size_of::<ElfSection32>() as u64 {
561                return Err(ElfLoadError::InvalidElfOrNotSupported);
562            }
563            let mut header_bytes = [0u8; mem::size_of::<ElfSection32>()];
564            if file.read(&mut header_bytes)? != header_bytes.len() as u64 {
565                return Err(ElfLoadError::UnexpectedEndOfFile);
566            }
567            let section = unsafe { *(header_bytes.as_ptr() as *const ElfSection32) };
568            Ok(Self::Section32(section))
569        }
570    }
571
572    pub fn name_index(&self) -> u32 {
573        match self {
574            Self::Section32(s) => s.name,
575            Self::Section64(s) => s.name,
576        }
577    }
578
579    pub fn ty(&self) -> ElfSectionType {
580        let ty_u32 = match self {
581            Self::Section32(s) => s.ty,
582            Self::Section64(s) => s.ty,
583        };
584
585        ElfSectionType::from_u32(ty_u32)
586    }
587
588    pub fn flags(&self) -> u64 {
589        match self {
590            Self::Section32(s) => s.flags as u64,
591            Self::Section64(s) => s.flags,
592        }
593    }
594
595    pub fn offset(&self) -> u64 {
596        match self {
597            Self::Section32(s) => s.offset as u64,
598            Self::Section64(s) => s.offset,
599        }
600    }
601
602    pub fn address(&self) -> u64 {
603        match self {
604            Self::Section32(s) => s.address as u64,
605            Self::Section64(s) => s.address,
606        }
607    }
608
609    pub fn size(&self) -> u64 {
610        match self {
611            Self::Section32(s) => s.size as u64,
612            Self::Section64(s) => s.size,
613        }
614    }
615
616    pub fn link(&self) -> u64 {
617        match self {
618            Self::Section32(s) => s.link as u64,
619            Self::Section64(s) => s.link as u64,
620        }
621    }
622
623    pub fn info(&self) -> u64 {
624        match self {
625            Self::Section32(s) => s.info as u64,
626            Self::Section64(s) => s.info as u64,
627        }
628    }
629
630    pub fn alignment(&self) -> u64 {
631        match self {
632            Self::Section32(s) => s.alignment as u64,
633            Self::Section64(s) => s.alignment,
634        }
635    }
636
637    pub fn entry_size(&self) -> u64 {
638        match self {
639            Self::Section32(s) => s.entry_size as u64,
640            Self::Section64(s) => s.entry_size,
641        }
642    }
643}
644
645#[derive(Clone)]
646pub struct ElfSection {
647    name: String,
648    inner: ElfSectionInner,
649}
650
651impl ElfSection {
652    pub fn new(inner: ElfSectionInner, string_table: &[u8]) -> Self {
653        let name_index = inner.name_index();
654        let name = String::from(
655            CStr::from_bytes_until_nul(&string_table[name_index as usize..])
656                .unwrap()
657                .to_str()
658                .unwrap(),
659        );
660        Self { name, inner }
661    }
662
663    pub fn name(&self) -> &str {
664        &self.name
665    }
666}
667
668impl fmt::Debug for ElfSection {
669    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
670        f.debug_struct("ElfSection")
671            .field("name", &self.name)
672            .field("type", &self.inner.ty())
673            .field("flags", &self.inner.flags())
674            .field("offset", &self.inner.offset())
675            .field("address", &self.inner.address())
676            .field("size", &self.inner.size())
677            .field("link", &self.inner.link())
678            .field("info", &self.inner.info())
679            .field("alignment", &self.inner.alignment())
680            .field("entry_size", &self.inner.entry_size())
681            .finish()
682    }
683}
684
685impl Deref for ElfSection {
686    type Target = ElfSectionInner;
687
688    fn deref(&self) -> &Self::Target {
689        &self.inner
690    }
691}
692
693#[derive(Debug)]
694pub struct Elf {
695    header: ElfHeader,
696    program_headers: Vec<ElfProgram>,
697    sections: Vec<ElfSection>,
698}
699
700impl Elf {
701    pub fn load(file: &mut fs::File) -> Result<Self, ElfLoadError> {
702        // take the largest
703        let mut header = [0u8; mem::size_of::<ElfHeader>()];
704        if file.read(&mut header)? != header.len() as u64 {
705            return Err(ElfLoadError::UnexpectedEndOfFile);
706        }
707        let header = unsafe { *(header.as_ptr() as *const ElfHeader) };
708
709        if &header.base.magic != consts::ELF_MAGIC {
710            return Err(ElfLoadError::InvalidMagic);
711        }
712        if !header.is_valid_and_supported() {
713            return Err(ElfLoadError::InvalidElfOrNotSupported);
714        }
715        file.seek(header.program_header_offset())?;
716        let mut program_headers = Vec::with_capacity(header.program_header_entry_count() as usize);
717
718        for _ in 0..header.program_header_entry_count() {
719            let program =
720                ElfProgram::load(file, header.is_elf64(), header.program_header_entry_size())?;
721            program_headers.push(program);
722        }
723
724        let string_table_index = header.section_header_string_table_index() as usize;
725        assert!(string_table_index < header.section_header_entry_count() as usize);
726        let string_table_position = header.section_header_offset()
727            + header.section_header_entry_size() * string_table_index as u64;
728        file.seek(string_table_position)?;
729        let string_table_section =
730            ElfSectionInner::load(file, header.is_elf64(), header.section_header_entry_size())?;
731        let mut string_table = vec![0u8; string_table_section.size() as usize];
732        file.seek(string_table_section.offset())?;
733        file.read(&mut string_table)?;
734
735        file.seek(header.section_header_offset())?;
736        let mut sections = Vec::with_capacity(header.section_header_entry_count() as usize);
737        for _ in 0..header.section_header_entry_count() {
738            let section_inner =
739                ElfSectionInner::load(file, header.is_elf64(), header.section_header_entry_size())?;
740            let section = ElfSection::new(section_inner, &string_table);
741            sections.push(section);
742        }
743
744        Ok(Self {
745            header,
746            program_headers,
747            sections,
748        })
749    }
750
751    pub fn entry_point(&self) -> u64 {
752        self.header.entry()
753    }
754
755    pub fn program_headers(&self) -> &[ElfProgram] {
756        &self.program_headers
757    }
758
759    pub fn sections(&self) -> &[ElfSection] {
760        &self.sections
761    }
762}