Skip to content

Commit

Permalink
Apply fixup to cthread initialization (#301)
Browse files Browse the repository at this point in the history
Cosmopolitan Threads are currently Linux-only (with some NetBSD
and Windows support too!). This change ensures we only initialize
the high-level threading runtime when Cosmopolitan Threads are used.
  • Loading branch information
jart committed Oct 25, 2021
1 parent 45a7435 commit 91d7833
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 9 deletions.
1 change: 0 additions & 1 deletion libc/runtime/cosmo.S
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ cosmo: push %rbp
pop %rax
#endif
call _init
call _main_thread_init # FIXME: use .init.start macro
ezlea __init_array_start,ax # static ctors in forward order
.weak __init_array_start # could be called multiple times
ezlea __init_array_end,cx # idempotency recommended
Expand Down
9 changes: 4 additions & 5 deletions libc/thread/create.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,17 @@
#include "libc/sysv/consts/prot.h"
#include "libc/thread/create.h"

STATIC_YOINK("_main_thread_ctor");

// TLS boundaries
extern char _tbss_start, _tbss_end, _tdata_start, _tdata_end;

static cthread_t _thread_allocate(const cthread_attr_t* attr) {
//extern void _main_thread_init(void);
//void (*dummy)(void) = &_main_thread_init;
//asm(""::"r"(dummy));
size_t stacksize = attr->stacksize;
size_t guardsize = attr->guardsize;
size_t tbsssize = &_tbss_end - &_tbss_start;
size_t tbsssize = &_tbss_end - &_tbss_start;
size_t tdatasize = &_tdata_end - &_tdata_start;
size_t tlssize = tbsssize + tdatasize;
size_t tlssize = tbsssize + tdatasize;

size_t totalsize =
3 * guardsize + stacksize + tlssize + sizeof(struct cthread_descriptor_t);
Expand Down
12 changes: 9 additions & 3 deletions libc/runtime/cthread_init.c → libc/thread/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
Expand All @@ -27,10 +28,11 @@
// TLS boundaries
extern char _tbss_start, _tbss_end, _tdata_start, _tdata_end;

void _main_thread_init(void) {
size_t tbsssize = &_tbss_end - &_tbss_start;
static textstartup void _main_thread_init(void) {
if (!IsLinux()) return; /* TODO */
size_t tbsssize = &_tbss_end - &_tbss_start;
size_t tdatasize = &_tdata_end - &_tdata_start;
size_t tlssize = tbsssize + tdatasize;
size_t tlssize = tbsssize + tdatasize;
size_t totalsize = tlssize + sizeof(struct cthread_descriptor_t);
totalsize = (totalsize + PAGESIZE - 1) & -PAGESIZE;

Expand Down Expand Up @@ -66,3 +68,7 @@ void _main_thread_init(void) {
abort();
}
}

const void* const _main_thread_ctor[] initarray = {
_main_thread_init,
};

4 comments on commit 91d7833

@lemaitre
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jart We need to be careful that thread local variables should still work without cthread (%fs should be set correctly). Maybe we use the .tdata and .tbss directly in that case. It would add the benefit to never fail.

Another solution would be to tell the compiler that thread locals are just plain old globals, but I don't know how to do it.

@jart
Copy link
Owner Author

@jart jart commented on 91d7833 Oct 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lemaitre Programs that need TLS can work around it for the time being by using the static yoink. Keep in mind thread locals are going to be problematic in general. Other operating systems don't let us change the segment registers. Worst of all, x86-64-linux-gnu doesn't have a flag for generating TLS code that doesn't assume %fs is configured. I've considered solving the problem by having tool/build/package.c rewrite binary objects post-compilation to change the TLS instructions into something more manageable. But that only helps code in the Cosmopolitan codebase. We might have to rewrite the binary in memory at runtime using third_party/xed/ in order to fully support TLS.

@lemaitre
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't care much about TLS performance, we could most likely tell gcc to generate thread locals for the dynamic local model in order to have a call to __tls_get_addr in which we can have the magic we need.
Also, we can tell gcc that object is relative to either %fs or %gs with an attribute (even though, it is not TLS as far as the compiler is aware).

Regarding to other systems, I think it will be manageable in Windows : https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadcontext
I don't know about BSD or MacOS.

@jart
Copy link
Owner Author

@jart jart commented on 91d7833 Oct 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do those flags exist and do they work? Where does Windows store the TLS data? I know for example it needs %gs for its PEB internals. XNU lets us change %gs but not %fs. OpenBSD lets us change %fs but not %gs. FreeBSD and Linux let us set either. Haven't checked NetBSD yet. If we can figure out a way to control a segment register on Windows, then we can have fast TLS on all platforms. But we'd need to have Xed change the 0x64 segment prefixes to 0x65 at startup on XNU. That should be reasonably inexpensive since it doesn't need to relocate instructions.

Please sign in to comment.