aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-07-15 15:24:54 -0700
committerAlon Zakai <alonzakai@gmail.com>2013-07-15 15:24:54 -0700
commit6e7a7aa3e8c2096463fed1f24d561557f49787f8 (patch)
treef81ea4377db6d4fd1b002cb55875d2291b71b62e
parent9bf755607fc7a0e7f446a5e2d6c82738d77d876f (diff)
parent61c31f69359132e7630a9c4c2c3d25a6ed742247 (diff)
Merge branch 'self-dlopen' of github.com:int3/emscripten into incoming
-rwxr-xr-xemcc3
-rwxr-xr-xemscripten.py14
-rw-r--r--src/intertyper.js2
-rw-r--r--src/jsifier.js4
-rw-r--r--src/library.js81
-rw-r--r--src/modules.js3
-rw-r--r--src/settings.js4
-rwxr-xr-xtests/runner.py44
8 files changed, 117 insertions, 38 deletions
diff --git a/emcc b/emcc
index bffe8d5d..895aab87 100755
--- a/emcc
+++ b/emcc
@@ -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..fd883e82 100644
--- a/src/library.js
+++ b/src/library.js
@@ -5881,7 +5881,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 +5890,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 +5970,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