From c58f0a84a89eaf14f68469e0fed4623bfe8bdbab Mon Sep 17 00:00:00 2001 From: fincs Date: Wed, 8 Nov 2017 18:52:37 +0100 Subject: [PATCH 1/8] Add -z nodynamic-undefined-weak to switch.specs --- buildscripts/lib/switch.specs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildscripts/lib/switch.specs b/buildscripts/lib/switch.specs index 323475f2..5fa72298 100644 --- a/buildscripts/lib/switch.specs +++ b/buildscripts/lib/switch.specs @@ -1,7 +1,7 @@ %rename link old_link *link: -%(old_link) -T switch.ld%s -pie --gc-sections -z text +%(old_link) -T switch.ld%s -pie --gc-sections -z text -z nodynamic-undefined-weak *startfile: switch_crt0%O%s crti%O%s crtbegin%O%s From 757349c5c4a270058aaa9d7fa6780850f4e0abe3 Mon Sep 17 00:00:00 2001 From: fincs Date: Wed, 8 Nov 2017 18:53:06 +0100 Subject: [PATCH 2/8] Add -mtp=soft to ARCH setting --- nx/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nx/Makefile b/nx/Makefile index b398a3b7..e8ec3f88 100644 --- a/nx/Makefile +++ b/nx/Makefile @@ -32,7 +32,7 @@ INCLUDES := include #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -ARCH := -march=armv8-a -fPIC +ARCH := -march=armv8-a -mtp=soft -fPIC CFLAGS := -g -Wall -Werror \ -ffunction-sections \ From f27685b7b38ef115e1eff4147a40e44f5dddd3c2 Mon Sep 17 00:00:00 2001 From: fincs Date: Wed, 8 Nov 2017 18:54:23 +0100 Subject: [PATCH 3/8] Integrate newlib locks with libnx (R)Mutex (untested, incomplete) --- nx/include/switch/kernel/mutex.h | 12 ++++-------- nx/source/kernel/mutex.c | 28 ++++++++++++++-------------- nx/source/system/init.c | 2 +- nx/source/system/newlib.c | 16 ++++++++-------- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/nx/include/switch/kernel/mutex.h b/nx/include/switch/kernel/mutex.h index 05b61b14..dbc79b7d 100644 --- a/nx/include/switch/kernel/mutex.h +++ b/nx/include/switch/kernel/mutex.h @@ -1,13 +1,9 @@ // Copyright 2017 plutoo -typedef struct { - u32 Tag; -} Mutex; +#pragma once +#include -typedef struct { - u32 Owner; - Mutex Lock; - size_t Count; -} RMutex; +typedef _LOCK_T Mutex; +typedef _LOCK_RECURSIVE_T RMutex; void mutexLock(Mutex* m); void mutexUnlock(Mutex* m); diff --git a/nx/source/kernel/mutex.c b/nx/source/kernel/mutex.c index e13f0e35..103108c6 100644 --- a/nx/source/kernel/mutex.c +++ b/nx/source/kernel/mutex.c @@ -11,7 +11,7 @@ static u32 _GetTag() { void mutexLock(Mutex* m) { u32 self = _GetTag(); - u32 cur = __sync_val_compare_and_swap(&m->Tag, 0, self); + u32 cur = __sync_val_compare_and_swap(m, 0, self); while (1) { if (cur == 0) { @@ -26,44 +26,44 @@ void mutexLock(Mutex* m) { if (cur & HAS_LISTENERS) { // The flag is already set, we can use the syscall. - svcArbitrateLock(cur &~ HAS_LISTENERS, &m->Tag, self); + svcArbitrateLock(cur &~ HAS_LISTENERS, (u32*)m, self); } else { // The flag is not set, we need to set it. - u32 old = __sync_val_compare_and_swap(&m->Tag, cur, cur | HAS_LISTENERS); + u32 old = __sync_val_compare_and_swap(m, cur, cur | HAS_LISTENERS); if (old == cur) { // Flag was set successfully. - svcArbitrateLock(cur &~ HAS_LISTENERS, &m->Tag, self); + svcArbitrateLock(cur &~ HAS_LISTENERS, (u32*)m, self); } } - cur = __sync_val_compare_and_swap(&m->Tag, 0, self); + cur = __sync_val_compare_and_swap(m, 0, self); } } void mutexUnlock(Mutex* m) { u32 self = _GetTag(); - u32 old = __sync_val_compare_and_swap(&m->Tag, self, 0); + u32 old = __sync_val_compare_and_swap(m, self, 0); if (old & HAS_LISTENERS) { - svcArbitrateUnlock(&m->Tag); + svcArbitrateUnlock((u32*)m); } } void rmutexLock(RMutex* m) { - if (m->Owner == _GetTag()) { - m->Count++; + if (m->thread_tag == _GetTag()) { + m->counter++; } else { - mutexLock(&m->Lock); - m->Owner = _GetTag(); + mutexLock(&m->lock); + m->thread_tag = _GetTag(); } } void rmutexUnlock(RMutex* m) { - if (--m->Count == 0) { - m->Owner = 0; - mutexUnlock(&m->Lock); + if (--m->counter == 0) { + m->thread_tag = 0; + mutexUnlock(&m->lock); } } diff --git a/nx/source/system/init.c b/nx/source/system/init.c index 1d7d9094..7a832310 100644 --- a/nx/source/system/init.c +++ b/nx/source/system/init.c @@ -63,7 +63,7 @@ void __attribute__((weak)) __libnx_init(void) __libc_init_array(); } -void __attribute__((weak)) NORETURN __libnx_exit(void) +void __attribute__((weak)) NORETURN __libnx_exit(int rc) { // Call destructors. void __libc_fini_array(void); diff --git a/nx/source/system/newlib.c b/nx/source/system/newlib.c index 537f5431..cc9cd572 100644 --- a/nx/source/system/newlib.c +++ b/nx/source/system/newlib.c @@ -4,15 +4,15 @@ #include #include -void __attribute__((weak)) NORETURN __libnx_exit(void); - - -static void NORETURN _ExitImpl(int rc) { - __libnx_exit(); -} +void __attribute__((weak)) NORETURN __libnx_exit(int rc); void newlibSetup() { - void exitImpl(int rc); - __syscalls.exit = _ExitImpl; + // Register newlib syscalls + __syscalls.exit = __libnx_exit; + // Register locking syscalls + __syscalls.lock_acquire = mutexLock; + __syscalls.lock_release = mutexUnlock; + __syscalls.lock_acquire_recursive = rmutexLock; + __syscalls.lock_release_recursive = rmutexUnlock; } From 03674bba858814b0ac8f7454740b784090d06341 Mon Sep 17 00:00:00 2001 From: fincs Date: Wed, 8 Nov 2017 20:37:50 +0100 Subject: [PATCH 4/8] Implement lock_init{_recursive} newlib syscalls --- nx/include/switch/kernel/mutex.h | 12 ++++++++++++ nx/source/system/newlib.c | 2 ++ 2 files changed, 14 insertions(+) diff --git a/nx/include/switch/kernel/mutex.h b/nx/include/switch/kernel/mutex.h index dbc79b7d..e79b9264 100644 --- a/nx/include/switch/kernel/mutex.h +++ b/nx/include/switch/kernel/mutex.h @@ -5,8 +5,20 @@ typedef _LOCK_T Mutex; typedef _LOCK_RECURSIVE_T RMutex; +static inline void mutexInit(Mutex* m) +{ + *m = 0; +} + void mutexLock(Mutex* m); void mutexUnlock(Mutex* m); +static inline void rmutexInit(RMutex* m) +{ + m->lock = 0; + m->thread_tag = 0; + m->counter = 0; +} + void rmutexLock(RMutex* m); void rmutexUnlock(RMutex* m); diff --git a/nx/source/system/newlib.c b/nx/source/system/newlib.c index cc9cd572..876d842b 100644 --- a/nx/source/system/newlib.c +++ b/nx/source/system/newlib.c @@ -11,8 +11,10 @@ void newlibSetup() { __syscalls.exit = __libnx_exit; // Register locking syscalls + __syscalls.lock_init = mutexInit; __syscalls.lock_acquire = mutexLock; __syscalls.lock_release = mutexUnlock; + __syscalls.lock_init_recursive = rmutexInit; __syscalls.lock_acquire_recursive = rmutexLock; __syscalls.lock_release_recursive = rmutexUnlock; } From e836ee7bb9a61b3f5a81e75cc1c2c12ac670d75a Mon Sep 17 00:00:00 2001 From: fincs Date: Tue, 14 Nov 2017 00:29:28 +0100 Subject: [PATCH 5/8] Implement TLS/newlib reent support (untested) --- nx/include/switch/kernel/thread.h | 2 - nx/include/switch/result.h | 1 + nx/source/internal.h | 24 ++++++++++++ nx/source/kernel/thread.c | 61 +++++++++++++++++++++++++++---- nx/source/system/newlib.c | 28 +++++++++++++- nx/source/system/readtp.s | 10 +++++ 6 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 nx/source/internal.h create mode 100644 nx/source/system/readtp.s diff --git a/nx/include/switch/kernel/thread.h b/nx/include/switch/kernel/thread.h index 1b379166..ae80db52 100644 --- a/nx/include/switch/kernel/thread.h +++ b/nx/include/switch/kernel/thread.h @@ -1,7 +1,5 @@ typedef struct { Handle handle; - ThreadFunc entry; - void* arg; void* stack_mem; void* stack_mirror; size_t stack_sz; diff --git a/nx/include/switch/result.h b/nx/include/switch/result.h index 59d21f6b..b2850551 100644 --- a/nx/include/switch/result.h +++ b/nx/include/switch/result.h @@ -29,4 +29,5 @@ #define LIBNX_NOTFOUND 8 #define LIBNX_IOERROR 9 #define LIBNX_BADINPUT 10 +#define LIBNX_BADREENT 11 #define LIBNX_PARCEL_ERRBASE 100 diff --git a/nx/source/internal.h b/nx/source/internal.h new file mode 100644 index 00000000..209eb9cd --- /dev/null +++ b/nx/source/internal.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include + +#define THREADVARS_MAGIC 0x21545624 // !TV$ + +// This structure is exactly 0x20 bytes, if more is needed modify getThreadVars() below +typedef struct { + // Magic value used to check if the struct is initialized + u32 magic; + + // Pointer to the current thread (if exists) + Thread* thread_ptr; + + // Pointer to this thread's newlib state + struct _reent* reent; + + // Pointer to this thread's thread-local segment + void* tls_tp; // !! Offset needs to be TLS+0x1F8 for __aarch64_read_tp !! +} ThreadVars; + +static inline ThreadVars* getThreadVars(void) { + return (ThreadVars*)((u8*)armGetTls() + 0x1E0); +} diff --git a/nx/source/kernel/thread.c b/nx/source/kernel/thread.c index 20663e24..5fa94e60 100644 --- a/nx/source/kernel/thread.c +++ b/nx/source/kernel/thread.c @@ -1,9 +1,32 @@ // Copyright 2017 plutoo #include #include +#include "../internal.h" -static void _EntryWrap(Thread* t) { - t->entry(t->arg); +extern const u8 __tdata_lma[]; +extern const u8 __tdata_lma_end[]; +extern u8 __tls_start[]; +extern u8 __tls_end[]; + +typedef struct { + Thread* t; + ThreadFunc entry; + void* arg; + struct _reent* reent; + void* tls; + void* padding; +} ThreadEntryArgs; + +static void _EntryWrap(ThreadEntryArgs* args) { + // Initialize thread vars + ThreadVars* tv = getThreadVars(); + tv->magic = THREADVARS_MAGIC; + tv->thread_ptr = args->t; + tv->reent = args->reent; + tv->tls_tp = (u8*)args->tls-2*sizeof(void*); // subtract size of Thread Control Block (TCB) + + // Launch thread entrypoint + args->entry(args->arg); svcExitThread(); } @@ -11,8 +34,12 @@ Result threadCreate( Thread* t, ThreadFunc entry, void* arg, size_t stack_sz, int prio, int cpuid) { + stack_sz = (stack_sz+0xF) &~ 0xF; + Result rc = 0; - void* stack = memalign(0x1000, stack_sz); + size_t reent_sz = (sizeof(struct _reent)+0xF) &~ 0xF; + size_t tls_sz = (__tls_end-__tls_start+0xF) &~ 0xF; + void* stack = memalign(0x1000, stack_sz + reent_sz + tls_sz); if (stack == NULL) { rc = MAKERESULT(MODULE_LIBNX, LIBNX_OUTOFMEM); @@ -24,21 +51,41 @@ Result threadCreate( if (R_SUCCEEDED(rc)) { - u64 stack_top = ((u64)stack_mirror) + t->stack_sz; + u64 stack_top = ((u64)stack_mirror) + t->stack_sz - sizeof(ThreadEntryArgs); + ThreadEntryArgs* args = (ThreadEntryArgs*) stack_top; Handle handle; rc = svcCreateThread( - &handle, (ThreadFunc) &_EntryWrap, (void*) t, (void*) stack_top, + &handle, (ThreadFunc) &_EntryWrap, args, (void*)stack_top, prio, cpuid); if (R_SUCCEEDED(rc)) { t->handle = handle; - t->entry = entry; - t->arg = arg; t->stack_mem = stack; t->stack_mirror = stack_mirror; t->stack_sz = stack_sz; + + args->t = t; + args->entry = entry; + args->arg = arg; + args->reent = (struct _reent*)((u8*)stack + stack_sz); + args->tls = (u8*)stack + stack_sz + reent_sz; + + // Set up child thread's reent struct, inheriting standard file handles + _REENT_INIT_PTR(args->reent); + struct _reent* cur = getThreadVars()->reent; + args->reent->_stdin = cur->_stdin; + args->reent->_stdout = cur->_stdout; + args->reent->_stderr = cur->_stderr; + + // Set up child thread's TLS segment + size_t tls_load_sz = __tdata_lma_end-__tdata_lma; + size_t tls_bss_sz = tls_sz - tls_load_sz; + if (tls_load_sz) + memcpy(args->tls, __tdata_lma, tls_load_sz); + if (tls_bss_sz) + memset(args->tls+tls_load_sz, 0, tls_bss_sz); } if (R_FAILED(rc)) { diff --git a/nx/source/system/newlib.c b/nx/source/system/newlib.c index 876d842b..8d2776c5 100644 --- a/nx/source/system/newlib.c +++ b/nx/source/system/newlib.c @@ -3,12 +3,27 @@ #include #include #include +#include "../internal.h" void __attribute__((weak)) NORETURN __libnx_exit(int rc); +extern const u8 __tdata_lma[]; +extern const u8 __tdata_lma_end[]; +extern u8 __tls_start[]; + +static struct _reent* __libnx_get_reent() { + ThreadVars* tv = getThreadVars(); + if (tv->magic != THREADVARS_MAGIC) { + fatalSimple(MAKERESULT(MODULE_LIBNX, LIBNX_BADREENT)); + for (;;); + } + return tv->reent; +} + void newlibSetup() { // Register newlib syscalls - __syscalls.exit = __libnx_exit; + __syscalls.exit = __libnx_exit; + __syscalls.getreent = __libnx_get_reent; // Register locking syscalls __syscalls.lock_init = mutexInit; @@ -17,4 +32,15 @@ void newlibSetup() { __syscalls.lock_init_recursive = rmutexInit; __syscalls.lock_acquire_recursive = rmutexLock; __syscalls.lock_release_recursive = rmutexUnlock; + + // Initialize thread vars for the main thread + ThreadVars* tv = getThreadVars(); + tv->magic = THREADVARS_MAGIC; + tv->thread_ptr = NULL; + tv->reent = _impure_ptr; + tv->tls_tp = __tls_start-2*sizeof(void*); // subtract size of Thread Control Block (TCB) + + u32 tls_size = __tdata_lma_end - __tdata_lma; + if (tls_size) + memcpy(__tls_start, __tdata_lma, tls_size); } diff --git a/nx/source/system/readtp.s b/nx/source/system/readtp.s new file mode 100644 index 00000000..1d2065d2 --- /dev/null +++ b/nx/source/system/readtp.s @@ -0,0 +1,10 @@ + .section .text.__aarch64_read_tp, "ax", %progbits + .global __aarch64_read_tp + .type __aarch64_read_tp, %function + .align 2 + .cfi_startproc +__aarch64_read_tp: + mrs x0, tpidrro_el0 + ldr x0, [x0, #0x1F8] + ret + .cfi_endproc From 508a87ea5dce6503fd4b2ba4dd6ecfd664248575 Mon Sep 17 00:00:00 2001 From: fincs Date: Tue, 14 Nov 2017 01:14:23 +0100 Subject: [PATCH 6/8] Mark fatalSimple with __attribute__((noreturn)) --- nx/include/switch/services/fatal.h | 2 +- nx/source/services/fatal.c | 1 + nx/source/system/newlib.c | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/nx/include/switch/services/fatal.h b/nx/include/switch/services/fatal.h index 59ddb6c1..f572dec6 100644 --- a/nx/include/switch/services/fatal.h +++ b/nx/include/switch/services/fatal.h @@ -1 +1 @@ -void fatalSimple(Result err); +__attribute__((noreturn)) void fatalSimple(Result err); diff --git a/nx/source/services/fatal.c b/nx/source/services/fatal.c index d99e23ff..54698999 100644 --- a/nx/source/services/fatal.c +++ b/nx/source/services/fatal.c @@ -36,4 +36,5 @@ void fatalSimple(Result err) { } ((void(*)())0xBADC0DE)(); + __builtin_unreachable(); } diff --git a/nx/source/system/newlib.c b/nx/source/system/newlib.c index 8d2776c5..4404a984 100644 --- a/nx/source/system/newlib.c +++ b/nx/source/system/newlib.c @@ -13,10 +13,8 @@ extern u8 __tls_start[]; static struct _reent* __libnx_get_reent() { ThreadVars* tv = getThreadVars(); - if (tv->magic != THREADVARS_MAGIC) { + if (tv->magic != THREADVARS_MAGIC) fatalSimple(MAKERESULT(MODULE_LIBNX, LIBNX_BADREENT)); - for (;;); - } return tv->reent; } From 5e0b10db70b9320933fef37f05587398c83c1eea Mon Sep 17 00:00:00 2001 From: fincs Date: Tue, 14 Nov 2017 01:14:36 +0100 Subject: [PATCH 7/8] Page-align stack size in threadCreate --- nx/source/kernel/thread.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nx/source/kernel/thread.c b/nx/source/kernel/thread.c index 5fa94e60..d99bc366 100644 --- a/nx/source/kernel/thread.c +++ b/nx/source/kernel/thread.c @@ -8,6 +8,7 @@ extern const u8 __tdata_lma_end[]; extern u8 __tls_start[]; extern u8 __tls_end[]; +// Thread creation args; keep this struct's size 16-byte aligned typedef struct { Thread* t; ThreadFunc entry; @@ -34,7 +35,7 @@ Result threadCreate( Thread* t, ThreadFunc entry, void* arg, size_t stack_sz, int prio, int cpuid) { - stack_sz = (stack_sz+0xF) &~ 0xF; + stack_sz = (stack_sz+0xFFF) &~ 0xFFF; Result rc = 0; size_t reent_sz = (sizeof(struct _reent)+0xF) &~ 0xF; From 00b5758912257ad3266a38adae126861261028c1 Mon Sep 17 00:00:00 2001 From: fincs Date: Tue, 14 Nov 2017 19:33:29 +0100 Subject: [PATCH 8/8] Fix whitespace in fatal.c --- nx/source/services/fatal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nx/source/services/fatal.c b/nx/source/services/fatal.c index 54698999..f27d34e0 100644 --- a/nx/source/services/fatal.c +++ b/nx/source/services/fatal.c @@ -36,5 +36,5 @@ void fatalSimple(Result err) { } ((void(*)())0xBADC0DE)(); - __builtin_unreachable(); + __builtin_unreachable(); }