Skip to content

Commit 6ca2853

Browse files
authored
Read exports from side modules at link time (emscripten-core#13915)
This means we can reason about the symbols which are or are not defined across the entire program which allows for ERROR_ON_UNDEFINED_SYMBOLS to be enabled for any build that includes all its side modules on the command line.
1 parent 78214a9 commit 6ca2853

13 files changed

+77
-14
lines changed

ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ Current Trunk
2828
list explicit `EXPORTED_FUNCTIONS`. Also, users of `MAIN_MODULE=1` with
2929
dynamic linking (not dlopen) who list all side modules on the command line,
3030
should be able to switch to `MAIN_MODULE=2` and get a reduction in code size.
31+
- When building with `MAIN_MODULE` it is now possbile to warn or error on
32+
undefined symbols assuming all the side modules are passed at link time. This
33+
means that for many projects it should now be possbile to enable
34+
`ERROR_ON_UNDEFINED_SYMBOLS` along with `MAIN_MODULE`.
3135

3236
2.0.17: 04/10/2021
3337
------------------

emcc.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -631,13 +631,17 @@ def process_dynamic_libs(dylibs):
631631
imports = webassembly.get_imports(dylib)
632632
new_exports = []
633633
for imp in imports:
634-
if imp.type not in (webassembly.ExternType.FUNC, webassembly.ExternType.GLOBAL):
634+
if imp.kind not in (webassembly.ExternType.FUNC, webassembly.ExternType.GLOBAL):
635635
continue
636636
new_exports.append(imp.field)
637637
logger.debug('Adding exports based on `%s`: %s', dylib, new_exports)
638638
settings.EXPORTED_FUNCTIONS.extend(shared.asmjs_mangle(e) for e in new_exports)
639639
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.extend(new_exports)
640640

641+
exports = webassembly.get_exports(dylib)
642+
for export in exports:
643+
settings.SIDE_MODULE_EXPORTS.append(shared.asmjs_mangle(export.name))
644+
641645

642646
def unmangle_symbols_from_cmdline(symbols):
643647
def unmangle(x):
@@ -1447,9 +1451,13 @@ def default_setting(name, new_default):
14471451
'__heap_base',
14481452
'__stack_pointer',
14491453
]
1450-
# This needs to be exported on the Module object too so it's visible
1451-
# to side modules too.
1452-
settings.EXPORTED_FUNCTIONS += ['___heap_base']
1454+
settings.EXPORTED_FUNCTIONS += [
1455+
# This needs to be exported on the Module object too so it's visible
1456+
# to side modules too.
1457+
'___heap_base',
1458+
# Unconditional dependency in library_dylink.js
1459+
'_setThrew',
1460+
]
14531461
if settings.MINIMAL_RUNTIME:
14541462
exit_with_error('MINIMAL_RUNTIME is not compatible with relocatable output')
14551463
if settings.WASM2JS:

src/compiler.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ if (settingsFile) {
6767
EXPORTED_FUNCTIONS = set(EXPORTED_FUNCTIONS);
6868
EXCEPTION_CATCHING_ALLOWED = set(EXCEPTION_CATCHING_ALLOWED);
6969
WASM_EXPORTS = set(WASM_EXPORTS);
70+
SIDE_MODULE_EXPORTS = set(SIDE_MODULE_EXPORTS);
7071
INCOMING_MODULE_JS_API = set(INCOMING_MODULE_JS_API);
7172

7273
RUNTIME_DEBUG = LIBRARY_DEBUG || GL_DEBUG;

src/jsifier.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ function stringifyWithFunctions(obj) {
4949
}
5050
}
5151

52+
function isDefined(symName) {
53+
if (symName in WASM_EXPORTS || symName in SIDE_MODULE_EXPORTS) {
54+
return true;
55+
}
56+
// 'invoke_' symbols are created at runtime in libary_dylink.py so can
57+
// always be considered as defined.
58+
if (RELOCATABLE && symName.startsWith('_invoke_')) {
59+
return true;
60+
}
61+
return false;
62+
}
63+
5264
// JSifier
5365
function JSify(functionsOnly) {
5466
var mainPass = !functionsOnly;
@@ -147,7 +159,7 @@ function JSify(functionsOnly) {
147159
var noExport = false;
148160

149161
if (!LibraryManager.library.hasOwnProperty(ident)) {
150-
if (!(finalName in WASM_EXPORTS) && !LINKABLE) {
162+
if (!isDefined(finalName) && !LINKABLE) {
151163
var msg = 'undefined symbol: ' + ident;
152164
if (dependent) msg += ' (referenced by ' + dependent + ')';
153165
if (ERROR_ON_UNDEFINED_SYMBOLS) {

src/library.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ LibraryManager.library = {
3535
{{{ makeSetTempRet0('$i') }}};
3636
},
3737

38+
#if SAFE_HEAP
39+
// Trivial wrappers around runtime functions that make these symbols available
40+
// to native code.
41+
segfault: function() { segfault(); },
42+
alignfault: function() { alignfault(); },
43+
ftfault: function() { ftfault(); },
44+
#endif
45+
3846
// ==========================================================================
3947
// JavaScript <-> C string interop
4048
// ==========================================================================

src/settings_internal.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,5 @@ var HEAP_BASE = 0;
193193
// Also set for STANDALONE_WASM since the _start function is needed to call
194194
// static ctors, even if there is no user main.
195195
var HAS_MAIN = 0;
196+
197+
var SIDE_MODULE_EXPORTS = [];

tests/other/metadce/hello_world_O3_MAIN_MODULE_2.exports

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ __wasm_call_ctors
22
dynCall_jiji
33
main
44
malloc
5+
setThrew
56
stackAlloc
67
stackRestore
78
stackSave

tests/other/metadce/hello_world_O3_MAIN_MODULE_2.funcs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ $dlmalloc
99
$legalstub$dynCall_jiji
1010
$main
1111
$sbrk
12+
$setThrew
1213
$stackAlloc
1314
$stackRestore
1415
$stackSave
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
wrote profile of 236 bytes (allocated 236 bytes)
1+
wrote profile of 240 bytes (allocated 240 bytes)
22
Hello from main!
33
Hello from lib!

tests/test_core.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3594,6 +3594,7 @@ def dylink_testf(self, main, side, expected=None, force_c=False, main_emcc_args=
35943594

35953595
# side settings
35963596
self.clear_setting('MAIN_MODULE')
3597+
self.clear_setting('ERROR_ON_UNDEFINED_SYMBOLS')
35973598
self.set_setting('SIDE_MODULE')
35983599
side_suffix = 'wasm' if self.is_wasm() else 'js'
35993600
if isinstance(side, list):
@@ -3608,10 +3609,14 @@ def dylink_testf(self, main, side, expected=None, force_c=False, main_emcc_args=
36083609
self.set_setting('MAIN_MODULE', main_module)
36093610
self.clear_setting('SIDE_MODULE')
36103611
if auto_load:
3612+
# Normally we don't report undefined symbols when linking main modules but
3613+
# in this case we know all the side modules are specified on the command line.
3614+
# TODO(sbc): Make this the default one day
3615+
self.set_setting('ERROR_ON_UNDEFINED_SYMBOLS')
36113616
self.emcc_args += main_emcc_args
36123617
self.emcc_args.append('liblib.so')
3613-
if force_c:
3614-
self.emcc_args.append('-nostdlib++')
3618+
if force_c:
3619+
self.emcc_args.append('-nostdlib++')
36153620

36163621
if isinstance(main, list):
36173622
# main is just a library

0 commit comments

Comments
 (0)