mirror of
https://github.com/sgmarz/osblog.git
synced 2024-11-24 02:16:19 +04:00
Added ELF loader in elf.rs
This commit is contained in:
parent
bb4f3bc0a6
commit
c74316adb5
@ -4,12 +4,14 @@
|
|||||||
// 26-April-2020
|
// 26-April-2020
|
||||||
// Stephen Marz
|
// Stephen Marz
|
||||||
|
|
||||||
|
use crate::{buffer::Buffer, cpu::memcpy};
|
||||||
|
use alloc::collections::VecDeque;
|
||||||
// Every ELF file starts with ELF "magic", which is a sequence of four bytes 0x7f followed by capital ELF, which is 0x45, 0x4c, and 0x46 respectively.
|
// Every ELF file starts with ELF "magic", which is a sequence of four bytes 0x7f followed by capital ELF, which is 0x45, 0x4c, and 0x46 respectively.
|
||||||
pub const MAGIC: u32 = 0x464c_457f;
|
pub const MAGIC: u32 = 0x464c_457f;
|
||||||
|
|
||||||
/// The ELF header contains information about placement and numbers of the important sections within our file.
|
/// The ELF header contains information about placement and numbers of the important sections within our file.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
pub magic: u32,
|
pub magic: u32,
|
||||||
pub bitsize: u8,
|
pub bitsize: u8,
|
||||||
@ -34,6 +36,7 @@ pub struct Header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct ProgramHeader {
|
pub struct ProgramHeader {
|
||||||
pub seg_type: u32,
|
pub seg_type: u32,
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
@ -58,3 +61,70 @@ pub const PH_SEG_TYPE_DYNAMIC: u32 = 2;
|
|||||||
pub const PH_SEG_TYPE_INTERP: u32 = 3;
|
pub const PH_SEG_TYPE_INTERP: u32 = 3;
|
||||||
pub const PH_SEG_TYPE_NOTE: u32 = 4;
|
pub const PH_SEG_TYPE_NOTE: u32 = 4;
|
||||||
|
|
||||||
|
pub struct Program {
|
||||||
|
pub header: ProgramHeader,
|
||||||
|
pub data: Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct File {
|
||||||
|
pub header: Header,
|
||||||
|
pub programs: VecDeque<Program>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
pub fn load(buffer: &Buffer) -> Option<Self> {
|
||||||
|
let elf_hdr;
|
||||||
|
unsafe {
|
||||||
|
// Load the ELF
|
||||||
|
elf_hdr =
|
||||||
|
(buffer.get() as *const Header).as_ref().unwrap();
|
||||||
|
}
|
||||||
|
// The ELF magic is 0x75, followed by ELF
|
||||||
|
if elf_hdr.magic != MAGIC {
|
||||||
|
println!("ELF magic didn't match.");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// We need to make sure we're built for RISC-V
|
||||||
|
if elf_hdr.machine != MACHINE_RISCV {
|
||||||
|
println!("ELF loaded is not RISC-V.");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// ELF has several types. However, we can only load
|
||||||
|
// executables.
|
||||||
|
if elf_hdr.obj_type != TYPE_EXEC {
|
||||||
|
println!("ELF is not an executable.");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let ph_tab = unsafe {buffer.get().add(elf_hdr.phoff) }
|
||||||
|
as *const ProgramHeader;
|
||||||
|
// There are phnum number of program headers. We need to go through
|
||||||
|
// each one and load it into memory, if necessary.
|
||||||
|
let mut ret = Self {
|
||||||
|
header: *elf_hdr,
|
||||||
|
programs: VecDeque::new()
|
||||||
|
};
|
||||||
|
for i in 0..elf_hdr.phnum as usize {
|
||||||
|
unsafe {
|
||||||
|
let ph = ph_tab.add(i).as_ref().unwrap();
|
||||||
|
// If the segment isn't marked as LOAD (loaded into memory),
|
||||||
|
// then there is no point to this. Most executables use a LOAD
|
||||||
|
// type for their program headers.
|
||||||
|
if ph.seg_type != PH_SEG_TYPE_LOAD {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// If there's nothing in this section, don't load it.
|
||||||
|
if ph.memsz == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut ph_buffer = Buffer::new(ph.memsz as u32);
|
||||||
|
|
||||||
|
memcpy(ph_buffer.get_mut(), buffer.get().add(ph.off), ph.memsz);
|
||||||
|
ret.programs.push_back(Program {
|
||||||
|
header: *ph,
|
||||||
|
data: ph_buffer
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -175,6 +175,7 @@ extern "C" fn kinit_hart(_hartid: usize) {
|
|||||||
|
|
||||||
pub mod assembly;
|
pub mod assembly;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
|
pub mod buffer;
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod elf;
|
pub mod elf;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
|
@ -6,8 +6,9 @@ use crate::{cpu::{build_satp,
|
|||||||
CpuMode,
|
CpuMode,
|
||||||
SatpMode,
|
SatpMode,
|
||||||
TrapFrame},
|
TrapFrame},
|
||||||
elf,
|
elf,
|
||||||
fs::{MinixFileSystem, BlockBuffer},
|
buffer::Buffer,
|
||||||
|
fs::MinixFileSystem,
|
||||||
page::{map, zalloc, EntryBits, Table, PAGE_SIZE},
|
page::{map, zalloc, EntryBits, Table, PAGE_SIZE},
|
||||||
process::{Process,
|
process::{Process,
|
||||||
ProcessData,
|
ProcessData,
|
||||||
@ -35,7 +36,7 @@ pub fn test_elf() {
|
|||||||
// The bytes to read would usually come from the inode, but we are in an
|
// The bytes to read would usually come from the inode, but we are in an
|
||||||
// interrupt context right now, so we cannot pause. Usually, this would
|
// interrupt context right now, so we cannot pause. Usually, this would
|
||||||
// be done by an exec system call.
|
// be done by an exec system call.
|
||||||
let mut buffer = BlockBuffer::new(ino.size);
|
let mut buffer = Buffer::new(ino.size);
|
||||||
// Read the file from the disk. I got the inode by mounting
|
// Read the file from the disk. I got the inode by mounting
|
||||||
// the harddrive as a loop on Linux and stat'ing the inode.
|
// the harddrive as a loop on Linux and stat'ing the inode.
|
||||||
|
|
||||||
@ -55,31 +56,15 @@ pub fn test_elf() {
|
|||||||
// Everything is "page" based since we're going to map pages to
|
// Everything is "page" based since we're going to map pages to
|
||||||
// user space. So, we need to know how many program pages we
|
// user space. So, we need to know how many program pages we
|
||||||
// need. Each page is 4096 bytes.
|
// need. Each page is 4096 bytes.
|
||||||
|
let elf_fl = elf::File::load(&buffer);
|
||||||
|
if elf_fl.is_none() {
|
||||||
|
println!("Error reading elf file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let elf_fl = elf_fl.unwrap();
|
||||||
|
|
||||||
let program_pages = (bytes_read as usize / PAGE_SIZE) + 1;
|
let program_pages = (bytes_read as usize / PAGE_SIZE) + 1;
|
||||||
let my_pid = unsafe { NEXT_PID + 1 };
|
let my_pid = unsafe { let p = NEXT_PID + 1; NEXT_PID += 1; p };
|
||||||
let elf_hdr;
|
|
||||||
unsafe {
|
|
||||||
NEXT_PID += 1;
|
|
||||||
// Load the ELF
|
|
||||||
elf_hdr =
|
|
||||||
(buffer.get() as *const elf::Header).as_ref().unwrap();
|
|
||||||
}
|
|
||||||
// The ELF magic is 0x75, followed by ELF
|
|
||||||
if elf_hdr.magic != elf::MAGIC {
|
|
||||||
println!("ELF magic didn't match.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We need to make sure we're built for RISC-V
|
|
||||||
if elf_hdr.machine != elf::MACHINE_RISCV {
|
|
||||||
println!("ELF loaded is not RISC-V.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// ELF has several types. However, we can only load
|
|
||||||
// executables.
|
|
||||||
if elf_hdr.obj_type != elf::TYPE_EXEC {
|
|
||||||
println!("ELF is not an executable.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let mut my_proc = Process { frame: zalloc(1) as *mut TrapFrame,
|
let mut my_proc = Process { frame: zalloc(1) as *mut TrapFrame,
|
||||||
stack: zalloc(STACK_PAGES),
|
stack: zalloc(STACK_PAGES),
|
||||||
pid: my_pid,
|
pid: my_pid,
|
||||||
@ -95,58 +80,43 @@ pub fn test_elf() {
|
|||||||
// .rodata, .data, and .bss sections, but not necessarily.
|
// .rodata, .data, and .bss sections, but not necessarily.
|
||||||
// What we do here is map the program headers into the process' page
|
// What we do here is map the program headers into the process' page
|
||||||
// table.
|
// table.
|
||||||
unsafe {
|
for p in elf_fl.programs.iter() {
|
||||||
// The program header table starts where the ELF header says it is
|
// The program header table starts where the ELF header says it is
|
||||||
// given by the field phoff (program header offset).
|
// given by the field phoff (program header offset).
|
||||||
let ph_tab = buffer.get().add(elf_hdr.phoff)
|
// Copy the buffer we got from the filesystem into the program
|
||||||
as *const elf::ProgramHeader;
|
// memory we're going to map to the user. The memsz field in the
|
||||||
// There are phnum number of program headers. We need to go through
|
// program header tells us how many bytes will need to be loaded.
|
||||||
// each one and load it into memory, if necessary.
|
// The ph.off is the offset to load this into.
|
||||||
for i in 0..elf_hdr.phnum as usize {
|
unsafe {
|
||||||
let ph = ph_tab.add(i).as_ref().unwrap();
|
|
||||||
// If the segment isn't marked as LOAD (loaded into memory),
|
|
||||||
// then there is no point to this. Most executables use a LOAD
|
|
||||||
// type for their program headers.
|
|
||||||
if ph.seg_type != elf::PH_SEG_TYPE_LOAD {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// If there's nothing in this section, don't load it.
|
|
||||||
if ph.memsz == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Copy the buffer we got from the filesystem into the program
|
|
||||||
// memory we're going to map to the user. The memsz field in the
|
|
||||||
// program header tells us how many bytes will need to be loaded.
|
|
||||||
// The ph.off is the offset to load this into.
|
|
||||||
memcpy(
|
memcpy(
|
||||||
program_mem.add(ph.off,),
|
program_mem.add(p.header.off),
|
||||||
buffer.get().add(ph.off,),
|
p.data.get(),
|
||||||
ph.memsz,
|
p.header.memsz,
|
||||||
);
|
);
|
||||||
// We start off with the user bit set.
|
}
|
||||||
let mut bits = EntryBits::User.val();
|
// We start off with the user bit set.
|
||||||
// This sucks, but we check each bit in the flags to see
|
let mut bits = EntryBits::User.val();
|
||||||
// if we need to add it to the PH permissions.
|
// This sucks, but we check each bit in the flags to see
|
||||||
if ph.flags & elf::PROG_EXECUTE != 0 {
|
// if we need to add it to the PH permissions.
|
||||||
bits |= EntryBits::Execute.val();
|
if p.header.flags & elf::PROG_EXECUTE != 0 {
|
||||||
}
|
bits |= EntryBits::Execute.val();
|
||||||
if ph.flags & elf::PROG_READ != 0 {
|
}
|
||||||
bits |= EntryBits::Read.val();
|
if p.header.flags & elf::PROG_READ != 0 {
|
||||||
}
|
bits |= EntryBits::Read.val();
|
||||||
if ph.flags & elf::PROG_WRITE != 0 {
|
}
|
||||||
bits |= EntryBits::Write.val();
|
if p.header.flags & elf::PROG_WRITE != 0 {
|
||||||
}
|
bits |= EntryBits::Write.val();
|
||||||
// Now we map the program counter. The virtual address
|
}
|
||||||
// is provided in the ELF program header.
|
// Now we map the program counter. The virtual address
|
||||||
let pages = (ph.memsz + PAGE_SIZE) / PAGE_SIZE;
|
// is provided in the ELF program header.
|
||||||
for i in 0..pages {
|
let pages = (p.header.memsz + PAGE_SIZE) / PAGE_SIZE;
|
||||||
let vaddr = ph.vaddr + i * PAGE_SIZE;
|
for i in 0..pages {
|
||||||
// The ELF specifies a paddr, but not when we
|
let vaddr = p.header.vaddr + i * PAGE_SIZE;
|
||||||
// use the vaddr!
|
// The ELF specifies a paddr, but not when we
|
||||||
let paddr = program_mem as usize + ph.off + i * PAGE_SIZE;
|
// use the vaddr!
|
||||||
// println!("DEBUG: Map 0x{:08x} to 0x{:08x} {:02x}", vaddr, paddr, bits);
|
let paddr = program_mem as usize + p.header.off + i * PAGE_SIZE;
|
||||||
map(table, vaddr, paddr, bits, 0);
|
// println!("DEBUG: Map 0x{:08x} to 0x{:08x} {:02x}", vaddr, paddr, bits);
|
||||||
}
|
map(table, vaddr, paddr, bits, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This will map all of the program pages. Notice that in linker.lds in
|
// This will map all of the program pages. Notice that in linker.lds in
|
||||||
@ -165,7 +135,7 @@ pub fn test_elf() {
|
|||||||
unsafe {
|
unsafe {
|
||||||
// The program counter is a virtual memory address and is loaded
|
// The program counter is a virtual memory address and is loaded
|
||||||
// into mepc when we execute mret.
|
// into mepc when we execute mret.
|
||||||
(*my_proc.frame).pc = elf_hdr.entry_addr;
|
(*my_proc.frame).pc = elf_fl.header.entry_addr;
|
||||||
// Stack pointer. The stack starts at the bottom and works its
|
// Stack pointer. The stack starts at the bottom and works its
|
||||||
// way up, so we have to set the stack pointer to the bottom.
|
// way up, so we have to set the stack pointer to the bottom.
|
||||||
(*my_proc.frame).regs[2] =
|
(*my_proc.frame).regs[2] =
|
||||||
|
Loading…
Reference in New Issue
Block a user