Skip to content

Commit 3204550

Browse files
sbc100impact-maker
authored andcommitted
Fix external useage of EM_JS function (emscripten-core#18928)
When no local usage of an EM_JS function is present the compiler was completely removing the import of the function (and its import_name). With this change we force at least one reference to the function by taking its address in an otherwise unused (and GC-able) pointer. This use case was broken by the switch to LLD_REPORT_UNDEFINED in emscripten-core#16003. Fixes: emscripten-core#18927
1 parent 2f32971 commit 3204550

File tree

2 files changed

+37
-6
lines changed

2 files changed

+37
-6
lines changed

system/include/emscripten/em_js.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
//
4040
// __attribute__((import_name("foo"))) int foo(int x, int y);
4141
//
42+
// __attribute__((used)) static void* __em_js_ref_foo = (void*)&foo;
43+
//
4244
// __attribute__((used, visibility("default")))
4345
// char __em_js__foo[] = "(int x, int y)<::>{ return 2 * x + y; }";
4446
//
@@ -50,6 +52,11 @@
5052
// We use <::> to separate the arguments from the function body because it isn't
5153
// valid anywhere in a C function declaration.
5254

55+
// The __em_js_ref_foo pointer simply exists in order to force a reference to
56+
// `foo` to exist in the object file, even if there are no other local uses.
57+
// This means the linker will always use the import_name attribute for this
58+
// function even if it is not locally used.
59+
5360
// Generated __em_js__-prefixed symbols are read by binaryen, and the string
5461
// data is extracted into the Emscripten metadata dictionary under the
5562
// "emJsFuncs" key. emJsFuncs itself is a dictionary where the keys are function
@@ -59,12 +66,13 @@
5966
// emJsFuncs metadata is read in emscripten.py's create_em_js, which creates an
6067
// array of JS function strings to be included in the JS output.
6168

62-
#define _EM_JS(ret, c_name, js_name, params, code) \
63-
_EM_JS_CPP_BEGIN \
64-
ret c_name params EM_IMPORT(js_name); \
65-
EMSCRIPTEN_KEEPALIVE \
66-
__attribute__((section("em_js"), aligned(1))) char __em_js__##js_name[] = \
67-
#params "<::>" code; \
69+
#define _EM_JS(ret, c_name, js_name, params, code) \
70+
_EM_JS_CPP_BEGIN \
71+
ret c_name params EM_IMPORT(js_name); \
72+
__attribute__((used)) static void* __em_js_ref_##c_name = (void*)&c_name; \
73+
EMSCRIPTEN_KEEPALIVE \
74+
__attribute__((section("em_js"), aligned(1))) char __em_js__##js_name[] = \
75+
#params "<::>" code; \
6876
_EM_JS_CPP_END
6977

7078
#define EM_JS(ret, name, params, ...) _EM_JS(ret, name, name, params, #__VA_ARGS__)

test/test_other.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11914,6 +11914,29 @@ def test_em_js_main_module_address(self):
1191411914
expected = 'Aborted(Assertion failed: Missing signature argument to addFunction: function foo() { err("hello"); })'
1191511915
self.do_runf(test_file('other/test_em_js_main_module_address.c'), expected, assert_returncode=NON_ZERO)
1191611916

11917+
def test_em_js_external_usage(self):
11918+
# Verify that EM_JS functions can be called from other source files, even in the case
11919+
# when they are not used within the defining file.
11920+
create_file('em_js.c', r'''
11921+
#include <emscripten/em_js.h>
11922+
11923+
EM_JS(void, js_func, (), {
11924+
out('js_func called');
11925+
});
11926+
11927+
// js_func is unused within this file
11928+
''')
11929+
create_file('main.c', '''
11930+
#include <stdio.h>
11931+
11932+
void js_func();
11933+
int main() {
11934+
js_func();
11935+
}
11936+
''')
11937+
self.run_process([EMCC, 'em_js.c', '-c'])
11938+
self.do_runf('main.c', 'js_func called\n', emcc_args=['em_js.o'])
11939+
1191711940
# On Windows maximum command line length is 32767 characters. Create such a long build line by linking together
1191811941
# several .o files to test that emcc internally uses response files properly when calling llvm-nm and wasm-ld.
1191911942
@is_slow_test

0 commit comments

Comments
 (0)