summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Sandberg Ericsson <adam@sandbergericsson.se>2020-07-01 14:44:12 +0100
committerMarge Bot <ben+marge-bot@smart-cactus.org>2020-07-07 13:56:05 -0400
commit0effc57d48ace6b719a9f4cbeac67c95ad55010b (patch)
tree7ea0f0c0052ec6c3c4c30dba5343d2530e30b0b3
parentc59faf67fec83c98ffd1b65f1be0775b34f36595 (diff)
downloadhaskell-0effc57d48ace6b719a9f4cbeac67c95ad55010b.tar.gz
rts linker: teach the linker about GLIBC's special handling of *stat, mknod and atexit functions #7072
-rw-r--r--rts/Linker.c34
-rw-r--r--testsuite/tests/rts/linker/Makefile7
-rw-r--r--testsuite/tests/rts/linker/T7072-main.c39
-rw-r--r--testsuite/tests/rts/linker/T7072-obj.c17
-rw-r--r--testsuite/tests/rts/linker/T7072.stderr1
-rw-r--r--testsuite/tests/rts/linker/all.T7
6 files changed, 102 insertions, 3 deletions
diff --git a/rts/Linker.c b/rts/Linker.c
index 3e8847d8fc..443de6a356 100644
--- a/rts/Linker.c
+++ b/rts/Linker.c
@@ -637,23 +637,51 @@ internal_dlsym(const char *symbol) {
// We acquire dl_mutex as concurrent dl* calls may alter dlerror
ACQUIRE_LOCK(&dl_mutex);
+
+ // clears dlerror
dlerror();
+
// look in program first
v = dlsym(dl_prog_handle, symbol);
if (dlerror() == NULL) {
RELEASE_LOCK(&dl_mutex);
+ IF_DEBUG(linker, debugBelch("internal_dlsym: found symbol '%s' in program\n", symbol));
return v;
}
for (o_so = openedSOs; o_so != NULL; o_so = o_so->next) {
v = dlsym(o_so->handle, symbol);
if (dlerror() == NULL) {
+ IF_DEBUG(linker, debugBelch("internal_dlsym: found symbol '%s' in shared object\n", symbol));
RELEASE_LOCK(&dl_mutex);
return v;
}
}
RELEASE_LOCK(&dl_mutex);
- return v;
+
+# if defined(HAVE_SYS_STAT_H) && defined(linux_HOST_OS) && defined(__GLIBC__)
+ // HACK: GLIBC implements these functions with a great deal of trickery where
+ // they are either inlined at compile time to their corresponding
+ // __xxxx(SYS_VER, ...) function or direct syscalls, or resolved at
+ // link time via libc_nonshared.a.
+ //
+ // We borrow the approach that the LLVM JIT uses to resolve these
+ // symbols. See http://llvm.org/PR274 and #7072 for more info.
+
+ IF_DEBUG(linker, debugBelch("internal_dlsym: looking for symbol '%s' in GLIBC special cases\n", symbol));
+
+ if (strcmp(symbol, "stat") == 0) return (void*)&stat;
+ if (strcmp(symbol, "fstat") == 0) return (void*)&fstat;
+ if (strcmp(symbol, "lstat") == 0) return (void*)&lstat;
+ if (strcmp(symbol, "stat64") == 0) return (void*)&stat64;
+ if (strcmp(symbol, "fstat64") == 0) return (void*)&fstat64;
+ if (strcmp(symbol, "lstat64") == 0) return (void*)&lstat64;
+ if (strcmp(symbol, "atexit") == 0) return (void*)&atexit;
+ if (strcmp(symbol, "mknod") == 0) return (void*)&mknod;
+# endif
+
+ // we failed to find the symbol
+ return NULL;
}
# endif
@@ -829,13 +857,13 @@ SymbolAddr* lookupSymbol_ (SymbolName* lbl)
SymbolAddr* lookupSymbol_ (SymbolName* lbl)
{
- IF_DEBUG(linker, debugBelch("lookupSymbol: looking up %s\n", lbl));
+ IF_DEBUG(linker, debugBelch("lookupSymbol: looking up '%s'\n", lbl));
ASSERT(symhash != NULL);
RtsSymbolInfo *pinfo;
if (!ghciLookupSymbolInfo(symhash, lbl, &pinfo)) {
- IF_DEBUG(linker, debugBelch("lookupSymbol: symbol not found\n"));
+ IF_DEBUG(linker, debugBelch("lookupSymbol: symbol '%s' not found, trying dlsym\n", lbl));
# if defined(OBJFORMAT_ELF)
return internal_dlsym(lbl);
diff --git a/testsuite/tests/rts/linker/Makefile b/testsuite/tests/rts/linker/Makefile
index de4c19b0ce..738e5ef6a3 100644
--- a/testsuite/tests/rts/linker/Makefile
+++ b/testsuite/tests/rts/linker/Makefile
@@ -96,3 +96,10 @@ linker_error3:
"$(TEST_HC)" -c linker_error3.c -o linker_error3_o.o
"$(TEST_HC)" linker_error3.o -o linker_error3 -no-hs-main -optc-g -debug -threaded
./linker_error3 linker_error3_o.o
+
+.PHONY: T7072
+T7072:
+ "$(TEST_HC)" -c T7072-obj.c -o T7072-obj.o
+ "$(TEST_HC)" -c T7072-main.c -o T7072-main.o
+ "$(TEST_HC)" T7072-main.c -o T7072-main -no-hs-main -debug
+ ./T7072-main T7072-obj.o
diff --git a/testsuite/tests/rts/linker/T7072-main.c b/testsuite/tests/rts/linker/T7072-main.c
new file mode 100644
index 0000000000..36760de16c
--- /dev/null
+++ b/testsuite/tests/rts/linker/T7072-main.c
@@ -0,0 +1,39 @@
+#include "ghcconfig.h"
+#include "Rts.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+int main (int argc, char *argv[])
+{
+ int r;
+ char *obj;
+
+ hs_init(&argc, &argv);
+
+ initLinker_(0);
+
+ // Load object file argv[1] repeatedly
+
+ if (argc != 2) {
+ errorBelch("usage: T7072-main <object-file>");
+ exit(1);
+ }
+
+ obj = argv[1];
+
+ r = loadObj(obj);
+ if (!r) {
+ debugBelch("loadObj(%s) failed\n", obj);
+ exit(1);
+ }
+ r = resolveObjs();
+ if (!r) {
+ debugBelch("resolveObjs failed\n");
+ unloadObj(obj);
+ exit(1);
+ }
+ debugBelch("loading succeeded");
+
+ hs_exit();
+ return 0;
+}
diff --git a/testsuite/tests/rts/linker/T7072-obj.c b/testsuite/tests/rts/linker/T7072-obj.c
new file mode 100644
index 0000000000..d0b6af5738
--- /dev/null
+++ b/testsuite/tests/rts/linker/T7072-obj.c
@@ -0,0 +1,17 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+
+typedef int stat_func(const char*, struct stat*);
+
+stat_func *foo = &stat;
+
+void stat_test(void)
+{
+ struct stat buf;
+
+ printf("About to stat-test.c\n");
+ foo("stat-test.c", &buf);
+ printf("Done\n");
+}
diff --git a/testsuite/tests/rts/linker/T7072.stderr b/testsuite/tests/rts/linker/T7072.stderr
new file mode 100644
index 0000000000..dfd9cfbe46
--- /dev/null
+++ b/testsuite/tests/rts/linker/T7072.stderr
@@ -0,0 +1 @@
+loading succeeded \ No newline at end of file
diff --git a/testsuite/tests/rts/linker/all.T b/testsuite/tests/rts/linker/all.T
index ba4837fc23..5789c860c7 100644
--- a/testsuite/tests/rts/linker/all.T
+++ b/testsuite/tests/rts/linker/all.T
@@ -102,3 +102,10 @@ test('rdynamic', [ unless(opsys('linux') or opsys('mingw32'), skip)
, omit_ways(['ghci'])
],
compile_and_run, ['-rdynamic -package ghc'])
+
+
+test('T7072',
+ [extra_files(['T7072-main.c', 'T7072-obj.c']),
+ unless(opsys('linux'), skip),
+ req_rts_linker],
+ makefile_test, ['T7072'])