Skip to content

Implment RTDL_LOCAL and RTDL_NOW/RTDL_LAZY dlopen flags #15339

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions src/library_dylink.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,7 @@ var LibraryDylink = {

// Applies relocations to exported things.
$relocateExports__internal: true,
$relocateExports__deps: ['$updateGOT'],
$relocateExports: function(exports, memoryBase, replace) {
$relocateExports: function(exports, memoryBase) {
var relocated = {};

for (var e in exports) {
Expand All @@ -150,7 +149,6 @@ var LibraryDylink = {
}
relocated[e] = value;
}
updateGOT(relocated, replace);
return relocated;
},

Expand Down Expand Up @@ -379,7 +377,7 @@ var LibraryDylink = {
},

// Module.symbols <- libModule.symbols (flags.global handler)
$mergeLibSymbols__deps: ['$asmjsMangle'],
$mergeLibSymbols__deps: ['$asmjsMangle', '$updateGOT'],
$mergeLibSymbols: function(exports, libName) {
// add symbols into global namespace TODO: weak linking etc.
for (var sym in exports) {
Expand Down Expand Up @@ -412,6 +410,7 @@ var LibraryDylink = {
Module[module_sym] = exports[sym];
}
}
updateGOT(exports);
},

// Loads a side module from binary data or compiled Module. Returns the module's exports or a
Expand Down Expand Up @@ -471,9 +470,6 @@ var LibraryDylink = {
if (!resolved) {
resolved = moduleExports[sym];
}
#if ASSERTIONS
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');
#endif
return resolved;
}

Expand Down Expand Up @@ -506,14 +502,18 @@ var LibraryDylink = {
if (!(prop in stubs)) {
var resolved;
stubs[prop] = function() {
if (!resolved) resolved = resolveSymbol(prop, true);
if (!resolved) resolved = resolveSymbol(prop);
#if ASSERTIONS
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');
#endif
return resolved.apply(null, arguments);
};
}
return stubs[prop];
}
};
var proxy = new Proxy({}, proxyHandler);
var stubs = {}
var proxy = new Proxy(stubs, proxyHandler);
var info = {
'GOT.mem': new Proxy({}, GOTHandler),
'GOT.func': new Proxy({}, GOTHandler),
Expand All @@ -526,10 +526,18 @@ var LibraryDylink = {
// the table should be unchanged
assert(wasmTable === originalTable);
#endif

// add new entries to functionsInTableMap
updateTableMap(tableBase, metadata.tableSize);
moduleExports = relocateExports(instance.exports, memoryBase);
if (!flags.allowUndefined) {
if (!flags.lazy) {
for (var symbol in stubs) {
if (!resolveSymbol(symbol)) {
throw Error('undefined symbol: ' + symbol);
}
}
}
reportUndefinedSymbols();
Copy link
Member

Choose a reason for hiding this comment

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

Maybe add a comment here? I had to read this a few times before I realized the new code checks for undefined symbols in stubs, while the call to reportUndefinedSymbols does the rest.

Actually, can that function do everything, and get the stubs as an optional param?

}
#if STACK_OVERFLOW_CHECK >= 2
Expand Down Expand Up @@ -710,10 +718,10 @@ var LibraryDylink = {

// module for lib is loaded - update the dso & global namespace
function moduleLoaded(libModule) {
dso.module = libModule;
if (dso.global) {
mergeLibSymbols(libModule, lib);
mergeLibSymbols(dso.module, lib);
}
dso.module = libModule;
}

if (flags.loadAsync) {
Expand Down Expand Up @@ -794,10 +802,10 @@ var LibraryDylink = {
err('dlopenInternal: ' + filename);
#endif

// We don't care about RTLD_NOW and RTLD_LAZY.
var combinedFlags = {
global: Boolean(flags & {{{ cDefine('RTLD_GLOBAL') }}}),
nodelete: Boolean(flags & {{{ cDefine('RTLD_NODELETE') }}}),
lazy: Boolean(flags & {{{ cDefine('RTLD_LAZY') }}}),
loadAsync: jsflags.loadAsync,
fs: jsflags.fs,
}
Expand Down
6 changes: 5 additions & 1 deletion src/library_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,9 @@ var LibraryPThread = {
}
},

#if RELOCATABLE
$registerTlsInit__deps: ['$updateGOT'],
#endif
$registerTlsInit: function(tlsInitFunc, moduleExports, metadata) {
#if DYLINK_DEBUG
out("registerTlsInit: " + tlsInitFunc);
Expand All @@ -519,7 +522,8 @@ var LibraryPThread = {
for (var sym in metadata.tlsExports) {
metadata.tlsExports[sym] = moduleExports[sym];
}
relocateExports(metadata.tlsExports, __tls_base, /*replace=*/true);
exports = relocateExports(metadata.tlsExports, __tls_base);
updateGOT(exports, /*replace=*/true);
}

// Register this function so that its gets called for each thread on
Expand Down
64 changes: 57 additions & 7 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2612,7 +2612,7 @@ def build_dlfcn_lib(self, filename):
self.clear_setting('MAIN_MODULE')
self.set_setting('SIDE_MODULE')
outfile = self.build(filename, js_outfile=not self.is_wasm())
shutil.move(outfile, 'liblib.so')
shutil.move(outfile, shared.unsuffixed_basename(filename) + '.so')

@needs_dylink
def test_dlfcn_missing(self):
Expand Down Expand Up @@ -3431,7 +3431,7 @@ def test_dlfcn_handle_alloc(self):
def indir(name):
return os.path.join(dirname, name)

create_file('a.cpp', r'''
create_file('liba.cpp', r'''
#include <stdio.h>

static class A {
Expand All @@ -3442,7 +3442,7 @@ def indir(name):
} _;
''')

create_file('b.cpp', r'''
create_file('libb.cpp', r'''
#include <stdio.h>

static class B {
Expand All @@ -3453,10 +3453,8 @@ def indir(name):
} _;
''')

self.build_dlfcn_lib('a.cpp')
shutil.move(indir('liblib.so'), indir('liba.so'))
self.build_dlfcn_lib('b.cpp')
shutil.move(indir('liblib.so'), indir('libb.so'))
self.build_dlfcn_lib('liba.cpp')
self.build_dlfcn_lib('libb.cpp')

self.set_setting('MAIN_MODULE')
self.set_setting('NODERAWFS')
Expand Down Expand Up @@ -3528,6 +3526,58 @@ def test_dlfcn_feature_in_lib(self):
'''
self.do_run(src, 'float: 42.\n')

@needs_dylink
def test_dlfcn_rtld_local(self):
create_file('liblib.c', r'''
int foo() { return 42; }
''')
self.build_dlfcn_lib('liblib.c')

self.set_setting('ERROR_ON_UNDEFINED_SYMBOLS', 0)
create_file('libbar.c', r'''
extern int foo();
int bar() { return foo(); }
''')
self.build_dlfcn_lib('libbar.c')

self.prep_dlfcn_main()
src = r'''
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

typedef int (*func_t)();

int main() {
void *lib_handle = dlopen("liblib.so", RTLD_LOCAL|RTLD_NOW);
if (!lib_handle) {
puts(dlerror());
abort();
}
func_t foo = (func_t)dlsym(lib_handle, "foo");
if (!foo) {
puts(dlerror());
abort();
}
printf("foo: %d\n", foo());

// Verify that "foo" is not visible in the global
// namespace.
foo = (func_t)dlsym(RTLD_DEFAULT, "foo");
assert(foo == NULL);

// libbar.so should not be loadable since it depends on the symbol
// `foo` which should not be in the global namespace.
void *libbar_handle = dlopen("libbar.so", RTLD_NOW);
printf("libbar_handle: %p\n", libbar_handle);
assert(libbar_handle == NULL);
puts(dlerror());
return 0;
}
'''
self.do_run(src, ['foo: 42', 'Error: undefined symbol: foo'], assert_all=True)

def dylink_test(self, main, side, expected=None, header=None, force_c=False,
main_module=2, **kwargs):
# Same as dylink_testf but take source code in string form
Expand Down