diff options
-rwxr-xr-x | emcc | 3 | ||||
-rwxr-xr-x | emscripten.py | 14 | ||||
-rw-r--r-- | src/intertyper.js | 2 | ||||
-rw-r--r-- | src/jsifier.js | 4 | ||||
-rw-r--r-- | src/library.js | 85 | ||||
-rw-r--r-- | src/modules.js | 3 | ||||
-rw-r--r-- | src/settings.js | 4 | ||||
-rwxr-xr-x | tests/runner.py | 44 |
8 files changed, 121 insertions, 38 deletions
@@ -1077,6 +1077,9 @@ try: shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules debug_level = max(debug_level, 2) + if shared.Settings.DLOPEN_SUPPORT: + shared.Settings.LINKABLE = 1 + ## Compile source code to bitcode logging.debug('compiling to bitcode') diff --git a/emscripten.py b/emscripten.py index 9a2abb9a..3e3538e9 100755 --- a/emscripten.py +++ b/emscripten.py @@ -9,7 +9,7 @@ header files (so that the JS compiler can see the constants in those headers, for the libc implementation in JS). ''' -import os, sys, json, optparse, subprocess, re, time, multiprocessing, functools +import os, sys, json, optparse, subprocess, re, time, multiprocessing, string from tools import shared from tools import jsrun, cache as cache_module, tempfiles @@ -625,6 +625,18 @@ Runtime.stackRestore = function(top) { asm['stackRestore'](top) }; // EMSCRIPTEN_END_FUNCS '''] + # Create symbol table for self-dlopen + if settings.get('DLOPEN_SUPPORT'): + symbol_table = { k:v+forwarded_json['Runtime']['GLOBAL_BASE'] + for k,v in forwarded_json['Variables']['indexedGlobals'].iteritems() + if forwarded_json['Variables']['globals'][k]['named'] } + for raw in last_forwarded_json['Functions']['tables'].itervalues(): + if raw == '': continue + table = map(string.strip, raw[raw.find('[')+1:raw.find(']')].split(",")) + symbol_table.update(map(lambda x: (x[1], x[0]), + filter(lambda x: x[1] != '0', enumerate(table)))) + outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table)) + for funcs_js_item in funcs_js: # do this loop carefully to save memory funcs_js_item = indexize(funcs_js_item) funcs_js_item = blockaddrsize(funcs_js_item) diff --git a/src/intertyper.js b/src/intertyper.js index 94d937e1..abfbdacb 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -503,6 +503,7 @@ function intertyper(data, sidePass, baseLineNums) { // variable var ident = item.tokens[0].text; var private_ = findTokenText(item, 'private') >= 0 || findTokenText(item, 'internal') >= 0; + var named = findTokenText(item, 'unnamed_addr') < 0; cleanOutTokens(LLVM.GLOBAL_MODIFIERS, item.tokens, [2, 3]); var external = false; if (item.tokens[2].text === 'external') { @@ -516,6 +517,7 @@ function intertyper(data, sidePass, baseLineNums) { type: item.tokens[2].text, external: external, private_: private_, + named: named, lineNum: item.lineNum }; if (!NAMED_GLOBALS) { diff --git a/src/jsifier.js b/src/jsifier.js index 82b78d0a..38581ce4 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -286,6 +286,8 @@ function JSify(data, functionsOnly, givenFunctions) { allocator = 'ALLOC_NONE'; } + Variables.globals[item.ident].named = item.named; + if (ASM_JS && (MAIN_MODULE || SIDE_MODULE) && !item.private_ && !NAMED_GLOBALS && isIndexableGlobal(item.ident)) { // We need this to be named (and it normally would not be), so that it can be linked to and used from other modules Variables.globals[item.ident].linkable = 1; @@ -602,6 +604,8 @@ function JSify(data, functionsOnly, givenFunctions) { var associatedSourceFile = "NO_SOURCE"; } + if (DLOPEN_SUPPORT) Functions.getIndex(func.ident); + func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n'; if (PGO) { diff --git a/src/library.js b/src/library.js index c5618b53..0ea9c135 100644 --- a/src/library.js +++ b/src/library.js @@ -5735,10 +5735,14 @@ LibraryManager.library = { rintf: 'rint', lrint: 'rint', lrintf: 'rint', +#if USE_TYPED_ARRAYS == 2 llrint: function(x) { x = (x < 0) ? -Math.round(-x) : Math.round(x); {{{ makeStructuralReturn(splitI64('x')) }}}; }, +#else + llrint: 'rint', +#endif llrintf: 'llrint', nearbyint: 'rint', nearbyintf: 'rint', @@ -5881,7 +5885,7 @@ LibraryManager.library = { dlopen: function(filename, flag) { // void *dlopen(const char *file, int mode); // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html - filename = (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename); + filename = filename === 0 ? '__self__' : (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename); if (DLFCN_DATA.loadedLibNames[filename]) { // Already loaded; increment ref count and return. @@ -5890,48 +5894,55 @@ LibraryManager.library = { return handle; } - var target = FS.findObject(filename); - if (!target || target.isFolder || target.isDevice) { - DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename; - return 0; + if (filename === '__self__') { + var handle = -1; + var lib_module = Module; + var cached_functions = SYMBOL_TABLE; } else { - FS.forceLoadFile(target); - var lib_data = intArrayToString(target.contents); - } + var target = FS.findObject(filename); + if (!target || target.isFolder || target.isDevice) { + DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename; + return 0; + } else { + FS.forceLoadFile(target); + var lib_data = intArrayToString(target.contents); + } - try { - var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length); - } catch (e) { + try { + var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length); + } catch (e) { #if ASSERTIONS - Module.printErr('Error in loading dynamic library: ' + e); + Module.printErr('Error in loading dynamic library: ' + e); #endif - DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename; - return 0; - } + DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename; + return 0; + } - // Not all browsers support Object.keys(). - var handle = 1; - for (var key in DLFCN_DATA.loadedLibs) { - if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++; - } + // Not all browsers support Object.keys(). + var handle = 1; + for (var key in DLFCN_DATA.loadedLibs) { + if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++; + } + + // We don't care about RTLD_NOW and RTLD_LAZY. + if (flag & 256) { // RTLD_GLOBAL + for (var ident in lib_module) { + if (lib_module.hasOwnProperty(ident)) { + Module[ident] = lib_module[ident]; + } + } + } + var cached_functions = {}; + } DLFCN_DATA.loadedLibs[handle] = { refcount: 1, name: filename, module: lib_module, - cached_functions: {} + cached_functions: cached_functions }; DLFCN_DATA.loadedLibNames[filename] = handle; - // We don't care about RTLD_NOW and RTLD_LAZY. - if (flag & 256) { // RTLD_GLOBAL - for (var ident in lib_module) { - if (lib_module.hasOwnProperty(ident)) { - Module[ident] = lib_module[ident]; - } - } - } - return handle; }, // int dlclose(void* handle); @@ -5963,13 +5974,15 @@ LibraryManager.library = { return 0; } else { var lib = DLFCN_DATA.loadedLibs[handle]; - if (!lib.module.hasOwnProperty(symbol)) { - DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol + - '" in dynamic lib: ' + lib.name); - return 0; + // self-dlopen means that lib.module is not a superset of + // cached_functions, so check the latter first + if (lib.cached_functions.hasOwnProperty(symbol)) { + return lib.cached_functions[symbol]; } else { - if (lib.cached_functions.hasOwnProperty(symbol)) { - return lib.cached_functions[symbol]; + if (!lib.module.hasOwnProperty(symbol)) { + DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol + + '" in dynamic lib: ' + lib.name); + return 0; } else { var result = lib.module[symbol]; if (typeof result == 'function') { diff --git a/src/modules.js b/src/modules.js index a6aa2644..53d97817 100644 --- a/src/modules.js +++ b/src/modules.js @@ -449,7 +449,8 @@ var PassManager = { Types: Types, Variables: Variables, Functions: Functions, - EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS // needed for asm.js global constructors (ctors) + EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS, // needed for asm.js global constructors (ctors) + Runtime: { GLOBAL_BASE: Runtime.GLOBAL_BASE } })); } else if (phase == 'funcs') { print('\n//FORWARDED_DATA:' + JSON.stringify({ diff --git a/src/settings.js b/src/settings.js index 7f9dca3b..10e93975 100644 --- a/src/settings.js +++ b/src/settings.js @@ -327,6 +327,10 @@ var LINKABLE = 0; // If set to 1, this file can be linked with others, either as // LINKABLE of 0 is very useful in that we can reduce the size of the // generated code very significantly, by removing everything not actually used. +var DLOPEN_SUPPORT = 0; // Whether to support dlopen(NULL, ...) which enables dynamic access to the + // module's functions and globals. Implies LINKABLE=1, because we do not want + // dead code elimination. + var RUNTIME_TYPE_INFO = 0; // Whether to expose type info to the script at run time. This // increases the size of the generated script, but allows you // to more easily perform operations from handwritten JS on diff --git a/tests/runner.py b/tests/runner.py index 0efcce86..c08434f5 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -6021,6 +6021,50 @@ def process(filename): self.do_run(src, '100\n200\n13\n42\n', post_build=add_pre_run_and_checks) + def test_dlfcn_self(self): + if Settings.USE_TYPED_ARRAYS == 1: return self.skip('Does not work with USE_TYPED_ARRAYS=1') + Settings.DLOPEN_SUPPORT = 1 + + src = r''' +#include <stdio.h> +#include <dlfcn.h> + +int global = 123; + +extern "C" __attribute__((noinline)) void foo(int x) { + printf("%d\n", x); +} + +extern "C" __attribute__((noinline)) void repeatable() { + void* self = dlopen(NULL, RTLD_LAZY); + int* global_ptr = (int*)dlsym(self, "global"); + void (*foo_ptr)(int) = (void (*)(int))dlsym(self, "foo"); + foo_ptr(*global_ptr); + dlclose(self); +} + +int main() { + repeatable(); + repeatable(); + return 0; +}''' + def post(filename): + with open(filename) as f: + for line in f: + if 'var SYMBOL_TABLE' in line: + table = line + break + else: + raise Exception('Could not find symbol table!') + import json + table = json.loads(table[table.find('{'):table.rfind('}')+1]) + actual = list(sorted(table.keys())) + # ensure there aren't too many globals; we don't want unnamed_addr + assert actual == ['_foo', '_global', '_main', '_repeatable'], \ + "Symbol table does not match: %s" % actual + + self.do_run(src, '123\n123', post_build=(None, post)) + def test_rand(self): return self.skip('rand() is now random') # FIXME |