rusty_mos/process/
elf_loader.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
use core::{cmp::min, mem::size_of, ptr::null};

use crate::{
    consts::error::KError,
    memory::regions::{PAGE_SIZE, PTE_D, PTE_V},
    ROUNDDOWN,
};

/// Half a word (16 bits).
type Elf32Half = u16;
/// A word (32 bits).
type Elf32Word = u32;
/// The offset type (32 bits).
type Elf32Off = u32;
/// The address type (32 bits).
type Elf32Addr = u32;

/// The type used in the walker of the header.
pub type ElfMapperFn = fn(usize, usize, isize, u32, *const u8, usize) -> Result<(), KError>;

/// The magic number size for elf file.
const EI_NIDNET: usize = 16;

/// The ELF32 file header structure. The members are defined in the same order
/// as in the actual elf file.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Elf32Ehdr {
    /// Magic number and other information.
    ident: [u8; EI_NIDNET],
    /// The object file type.
    ftype: Elf32Half,
    /// The architecture.
    _machine: Elf32Half,
    /// The object file version.
    _version: Elf32Word,
    /// The **virtual** address for the entry point.
    pub entry: Elf32Addr,
    /// The offset of the program header table from the file's start.
    phoff: Elf32Off,
    /// The offset of the section header table from the file's start.
    _shoff: Elf32Off,
    /// The processor-specific flag.
    _flags: Elf32Word,
    /// The elf header size (in bytes).
    _ehsize: Elf32Half,
    /// The program header table entry size.
    phentsize: Elf32Half,
    /// The program header table entry count.
    phnum: Elf32Half,
    /// The section header table entry size.
    _shentsize: Elf32Half,
    /// The section header table entry count.
    _shnum: Elf32Half,
    /// The section header string table index.
    _shstrndx: Elf32Half,
}

impl Elf32Ehdr {
    /// Build the Elf32Ehdr object from the binary.
    ///
    /// # Return
    ///
    /// The raw-pointer of the binary start if the size and the magic number
    /// is valid. Otherwise, a `null` point will be returned.
    pub fn from(binary: *const u8, size: usize) -> *const Self {
        let ehdr = unsafe { *(binary as *const Self) };
        if size >= size_of::<Elf32Ehdr>()
            && ehdr.ftype == 2
            && ehdr.ident[0] == 0x7f
            && ehdr.ident[1] == b'E'
            && ehdr.ident[2] == b'L'
            && ehdr.ident[3] == b'F'
        {
            binary as *const Self
        } else {
            null()
        }
    }

    /// Walk all the program header entry, use the function `apply` passed.
    pub fn foreach(&self, apply: impl Fn(Elf32Off)) {
        let mut ph_off = self.phoff;
        for _ in 0..self.phnum {
            apply(ph_off);
            ph_off += self.phentsize as Elf32Off;
        }
    }
}

/// The program segment header structure. The members are defined in the same
/// order as in the actual elf file.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Elf32Phdr {
    /// The segment type.
    pub stype: Elf32Word,
    /// The segment offset.
    pub offset: Elf32Off,
    /// The **virtual** address of the segment.
    pub vaddr: Elf32Addr,
    /// The **physical** address of the segment.
    _paddr: Elf32Addr,
    /// The segment size in **file**.
    pub filesz: Elf32Word,
    /// The segment size in **memory**.
    pub memsz: Elf32Word,
    /// The segment flag.
    pub flags: Elf32Word,
    /// The segment alignment.
    _align: Elf32Word,
}

/// Mark the segment as executable.
pub const PF_X: u32 = 1 << 0;

/// Mark the segment as writable.
pub const PF_W: u32 = 1 << 1;

/// Mark the segment as readable.
pub const PF_R: u32 = 1 << 2;

/// Mark the segment as loadble and load-needed.
pub const PT_LOAD: u32 = 1;

/// Load an elf-format binary file in memory. This method will map all sections
/// to correct virtual address.
///
/// An KError will be transmitted if the `map_segment` failed.
///
/// # Safety
/// The raw ptr **SHALL** be readable in all loadable sections and the phdr
/// **SHALL** be valid.
pub fn elf_load_seg(
    ph: *const Elf32Phdr,
    bin: *const u8,
    map_page: ElfMapperFn,
    data: usize,
) -> Result<(), KError> {
    let ph_ = ph; // deceits
    let phdr = unsafe { *ph_ };
    let va = phdr.vaddr;
    let bin_size = phdr.filesz;
    let seg_size = phdr.memsz;

    // Load the perm. Place the dirty bit acording the section attribute.
    let mut perm = PTE_V;
    if phdr.flags & PF_W > 0 {
        perm |= PTE_D;
    }

    // Map the unaligned data at head.
    let offset = va - ROUNDDOWN!(va; PAGE_SIZE as u32);
    if offset != 0 {
        map_page(
            data,
            va as usize,
            offset as isize,
            perm,
            bin,
            min(bin_size, PAGE_SIZE as u32 - offset) as usize,
        )?;
    }

    let mut i = if offset != 0 {
        min(bin_size, PAGE_SIZE as u32 - offset) as usize
    } else {
        0
    };

    while i < bin_size as usize {
        map_page(
            data,
            va as usize + i,
            0,
            perm,
            bin.wrapping_add(i),
            min(bin_size as usize - i, PAGE_SIZE),
        )?;
        i += PAGE_SIZE;
    }

    // `bin_size` < `sgsize`
    while i < seg_size as usize {
        map_page(
            data,
            va as usize + i,
            0,
            perm,
            null(),
            min(seg_size as usize - i, PAGE_SIZE),
        )?;
        i += PAGE_SIZE;
    }

    Ok(())
}