From 30aef27c76930730d38e2de14d9163c67736101d Mon Sep 17 00:00:00 2001
From: Jeremy Soller <jeremy@system76.com>
Date: Sun, 14 Apr 2019 19:08:58 -0600
Subject: [PATCH] Correctly set up TLS on Redox and other fixes for
 pthread_clone

---
 src/header/dl-tls/mod.rs  |  1 +
 src/ld_so/linker.rs       |  4 +-
 src/ld_so/tcb.rs          | 25 ++++++-----
 src/platform/linux/mod.rs | 11 ++++-
 src/platform/pte.rs       | 94 ++++++++++++++++++++++++++-------------
 src/platform/redox/mod.rs | 11 ++++-
 6 files changed, 97 insertions(+), 49 deletions(-)

diff --git a/src/header/dl-tls/mod.rs b/src/header/dl-tls/mod.rs
index f1a44b6f8..b61cc4ced 100644
--- a/src/header/dl-tls/mod.rs
+++ b/src/header/dl-tls/mod.rs
@@ -1,5 +1,6 @@
 //! dl-tls implementation for Redox
 
+use ld_so::tcb::Tcb;
 use platform::types::*;
 
 #[repr(C)]
diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs
index e4e2c5482..980ff00ef 100644
--- a/src/ld_so/linker.rs
+++ b/src/ld_so/linker.rs
@@ -199,7 +199,7 @@ impl Linker {
                     if let Some(name_res) = elf.dynstrtab.get(sym.st_name) {
                         let name = name_res?;
                         let value = mmap.as_ptr() as usize + sym.st_value as usize;
-                        //println!("  global {}: {:x?} = {:#x}", name, sym, value);
+                        // println!("  global {}: {:x?} = {:#x}", name, sym, value);
                         globals.insert(name, value);
                     }
                 }
@@ -356,7 +356,7 @@ impl Linker {
                 };
 
                 let set_u64 = |value| {
-                    //println!("    set_u64 {:#x}", value);
+                    // println!("    set_u64 {:#x}", value);
                     unsafe { *(ptr as *mut u64) = value; }
                 };
 
diff --git a/src/ld_so/tcb.rs b/src/ld_so/tcb.rs
index d39253bd8..643e51223 100644
--- a/src/ld_so/tcb.rs
+++ b/src/ld_so/tcb.rs
@@ -3,7 +3,7 @@ use core::{mem, ptr, slice};
 use core::ops::Range;
 use goblin::error::{Error, Result};
 
-use header::sys_mman;
+use header::{sys_mman, unistd};
 
 use super::PAGE_SIZE;
 
@@ -36,17 +36,17 @@ impl Master {
 #[repr(C)]
 pub struct Tcb {
     /// Pointer to the end of static TLS. Must be the first member
-    tls_end: *mut u8,
+    pub tls_end: *mut u8,
     /// Size of the memory allocated for the static TLS in bytes (multiple of PAGE_SIZE)
-    tls_len: usize,
+    pub tls_len: usize,
     /// Pointer to this structure
-    tcb_ptr: *mut Tcb,
+    pub tcb_ptr: *mut Tcb,
     /// Size of the memory allocated for this structure in bytes (should be PAGE_SIZE)
-    tcb_len: usize,
+    pub tcb_len: usize,
     /// Pointer to a list of initial TLS data
-    masters_ptr: *mut Master,
+    pub masters_ptr: *mut Master,
     /// Size of the masters list in bytes (multiple of mem::size_of::<Master>())
-    masters_len: usize,
+    pub masters_len: usize,
 }
 
 impl Tcb {
@@ -55,7 +55,7 @@ impl Tcb {
         let (tls, tcb_page) = Self::os_new(size)?;
 
         let tcb_ptr = tcb_page.as_mut_ptr() as *mut Self;
-        ptr::write(tcb_ptr, Tcb {
+        ptr::write(tcb_ptr, Self {
             tls_end: tls.as_mut_ptr().add(tls.len()),
             tls_len: tls.len(),
             tcb_ptr: tcb_ptr,
@@ -171,11 +171,14 @@ impl Tcb {
     /// OS specific code to create a new TLS and TCB - Redox
     #[cfg(target_os = "redox")]
     unsafe fn os_new(size: usize) -> Result<(&'static mut [u8], &'static mut [u8])> {
+        //TODO: better method of finding fs offset
+        let pid = unistd::getpid();
+        let tcb_addr = 0xB000_0000 + pid as usize * PAGE_SIZE;
         let tls = Self::map(size)?;
         Ok((
             tls,
             //TODO: Consider allocating TCB as part of TLS
-            slice::from_raw_parts_mut(0xB000_0000 as *mut u8, PAGE_SIZE)
+            slice::from_raw_parts_mut(tcb_addr as *mut u8, PAGE_SIZE)
         ))
     }
 
@@ -185,10 +188,10 @@ impl Tcb {
     unsafe fn arch_read(offset: usize) -> usize {
         let value;
         asm!("
-            mov rax, [fs:rdi]
+            mov rax, fs:[rdi]
             "
             : "={rax}"(value)
-            : "{rax}"(offset)
+            : "{rdi}"(offset)
             :
             : "intel"
         );
diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs
index ed0cb7065..c3018d25a 100644
--- a/src/platform/linux/mod.rs
+++ b/src/platform/linux/mod.rs
@@ -343,9 +343,16 @@ impl Pal for Sys {
             test rax, rax
             jnz .parent
 
-            # Call entry point
-            pop rdi
+            # Load registers
             pop rax
+            pop rdi
+            pop rsi
+            pop rdx
+            pop rcx
+            pop r8
+            pop r9
+
+            # Call entry point
             call rax
 
             # Exit
diff --git a/src/platform/pte.rs b/src/platform/pte.rs
index 37d14a33d..2a4bf5e97 100644
--- a/src/platform/pte.rs
+++ b/src/platform/pte.rs
@@ -7,6 +7,7 @@ use core::{intrinsics, ptr};
 
 use header::sys_mman;
 use header::time::timespec;
+use ld_so::tcb::{Tcb, Master};
 use mutex::{FUTEX_WAIT, FUTEX_WAKE};
 use platform::types::{c_int, c_uint, c_void, pid_t, size_t};
 use platform::{Pal, Sys};
@@ -58,6 +59,27 @@ pub unsafe extern "C" fn pte_osInit() -> pte_osResult {
     PTE_OS_OK
 }
 
+/// A shim to wrap thread entry points in logic to set up TLS, for example
+unsafe extern "C" fn pte_osThreadShim(
+    entryPoint: pte_osThreadEntryPoint,
+    argv: *mut c_void,
+    mutex: pte_osMutexHandle,
+    tls_size: usize,
+    tls_masters_ptr: *mut Master,
+    tls_masters_len: usize
+) {
+    let mut tcb = Tcb::new(tls_size).unwrap();
+    tcb.masters_ptr = tls_masters_ptr;
+    tcb.masters_len = tls_masters_len;
+    tcb.copy_masters().unwrap();
+    tcb.activate();
+
+    // Wait until pte_osThreadStart
+    pte_osMutexLock(mutex);
+    entryPoint(argv);
+    pte_osThreadExit();
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn pte_osThreadCreate(
     entryPoint: pte_osThreadEntryPoint,
@@ -66,6 +88,9 @@ pub unsafe extern "C" fn pte_osThreadCreate(
     argv: *mut c_void,
     ppte_osThreadHandle: *mut pte_osThreadHandle,
 ) -> pte_osResult {
+    // Create a locked mutex, unlocked by pte_osThreadStart
+    let mutex: pte_osMutexHandle = Box::into_raw(Box::new(2));
+
     let stack_size = if stackSize == 0 {
         1024 * 1024
     } else {
@@ -85,44 +110,49 @@ pub unsafe extern "C" fn pte_osThreadCreate(
     let stack_end = stack_base.add(stack_size);
     let mut stack = stack_end as *mut usize;
     {
-        stack = stack.offset(-1);
-        *stack = entryPoint as usize;
+        let mut push = |value: usize| {
+            stack = stack.offset(-1);
+            *stack = value;
+        };
+
+        if let Some(tcb) = Tcb::current() {
+            push(tcb.masters_len);
+            push(tcb.masters_ptr as usize);
+            push(tcb.tls_len);
+        } else {
+            push(0);
+            push(0);
+            push(0);
+        }
 
-        stack = stack.offset(-1);
-        *stack = argv as usize;
-    }
+        push(mutex as usize);
 
-    // Create a locked mutex, unlocked by pte_osThreadStart
-    let mutex = Box::into_raw(Box::new(2));
-    {
-        let id = Sys::pte_clone(stack);
-        if id < 0 {
-            return PTE_OS_GENERAL_FAILURE;
-        }
+        push(argv as usize);
+        push(entryPoint as usize);
 
-        if id == 0 {
-            // Wait until pte_osThreadStart
-            pte_osMutexLock(mutex);
-            entryPoint(argv);
-            pte_osThreadExit();
-        } else {
-            pte_osMutexLock(&mut pid_mutexes_lock);
-            if pid_mutexes.is_none() {
-                pid_mutexes = Some(BTreeMap::new());
-            }
-            pid_mutexes.as_mut().unwrap().insert(id, mutex);
-            pte_osMutexUnlock(&mut pid_mutexes_lock);
+        push(pte_osThreadShim as usize);
+    }
 
-            pte_osMutexLock(&mut pid_stacks_lock);
-            if pid_stacks.is_none() {
-                pid_stacks = Some(BTreeMap::new());
-            }
-            pid_stacks.as_mut().unwrap().insert(id, (stack_base, stack_size));
-            pte_osMutexUnlock(&mut pid_stacks_lock);
+    let id = Sys::pte_clone(stack);
+    if id < 0 {
+        return PTE_OS_GENERAL_FAILURE;
+    }
 
-            *ppte_osThreadHandle = id;
-        }
+    pte_osMutexLock(&mut pid_mutexes_lock);
+    if pid_mutexes.is_none() {
+        pid_mutexes = Some(BTreeMap::new());
     }
+    pid_mutexes.as_mut().unwrap().insert(id, mutex);
+    pte_osMutexUnlock(&mut pid_mutexes_lock);
+
+    pte_osMutexLock(&mut pid_stacks_lock);
+    if pid_stacks.is_none() {
+        pid_stacks = Some(BTreeMap::new());
+    }
+    pid_stacks.as_mut().unwrap().insert(id, (stack_base, stack_size));
+    pte_osMutexUnlock(&mut pid_stacks_lock);
+
+    *ppte_osThreadHandle = id;
 
     PTE_OS_OK
 }
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
index 09e1d6c3a..a2dfa9667 100644
--- a/src/platform/redox/mod.rs
+++ b/src/platform/redox/mod.rs
@@ -832,9 +832,16 @@ impl Pal for Sys {
             test rax, rax
             jnz .parent
 
-            # Call entry point
-            pop rdi
+            # Load registers
             pop rax
+            pop rdi
+            pop rsi
+            pop rdx
+            pop rcx
+            pop r8
+            pop r9
+
+            # Call entry point
             call rax
 
             # Exit
-- 
GitLab