Unverified Commit 86139ca1 authored by bors[bot]'s avatar bors[bot] Committed by GitHub
Browse files

Merge #36

36: Parse ELF lazily r=stlankes a=mkroening

Closes https://github.com/hermitcore/rusty-loader/issues/32

.

This reduces allocations using an example kernel from

```console
[LOADER] Allocating 0x268 bytes at 0x11ACC0, index 0
[LOADER] Allocating 0x8B8 bytes at 0x11AF40, index 640
[LOADER] Allocating 0xD0 bytes at 0x11B840, index 2944
[LOADER] Allocating 0xE0 bytes at 0x11B940, index 3200
```

to

```console
[LOADER] Allocating 0x268 bytes at 0x117E00, index 0
[LOADER] Allocating 0xD0 bytes at 0x118080, index 640
```
Co-authored-by: Martin Kröning's avatarMartin Kröning <mkroening@posteo.net>
parents f6aa1cb4 f245f164
Pipeline #567760 passed with stages
in 2 minutes and 3 seconds
......@@ -55,9 +55,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "goblin"
version = "0.4.1"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee05c709047abe5eb0571880d2887ac77299c5f0835f8e6b7f4d5404f8962921"
checksum = "32401e89c6446dcd28185931a01b1093726d0356820ac744023e6850689bf926"
dependencies = [
"log",
"plain",
......
......@@ -11,7 +11,7 @@ aarch64-qemu-stdout = [] # Output to special qemu address 0x3F20_1000 instead
[dependencies]
bitflags = "1.3"
goblin = { version = "0.4.1", default-features = false, features = ["elf64", "elf32", "endian_fd"] }
goblin = { version = "0.4", default-features = false, features = ["elf64", "elf32", "endian_fd"] }
[target.'cfg(target_arch = "x86_64")'.dependencies]
multiboot = "0.7"
......@@ -22,7 +22,7 @@ target_build_utils = "0.3"
# The release profile, used for `cargo build --release`.
[profile.dev]
opt-level = 1 # controls the `--opt-level` the compiler builds with
opt-level = 2 # controls the `--opt-level` the compiler builds with
debug = true # controls whether the compiler passes `-C debuginfo`
# a value of `true` is equivalent to `2`
rpath = false # controls whether the compiler passes `-C rpath`
......
use crate::arch::{self, get_memory, BOOT_INFO};
use core::{
convert::TryInto,
ptr::{copy_nonoverlapping, write_bytes},
};
use goblin::{
container::{Container, Ctx, Endian},
elf::{header, program_header, reloc, Dynamic, Elf, Header, ProgramHeader, RelocSection},
error::{Error as GoblinError, Result as GoblinResult},
};
fn parse_ctx(header: &Header) -> GoblinResult<Ctx> {
let is_lsb = header.e_ident[header::EI_DATA] == header::ELFDATA2LSB;
let endianness = Endian::from(is_lsb);
let class = header.e_ident[header::EI_CLASS];
if class != header::ELFCLASS64 && class != header::ELFCLASS32 {
return Err(GoblinError::Malformed(format!(
"Unknown values in ELF ident header: class: {} endianness: {}",
class,
header.e_ident[header::EI_DATA]
)));
}
let is_64 = class == header::ELFCLASS64;
let container = if is_64 {
Container::Big
} else {
Container::Little
};
let ctx = Ctx::new(container, endianness);
Ok(ctx)
}
pub fn parse(bytes: &[u8]) -> GoblinResult<Elf<'_>> {
let header = Elf::parse_header(bytes)?;
let mut elf = Elf::lazy_parse(header)?;
let ctx = parse_ctx(&header)?;
elf.program_headers =
ProgramHeader::parse(bytes, header.e_phoff as usize, header.e_phnum as usize, ctx)?;
elf.dynamic = Dynamic::parse(bytes, &elf.program_headers, ctx)?;
if let Some(dynamic) = &elf.dynamic {
let dyn_info = &dynamic.info;
elf.dynrelas = RelocSection::parse(bytes, dyn_info.rela, dyn_info.relasz, true, ctx)?;
}
Ok(elf)
}
pub fn check_kernel_elf_file(elf: &Elf<'_>) -> u64 {
if !elf.libraries.is_empty() {
panic!(
"Error: file depends on following libraries: {:?}",
elf.libraries
);
}
// Verify that this module is a HermitCore ELF executable.
assert!(elf.header.e_type == header::ET_DYN);
assert!(elf.header.e_machine == arch::ELF_ARCH);
loaderlog!("This is a supported HermitCore Application");
// Get all necessary information about the ELF executable.
let mut file_size: u64 = 0;
let mut mem_size: u64 = 0;
for program_header in &elf.program_headers {
if program_header.p_type == program_header::PT_LOAD {
file_size = program_header.p_vaddr + program_header.p_filesz;
mem_size = program_header.p_vaddr + program_header.p_memsz;
}
}
// Verify the information.
assert!(file_size > 0);
assert!(mem_size > 0);
loaderlog!("Found entry point: {:#x}", elf.entry);
loaderlog!("File Size: {} Bytes", file_size);
loaderlog!("Mem Size: {} Bytes", mem_size);
mem_size
}
pub unsafe fn load_kernel(elf: &Elf<'_>, elf_start: u64, mem_size: u64) -> (u64, u64) {
loaderlog!("start {:#x}, size {:#x}", elf_start, mem_size);
if !elf.libraries.is_empty() {
panic!(
"Error: file depends on following libraries: {:?}",
elf.libraries
);
}
// Verify that this module is a HermitCore ELF executable.
assert!(elf.header.e_type == header::ET_DYN);
assert!(elf.header.e_machine == arch::ELF_ARCH);
if elf.header.e_ident[7] != 0xFF {
loaderlog!("Unsupported OS ABI {:#x}", elf.header.e_ident[7]);
}
let address = get_memory(mem_size);
loaderlog!("Load HermitCore Application at {:#x}", address);
// load application
for program_header in &elf.program_headers {
if program_header.p_type == program_header::PT_LOAD {
let pos = program_header.p_vaddr;
copy_nonoverlapping(
(elf_start + program_header.p_offset) as *const u8,
(address + pos) as *mut u8,
program_header.p_filesz.try_into().unwrap(),
);
write_bytes(
(address + pos + program_header.p_filesz) as *mut u8,
0,
(program_header.p_memsz - program_header.p_filesz)
.try_into()
.unwrap(),
);
} else if program_header.p_type == program_header::PT_TLS {
BOOT_INFO.tls_start = address + program_header.p_vaddr as u64;
BOOT_INFO.tls_filesz = program_header.p_filesz as u64;
BOOT_INFO.tls_memsz = program_header.p_memsz as u64;
loaderlog!(
"Found TLS starts at {:#x} (size {} Bytes)",
BOOT_INFO.tls_start,
BOOT_INFO.tls_memsz
);
}
}
// relocate entries (strings, copy-data, etc.) without an addend
for rel in &elf.dynrels {
loaderlog!("Unsupported relocation type {}", rel.r_type);
}
extern "C" {
static kernel_end: u8;
}
// relocate entries (strings, copy-data, etc.) with an addend
for rela in &elf.dynrelas {
match rela.r_type {
#[cfg(target_arch = "x86_64")]
reloc::R_X86_64_RELATIVE => {
use crate::arch::x86_64::paging::{LargePageSize, PageSize};
let offset = (address + rela.r_offset) as *mut u64;
let new_addr =
align_up!(&kernel_end as *const u8 as usize, LargePageSize::SIZE) as u64;
*offset = (new_addr as i64 + rela.r_addend.unwrap_or(0)) as u64;
}
#[cfg(target_arch = "aarch64")]
reloc::R_AARCH64_RELATIVE => {
let offset = (address + rela.r_offset) as *mut u64;
*offset = (address as i64 + rela.r_addend.unwrap_or(0)) as u64;
}
_ => {
loaderlog!("Unsupported relocation type {}", rela.r_type);
}
}
}
(address, elf.entry + address)
}
......@@ -15,18 +15,12 @@
#![allow(clippy::missing_safety_doc)]
// EXTERNAL CRATES
#[cfg(target_arch = "x86_64")]
#[macro_use]
extern crate bitflags;
extern crate alloc;
#[cfg(target_arch = "x86_64")]
use crate::arch::x86_64::paging::{LargePageSize, PageSize};
use crate::arch::{get_memory, BOOT_INFO, ELF_ARCH};
use core::convert::TryInto;
use core::ptr::{self, copy_nonoverlapping, write_bytes};
use goblin::elf;
use goblin::elf::program_header::{PT_LOAD, PT_TLS};
use goblin::elf64::reloc::*;
#[macro_use]
extern crate bitflags;
// MODULES
#[macro_use]
......@@ -34,21 +28,22 @@ pub mod macros;
pub mod arch;
pub mod console;
pub mod kernel;
pub mod mm;
mod runtime_glue;
extern "C" {
#[allow(dead_code)]
static kernel_end: u8;
static bss_end: u8;
static mut bss_start: u8;
}
use core::ptr;
#[global_allocator]
static ALLOCATOR: mm::allocator::Allocator = mm::allocator::Allocator;
// FUNCTIONS
pub unsafe fn sections_init() {
extern "C" {
static bss_end: u8;
static mut bss_start: u8;
}
// Initialize .bss section
ptr::write_bytes(
&mut bss_start as *mut u8,
......@@ -56,116 +51,3 @@ pub unsafe fn sections_init() {
&bss_end as *const u8 as usize - &bss_start as *const u8 as usize,
);
}
pub unsafe fn load_kernel(elf: &elf::Elf<'_>, elf_start: u64, mem_size: u64) -> (u64, u64) {
loaderlog!("start {:#x}, size {:#x}", elf_start, mem_size);
if !elf.libraries.is_empty() {
panic!(
"Error: file depends on following libraries: {:?}",
elf.libraries
);
}
// Verify that this module is a HermitCore ELF executable.
assert!(elf.header.e_type == elf::header::ET_DYN);
assert!(elf.header.e_machine == ELF_ARCH);
if elf.header.e_ident[7] != 0xFF {
loaderlog!("Unsupported OS ABI {:#x}", elf.header.e_ident[7]);
}
let address = get_memory(mem_size);
loaderlog!("Load HermitCore Application at {:#x}", address);
// load application
for program_header in &elf.program_headers {
if program_header.p_type == PT_LOAD {
let pos = program_header.p_vaddr;
copy_nonoverlapping(
(elf_start + program_header.p_offset) as *const u8,
(address + pos) as *mut u8,
program_header.p_filesz.try_into().unwrap(),
);
write_bytes(
(address + pos + program_header.p_filesz) as *mut u8,
0,
(program_header.p_memsz - program_header.p_filesz)
.try_into()
.unwrap(),
);
} else if program_header.p_type == PT_TLS {
BOOT_INFO.tls_start = address + program_header.p_vaddr as u64;
BOOT_INFO.tls_filesz = program_header.p_filesz as u64;
BOOT_INFO.tls_memsz = program_header.p_memsz as u64;
loaderlog!(
"Found TLS starts at {:#x} (size {} Bytes)",
BOOT_INFO.tls_start,
BOOT_INFO.tls_memsz
);
}
}
// relocate entries (strings, copy-data, etc.) without an addend
for rel in &elf.dynrels {
loaderlog!("Unsupported relocation type {}", rel.r_type);
}
// relocate entries (strings, copy-data, etc.) with an addend
for rela in &elf.dynrelas {
match rela.r_type {
#[cfg(target_arch = "x86_64")]
R_X86_64_RELATIVE => {
let offset = (address + rela.r_offset) as *mut u64;
let new_addr =
align_up!(&kernel_end as *const u8 as usize, LargePageSize::SIZE) as u64;
*offset = (new_addr as i64 + rela.r_addend.unwrap_or(0)) as u64;
}
#[cfg(target_arch = "aarch64")]
R_AARCH64_RELATIVE => {
let offset = (address + rela.r_offset) as *mut u64;
*offset = (address as i64 + rela.r_addend.unwrap_or(0)) as u64;
}
_ => {
loaderlog!("Unsupported relocation type {}", rela.r_type);
}
}
}
(address, elf.entry + address)
}
pub fn check_kernel_elf_file(elf: &elf::Elf<'_>) -> u64 {
if !elf.libraries.is_empty() {
panic!(
"Error: file depends on following libraries: {:?}",
elf.libraries
);
}
// Verify that this module is a HermitCore ELF executable.
assert!(elf.header.e_type == elf::header::ET_DYN);
assert!(elf.header.e_machine == ELF_ARCH);
loaderlog!("This is a supported HermitCore Application");
// Get all necessary information about the ELF executable.
let mut file_size: u64 = 0;
let mut mem_size: u64 = 0;
for program_header in &elf.program_headers {
if program_header.p_type == PT_LOAD {
file_size = program_header.p_vaddr + program_header.p_filesz;
mem_size = program_header.p_vaddr + program_header.p_memsz;
}
}
// Verify the information.
assert!(file_size > 0);
assert!(mem_size > 0);
loaderlog!("Found entry point: {:#x}", elf.entry);
loaderlog!("File Size: {} Bytes", file_size);
loaderlog!("Mem Size: {} Bytes", mem_size);
mem_size
}
......@@ -12,7 +12,6 @@
#![warn(rust_2018_idioms)]
#![allow(clippy::missing_safety_doc)]
use goblin::elf;
use rusty_loader::arch;
use rusty_loader::*;
......@@ -35,13 +34,13 @@ pub unsafe extern "C" fn loader_main() -> ! {
);
let app = arch::find_kernel();
let elf = elf::Elf::parse(app).expect("Unable to parse ELF file");
let elf = kernel::parse(app).expect("Unable to parse ELF file");
assert_ne!(
elf.entry, 0,
"Goblin failed to find entry point of the kernel in the Elf header"
);
let mem_size = check_kernel_elf_file(&elf);
let (kernel_location, entry_point) = load_kernel(&elf, app.as_ptr() as u64, mem_size);
let mem_size = kernel::check_kernel_elf_file(&elf);
let (kernel_location, entry_point) = kernel::load_kernel(&elf, app.as_ptr() as u64, mem_size);
// boot kernel
arch::boot_kernel(kernel_location, mem_size, entry_point)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment