Skip to content

Commit 2ee6956

Browse files
committed
Add support for TLS imports/exports to/from DSOs
1 parent 8971e6f commit 2ee6956

9 files changed

+188
-46
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

+54-30
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

@@ -256,37 +255,46 @@ var LibraryDylink = {
256255
// returns the side module metadata as an object
257256
// { memorySize, memoryAlign, tableSize, tableAlign, neededDynlibs}
258257
$getDylinkMetadata: function(binary) {
259-
var next = 0;
258+
var offset = 0;
259+
var end = 0;
260+
261+
function getU8() {
262+
return binary[offset++];
263+
}
264+
260265
function getLEB() {
261266
var ret = 0;
262267
var mul = 1;
263268
while (1) {
264-
var byte = binary[next++];
269+
var byte = binary[offset++];
265270
ret += ((byte & 0x7f) * mul);
266271
mul *= 0x80;
267272
if (!(byte & 0x80)) break;
268273
}
269274
return ret;
270275
}
271276

277+
function getString() {
278+
var len = getLEB();
279+
offset += len;
280+
return UTF8ArrayToString(binary, offset - len, len);
281+
}
282+
272283
if (binary instanceof WebAssembly.Module) {
273284
var dylinkSection = WebAssembly.Module.customSections(binary, "dylink");
274285
assert(dylinkSection.length != 0, 'need dylink section');
275286
binary = new Uint8Array(dylinkSection[0]);
287+
end = binary.length
276288
} else {
277289
var int32View = new Uint32Array(new Uint8Array(binary.subarray(0, 24)).buffer);
278290
assert(int32View[0] == 0x6d736100, 'need to see wasm magic number'); // \0asm
279291
// we should see the dylink section right after the magic number and wasm version
280292
assert(binary[8] === 0, 'need the dylink section to be first')
281-
next = 9;
282-
getLEB(); //section size
283-
assert(binary[next] === 6); next++; // size of "dylink" string
284-
assert(binary[next] === 'd'.charCodeAt(0)); next++;
285-
assert(binary[next] === 'y'.charCodeAt(0)); next++;
286-
assert(binary[next] === 'l'.charCodeAt(0)); next++;
287-
assert(binary[next] === 'i'.charCodeAt(0)); next++;
288-
assert(binary[next] === 'n'.charCodeAt(0)); next++;
289-
assert(binary[next] === 'k'.charCodeAt(0)); next++;
293+
offset = 9;
294+
var section_size = getLEB(); //section size
295+
end = offset + section_size;
296+
var name = getString();
297+
assert(name == 'dylink');
290298
}
291299

292300
var customSection = {};
@@ -304,12 +312,38 @@ var LibraryDylink = {
304312
var neededDynlibsCount = getLEB();
305313
customSection.neededDynlibs = [];
306314
for (var i = 0; i < neededDynlibsCount; ++i) {
307-
var nameLen = getLEB();
308-
var nameUTF8 = binary.subarray(next, next + nameLen);
309-
next += nameLen;
310-
var name = UTF8ArrayToString(nameUTF8, 0);
315+
var name = getString();
311316
customSection.neededDynlibs.push(name);
312317
}
318+
319+
#if DYLINK_DEBUG
320+
err('dylink needed:' + customSection.neededDynlibs);
321+
#endif
322+
customSection.tlsExports = {};
323+
324+
var WASM_DYLINK_EXPORT_INFO = 0x1;
325+
var WASM_DYLINK_IMPORT_INFO = 0x2;
326+
var WASM_SYMBOL_TLS = 0x100;
327+
328+
while (offset < end) {
329+
var subsectionType = getU8();
330+
var subsectionSize = getLEB();
331+
if (subsectionType === WASM_DYLINK_EXPORT_INFO) {
332+
var count = getLEB();
333+
while (count--) {
334+
var name = getString();
335+
var flags = getLEB();
336+
if (flags & WASM_SYMBOL_TLS) {
337+
customSection.tlsExports[name] = 1;
338+
}
339+
}
340+
} else {
341+
err('unknown subsection: ' + subsectionType)
342+
// unknown subsection
343+
offset += subsectionSize;
344+
}
345+
}
346+
assert(offset == end);
313347
return customSection;
314348
},
315349

@@ -485,17 +519,7 @@ var LibraryDylink = {
485519
#if USE_PTHREADS
486520
// Only one thread (currently The main thread) should call
487521
// __wasm_call_ctors, but all threads need to call emscripten_tls_init
488-
var initTLS = moduleExports['emscripten_tls_init'];
489-
#if ASSERTIONS
490-
assert(initTLS);
491-
#endif
492-
#if DYLINK_DEBUG
493-
out("adding to tlsInitFunctions: " + initTLS);
494-
#endif
495-
PThread.tlsInitFunctions.push(initTLS);
496-
if (runtimeInitialized) {
497-
initTLS();
498-
}
522+
registerTlsInit(instance.exports, metadata)
499523
if (!ENVIRONMENT_IS_PTHREAD) {
500524
#endif
501525
var init = moduleExports['__wasm_call_ctors'];

src/library_pthread.js

+27
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,33 @@ var LibraryPThread = {
470470
}
471471
},
472472

473+
$registerTlsInit: function(moduleExports, metadata) {
474+
var emscripten_tls_init = moduleExports['emscripten_tls_init'];
475+
assert(emscripten_tls_init);
476+
#if DYLINK_DEBUG
477+
out("adding to tlsInitFunctions: " + emscripten_tls_init);
478+
#endif
479+
#if RELOCATABLE
480+
function tlsInint() {
481+
var __tls_base = emscripten_tls_init();
482+
#if DYLINK_DEBUG
483+
err('tlsInint -> ' + __tls_base);
484+
#endif
485+
for (var sym in metadata.tlsExports) {
486+
metadata.tlsExports[sym] = moduleExports[sym];
487+
}
488+
relocateExports(metadata.tlsExports, __tls_base, true);
489+
}
490+
491+
PThread.tlsInitFunctions.push(tlsInint);
492+
if (runtimeInitialized) {
493+
tlsInint();
494+
}
495+
#else
496+
PThread.tlsInitFunctions.push(emscripten_tls_init);
497+
#endif
498+
},
499+
473500
$cancelThread: function(pthread_ptr) {
474501
if (ENVIRONMENT_IS_PTHREAD) throw 'Internal Error! cancelThread() can only ever be called from main application thread!';
475502
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(instance.exports, metadata);
985+
#else
986+
registerTlsInit(instance.exports);
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/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("side: baz=%d\n", get_baz());
31+
printf("side: 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+
side: baz=42
4+
side: 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
@@ -4586,16 +4586,17 @@ def test_dylink_weak(self):
45864586
@node_pthreads
45874587
@needs_dylink
45884588
def test_dylink_tls(self):
4589-
# We currently can't export TLS symbols from module since we don't have
4590-
# and ABI for signaling which exports are TLS and which are regular
4591-
# data exports.
4592-
4593-
# TODO(sbc): Add tests that depend on importing/exported TLS symbols
4594-
# once we figure out how to do that.
45954589
self.emcc_args.append('-Wno-experimental')
45964590
self.dylink_testf(test_file('core/test_dylink_tls.c'),
45974591
need_reverse=False)
45984592

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

0 commit comments

Comments
 (0)