diff --git a/.gitignore b/.gitignore index 3d392c13630eaa6442c7ab7751a5876de6205d55..f0f3a36cd6bc2d2df8736d3badc9b3d3479b9711 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ sysroot/ **/target/ .gdb_history *.patch +*.swp +*.swo diff --git a/src/header/dl-tls/mod.rs b/src/header/dl-tls/mod.rs index 83db3fa3f39202be40ab5712f03c6724c1159313..cb5fec330282b5fcf795ca84bd111e8f44d84675 100644 --- a/src/header/dl-tls/mod.rs +++ b/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; } } } diff --git a/src/header/dlfcn/mod.rs b/src/header/dlfcn/mod.rs index 784a2b83c2b41bc72e7d2042176ee4729797340a..0f7ab20ec84d9f52b1bf70be838adbf8957de611 100644 --- a/src/header/dlfcn/mod.rs +++ b/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 } diff --git a/src/ld_so/callbacks.rs b/src/ld_so/callbacks.rs index f0e851cbc7623b064d6bdaab23f98081a22bbf55..1d7d7a05f499226e27820faf8bbb1cdc965a96e4 100644 --- a/src/ld_so/callbacks.rs +++ b/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) } diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs new file mode 100644 index 0000000000000000000000000000000000000000..3f053d091e126a718a40f84e22bac93a5882803a --- /dev/null +++ b/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("/"); +} diff --git a/src/ld_so/library.rs b/src/ld_so/library.rs deleted file mode 100644 index 19862f7e3d4281b908bf86903a54914b3cb7eb87..0000000000000000000000000000000000000000 --- a/src/ld_so/library.rs +++ /dev/null @@ -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 - } - } -} diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index bf3b65ba349cf55967aa1f8553f5a33f12a5c74c..21d35b1804a3b9b4548a0b789b84723626f8cd4f 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -1,52 +1,31 @@ use alloc::{ - boxed::Box, - collections::{BTreeMap, BTreeSet}, + collections::BTreeMap, rc::Rc, string::{String, ToString}, vec::Vec, }; -use core::{ - cell::RefCell, - mem::{size_of, swap, transmute}, - ptr, slice, -}; +use core::{cell::RefCell, mem::transmute, ptr}; use goblin::{ - elf::{ - header::ET_DYN, - program_header, - r#dyn::{Dyn, DT_DEBUG, DT_RUNPATH}, - reloc, sym, Elf, - }, + elf::{program_header, reloc, Elf}, error::{Error, Result}, }; use crate::{ c_str::CString, fs::File, - header::{errno::STR_ERROR, fcntl, sys_mman, unistd}, + header::{fcntl, sys_mman, unistd::F_OK}, io::Read, - platform::{errno, types::c_void}, + platform::types::c_void, }; use super::{ access::accessible, callbacks::LinkerCallbacks, - debug::{RTLDDebug, RTLDState, _dl_debug_state, _r_debug}, - library::{DepTree, Library}, - tcb::{Master, Tcb}, - PAGE_SIZE, + debug::{RTLDState, _dl_debug_state, _r_debug}, + dso::{is_pie_enabled, DSO}, + tcb::{round_up, Master, Tcb}, + PATH_SEP, }; -#[cfg(target_os = "redox")] -pub const PATH_SEP: char = ';'; - -#[cfg(target_os = "linux")] -pub const PATH_SEP: char = ':'; - -pub struct DSO { - pub name: String, - pub base_addr: usize, - pub entry_point: usize, -} #[derive(Clone, Copy, Debug)] pub struct Symbol { @@ -54,6 +33,7 @@ pub struct Symbol { pub base: usize, pub size: usize, } + impl Symbol { pub fn as_ptr(self) -> *mut c_void { (self.base + self.value) as *mut c_void @@ -61,684 +41,242 @@ impl Symbol { } pub struct Linker { - // Used by load - /// Library path to search when loading library by name - default_library_path: String, ld_library_path: Option<String>, - root: Library, - verbose: bool, - tls_index_offset: usize, - lib_spaces: BTreeMap<usize, Library>, - counter: usize, + next_object_id: usize, + next_tls_module_id: usize, + tls_size: usize, + objects: BTreeMap<usize, DSO>, + name_to_object_id_map: BTreeMap<String, usize>, pub cbs: Rc<RefCell<LinkerCallbacks>>, } const root_id: usize = 1; impl Linker { - pub fn new(ld_library_path: Option<String>, verbose: bool) -> Self { + pub fn new(ld_library_path: Option<String>) -> Self { Self { - default_library_path: "/lib".to_string(), ld_library_path: ld_library_path, - root: Library::new(), - verbose, - tls_index_offset: 0, - lib_spaces: BTreeMap::new(), - counter: root_id + 1, + next_object_id: root_id, + next_tls_module_id: 0, + tls_size: 0, + objects: BTreeMap::new(), + name_to_object_id_map: BTreeMap::new(), cbs: Rc::new(RefCell::new(LinkerCallbacks::new())), } } - pub fn load(&mut self, name: &str, path: &str) -> Result<()> { - let mut lib: Library = Library::new(); - swap(&mut lib, &mut self.root); - lib.dep_tree = self.load_recursive(name, path, &mut lib)?; - swap(&mut lib, &mut self.root); - if self.verbose { - println!("Dep tree: {:#?}", self.root.dep_tree); - } - return Ok(()); - } - pub fn unload(&mut self, libspace: usize) { - if let Some(lib) = self.lib_spaces.remove(&libspace) { - for (_, (_, mmap)) in lib.mmaps { - unsafe { sys_mman::munmap(mmap.as_mut_ptr() as *mut c_void, mmap.len()) }; - } - } - } - fn load_recursive(&mut self, name: &str, path: &str, lib: &mut Library) -> Result<DepTree> { - if self.verbose { - println!("load {}: {}", name, path); - } - if lib.cir_dep.contains(name) { - return Err(Error::Malformed(format!( - "Circular dependency: {} is a dependency of itself", - name - ))); - } - - let mut deps = DepTree::new(name.to_string()); - let mut data = Vec::new(); - lib.cir_dep.insert(name.to_string()); - let path_c = CString::new(path) - .map_err(|err| Error::Malformed(format!("invalid path '{}': {}", path, err)))?; - - { - let flags = fcntl::O_RDONLY | fcntl::O_CLOEXEC; - let mut file = File::open(&path_c, flags) - .map_err(|err| Error::Malformed(format!("failed to open '{}': {}", path, err)))?; - file.read_to_end(&mut data) - .map_err(|err| Error::Malformed(format!("failed to read '{}': {}", path, err)))?; - } - deps.deps = self.load_data(name, data.into_boxed_slice(), lib)?; - lib.cir_dep.remove(name); - Ok(deps) - } - - fn load_data( - &mut self, - name: &str, - data: Box<[u8]>, - lib: &mut Library, - ) -> Result<Vec<DepTree>> { - let elf = Elf::parse(&data)?; - //println!("{:#?}", elf); - - // search for RUNPATH - lib.runpath = if let Some(dynamic) = elf.dynamic { - let entry = dynamic.dyns.iter().find(|d| d.d_tag == DT_RUNPATH); - match entry { - Some(entry) => { - let path = elf - .dynstrtab - .get(entry.d_val as usize) - .ok_or(Error::Malformed("Missing RUNPATH in dynstrtab".to_string()))??; - Some(path.to_string()) - } - _ => None, - } - } else { - None - }; - - let mut deps = Vec::new(); - for library in elf.libraries.iter() { - if let Some(dep) = self._load_library(library, lib)? { - deps.push(dep); - } - } - let key = match elf.soname { - Some(soname) => soname, - _ => name, - }; - if !lib.objects.contains_key(key) { - lib.objects.insert(key.to_string(), data); - } - return Ok(deps); + pub fn load_program(&mut self, path: &str, base_addr: Option<usize>) -> Result<usize> { + self.load_object(path, &None, base_addr, false)?; + return Ok(self.objects.get(&root_id).unwrap().entry_point); } pub fn load_library(&mut self, name: Option<&str>) -> Result<usize> { match name { Some(name) => { - let mut lib = Library::new(); - self._load_library(name, &mut lib)?; - let ret = self.counter; - self.lib_spaces.insert(ret, lib); - self.counter += 1; - return Ok(ret); - } - None => return Ok(root_id), - } - } - fn _load_library(&mut self, name: &str, lib: &mut Library) -> Result<Option<DepTree>> { - if lib.objects.contains_key(name) || self.root.objects.contains_key(name) { - // It should be previously resolved so we don't need to worry about it - Ok(None) - } else if name.contains('/') { - Ok(Some(self.load_recursive(name, name, lib)?)) - } else { - let mut paths = Vec::new(); - if let Some(ld_library_path) = &self.ld_library_path { - paths.push(ld_library_path); - } - if let Some(runpath) = &lib.runpath { - paths.push(runpath); - } - paths.push(&self.default_library_path); - for part in paths.iter() { - let path = if part.is_empty() { - format!("./{}", name) + if let Some(id) = self.name_to_object_id_map.get(name) { + let obj = self.objects.get_mut(id).unwrap(); + obj.use_count += 1; + return Ok(*id); } else { - format!("{}/{}", part, name) - }; - if self.verbose { - println!("check {}", path); - } + let parent_runpath = &self.objects.get(&root_id).unwrap().runpath.clone(); + let lib_id = self.next_object_id; + self.load_object(name, parent_runpath, None, true)?; - if accessible(&path, unistd::F_OK) == 0 { - return Ok(Some(self.load_recursive(name, &path, lib)?)); + return Ok(lib_id); } } + None => return Ok(root_id), + } + } - Err(Error::Malformed(format!("failed to locate '{}'", name))) + pub fn get_sym(&self, lib_id: usize, name: &str) -> Option<*mut c_void> { + match self.objects.get(&lib_id) { + Some(obj) => obj.get_sym(name).map(|s| s.as_ptr()), + _ => None, } } - fn collect_syms( - elf: &Elf, - mmap: &[u8], - verbose: bool, - ) -> 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_value == 0 || ![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, + pub fn unload(&mut self, lib_id: usize) { + if let Some(obj) = self.objects.get_mut(&lib_id) { + if obj.dlopened { + if obj.use_count == 1 { + let obj = self.objects.remove(&lib_id).unwrap(); + for dep in obj.dependencies.iter() { + self.unload(*self.name_to_object_id_map.get(dep).unwrap()); } + self.name_to_object_id_map.remove(&obj.name); + drop(obj); } 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 => { - if verbose { - println!(" global {}: {:x?} = {:p}", &name, sym, value.as_ptr()); - } - globals.insert(name, value); - } - sym::STB_WEAK => { - if verbose { - println!(" weak {}: {:x?} = {:p}", &name, sym, value.as_ptr()); - } - weak_syms.insert(name, value); + obj.use_count -= 1; } - _ => unreachable!(), } } - return Ok((globals, weak_syms)); } - pub fn get_sym(&self, name: &str, libspace: Option<usize>) -> Option<Symbol> { - match libspace { - None | Some(root_id) => self.root.get_sym(name), - Some(id) => { - let lib = self.lib_spaces.get(&id)?; - lib.get_sym(name) - } - } - } + fn load_object( + &mut self, + path: &str, + runpath: &Option<String>, + base_addr: Option<usize>, + dlopened: bool, + ) -> Result<()> { + unsafe { _r_debug.state = RTLDState::RT_ADD }; + _dl_debug_state(); - pub fn run_init(&self, libspace: Option<usize>) -> Result<()> { - match libspace { - Some(id) => { - let lib = self.lib_spaces.get(&id).unwrap(); - self.run_tree(&lib, &lib.dep_tree, ".init_array") - } - None => self.run_tree(&self.root, &self.root.dep_tree, ".init_array"), + let mut new_objects = Vec::new(); + let mut objects_data = Vec::new(); + let mut tcb_masters = Vec::new(); + self.load_objects_recursive( + path, + runpath, + base_addr, + dlopened, + &mut new_objects, + &mut objects_data, + &mut tcb_masters, + )?; + + unsafe { + let tcb = if self.objects.len() == 0 { + Tcb::new(self.tls_size)? + } else { + Tcb::current().unwrap() + }; + tcb.append_masters(tcb_masters); + tcb.copy_masters()?; + tcb.activate(); } - } - pub fn run_fini(&self, libspace: Option<usize>) -> Result<()> { - match libspace { - Some(root_id) => return Ok(()), - Some(id) => { - let lib = self.lib_spaces.get(&id).unwrap(); - self.run_tree(&lib, &lib.dep_tree, ".fini_array") - } - None => { - //TODO we first need to deinitialize all the loaded libraries first! - self.run_tree(&self.root, &self.root.dep_tree, ".fini_array") - } - } - } + self.relocate(&new_objects, &objects_data)?; + self.run_init(&new_objects); - fn run_tree(&self, lib: &Library, root: &DepTree, tree_name: &str) -> Result<()> { - for node in root.deps.iter() { - self.run_tree(lib, node, tree_name)?; - } - if self.verbose { - println!("running {} {}", tree_name, &root.name); - } - let (_, mmap) = match lib.mmaps.get(&root.name) { - Some(some) => some, - None => return Ok(()), - }; - let elf = Elf::parse(lib.objects.get(&root.name).unwrap())?; - for section in &elf.section_headers { - let name = match elf.shdr_strtab.get(section.sh_name) { - Some(x) => match x { - Ok(y) => y, - _ => continue, - }, - _ => continue, - }; - if name == tree_name { - let addr = if is_pie_enabled(&elf) { - mmap.as_ptr() as usize + section.vm_range().start - } else { - section.vm_range().start - }; - for i in (0..section.sh_size).step_by(8) { - unsafe { call_inits_finis(addr + i as usize) }; - } - } + for obj in new_objects.into_iter() { + self.name_to_object_id_map.insert(obj.name.clone(), obj.id); + self.objects.insert(obj.id, obj); } + + unsafe { _r_debug.state = RTLDState::RT_CONSISTENT }; + _dl_debug_state(); + return Ok(()); } - pub fn link( + fn load_objects_recursive( &mut self, - primary_opt: Option<&str>, - dso: Option<DSO>, - libspace: Option<usize>, - ) -> Result<Option<usize>> { - match libspace { - Some(id) => { - let mut lib = self.lib_spaces.remove(&id).unwrap(); - let res = self._link(primary_opt, dso, &mut lib); - self.lib_spaces.insert(id, lib); - res - } - None => { - let mut lib = Library::new(); - swap(&mut lib, &mut self.root); - let res = self._link(primary_opt, dso, &mut lib); - swap(&mut lib, &mut self.root); - res + name: &str, + parent_runpath: &Option<String>, + base_addr: Option<usize>, + dlopened: bool, + new_objects: &mut Vec<DSO>, + objects_data: &mut Vec<Vec<u8>>, + tcb_masters: &mut Vec<Master>, + ) -> Result<()> { + if let Some(obj) = { + if let Some(id) = self.name_to_object_id_map.get(name) { + self.objects.get_mut(id) + } else { + new_objects.iter_mut().find(|o| o.name == name) } + } { + obj.use_count += 1; + return Ok(()); } - } - pub fn _link( - &mut self, - primary_opt: Option<&str>, - dso: Option<DSO>, - lib: &mut Library, - ) -> Result<Option<usize>> { - unsafe { _r_debug.state = RTLDState::RT_ADD }; - _dl_debug_state(); - let mut skip_list = BTreeSet::new(); - let elfs = { - let mut elfs = BTreeMap::new(); - for (name, data) in lib.objects.iter() { - // Skip already linked libraries - if !lib.mmaps.contains_key(&*name) && !self.root.mmaps.contains_key(&*name) { - elfs.insert(name.as_str(), Elf::parse(&data)?); - } else { - skip_list.insert(name.as_str()); - } - } - elfs - }; + let path = Linker::search_object(name, &self.ld_library_path, parent_runpath)?; + let data = Linker::read_file(&path)?; + let (obj, tcb_master) = DSO::new( + &path, + &data, + base_addr, + dlopened, + self.next_object_id, + self.next_tls_module_id, + self.tls_size, + )?; + new_objects.push(obj); + objects_data.push(data); + self.next_object_id += 1; + + if let Some(master) = tcb_master { + self.next_tls_module_id += 1; + self.tls_size = master.offset; + tcb_masters.push(master); + } - // Load all ELF files into memory and find all globals - let mut tls_primary = 0; - let mut tls_size = 0; - for (elf_name, elf) in elfs.iter() { - if skip_list.contains(elf_name) { - continue; - } - if self.verbose { - println!("map {}", elf_name); - } - let object = match lib.objects.get(*elf_name) { - Some(some) => some, - None => continue, - }; - // 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 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; + let (runpath, dependencies) = { + let parent = new_objects.last().unwrap(); + (parent.runpath.clone(), parent.dependencies.clone()) + }; + for dep_name in dependencies.iter() { + self.load_objects_recursive( + dep_name, + &runpath, + None, + dlopened, + new_objects, + objects_data, + tcb_masters, + )?; + } - match ph.p_type { - program_header::PT_DYNAMIC => { - l_ld = ph.p_vaddr; - } - program_header::PT_LOAD => { - if self.verbose { - println!(" 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)); - } - } - program_header::PT_TLS => { - if self.verbose { - println!(" load tls {:#x}: {:x?}", vsize, ph); - } - tls_size += vsize; - if Some(*elf_name) == primary_opt { - tls_primary += vsize; - } - } - _ => (), - } - } - match bounds_opt { - Some(some) => some, - None => continue, - } - }; - if self.verbose { - println!(" bounds {:#x}, {:#x}", bounds.0, bounds.1); - } - // Allocate memory - let mmap = unsafe { - let same_elf = if let Some(prog) = dso.as_ref() { - if prog.name == *elf_name { - true - } else { - false - } - } else { - false - }; - if same_elf { - let addr = dso.as_ref().unwrap().base_addr; - let size = if is_pie_enabled(&elf) { - bounds.1 - } else { - bounds.1 - bounds.0 - }; + return Ok(()); + } - // Fill the gaps i the binary - let mut ranges = Vec::new(); - for ph in elf.program_headers.iter() { - if ph.p_type == program_header::PT_LOAD { - 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; - if is_pie_enabled(&elf) { - ranges.push((vaddr, vsize)); - } else { - ranges.push((vaddr - addr, vsize)); - } - } - } - ranges.sort(); - let mut start = addr; - for (vaddr, vsize) in ranges.iter() { - if start < addr + vaddr { - if self.verbose { - println!("mmap({:#x}, {})", start, addr + vaddr - start); - } - let mut flags = sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE; - if start != 0 { - flags |= sys_mman::MAP_FIXED_NOREPLACE; - } - let ptr = sys_mman::mmap( - start as *mut c_void, - addr + vaddr - start, - //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: {}", - elf_name, 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" - ); - } - } - start = addr + vaddr + vsize - } - sys_mman::mprotect( - addr as *mut c_void, - size, - sys_mman::PROT_READ | sys_mman::PROT_WRITE, - ); - _r_debug.insert_first(addr as usize, &elf_name, addr + l_ld as usize); - ( - addr as usize, - slice::from_raw_parts_mut(addr as *mut u8, size), - ) - } else { - let (start, end) = bounds; - let size = end - start; - if self.verbose { - println!("mmap({:#x}, {})", start, size); - } - let mut flags = sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE; - if start != 0 { - flags |= sys_mman::MAP_FIXED_NOREPLACE; - } - 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: {}", - elf_name, 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, &elf_name, ptr as usize + l_ld as usize); - (start, slice::from_raw_parts_mut(ptr as *mut u8, size)) + fn search_object( + name: &str, + ld_library_path: &Option<String>, + parent_runpath: &Option<String>, + ) -> Result<String> { + let mut full_path = name.to_string(); + if accessible(&full_path, F_OK) == 0 { + return Ok(full_path); + } else { + let mut search_paths = Vec::new(); + if let Some(runpath) = parent_runpath { + search_paths.extend(runpath.split(PATH_SEP)); + } + if let Some(ld_path) = ld_library_path { + search_paths.extend(ld_path.split(PATH_SEP)); + } + search_paths.push("/lib"); + for part in search_paths.iter() { + full_path = format!("{}/{}", part, name); + trace!("trying path {}", full_path); + if accessible(&full_path, F_OK) == 0 { + return Ok(full_path); } - }; - if self.verbose { - println!(" mmap {:p}, {:#x}", mmap.1.as_mut_ptr(), mmap.1.len()); } - let (globals, weak_syms) = Linker::collect_syms(&elf, &mmap.1, self.verbose)?; - lib.globals.extend(globals.into_iter()); - lib.weak_syms.extend(weak_syms.into_iter()); - lib.mmaps.insert(elf_name.to_string(), mmap); } + return Err(Error::Malformed(format!("failed to locate '{}'", name))); + } - // Allocate TLS - let mut tcb_opt = if primary_opt.is_some() { - Some(unsafe { Tcb::new(tls_size)? }) - } else { - None - }; - if self.verbose { - println!("tcb {:x?}", tcb_opt); - } - // Copy data - let mut tls_offset = tls_primary; - let mut tcb_masters = Vec::new(); - // Insert main image master - tcb_masters.push(Master { - ptr: ptr::null_mut(), - len: 0, - offset: 0, - }); - let mut tls_ranges = BTreeMap::new(); - for (elf_name, elf) in elfs.iter() { - if skip_list.contains(elf_name) { - continue; - } - let same_elf = if let Some(prog) = dso.as_ref() { - if prog.name == *elf_name { - true - } else { - false - } - } else { - false - }; - let object = match lib.objects.get(*elf_name) { - Some(some) => some, - None => continue, - }; + fn read_file(path: &str) -> Result<Vec<u8>> { + let mut data = Vec::new(); + let path_c = CString::new(path) + .map_err(|err| Error::Malformed(format!("invalid path '{}': {}", path, err)))?; + let flags = fcntl::O_RDONLY | fcntl::O_CLOEXEC; + let mut file = File::open(&path_c, flags) + .map_err(|err| Error::Malformed(format!("failed to open '{}': {}", path, err)))?; + file.read_to_end(&mut data) + .map_err(|err| Error::Malformed(format!("failed to read '{}': {}", path, err)))?; - let &mut (base_addr, ref mut mmap) = match lib.mmaps.get_mut(*elf_name) { - Some(some) => some, - None => continue, - }; - if self.verbose { - println!("load {}", elf_name); - } - // Copy data - for ph in elf.program_headers.iter() { - 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; + return Ok(data); + } - match ph.p_type { - program_header::PT_LOAD => { - if same_elf { - continue; - } - let obj_data = { - let range = ph.file_range(); - match object.get(range.clone()) { - Some(some) => some, - None => { - return Err(Error::Malformed(format!( - "failed to read {:x?}", - range - ))) - } - } - }; + fn relocate(&self, new_objects: &Vec<DSO>, objects_data: &Vec<Vec<u8>>) -> Result<()> { + let symbols_lookup_objects: Vec<&DSO> = + self.objects.values().chain(new_objects.iter()).collect(); - let mmap_data = { - let range = ph.p_vaddr as usize - base_addr - ..ph.p_vaddr as usize + obj_data.len() - base_addr; - match mmap.get_mut(range.clone()) { - Some(some) => some, - None => { - println!("mmap: {}", mmap.len()); - return Err(Error::Malformed(format!( - "failed to write {:x?}", - range - ))); - } - } - }; - if self.verbose { - println!( - " copy {:#x}, {:#x}: {:#x}, {:#x}", - vaddr, - vsize, - voff, - obj_data.len() - ); - } - mmap_data.copy_from_slice(obj_data); - } - 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 ptr = unsafe { - if is_pie_enabled(elf) { - mmap.as_ptr().add(ph.p_vaddr as usize) - } else { - ph.p_vaddr as *const u8 - } - }; - let mut tcb_master = Master { - ptr: ptr, - len: ph.p_filesz as usize, - offset: tls_size - valign, - }; - if self.verbose { - println!( - " tls master {:p}, {:#x}: {:#x}, {:#x}", - tcb_master.ptr, tcb_master.len, tcb_master.offset, valign, - ); - } - if Some(*elf_name) == primary_opt { - tls_ranges.insert( - elf_name.to_string(), - (self.tls_index_offset, tcb_master.range()), - ); - tcb_masters[0] = tcb_master; - } else { - tcb_master.offset -= tls_offset; - tls_offset += vsize; - tls_ranges.insert( - elf_name.to_string(), - ( - self.tls_index_offset + tcb_masters.len(), - tcb_master.range(), - ), - ); - tcb_masters.push(tcb_master); - } - } - _ => (), - } - } - } + // Perform relocations + for i in (0..new_objects.len()).rev() { + let elf = Elf::parse(&objects_data[i])?; + let obj = &new_objects[i]; - self.tls_index_offset += tcb_masters.len(); + trace!("link {}", obj.name); - // Set master images for TLS and copy TLS data - if let Some(ref mut tcb) = tcb_opt { - unsafe { - tcb.set_masters(tcb_masters.into_boxed_slice()); - tcb.copy_masters()?; - } - } + let mmap = &obj.mmap; + let b = mmap.as_ptr() as usize; - // Perform relocations, and protect pages - for (elf_name, elf) in elfs.iter() { - if skip_list.contains(elf_name) { - continue; - } - if self.verbose { - println!("link {}", elf_name); - } // Relocate for rel in elf .dynrelas @@ -746,16 +284,18 @@ impl Linker { .chain(elf.dynrels.iter()) .chain(elf.pltrelocs.iter()) { - // println!(" rel {}: {:x?}", - // reloc::r_to_str(rel.r_type, elf.header.e_machine), - // rel - // ); - let symbol = if rel.r_sym > 0 { + trace!( + " rel {}: {:x?}", + reloc::r_to_str(rel.r_type, elf.header.e_machine), + rel + ); + let (symbol, t) = if rel.r_sym > 0 { let sym = elf.dynsyms.get(rel.r_sym).ok_or(Error::Malformed(format!( "missing symbol for relocation {:?}", rel )))?; + let mut t = 0; let name = elf.dynstrtab .get(sym.st_name) @@ -763,9 +303,32 @@ impl Linker { "missing name for symbol {:?}", sym )))??; - lib.get_sym(name).or_else(|| self.root.get_sym(name)) + let mut symbol = None; + let mut found = false; + let lookup_start = match rel.r_type { + reloc::R_X86_64_COPY => 1, + _ => 0, + }; + for lookup_id in lookup_start..symbols_lookup_objects.len() { + let obj = &symbols_lookup_objects[lookup_id]; + let s = obj.get_sym(name); + if s.is_some() { + trace!("symbol {} from {} found in {}", name, obj.name, obj.name); + symbol = s; + t = obj.tls_offset; + found = true; + break; + } + } + // TODO: below doesn't work because of missing __preinit_array_{start,end} and __init_array_{start,end} symbols in dynamic linked programs + /* + if !found { + return Err(Error::Malformed(format!("missing symbol for name {}", name))); + } + */ + (symbol, t) } else { - None + (None, 0) }; let s = symbol @@ -775,26 +338,13 @@ impl Linker { let a = rel.r_addend.unwrap_or(0) as usize; - let (_, mmap) = match lib.mmaps.get_mut(*elf_name) { - Some(some) => some, - None => continue, - }; - - let b = mmap.as_mut_ptr() as usize; - - let (tm, t) = if let Some((tls_index, tls_range)) = tls_ranges.get(*elf_name) { - (*tls_index, tls_range.start) - } else { - (0, 0) - }; - let ptr = if is_pie_enabled(&elf) { - unsafe { mmap.as_mut_ptr().add(rel.r_offset as usize) } + (b + rel.r_offset as usize) as *mut u8 } else { rel.r_offset as *mut u8 }; let set_u64 = |value| { - // println!(" set_u64 {:#x}", value); + trace!(" set_u64 {:#x}", value); unsafe { *(ptr as *mut u64) = value; } @@ -805,7 +355,7 @@ impl Linker { set_u64((s + a) as u64); } reloc::R_X86_64_DTPMOD64 => { - set_u64(tm as u64); + set_u64(obj.tls_module_id as u64); } reloc::R_X86_64_DTPOFF64 => { if s != 0 { @@ -821,11 +371,16 @@ impl Linker { set_u64((b + a) as u64); } reloc::R_X86_64_TPOFF64 => { - set_u64((s + a).wrapping_sub(t) as u64); + let sym = symbol + .as_ref() + .expect("R_X86_64_TPOFF64 called without valid symbol"); + set_u64((sym.value + a).wrapping_sub(t) as u64); } - reloc::R_X86_64_IRELATIVE => (), // Handled below + reloc::R_X86_64_IRELATIVE => unsafe { + let f: unsafe extern "C" fn() -> u64 = transmute(b + a); + set_u64(f()); + }, reloc::R_X86_64_COPY => unsafe { - // TODO: Make this work let sym = symbol .as_ref() .expect("R_X86_64_COPY called without valid symbol"); @@ -840,202 +395,49 @@ impl Linker { } } - // overwrite DT_DEBUG if exist in DYNAMIC segment - // first we identify the location of DYNAMIC segment - let mut dyn_start = None; - let mut debug_start = None; - for ph in elf.program_headers.iter() { - if ph.p_type == program_header::PT_DYNAMIC { - dyn_start = Some(ph.p_vaddr as usize); - } - } - // 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(dyn_start_addr) = dyn_start { - if let Some(i) = debug_start { - let (_, mmap) = match lib.mmaps.get_mut(*elf_name) { - Some(some) => some, - None => continue, - }; - 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_addr + i * size_of::<Dyn>() + size_of::<Dyn>() / 2 - } else { - dyn_start_addr + 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); - } - } - // Protect pages - for ph in elf.program_headers.iter() { - if ph.p_type == program_header::PT_LOAD { - 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; - - let mut prot = 0; - - if ph.p_flags & program_header::PF_R == program_header::PF_R { - prot |= sys_mman::PROT_READ; - } - - // W ^ X. If it is executable, do not allow it to be writable, even if requested - if ph.p_flags & program_header::PF_X == program_header::PF_X { - prot |= sys_mman::PROT_EXEC; - } else if ph.p_flags & program_header::PF_W == program_header::PF_W { - prot |= sys_mman::PROT_WRITE; - } - - let (_, mmap) = match lib.mmaps.get_mut(*elf_name) { - Some(some) => some, - None => continue, - }; - let res = unsafe { - let ptr = if is_pie_enabled(elf) { - mmap.as_mut_ptr().add(vaddr) - } else { - vaddr as *const u8 - }; - if self.verbose { - println!(" prot {:#x}, {:#x}: {:p}, {:#x}", vaddr, vsize, ptr, prot); - } - sys_mman::mprotect(ptr as *mut c_void, vsize, prot) - }; - - if res < 0 { - return Err(Error::Malformed(format!("failed to mprotect {}", elf_name))); - } - } - } - } - - // Activate TLS - if let Some(ref mut tcb) = tcb_opt { - unsafe { - tcb.activate(); - } - } - - // Perform indirect relocations (necessary evil), gather entry point - let mut entry_opt = None; - for (elf_name, elf) in elfs.iter() { - if skip_list.contains(elf_name) { - continue; - } - let (_, mmap) = match lib.mmaps.get_mut(*elf_name) { - Some(some) => some, - None => continue, - }; - if self.verbose { - println!("entry {}", elf_name); - } - if Some(*elf_name) == primary_opt { - if is_pie_enabled(&elf) { - entry_opt = Some(mmap.as_mut_ptr() as usize + elf.header.e_entry as usize); - } else { - entry_opt = Some(elf.header.e_entry as usize); - } - } - - // Relocate - for rel in elf - .dynrelas + for ph in elf + .program_headers .iter() - .chain(elf.dynrels.iter()) - .chain(elf.pltrelocs.iter()) + .filter(|ph| ph.p_type == program_header::PT_LOAD) { - // println!(" rel {}: {:x?}", - // reloc::r_to_str(rel.r_type, elf.header.e_machine), - // rel - // ); - - let a = rel.r_addend.unwrap_or(0) as usize; - - let b = mmap.as_mut_ptr() as usize; - - let ptr = unsafe { mmap.as_mut_ptr().add(rel.r_offset as usize) }; - - let set_u64 = |value| { - // println!(" set_u64 {:#x}", value); - unsafe { - *(ptr as *mut u64) = value; - } - }; - - if rel.r_type == reloc::R_X86_64_IRELATIVE { - unsafe { - let f: unsafe extern "C" fn() -> u64 = transmute(b + a); - set_u64(f()); - } + 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); + let mut prot = 0; + if ph.p_flags & program_header::PF_R == program_header::PF_R { + prot |= sys_mman::PROT_READ; } - } - // Protect pages - for ph in elf.program_headers.iter() { - if let program_header::PT_LOAD = ph.p_type { - 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; - - let mut prot = 0; - - if ph.p_flags & program_header::PF_R == program_header::PF_R { - prot |= sys_mman::PROT_READ; - } - // W ^ X. If it is executable, do not allow it to be writable, even if requested - if ph.p_flags & program_header::PF_X == program_header::PF_X { - prot |= sys_mman::PROT_EXEC; - } else if ph.p_flags & program_header::PF_W == program_header::PF_W { - prot |= sys_mman::PROT_WRITE; - } + // W ^ X. If it is executable, do not allow it to be writable, even if requested + if ph.p_flags & program_header::PF_X == program_header::PF_X { + prot |= sys_mman::PROT_EXEC; + } else if ph.p_flags & program_header::PF_W == program_header::PF_W { + prot |= sys_mman::PROT_WRITE; + } - let res = unsafe { - let ptr = if is_pie_enabled(&elf) { - mmap.as_mut_ptr().add(vaddr) - } else { - vaddr as *const u8 - }; - if self.verbose { - println!(" prot {:#x}, {:#x}: {:p}, {:#x}", vaddr, vsize, ptr, prot); - } - sys_mman::mprotect(ptr as *mut c_void, vsize, prot) + let res = unsafe { + let ptr = if is_pie_enabled(&elf) { + mmap.as_ptr().add(vaddr) + } else { + vaddr as *const u8 }; + trace!(" prot {:#x}, {:#x}: {:p}, {:#x}", vaddr, vsize, ptr, prot); + sys_mman::mprotect(ptr as *mut c_void, vsize, prot) + }; - if res < 0 { - return Err(Error::Malformed(format!("failed to mprotect {}", elf_name))); - } + if res < 0 { + return Err(Error::Malformed(format!("failed to mprotect {}", obj.name))); } } } - unsafe { _r_debug.state = RTLDState::RT_CONSISTENT }; - _dl_debug_state(); - Ok(entry_opt) - } -} -unsafe fn call_inits_finis(addr: usize) { - let func = transmute::<usize, *const Option<extern "C" fn()>>(addr); - (*func).map(|x| x()); -} + return Ok(()); + } -fn is_pie_enabled(elf: &Elf) -> bool { - if elf.header.e_type == ET_DYN { - true - } else { - false + fn run_init(&self, objects: &Vec<DSO>) { + for obj in objects.iter().rev() { + obj.run_init(); + } } } diff --git a/src/ld_so/mod.rs b/src/ld_so/mod.rs index 19dfe05f521964d1cb6d4d229a976ee1ba698340..7a1324387612d0a9a8aceca53f82dc7be86dba3b 100644 --- a/src/ld_so/mod.rs +++ b/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; diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 6a9439d001d6aabc03bd6f6ae9a8879294f80c8d..25a1f16d0af132ff08a952a0b091c0f98369284d 100644 --- a/src/ld_so/start.rs +++ b/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(); diff --git a/src/ld_so/tcb.rs b/src/ld_so/tcb.rs index 848901a6ed106fd50f218679a67a54a58b50f70b..0ff9e557d358aef22bdc89d01c3ca3129cad930e 100644 --- a/src/ld_so/tcb.rs +++ b/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)); +} diff --git a/src/macros.rs b/src/macros.rs index 132d784186e16994125fed666518ae6d0e682a81..6f9db24f7b8187a3be1a0d7672e807bcbdb0c0c5 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -78,7 +78,6 @@ macro_rules! trace { ($($arg:tt)*) => ({ use $crate::{Pal, Sys}; eprintln!($($arg)*); - Sys::fsync(2); }); } diff --git a/src/sync/semaphore.rs b/src/sync/semaphore.rs index 7734c58a171bb0c8c8afecb94e30f792f8b189b3..aba5aff6689962f768c911ab395f8edfbd0b3390 100644 --- a/src/sync/semaphore.rs +++ b/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) => { diff --git a/tests/Makefile b/tests/Makefile index 815911acb095116eab7a75674c15248f866ec7ac..240d88188fa2743073f64d2b1de56d34f7e8f758 100644 --- a/tests/Makefile +++ b/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) diff --git a/tests/futimens.c b/tests/futimens.c index 78e7f4c96b123555cbc21ebf2c50eafb489b4ea3..820e936a4ae99f5b4e2e1329f11d53cbbf3377a8 100644 --- a/tests/futimens.c +++ b/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));