Add ET_DYN support for the ELF loader, which required adding

relocations, add some more functions to libxunil, use include inside of
tests instead of manually defining types and add basic libc header
files. Move libxunil to the user folder, and add helloworld and
doomgeneric as apps
This commit is contained in:
csd4ni3l
2026-04-01 21:25:34 +02:00
parent 47a480009f
commit 6ac13a876f
23 changed files with 437 additions and 37 deletions

View File

@@ -101,6 +101,39 @@ pub const AT_UID: u64 = 11;
pub const AT_GID: u64 = 13;
pub const AT_NULL: u64 = 0;
// Dynamic structure types
pub const DT_NULL: i64 = 0;
pub const DT_NEEDED: i64 = 1;
pub const DT_PLTRELSZ: i64 = 2;
pub const DT_PLTGOT: i64 = 3;
pub const DT_HASH: i64 = 4;
pub const DT_STRTAB: i64 = 5;
pub const DT_SYMTAB: i64 = 6;
pub const DT_RELA: i64 = 7;
pub const DT_RELASZ: i64 = 8;
pub const DT_RELAENT: i64 = 9;
pub const DT_STRSZ: i64 = 10;
pub const DT_SYMENT: i64 = 11;
pub const DT_INIT: i64 = 12;
pub const DT_FINI: i64 = 13;
pub const DT_SONAME: i64 = 14;
pub const DT_RPATH: i64 = 15;
pub const DT_SYMBOLIC: i64 = 16;
pub const DT_REL: i64 = 17;
pub const DT_RELSZ: i64 = 18;
pub const DT_RELENT: i64 = 19;
pub const DT_PLTREL: i64 = 20;
pub const DT_DEBUG: i64 = 21;
pub const DT_TEXTREL: i64 = 22;
pub const DT_JMPREL: i64 = 23;
pub const DT_BIND_NOW: i64 = 24;
pub const DT_RUNPATH: i64 = 29;
pub const DT_FLAGS: i64 = 30;
pub const DT_GNU_HASH: i64 = 0x6ffffef5;
pub const DT_VERSYM: i64 = 0x6ffffff0;
pub const DT_VERNEED: i64 = 0x6ffffffe;
pub const DT_VERNEEDNUM: i64 = 0x6fffffff;
#[repr(C)]
pub struct Elf64Ehdr {
pub e_ident: [u8; 16],
@@ -155,6 +188,13 @@ pub struct Elf64Sym {
pub st_size: u64,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Elf64Dyn {
pub d_tag: i64,
pub d_val: u64,
}
#[repr(C)]
pub struct Elf64Rela {
pub r_offset: u64,

View File

@@ -36,8 +36,8 @@ pub fn load_file(
}
return match elf_header.e_type {
ET_EXEC => unsafe { load_program(frame_allocator, mapper, elf_header, elf_bytes) },
ET_DYN => return null(), // TODO
ET_EXEC => unsafe { load_program(frame_allocator, mapper, elf_header, elf_bytes, false) },
ET_DYN => unsafe { load_program(frame_allocator, mapper, elf_header, elf_bytes, true) }, // TODO
ET_REL => return null(),
_ => return null(),
};

View File

@@ -1,3 +1,9 @@
use core::{
ffi::CStr,
ptr::{null, null_mut},
};
use alloc::vec::Vec;
use x86_64::{
VirtAddr,
structures::paging::{
@@ -8,7 +14,16 @@ use x86_64::{
use crate::{
arch::x86_64::paging::XunilFrameAllocator,
driver::{
elf::header::{Elf64Ehdr, Elf64Phdr, PF_X, PT_LOAD},
elf::{
header::{
DT_JMPREL, DT_NEEDED, DT_NULL, DT_PLTREL, DT_PLTRELSZ, DT_RELA, DT_RELAENT,
DT_RELASZ, DT_STRSZ, DT_STRTAB, DT_SYMENT, DT_SYMTAB, Elf64Dyn, Elf64Ehdr,
Elf64Phdr, Elf64Rela, Elf64Sym, PF_X, PT_DYNAMIC, PT_LOAD, R_X86_64_64,
R_X86_64_GLOB_DAT, R_X86_64_JUMP_SLOT, R_X86_64_NONE, R_X86_64_RELATIVE, SHN_UNDEF,
STB_WEAK,
},
reloc::elf_do_reloc,
},
syscall::memset,
},
util::{align_down, align_up},
@@ -20,24 +35,229 @@ pub unsafe fn elf_pheader(hdr: *const Elf64Ehdr) -> *const Elf64Phdr {
unsafe { (hdr as *const u8).add((*hdr).e_phoff as usize) as *const Elf64Phdr }
}
pub fn get_vaddr(phdr: *const Elf64Phdr, load_bias: u64) -> *mut u8 {
unsafe { ((*phdr).p_vaddr + load_bias) as *mut u8 }
}
pub unsafe fn load_program(
frame_allocator: &mut XunilFrameAllocator,
mapper: &mut OffsetPageTable,
hdr: *const Elf64Ehdr,
elf_bytes: &[u8],
pie: bool,
) -> *const u8 {
let phdr = unsafe { elf_pheader(hdr) };
let phnum = unsafe { (*hdr).e_phnum };
let mut program_headers: Vec<*const Elf64Phdr> = Vec::new();
let mut pt_dynamic_header: *const Elf64Phdr = core::ptr::null();
for i in 0..phnum {
let program_header = unsafe { phdr.add(i as usize) };
let p_type = unsafe { (*program_header).p_type };
if unsafe { (*program_header).p_type } == PT_LOAD {
load_segment_to_memory(frame_allocator, mapper, program_header, elf_bytes);
if p_type == PT_LOAD {
program_headers.push(program_header);
} else if p_type == PT_DYNAMIC {
pt_dynamic_header = program_header as *const Elf64Phdr;
}
}
return unsafe { (*hdr).e_entry as *const u8 };
if !pie {
for program_header in program_headers {
load_segment_to_memory(frame_allocator, mapper, program_header, elf_bytes, 0);
}
return unsafe { (*hdr).e_entry as *const u8 };
} else {
let base_address = 0x0000_0100_0000; // TODO: add per-process memory
let min_vaddr = align_down(
program_headers
.iter()
.map(|&phdr| get_vaddr(phdr, 0) as u64)
.min()
.unwrap(),
4096,
);
let load_bias = base_address - min_vaddr;
for program_header in program_headers {
load_segment_to_memory(
frame_allocator,
mapper,
program_header,
elf_bytes,
load_bias,
);
}
if pt_dynamic_header.is_null() {
return null();
}
parse_dyn(
hdr,
pt_dynamic_header,
unsafe { elf_bytes.as_ptr().add((*phdr).p_offset as usize) as *const Elf64Dyn },
load_bias,
);
return unsafe { ((*hdr).e_entry + load_bias) as *const u8 };
}
}
fn cstr_from_strtab(
strtab_ptr: *const u8,
strtab_size: u64,
off: u32,
) -> Option<&'static core::ffi::CStr> {
let off = off as u64;
if strtab_ptr.is_null() || off >= strtab_size {
return None;
}
let mut i = off;
while i < strtab_size {
let b = unsafe { *strtab_ptr.add(i as usize) };
if b == 0 {
let start = unsafe { strtab_ptr.add(off as usize) } as *const core::ffi::c_char;
return Some(unsafe { CStr::from_ptr(start as *const i8) });
}
i += 1;
}
None
}
pub fn dyn_get_symaddr(
strtab_ptr: *const u8,
strtab_size: u64,
symtab_ptr: *const Elf64Sym,
idx: u64,
load_bias: u64,
) -> Result<u64, ()> {
let sym = unsafe { &*symtab_ptr.add(idx as usize) };
if sym.st_shndx != SHN_UNDEF {
return Ok(load_bias + sym.st_value);
}
let name = cstr_from_strtab(strtab_ptr, strtab_size, idx as u32);
let bind = sym.st_info >> 4;
if bind == STB_WEAK { Ok(0) } else { Err(()) }
}
fn apply_relocations(
hdr: *const Elf64Ehdr,
rela_ptr: *mut Elf64Rela,
rela_table_size: u64,
symtab_ptr: *const Elf64Sym,
strtab_ptr: *const u8,
strtab_size: u64,
load_bias: u64,
) {
for i in 0..rela_table_size as usize / size_of::<Elf64Rela>() {
let rela_ptr = unsafe { rela_ptr.add(i) };
let ptr = unsafe { (load_bias + (*rela_ptr).r_offset) as *mut u64 };
let mut value: u64 = 0;
match unsafe { ((*rela_ptr).r_info & 0xffff_ffff) as u32 } {
x if x == R_X86_64_RELATIVE as u32 => unsafe {
value = (load_bias as i64 + (*rela_ptr).r_addend) as u64;
},
x if x == R_X86_64_64 as u32 => unsafe {
value = (dyn_get_symaddr(
strtab_ptr,
strtab_size,
symtab_ptr,
(*rela_ptr).r_info >> 32,
load_bias,
)
.unwrap() as i64
+ (*rela_ptr).r_addend) as u64;
},
x if [R_X86_64_GLOB_DAT, R_X86_64_JUMP_SLOT].contains(&x) => unsafe {
value = dyn_get_symaddr(
strtab_ptr,
strtab_size,
symtab_ptr,
(*rela_ptr).r_info >> 32,
load_bias,
)
.unwrap() as u64;
},
x if x == R_X86_64_NONE as u32 => {
continue; // explicitly do nothing
}
_ => {
continue;
}
}
unsafe { *ptr = value };
}
}
fn parse_dyn(
hdr: *const Elf64Ehdr,
phdr: *const Elf64Phdr,
dyn_hdr_original: *const Elf64Dyn,
load_bias: u64,
) {
let mut i: usize = 0;
let file_size: u64 = unsafe { (*phdr).p_filesz };
let mut rela_ptr: *mut Elf64Rela = null_mut();
let mut rela_table_size: u64 = 0;
let mut symtab_ptr: *const Elf64Sym = null();
let mut strtab_ptr: *const u8 = null();
let mut strtab_size: u64 = 0;
let max = file_size as usize / size_of::<Elf64Dyn>();
while unsafe { (*dyn_hdr_original.add(i)).d_tag != DT_NULL } && i < max {
let dyn_hdr = unsafe { *(dyn_hdr_original.add(i)) };
match dyn_hdr.d_tag {
DT_RELA => rela_ptr = (dyn_hdr.d_val + load_bias) as *mut Elf64Rela,
DT_RELASZ => {
rela_table_size = dyn_hdr.d_val;
}
DT_JMPREL => {
// TODO: plt relocations
}
DT_PLTREL => {
// TODO: plt relocations
}
DT_PLTRELSZ => {
// TODO: plt relocations
}
DT_NEEDED => {
// TODO: do dynamic loading
}
DT_SYMTAB => symtab_ptr = (dyn_hdr.d_val + load_bias) as *const Elf64Sym,
DT_STRTAB => strtab_ptr = (dyn_hdr.d_val + load_bias) as *const u8,
DT_STRSZ => {
strtab_size = dyn_hdr.d_val;
}
_ => (),
}
i += 1;
}
if rela_ptr.is_null() || symtab_ptr.is_null() || strtab_ptr.is_null() {
return;
}
apply_relocations(
hdr,
rela_ptr,
rela_table_size,
symtab_ptr,
strtab_ptr,
strtab_size,
load_bias,
);
}
pub fn load_segment_to_memory(
@@ -45,12 +265,13 @@ pub fn load_segment_to_memory(
mapper: &mut OffsetPageTable,
phdr: *const Elf64Phdr,
elf_bytes: &[u8],
load_bias: u64,
) {
let mem_size: u64 = unsafe { (*phdr).p_memsz };
let p_offset: u64 = unsafe { (*phdr).p_offset };
let file_size: u64 = unsafe { (*phdr).p_filesz };
let vaddr: *mut u8 = unsafe { (*phdr).p_vaddr as *mut u8 };
let vaddr: *mut u8 = get_vaddr(phdr, load_bias);
if p_offset > elf_bytes.len() as u64
|| file_size > elf_bytes.len() as u64

View File

@@ -11,25 +11,29 @@ pub unsafe fn elf_section(hdr: *const Elf64Ehdr, idx: usize) -> *const Elf64Shdr
unsafe { elf_sheader(hdr).add(idx) }
}
// pub unsafe fn elf_str_table(hdr: *const Elf64Ehdr) -> *const u8 {
// if unsafe { (*hdr).e_shstrndx == SHN_UNDEF } {
// return core::ptr::null();
// }
pub unsafe fn elf_str_table(hdr: *const Elf64Ehdr) -> *const u8 {
if unsafe { (*hdr).e_shstrndx == SHN_UNDEF } {
return core::ptr::null();
}
// let shdr = unsafe { elf_section(hdr, (*hdr).e_shstrndx as usize) };
let shdr = unsafe { elf_section(hdr, (*hdr).e_shstrndx as usize) };
// unsafe { (hdr as *const u8).add((*shdr).sh_offset as usize) }
// }
unsafe { (hdr as *const u8).add((*shdr).sh_offset as usize) }
}
// pub unsafe fn elf_lookup_string(hdr: *const Elf64Ehdr, offset: usize) -> *const u8 {
// let str_tab: *const u8 = unsafe { elf_str_table(hdr) };
fn elf_lookup_string<'a>(hdr: *const Elf64Ehdr, offset: usize) -> &'a str {
let strtab: *const u8 = unsafe { elf_str_table(hdr) };
let mut len = 0;
// if str_tab.is_null() {
// return core::ptr::null();
// }
let start = unsafe { strtab.add(offset) };
// return unsafe { str_tab.add(offset) };
// }
unsafe {
while *start.add(len) != 0 {
len += 1;
}
core::str::from_raw_parts(start, len)
}
}
pub fn elf_lookup_symbol(name: &CStr) -> *const u8 {
return core::ptr::null();

View File

@@ -39,6 +39,10 @@ pub unsafe fn free(ptr: *mut u8, size: usize, align: usize) {
}
}
pub unsafe fn memset(ptr: *mut u8, val: u8, count: usize) {
unsafe { core::ptr::write_bytes(ptr, val, count) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn syscall_dispatch(
num: usize,
@@ -65,9 +69,5 @@ pub unsafe extern "C" fn syscall_dispatch(
}
}
pub unsafe fn memset(ptr: *mut u8, val: u8, count: usize) {
unsafe { core::ptr::write_bytes(ptr, val, count) };
}
pub type Fd = i32;
pub type Off = i64;

View File

@@ -1,7 +1,8 @@
#![no_std]
#![no_main]
#![feature(abi_x86_interrupt)]
#![feature(str_from_raw_parts)]
#![allow(warnings)]
extern crate alloc;
use core::fmt::Write;