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);
+}