Browse Source

Add static TLS init on Linux

Jeremy Soller 5 years ago
parent
commit
6a069d1d9e
2 changed files with 92 additions and 0 deletions
  1. 89 0
      src/ld_so/mod.rs
  2. 3 0
      src/start.rs

+ 89 - 0
src/ld_so/mod.rs

@@ -1,5 +1,94 @@
+use goblin::elf::program_header::{self, program_header32, program_header64, ProgramHeader};
+
+use crate::start::Stack;
+use self::tcb::{Tcb, Master};
+
 pub const PAGE_SIZE: usize = 4096;
 
 pub mod linker;
 pub mod start;
 pub mod tcb;
+
+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 == 0 {
+            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("failed to find AT_PHDR");
+    let phent = phent_opt.expect("failed to find AT_PHENT");
+    let phnum = phnum_opt.expect("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!("unknown AT_PHENT size {}", phent),
+        };
+
+        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;
+
+                let tcb_master = Master {
+                    ptr: ph.p_vaddr as usize as *const u8,
+                    len: ph.p_filesz as usize,
+                    offset: vsize - valign,
+                };
+
+                unsafe {
+                    let tcb = Tcb::new(vsize).expect("failed to allocate TCB");
+                    tcb.set_masters(vec![tcb_master].into_boxed_slice());
+                    tcb.copy_masters().expect("failed to copy TLS master data");
+                    tcb.activate();
+                }
+
+                //TODO: Warning on multiple TLS sections?
+                return;
+            }
+            _ => (),
+        }
+    }
+}
+
+#[cfg(target_os = "linux")]
+pub unsafe fn init(sp: &'static Stack) {
+    let mut tp = 0usize;
+    const ARCH_GET_FS: usize = 0x1003;
+    syscall!(ARCH_PRCTL, ARCH_GET_FS, &mut tp as *mut usize);
+    if tp == 0 {
+        static_init(sp);
+    }
+}
+
+#[cfg(target_os = "redox")]
+pub unsafe fn init(_sp: &'static Stack) {}

+ 3 - 0
src/start.rs

@@ -5,6 +5,7 @@ use header::{stdio, stdlib};
 use platform;
 use platform::types::*;
 use platform::{Pal, Sys};
+use ld_so;
 
 #[repr(C)]
 pub struct Stack {
@@ -77,6 +78,8 @@ pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! {
     // Ensure correct host system before executing more system calls
     relibc_verify_host();
 
+    ld_so::init(sp);
+
     // Set up argc and argv
     let argc = sp.argc;
     let argv = sp.argv();