#include "HsFFI.h"
#include "sm/Storage.h"
+#include "Stats.h"
#include "Hash.h"
#include "LinkerInternals.h"
#include "RtsUtils.h"
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
+#include <assert.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#if defined(linux_HOST_OS) || defined(solaris2_HOST_OS) || defined(freebsd_HOST_OS) || defined(dragonfly_HOST_OS) || defined(netbsd_HOST_OS) || defined(openbsd_HOST_OS)
# define OBJFORMAT_ELF
+# include <regex.h> // regex is already used by dlopen() so this is OK
+ // to use here without requiring an additional lib
#elif defined(cygwin32_HOST_OS) || defined (mingw32_HOST_OS)
# define OBJFORMAT_PEi386
# include <windows.h>
# include <math.h>
#elif defined(darwin_HOST_OS)
# define OBJFORMAT_MACHO
+# include <regex.h>
# include <mach-o/loader.h>
# include <mach-o/nlist.h>
# include <mach-o/reloc.h>
SymI_HasProto(RET_SEMI_loads_avoided)
+// On most platforms, the garbage collector rewrites references
+// to small integer and char objects to a set of common, shared ones.
+//
+// We don't do this when compiling to Windows DLLs at the moment because
+// it doesn't support cross package data references well.
+//
+#if defined(__PIC__) && defined(mingw32_HOST_OS)
+#define RTS_INTCHAR_SYMBOLS
+#else
+#define RTS_INTCHAR_SYMBOLS \
+ SymI_HasProto(stg_CHARLIKE_closure) \
+ SymI_HasProto(stg_INTLIKE_closure)
+#endif
+
+
#define RTS_SYMBOLS \
Maybe_Stable_Names \
RTS_TICKY_SYMBOLS \
SymI_HasProto(stg_CAF_BLACKHOLE_info) \
SymI_HasProto(__stg_EAGER_BLACKHOLE_info) \
SymI_HasProto(startTimer) \
- SymI_HasProto(stg_CHARLIKE_closure) \
SymI_HasProto(stg_MVAR_CLEAN_info) \
SymI_HasProto(stg_MVAR_DIRTY_info) \
SymI_HasProto(stg_IND_STATIC_info) \
- SymI_HasProto(stg_INTLIKE_closure) \
SymI_HasProto(stg_ARR_WORDS_info) \
SymI_HasProto(stg_MUT_ARR_PTRS_DIRTY_info) \
SymI_HasProto(stg_MUT_ARR_PTRS_FROZEN_info) \
SymI_HasProto(stg_writeTVarzh) \
SymI_HasProto(stg_yieldzh) \
SymI_NeedsProto(stg_interp_constr_entry) \
- SymI_HasProto(alloc_blocks) \
SymI_HasProto(alloc_blocks_lim) \
- SymI_HasProto(allocateLocal) \
+ SymI_HasProto(g0) \
+ SymI_HasProto(allocate) \
SymI_HasProto(allocateExec) \
SymI_HasProto(freeExec) \
SymI_HasProto(getAllocations) \
SymI_HasProto(n_capabilities) \
SymI_HasProto(stg_traceCcszh) \
SymI_HasProto(stg_traceEventzh) \
- RTS_USER_SIGNALS_SYMBOLS
+ RTS_USER_SIGNALS_SYMBOLS \
+ RTS_INTCHAR_SYMBOLS
// 64-bit support functions in libgcc.a
SymI_NeedsProto(__muldi3) \
SymI_NeedsProto(__ashldi3) \
SymI_NeedsProto(__ashrdi3) \
- SymI_NeedsProto(__lshrdi3) \
- SymI_NeedsProto(__eprintf)
+ SymI_NeedsProto(__lshrdi3)
#else
#define RTS_LIBGCC_SYMBOLS
#endif
#if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO)
static void *dl_prog_handle;
+static regex_t re_invalid;
+static regex_t re_realso;
+#ifdef THREADED_RTS
+static Mutex dl_mutex; // mutex to protect dlopen/dlerror critical section
+#endif
#endif
void
initLinker( void )
{
RtsSymbolVal *sym;
+#if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO)
+ int compileResult;
+#endif
/* Make initLinker idempotent, so we can call it
before evey relevant operation; that means we
linker_init_done = 1;
}
+#if defined(THREADED_RTS) && (defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO))
+ initMutex(&dl_mutex);
+#endif
stablehash = allocStrHashTable();
symhash = allocStrHashTable();
# else
dl_prog_handle = dlopen(NULL, RTLD_LAZY);
# endif /* RTLD_DEFAULT */
+
+ compileResult = regcomp(&re_invalid,
+ "(([^ \t()])+\\.so([^ \t:()])*):([ \t])*invalid ELF header",
+ REG_EXTENDED);
+ ASSERT( compileResult == 0 );
+ compileResult = regcomp(&re_realso,
+ "GROUP *\\( *(([^ )])+)",
+ REG_EXTENDED);
+ ASSERT( compileResult == 0 );
# endif
#if defined(x86_64_HOST_ARCH)
#endif
}
+void
+exitLinker( void ) {
+#if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO)
+ if (linker_init_done == 1) {
+ regfree(&re_invalid);
+ regfree(&re_realso);
+#ifdef THREADED_RTS
+ closeMutex(&dl_mutex);
+#endif
+ }
+#endif
+}
+
/* -----------------------------------------------------------------------------
* Loading DLL or .so dynamic libraries
* -----------------------------------------------------------------------------
static OpenedDLL* opened_dlls = NULL;
#endif
-const char *
-addDLL( char *dll_name )
-{
# if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO)
- /* ------------------- ELF DLL loader ------------------- */
- void *hdl;
- const char *errmsg;
- initLinker();
+static char *
+internal_dlopen(const char *dll_name)
+{
+ void *hdl;
+ char *errmsg, *errmsg_copy;
// omitted: RTLD_NOW
// see http://www.haskell.org/pipermail/cvs-ghc/2007-September/038570.html
- hdl= dlopen(dll_name, RTLD_LAZY | RTLD_GLOBAL);
+ IF_DEBUG(linker,
+ debugBelch("internal_dlopen: dll_name = '%s'\n", dll_name));
+
+ //-------------- Begin critical section ------------------
+ // This critical section is necessary because dlerror() is not
+ // required to be reentrant (see POSIX -- IEEE Std 1003.1-2008)
+ // Also, the error message returned must be copied to preserve it
+ // (see POSIX also)
+
+ ACQUIRE_LOCK(&dl_mutex);
+ hdl = dlopen(dll_name, RTLD_LAZY | RTLD_GLOBAL);
+ errmsg = NULL;
if (hdl == NULL) {
/* dlopen failed; return a ptr to the error msg. */
errmsg = dlerror();
if (errmsg == NULL) errmsg = "addDLL: unknown error";
- return errmsg;
- } else {
+ errmsg_copy = stgMallocBytes(strlen(errmsg)+1, "addDLL");
+ strcpy(errmsg_copy, errmsg);
+ errmsg = errmsg_copy;
+ }
+ RELEASE_LOCK(&dl_mutex);
+ //--------------- End critical section -------------------
+
+ return errmsg;
+}
+# endif
+
+const char *
+addDLL( char *dll_name )
+{
+# if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO)
+ /* ------------------- ELF DLL loader ------------------- */
+
+#define NMATCH 5
+ regmatch_t match[NMATCH];
+ char *errmsg;
+ FILE* fp;
+ size_t match_length;
+#define MAXLINE 1000
+ char line[MAXLINE];
+ int result;
+
+ initLinker();
+
+ IF_DEBUG(linker, debugBelch("addDLL: dll_name = '%s'\n", dll_name));
+ errmsg = internal_dlopen(dll_name);
+
+ if (errmsg == NULL) {
return NULL;
}
- /*NOTREACHED*/
+
+ // GHC Trac ticket #2615
+ // On some systems (e.g., Gentoo Linux) dynamic files (e.g. libc.so)
+ // contain linker scripts rather than ELF-format object code. This
+ // code handles the situation by recognizing the real object code
+ // file name given in the linker script.
+ //
+ // If an "invalid ELF header" error occurs, it is assumed that the
+ // .so file contains a linker script instead of ELF object code.
+ // In this case, the code looks for the GROUP ( ... ) linker
+ // directive. If one is found, the first file name inside the
+ // parentheses is treated as the name of a dynamic library and the
+ // code attempts to dlopen that file. If this is also unsuccessful,
+ // an error message is returned.
+
+ // see if the error message is due to an invalid ELF header
+ IF_DEBUG(linker, debugBelch("errmsg = '%s'\n", errmsg));
+ result = regexec(&re_invalid, errmsg, (size_t) NMATCH, match, 0);
+ IF_DEBUG(linker, debugBelch("result = %i\n", result));
+ if (result == 0) {
+ // success -- try to read the named file as a linker script
+ match_length = (size_t) stg_min((match[1].rm_eo - match[1].rm_so),
+ MAXLINE-1);
+ strncpy(line, (errmsg+(match[1].rm_so)),match_length);
+ line[match_length] = '\0'; // make sure string is null-terminated
+ IF_DEBUG(linker, debugBelch ("file name = '%s'\n", line));
+ if ((fp = fopen(line, "r")) == NULL) {
+ return errmsg; // return original error if open fails
+ }
+ // try to find a GROUP ( ... ) command
+ while (fgets(line, MAXLINE, fp) != NULL) {
+ IF_DEBUG(linker, debugBelch("input line = %s", line));
+ if (regexec(&re_realso, line, (size_t) NMATCH, match, 0) == 0) {
+ // success -- try to dlopen the first named file
+ IF_DEBUG(linker, debugBelch("match%s\n",""));
+ line[match[1].rm_eo] = '\0';
+ errmsg = internal_dlopen(line+match[1].rm_so);
+ break;
+ }
+ // if control reaches here, no GROUP ( ... ) directive was found
+ // and the original error message is returned to the caller
+ }
+ fclose(fp);
+ }
+ return errmsg;
# elif defined(OBJFORMAT_PEi386)
/* ------------------- Win32 DLL loader ------------------- */
i++;
}
#endif
- else
+ else
{
barf ("Don't know how to handle this Mach-O "
"scattered relocation entry: "
oc->fileName, scat->r_type, scat->r_address);
return 0;
}
-
+
#ifdef powerpc_HOST_ARCH
if(scat->r_type == GENERIC_RELOC_VANILLA
|| scat->r_type == PPC_RELOC_SECTDIFF)
"object file %s; entry type %ld; address %#lx\n",
oc->fileName, scat->r_type, scat->r_address);
return 0;
- }
-
+ }
+
}
else /* !(relocs[i].r_address & R_SCATTERED) */
{