Browse Source

Fix global symbols relocations

Instead of a single source of symbols, now linker keeps a list of DSO (former Library) objects
with their own symbols map. That helps to process R_X86_64_COPY relocations correctly.
For example, if 'a.out' executable with dependencies ['libstdc++.so', 'libc.so'] is being loaded
and 'a.out' uses 'stdout' symbol from 'libc.so', its relocation process goes as follows:
- linker processes relocation entry 'stdout' of type R_X86_64_GLOB_DAT from 'libc.so',
- it goes through object list ['a.out', 'libstdc++.so', 'libc.so'] to find first object
  that exports 'stdout' symbol. The symbol is in 'a.out' with the value e.g. '0x404070',
- linker sets 'stdout' symbol GOT entry in 'libc.so' to '0x404070',
....
- linker processes relocation entry 'stdout' of type R_X86_64_COPY from 'a.out',
- it goes through object list excluding 'a.out': ['libstdc++.so', 'libc.so']. The symbol is found in 'libc.so',
- linker copies the 'stdout' symbol content from 'libc.so' to memory at address '0x404070' (in 'a.out' object).

Objects are relocated in reverse order they were loaded. So in the example above, linker starts with relocating
'libc.so' and ends with 'a.out'. It is necessary e.g. when linking with 'libstdc++.so' - there are many
relocations which symbols are found in 'libstdc++.so', so they need to be resolved before their contents are
copied to 'a.out'. That also matches GNU ld.so behavior.
Mateusz Tabaka 4 years ago
parent
commit
a7480ea656
14 changed files with 776 additions and 912 deletions
  1. 2 0
      .gitignore
  2. 11 15
      src/header/dl-tls/mod.rs
  3. 6 32
      src/header/dlfcn/mod.rs
  4. 7 28
      src/ld_so/callbacks.rs
  5. 429 0
      src/ld_so/dso.rs
  6. 0 54
      src/ld_so/library.rs
  7. 222 672
      src/ld_so/linker.rs
  8. 8 2
      src/ld_so/mod.rs
  9. 9 41
      src/ld_so/start.rs
  10. 44 24
      src/ld_so/tcb.rs
  11. 0 1
      src/macros.rs
  12. 2 2
      src/sync/semaphore.rs
  13. 35 40
      tests/Makefile
  14. 1 1
      tests/futimens.c

+ 2 - 0
.gitignore

@@ -4,3 +4,5 @@ sysroot/
 **/target/
 .gdb_history
 *.patch
+*.swp
+*.swo

+ 11 - 15
src/header/dl-tls/mod.rs

@@ -17,21 +17,17 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
         (*ti).ti_offset
     );
     if let Some(tcb) = Tcb::current() {
-        if let Some(tls) = tcb.tls() {
-            if let Some(masters) = tcb.masters() {
-                if let Some(master) = masters.get((*ti).ti_module as usize) {
-                    let addr = tls
-                        .as_mut_ptr()
-                        .add(master.offset + (*ti).ti_offset as usize);
-                    trace!(
-                        "__tls_get_addr({:p}: {:#x}, {:#x}) = {:p}",
-                        ti,
-                        (*ti).ti_module,
-                        (*ti).ti_offset,
-                        addr
-                    );
-                    return addr as *mut c_void;
-                }
+        if let Some(masters) = tcb.masters() {
+            if let Some(master) = masters.get((*ti).ti_module as usize) {
+                let addr = tcb.tls_end.sub(master.offset).add((*ti).ti_offset as usize);
+                trace!(
+                    "__tls_get_addr({:p}: {:#x}, {:#x}) = {:p}",
+                    ti,
+                    (*ti).ti_module,
+                    (*ti).ti_offset,
+                    addr
+                );
+                return addr as *mut c_void;
             }
         }
     }

+ 6 - 32
src/header/dlfcn/mod.rs

@@ -50,13 +50,11 @@ pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut
     let tcb = match Tcb::current() {
         Some(tcb) => tcb,
         None => {
-            eprintln!("dlopen: tcb not found");
             ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
             return ptr::null_mut();
         }
     };
     if tcb.linker_ptr.is_null() {
-        eprintln!("dlopen: linker not found");
         ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
         return ptr::null_mut();
     }
@@ -67,28 +65,12 @@ pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut
 
     let id = match (cbs.load_library)(&mut linker, filename) {
         Err(err) => {
-            eprintln!("dlopen: failed to load {:?}", filename);
             ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
             return ptr::null_mut();
         }
         Ok(id) => id,
     };
 
-    if let Some(fname) = filename {
-        if let Err(err) = (cbs.link)(&mut linker, None, None, Some(id)) {
-            (cbs.unload)(&mut linker, id);
-            eprintln!("dlopen: failed to link '{}': {}", fname, err);
-            ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
-            return ptr::null_mut();
-        };
-
-        if let Err(err) = (cbs.run_init)(&mut linker, Some(id)) {
-            (cbs.unload)(&mut linker, id);
-            eprintln!("dlopen: failed to link '{}': {}", fname, err);
-            ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
-            return ptr::null_mut();
-        };
-    }
     id as *mut c_void
 }
 
@@ -104,14 +86,12 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m
     let tcb = match Tcb::current() {
         Some(tcb) => tcb,
         None => {
-            eprintln!("dlsym: tcb not found");
             ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
             return ptr::null_mut();
         }
     };
 
     if tcb.linker_ptr.is_null() {
-        eprintln!("dlsym: linker not found");
         ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
         return ptr::null_mut();
     }
@@ -119,12 +99,12 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m
     let linker = (&*tcb.linker_ptr).lock();
     let cbs_c = linker.cbs.clone();
     let cbs = cbs_c.borrow();
-    if let Some(global) = (cbs.get_sym)(&linker, symbol_str, Some(handle as usize)) {
-        global.as_ptr()
-    } else {
-        eprintln!("dlsym: symbol not found");
-        ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
-        ptr::null_mut()
+    match (cbs.get_sym)(&linker, handle as usize, symbol_str) {
+        Some(sym) => sym,
+        _ => {
+            ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
+            ptr::null_mut()
+        }
     }
 }
 
@@ -133,24 +113,18 @@ pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int {
     let tcb = match Tcb::current() {
         Some(tcb) => tcb,
         None => {
-            eprintln!("dlclose: tcb not found");
             ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
             return -1;
         }
     };
 
     if tcb.linker_ptr.is_null() {
-        eprintln!("dlclose: linker not found");
         ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
         return -1;
     };
     let mut linker = (&*tcb.linker_ptr).lock();
     let cbs_c = linker.cbs.clone();
     let cbs = cbs_c.borrow();
-    if let Err(err) = (cbs.run_fini)(&mut linker, Some(handle as usize)) {
-        ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
-        return -1;
-    };
     (cbs.unload)(&mut linker, handle as usize);
     0
 }

+ 7 - 28
src/ld_so/callbacks.rs

@@ -1,15 +1,12 @@
-use super::linker::{Linker, Symbol, DSO};
+use super::linker::Linker;
+use crate::platform::types::c_void;
 use alloc::boxed::Box;
 use goblin::error::Result;
 
 pub struct LinkerCallbacks {
     pub unload: Box<dyn Fn(&mut Linker, usize)>,
     pub load_library: Box<dyn Fn(&mut Linker, Option<&str>) -> Result<usize>>,
-    pub link:
-        Box<dyn Fn(&mut Linker, Option<&str>, Option<DSO>, Option<usize>) -> Result<Option<usize>>>,
-    pub get_sym: Box<dyn Fn(&Linker, &str, Option<usize>) -> Option<Symbol>>,
-    pub run_init: Box<dyn Fn(&Linker, Option<usize>) -> Result<()>>,
-    pub run_fini: Box<dyn Fn(&Linker, Option<usize>) -> Result<()>>,
+    pub get_sym: Box<dyn Fn(&Linker, usize, &str) -> Option<*mut c_void>>,
 }
 
 impl LinkerCallbacks {
@@ -17,37 +14,19 @@ impl LinkerCallbacks {
         LinkerCallbacks {
             unload: Box::new(unload),
             load_library: Box::new(load_library),
-            link: Box::new(link),
             get_sym: Box::new(get_sym),
-            run_init: Box::new(run_init),
-            run_fini: Box::new(run_fini),
         }
     }
 }
 
-fn unload(linker: &mut Linker, libspace: usize) {
-    linker.unload(libspace)
+fn unload(linker: &mut Linker, lib_id: usize) {
+    linker.unload(lib_id)
 }
 
 fn load_library(linker: &mut Linker, name: Option<&str>) -> Result<usize> {
     linker.load_library(name)
 }
 
-fn link(
-    linker: &mut Linker,
-    primary_opt: Option<&str>,
-    dso: Option<DSO>,
-    libspace: Option<usize>,
-) -> Result<Option<usize>> {
-    linker.link(primary_opt, dso, libspace)
-}
-
-fn get_sym(linker: &Linker, name: &str, libspace: Option<usize>) -> Option<Symbol> {
-    linker.get_sym(name, libspace)
-}
-fn run_init(linker: &Linker, libspace: Option<usize>) -> Result<()> {
-    linker.run_init(libspace)
-}
-fn run_fini(linker: &Linker, libspace: Option<usize>) -> Result<()> {
-    linker.run_fini(libspace)
+fn get_sym(linker: &Linker, lib_id: usize, name: &str) -> Option<*mut c_void> {
+    linker.get_sym(lib_id, name)
 }

+ 429 - 0
src/ld_so/dso.rs

@@ -0,0 +1,429 @@
+use super::{
+    debug::{RTLDDebug, _r_debug},
+    linker::Symbol,
+    tcb::{round_up, Master},
+};
+use crate::{
+    header::{errno::STR_ERROR, sys_mman},
+    platform::{errno, types::c_void},
+};
+use alloc::{
+    collections::BTreeMap,
+    string::{String, ToString},
+    vec::Vec,
+};
+use core::{
+    mem::{size_of, transmute},
+    ptr, slice,
+};
+use goblin::{
+    elf::{
+        header::ET_DYN,
+        program_header,
+        r#dyn::{Dyn, DT_DEBUG, DT_RUNPATH},
+        section_header::{SHN_UNDEF, SHT_FINI_ARRAY, SHT_INIT_ARRAY},
+        sym, Elf,
+    },
+    error::{Error, Result},
+};
+
+/// Use to represent a library as well as all the symbols that is loaded withen it.
+#[derive(Default)]
+pub struct DSO {
+    pub name: String,
+    pub id: usize,
+    pub dlopened: bool,
+    pub entry_point: usize,
+    pub runpath: Option<String>,
+    /// Loaded library in-memory data
+    pub mmap: &'static mut [u8],
+    pub global_syms: BTreeMap<String, Symbol>,
+    pub weak_syms: BTreeMap<String, Symbol>,
+    pub dependencies: Vec<String>,
+    /// .init_array addr and len
+    pub init_array: (usize, usize),
+    /// .fini_array addr and len
+    pub fini_array: (usize, usize),
+    pub tls_module_id: usize,
+    pub tls_offset: usize,
+    pub use_count: usize,
+}
+
+impl DSO {
+    pub fn new(
+        path: &str,
+        data: &Vec<u8>,
+        base_addr: Option<usize>,
+        dlopened: bool,
+        id: usize,
+        tls_module_id: usize,
+        tls_offset: usize,
+    ) -> Result<(DSO, Option<Master>)> {
+        let elf = Elf::parse(data)?;
+        let (mmap, tcb_master) = DSO::mmap_and_copy(&path, &elf, &data, base_addr, tls_offset)?;
+        let (global_syms, weak_syms) = DSO::collect_syms(&elf, &mmap)?;
+        let (init_array, fini_array) = DSO::init_fini_arrays(&elf, mmap.as_ptr() as usize);
+
+        let name = match elf.soname {
+            Some(soname) => soname.to_string(),
+            _ => basename(&path),
+        };
+        let tls_offset = match tcb_master {
+            Some(ref master) => master.offset,
+            _ => 0,
+        };
+        let entry_point = if is_pie_enabled(&elf) {
+            mmap.as_ptr() as usize + elf.header.e_entry as usize
+        } else {
+            elf.header.e_entry as usize
+        };
+        let dso = DSO {
+            name: name,
+            id: id,
+            use_count: 1,
+            dlopened: dlopened,
+            entry_point: entry_point,
+            runpath: DSO::get_runpath(&path, &elf)?,
+            mmap: mmap,
+            global_syms: global_syms,
+            weak_syms: weak_syms,
+            dependencies: elf.libraries.iter().map(|s| s.to_string()).collect(),
+            init_array: init_array,
+            fini_array: fini_array,
+            tls_module_id: tls_module_id,
+            tls_offset: tls_offset,
+        };
+        return Ok((dso, tcb_master));
+    }
+
+    pub fn get_sym(&self, name: &str) -> Option<Symbol> {
+        if let Some(value) = self.global_syms.get(name) {
+            Some(*value)
+        } else if let Some(value) = self.weak_syms.get(name) {
+            Some(*value)
+        } else {
+            None
+        }
+    }
+
+    pub fn run_init(&self) {
+        unsafe {
+            let (addr, size) = self.init_array;
+            for i in (0..size).step_by(8) {
+                let func = transmute::<usize, *const Option<extern "C" fn()>>(addr + i);
+                (*func).map(|x| x());
+            }
+        }
+    }
+
+    pub fn run_fini(&self) {
+        unsafe {
+            let (addr, size) = self.fini_array;
+            for i in (0..size).step_by(8).rev() {
+                let func = transmute::<usize, *const Option<extern "C" fn()>>(addr + i);
+                (*func).map(|x| x());
+            }
+        }
+    }
+
+    fn get_runpath(path: &str, elf: &Elf) -> Result<Option<String>> {
+        if let Some(dynamic) = &elf.dynamic {
+            let entry = dynamic.dyns.iter().find(|d| d.d_tag == DT_RUNPATH);
+            match entry {
+                Some(entry) => {
+                    let runpath = elf
+                        .dynstrtab
+                        .get(entry.d_val as usize)
+                        .ok_or(Error::Malformed("Missing RUNPATH in dynstrtab".to_string()))??;
+                    let base = dirname(path);
+                    return Ok(Some(runpath.replace("$ORIGIN", &base)));
+                }
+                _ => return Ok(None),
+            }
+        }
+        return Ok(None);
+    }
+
+    fn mmap_and_copy(
+        path: &str,
+        elf: &Elf,
+        data: &Vec<u8>,
+        base_addr: Option<usize>,
+        tls_offset: usize,
+    ) -> Result<(&'static mut [u8], Option<Master>)> {
+        // data for struct LinkMap
+        let mut l_ld = 0;
+        // Calculate virtual memory bounds
+        let bounds = {
+            let mut bounds_opt: Option<(usize, usize)> = None;
+            for ph in elf.program_headers.iter() {
+                let voff = ph.p_vaddr % ph.p_align;
+                let vaddr = (ph.p_vaddr - voff) as usize;
+                let vsize = round_up((ph.p_memsz + voff) as usize, ph.p_align as usize);
+
+                match ph.p_type {
+                    program_header::PT_DYNAMIC => {
+                        l_ld = ph.p_vaddr;
+                    }
+                    program_header::PT_LOAD => {
+                        trace!("  load {:#x}, {:#x}: {:x?}", vaddr, vsize, ph);
+                        if let Some(ref mut bounds) = bounds_opt {
+                            if vaddr < bounds.0 {
+                                bounds.0 = vaddr;
+                            }
+                            if vaddr + vsize > bounds.1 {
+                                bounds.1 = vaddr + vsize;
+                            }
+                        } else {
+                            bounds_opt = Some((vaddr, vaddr + vsize));
+                        }
+                    }
+                    _ => (),
+                }
+            }
+            bounds_opt.ok_or(Error::Malformed(
+                "Unable to find PT_LOAD section".to_string(),
+            ))?
+        };
+        trace!("  bounds {:#x}, {:#x}", bounds.0, bounds.1);
+        // Allocate memory
+        let mmap = unsafe {
+            if let Some(addr) = base_addr {
+                let size = if is_pie_enabled(&elf) {
+                    bounds.1
+                } else {
+                    bounds.1 - bounds.0
+                };
+                _r_debug.insert_first(addr as usize, path, addr + l_ld as usize);
+                slice::from_raw_parts_mut(addr as *mut u8, size)
+            } else {
+                let (start, end) = bounds;
+                let size = end - start;
+                let mut flags = sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE;
+                if start != 0 {
+                    flags |= sys_mman::MAP_FIXED_NOREPLACE;
+                }
+                trace!("mmap({:#x}, {:x}, {:x})", start, size, flags);
+                let ptr = sys_mman::mmap(
+                    start as *mut c_void,
+                    size,
+                    //TODO: Make it possible to not specify PROT_EXEC on Redox
+                    sys_mman::PROT_READ | sys_mman::PROT_WRITE,
+                    flags,
+                    -1,
+                    0,
+                );
+                if ptr as usize == !0
+                /* MAP_FAILED */
+                {
+                    return Err(Error::Malformed(format!(
+                        "failed to map {}. errno: {}",
+                        path, STR_ERROR[errno as usize]
+                    )));
+                }
+                if start as *mut c_void != ptr::null_mut() {
+                    assert_eq!(
+                        ptr, start as *mut c_void,
+                        "mmap must always map on the destination we requested"
+                    );
+                }
+                ptr::write_bytes(ptr as *mut u8, 0, size);
+                _r_debug.insert(ptr as usize, path, ptr as usize + l_ld as usize);
+                slice::from_raw_parts_mut(ptr as *mut u8, size)
+            }
+        };
+
+        let skip_load_segment_copy = base_addr.is_some();
+        let mut tcb_master = None;
+
+        // Copy data
+        for ph in elf.program_headers.iter() {
+            let voff = ph.p_vaddr % ph.p_align;
+            let vaddr = (ph.p_vaddr - voff) as usize;
+            let vsize = round_up((ph.p_memsz + voff) as usize, ph.p_align as usize);
+
+            match ph.p_type {
+                program_header::PT_LOAD => {
+                    if skip_load_segment_copy {
+                        continue;
+                    }
+                    let obj_data = {
+                        let range = ph.file_range();
+                        match data.get(range.clone()) {
+                            Some(some) => some,
+                            None => {
+                                return Err(Error::Malformed(format!(
+                                    "failed to read {:x?}",
+                                    range
+                                )))
+                            }
+                        }
+                    };
+
+                    let mmap_data = {
+                        let range = if is_pie_enabled(elf) {
+                            let addr = ph.p_vaddr as usize;
+                            addr..addr + obj_data.len()
+                        } else {
+                            let addr = ph.p_vaddr as usize - mmap.as_ptr() as usize;
+                            addr..addr + obj_data.len()
+                        };
+                        match mmap.get_mut(range.clone()) {
+                            Some(some) => some,
+                            None => {
+                                return Err(Error::Malformed(format!(
+                                    "failed to write {:x?}",
+                                    range
+                                )));
+                            }
+                        }
+                    };
+                    trace!(
+                        "  copy {:#x}, {:#x}: {:#x}, {:#x}",
+                        vaddr,
+                        vsize,
+                        voff,
+                        obj_data.len()
+                    );
+                    mmap_data.copy_from_slice(obj_data);
+                }
+                program_header::PT_TLS => {
+                    let ptr = unsafe {
+                        if is_pie_enabled(elf) {
+                            mmap.as_ptr().add(ph.p_vaddr as usize)
+                        } else {
+                            ph.p_vaddr as *const u8
+                        }
+                    };
+                    tcb_master = Some(Master {
+                        ptr: ptr,
+                        len: ph.p_filesz as usize,
+                        offset: tls_offset + vsize,
+                    });
+                    trace!("  tcb master {:x?}", tcb_master);
+                }
+                program_header::PT_DYNAMIC => {
+                    // overwrite DT_DEBUG if exist in DYNAMIC segment
+                    // first we identify the location of DYNAMIC segment
+                    let dyn_start = ph.p_vaddr as usize;
+                    let mut debug_start = None;
+                    // next we identify the location of DT_DEBUG in .dynamic section
+                    if let Some(dynamic) = elf.dynamic.as_ref() {
+                        let mut i = 0;
+                        for entry in &dynamic.dyns {
+                            if entry.d_tag == DT_DEBUG {
+                                debug_start = Some(i as usize);
+                                break;
+                            }
+                            i += 1;
+                        }
+                    }
+                    if let Some(i) = debug_start {
+                        let bytes: [u8; size_of::<Dyn>() / 2] =
+                            unsafe { transmute((&_r_debug) as *const RTLDDebug as usize) };
+                        let start = if is_pie_enabled(elf) {
+                            dyn_start + i * size_of::<Dyn>() + size_of::<Dyn>() / 2
+                        } else {
+                            dyn_start + i * size_of::<Dyn>() + size_of::<Dyn>() / 2
+                                - mmap.as_mut_ptr() as usize
+                        };
+                        mmap[start..start + size_of::<Dyn>() / 2].clone_from_slice(&bytes);
+                    }
+                }
+                _ => (),
+            }
+        }
+        return Ok((mmap, tcb_master));
+    }
+
+    fn collect_syms(
+        elf: &Elf,
+        mmap: &[u8],
+    ) -> Result<(BTreeMap<String, Symbol>, BTreeMap<String, Symbol>)> {
+        let mut globals = BTreeMap::new();
+        let mut weak_syms = BTreeMap::new();
+        for sym in elf.dynsyms.iter() {
+            let bind = sym.st_bind();
+            if sym.st_shndx == SHN_UNDEF as usize
+                || ![sym::STB_GLOBAL, sym::STB_WEAK].contains(&bind)
+            {
+                continue;
+            }
+            let name: String;
+            let value: Symbol;
+            if let Some(name_res) = elf.dynstrtab.get(sym.st_name) {
+                name = name_res?.to_string();
+                value = if is_pie_enabled(elf) {
+                    Symbol {
+                        base: mmap.as_ptr() as usize,
+                        value: sym.st_value as usize,
+                        size: sym.st_size as usize,
+                    }
+                } else {
+                    Symbol {
+                        base: 0,
+                        value: sym.st_value as usize,
+                        size: sym.st_size as usize,
+                    }
+                };
+            } else {
+                continue;
+            }
+            match sym.st_bind() {
+                sym::STB_GLOBAL => {
+                    trace!("  global {}: {:x?} = {:p}", &name, sym, value.as_ptr());
+                    globals.insert(name, value);
+                }
+                sym::STB_WEAK => {
+                    trace!("  weak {}: {:x?} = {:p}", &name, sym, value.as_ptr());
+                    weak_syms.insert(name, value);
+                }
+                _ => unreachable!(),
+            }
+        }
+        return Ok((globals, weak_syms));
+    }
+
+    fn init_fini_arrays(elf: &Elf, mmap_addr: usize) -> ((usize, usize), (usize, usize)) {
+        let mut init_array: (usize, usize) = (0, 0);
+        let mut fini_array: (usize, usize) = (0, 0);
+        for section in elf
+            .section_headers
+            .iter()
+            .filter(|s| s.sh_type == SHT_INIT_ARRAY || s.sh_type == SHT_FINI_ARRAY)
+        {
+            let addr = if is_pie_enabled(&elf) {
+                mmap_addr + section.vm_range().start
+            } else {
+                section.vm_range().start
+            };
+            if section.sh_type == SHT_INIT_ARRAY {
+                init_array = (addr, section.sh_size as usize);
+            } else {
+                fini_array = (addr, section.sh_size as usize);
+            }
+        }
+        return (init_array, fini_array);
+    }
+}
+
+impl Drop for DSO {
+    fn drop(&mut self) {
+        self.run_fini();
+        unsafe { sys_mman::munmap(self.mmap.as_mut_ptr() as *mut c_void, self.mmap.len()) };
+    }
+}
+
+pub fn is_pie_enabled(elf: &Elf) -> bool {
+    return elf.header.e_type == ET_DYN;
+}
+
+fn basename(path: &str) -> String {
+    return path.split("/").last().unwrap_or(path).to_string();
+}
+
+fn dirname(path: &str) -> String {
+    let mut parts: Vec<&str> = path.split("/").collect();
+    parts.truncate(parts.len() - 1);
+    return parts.join("/");
+}

+ 0 - 54
src/ld_so/library.rs

@@ -1,54 +0,0 @@
-use super::linker::Symbol;
-use alloc::{
-    boxed::Box,
-    collections::{BTreeMap, BTreeSet},
-    string::String,
-    vec::Vec,
-};
-
-#[derive(Default, Debug)]
-pub struct DepTree {
-    pub name: String,
-    pub deps: Vec<DepTree>,
-}
-
-impl DepTree {
-    pub fn new(name: String) -> DepTree {
-        DepTree {
-            name,
-            deps: Vec::new(),
-        }
-    }
-}
-
-/// Use to represnt a library as well as all th symbols that is loaded withen it.
-#[derive(Default)]
-pub struct Library {
-    /// Global symbols
-    pub globals: BTreeMap<String, Symbol>,
-    /// Weak symbols
-    pub weak_syms: BTreeMap<String, Symbol>,
-    /// Loaded library raw data
-    pub objects: BTreeMap<String, Box<[u8]>>,
-    /// Loaded library in-memory data
-    pub mmaps: BTreeMap<String, (usize, &'static mut [u8])>,
-    /// Each object will have its children called once with no repetition.
-    pub dep_tree: DepTree,
-    /// A set used to detect circular dependencies in the Linker::load function
-    pub cir_dep: BTreeSet<String>,
-    pub runpath: Option<String>,
-}
-impl Library {
-    pub fn new() -> Library {
-        Default::default()
-    }
-    pub fn get_sym(&self, name: &str) -> Option<Symbol> {
-        if let Some(value) = self.globals.get(name) {
-            Some(*value)
-        } else if let Some(value) = self.weak_syms.get(name) {
-            Some(*value)
-        } else {
-            None
-        }
-    }
-}

File diff suppressed because it is too large
+ 222 - 672
src/ld_so/linker.rs


+ 8 - 2
src/ld_so/mod.rs

@@ -6,10 +6,16 @@ use crate::{header::sys_auxv::AT_NULL, start::Stack};
 
 pub const PAGE_SIZE: usize = 4096;
 
+#[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 library;
+mod dso;
 pub mod linker;
 pub mod start;
 pub mod tcb;
@@ -73,7 +79,7 @@ pub fn static_init(sp: &'static Stack) {
                 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 = vsize - valign;
+                    STATIC_TCB_MASTER.offset = valign;
 
                     let tcb = Tcb::new(vsize).expect("failed to allocate TCB");
                     tcb.masters_ptr = &mut STATIC_TCB_MASTER;

+ 9 - 41
src/ld_so/start.rs

@@ -17,13 +17,9 @@ use crate::{
     ALLOCATOR,
 };
 
-use super::{
-    access::accessible,
-    debug::_r_debug,
-    linker::{Linker, DSO, PATH_SEP},
-    tcb::Tcb,
-};
+use super::{access::accessible, debug::_r_debug, linker::Linker, tcb::Tcb, PATH_SEP};
 use crate::header::sys_auxv::{AT_ENTRY, AT_PHDR};
+use goblin::elf::header::header64::SIZEOF_EHDR;
 
 unsafe fn get_argv(mut ptr: *const usize) -> (Vec<String>, *const usize) {
     //traverse the stack and collect argument vector
@@ -214,53 +210,25 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
     // if we are not running in manual mode, then the main
     // program is already loaded by the kernel and we want
     // to use it. on redox, we treat it the same.
-    let program = {
-        let mut pr = None;
+    let base_addr = {
+        let mut base = None;
         if !is_manual && cfg!(not(target_os = "redox")) {
             let phdr = *auxv.get(&AT_PHDR).unwrap();
             if phdr != 0 {
-                let p = DSO {
-                    name: path.to_owned(),
-                    entry_point: *auxv.get(&AT_ENTRY).unwrap(),
-                    // The 0x40 is the size of Elf header not a good idea for different bit size
-                    // compatiablility but it will always work on 64 bit systems,
-                    base_addr: phdr - 0x40,
-                };
-                pr = Some(p);
+                base = Some(phdr - SIZEOF_EHDR);
             }
         }
-        pr
+        base
     };
-    let mut linker = Linker::new(ld_library_path, false);
-    match linker.load(&path, &path) {
-        Ok(()) => (),
-        Err(err) => {
-            eprintln!("ld.so: failed to load '{}': {}", path, err);
-            unistd::_exit(1);
-            loop {}
-        }
-    }
-
-    let entry = match linker.link(Some(&path), program, None) {
-        Ok(ok) => match ok {
-            Some(some) => some,
-            None => {
-                eprintln!("ld.so: failed to link '{}': missing entry", path);
-                unistd::_exit(1);
-                loop {}
-            }
-        },
+    let mut linker = Linker::new(ld_library_path);
+    let entry = match linker.load_program(&path, base_addr) {
+        Ok(entry) => entry,
         Err(err) => {
             eprintln!("ld.so: failed to link '{}': {}", path, err);
             unistd::_exit(1);
             loop {}
         }
     };
-    if let Err(e) = linker.run_init(None) {
-        eprintln!("ld.so: failed to run .init_array");
-        unistd::_exit(1);
-        loop {}
-    }
     if let Some(tcb) = unsafe { Tcb::current() } {
         tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker)));
         tcb.mspace = ALLOCATOR.get_book_keeper();

+ 44 - 24
src/ld_so/tcb.rs

@@ -1,5 +1,5 @@
-use alloc::boxed::Box;
-use core::{mem, ops::Range, ptr, slice};
+use alloc::vec::Vec;
+use core::{mem, ptr, slice};
 use goblin::error::{Error, Result};
 
 use crate::{header::sys_mman, ld_so::linker::Linker, sync::mutex::Mutex};
@@ -7,6 +7,7 @@ use crate::{header::sys_mman, ld_so::linker::Linker, sync::mutex::Mutex};
 use super::PAGE_SIZE;
 
 #[repr(C)]
+#[derive(Debug)]
 pub struct Master {
     /// Pointer to initial data
     pub ptr: *const u8,
@@ -21,11 +22,6 @@ impl Master {
     pub unsafe fn data(&self) -> &'static [u8] {
         slice::from_raw_parts(self.ptr, self.len)
     }
-
-    /// The region of TLS that the master will initialize
-    pub fn range(&self) -> Range<usize> {
-        self.offset..self.offset + self.len
-    }
 }
 
 #[derive(Debug)]
@@ -43,6 +39,8 @@ pub struct Tcb {
     pub masters_ptr: *mut Master,
     /// Size of the masters list in bytes (multiple of mem::size_of::<Master>())
     pub masters_len: usize,
+    /// Index of last copied Master
+    pub last_master_copied: usize,
     /// Pointer to dynamic linker
     pub linker_ptr: *const Mutex<Linker>,
     /// pointer to rust memory allocator structure
@@ -52,10 +50,10 @@ pub struct Tcb {
 impl Tcb {
     /// Create a new TCB
     pub unsafe fn new(size: usize) -> Result<&'static mut Self> {
-        let (tls, tcb_page) = Self::os_new(size)?;
+        let (tls, tcb_page) = Self::os_new(round_up(size, PAGE_SIZE))?;
 
         let tcb_ptr = tcb_page.as_mut_ptr() as *mut Self;
-        // println!("New TCB: {:p}", tcb_ptr);
+        trace!("New TCB: {:p}", tcb_ptr);
         ptr::write(
             tcb_ptr,
             Self {
@@ -65,6 +63,7 @@ impl Tcb {
                 tcb_len: tcb_page.len(),
                 masters_ptr: ptr::null_mut(),
                 masters_len: 0,
+                last_master_copied: 0,
                 linker_ptr: ptr::null(),
                 mspace: 0,
             },
@@ -109,26 +108,34 @@ impl Tcb {
     }
 
     /// Copy data from masters
-    pub unsafe fn copy_masters(&self) -> Result<()> {
+    pub unsafe fn copy_masters(&mut self) -> Result<()> {
         //TODO: Complain if masters or tls exist without the other
         if let Some(tls) = self.tls() {
             if let Some(masters) = self.masters() {
-                for (i, master) in masters.iter().enumerate() {
-                    let range = master.range();
-                    let data = master.data();
+                for (i, master) in masters
+                    .iter()
+                    .skip(self.last_master_copied)
+                    .filter(|m| m.len > 0)
+                    .enumerate()
+                {
+                    let range =
+                        self.tls_len - master.offset..self.tls_len - master.offset + master.len;
                     if let Some(tls_data) = tls.get_mut(range) {
-                        // println!(
-                        //     "tls master {}: {:p}, {:#x}: {:p}, {:#x}",
-                        //     i,
-                        //     data.as_ptr(), data.len(),
-                        //     tls_data.as_mut_ptr(), tls_data.len()
-                        // );
-
+                        let data = master.data();
+                        trace!(
+                            "tls master {}: {:p}, {:#x}: {:p}, {:#x}",
+                            i,
+                            data.as_ptr(),
+                            data.len(),
+                            tls_data.as_mut_ptr(),
+                            tls_data.len()
+                        );
                         tls_data.copy_from_slice(data);
                     } else {
                         return Err(Error::Malformed(format!("failed to copy tls master {}", i)));
                     }
                 }
+                self.last_master_copied = masters.len();
             }
         }
 
@@ -136,10 +143,19 @@ impl Tcb {
     }
 
     /// The initial images for TLS
-    pub unsafe fn set_masters(&mut self, mut masters: Box<[Master]>) {
-        self.masters_ptr = masters.as_mut_ptr();
-        self.masters_len = masters.len() * mem::size_of::<Master>();
-        mem::forget(masters);
+    pub unsafe fn append_masters(&mut self, mut new_masters: Vec<Master>) {
+        if self.masters_ptr.is_null() {
+            self.masters_ptr = new_masters.as_mut_ptr();
+            self.masters_len = new_masters.len() * mem::size_of::<Master>();
+            mem::forget(new_masters);
+        } else {
+            let len = self.masters_len / mem::size_of::<Master>();
+            let mut masters = Vec::from_raw_parts(self.masters_ptr, len, len);
+            masters.extend(new_masters.into_iter());
+            self.masters_ptr = masters.as_mut_ptr();
+            self.masters_len = masters.len() * mem::size_of::<Master>();
+            mem::forget(masters);
+        }
     }
 
     /// Activate TLS
@@ -217,3 +233,7 @@ impl Tcb {
         //TODO: Consider setting FS offset to TCB pointer
     }
 }
+
+pub fn round_up(value: usize, alignment: usize) -> usize {
+    return (value + alignment - 1) & (!(alignment - 1));
+}

+ 0 - 1
src/macros.rs

@@ -78,7 +78,6 @@ macro_rules! trace {
     ($($arg:tt)*) => ({
         use $crate::{Pal, Sys};
         eprintln!($($arg)*);
-        Sys::fsync(2);
     });
 }
 

+ 2 - 2
src/sync/semaphore.rs

@@ -1,8 +1,8 @@
 // From https://www.remlab.net/op/futex-misc.shtml
 //TODO: improve implementation
 
-use crate::platform::{types::*, Pal, Sys};
 use super::AtomicLock;
+use crate::platform::{types::*, Pal, Sys};
 use core::sync::atomic::Ordering;
 
 pub struct Semaphore {
@@ -29,7 +29,7 @@ impl Semaphore {
                 value,
                 value - 1,
                 Ordering::Acquire,
-                Ordering::Relaxed
+                Ordering::Relaxed,
             ) {
                 Ok(ok) => return,
                 Err(err) => {

+ 35 - 40
tests/Makefile

@@ -12,6 +12,7 @@ EXPECT_NAMES=\
 	fcntl/create \
 	fcntl/fcntl \
 	fnmatch \
+	futimens \
 	libgen \
 	locale \
 	math \
@@ -22,18 +23,25 @@ EXPECT_NAMES=\
 	setjmp \
 	sigaction \
 	signal \
+	stdio/all \
+	stdio/buffer \
+	stdio/fgets \
 	stdio/fputs \
 	stdio/fread \
+	stdio/freopen \
 	stdio/fseek \
 	stdio/fwrite \
+	stdio/getc_unget \
 	stdio/mutex \
 	stdio/popen \
 	stdio/printf \
 	stdio/rename \
 	stdio/scanf \
+	stdio/setvbuf \
 	stdio/sprintf \
 	stdio/printf_space_pad \
 	stdio/ungetc_ftell \
+	stdio/ungetc_multiple \
 	stdio/fscanf_offby1 \
 	stdio/fscanf \
 	stdio/printf_neg_pad \
@@ -69,6 +77,7 @@ EXPECT_NAMES=\
 	sys_mman \
 	time/asctime \
 	time/gmtime \
+	time/localtime \
 	time/macros \
 	time/mktime \
 	time/strftime \
@@ -82,6 +91,8 @@ EXPECT_NAMES=\
 	unistd/fork \
 	unistd/fsync \
 	unistd/ftruncate \
+	unistd/getopt \
+	unistd/getopt_long \
 	unistd/pipe \
 	unistd/rmdir \
 	unistd/sleep \
@@ -98,34 +109,20 @@ EXPECT_NAMES=\
 	wchar/wcsrchr \
 	wchar/wcsstr \
 	wchar/wcstod \
+	wchar/wcstok \
 	wchar/wcstol \
 	wchar/wcscasecmp \
 	wchar/wcsncasecmp \
+	wctype/towlower \
+	wctype/towupper
 	# TODO: Fix these
 	# mkfifo
 	# netdb/netdb \
 
-# issues with linking stdin, stdout, stderr
-STATIC_ONLY_NAMES=\
-	futimens \
-	stdio/all \
-	stdio/buffer \
-	stdio/fgets \
-	stdio/freopen \
-	stdio/getc_unget \
-	stdio/setvbuf \
-	stdio/ungetc_multiple \
-	time/localtime \
-	wchar/wcstok \
-	wctype/towlower \
-	wctype/towupper \
 # need to call fini in ld_so's _start
-STATIC_ONLY_NAMES+=\
-	destructor \
-# issues with linking optarg, optind etc.
-STATIC_ONLY_NAMES+=\
-	unistd/getopt \
-	unistd/getopt_long \
+STATIC_ONLY_NAMES=\
+	destructor
+
 
 DYNAMIC_ONLY_NAMES=\
 	dlfcn
@@ -205,9 +202,15 @@ FLAGS=\
 	-I .
 
 STATIC_FLAGS=\
-	../sysroot/lib/libc.a \
 	-static
 
+DYNAMIC_FLAGS=\
+	-Wl,--enable-new-dtags \
+	-Wl,-export-dynamic
+
+../sysroot:
+	$(MAKE) -C .. sysroot
+
 NATIVE_RELIBC?=0
 ifeq ($(NATIVE_RELIBC),0)
 FLAGS+=\
@@ -218,32 +221,24 @@ FLAGS+=\
 	../sysroot/lib/crti.o \
 	../sysroot/lib/crtn.o
 
-../sysroot:
-	$(MAKE) -C .. sysroot
-
-bins_static/%: %.c ../sysroot
-	mkdir -p "$$(dirname "$@")"
-	$(CC) "$<" -o "$@" $(FLAGS) $(STATIC_FLAGS)
-
 SYSROOT_LIB=$(shell realpath ../sysroot/lib/)
 
-DYNAMIC_FLAGS=\
+STATIC_FLAGS+=\
+	$(SYSROOT_LIB)/libc.a
+
+DYNAMIC_FLAGS+=\
 	-Wl,-dynamic-linker=$(SYSROOT_LIB)/ld64.so.1 \
-	-Wl,--enable-new-dtags \
-	-Wl,-rpath=$(SYSROOT_LIB) \
-	-Wl,-export-dynamic \
+	-Wl,-rpath=$(SYSROOT_LIB):\$$ORIGIN \
 	-L $(SYSROOT_LIB) \
 	-lc
 
-bins_dynamic/%: %.c ../sysroot
-	mkdir -p "$$(dirname "$@")"
-	$(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS)
-else
-bins_static/%: %.c
+DEPS=../sysroot
+endif
+
+bins_static/%: %.c $(DEPS)
 	mkdir -p "$$(dirname "$@")"
 	$(CC) "$<" -o "$@" $(FLAGS) $(STATIC_FLAGS)
 
-bins_dynamic/%: %.c
+bins_dynamic/%: %.c $(DEPS)
 	mkdir -p "$$(dirname "$@")"
-	$(CC) "$<" -o "$@" $(FLAGS)
-endif
+	$(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS)

+ 1 - 1
tests/futimens.c

@@ -12,7 +12,7 @@ int main(int argc, char** argv) {
   char temp[] = "/tmp/stattest-XXXXXX";
   const char file[] = "/mkfifo_fifo";
   int len = sizeof(temp) + sizeof(int);
-  char* path = malloc(len * sizeof(char));
+  char* path = calloc(len, sizeof(char));
 
   if (path == NULL) {
     fprintf(stderr, "Could not allocate: %s\n", strerror(errno));

Some files were not shown because too many files changed in this diff