#8935: Obscure linker bug leads to crash in GHCi -------------------------------------+------------------------------------ Reporter: simonmar | Owner: trommler Type: bug | Status: patch Priority: high | Milestone: 7.8.3 Component: Runtime System | Version: 7.8.1-rc2 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Unknown/Multiple Type of failure: GHCi crash | Difficulty: Rocket Science Test Case: | Blocked By: Blocking: | Related Tickets: -------------------------------------+------------------------------------ Comment (by dagit): I think you test depends on how you link the program and libraries. I'm using your setup, but I made the following tweaks. `test.c`, in this version `dlopen` of libbar is `RTLD_GLOBAL`: {{{ #include <dlfcn.h> #include <stdio.h> #include <stdlib.h> extern int getbar(void); extern void setbar(int); int main(int argc, char *argv[]) { void *deflt, *hdl; int (*pgetbar)(void); int (*psetbar)(int); //setbar(2); //fprintf(stderr,"getbar() = %d\n", getbar()); deflt = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL); if (deflt == NULL) { fprintf(stderr,"%s\n", dlerror()); exit(1); } //pgetbar = dlsym(deflt, "getbar"); //fprintf(stderr,"dlsym(deflt, \"getbar\") = %p, pgetbar() = %d\n", pgetbar, (*pgetbar)()); hdl = dlopen("libbar.so", RTLD_GLOBAL | RTLD_LAZY); if (hdl == NULL) { fprintf(stderr,"%s\n", dlerror()); exit(1); } pgetbar = dlsym(hdl, "_getbar"); char* e; e = dlerror(); if( e != NULL ){ fprintf(stderr,"%s\n", e); } psetbar = dlsym(hdl, "_setbar"); e = dlerror(); if( e != NULL ){ fprintf(stderr,"%s\n", e); } fprintf(stderr,"dlsym(deflt, \"_getbar\") = %p, pgetbar() = %d\n", pgetbar, (*pgetbar)()); (*psetbar)(3); fprintf(stderr,"(*psetbar)(3); pgetbar() = %d\n", (*pgetbar)()); hdl = dlopen("libfoo.so", RTLD_LOCAL | RTLD_LAZY); if (hdl == NULL) { fprintf(stderr,"%s\n", dlerror()); exit(1); } pgetbar = dlsym(hdl, "getbar"); e = dlerror(); if( e != NULL ){ fprintf(stderr,"%s\n", e); } psetbar = dlsym(hdl, "setbar"); e = dlerror(); if( e != NULL ){ fprintf(stderr,"%s\n", e); } fprintf(stderr,"dlsym(deflt, \"getbar\") = %p, pgetbar() = %d\n", pgetbar, (*pgetbar)()); (*psetbar)(4); fprintf(stderr,"(*psetbar)(4); pgetbar() = %d\n", (*pgetbar)()); return 0; } }}} Here, I don't link together the libraries at compile time, not even for `test.c` (I'm trying to be careful not to call this static linking, because as we'll see later it's not): {{{ gcc -shared -fPIC bar.c -o libbar.so gcc -shared -fPIC foo.c -o libfoo.so gcc -Wall test.c -ldl -g LD_LIBRARY_PATH=. ./a.out }}} Now when I run the program I get: {{{ dlsym(deflt, "_getbar") = 0x7fddcc3a5698, pgetbar() = 1 (*psetbar)(3); pgetbar() = 3 dlsym(deflt, "getbar") = 0x7fddcc1a3728, pgetbar() = 3 (*psetbar)(4); pgetbar() = 4 }}} Okay, as we would expect libfoo uses the definition in libbar. Now, switch the `dlopen` to `RTLD_LOCAL`, and run it again: {{{ dlsym(deflt, "_getbar") = 0x7f056f698698, pgetbar() = 1 (*psetbar)(3); pgetbar() = 3 ./a.out: symbol lookup error: ./libfoo.so: undefined symbol: _getbar }}} Now if we link libbar and the final program: {{{ gcc -shared -fPIC bar.c -o libbar.so gcc -shared -fPIC foo.c -o libfoo.so gcc -Wall test.c -L. -lbar -ldl -g LD_LIBRARY_PATH=. ./a.out dlsym(deflt, "_getbar") = 0x7f75b9914698, pgetbar() = 1 (*psetbar)(3); pgetbar() = 3 dlsym(deflt, "getbar") = 0x7f75b96f9728, pgetbar() = 3 (*psetbar)(4); pgetbar() = 4 }}} Now it finds the symbol again. The difference seems to be this: {{{ readelf -Wa a.out.withbar | grep NEEDED 0x0000000000000001 (NEEDED) Shared library: [libbar.so] 0x0000000000000001 (NEEDED) Shared library: [libdl.so.2] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] readelf -Wa a.out.withoutbar | grep NEEDED 0x0000000000000001 (NEEDED) Shared library: [libdl.so.2] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] }}} In other words, mentioning libbar on the gcc line for `test.c` adds an entry that the elf interpreter sees and it resolves the symbol, lazily, before we get into main. I would expect the same behavior if libfoo had been linked to libbar instead of with the main application. And indeed, that's what we get: {{{ gcc -shared -fPIC bar.c -o libbar.so gcc -shared -fPIC foo.c -o libfoo.so -L. -lbar gcc -Wall test.c -ldl -g -o a.out.withoutbar LD_LIBRARY_PATH=. ./a.out.withoutbar dlsym(deflt, "_getbar") = 0x7fc75e77c698, pgetbar() = 1 (*psetbar)(3); pgetbar() = 3 dlsym(deflt, "getbar") = 0x7fc75e57a728, pgetbar() = 3 (*psetbar)(4); pgetbar() = 4 readelf -Wa libfoo.so | grep NEEDED 0x0000000000000001 (NEEDED) Shared library: [libbar.so] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] }}} -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8935#comment:48> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler