244 lines
7.6 KiB
Rust
244 lines
7.6 KiB
Rust
use core::mem::size_of;
|
|
|
|
use alloc::{string::String, vec, vec::Vec};
|
|
use bytemuck::{Pod, Zeroable};
|
|
use elf::{
|
|
io_traits::{InputStream, SeekFrom},
|
|
relocation::Rela,
|
|
symbol::Symbol as ElfSymbol,
|
|
};
|
|
use libk_mm::PageBox;
|
|
use libk_util::{hash_table::DefaultHashTable, io::Read, sync::LockMethod, OneTimeInit};
|
|
use yggdrasil_abi::{
|
|
error::Error,
|
|
io::{FileMode, OpenOptions},
|
|
path::Path,
|
|
};
|
|
|
|
use crate::{
|
|
task::{
|
|
binary::{
|
|
self,
|
|
elf::{ElfSegment, ElfSegmentType},
|
|
},
|
|
sync::Mutex,
|
|
},
|
|
vfs::{FileRef, IoContext},
|
|
};
|
|
|
|
#[derive(Clone, Copy, Debug, Zeroable, Pod)]
|
|
#[repr(C)]
|
|
pub struct Symbol {
|
|
pub st_name: u32,
|
|
pub st_info: u8,
|
|
pub st_other: u8,
|
|
pub st_shndx: u16,
|
|
pub st_value: u64,
|
|
pub st_size: u64,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
#[repr(C)]
|
|
pub struct ModuleInfo {
|
|
pub name: &'static str,
|
|
pub version: &'static str,
|
|
pub init: fn() -> Result<(), Error>,
|
|
pub unload: Option<fn() -> Result<(), Error>>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct LoadedModule {
|
|
pub image_data: PageBox<[u8]>,
|
|
// Ref into the image_data PageBox
|
|
pub info: &'static ModuleInfo,
|
|
}
|
|
|
|
pub fn load_kernel_symbol_table<P: AsRef<Path>>(
|
|
ioctx: &mut IoContext,
|
|
path: P,
|
|
) -> Result<(), Error> {
|
|
let symbol_file = match ioctx.open(None, path, OpenOptions::READ, FileMode::empty()) {
|
|
Ok(file) => file,
|
|
Err(Error::DoesNotExist) => return Ok(()),
|
|
Err(err) => return Err(err),
|
|
};
|
|
let mut string_buffer = PageBox::new_slice(0, 4096)?;
|
|
let mut map = DefaultHashTable::new();
|
|
|
|
loop {
|
|
let mut len = [0; size_of::<u32>()];
|
|
let mut value = [0; size_of::<u64>()];
|
|
|
|
if symbol_file.read(&mut len)? != len.len() {
|
|
break;
|
|
}
|
|
let len = u32::from_le_bytes(len) as usize;
|
|
symbol_file.read_exact(&mut string_buffer[..len])?;
|
|
let name = core::str::from_utf8(&string_buffer[..len]).unwrap();
|
|
symbol_file.read_exact(&mut value)?;
|
|
let value = u64::from_le_bytes(value).try_into().unwrap();
|
|
map.insert(name.into(), value);
|
|
}
|
|
|
|
KERNEL_SYMBOL_TABLE.init(map);
|
|
MODULE_SYMBOL_TABLE.init(Mutex::new(DefaultHashTable::new()));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn lookup_symbol(name: &str) -> Option<usize> {
|
|
let symtab = KERNEL_SYMBOL_TABLE.get();
|
|
if let Some(addr) = symtab.get(name).copied() {
|
|
return Some(addr);
|
|
}
|
|
let symtab = MODULE_SYMBOL_TABLE.get();
|
|
if let Some(addr) = symtab.lock().unwrap().get(name).copied() {
|
|
return Some(addr);
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
pub fn load(file: FileRef) -> Result<LoadedModule, Error> {
|
|
// TODO better dependency tracking than just throwing undefined references
|
|
let (mut elf, mut file) = binary::elf::open_elf_direct(file)?;
|
|
|
|
// Find out image size
|
|
let (vaddr_min, vaddr_max) = binary::elf::elf_virtual_range(&elf);
|
|
let mut image_data = PageBox::new_slice(0u8, vaddr_max - vaddr_min)?;
|
|
|
|
// Load the segments
|
|
for segment in elf
|
|
.segments()
|
|
.iter()
|
|
.filter_map(ElfSegment::from_phdr)
|
|
.filter(|seg| seg.ty == ElfSegmentType::Load)
|
|
{
|
|
let rel_offset = segment.vaddr as usize - vaddr_min;
|
|
let segment_data = &mut image_data[rel_offset..rel_offset + segment.full_size as usize];
|
|
|
|
if segment.data_size > 0 {
|
|
file.seek(SeekFrom::Start(segment.offset)).unwrap();
|
|
file.read_exact(&mut segment_data[..segment.data_size as usize])
|
|
.unwrap();
|
|
}
|
|
}
|
|
|
|
let mut info_struct_addr = None;
|
|
|
|
// Extract dynamic symbols
|
|
let (dynsym, dynstr) = elf.dynamic_symbol_table().unwrap().unwrap();
|
|
let mut dynamic_symbols = vec![];
|
|
for mut sym in dynsym {
|
|
let name = dynstr.get(sym.st_name as _).unwrap();
|
|
|
|
if sym.st_shndx == elf::abi::SHN_UNDEF && sym.st_bind() != elf::abi::STB_LOCAL {
|
|
let Some(value) = lookup_symbol(name) else {
|
|
log::warn!("Could not load module: undefined reference to {:?} (possibly compiled against a different libk?)", name);
|
|
return Err(Error::InvalidArgument);
|
|
};
|
|
sym.st_value = value as u64;
|
|
|
|
dynamic_symbols.push(sym);
|
|
} else {
|
|
// Adjust symbol value by image base
|
|
sym.st_value += image_data.as_ptr().addr() as u64;
|
|
|
|
if name == "MODULE" && sym.st_symtype() == elf::abi::STT_OBJECT {
|
|
if sym.st_size != size_of::<ModuleInfo>() as u64 {
|
|
todo!();
|
|
}
|
|
info_struct_addr = Some(sym.st_value as usize);
|
|
}
|
|
|
|
dynamic_symbols.push(sym);
|
|
}
|
|
}
|
|
|
|
// Apply relocations
|
|
let rela_sections: Vec<_> = elf
|
|
.section_headers()
|
|
.iter()
|
|
.filter(|c| c.sh_type == elf::abi::SHT_RELA)
|
|
.copied()
|
|
.collect();
|
|
|
|
for rela in rela_sections {
|
|
let rela = elf.section_data_as_relas(&rela).unwrap();
|
|
for rela in rela {
|
|
handle_relocation(&mut image_data, &rela, |idx| {
|
|
dynamic_symbols
|
|
.get(idx as usize)
|
|
.ok_or(Error::InvalidArgument)
|
|
})?;
|
|
}
|
|
}
|
|
|
|
// TODO Add module symbols to export list: make sure to only export properly namespaced items
|
|
// let module_syms = MODULE_SYMBOL_TABLE.get();
|
|
// for (name, sym) in dynamic_symbols.iter() {
|
|
// if sym.st_shndx == elf::abi::SHN_UNDEF || sym.st_vis() != elf::abi::STV_DEFAULT {
|
|
// continue;
|
|
// }
|
|
// if !name.starts_with("_ZN") {
|
|
// continue;
|
|
// }
|
|
// log::info!("Export {:?} -> {:#x}", name, sym.st_value);
|
|
// module_syms
|
|
// .lock()
|
|
// .unwrap()
|
|
// .insert(name.clone(), sym.st_value as usize);
|
|
// }
|
|
|
|
let info = info_struct_addr
|
|
.map(|addr| unsafe { core::mem::transmute(addr) })
|
|
.expect("TODO return error if module is missing MODULE info struct");
|
|
|
|
Ok(LoadedModule { image_data, info })
|
|
}
|
|
|
|
pub fn load_and_execute(file: FileRef) -> Result<(), Error> {
|
|
let module = load(file)?;
|
|
(module.info.init)()
|
|
}
|
|
|
|
fn handle_relocation<'a, F: FnOnce(u32) -> Result<&'a ElfSymbol, Error>>(
|
|
image_data: &mut PageBox<[u8]>,
|
|
rela: &Rela,
|
|
resolve_symbol: F,
|
|
) -> Result<(), Error> {
|
|
let value: i64 = match rela.r_type {
|
|
// Symbol + addend
|
|
#[cfg(target_arch = "x86_64")]
|
|
elf::abi::R_X86_64_64 => resolve_symbol(rela.r_sym)?.st_value as i64 + rela.r_addend,
|
|
elf::abi::R_AARCH64_ABS64 => resolve_symbol(rela.r_sym)?.st_value as i64 + rela.r_addend,
|
|
|
|
// Image base + addend
|
|
#[cfg(target_arch = "x86_64")]
|
|
elf::abi::R_X86_64_RELATIVE => image_data.as_ptr().addr() as i64 + rela.r_addend,
|
|
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
|
elf::abi::R_AARCH64_RELATIVE => image_data.as_ptr().addr() as i64 + rela.r_addend,
|
|
|
|
// Symbol value
|
|
#[cfg(target_arch = "x86_64")]
|
|
elf::abi::R_X86_64_JUMP_SLOT | elf::abi::R_X86_64_GLOB_DAT => {
|
|
resolve_symbol(rela.r_sym)?.st_value as i64
|
|
}
|
|
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
|
elf::abi::R_AARCH64_JUMP_SLOT | elf::abi::R_AARCH64_GLOB_DAT => {
|
|
resolve_symbol(rela.r_sym)?.st_value as i64
|
|
}
|
|
_ => todo!("Unhandled relocation: {}", rela.r_type),
|
|
};
|
|
|
|
image_data[rela.r_offset as usize..rela.r_offset as usize + 8]
|
|
.copy_from_slice(&value.to_ne_bytes());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// TODO roll own hashmap
|
|
static KERNEL_SYMBOL_TABLE: OneTimeInit<DefaultHashTable<String, usize, 128>> = OneTimeInit::new();
|
|
static MODULE_SYMBOL_TABLE: OneTimeInit<Mutex<DefaultHashTable<String, usize>>> =
|
|
OneTimeInit::new();
|