diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs index 3f053d091e126a718a40f84e22bac93a5882803a..f8e72d7ae66a58ba576dc5c41ee732103e479e79 100644 --- a/src/ld_so/dso.rs +++ b/src/ld_so/dso.rs @@ -358,12 +358,14 @@ impl DSO { base: mmap.as_ptr() as usize, value: sym.st_value as usize, size: sym.st_size as usize, + sym_type: sym::st_type(sym.st_info), } } else { Symbol { base: 0, value: sym.st_value as usize, size: sym.st_size as usize, + sym_type: sym::st_type(sym.st_info), } }; } else { diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index 67bd467477c6ff051aed10c40e32c286ffd231e2..69ba71448fda97e96631bbfaacf46f7ef7cd649e 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -6,14 +6,18 @@ use alloc::{ }; use core::{cell::RefCell, mem::transmute, ptr}; use goblin::{ - elf::{program_header, reloc, Elf}, + elf::{program_header, reloc, sym::STT_TLS, Elf}, error::{Error, Result}, }; use crate::{ c_str::CString, fs::File, - header::{fcntl, sys_mman, unistd::F_OK}, + header::{ + dl_tls::{__tls_get_addr, dl_tls_index}, + fcntl, sys_mman, + unistd::F_OK, + }, io::Read, platform::types::c_void, }; @@ -32,6 +36,7 @@ pub struct Symbol { pub value: usize, pub base: usize, pub size: usize, + pub sym_type: u8, } impl Symbol { @@ -91,8 +96,24 @@ impl Linker { 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, + Some(obj) => { + return obj.get_sym(name).map(|s| { + if s.sym_type != STT_TLS { + s.as_ptr() + } else { + unsafe { + let mut tls_index = dl_tls_index { + ti_module: obj.tls_module_id as u64, + ti_offset: s.value as u64, + }; + __tls_get_addr(&mut tls_index) + } + } + }); + } + _ => { + return None; + } } } diff --git a/src/ld_so/tcb.rs b/src/ld_so/tcb.rs index 0ff9e557d358aef22bdc89d01c3ca3129cad930e..152486aa50511202cff688d3be1dbf02440609c3 100644 --- a/src/ld_so/tcb.rs +++ b/src/ld_so/tcb.rs @@ -40,7 +40,7 @@ pub struct Tcb { /// 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, + pub num_copied_masters: usize, /// Pointer to dynamic linker pub linker_ptr: *const Mutex<Linker>, /// pointer to rust memory allocator structure @@ -63,7 +63,7 @@ impl Tcb { tcb_len: tcb_page.len(), masters_ptr: ptr::null_mut(), masters_len: 0, - last_master_copied: 0, + num_copied_masters: 0, linker_ptr: ptr::null(), mspace: 0, }, @@ -114,7 +114,7 @@ impl Tcb { if let Some(masters) = self.masters() { for (i, master) in masters .iter() - .skip(self.last_master_copied) + .skip(self.num_copied_masters) .filter(|m| m.len > 0) .enumerate() { @@ -135,7 +135,7 @@ impl Tcb { return Err(Error::Malformed(format!("failed to copy tls master {}", i))); } } - self.last_master_copied = masters.len(); + self.num_copied_masters = masters.len(); } } diff --git a/tests/Makefile b/tests/Makefile index cb482cd3f33bc68e5371ae8a3cdc07763887a721..66a28c1ae414603da48faf459dbb0b6c2e4c31b6 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -227,12 +227,23 @@ DYNAMIC_FLAGS+=\ -lc DEPS=../sysroot +else +DYNAMIC_FLAGS+=\ + -Wl,-rpath=\$$ORIGIN endif bins_static/%: %.c $(DEPS) mkdir -p "$$(dirname "$@")" $(CC) "$<" -o "$@" $(FLAGS) $(STATIC_FLAGS) +bins_dynamic/%.so: %.c $(DEPS) + mkdir -p "$$(dirname "$@")" + $(CC) "$<" -o "$@" -shared -fpic $(FLAGS) $(DYNAMIC_FLAGS) + +bins_dynamic/dlfcn: dlfcn.c bins_dynamic/sharedlib.so $(DEPS) + mkdir -p "$$(dirname "$@")" + $(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS) + bins_dynamic/%: %.c $(DEPS) mkdir -p "$$(dirname "$@")" $(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS) diff --git a/tests/dlfcn.c b/tests/dlfcn.c index 16729e1cec6c75580b1a6f9af7b04cb22f86cf70..47e2f69bfa60bab4dbe8eb5e7da1453511aa318f 100644 --- a/tests/dlfcn.c +++ b/tests/dlfcn.c @@ -5,24 +5,98 @@ int add(int a, int b) { - return a + b; + return a + b; } +void test_dlopen_null() +{ + void* handle = dlopen(NULL, RTLD_LAZY); + if (!handle) { + printf("dlopen(NULL) failed\n"); + exit(1); + } + int (*f)(int, int) = dlsym(handle, "add"); + if (!f) { + printf("dlsym(handle, add) failed\n"); + exit(2); + } + int a = 22; + int b = 33; + printf("add(%d, %d) = %d\n", a, b, f(a, b)); + dlclose(handle); +} + +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*) = dlsym(handle, "puts"); + if (!f) { + printf("dlsym(handle, puts) failed\n"); + exit(2); + } + f("puts from dlopened libc"); + dlclose(handle); +} + + +void test_dlsym_function() +{ + void* handle = dlopen("sharedlib.so", RTLD_LAZY); + if (!handle) { + printf("dlopen(sharedlib.so) failed\n"); + exit(1); + } + void (*f)() = dlsym(handle, "print"); + if (!f) { + printf("dlsym(handle, print) failed\n"); + exit(2); + } + f(); + dlclose(handle); +} + +void test_dlsym_global_var() +{ + void* handle = dlopen("sharedlib.so", RTLD_LAZY); + if (!handle) { + printf("dlopen(sharedlib.so) failed\n"); + exit(1); + } + int* global_var = dlsym(handle, "global_var"); + if (!global_var) { + printf("dlsym(handle, global_var) failed\n"); + exit(2); + } + printf("main: global_var == %d\n", *global_var); + dlclose(handle); +} + +void test_dlsym_tls_var() +{ + void* handle = dlopen("sharedlib.so", RTLD_LAZY); + if (!handle) { + printf("dlopen(sharedlib.so) failed\n"); + exit(1); + } + int* tls_var = dlsym(handle, "tls_var"); + if (!tls_var) { + printf("dlsym(handle, tls_var) failed\n"); + exit(2); + } + printf("main: tls_var == %d\n", *tls_var); + dlclose(handle); +} int main() { - void* handle = dlopen(NULL, RTLD_LAZY); - if (!handle) { - printf("dlopen(NULL) failed\n"); - exit(1); - } - int (*f)(int, int) = dlsym(handle, "add"); - if (!f) { - printf("dlsym(handle, add) failed\n"); - exit(2); - } - int a = 22; - int b = 33; - printf("add(%d, %d) = %d\n", a, b, f(a, b)); + test_dlopen_null(); + test_dlopen_libc(); + test_dlsym_function(); + test_dlsym_global_var(); + test_dlsym_tls_var(); } diff --git a/tests/expected/dlfcn.stdout b/tests/expected/dlfcn.stdout index 36ae1185bdb1318e7a81ffb2951b7838b5339d54..5b51826beab39811e7260547cedf35c2076882a1 100644 --- a/tests/expected/dlfcn.stdout +++ b/tests/expected/dlfcn.stdout @@ -1 +1,6 @@ add(22, 33) = 55 +puts from dlopened libc +sharedlib: global_var == 42 +sharedlib: tls_var == 21 +main: global_var == 42 +main: tls_var == 21 diff --git a/tests/sharedlib.c b/tests/sharedlib.c new file mode 100644 index 0000000000000000000000000000000000000000..fe2dbf8256859f9f0c6ef7b7a97f36a8531c9b73 --- /dev/null +++ b/tests/sharedlib.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +int global_var = 42; +_Thread_local int tls_var = 21; + +void print() +{ + fprintf(stdout, "sharedlib: global_var == %d\n", global_var); + fprintf(stdout, "sharedlib: tls_var == %d\n", tls_var); +}