diff options
author | Peter Trommler <ptrommler@acm.org> | 2014-11-30 12:00:39 -0600 |
---|---|---|
committer | Austin Seipp <austin@well-typed.com> | 2014-11-30 12:00:40 -0600 |
commit | 383733b9191a36e2d3f757700842dbc3855911d9 (patch) | |
tree | 5d86bffc23aec991454ff506124e7a075e983bea /rts | |
parent | 643635ea1d779054e1bb3b1825cd7894c5748811 (diff) | |
download | haskell-383733b9191a36e2d3f757700842dbc3855911d9.tar.gz |
Fix obscure problem with using the system linker (#8935)
Summary:
In a statically linked GHCi symbol `environ` resolves to NULL when
called from a Haskell script.
When resolving symbols in a Haskell script we need to search the
executable program and its dependent (DT_NEEDED) shared libraries
first and then search the loaded libraries.
We want to be able to override functions in loaded libraries later.
Libraries must be opened with local scope (RTLD_LOCAL) and not global.
The latter adds all symbols to the executable program's symbols where
they are then searched in loading order. We want reverse loading order.
When libraries are loaded with local scope the dynamic linker
cannot use symbols in that library when resolving the dependencies
in another shared library. This changes the way files compiled to
object code must be linked into temporary shared libraries. We link
with the last temporary shared library created so far if it exists.
Since each temporary shared library is linked to the previous temporary
shared library the dynamic linker finds the latest definition of a
symbol by following the dependency chain.
See also Note [RTLD_LOCAL] for a summary of the problem and solution.
Cherry-picked commit 2f8b4c
Changed linker argument ordering
On some ELF systems GNU ld (and others?) default to
--as-needed and the order of libraries in the link
matters.
The last temporary shared library, must appear
before all other libraries. Switching the position
of extra_ld_inputs and lib_path_objs does that.
Fixes #8935 and #9186
Reviewers: austin, hvr, rwbarton, simonmar
Reviewed By: simonmar
Subscribers: thomie, carter, simonmar
Differential Revision: https://phabricator.haskell.org/D349
GHC Trac Issues: #8935, #9186, #9480
Diffstat (limited to 'rts')
-rw-r--r-- | rts/Linker.c | 43 |
1 files changed, 34 insertions, 9 deletions
diff --git a/rts/Linker.c b/rts/Linker.c index 0c390ae46c..9c9de61519 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -1839,7 +1839,7 @@ internal_dlopen(const char *dll_name) // (see POSIX also) ACQUIRE_LOCK(&dl_mutex); - hdl = dlopen(dll_name, RTLD_LAZY | RTLD_GLOBAL); + hdl = dlopen(dll_name, RTLD_LAZY|RTLD_LOCAL); /* see Note [RTLD_LOCAL] */ errmsg = NULL; if (hdl == NULL) { @@ -1849,11 +1849,12 @@ internal_dlopen(const char *dll_name) errmsg_copy = stgMallocBytes(strlen(errmsg)+1, "addDLL"); strcpy(errmsg_copy, errmsg); errmsg = errmsg_copy; + } else { + o_so = stgMallocBytes(sizeof(OpenedSO), "addDLL"); + o_so->handle = hdl; + o_so->next = openedSOs; + openedSOs = o_so; } - o_so = stgMallocBytes(sizeof(OpenedSO), "addDLL"); - o_so->handle = hdl; - o_so->next = openedSOs; - openedSOs = o_so; RELEASE_LOCK(&dl_mutex); //--------------- End critical section ------------------- @@ -1861,14 +1862,39 @@ internal_dlopen(const char *dll_name) return errmsg; } +/* + Note [RTLD_LOCAL] + + In GHCi we want to be able to override previous .so's with newly + loaded .so's when we recompile something. This further implies that + when we look up a symbol in internal_dlsym() we have to iterate + through the loaded libraries (in order from most recently loaded to + oldest) looking up the symbol in each one until we find it. + + However, this can cause problems for some symbols that are copied + by the linker into the executable image at runtime - see #8935 for a + lengthy discussion. To solve that problem we need to look up + symbols in the main executable *first*, before attempting to look + them up in the loaded .so's. But in order to make that work, we + have to always call dlopen with RTLD_LOCAL, so that the loaded + libraries don't populate the global symbol table. +*/ + static void * -internal_dlsym(void *hdl, const char *symbol) { +internal_dlsym(const char *symbol) { OpenedSO* o_so; void *v; // We acquire dl_mutex as concurrent dl* calls may alter dlerror ACQUIRE_LOCK(&dl_mutex); dlerror(); + // look in program first + v = dlsym(dl_prog_handle, symbol); + if (dlerror() == NULL) { + RELEASE_LOCK(&dl_mutex); + return v; + } + for (o_so = openedSOs; o_so != NULL; o_so = o_so->next) { v = dlsym(o_so->handle, symbol); if (dlerror() == NULL) { @@ -1876,7 +1902,6 @@ internal_dlsym(void *hdl, const char *symbol) { return v; } } - v = dlsym(hdl, symbol); RELEASE_LOCK(&dl_mutex); return v; } @@ -2036,7 +2061,7 @@ static void* lookupSymbol_ (char *lbl) if (!ghciLookupSymbolTable(symhash, lbl, &val)) { IF_DEBUG(linker, debugBelch("lookupSymbol: symbol not found\n")); # if defined(OBJFORMAT_ELF) - return internal_dlsym(dl_prog_handle, lbl); + return internal_dlsym(lbl); # elif defined(OBJFORMAT_MACHO) # if HAVE_DLFCN_H /* On OS X 10.3 and later, we use dlsym instead of the old legacy @@ -2050,7 +2075,7 @@ static void* lookupSymbol_ (char *lbl) */ IF_DEBUG(linker, debugBelch("lookupSymbol: looking up %s with dlsym\n", lbl)); ASSERT(lbl[0] == '_'); - return internal_dlsym(dl_prog_handle, lbl + 1); + return internal_dlsym(lbl + 1); # else if (NSIsSymbolNameDefined(lbl)) { NSSymbol symbol = NSLookupAndBindSymbol(lbl); |