diff options
author | Ben Gamari <ben@smart-cactus.org> | 2021-09-24 14:53:59 -0400 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2022-08-15 17:11:26 -0400 |
commit | cdf69083a8fc80825aba2e13f8ac1fec6a024b4c (patch) | |
tree | 573c25303f62842ab7e53087857227390e0d1554 | |
parent | e2832cbd830a98541193ec07dddf94fcd247f026 (diff) | |
download | haskell-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.c | 59 |
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; } |