summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2021-09-24 14:53:59 -0400
committerBen Gamari <ben@smart-cactus.org>2022-08-15 17:11:26 -0400
commitcdf69083a8fc80825aba2e13f8ac1fec6a024b4c (patch)
tree573c25303f62842ab7e53087857227390e0d1554
parente2832cbd830a98541193ec07dddf94fcd247f026 (diff)
downloadhaskell-cdf69083a8fc80825aba2e13f8ac1fec6a024b4c.tar.gz
rts/linker: Resolve iconv_* on FreeBSD
FreeBSD's libiconv includes an implementation of the iconv_* functions in libc. Unfortunately these can only be resolved using dlvsym, which is how the RTS linker usually resolves such functions. To fix this we include an ad-hoc special case for iconv_*. Fixes #20354. (cherry picked from commit 844df61e8de5e2d9a058e6cbe388802755fc0305) (cherry picked from commit d8961a2dc974b7f8f8752781c4aec261ae8f8c0f)
-rw-r--r--rts/Linker.c59
1 files changed, 50 insertions, 9 deletions
diff --git a/rts/Linker.c b/rts/Linker.c
index 1faff3b371..12d5418d02 100644
--- a/rts/Linker.c
+++ b/rts/Linker.c
@@ -80,6 +80,33 @@
#if defined(dragonfly_HOST_OS)
#include <sys/tls.h>
#endif
+
+/*
+ * Note [iconv and FreeBSD]
+ * ~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * On FreeBSD libc.so provides an implementation of the iconv_* family of
+ * functions. However, due to their implementation, these symbols cannot be
+ * resolved via dlsym(); rather, they can only be resolved using the
+ * explicitly-versioned dlvsym().
+ *
+ * This is problematic for the RTS linker since we may be asked to load
+ * an object that depends upon iconv. To handle this we include a set of
+ * fallback cases for these functions, allowing us to resolve them to the
+ * symbols provided by the libc against which the RTS is linked.
+ *
+ * See #20354.
+ */
+
+#if defined(freebsd_HOST_OS)
+extern void iconvctl();
+extern void iconv_open_into();
+extern void iconv_open();
+extern void iconv_close();
+extern void iconv_canonicalize();
+extern void iconv();
+#endif
+
/*
Note [runtime-linker-support]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -637,6 +664,10 @@ internal_dlsym(const char *symbol) {
}
RELEASE_LOCK(&dl_mutex);
+ IF_DEBUG(linker, debugBelch("internal_dlsym: looking for symbol '%s' in special cases\n", symbol));
+# define SPECIAL_SYMBOL(sym) \
+ if (strcmp(symbol, #sym) == 0) return (void*)&sym;
+
# 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
@@ -646,18 +677,28 @@ internal_dlsym(const char *symbol) {
// 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));
+ SPECIAL_SYMBOL(stat);
+ SPECIAL_SYMBOL(fstat);
+ SPECIAL_SYMBOL(lstat);
+ SPECIAL_SYMBOL(stat64);
+ SPECIAL_SYMBOL(fstat64);
+ SPECIAL_SYMBOL(lstat64);
+ SPECIAL_SYMBOL(atexit);
+ SPECIAL_SYMBOL(mknod);
+# endif
- 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;
+ // See Note [iconv and FreeBSD]
+# if defined(freebsd_HOST_OS)
+ SPECIAL_SYMBOL(iconvctl);
+ SPECIAL_SYMBOL(iconv_open_into);
+ SPECIAL_SYMBOL(iconv_open);
+ SPECIAL_SYMBOL(iconv_close);
+ SPECIAL_SYMBOL(iconv_canonicalize);
+ SPECIAL_SYMBOL(iconv);
# endif
+#undef SPECIAL_SYMBOL
+
// we failed to find the symbol
return NULL;
}