Skip to content

Commit c4f7a55

Browse files
authored
dylink: Remove dynamic linking start code and allocation (#18643)
With this change I completely remove the `ensure_init` function and the associated allocation in favor a static handle for the "main" dso. I also avoid the initial `_dlinit` call out to JS. This change relies on the fact that `dlopen(NULL, ..`) return a handle the is equivalent of `RTLD_DEFAULT` when passed to `dlsym`. See: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlopen.3.html
1 parent 36c194d commit c4f7a55

File tree

3 files changed

+77
-99
lines changed

3 files changed

+77
-99
lines changed

src/library_dylink.js

Lines changed: 37 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,15 @@ var LibraryDylink = {
274274
__dlsym: function(handle, symbol) {
275275
abort(dlopenMissingError);
276276
},
277-
_dlinit: function(main_dso_handle) {},
278-
_dlinit: function(main_dso_handle) {},
279277
#else // MAIN_MODULE != 0
280278
// dynamic linker/loader (a-la ld.so on ELF systems)
279+
$LDSO__deps: ['$newDSO'],
281280
$LDSO: {
282281
// name -> dso [refcount, name, module, global]; Used by dlopen
283282
loadedLibsByName: {},
284283
// handle -> dso; Used by dlsym
285284
loadedLibsByHandle: {},
285+
init: () => newDSO('__main__', {{{ cDefine('RTLD_DEFAULT') }}}, wasmImports),
286286
},
287287

288288
$dlSetError__internal: true,
@@ -824,6 +824,20 @@ var LibraryDylink = {
824824
},
825825
#endif
826826

827+
$newDSO: function(name, handle, syms) {
828+
var dso = {
829+
refcount: Infinity,
830+
name: name,
831+
module: syms,
832+
global: true,
833+
};
834+
LDSO.loadedLibsByName[name] = dso;
835+
if (handle != undefined) {
836+
LDSO.loadedLibsByHandle[handle] = dso;
837+
}
838+
return dso;
839+
},
840+
827841
// loadDynamicLibrary loads dynamic library @ lib URL / path and returns
828842
// handle for loaded DSO.
829843
//
@@ -845,9 +859,9 @@ var LibraryDylink = {
845859
// If a library was already loaded, it is not loaded a second time. However
846860
// flags.global and flags.nodelete are handled every time a load request is made.
847861
// Once a library becomes "global" or "nodelete", it cannot be removed or unloaded.
848-
$loadDynamicLibrary__deps: ['$LDSO', '$loadWebAssemblyModule', '$isInternalSym', '$mergeLibSymbols'],
862+
$loadDynamicLibrary__deps: ['$LDSO', '$loadWebAssemblyModule', '$isInternalSym', '$mergeLibSymbols', '$newDSO'],
849863
$loadDynamicLibrary__docs: '/** @param {number=} handle */',
850-
$loadDynamicLibrary: function(lib, flags = {global: true, nodelete: true}, handle = 0) {
864+
$loadDynamicLibrary: function(lib, flags = {global: true, nodelete: true}, handle) {
851865
#if DYLINK_DEBUG
852866
dbg('loadDynamicLibrary: ' + lib + ' handle:' + handle);
853867
dbg('existing: ' + Object.keys(LDSO.loadedLibsByName));
@@ -880,16 +894,9 @@ var LibraryDylink = {
880894
}
881895

882896
// allocate new DSO
883-
dso = {
884-
refcount: flags.nodelete ? Infinity : 1,
885-
name: lib,
886-
module: 'loading',
887-
global: flags.global,
888-
};
889-
LDSO.loadedLibsByName[lib] = dso;
890-
if (handle) {
891-
LDSO.loadedLibsByHandle[handle] = dso;
892-
}
897+
dso = newDSO(lib, handle, 'loading');
898+
dso.refcount = flags.nodelete ? Infinity : 1;
899+
dso.global = flags.global;
893900

894901
// libData <- libFile
895902
function loadLibData(libFile) {
@@ -1095,11 +1102,8 @@ var LibraryDylink = {
10951102
#if DYLINK_DEBUG
10961103
dbg("_dlsym_catchup: handle=" + ptrToString(handle) + " symbolIndex=" + symbolIndex);
10971104
#endif
1098-
var symDict = wasmImports;
1099-
if (handle != {{{ cDefine('RTLD_DEFAULT') }}}) {
1100-
var lib = LDSO.loadedLibsByHandle[handle];
1101-
symDict = lib.module;
1102-
}
1105+
var lib = LDSO.loadedLibsByHandle[handle];
1106+
var symDict = lib.module;
11031107
var symName = Object.keys(symDict)[symbolIndex];
11041108
var sym = symDict[symName];
11051109
var result = addFunction(sym, sym.sig);
@@ -1122,34 +1126,24 @@ var LibraryDylink = {
11221126
var result;
11231127
var newSymIndex;
11241128

1125-
if (handle == {{{ cDefine('RTLD_DEFAULT') }}}) {
1126-
var resolved = resolveGlobalSymbol(symbol, true);
1127-
result = resolved.sym;
1128-
if (!result) {
1129-
dlSetError('Tried to lookup unknown symbol "' + symbol + '" in dynamic lib: RTLD_DEFAULT');
1130-
return 0;
1131-
}
1132-
newSymIndex = Object.keys(wasmImports).indexOf(resolved.name);
1133-
} else {
1134-
var lib = LDSO.loadedLibsByHandle[handle];
1129+
var lib = LDSO.loadedLibsByHandle[handle];
11351130
#if ASSERTIONS
1136-
assert(lib, 'Tried to dlsym() from an unopened handle: ' + handle);
1131+
assert(lib, 'Tried to dlsym() from an unopened handle: ' + handle);
11371132
#endif
1138-
if (!lib.module.hasOwnProperty(symbol)) {
1139-
dlSetError('Tried to lookup unknown symbol "' + symbol + '" in dynamic lib: ' + lib.name)
1140-
return 0;
1141-
}
1142-
newSymIndex = Object.keys(lib.module).indexOf(symbol);
1133+
if (!lib.module.hasOwnProperty(symbol)) {
1134+
dlSetError('Tried to lookup unknown symbol "' + symbol + '" in dynamic lib: ' + lib.name)
1135+
return 0;
1136+
}
1137+
newSymIndex = Object.keys(lib.module).indexOf(symbol);
11431138
#if !WASM_BIGINT
1144-
var origSym = 'orig$' + symbol;
1145-
result = lib.module[origSym];
1146-
if (result) {
1147-
newSymIndex = Object.keys(lib.module).indexOf(origSym);
1148-
}
1149-
else
1150-
#endif
1151-
result = lib.module[symbol];
1139+
var origSym = 'orig$' + symbol;
1140+
result = lib.module[origSym];
1141+
if (result) {
1142+
newSymIndex = Object.keys(lib.module).indexOf(origSym);
11521143
}
1144+
else
1145+
#endif
1146+
result = lib.module[symbol];
11531147

11541148
if (typeof result == 'function') {
11551149
#if DYLINK_DEBUG
@@ -1185,17 +1179,6 @@ var LibraryDylink = {
11851179
#endif
11861180
return result;
11871181
},
1188-
1189-
_dlinit: function(main_dso_handle) {
1190-
var dso = {
1191-
refcount: Infinity, // = nodelete
1192-
name: '__main__',
1193-
module: Module['asm'],
1194-
global: true
1195-
};
1196-
LDSO.loadedLibsByName[dso.name] = dso;
1197-
LDSO.loadedLibsByHandle[main_dso_handle] = dso;
1198-
}
11991182
#endif // MAIN_MODULE != 0
12001183
};
12011184

src/postamble.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ function stackCheckInit() {
140140

141141
#if RELOCATABLE
142142
var dylibsLoaded = false;
143+
#if '$LDSO' in addedLibraryItems
144+
LDSO.init();
145+
#endif
143146
#endif
144147

145148
/** @type {function(Array=)} */

system/lib/libc/dynlink.c

Lines changed: 37 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ struct async_data {
3737
};
3838
typedef void (*dlopen_callback_func)(struct dso*, struct async_data* user_data);
3939

40-
void _dlinit(struct dso* main_dso_handle);
4140
void* _dlopen_js(struct dso* handle);
4241
void* _dlsym_js(struct dso* handle, const char* symbol, int* sym_index);
4342
void _emscripten_dlopen_js(struct dso* handle,
@@ -60,22 +59,34 @@ struct dlevent {
6059
int id;
6160
#endif
6261
};
63-
static struct dlevent * _Atomic head, * _Atomic tail;
62+
63+
// Handle to "main" dso, needed for dlopen(NULL,..)
64+
static struct dso main_dso = {
65+
.name = "__main__",
66+
.flags = 0,
67+
};
68+
69+
static struct dlevent main_event = {
70+
.prev = NULL,
71+
.next = NULL,
72+
.sym_index = -1,
73+
.dso = &main_dso,
74+
};
75+
76+
static struct dlevent* _Atomic head = &main_event;
77+
static struct dlevent* _Atomic tail = &main_event;
6478

6579
#ifdef _REENTRANT
66-
static thread_local struct dlevent* thread_local_tail;
80+
static thread_local struct dlevent* thread_local_tail = &main_event;
6781
static pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
6882

6983
void* _dlsym_catchup_js(struct dso* handle, int sym_index);
7084

7185
static void dlsync() {
72-
if (!thread_local_tail) {
73-
thread_local_tail = head;
74-
}
7586
if (!thread_local_tail->next) {
7687
return;
7788
}
78-
dbg("dlsync: catching up %p %p", thread_local_tail, tail);
89+
dbg("dlsync: catching up %p", thread_local_tail);
7990
while (thread_local_tail->next) {
8091
struct dlevent* p = thread_local_tail->next;
8192
if (p->sym_index != -1) {
@@ -114,17 +125,9 @@ static void dlsync() {
114125
// taking the lock below.
115126
static thread_local bool skip_dlsync = false;
116127

117-
static void ensure_init();
118-
119128
void _emscripten_thread_sync_code() {
120-
if (skip_dlsync) {
121-
return;
122-
}
123-
ensure_init();
124-
if (!thread_local_tail) {
125-
thread_local_tail = head;
126-
}
127-
if (thread_local_tail != tail) {
129+
if (!skip_dlsync && thread_local_tail != tail) {
130+
dbg("_emscripten_thread_sync_code: cur=%p head=%p tail=%p", thread_local_tail, head, tail);
128131
skip_dlsync = true;
129132
dlsync();
130133
skip_dlsync = false;
@@ -184,7 +187,8 @@ void new_dlevent(struct dso* p, int sym_index) {
184187
ev->id = tail->id + 1;
185188
#endif
186189
}
187-
dbg("new_dlevent: id=%d %s dso=%p sym_index=%d",
190+
dbg("new_dlevent: ev=%p id=%d %s dso=%p sym_index=%d",
191+
ev,
188192
ev->id,
189193
p ? p->name : "RTLD_DEFAULT",
190194
p,
@@ -193,10 +197,6 @@ void new_dlevent(struct dso* p, int sym_index) {
193197
#if _REENTRANT
194198
thread_local_tail = ev;
195199
#endif
196-
197-
if (!head) {
198-
head = ev;
199-
}
200200
}
201201

202202
static void load_library_done(struct dso* p) {
@@ -244,28 +244,13 @@ static void dlopen_js_onerror(struct dso* dso, struct async_data* data) {
244244
free(data);
245245
}
246246

247-
// This function is called at the start of all entry points so that the dso
248-
// list gets initialized on first use.
249-
static void ensure_init() {
250-
if (head) {
251-
return;
252-
}
253-
// Initialize the dso list. This happens on first run.
254-
do_write_lock();
255-
if (!head) {
256-
// Flags are not important since the main module is already loaded.
257-
struct dso* p = load_library_start("__main__", RTLD_NOW|RTLD_GLOBAL);
258-
assert(p);
259-
_dlinit(p);
260-
load_library_done(p);
261-
assert(head);
262-
}
263-
do_write_unlock();
264-
}
265-
266-
void* dlopen(const char* file, int flags) {
267-
ensure_init();
247+
// Internal version of dlopen with typed return value.
248+
// Without this, the compiler won't tell us if we have the wrong return type.
249+
static struct dso* _dlopen(const char* file, int flags) {
268250
if (!file) {
251+
// If a null pointer is passed in path, dlopen() returns a handle equivalent
252+
// to RTLD_DEFAULT.
253+
dbg("dlopen: NULL -> %p", head->dso);
269254
return head->dso;
270255
}
271256
dbg("dlopen: %s [%d]", file, flags);
@@ -309,9 +294,12 @@ void* dlopen(const char* file, int flags) {
309294
return p;
310295
}
311296

297+
void* dlopen(const char* file, int flags) {
298+
return _dlopen(file, flags);
299+
}
300+
312301
void emscripten_dlopen(const char* filename, int flags, void* user_data,
313302
em_dlopen_callback onsuccess, em_arg_callback_func onerror) {
314-
ensure_init();
315303
if (!filename) {
316304
onsuccess(user_data, head);
317305
return;
@@ -340,11 +328,15 @@ void emscripten_dlopen(const char* filename, int flags, void* user_data,
340328
}
341329

342330
void* __dlsym(void* restrict p, const char* restrict s, void* restrict ra) {
343-
ensure_init();
344331
dbg("__dlsym dso:%p sym:%s", p, s);
345332
if (p != RTLD_DEFAULT && p != RTLD_NEXT && __dl_invalid_handle(p)) {
346333
return 0;
347334
}
335+
// The first "dso" is always the default one which is equivelent to
336+
// RTLD_DEFAULT. This is what is returned from `dlopen(NULL, ...)`.
337+
if (p == head->dso) {
338+
p = RTLD_DEFAULT;
339+
}
348340
void* res;
349341
int sym_index = -1;
350342
do_write_lock();

0 commit comments

Comments
 (0)