Skip to content

Commit

Permalink
Enable EM_ASM in side modules
Browse files Browse the repository at this point in the history
The only way I could find to make work was to use `eval`.  Sadly even
using `new Function` didn't work since it evaluates the code not in the
containing (module) scope but in the global scope.

Depends on WebAssembly/binaryen#5274

Fixes: #18199
  • Loading branch information
sbc100 committed Nov 21, 2022
1 parent d659edc commit ef3c22c
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 16 deletions.
2 changes: 2 additions & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,8 @@ def get_binaryen_passes():
passes += ['--signext-lowering']
if optimizing:
passes += ['--post-emscripten']
if settings.SIDE_MODULE:
passes += ['--pass-arg=post-emscripten-side-module']
if optimizing:
passes += [building.opt_level_to_str(settings.OPT_LEVEL, settings.SHRINK_LEVEL)]
# when optimizing, use the fact that low memory is never used (1024 is a
Expand Down
2 changes: 0 additions & 2 deletions emscripten.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,6 @@ def emscript(in_wasm, out_wasm, outfile_js, memfile):
diagnostics.warning('em-js-i64', 'using 64-bit arguments in EM_JS function without WASM_BIGINT is not yet fully supported: `%s` (%s, %s)', em_js_func, c_sig, signature.params)

if settings.SIDE_MODULE:
if metadata.asmConsts:
exit_with_error('EM_ASM is not supported in side modules')
if metadata.emJsFuncs:
exit_with_error('EM_JS is not supported in side modules')
logger.debug('emscript: skipping remaining js glue generation')
Expand Down
4 changes: 4 additions & 0 deletions site/source/docs/compiling/Dynamic-Linking.rst
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ Limitations
startup
`[doc] <https://emscripten.org/docs/porting/files/packaging_files.html#preloading-files>`__
`[discuss] <https://groups.google.com/forum/#!topic/emscripten-discuss/cE3hUV3fDSw>`__.
- ``EM_ASM`` code defined within side modules depends on ``eval`` support are
is therefore incompatible with ``-sDYNAMIC_EXECUTION=0``.
- ``EM_JS`` functions defined in side modules are not yet supported.


Pthreads support
----------------
Expand Down
33 changes: 33 additions & 0 deletions src/library_dylink.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ var LibraryDylink = {
'_emscripten_tls_init',
'__wasm_init_tls',
'__wasm_call_ctors',
'__start_em_asm',
'__stop_em_asm',
].includes(symName)
#if SPLIT_MODULE
// Exports synthesized by wasm-split should be prefixed with '%'
Expand Down Expand Up @@ -657,6 +659,37 @@ var LibraryDylink = {
}
#endif

#if MAIN_MODULE
function addEmAsm(addr, body) {
var args = [];
var arity = 0;
for (; arity < 16; arity++) {
if (body.indexOf('$' + arity) != -1) {
args.push('$' + arity);
} else {
break;
}
}
args = args.join(',');
var func = '(' + args +' ) => { ' + body + '};'
#if DYLINK_DEBUG
dbg('adding new EM_ASM constant at: ' + ptrToString(start));
#endif
{{{ makeEval('ASM_CONSTS[start] = eval(func)') }}};
}

// Add any EM_ASM function that exist in the side module
if ('__start_em_asm' in moduleExports) {
var start = {{{ from64("moduleExports['__start_em_asm']") }}};
var stop = {{{ from64("moduleExports['__stop_em_asm']") }}};
while (start < stop) {
var jsString = UTF8ToString(start);
addEmAsm(start, jsString);
start = HEAPU8.indexOf(0, start) + 1;
}
}
#endif

// initialize the module
#if USE_PTHREADS
// Only one thread (currently The main thread) should call
Expand Down
22 changes: 12 additions & 10 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1251,12 +1251,13 @@ var BENCHMARK = false;
// [link]
var EXPORT_NAME = 'Module';

// When set to 0, we do not emit eval() and new Function(), which disables some functionality
// (causing runtime errors if attempted to be used), but allows the emitted code to be
// acceptable in places that disallow dynamic code execution (chrome packaged app,
// privileged firefox app, etc.). Pass this flag when developing an Emscripten application
// that is targeting a privileged or a certified execution environment, see
// Firefox Content Security Policy (CSP) webpage for details:
// When set to 0, we do not emit eval() and new Function(), which disables some
// functionality (causing runtime errors if attempted to be used), but allows
// the emitted code to be acceptable in places that disallow dynamic code
// execution (chrome packaged app, privileged firefox app, etc.). Pass this flag
// when developing an Emscripten application that is targeting a privileged or a
// certified execution environment, see Firefox Content Security Policy (CSP)
// webpage for details:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src
// in particular the 'unsafe-eval' and 'wasm-unsafe-eval' policies.
//
Expand All @@ -1271,11 +1272,12 @@ var EXPORT_NAME = 'Module';
// - emscripten_run_script_int(),
// - emscripten_run_script_string(),
// - dlopen(),
// - the functions ccall() and cwrap() are still available, but they are restricted to only
// being able to call functions that have been exported in the Module object in advance.
// - the functions ccall() and cwrap() are still available, but they are
// restricted to only being able to call functions that have been exported in
// the Module object in advance.
//
// When set to -sDYNAMIC_EXECUTION=2 flag is set, attempts to call to eval() are demoted
// to warnings instead of throwing an exception.
// When -sDYNAMIC_EXECUTION=2 is set, attempts to call to eval() are demoted to
// warnings instead of throwing an exception.
// [link]
var DYNAMIC_EXECUTION = 1;

Expand Down
10 changes: 10 additions & 0 deletions test/core/test_em_asm_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <stdio.h>
#include <emscripten.h>

int test_side();

int main() {
printf("in main\n");
EM_ASM(out("em_asm in main"));
return test_side();
}
17 changes: 17 additions & 0 deletions test/core/test_em_asm_main.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
in main
em_asm in main
no args works
int types:
char : 1
signed char : 2
unsigned char : 3
short: 4
signed short: 5
unsigned short: 6
int : 7
signed int : 8
unsigned int : 9
long : 10
signed long : 11
unsigned long : 12
terminator: 42
28 changes: 28 additions & 0 deletions test/core/test_em_asm_side.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <emscripten.h>
#include <stdio.h>

int test_side() {
EM_ASM(out("no args works"));

EM_ASM({
console.log("int types:");
out(" char : " + $0);
out(" signed char : " + $1);
out("unsigned char : " + $2);
out(" short: " + $3);
out(" signed short: " + $4);
out("unsigned short: " + $5);
out(" int : " + $6);
out(" signed int : " + $7);
out("unsigned int : " + $8);
out(" long : " + $9);
out(" signed long : " + $10);
out("unsigned long : " + $11);
out(" terminator: " + $12);
}, (char)1, (signed char)2, (unsigned char)3,
(short)4, (signed short)5, (unsigned short)6,
(int)7, (signed int)8, (unsigned int)9,
(long)10, (signed long)11, (unsigned long)12, 42);

return 0;
}
5 changes: 5 additions & 0 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2257,6 +2257,11 @@ def test_em_asm_arguments_side_effects(self):
def test_em_asm_direct(self):
self.do_core_test('test_em_asm_direct.c')

@needs_dylink
def test_em_asm_side_module(self):
self.build(test_file('core/test_em_asm_side.c'), js_outfile=False, emcc_args=['-sSIDE_MODULE'], output_basename='side')
self.do_core_test('test_em_asm_main.c', emcc_args=['-sMAIN_MODULE=2', 'side.wasm'])

@parameterized({
'': ([], False),
'pthreads': (['-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'], False),
Expand Down
4 changes: 0 additions & 4 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -11645,10 +11645,6 @@ def test_runtime_keepalive(self):
self.set_setting('EXIT_RUNTIME')
self.do_other_test('test_runtime_keepalive.cpp')

def test_em_asm_side_module(self):
err = self.expect_fail([EMCC, '-sSIDE_MODULE', test_file('hello_world_em_asm.c')])
self.assertContained('EM_ASM is not supported in side modules', err)

def test_em_js_side_module(self):
err = self.expect_fail([EMXX, '-sSIDE_MODULE', test_file('core/test_em_js.cpp')])
self.assertContained('EM_JS is not supported in side modules', err)
Expand Down

0 comments on commit ef3c22c

Please sign in to comment.