Skip to content
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

[dylink] Fix multiple loading of the same library with RTLD_LOCAL #20210

Merged
merged 1 commit into from
Sep 8, 2023
Merged
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
19 changes: 11 additions & 8 deletions src/library_dylink.js
Original file line number Diff line number Diff line change
Expand Up @@ -959,15 +959,18 @@ var LibraryDylink = {
var dso = LDSO.loadedLibsByName[libName];
if (dso) {
// the library is being loaded or has been loaded already.
//
// however it could be previously loaded only locally and if we get
// load request with global=true we have to make it globally visible now.
if (flags.global && !dso.global) {
dso.global = true;
if (dso.exports !== 'loading') {
// ^^^ if module is 'loading' - symbols merging will be eventually done by the loader.
mergeLibSymbols(dso.exports, libName)
#if ASSERTIONS
assert(dso.exports !== 'loading', `Attempt to load '${libName}' twice before the first load completed`);
#endif
if (!flags.global) {
if (localScope) {
Object.assign(localScope, dso.exports);
}
} else if (!dso.global) {
// The library was previously loaded only locally but not
// we have a request with global=true.
dso.global = true;
mergeLibSymbols(dso.exports, libName)
}
// same for "nodelete"
if (flags.nodelete && dso.refcount !== Infinity) {
Expand Down
54 changes: 42 additions & 12 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4102,29 +4102,46 @@ def test_dlfcn_asyncify(self):

@needs_dylink
def test_dlfcn_rtld_local(self):
# Create two shared libraries that both depend on a third.
# liba.so -> libsub.so
# libb.so -> libsub.so
create_file('liba.c', r'''
#include <stdio.h>

void func_b();
void func_sub();

void func_a() {
printf("func_a\n");
// Call a function from a dependent DSO. This symbol should
// be available here even though liba itself is loaded with RTLD_LOCAL.
func_b();
func_sub();
}
''')

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

void func_sub();

void func_b() {
printf("func_b\n");
// Call a function from a dependent DSO. This symbol should
// be available here even though liba itself is loaded with RTLD_LOCAL.
func_sub();
}
''')

create_file('libsub.c', r'''
#include <stdio.h>

void func_sub() {
printf("func_sub\n");
}
''')

self.build_dlfcn_lib('libb.c', outfile='libb.so')
self.build_dlfcn_lib('liba.c', outfile='liba.so', emcc_args=['libb.so'])
self.build_dlfcn_lib('libsub.c', outfile='libsub.so')
self.build_dlfcn_lib('libb.c', outfile='libb.so', emcc_args=['libsub.so'])
self.build_dlfcn_lib('liba.c', outfile='liba.so', emcc_args=['libsub.so'])

self.prep_dlfcn_main(['liba.so', 'libb.so', '-L.'])
create_file('main.c', r'''
Expand All @@ -4133,28 +4150,41 @@ def test_dlfcn_rtld_local(self):
#include <stdio.h>

int main() {
void* handle;
void (*f)();

printf("main\n");
void* handle = dlopen("liba.so", RTLD_NOW|RTLD_LOCAL);
// Call a function from libb
handle = dlopen("liba.so", RTLD_NOW|RTLD_LOCAL);
assert(handle);

void (*f)();
f = dlsym(handle, "func_a");
assert(f);
f();

// Verify that symbols from liba.so and libb.so are not globally
// Same for libb
handle = dlopen("libb.so", RTLD_NOW|RTLD_LOCAL);
assert(handle);

f = dlsym(handle, "func_b");
assert(f);
f();

// Verify that symbols from all three libraries are not globally
// visible.
void* func_a = dlsym(RTLD_DEFAULT, "func_a");
assert(func_a == NULL);
void* func_b = dlsym(RTLD_DEFAULT, "func_b");
assert(func_b == NULL);
f = dlsym(RTLD_DEFAULT, "func_a");
assert(f == NULL);
f = dlsym(RTLD_DEFAULT, "func_b");
assert(f == NULL);
f = dlsym(RTLD_DEFAULT, "func_sub");
assert(f == NULL);

printf("done\n");
return 0;
}
''')

self.do_runf('main.c', 'main\nfunc_a\nfunc_b\ndone\n')
self.do_runf('main.c', 'main\nfunc_a\nfunc_sub\nfunc_b\nfunc_sub\ndone\n')

def dylink_test(self, main, side, expected=None, header=None, force_c=False,
main_module=2, **kwargs):
Expand Down
3 changes: 1 addition & 2 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -8339,8 +8339,7 @@ def test_side_module_naming(self):
self.assertTrue(building.is_wasm_dylib(target))

create_file('main.c', '')
self.run_process([EMCC, '-sMAIN_MODULE=2', 'main.c', '-Werror', target])
self.run_js('a.out.js')
self.do_runf('main.c', emcc_args=['-sMAIN_MODULE=2', 'main.c', '-Werror', target])

def test_side_module_missing(self):
self.run_process([EMCC, test_file('hello_world.c'), '-sSIDE_MODULE', '-o', 'libside1.wasm'])
Expand Down