diff --git a/src/header/dlfcn/mod.rs b/src/header/dlfcn/mod.rs index f9c3bc8149..732068301a 100644 --- a/src/header/dlfcn/mod.rs +++ b/src/header/dlfcn/mod.rs @@ -57,22 +57,16 @@ pub unsafe extern "C" fn dlopen(filename: *const c_char, flags: c_int) -> *mut c eprintln!("dlopen: linker_ptr: {:p}", tcb.linker_ptr); let mut linker = (&*tcb.linker_ptr).lock(); - match linker.load_library(filename) { - Ok(()) => (), - Err(err) => { - eprintln!("dlopen: failed to load {}", filename); - ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); - return ptr::null_mut(); - } + if let Err(err) = linker.load_library(filename) { + eprintln!("dlopen: failed to load {}", filename); + ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); + return ptr::null_mut(); } - match linker.link(None, None) { - Ok(ok) => (), - Err(err) => { - eprintln!("dlopen: failed to link '{}': {}", filename, err); - ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); - return ptr::null_mut(); - } + if let Err(err) = linker.link(None, None) { + eprintln!("dlopen: failed to link '{}': {}", filename, err); + ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); + return ptr::null_mut(); }; // TODO @@ -125,6 +119,8 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m #[no_mangle] pub extern "C" fn dlclose(handle: *mut c_void) -> c_int { + // TODO: Loader::fini() should be called about here + 0 } diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index 818a5feb48..fbd7dd0abd 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -1,6 +1,6 @@ use alloc::{ boxed::Box, - collections::BTreeMap, + collections::{BTreeMap, BTreeSet}, string::{String, ToString}, vec::Vec, }; @@ -42,7 +42,20 @@ pub struct DSO { pub base_addr: usize, pub entry_point: usize, } +#[derive(Default, Debug)] +pub struct DepTree { + pub name: String, + pub deps: Vec, +} +impl DepTree { + fn new(name: String) -> DepTree { + DepTree { + name, + deps: Vec::new(), + } + } +} pub struct Linker { // Used by load /// Library path to search when loading library by name @@ -59,6 +72,10 @@ pub struct Linker { mmaps: BTreeMap, verbose: bool, tls_index_offset: usize, + /// A set used to detect circular dependencies in the Linker::load function + cir_dep: BTreeSet, + /// Each object will have its children callec once with no repetition. + dep_tree: DepTree, } impl Linker { @@ -71,15 +88,33 @@ impl Linker { mmaps: BTreeMap::new(), verbose, tls_index_offset: 0, + cir_dep: BTreeSet::new(), + dep_tree: Default::default(), } } pub fn load(&mut self, name: &str, path: &str) -> Result<()> { + self.dep_tree = self.load_recursive(name, path)?; + if self.verbose { + println!("Dep tree: {:#?}", self.dep_tree); + } + return Ok(()); + } + + fn load_recursive(&mut self, name: &str, path: &str) -> Result { if self.verbose { println!("load {}: {}", name, path); } - let mut data = Vec::new(); + if self.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(); + self.cir_dep.insert(name.to_string()); let path_c = CString::new(path) .map_err(|err| Error::Malformed(format!("invalid path '{}': {}", path, err)))?; @@ -91,31 +126,32 @@ impl Linker { file.read_to_end(&mut data) .map_err(|err| Error::Malformed(format!("failed to read '{}': {}", path, err)))?; } - - self.load_data(name, data.into_boxed_slice()) + deps.deps = self.load_data(name, data.into_boxed_slice())?; + self.cir_dep.remove(name); + Ok(deps) } - pub fn load_data(&mut self, name: &str, data: Box<[u8]>) -> Result<()> { - //TODO: Prevent failures due to recursion - { - let elf = Elf::parse(&data)?; - //println!("{:#?}", elf); - - for library in elf.libraries.iter() { - self.load_library(library)?; + pub fn load_data(&mut self, name: &str, data: Box<[u8]>) -> Result> { + let elf = Elf::parse(&data)?; + //println!("{:#?}", elf); + let mut deps = Vec::new(); + for library in elf.libraries.iter() { + if let Some(dep) = self.load_library(library)? { + deps.push(dep); } } self.objects.insert(name.to_string(), data); - Ok(()) + return Ok(deps); } - pub fn load_library(&mut self, name: &str) -> Result<()> { + pub fn load_library(&mut self, name: &str) -> Result> { if self.objects.contains_key(name) { - Ok(()) + // It should be previously resolved so we don't need to worry about it + Ok(None) } else if name.contains('/') { - self.load(name, name) + Ok(Some(self.load_recursive(name, name)?)) } else { let library_path = self.library_path.clone(); for part in library_path.split(PATH_SEP) { @@ -137,8 +173,7 @@ impl Linker { }; if access { - self.load(name, &path)?; - return Ok(()); + return Ok(Some(self.load_recursive(name, &path)?)); } } @@ -203,6 +238,73 @@ impl Linker { None } } + pub fn run_init(&self) -> Result<()> { + self.run_init_tree(&self.dep_tree) + } + + fn run_init_tree(&self, root: &DepTree) -> Result<()> { + for node in root.deps.iter() { + self.run_init_tree(node)?; + } + if self.verbose { + println!("init {}", &root.name); + } + let mmap = match self.mmaps.get(&root.name) { + Some(some) => some, + None => return Ok(()), + }; + let elf = Elf::parse(self.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 == ".init_array" { + let addr = mmap.as_ptr() as usize + section.vm_range().start; + for i in (0..section.sh_size).step_by(8) { + unsafe { call_inits_finis(addr + i as usize) }; + } + } + } + return Ok(()); + } + + pub fn run_fini(&self) -> Result<()> { + self.run_fini_tree(&self.dep_tree) + } + + fn run_fini_tree(&self, root: &DepTree) -> Result<()> { + if self.verbose { + println!("init {}", &root.name); + } + let mmap = match self.mmaps.get(&root.name) { + Some(some) => some, + None => return Ok(()), + }; + let elf = Elf::parse(self.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 == ".fini_array" { + let addr = mmap.as_ptr() as usize + section.vm_range().start; + for i in (0..section.sh_size).step_by(8) { + unsafe { call_inits_finis(addr + i as usize) }; + } + } + } + for node in root.deps.iter() { + self.run_fini_tree(node)?; + } + return Ok(()); + } pub fn link(&mut self, primary_opt: Option<&str>, dso: Option) -> Result> { unsafe { _r_debug.state = RTLDState::RT_ADD }; @@ -717,3 +819,18 @@ impl Linker { Ok(entry_opt) } } + +unsafe extern "C" fn call_inits_finis(addr: usize) { + #[cfg(target_arch = "x86_64")] + asm!(" + cmp qword ptr [rdi], 0 + je end + call [rdi] +end: nop + " + : + : + : + : "intel", "volatile" + ); +} diff --git a/src/ld_so/src/lib.rs b/src/ld_so/src/lib.rs index f26bc0af64..e3d17c6565 100644 --- a/src/ld_so/src/lib.rs +++ b/src/ld_so/src/lib.rs @@ -33,6 +33,7 @@ next: pop rsi xor r11, r11 fninit jmp rax + # TODO: Loader::fini() should be called about here " : : diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 1d60b714fd..7b433faa4a 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -203,7 +203,11 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> loop {} } }; - + if let Err(e) = linker.run_init() { + 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))); } diff --git a/src/start.rs b/src/start.rs index ade155b965..3dd856f59f 100644 --- a/src/start.rs +++ b/src/start.rs @@ -60,6 +60,24 @@ pub unsafe fn relibc_verify_host() { intrinsics::abort(); } } +#[link_section = ".init_array"] +#[used] +static INIT_ARRAY: [extern "C" fn(); 1] = [init_array]; + +static mut init_complete: bool = false; + +extern "C" fn init_array() { + io_init(); + unsafe { init_complete = true }; +} +fn io_init() { + unsafe { + // Initialize stdin/stdout/stderr, see https://github.com/rust-lang/rust/issues/51718 + stdio::stdin = stdio::default_stdin.get(); + stdio::stdout = stdio::default_stdout.get(); + stdio::stderr = stdio::default_stderr.get(); + } +} #[inline(never)] #[no_mangle] @@ -95,11 +113,9 @@ pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! { platform::inner_environ = copy_string_array(envp, len); platform::environ = platform::inner_environ.as_mut_ptr(); - // Initialize stdin/stdout/stderr, see https://github.com/rust-lang/rust/issues/51718 - stdio::stdin = stdio::default_stdin.get(); - stdio::stdout = stdio::default_stdout.get(); - stdio::stderr = stdio::default_stderr.get(); - + if !init_complete { + init_array(); + } pthread_init(); // Run preinit array