diff --git a/src/header/dl-tls/mod.rs b/src/header/dl-tls/mod.rs index 095f1edbd0..de591772be 100644 --- a/src/header/dl-tls/mod.rs +++ b/src/header/dl-tls/mod.rs @@ -29,14 +29,14 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void { tcb.dtv_mut().unwrap().len() ); - if tcb.dtv_mut().unwrap_or_default().len() < masters.len() { + if tcb.dtv_mut().len() < masters.len() { // Reallocate DTV. tcb.setup_dtv(masters.len()); } let dtv_index = ti.ti_module as usize - 1; - if tcb.dtv_mut().unwrap()[dtv_index].is_null() { + if tcb.dtv_mut()[dtv_index].is_null() { // Allocate TLS for module. let master = &masters[dtv_index]; @@ -50,10 +50,10 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void { unsafe { core::ptr::copy_nonoverlapping(master.ptr, module_tls, master.len) }; // Set the DTV entry. - tcb.dtv_mut().unwrap()[dtv_index] = module_tls; + tcb.dtv_mut()[dtv_index] = module_tls; } - let mut ptr = tcb.dtv_mut().unwrap()[dtv_index]; + let mut ptr = tcb.dtv_mut()[dtv_index]; if ptr.is_null() { panic!( diff --git a/src/header/dlfcn/mod.rs b/src/header/dlfcn/mod.rs index d8bd4f06fd..324b3ba303 100644 --- a/src/header/dlfcn/mod.rs +++ b/src/header/dlfcn/mod.rs @@ -3,14 +3,17 @@ #![deny(unsafe_op_in_unsafe_fn)] use core::{ - ptr, str, + ptr::{self, NonNull}, + str, sync::atomic::{AtomicUsize, Ordering}, }; +use alloc::sync::Arc; + use crate::{ c_str::CStr, ld_so::{ - linker::{ObjectScope, Resolve}, + linker::{ObjectHandle, ObjectScope, Resolve}, tcb::Tcb, }, platform::types::*, @@ -18,7 +21,10 @@ use crate::{ pub const RTLD_LAZY: c_int = 1 << 0; pub const RTLD_NOW: c_int = 1 << 1; +// FIXME(andypython): +// #ifdef _GNU_SOURCE pub const RTLD_NOLOAD: c_int = 1 << 2; +// #endif pub const RTLD_GLOBAL: c_int = 1 << 8; pub const RTLD_LOCAL: c_int = 0x0000; @@ -62,6 +68,8 @@ pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut ObjectScope::Local }; + let noload = flags & RTLD_NOLOAD == RTLD_NOLOAD; + let filename = if cfilename.is_null() { None } else { @@ -88,19 +96,19 @@ pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut let cbs_c = linker.cbs.clone(); let cbs = cbs_c.borrow(); - let id = match (cbs.load_library)(&mut linker, filename, resolve, scope) { - Err(err) => { + match (cbs.load_library)(&mut linker, filename, resolve, scope, noload) { + Ok(handle) => handle.as_ptr().cast_mut(), + Err(error) => { ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); - return ptr::null_mut(); + ptr::null_mut() } - Ok(id) => id, - }; - - id as *mut c_void + } } #[no_mangle] pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void { + let handle = ObjectHandle::from_ptr(handle); + if symbol.is_null() { ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); return ptr::null_mut(); @@ -108,6 +116,9 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m let symbol_str = unsafe { str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes()) }; + // FIXME(andypython): just call obj.scope.get_sym() directly or search the + // global scope. The rest is unnecessary as Linker::get_sym() does not + // depend on the Linker state. let tcb = match unsafe { Tcb::current() } { Some(tcb) => tcb, None => { @@ -124,7 +135,7 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m let linker = unsafe { (&*tcb.linker_ptr).lock() }; let cbs_c = linker.cbs.clone(); let cbs = cbs_c.borrow(); - match (cbs.get_sym)(&linker, handle as usize, symbol_str) { + match (cbs.get_sym)(&linker, handle, symbol_str) { Some(sym) => sym, _ => { ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); @@ -147,10 +158,16 @@ pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int { ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); return -1; }; + + let Some(handle) = ObjectHandle::from_ptr(handle) else { + ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); + return -1; + }; + let mut linker = unsafe { (&*tcb.linker_ptr).lock() }; let cbs_c = linker.cbs.clone(); let cbs = cbs_c.borrow(); - (cbs.unload)(&mut linker, handle as usize); + (cbs.unload)(&mut linker, handle); 0 } diff --git a/src/ld_so/callbacks.rs b/src/ld_so/callbacks.rs index e9d804bcdc..64dc41dc08 100644 --- a/src/ld_so/callbacks.rs +++ b/src/ld_so/callbacks.rs @@ -1,12 +1,13 @@ -use super::linker::{Linker, ObjectScope, Resolve}; +use super::linker::{Linker, ObjectHandle, ObjectScope, Resolve}; use crate::platform::types::c_void; use alloc::boxed::Box; use goblin::error::Result; pub struct LinkerCallbacks { - pub unload: Box, - pub load_library: Box, Resolve, ObjectScope) -> Result>, - pub get_sym: Box Option<*mut c_void>>, + pub unload: Box, + pub load_library: + Box, Resolve, ObjectScope, bool) -> Result>, + pub get_sym: Box, &str) -> Option<*mut c_void>>, } impl LinkerCallbacks { @@ -19,8 +20,8 @@ impl LinkerCallbacks { } } -fn unload(linker: &mut Linker, lib_id: usize) { - linker.unload(lib_id) +fn unload(linker: &mut Linker, handle: ObjectHandle) { + linker.unload(handle) } fn load_library( @@ -28,10 +29,11 @@ fn load_library( name: Option<&str>, resolve: Resolve, scope: ObjectScope, -) -> Result { - linker.load_library(name, resolve, scope) + noload: bool, +) -> Result { + linker.load_library(name, resolve, scope, noload) } -fn get_sym(linker: &Linker, lib_id: usize, name: &str) -> Option<*mut c_void> { - linker.get_sym(lib_id, name) +fn get_sym(linker: &Linker, handle: Option, name: &str) -> Option<*mut c_void> { + linker.get_sym(handle, name) } diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs index f8159fe3c7..c2cc36790a 100644 --- a/src/ld_so/dso.rs +++ b/src/ld_so/dso.rs @@ -16,8 +16,8 @@ use alloc::{ }; use core::{ mem::{size_of, transmute}, - ptr, slice, - sync::atomic::AtomicUsize, + ptr::{self, NonNull}, + slice, }; #[cfg(target_pointer_width = "32")] use goblin::elf32::{ @@ -37,6 +37,7 @@ use goblin::elf64::{ }; use goblin::{ elf::{ + dynamic::DT_PLTGOT, sym::{STB_GLOBAL, STB_WEAK}, Dynamic, Elf, }, @@ -80,7 +81,6 @@ pub struct DSO { pub fini_array: (usize, usize), pub tls_module_id: usize, pub tls_offset: usize, - pub use_count: AtomicUsize, pub dynamic: Option, pub dynsyms: Vec, @@ -121,7 +121,6 @@ impl DSO { let dso = DSO { name, id, - use_count: AtomicUsize::new(1), dlopened, entry_point, runpath: DSO::get_runpath(&path, &elf)?, @@ -142,12 +141,40 @@ impl DSO { dynamic: elf.dynamic.map(|dynamic| dynamic), dynsyms: elf.dynsyms.iter().collect(), - scope: Scope::new(), + scope: Scope::local(), }; Ok((dso, tcb_master)) } + /// Global Offset Table + pub(super) fn got(&self) -> Option> { + let Some(dynamic) = self.dynamic.as_ref() else { + return None; + }; + + let object_base_addr = self.mmap.as_ptr() as u64; + + let got = if let Some(ptr) = { + dynamic + .dyns + .iter() + .find(|r#dyn| r#dyn.d_tag == DT_PLTGOT) + .map(|r#dyn| r#dyn.d_val) + } { + if self.pie { + (object_base_addr + ptr) as *mut usize + } else { + ptr as *mut usize + } + } else { + assert_eq!(dynamic.info.jmprel, 0); + return None; + }; + + Some(NonNull::new(got).expect("global offset table")) + } + pub fn get_sym(&self, name: &str) -> Option<(Symbol, SymbolBinding)> { if let Some(value) = self.global_syms.get(name) { Some((*value, SymbolBinding::Global)) @@ -287,7 +314,7 @@ impl DSO { // 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 vaddr = (ph.p_vaddr - voff) as usize; let vsize = ((ph.p_memsz + voff) as usize).next_multiple_of(ph.p_align as usize); match ph.p_type { diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index e19a3bcf70..1ac0b59e92 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -5,14 +5,13 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use core::{cell::RefCell, mem::transmute, ptr, sync::atomic::Ordering}; +use core::{ + cell::RefCell, + mem::transmute, + ptr::{self, NonNull}, +}; use goblin::{ - elf::{ - dynamic::{DT_PLTGOT, DT_STRTAB}, - program_header, reloc, - sym::STT_TLS, - Elf, - }, + elf::{dynamic::DT_STRTAB, program_header, reloc, sym::STT_TLS, Elf}, error::{Error, Result}, }; @@ -42,8 +41,15 @@ use super::{ PATH_SEP, }; +// FIXME: ERROR HANDLING IS WRONG! +// load_library may return an error which may have some variants heap allocated. +// eg Error::Malformed(String::from("failed to locate 'libfoo.so'")) +// therefore dropping it in dlopen would cause heap corruption as it was allocated +// by the dynamic linker's and was freed by the program. +// do better errors than just goblin::Error::Malformed + // TODO: rwlock? -static GLOBAL_SCOPE: Mutex = Mutex::new(Scope::new()); +static GLOBAL_SCOPE: Mutex = Mutex::new(Scope::global()); /// Same as [`crate::fs::File`], but does not touch [`crate::platform::ERRNO`] as the dynamic /// linker does not have thread-local storage. @@ -96,47 +102,143 @@ pub enum ObjectScope { Local, } -pub struct Scope { - objs: Vec>, +pub enum Scope { + Global { + objs: Vec>, + }, + Local { + owner: Option>, + objs: Vec>, + }, } impl Scope { #[inline] - pub const fn new() -> Self { - Self { objs: Vec::new() } + const fn global() -> Self { + Self::Global { objs: Vec::new() } } - pub fn add(&mut self, obj: Weak) { - for dso in self.objs.iter() { - if dso.ptr_eq(&obj) { - // Already in scope. - return; + #[inline] + pub(super) const fn local() -> Self { + Self::Local { + owner: None, + objs: Vec::new(), + } + } + + fn set_owner(&mut self, obj: Weak) { + match self { + Self::Global { .. } => panic!("attempted to set global scope owner"), + Self::Local { ref mut owner, .. } => { + assert!(owner.is_none(), "attempted to change local scope owner"); + *owner = Some(obj); } } - - self.objs.push(obj); } - pub fn get_sym(&self, name: &str) -> Option<(Symbol, SymbolBinding, Arc)> { - let mut res = None; - - for obj in self.objs.iter() { - let obj = obj.upgrade().expect("in scope object was dropped"); - if let Some((sym, binding)) = obj.get_sym(name) { - if binding.is_global() { - return Some((sym, binding, obj)); + fn add(&mut self, target: &Arc) { + match self { + Self::Global { objs } => { + let target = Arc::downgrade(&target); + for obj in objs.iter() { + if Weak::ptr_eq(&obj, &target) { + return; + } } - res = Some((sym, binding, obj)); + objs.push(target); + } + + Self::Local { objs, .. } => { + for obj in objs.iter() { + if Arc::ptr_eq(obj, &target) { + return; + } + } + + objs.push(target.clone()); } } - - res } - pub fn move_into(&self, other: &mut Self) { + fn get_sym(&self, name: &str) -> Option<(Symbol, SymbolBinding, Arc)> { + let mut res = None; + + let get_sym = |obj: Arc| { + if let Some((sym, binding)) = obj.get_sym(name) { + if binding.is_global() { + return Some((sym, binding, obj.clone())); + } + + res = Some((sym, binding, obj.clone())); + } + + None + }; + + match self { + Self::Global { objs } => objs.iter().map(|o| o.upgrade().unwrap()).find_map(get_sym), + Self::Local { owner, objs } => { + let owner = owner + .as_ref() + .expect("local scope without owner") + .upgrade() + .expect("local scope owner was dropped"); + + core::iter::once(owner) + .chain(objs.iter().cloned()) + .find_map(get_sym) + } + } + .or_else(|| res) + } + + fn move_into(&self, other: &mut Self) { // FIXME: move not copy? as afiak u cannot downgrade from a global to a local scope. - other.objs.extend(self.objs.iter().cloned()); + match (self, other) { + (Self::Local { owner, objs }, Self::Global { objs: other_objs }) => { + let owner = owner.as_ref().expect("local scope without owner"); + other_objs.push(owner.clone()); + other_objs.extend(objs.iter().map(|o| Arc::downgrade(o))); + } + + _ => unreachable!(), + } + } +} + +// Used by dlfcn.h +// +// We need this as the handle must be created and destroyed with the dynamic +// linker's allocator. +pub struct ObjectHandle(*const DSO); + +impl ObjectHandle { + #[inline] + fn new(obj: Arc) -> Self { + Self(Arc::into_raw(obj)) + } + + #[inline] + fn into_inner(&self) -> Arc { + unsafe { Arc::from_raw(self.0) } + } + + #[inline] + pub fn as_ptr(&self) -> *const c_void { + self.0.cast() + } + + #[inline] + pub fn from_ptr(ptr: *const c_void) -> Option { + NonNull::new(ptr as *mut DSO).map(|ptr| Self(ptr.as_ptr())) + } +} + +impl AsRef for ObjectHandle { + #[inline] + fn as_ref(&self) -> &DSO { + unsafe { &*self.0 } } } @@ -220,7 +322,7 @@ impl Linker { } pub fn load_program(&mut self, path: &str, base_addr: Option) -> Result { - self.load_object( + let dso = self.load_object( path, &None, base_addr, @@ -232,9 +334,7 @@ impl Linker { }, ObjectScope::Global, )?; - // TODO(andypython): make self.load_object() return a reference to the - // loaded object, thereby remove the ugly unwrap(). - Ok(self.objects.get(&ROOT_ID).unwrap().entry_point) + Ok(dso.entry_point) } pub fn load_library( @@ -242,36 +342,47 @@ impl Linker { name: Option<&str>, resolve: Resolve, scope: ObjectScope, - ) -> Result { + noload: bool, + ) -> Result { + trace!( + "[ld.so] dlopen({:?}, {:#?}, {:#?}, noload={})", + name, + resolve, + scope, + noload + ); + + if noload && resolve == Resolve::Now { + // Do not perform lazy binding anymore. + // * Check if loaded with Resolve::Now and if so, early return. + // * If not, resolve all symbols now. + todo!("resolve symbols now!"); + } + match name { Some(name) => { if let Some(id) = self.name_to_object_id_map.get(name) { let obj = self.objects.get_mut(id).unwrap(); - obj.use_count.fetch_add(1, Ordering::SeqCst); // We may be upgrading the object from a local scope to the // global scope. - // - // TODO(andypython): Only do it if RTLD_NOLOAD. Otherwise, - // we should reload rather than just promoting. if scope == ObjectScope::Global { if self.config.debug_flags.contains(DebugFlags::SCOPES) { eprintln!("[ld.so]: moving {} into the global scope", obj.name); } let mut global_scope = GLOBAL_SCOPE.lock(); - global_scope.add(Arc::downgrade(obj)); obj.scope.move_into(&mut global_scope); } - Ok(*id) - } else { + Ok(ObjectHandle::new(obj.clone())) + } else if !noload { let parent_runpath = &self .objects .get(&ROOT_ID) .and_then(|parent| parent.runpath.clone()); - let lib_id = self.next_object_id; - self.load_object( + + Ok(ObjectHandle::new(self.load_object( name, parent_runpath, None, @@ -282,24 +393,34 @@ impl Linker { resolve }, scope, - )?; - - Ok(lib_id) + )?)) + } else { + // FIXME: LoadError? + // Err(Error::Malformed(format!( + // "object '{}' has not yet been loaded", + // name + // ))) + Ok(ObjectHandle(ptr::null())) } } - None => Ok(ROOT_ID), + None => Ok(ObjectHandle::new( + self.objects + .get(&ROOT_ID) + .expect("root object missing") + .clone(), + )), } } - pub fn get_sym(&self, lib_id: usize, name: &str) -> Option<*mut c_void> { + pub fn get_sym(&self, handle: Option, name: &str) -> Option<*mut c_void> { let guard; - if lib_id == 0 { + if let Some(handle) = handle.as_ref() { + &handle.as_ref().scope + } else { guard = GLOBAL_SCOPE.lock(); &*guard - } else { - &self.objects.get(&lib_id)?.scope } .get_sym(name) .map(|(symbol, _, obj)| { @@ -307,9 +428,6 @@ impl Linker { symbol.as_ptr() } else { let mut tls_index = dl_tls_index { - // TODO(andypython): instead of an ID, why don't we pass a - // pointer to the object? Then we won't need to look it up. - // This is what we already do for __plt_resolve_trampoline. ti_module: obj.tls_module_id as u64, ti_offset: symbol.value as u64, }; @@ -319,21 +437,46 @@ impl Linker { }) } - 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.load(Ordering::SeqCst) == 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 { - obj.use_count.fetch_sub(1, Ordering::SeqCst); - } - } + pub fn unload(&mut self, handle: ObjectHandle) { + let obj = handle.into_inner(); + if !obj.dlopened { + return; } + + trace!( + "[ld.so] unloading {} (sc={}, wc={})", + obj.name, + Arc::strong_count(&obj), + Arc::weak_count(&obj) + ); + + // One for the reference we have and the other for the one in the + // objects map. + if Arc::strong_count(&obj) == 2 { + // Remove from the global scope. + match *GLOBAL_SCOPE.lock() { + Scope::Global { ref mut objs } => { + objs.retain(|o| !Weak::ptr_eq(o, &Arc::downgrade(&obj))); + } + + _ => unreachable!(), + } + + let _ = self.objects.remove(&obj.id).unwrap(); + for dep in obj.dependencies.iter() { + self.unload(ObjectHandle::new( + self.objects + .get(self.name_to_object_id_map.get(dep).unwrap()) + .unwrap() + .clone(), + )); + } + self.name_to_object_id_map.remove(&obj.name); + assert!(Arc::strong_count(&obj) == 1); + drop(obj); + } + + // obj is dropped here. } pub fn fini(&self) { @@ -350,7 +493,7 @@ impl Linker { dlopened: bool, resolve: Resolve, scope: ObjectScope, - ) -> Result<()> { + ) -> Result> { let resolve = if cfg!(target_arch = "x86_64") { resolve } else { @@ -364,7 +507,7 @@ impl Linker { let mut new_objects = Vec::new(); let mut objects_data = Vec::new(); let mut tcb_masters = Vec::new(); - self.load_objects_recursive( + let loaded_dso = self.load_objects_recursive( path, runpath, base_addr, @@ -444,7 +587,7 @@ impl Linker { if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { // Below the TP - tcb.dtv_mut().unwrap()[dtv_idx] = tcb_ptr.cast::().sub(obj.tls_offset); + tcb.dtv_mut()[dtv_idx] = tcb_ptr.cast::().sub(obj.tls_offset); } else { // FIMXE(andypython): Make it above the TP // @@ -452,8 +595,7 @@ impl Linker { // tcb_ptr.add(1).cast::().add(obj.tls_offset); // // FIXME(andypython): https://gitlab.redox-os.org/redox-os/relibc/-/merge_requests/570#note_35788 - tcb.dtv_mut().unwrap()[dtv_idx] = - tcb.tls_end.sub(tcb.tls_len).add(obj.tls_offset); + tcb.dtv_mut()[dtv_idx] = tcb.tls_end.sub(tcb.tls_len).add(obj.tls_offset); } } @@ -485,7 +627,7 @@ impl Linker { unsafe { _r_debug.state = RTLDState::RT_CONSISTENT }; _dl_debug_state(); - Ok(()) + Ok(loaded_dso) } fn register_object(&mut self, obj: Arc) { @@ -505,15 +647,14 @@ impl Linker { // The object that caused this object to be loaded. dependent: Option<&mut DSO>, scope: ObjectScope, - ) -> Result<()> { + ) -> Result> { + // fixme: double lookup slow if let Some(id) = self.name_to_object_id_map.get(name) { if let Some(obj) = self.objects.get_mut(id) { - obj.use_count.fetch_add(1, Ordering::SeqCst); - return Ok(()); + return Ok(obj.clone()); } } else if let Some(obj) = new_objects.iter_mut().find(|o| o.name == name) { - obj.use_count.fetch_add(1, Ordering::SeqCst); - return Ok(()); + return Ok(obj.clone()); } let path = self.search_object(name, parent_runpath)?; @@ -565,23 +706,23 @@ impl Linker { } let obj = Arc::new_cyclic(|sref| { - obj.scope.add(sref.clone()); + obj.scope.set_owner(sref.clone()); obj }); if let Some(dependent) = dependent { match scope { - ObjectScope::Local => dependent.scope.add(Arc::downgrade(&obj)), - ObjectScope::Global => GLOBAL_SCOPE.lock().add(Arc::downgrade(&obj)), + ObjectScope::Local => dependent.scope.add(&obj), + ObjectScope::Global => GLOBAL_SCOPE.lock().add(&obj), } } else if let ObjectScope::Global = scope { - GLOBAL_SCOPE.lock().add(Arc::downgrade(&obj)); + GLOBAL_SCOPE.lock().add(&obj); } objects_data.push(data); - new_objects.push(obj); + new_objects.push(obj.clone()); - Ok(()) + Ok(obj) } fn search_object(&self, name: &str, parent_runpath: &Option) -> Result { @@ -637,79 +778,59 @@ impl Linker { /// Perform lazy relocations. fn lazy_relocate(&self, obj: &Arc, elf: &Elf, resolve: Resolve) -> Result<()> { - if let Some(dynamic) = elf.dynamic.as_ref() { - let object_base_addr = obj.mmap.as_ptr() as u64; + let Some(got) = obj.got() else { return Ok(()) }; + let object_base_addr = obj.mmap.as_ptr() as u64; - // Global Offset Table - let got = if let Some(ptr) = { - dynamic - .dyns - .iter() - .find(|r#dyn| r#dyn.d_tag == DT_PLTGOT) - .map(|r#dyn| r#dyn.d_val) - } { - if is_pie_enabled(&elf) { - (object_base_addr + ptr) as *mut usize - } else { - ptr as *mut usize - } + unsafe { + got.add(1).write(Arc::as_ptr(obj) as usize); + got.add(2).write(__plt_resolve_trampoline as usize); + } + + for rel in elf.pltrelocs.iter() { + let ptr = if is_pie_enabled(elf) { + (object_base_addr + rel.r_offset) as *mut u64 } else { - assert_eq!(dynamic.info.jmprel, 0); - return Ok(()); + rel.r_offset as *mut u64 }; - unsafe { - let obj = Arc::into_raw(obj.clone()); - got.add(1).write(obj as usize); - got.add(2).write(__plt_resolve_trampoline as usize); - } + match (rel.r_type, resolve) { + (reloc::R_X86_64_JUMP_SLOT, Resolve::Lazy) if is_pie_enabled(elf) => unsafe { + *ptr += object_base_addr; + }, - for rel in elf.pltrelocs.iter() { - let ptr = if is_pie_enabled(elf) { - (object_base_addr + rel.r_offset) as *mut u64 - } else { - rel.r_offset as *mut u64 - }; - - match (rel.r_type, resolve) { - (reloc::R_X86_64_JUMP_SLOT, Resolve::Lazy) if is_pie_enabled(elf) => unsafe { - *ptr += object_base_addr; - }, - - (reloc::R_X86_64_JUMP_SLOT, Resolve::Lazy) => { - // NOP. - } - - (reloc::R_X86_64_JUMP_SLOT, Resolve::Now) => { - let sym = elf.dynsyms.get(rel.r_sym).ok_or(Error::Malformed(format!( - "missing symbol for relocation {:?}", - rel - )))?; - - let name = - elf.dynstrtab - .get_at(sym.st_name) - .ok_or(Error::Malformed(format!( - "missing name for symbol {:?}", - sym - )))?; - - let resolved = GLOBAL_SCOPE - .lock() - .get_sym(name) - .or_else(|| obj.scope.get_sym(name)) - .map(|(sym, _, _)| sym.as_ptr()) - .expect("unresolved symbol"); - - let addend = rel.r_addend.unwrap_or(0) as u64; - - unsafe { - *ptr = resolved as u64 + addend; - } - } - - _ => todo!("unsupported relocation type {:?}", rel.r_type), + (reloc::R_X86_64_JUMP_SLOT, Resolve::Lazy) => { + // NOP. } + + (reloc::R_X86_64_JUMP_SLOT, Resolve::Now) => { + let sym = elf.dynsyms.get(rel.r_sym).ok_or(Error::Malformed(format!( + "missing symbol for relocation {:?}", + rel + )))?; + + let name = + elf.dynstrtab + .get_at(sym.st_name) + .ok_or(Error::Malformed(format!( + "missing name for symbol {:?}", + sym + )))?; + + let resolved = GLOBAL_SCOPE + .lock() + .get_sym(name) + .or_else(|| obj.scope.get_sym(name)) + .map(|(sym, _, _)| sym.as_ptr()) + .expect("unresolved symbol"); + + let addend = rel.r_addend.unwrap_or(0) as u64; + + unsafe { + *ptr = resolved as u64 + addend; + } + } + + _ => todo!("unsupported relocation type {:?}", rel.r_type), } } diff --git a/src/ld_so/mod.rs b/src/ld_so/mod.rs index 334793e19e..73eca11249 100644 --- a/src/ld_so/mod.rs +++ b/src/ld_so/mod.rs @@ -77,7 +77,7 @@ pub fn static_init(sp: &'static Stack) { let page_size = Sys::getpagesize(); let voff = ph.p_vaddr as usize % page_size; - let vaddr = ph.p_vaddr as usize - voff; + // let vaddr = ph.p_vaddr as usize - voff; let vsize = ((ph.p_memsz as usize + voff + page_size - 1) / page_size) * page_size; match ph.p_type { @@ -174,7 +174,7 @@ pub unsafe fn init(sp: &'static Stack) { pub unsafe fn fini() { if let Some(tcb) = Tcb::current() { - if tcb.linker_ptr != ptr::null_mut() { + if !tcb.linker_ptr.is_null() { let linker = (&*tcb.linker_ptr).lock(); linker.fini(); } diff --git a/src/ld_so/tcb.rs b/src/ld_so/tcb.rs index ef3c17ea45..48fd756a0e 100644 --- a/src/ld_so/tcb.rs +++ b/src/ld_so/tcb.rs @@ -220,7 +220,7 @@ impl Tcb { // // XXX: [`Vec::from_raw_parts`] cannot be used here as the DTV was originally allocated // by the ld.so allocator and that would violate that function's invariants. - let mut dtv = self.dtv_mut().unwrap().to_vec(); + let mut dtv = self.dtv_mut().to_vec(); dtv.resize(n, ptr::null_mut()); let (ptr, len, _) = dtv.into_raw_parts(); @@ -229,11 +229,11 @@ impl Tcb { } } - pub fn dtv_mut(&mut self) -> Option<&'static mut [*mut u8]> { + pub fn dtv_mut(&mut self) -> &'static mut [*mut u8] { if self.dtv_len != 0 { - Some(unsafe { slice::from_raw_parts_mut(self.dtv_ptr, self.dtv_len) }) + unsafe { slice::from_raw_parts_mut(self.dtv_ptr, self.dtv_len) } } else { - None + &mut [] } } diff --git a/tests/dlfcn.c b/tests/dlfcn.c index bf1d7354d4..ed81a7e0e5 100644 --- a/tests/dlfcn.c +++ b/tests/dlfcn.c @@ -1,18 +1,16 @@ +#include +#include #include #include -#include +#define SHARED_LIB "sharedlib.so" -int add(int a, int b) -{ - return a + b; -} +int add(int a, int b) { return a + b; } -void test_dlopen_null() -{ - void* handle = dlopen(NULL, RTLD_LAZY); +void test_dlopen_null() { + void *handle = dlopen(NULL, RTLD_LAZY); if (!handle) { - printf("dlopen(NULL) failed\n"); + printf("dlopen(NULL) failed: %s\n", dlerror()); exit(1); } @@ -20,7 +18,7 @@ void test_dlopen_null() *(void **)(&f) = dlsym(handle, "add"); if (!f) { - printf("dlsym(handle, add) failed\n"); + printf("dlsym(handle, add) failed: %s\n", dlerror()); exit(2); } int a = 22; @@ -29,15 +27,14 @@ void test_dlopen_null() dlclose(handle); } -void test_dlopen_libc() -{ - void* handle = dlopen("libc.so.6", RTLD_LAZY); +void test_dlopen_libc() { + void *handle = dlopen("libc.so.6", RTLD_LAZY); if (!handle) { printf("dlopen(libc.so.6) failed\n"); exit(1); } - - int (*f)(const char*); + + int (*f)(const char *); *(void **)(&f) = dlsym(handle, "puts"); if (!f) { @@ -48,10 +45,8 @@ void test_dlopen_libc() dlclose(handle); } - -void test_dlsym_function() -{ - void* handle = dlopen("sharedlib.so", RTLD_LAZY); +void test_dlsym_function() { + void *handle = dlopen(SHARED_LIB, RTLD_LAZY); if (!handle) { printf("dlopen(sharedlib.so) failed\n"); exit(1); @@ -68,14 +63,13 @@ void test_dlsym_function() dlclose(handle); } -void test_dlsym_global_var() -{ - void* handle = dlopen("sharedlib.so", RTLD_LAZY); +void test_dlsym_global_var() { + void *handle = dlopen(SHARED_LIB, RTLD_LAZY); if (!handle) { printf("dlopen(sharedlib.so) failed\n"); exit(1); } - int* global_var = dlsym(handle, "global_var"); + int *global_var = dlsym(handle, "global_var"); if (!global_var) { printf("dlsym(handle, global_var) failed\n"); exit(2); @@ -84,14 +78,13 @@ void test_dlsym_global_var() dlclose(handle); } -void test_dlsym_tls_var() -{ - void* handle = dlopen("sharedlib.so", RTLD_LAZY); +void test_dlsym_tls_var() { + void *handle = dlopen(SHARED_LIB, RTLD_LAZY); if (!handle) { printf("dlopen(sharedlib.so) failed\n"); exit(1); } - int* tls_var = dlsym(handle, "tls_var"); + int *tls_var = dlsym(handle, "tls_var"); if (!tls_var) { printf("dlsym(handle, tls_var) failed\n"); exit(2); @@ -100,12 +93,30 @@ void test_dlsym_tls_var() dlclose(handle); } -int main() -{ +void test_dlunload(void) { + void *handle = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_LOCAL); + void *handle2 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_LOCAL); + assert(handle == handle2 && handle); + assert(!dlclose(handle)); + void *handle3 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_NOLOAD); + assert(handle3 == handle2); + assert(!dlclose(handle3)); + assert(!dlclose(handle2)); + void *handle4 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_NOLOAD); + assert(handle4 == NULL); + + void *handle5 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_GLOBAL); + assert(handle5); + assert(!dlclose(handle5)); + void *handle6 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_NOLOAD); + assert(handle6 == NULL); +} + +int main() { test_dlopen_null(); test_dlopen_libc(); test_dlsym_function(); test_dlsym_global_var(); test_dlsym_tls_var(); + test_dlunload(); } - diff --git a/tests/libfoo.c b/tests/libfoo.c index 3bc4cbd8bc..b2106a4ec5 100644 --- a/tests/libfoo.c +++ b/tests/libfoo.c @@ -1,4 +1,3 @@ char *FOO = "foo"; -char *BAZ = "baz"; void a() {} diff --git a/tests/libfoobar.c b/tests/libfoobar.c index c862ee0bf6..67cef36df6 100644 --- a/tests/libfoobar.c +++ b/tests/libfoobar.c @@ -1,5 +1,4 @@ char *BAR = "bar"; -extern char *BAZ; void a(); void b() { a(); }