Files
RedBear-OS/src/ld_so/mod.rs
T
2024-08-04 13:49:10 +02:00

170 lines
4.7 KiB
Rust

use core::{mem, ptr};
use goblin::elf::program_header::{self, program_header32, program_header64, ProgramHeader};
use self::tcb::{Master, Tcb};
use crate::{
header::sys_auxv::AT_NULL,
platform::{Pal, Sys},
start::Stack,
};
#[cfg(target_os = "redox")]
pub const PATH_SEP: char = ';';
#[cfg(target_os = "linux")]
pub const PATH_SEP: char = ':';
mod access;
pub mod callbacks;
pub mod debug;
mod dso;
pub mod linker;
pub mod start;
pub mod tcb;
pub use generic_rt::{panic_notls, ExpectTlsFree};
static mut STATIC_TCB_MASTER: Master = Master {
ptr: ptr::null_mut(),
len: 0,
offset: 0,
};
#[inline(never)]
pub fn static_init(sp: &'static Stack) {
let mut phdr_opt = None;
let mut phent_opt = None;
let mut phnum_opt = None;
let mut auxv = sp.auxv();
loop {
let (kind, value) = unsafe { *auxv };
if kind == AT_NULL {
break;
}
match kind {
3 => phdr_opt = Some(value),
4 => phent_opt = Some(value),
5 => phnum_opt = Some(value),
_ => (),
}
auxv = unsafe { auxv.add(1) };
}
let phdr = phdr_opt.expect_notls("failed to find AT_PHDR");
let phent = phent_opt.expect_notls("failed to find AT_PHENT");
let phnum = phnum_opt.expect_notls("failed to find AT_PHNUM");
for i in 0..phnum {
let ph_addr = phdr + phent * i;
let ph: ProgramHeader = match phent {
program_header32::SIZEOF_PHDR => {
unsafe { *(ph_addr as *const program_header32::ProgramHeader) }.into()
}
program_header64::SIZEOF_PHDR => {
unsafe { *(ph_addr as *const program_header64::ProgramHeader) }.into()
}
_ => panic_notls(format_args!("unknown AT_PHENT size {}", phent)),
};
let page_size = Sys::getpagesize();
let voff = ph.p_vaddr as usize % page_size;
let vaddr = ph.p_vaddr as usize - voff;
let vsize = ((ph.p_memsz as usize + voff + page_size - 1) / page_size) * page_size;
match ph.p_type {
program_header::PT_TLS => {
let valign = if ph.p_align > 0 {
((ph.p_memsz + (ph.p_align - 1)) / ph.p_align) * ph.p_align
} else {
ph.p_memsz
} as usize;
unsafe {
STATIC_TCB_MASTER.ptr = ph.p_vaddr as usize as *const u8;
STATIC_TCB_MASTER.len = ph.p_filesz as usize;
STATIC_TCB_MASTER.offset = valign;
let tcb = Tcb::new(vsize).expect_notls("failed to allocate TCB");
tcb.masters_ptr = &mut STATIC_TCB_MASTER;
tcb.masters_len = mem::size_of::<Master>();
tcb.copy_masters()
.expect_notls("failed to copy TLS master data");
tcb.activate();
}
//TODO: Warning on multiple TLS sections?
return;
}
_ => (),
}
}
}
#[cfg(any(target_os = "linux", target_os = "redox"))]
pub unsafe fn init(sp: &'static Stack) {
let tp: usize;
#[cfg(target_os = "linux")]
{
const ARCH_GET_FS: usize = 0x1003;
let mut val = 0usize;
syscall!(ARCH_PRCTL, ARCH_GET_FS, &mut val as *mut usize);
tp = val;
}
#[cfg(all(target_os = "redox", target_arch = "aarch64"))]
{
core::arch::asm!(
"mrs {}, tpidr_el0",
out(reg) tp,
);
}
#[cfg(all(target_os = "redox", target_arch = "x86"))]
{
let mut env = syscall::EnvRegisters::default();
let file = syscall::open(
"/scheme/thisproc/current/regs/env",
syscall::O_CLOEXEC | syscall::O_RDONLY,
)
.expect_notls("failed to open handle for process registers");
let _ = syscall::read(file, &mut env).expect_notls("failed to read gsbase");
let _ = syscall::close(file);
tp = env.gsbase as usize;
}
#[cfg(all(target_os = "redox", target_arch = "x86_64"))]
{
let mut env = syscall::EnvRegisters::default();
let file = syscall::open(
"/scheme/thisproc/current/regs/env",
syscall::O_CLOEXEC | syscall::O_RDONLY,
)
.expect_notls("failed to open handle for process registers");
let _ = syscall::read(file, &mut env).expect_notls("failed to read fsbase");
let _ = syscall::close(file);
tp = env.fsbase as usize;
}
if tp == 0 {
static_init(sp);
}
}
pub unsafe fn fini() {
if let Some(tcb) = Tcb::current() {
if tcb.linker_ptr != ptr::null_mut() {
let linker = (&*tcb.linker_ptr).lock();
linker.fini();
}
}
}