Skip to content

Commit 2749732

Browse files
committed
Add support for TLS imports/exports to/from DSOs
This change is based on the recently added EXPORT_INFO metadata that tells the dynamic linker which exports are TLS exports and which are normal data exports: https://reviews.llvm.org/D108877
1 parent 558b9f6 commit 2749732

13 files changed

+212
-48
lines changed

emcc.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -1623,7 +1623,15 @@ def default_setting(name, new_default):
16231623
settings.EXPORT_ALL = 1
16241624

16251625
if settings.MAIN_MODULE:
1626-
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$getDylinkMetadata', '$mergeLibSymbols']
1626+
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [
1627+
'$getDylinkMetadata',
1628+
'$mergeLibSymbols',
1629+
]
1630+
1631+
if settings.USE_PTHREADS:
1632+
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [
1633+
'$registerTlsInit',
1634+
]
16271635

16281636
if settings.RELOCATABLE:
16291637
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [

src/library_dylink.js

+16-16
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ var LibraryDylink = {
6868
},
6969

7070
$updateGOT__deps: ['$GOT', '$isInternalSym'],
71-
$updateGOT: function(exports) {
71+
$updateGOT: function(exports, replace) {
7272
#if DYLINK_DEBUG
7373
err("updateGOT: " + Object.keys(exports).length);
7474
#endif
@@ -77,7 +77,6 @@ var LibraryDylink = {
7777
continue;
7878
}
7979

80-
var replace = false;
8180
var value = exports[symName];
8281
#if !WASM_BIGINT
8382
if (symName.startsWith('orig$')) {
@@ -117,7 +116,7 @@ var LibraryDylink = {
117116

118117
// Applies relocations to exported things.
119118
$relocateExports__deps: ['$updateGOT'],
120-
$relocateExports: function(exports, memoryBase) {
119+
$relocateExports: function(exports, memoryBase, replace) {
121120
var relocated = {};
122121

123122
for (var e in exports) {
@@ -139,7 +138,7 @@ var LibraryDylink = {
139138
}
140139
relocated[e] = value;
141140
}
142-
updateGOT(relocated);
141+
updateGOT(relocated, replace);
143142
return relocated;
144143
},
145144

@@ -302,7 +301,7 @@ var LibraryDylink = {
302301
name = getString();
303302
}
304303

305-
var customSection = { neededDynlibs: [] };
304+
var customSection = { neededDynlibs: [], tlsExports: {} };
306305
if (name == 'dylink') {
307306
customSection.memorySize = getLEB();
308307
customSection.memoryAlign = getLEB();
@@ -320,6 +319,8 @@ var LibraryDylink = {
320319
assert(name === 'dylink.0');
321320
var WASM_DYLINK_MEM_INFO = 0x1;
322321
var WASM_DYLINK_NEEDED = 0x2;
322+
var WASM_DYLINK_EXPORT_INFO = 0x3;
323+
var WASM_SYMBOL_TLS = 0x100;
323324
while (offset < end) {
324325
var subsectionType = getU8();
325326
var subsectionSize = getLEB();
@@ -334,6 +335,15 @@ var LibraryDylink = {
334335
var name = getString();
335336
customSection.neededDynlibs.push(name);
336337
}
338+
} else if (subsectionType === WASM_DYLINK_EXPORT_INFO) {
339+
var count = getLEB();
340+
while (count--) {
341+
var name = getString();
342+
var flags = getLEB();
343+
if (flags & WASM_SYMBOL_TLS) {
344+
customSection.tlsExports[name] = 1;
345+
}
346+
}
337347
} else {
338348
#if ASSERTIONS
339349
err('unknown dylink.0 subsection: ' + subsectionType)
@@ -529,17 +539,7 @@ var LibraryDylink = {
529539
#if USE_PTHREADS
530540
// Only one thread (currently The main thread) should call
531541
// __wasm_call_ctors, but all threads need to call emscripten_tls_init
532-
var initTLS = moduleExports['emscripten_tls_init'];
533-
#if ASSERTIONS
534-
assert(initTLS);
535-
#endif
536-
#if DYLINK_DEBUG
537-
out("adding to tlsInitFunctions: " + initTLS);
538-
#endif
539-
PThread.tlsInitFunctions.push(initTLS);
540-
if (runtimeInitialized) {
541-
initTLS();
542-
}
542+
registerTlsInit(moduleExports['emscripten_tls_init'], instance.exports, metadata)
543543
if (!ENVIRONMENT_IS_PTHREAD) {
544544
#endif
545545
var init = moduleExports['__wasm_call_ctors'];

src/library_pthread.js

+34
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,40 @@ var LibraryPThread = {
490490
}
491491
},
492492

493+
$registerTlsInit: function(tlsInitFunc, moduleExports, metadata) {
494+
#if DYLINK_DEBUG
495+
out("registerTlsInit: " + tlsInitFunc);
496+
#endif
497+
#if RELOCATABLE
498+
// In relocatable builds, we use the result of calling tlsInitFunc
499+
// (`emscripten_tls_init`) to relocate the TLS exports of the module
500+
// according to this new __tls_base.
501+
function tlsInitWrapper() {
502+
var __tls_base = tlsInitFunc();
503+
#if DYLINK_DEBUG
504+
err('tlsInit -> ' + __tls_base);
505+
#endif
506+
for (var sym in metadata.tlsExports) {
507+
metadata.tlsExports[sym] = moduleExports[sym];
508+
}
509+
relocateExports(metadata.tlsExports, __tls_base, /*replace=*/true);
510+
}
511+
512+
// Register this function so that its gets called for each thread on
513+
// startup.
514+
PThread.tlsInitFunctions.push(tlsInitWrapper);
515+
516+
// If the main thread is already running we also need to call this function
517+
// now. If the main thread is not yet running this will happen when it
518+
// is initializes and processes `PThread.tlsInitFunctions`.
519+
if (runtimeInitialized) {
520+
tlsInitWrapper();
521+
}
522+
#else
523+
PThread.tlsInitFunctions.push(tlsInitFunc);
524+
#endif
525+
},
526+
493527
$cancelThread: function(pthread_ptr) {
494528
if (ENVIRONMENT_IS_PTHREAD) throw 'Internal Error! cancelThread() can only ever be called from main application thread!';
495529
if (!pthread_ptr) throw 'Internal Error! Null pthread_ptr in cancelThread!';

src/preamble.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -970,15 +970,23 @@ function createWasm() {
970970
Module['asm'] = exports;
971971

972972
#if MAIN_MODULE
973-
#if AUTOLOAD_DYLIBS
974973
var metadata = getDylinkMetadata(module);
974+
#if AUTOLOAD_DYLIBS
975975
if (metadata.neededDynlibs) {
976976
dynamicLibraries = metadata.neededDynlibs.concat(dynamicLibraries);
977977
}
978978
#endif
979979
mergeLibSymbols(exports, 'main')
980980
#endif
981981

982+
#if USE_PTHREADS
983+
#if MAIN_MODULE
984+
registerTlsInit(Module['asm']['emscripten_tls_init'], instance.exports, metadata);
985+
#else
986+
registerTlsInit(Module['asm']['emscripten_tls_init']);
987+
#endif
988+
#endif
989+
982990
#if !IMPORTED_MEMORY
983991
wasmMemory = Module['asm']['memory'];
984992
#if ASSERTIONS
@@ -1015,7 +1023,6 @@ function createWasm() {
10151023
exportAsmFunctions(exports);
10161024
#endif
10171025
#if USE_PTHREADS
1018-
PThread.tlsInitFunctions.push(Module['asm']['emscripten_tls_init']);
10191026
// We now have the Wasm module loaded up, keep a reference to the compiled module so we can post it to the workers.
10201027
wasmModule = module;
10211028
// Instantiation is synchronous in pthreads and we assert on run dependencies.

system/lib/pthread/emscripten_tls_init.c

+9-7
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,17 @@ static void free_tls(void* tls_block) {
2828
emscripten_builtin_free(tls_block);
2929
}
3030

31-
void emscripten_tls_init(void) {
31+
void* emscripten_tls_init(void) {
3232
size_t tls_size = __builtin_wasm_tls_size();
3333
size_t tls_align = __builtin_wasm_tls_align();
34-
if (tls_size) {
35-
void *tls_block = emscripten_builtin_memalign(tls_align, tls_size);
34+
if (!tls_size) {
35+
return NULL;
36+
}
37+
void *tls_block = emscripten_builtin_memalign(tls_align, tls_size);
3638
#ifdef DEBUG_TLS
37-
printf("tls init: thread[%p] dso[%p] -> %p\n", pthread_self(), &__dso_handle, tls_block);
39+
printf("tls init: thread[%p] dso[%p] -> %p\n", pthread_self(), &__dso_handle, tls_block);
3840
#endif
39-
__wasm_init_tls(tls_block);
40-
__cxa_thread_atexit(free_tls, tls_block, &__dso_handle);
41-
}
41+
__wasm_init_tls(tls_block);
42+
__cxa_thread_atexit(free_tls, tls_block, &__dso_handle);
43+
return tls_block;
4244
}

tests/core/pthread/test_pthread_dylink_tls.c

+16-3
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,36 @@
33
#include <pthread.h>
44

55
int get_side_tls();
6+
int get_side_tls2();
67
int* get_side_tls_address();
8+
int* get_side_tls_address2();
79

8-
static __thread int main_tls = 10;
10+
__thread int main_tls = 10;
11+
__thread int main_tls2 = 11;
12+
extern __thread int side_tls;
13+
extern __thread int side_tls2;
914

1015
int get_main_tls() {
1116
return main_tls;
1217
}
1318

19+
int get_main_tls2() {
20+
return main_tls2;
21+
}
22+
1423
int* get_main_tls_address() {
1524
return &main_tls;
1625
}
1726

1827
void report_tls() {
1928
//printf("side_tls address: %p\n", get_side_tls_address());
20-
printf("side_tls value : %d\n", get_side_tls());
29+
printf("side_tls value : %d %d\n", get_side_tls(), get_side_tls2());
30+
printf("side_tls direct : %d %d\n", side_tls, side_tls2);
2131
//printf("main_tls address: %p\n", get_main_tls_address());
22-
printf("main_tls value : %d\n", get_main_tls());
32+
printf("main_tls value : %d %d\n", get_main_tls(), get_main_tls2());
33+
printf("main_tls direct : %d %d\n", main_tls, main_tls2);
34+
assert(get_side_tls() == side_tls);
35+
assert(get_side_tls2() == side_tls2);
2336
}
2437

2538
void test_tls(int inc) {
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
thread: 1
2-
side_tls value : 42
3-
main_tls value : 10
2+
side_tls value : 42 43
3+
side_tls direct : 42 43
4+
main_tls value : 10 11
5+
main_tls direct : 10 11
46
increment done
5-
side_tls value : 62
6-
main_tls value : 30
7+
side_tls value : 62 43
8+
side_tls direct : 62 43
9+
main_tls value : 30 11
10+
main_tls direct : 30 11
711

812
thread: 2
9-
side_tls value : 42
10-
main_tls value : 10
13+
side_tls value : 42 43
14+
side_tls direct : 42 43
15+
main_tls value : 10 11
16+
main_tls direct : 10 11
1117
increment done
12-
side_tls value : 52
13-
main_tls value : 20
18+
side_tls value : 52 43
19+
side_tls direct : 52 43
20+
main_tls value : 20 11
21+
main_tls direct : 20 11
1422

1523
success
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
static __thread int mytls = 42;
1+
__thread int side_tls = 42;
2+
__thread int side_tls2 = 43;
23

34
int get_side_tls() {
4-
return mytls;
5+
return side_tls;
6+
}
7+
8+
int get_side_tls2() {
9+
return side_tls2;
510
}
611

712
int* get_side_tls_address() {
8-
return &mytls;
13+
return &side_tls;
14+
}
15+
16+
int* get_side_tls_address2() {
17+
return &side_tls2;
918
}

tests/core/test_dylink_tls_export.c

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <threads.h>
2+
#include <stdio.h>
3+
4+
thread_local int foo = 10;
5+
thread_local int bar = 11;
6+
extern thread_local int baz;
7+
extern thread_local int wiz;
8+
9+
void sidey();
10+
11+
int get_foo() {
12+
return foo;
13+
}
14+
15+
int get_bar() {
16+
return bar;
17+
}
18+
19+
int get_baz() {
20+
return baz;
21+
}
22+
23+
int get_wiz() {
24+
return wiz;
25+
}
26+
27+
int main(int argc, char const *argv[]) {
28+
printf("main: foo=%d\n", get_foo());
29+
printf("main: bar=%d\n", get_bar());
30+
printf("main: baz=%d\n", get_baz());
31+
printf("main: wiz=%d\n", get_wiz());
32+
sidey();
33+
return 0;
34+
}

tests/core/test_dylink_tls_export.out

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
main: foo=10
2+
main: bar=11
3+
main: baz=42
4+
main: wiz=43
5+
side: foo=10
6+
side: bar=11
7+
side: baz=42
8+
side: wiz=43
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <threads.h>
2+
#include <stdio.h>
3+
#include <threads.h>
4+
5+
thread_local int baz = 42;
6+
thread_local int wiz = 43;
7+
extern thread_local int foo;
8+
extern thread_local int bar;
9+
10+
int get_foo() {
11+
return foo;
12+
}
13+
14+
int get_bar() {
15+
return bar;
16+
}
17+
18+
int get_baz() {
19+
return baz;
20+
}
21+
22+
int get_wiz() {
23+
return wiz;
24+
}
25+
26+
void sidey() {
27+
printf("side: foo=%d\n", get_foo());
28+
printf("side: bar=%d\n", get_bar());
29+
printf("side: baz=%d\n", get_baz());
30+
printf("side: wiz=%d\n", get_wiz());
31+
}

tests/test_core.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -4588,16 +4588,17 @@ def test_dylink_weak(self):
45884588
@node_pthreads
45894589
@needs_dylink
45904590
def test_dylink_tls(self):
4591-
# We currently can't export TLS symbols from module since we don't have
4592-
# and ABI for signaling which exports are TLS and which are regular
4593-
# data exports.
4594-
4595-
# TODO(sbc): Add tests that depend on importing/exported TLS symbols
4596-
# once we figure out how to do that.
45974591
self.emcc_args.append('-Wno-experimental')
45984592
self.dylink_testf(test_file('core/test_dylink_tls.c'),
45994593
need_reverse=False)
46004594

4595+
@node_pthreads
4596+
@needs_dylink
4597+
def test_dylink_tls_export(self):
4598+
self.emcc_args.append('-Wno-experimental')
4599+
self.dylink_testf(test_file('core/test_dylink_tls_export.c'),
4600+
need_reverse=False)
4601+
46014602
def test_random(self):
46024603
src = r'''#include <stdlib.h>
46034604
#include <stdio.h>

0 commit comments

Comments
 (0)