Skip to content

Commit a15d4b7

Browse files
committed
Implment RTDL_LOCAL and RTDL_NOW/RTDL_LAZY dlopen flags
There are two changed here in the implementation of dlopen: 1. Avoid calling updateGOT when RTDL_LOCAL is set. 2. Check for undefined stub functions during postInstantiate. The reason we can't just avoid the stub functions completely here is the a module could both import and export a given symbol.
1 parent f978c50 commit a15d4b7

File tree

3 files changed

+82
-20
lines changed

3 files changed

+82
-20
lines changed

src/library_dylink.js

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,7 @@ var LibraryDylink = {
127127

128128
// Applies relocations to exported things.
129129
$relocateExports__internal: true,
130-
$relocateExports__deps: ['$updateGOT'],
131-
$relocateExports: function(exports, memoryBase, replace) {
130+
$relocateExports: function(exports, memoryBase) {
132131
var relocated = {};
133132

134133
for (var e in exports) {
@@ -150,7 +149,6 @@ var LibraryDylink = {
150149
}
151150
relocated[e] = value;
152151
}
153-
updateGOT(relocated, replace);
154152
return relocated;
155153
},
156154

@@ -379,7 +377,7 @@ var LibraryDylink = {
379377
},
380378

381379
// Module.symbols <- libModule.symbols (flags.global handler)
382-
$mergeLibSymbols__deps: ['$asmjsMangle'],
380+
$mergeLibSymbols__deps: ['$asmjsMangle', '$updateGOT'],
383381
$mergeLibSymbols: function(exports, libName) {
384382
// add symbols into global namespace TODO: weak linking etc.
385383
for (var sym in exports) {
@@ -412,6 +410,7 @@ var LibraryDylink = {
412410
Module[module_sym] = exports[sym];
413411
}
414412
}
413+
updateGOT(exports);
415414
},
416415

417416
// Loads a side module from binary data or compiled Module. Returns the module's exports or a
@@ -471,9 +470,6 @@ var LibraryDylink = {
471470
if (!resolved) {
472471
resolved = moduleExports[sym];
473472
}
474-
#if ASSERTIONS
475-
assert(resolved, 'undefined symbol `' + sym + '`. perhaps a side module was not linked in? if this global was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment');
476-
#endif
477473
return resolved;
478474
}
479475

@@ -506,14 +502,18 @@ var LibraryDylink = {
506502
if (!(prop in stubs)) {
507503
var resolved;
508504
stubs[prop] = function() {
509-
if (!resolved) resolved = resolveSymbol(prop, true);
505+
if (!resolved) resolved = resolveSymbol(prop);
506+
#if ASSERTIONS
507+
assert(resolved, 'undefined symbol `' + prop + '`. perhaps a side module was not linked in? if this global was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment');
508+
#endif
510509
return resolved.apply(null, arguments);
511510
};
512511
}
513512
return stubs[prop];
514513
}
515514
};
516-
var proxy = new Proxy({}, proxyHandler);
515+
var stubs = {}
516+
var proxy = new Proxy(stubs, proxyHandler);
517517
var info = {
518518
'GOT.mem': new Proxy({}, GOTHandler),
519519
'GOT.func': new Proxy({}, GOTHandler),
@@ -526,10 +526,18 @@ var LibraryDylink = {
526526
// the table should be unchanged
527527
assert(wasmTable === originalTable);
528528
#endif
529+
529530
// add new entries to functionsInTableMap
530531
updateTableMap(tableBase, metadata.tableSize);
531532
moduleExports = relocateExports(instance.exports, memoryBase);
532533
if (!flags.allowUndefined) {
534+
if (!flags.lazy) {
535+
for (var symbol in stubs) {
536+
if (!resolveSymbol(symbol)) {
537+
throw Error('undefined symbol: ' + symbol);
538+
}
539+
}
540+
}
533541
reportUndefinedSymbols();
534542
}
535543
#if STACK_OVERFLOW_CHECK >= 2
@@ -710,10 +718,10 @@ var LibraryDylink = {
710718

711719
// module for lib is loaded - update the dso & global namespace
712720
function moduleLoaded(libModule) {
721+
dso.module = libModule;
713722
if (dso.global) {
714-
mergeLibSymbols(libModule, lib);
723+
mergeLibSymbols(dso.module, lib);
715724
}
716-
dso.module = libModule;
717725
}
718726

719727
if (flags.loadAsync) {
@@ -794,10 +802,10 @@ var LibraryDylink = {
794802
err('dlopenInternal: ' + filename);
795803
#endif
796804

797-
// We don't care about RTLD_NOW and RTLD_LAZY.
798805
var combinedFlags = {
799806
global: Boolean(flags & {{{ cDefine('RTLD_GLOBAL') }}}),
800807
nodelete: Boolean(flags & {{{ cDefine('RTLD_NODELETE') }}}),
808+
lazy: Boolean(flags & {{{ cDefine('RTLD_LAZY') }}}),
801809
loadAsync: jsflags.loadAsync,
802810
fs: jsflags.fs,
803811
}

src/library_pthread.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,9 @@ var LibraryPThread = {
496496
}
497497
},
498498

499+
#if RELOCATABLE
500+
$registerTlsInit__deps: ['$updateGOT'],
501+
#endif
499502
$registerTlsInit: function(tlsInitFunc, moduleExports, metadata) {
500503
#if DYLINK_DEBUG
501504
out("registerTlsInit: " + tlsInitFunc);
@@ -519,7 +522,8 @@ var LibraryPThread = {
519522
for (var sym in metadata.tlsExports) {
520523
metadata.tlsExports[sym] = moduleExports[sym];
521524
}
522-
relocateExports(metadata.tlsExports, __tls_base, /*replace=*/true);
525+
exports = relocateExports(metadata.tlsExports, __tls_base);
526+
updateGOT(exports, /*replace=*/true);
523527
}
524528

525529
// Register this function so that its gets called for each thread on

tests/test_core.py

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2612,7 +2612,7 @@ def build_dlfcn_lib(self, filename):
26122612
self.clear_setting('MAIN_MODULE')
26132613
self.set_setting('SIDE_MODULE')
26142614
outfile = self.build(filename, js_outfile=not self.is_wasm())
2615-
shutil.move(outfile, 'liblib.so')
2615+
shutil.move(outfile, shared.unsuffixed_basename(filename) + '.so')
26162616

26172617
@needs_dylink
26182618
def test_dlfcn_missing(self):
@@ -3431,7 +3431,7 @@ def test_dlfcn_handle_alloc(self):
34313431
def indir(name):
34323432
return os.path.join(dirname, name)
34333433

3434-
create_file('a.cpp', r'''
3434+
create_file('liba.cpp', r'''
34353435
#include <stdio.h>
34363436
34373437
static class A {
@@ -3442,7 +3442,7 @@ def indir(name):
34423442
} _;
34433443
''')
34443444

3445-
create_file('b.cpp', r'''
3445+
create_file('libb.cpp', r'''
34463446
#include <stdio.h>
34473447
34483448
static class B {
@@ -3453,10 +3453,8 @@ def indir(name):
34533453
} _;
34543454
''')
34553455

3456-
self.build_dlfcn_lib('a.cpp')
3457-
shutil.move(indir('liblib.so'), indir('liba.so'))
3458-
self.build_dlfcn_lib('b.cpp')
3459-
shutil.move(indir('liblib.so'), indir('libb.so'))
3456+
self.build_dlfcn_lib('liba.cpp')
3457+
self.build_dlfcn_lib('libb.cpp')
34603458

34613459
self.set_setting('MAIN_MODULE')
34623460
self.set_setting('NODERAWFS')
@@ -3528,6 +3526,58 @@ def test_dlfcn_feature_in_lib(self):
35283526
'''
35293527
self.do_run(src, 'float: 42.\n')
35303528

3529+
@needs_dylink
3530+
def test_dlfcn_rtld_local(self):
3531+
create_file('liblib.c', r'''
3532+
int foo() { return 42; }
3533+
''')
3534+
self.build_dlfcn_lib('liblib.c')
3535+
3536+
self.set_setting('ERROR_ON_UNDEFINED_SYMBOLS', 0)
3537+
create_file('libbar.c', r'''
3538+
extern int foo();
3539+
int bar() { return foo(); }
3540+
''')
3541+
self.build_dlfcn_lib('libbar.c')
3542+
3543+
self.prep_dlfcn_main()
3544+
src = r'''
3545+
#include <assert.h>
3546+
#include <dlfcn.h>
3547+
#include <stdio.h>
3548+
#include <stdlib.h>
3549+
3550+
typedef int (*func_t)();
3551+
3552+
int main() {
3553+
void *lib_handle = dlopen("liblib.so", RTLD_LOCAL|RTLD_NOW);
3554+
if (!lib_handle) {
3555+
puts(dlerror());
3556+
abort();
3557+
}
3558+
func_t foo = (func_t)dlsym(lib_handle, "foo");
3559+
if (!foo) {
3560+
puts(dlerror());
3561+
abort();
3562+
}
3563+
printf("foo: %d\n", foo());
3564+
3565+
// Verify that "foo" is not visible in the global
3566+
// namespace.
3567+
foo = (func_t)dlsym(RTLD_DEFAULT, "foo");
3568+
assert(foo == NULL);
3569+
3570+
// libbar.so should not be loadable since it depends on the symbol
3571+
// `foo` which should not be in the global namespace.
3572+
void *libbar_handle = dlopen("libbar.so", RTLD_NOW);
3573+
printf("libbar_handle: %p\n", libbar_handle);
3574+
assert(libbar_handle == NULL);
3575+
puts(dlerror());
3576+
return 0;
3577+
}
3578+
'''
3579+
self.do_run(src, ['foo: 42', 'Error: undefined symbol: foo'], assert_all=True)
3580+
35313581
def dylink_test(self, main, side, expected=None, header=None, force_c=False,
35323582
main_module=2, **kwargs):
35333583
# Same as dylink_testf but take source code in string form

0 commit comments

Comments
 (0)