From 6aeb2d6fa24190d15525a9856e9fc580dd2322c6 Mon Sep 17 00:00:00 2001 From: oddcoder <ahmedsoliman@oddcoder.com> Date: Sun, 19 Apr 2020 13:54:50 +0200 Subject: [PATCH] Implement code that use .init_array and .fini_array This patch implements ld.so code that makes use of both .init_array and .fini_array. .init_array is fully utilized and is used in the correct manner. However .fini_array is not used yet although the function that runs .fini_array exists --- src/header/dlfcn/mod.rs | 24 +++---- src/ld_so/linker.rs | 136 +++++++++++++++++++++++++++++++++++----- src/ld_so/src/lib.rs | 1 + src/ld_so/start.rs | 6 +- 4 files changed, 138 insertions(+), 29 deletions(-) diff --git a/src/header/dlfcn/mod.rs b/src/header/dlfcn/mod.rs index f9c3bc81..73206830 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 b80d5d8b..fbd7dd0a 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -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<DepTree>, +} +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,8 +72,10 @@ pub struct Linker { mmaps: BTreeMap<String, &'static mut [u8]>, verbose: bool, tls_index_offset: usize, - // used to detect circular dependencies in the Linker::load function + /// A set used to detect circular dependencies in the Linker::load function cir_dep: BTreeSet<String>, + /// Each object will have its children callec once with no repetition. + dep_tree: DepTree, } impl Linker { @@ -74,10 +89,19 @@ impl Linker { 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<DepTree> { if self.verbose { println!("load {}: {}", name, path); } @@ -88,9 +112,9 @@ impl Linker { ))); } - self.cir_dep.insert(name.to_string()); + 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)))?; @@ -102,29 +126,32 @@ impl Linker { file.read_to_end(&mut data) .map_err(|err| Error::Malformed(format!("failed to read '{}': {}", path, err)))?; } - let result = self.load_data(name, data.into_boxed_slice()); + deps.deps = self.load_data(name, data.into_boxed_slice())?; self.cir_dep.remove(name); - result + Ok(deps) } - pub fn load_data(&mut self, name: &str, data: Box<[u8]>) -> Result<()> { + pub fn load_data(&mut self, name: &str, data: Box<[u8]>) -> Result<Vec<DepTree>> { let elf = Elf::parse(&data)?; //println!("{:#?}", elf); - + let mut deps = Vec::new(); for library in elf.libraries.iter() { - self.load_library(library)?; + 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<Option<DepTree>> { 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) { @@ -146,8 +173,7 @@ impl Linker { }; if access { - self.load(name, &path)?; - return Ok(()); + return Ok(Some(self.load_recursive(name, &path)?)); } } @@ -212,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<DSO>) -> Result<Option<usize>> { unsafe { _r_debug.state = RTLDState::RT_ADD }; @@ -726,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 f26bc0af..e3d17c65 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 1d60b714..7b433faa 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))); } -- GitLab