# include <mach-o/loader.h>
# include <mach-o/nlist.h>
# include <mach-o/reloc.h>
+#if !defined(HAVE_DLFCN_H)
# include <mach-o/dyld.h>
+#endif
#if defined(powerpc_HOST_ARCH)
# include <mach-o/ppc/reloc.h>
#endif
static int ocVerifyImage_ELF ( ObjectCode* oc );
static int ocGetNames_ELF ( ObjectCode* oc );
static int ocResolve_ELF ( ObjectCode* oc );
-#if defined(powerpc_HOST_ARCH)
+#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
static int ocAllocateSymbolExtras_ELF ( ObjectCode* oc );
#endif
#elif defined(OBJFORMAT_PEi386)
#endif
#endif
-#if defined(x86_64_HOST_ARCH) && defined(OBJFORMAT_ELF)
-static void*x86_64_high_symbol( char *lbl, void *addr );
-#endif
+/* on x86_64 we have a problem with relocating symbol references in
+ * code that was compiled without -fPIC. By default, the small memory
+ * model is used, which assumes that symbol references can fit in a
+ * 32-bit slot. The system dynamic linker makes this work for
+ * references to shared libraries by either (a) allocating a jump
+ * table slot for code references, or (b) moving the symbol at load
+ * time (and copying its contents, if necessary) for data references.
+ *
+ * We unfortunately can't tell whether symbol references are to code
+ * or data. So for now we assume they are code (the vast majority
+ * are), and allocate jump-table slots. Unfortunately this will
+ * SILENTLY generate crashing code for data references. This hack is
+ * enabled by X86_64_ELF_NONPIC_HACK.
+ *
+ * One workaround is to use shared Haskell libraries. This is
+ * coming. Another workaround is to keep the static libraries but
+ * compile them with -fPIC, because that will generate PIC references
+ * to data which can be relocated. The PIC code is still too green to
+ * do this systematically, though.
+ *
+ * See bug #781
+ * See thread http://www.haskell.org/pipermail/cvs-ghc/2007-September/038458.html
+ */
+#define X86_64_ELF_NONPIC_HACK 1
/* -----------------------------------------------------------------------------
* Built-in symbols from the RTS
#if !defined (mingw32_HOST_OS)
#define RTS_POSIX_ONLY_SYMBOLS \
+ Sym(lockFile) \
+ Sym(unlockFile) \
SymX(signal_handlers) \
SymX(stg_sig_install) \
Sym(nocldstop)
#define RTS_MINGW_EXTRA_SYMS
#endif
+#if HAVE_GETTIMEOFDAY
+#define RTS_MINGW_GETTIMEOFDAY_SYM Sym(gettimeofday)
+#else
+#define RTS_MINGW_GETTIMEOFDAY_SYM /**/
+#endif
+
/* These are statically linked from the mingw libraries into the ghc
executable, so we have to employ this hack. */
#define RTS_MINGW_ONLY_SYMBOLS \
Sym(readdir) \
Sym(rewinddir) \
RTS_MINGW_EXTRA_SYMS \
+ RTS_MINGW_GETTIMEOFDAY_SYM \
Sym(closedir)
#endif
SymX(console_handler)
#endif
+#define RTS_LIBFFI_SYMBOLS \
+ Sym(ffi_prep_cif) \
+ Sym(ffi_call) \
+ Sym(ffi_type_void) \
+ Sym(ffi_type_float) \
+ Sym(ffi_type_double) \
+ Sym(ffi_type_sint64) \
+ Sym(ffi_type_uint64) \
+ Sym(ffi_type_sint32) \
+ Sym(ffi_type_uint32) \
+ Sym(ffi_type_sint16) \
+ Sym(ffi_type_uint16) \
+ Sym(ffi_type_sint8) \
+ Sym(ffi_type_uint8) \
+ Sym(ffi_type_pointer)
+
#ifdef TABLES_NEXT_TO_CODE
#define RTS_RET_SYMBOLS /* nothing */
#else
SymX(stg_ap_pppppp_ret)
#endif
+/* On Windows, we link libgmp.a statically into libHSrts.dll */
+#ifdef mingw32_HOST_OS
+#define GMP_SYMS \
+ SymX(__gmpz_cmp) \
+ SymX(__gmpz_cmp_si) \
+ SymX(__gmpz_cmp_ui) \
+ SymX(__gmpz_get_si) \
+ SymX(__gmpz_get_ui)
+#else
+#define GMP_SYMS \
+ SymExtern(__gmpz_cmp) \
+ SymExtern(__gmpz_cmp_si) \
+ SymExtern(__gmpz_cmp_ui) \
+ SymExtern(__gmpz_get_si) \
+ SymExtern(__gmpz_get_ui)
+#endif
+
#define RTS_SYMBOLS \
Maybe_Stable_Names \
- Sym(StgReturn) \
+ SymX(StgReturn) \
SymX(stg_enter_info) \
SymX(stg_gc_void_info) \
SymX(__stg_gc_enter_1) \
SymX(__encodeDouble) \
SymX(__encodeFloat) \
SymX(addDLL) \
- SymX(__gmpn_gcd_1) \
- SymX(__gmpz_cmp) \
- SymX(__gmpz_cmp_si) \
- SymX(__gmpz_cmp_ui) \
- SymX(__gmpz_get_si) \
- SymX(__gmpz_get_ui) \
+ GMP_SYMS \
SymX(__int_encodeDouble) \
+ SymX(__word_encodeDouble) \
+ SymX(__2Int_encodeDouble) \
SymX(__int_encodeFloat) \
+ SymX(__word_encodeFloat) \
SymX(andIntegerzh_fast) \
SymX(atomicallyzh_fast) \
SymX(barf) \
SymX(createAdjustor) \
SymX(decodeDoublezh_fast) \
SymX(decodeFloatzh_fast) \
+ SymX(decodeDoublezu2Intzh_fast) \
+ SymX(decodeFloatzuIntzh_fast) \
SymX(defaultsHook) \
SymX(delayzh_fast) \
SymX(deRefWeakzh_fast) \
SymX(genSymZh) \
SymX(genericRaise) \
SymX(getProgArgv) \
+ SymX(getFullProgArgv) \
SymX(getStablePtr) \
SymX(hs_init) \
SymX(hs_exit) \
SymX(hs_perform_gc) \
SymX(hs_free_stable_ptr) \
SymX(hs_free_fun_ptr) \
+ SymX(hs_hpc_rootModule) \
SymX(initLinker) \
SymX(unpackClosurezh_fast) \
SymX(getApStackValzh_fast) \
SymX(rts_getDouble) \
SymX(rts_getFloat) \
SymX(rts_getInt) \
+ SymX(rts_getInt8) \
+ SymX(rts_getInt16) \
SymX(rts_getInt32) \
+ SymX(rts_getInt64) \
SymX(rts_getPtr) \
SymX(rts_getFunPtr) \
SymX(rts_getStablePtr) \
SymX(rts_getThreadId) \
SymX(rts_getWord) \
+ SymX(rts_getWord8) \
+ SymX(rts_getWord16) \
SymX(rts_getWord32) \
+ SymX(rts_getWord64) \
SymX(rts_lock) \
SymX(rts_mkBool) \
SymX(rts_mkChar) \
SymX(rts_mkDouble) \
SymX(rts_mkFloat) \
SymX(rts_mkInt) \
+ SymX(rts_mkInt8) \
SymX(rts_mkInt16) \
SymX(rts_mkInt32) \
SymX(rts_mkInt64) \
- SymX(rts_mkInt8) \
SymX(rts_mkPtr) \
SymX(rts_mkFunPtr) \
SymX(rts_mkStablePtr) \
SymX(rts_mkString) \
SymX(rts_mkWord) \
+ SymX(rts_mkWord8) \
SymX(rts_mkWord16) \
SymX(rts_mkWord32) \
SymX(rts_mkWord64) \
- SymX(rts_mkWord8) \
SymX(rts_unlock) \
SymX(rtsSupportsBoundThreads) \
SymX(__hscore_get_saved_termios) \
SymX(stg_CAF_BLACKHOLE_info) \
SymX(awakenBlockedQueue) \
SymX(stg_CHARLIKE_closure) \
- SymX(stg_EMPTY_MVAR_info) \
+ SymX(stg_MVAR_CLEAN_info) \
+ SymX(stg_MVAR_DIRTY_info) \
SymX(stg_IND_STATIC_info) \
SymX(stg_INTLIKE_closure) \
SymX(stg_MUT_ARR_PTRS_DIRTY_info) \
SymX(writeTVarzh_fast) \
SymX(xorIntegerzh_fast) \
SymX(yieldzh_fast) \
- SymX(stg_interp_constr_entry) \
+ Sym(stg_interp_constr_entry) \
SymX(allocateExec) \
SymX(freeExec) \
SymX(getAllocations) \
SymX(revertCAFs) \
SymX(RtsFlags) \
+ Sym(rts_breakpoint_io_action) \
+ Sym(rts_stop_next_breakpoint) \
+ Sym(rts_stop_on_exception) \
+ SymX(stopTimer) \
+ SymX(n_capabilities) \
RTS_USER_SIGNALS_SYMBOLS
#ifdef SUPPORT_LONG_LONGS
/* entirely bogus claims about types of these symbols */
#define Sym(vvv) extern void vvv(void);
+#if defined(__PIC__) && defined(mingw32_TARGET_OS)
+#define SymExtern(vvv) extern void _imp__ ## vvv (void);
+#else
+#define SymExtern(vvv) SymX(vvv)
+#endif
#define SymX(vvv) /**/
#define SymX_redirect(vvv,xxx) /**/
RTS_SYMBOLS
RTS_CYGWIN_ONLY_SYMBOLS
RTS_DARWIN_ONLY_SYMBOLS
RTS_LIBGCC_SYMBOLS
+RTS_LIBFFI_SYMBOLS
#undef Sym
#undef SymX
#undef SymX_redirect
+#undef SymExtern
#ifdef LEADING_UNDERSCORE
#define MAYBE_LEADING_UNDERSCORE_STR(s) ("_" s)
#define Sym(vvv) { MAYBE_LEADING_UNDERSCORE_STR(#vvv), \
(void*)(&(vvv)) },
#define SymX(vvv) Sym(vvv)
+#define SymExtern(vvv) { MAYBE_LEADING_UNDERSCORE_STR(#vvv), \
+ (void*)DLL_IMPORT_DATA_REF(vvv) },
// SymX_redirect allows us to redirect references to one symbol to
// another symbol. See newCAF/newDynCAF for an example.
RTS_CYGWIN_ONLY_SYMBOLS
RTS_DARWIN_ONLY_SYMBOLS
RTS_LIBGCC_SYMBOLS
+ RTS_LIBFFI_SYMBOLS
#if defined(darwin_HOST_OS) && defined(i386_HOST_ARCH)
// dyld stub code contains references to this,
// but it should never be called because we treat
static OpenedDLL* opened_dlls = NULL;
#endif
-char *
+const char *
addDLL( char *dll_name )
{
# if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO)
/* ------------------- ELF DLL loader ------------------- */
void *hdl;
- char *errmsg;
+ const char *errmsg;
initLinker();
if (val == NULL) {
# if defined(OBJFORMAT_ELF)
-# if defined(x86_64_HOST_ARCH)
- val = dlsym(dl_prog_handle, lbl);
- if (val >= (void *)0x80000000) {
- void *new_val;
- new_val = x86_64_high_symbol(lbl, val);
- IF_DEBUG(linker,debugBelch("lookupSymbol: relocating out of range symbol: %s = %p, now %p\n", lbl, val, new_val));
- return new_val;
- } else {
- return val;
- }
-# else
return dlsym(dl_prog_handle, lbl);
-# endif
# elif defined(OBJFORMAT_MACHO)
+# if HAVE_DLFCN_H
+ /* On OS X 10.3 and later, we use dlsym instead of the old legacy
+ interface.
+
+ HACK: On OS X, global symbols are prefixed with an underscore.
+ However, dlsym wants us to omit the leading underscore from the
+ symbol name. For now, we simply strip it off here (and ONLY
+ here).
+ */
+ ASSERT(lbl[0] == '_');
+ return dlsym(dl_prog_handle, lbl+1);
+# else
if(NSIsSymbolNameDefined(lbl)) {
NSSymbol symbol = NSLookupAndBindSymbol(lbl);
return NSAddressOfSymbol(symbol);
} else {
return NULL;
}
+# endif /* HAVE_DLFCN_H */
# elif defined(OBJFORMAT_PEi386)
OpenedDLL* o_dll;
void* sym;
/* Link objects into the lower 2Gb on x86_64. GHC assumes the
* small memory model on this architecture (see gcc docs,
* -mcmodel=small).
+ *
+ * MAP_32BIT not available on OpenBSD/amd64
*/
-#ifdef x86_64_HOST_ARCH
+#if defined(x86_64_HOST_ARCH) && defined(MAP_32BIT)
#define EXTRA_MAP_FLAGS MAP_32BIT
#else
#define EXTRA_MAP_FLAGS 0
#endif
+ /* MAP_ANONYMOUS is MAP_ANON on some systems, e.g. OpenBSD */
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
oc->image = mmap(map_addr, n, PROT_EXEC|PROT_READ|PROT_WRITE,
MAP_PRIVATE|EXTRA_MAP_FLAGS, fd, 0);
if (oc->image == MAP_FAILED)
# if defined(OBJFORMAT_MACHO) && (defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH))
r = ocAllocateSymbolExtras_MachO ( oc );
if (!r) { return r; }
-# elif defined(OBJFORMAT_ELF) && defined(powerpc_HOST_ARCH)
+# elif defined(OBJFORMAT_ELF) && (defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH))
r = ocAllocateSymbolExtras_ELF ( oc );
if (!r) { return r; }
#endif
* them right next to the object code itself.
*/
-#if defined(powerpc_HOST_ARCH) || (defined(x86_64_HOST_ARCH) \
- && defined(darwin_TARGET_OS))
+#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
/*
ocAllocateSymbolExtras
int pagesize, n, m;
#endif
int aligned;
+#ifndef USE_MMAP
int misalignment = 0;
-#if darwin_HOST_OS
+#ifdef darwin_HOST_OS
misalignment = oc->misalignment;
#endif
+#endif
if( count > 0 )
{
&& 0 != strcmp(".ctors", sectab_i->Name)
/* ignore section generated from .ident */
&& 0!= strcmp("/4", sectab_i->Name)
+ /* ignore unknown section that appeared in gcc 3.4.5(?) */
+ && 0!= strcmp(".reloc", sectab_i->Name)
) {
errorBelch("Unknown PEi386 section name `%s' (while processing: %s)", sectab_i->Name, oc->fileName);
return 0;
#endif
#if !defined(openbsd_HOST_OS)
-#include <elf.h>
+# include <elf.h>
#else
/* openbsd elf has things in different places, with diff names */
-#include <elf_abi.h>
-#include <machine/reloc.h>
-#define R_386_32 RELOC_32
-#define R_386_PC32 RELOC_PC32
+# include <elf_abi.h>
+# include <machine/reloc.h>
+# define R_386_32 RELOC_32
+# define R_386_PC32 RELOC_PC32
#endif
+/* If elf.h doesn't define it */
+# ifndef R_X86_64_PC64
+# define R_X86_64_PC64 24
+# endif
+
/*
* Define a set of types which can be used for both ELF32 and ELF64
*/
#endif
-#if x86_64_HOST_ARCH
-// On x86_64, 32-bit relocations are often used, which requires that
-// we can resolve a symbol to a 32-bit offset. However, shared
-// libraries are placed outside the 2Gb area, which leaves us with a
-// problem when we need to give a 32-bit offset to a symbol in a
-// shared library.
-//
-// For a function symbol, we can allocate a bounce sequence inside the
-// 2Gb area and resolve the symbol to this. The bounce sequence is
-// simply a long jump instruction to the real location of the symbol.
-//
-// For data references, we're screwed.
-//
-typedef struct {
- unsigned char jmp[8]; /* 6 byte instruction: jmpq *0x00000002(%rip) */
- void *addr;
-} x86_64_bounce;
-
-#define X86_64_BB_SIZE 1024
-
-static x86_64_bounce *x86_64_bounce_buffer = NULL;
-static nat x86_64_bb_next_off;
-
-static void*
-x86_64_high_symbol( char *lbl, void *addr )
-{
- x86_64_bounce *bounce;
-
- if ( x86_64_bounce_buffer == NULL ||
- x86_64_bb_next_off >= X86_64_BB_SIZE ) {
- x86_64_bounce_buffer =
- mmap(NULL, X86_64_BB_SIZE * sizeof(x86_64_bounce),
- PROT_EXEC|PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_32BIT|MAP_ANONYMOUS, -1, 0);
- if (x86_64_bounce_buffer == MAP_FAILED) {
- barf("x86_64_high_symbol: mmap failed");
- }
- x86_64_bb_next_off = 0;
- }
- bounce = &x86_64_bounce_buffer[x86_64_bb_next_off];
- bounce->jmp[0] = 0xff;
- bounce->jmp[1] = 0x25;
- bounce->jmp[2] = 0x02;
- bounce->jmp[3] = 0x00;
- bounce->jmp[4] = 0x00;
- bounce->jmp[5] = 0x00;
- bounce->addr = addr;
- x86_64_bb_next_off++;
-
- IF_DEBUG(linker, debugBelch("x86_64: allocated bounce entry for %s->%p at %p\n",
- lbl, addr, bounce));
-
- insertStrHashTable(symhash, lbl, bounce);
- return bounce;
-}
-#endif
-
-
/*
* Generic ELF functions
*/
case EM_PPC: IF_DEBUG(linker,debugBelch( "powerpc32" )); break;
#ifdef EM_X86_64
case EM_X86_64: IF_DEBUG(linker,debugBelch( "x86_64" )); break;
+#elif defined(EM_AMD64)
+ case EM_AMD64: IF_DEBUG(linker,debugBelch( "amd64" )); break;
#endif
default: IF_DEBUG(linker,debugBelch( "unknown" ));
- errorBelch("%s: unknown architecture", oc->fileName);
+ errorBelch("%s: unknown architecture (e_machine == %d)"
+ , oc->fileName, ehdr->e_machine);
return 0;
}
{
StgInt64 off = value - P;
if (off >= 0x7fffffffL || off < -0x80000000L) {
- barf("R_X86_64_PC32 relocation out of range: %s = %p",
- symbol, off);
- }
+#if X86_64_ELF_NONPIC_HACK
+ StgInt64 pltAddress = (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)
+ -> jumpIsland;
+ off = pltAddress + A - P;
+#else
+ barf("R_X86_64_PC32 relocation out of range: %s = %p\nRecompile %s with -fPIC.",
+ symbol, off, oc->fileName );
+#endif
+ }
+ *(Elf64_Word *)P = (Elf64_Word)off;
+ break;
+ }
+
+ case R_X86_64_PC64:
+ {
+ StgInt64 off = value - P;
*(Elf64_Word *)P = (Elf64_Word)off;
break;
}
case R_X86_64_32:
if (value >= 0x7fffffffL) {
- barf("R_X86_64_32 relocation out of range: %s = %p\n",
- symbol, value);
- }
+#if X86_64_ELF_NONPIC_HACK
+ StgInt64 pltAddress = (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)
+ -> jumpIsland;
+ value = pltAddress + A;
+#else
+ barf("R_X86_64_32 relocation out of range: %s = %p\nRecompile %s with -fPIC.",
+ symbol, value, oc->fileName );
+#endif
+ }
*(Elf64_Word *)P = (Elf64_Word)value;
break;
case R_X86_64_32S:
if ((StgInt64)value > 0x7fffffffL || (StgInt64)value < -0x80000000L) {
- barf("R_X86_64_32S relocation out of range: %s = %p\n",
- symbol, value);
+#if X86_64_ELF_NONPIC_HACK
+ StgInt64 pltAddress = (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)
+ -> jumpIsland;
+ value = pltAddress + A;
+#else
+ barf("R_X86_64_32S relocation out of range: %s = %p\nRecompile %s with -fPIC.",
+ symbol, value, oc->fileName );
+#endif
}
*(Elf64_Sword *)P = (Elf64_Sword)value;
break;
+
+ case R_X86_64_GOTPCREL:
+ {
+ StgInt64 gotAddress = (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)->addr;
+ StgInt64 off = gotAddress + A - P;
+ *(Elf64_Word *)P = (Elf64_Word)off;
+ break;
+ }
+
+ case R_X86_64_PLT32:
+ {
+ StgInt64 off = value - P;
+ if (off >= 0x7fffffffL || off < -0x80000000L) {
+ StgInt64 pltAddress = (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)
+ -> jumpIsland;
+ off = pltAddress + A - P;
+ }
+ *(Elf64_Word *)P = (Elf64_Word)off;
+ break;
+ }
#endif
default:
#endif /* ia64 */
/*
- * PowerPC ELF specifics
+ * PowerPC & X86_64 ELF specifics
*/
-#ifdef powerpc_HOST_ARCH
+#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
static int ocAllocateSymbolExtras_ELF( ObjectCode *oc )
{
if( shdr[i].sh_entsize != sizeof( Elf_Sym ) )
{
errorBelch( "The entry size (%d) of the symtab isn't %d\n",
- shdr[i].sh_entsize, sizeof( Elf_Sym ) );
+ (int) shdr[i].sh_entsize, (int) sizeof( Elf_Sym ) );
return 0;
}
#ifdef powerpc_HOST_ARCH
// In the .o file, this should be a relative jump to NULL
// and we'll change it to a relative jump to the symbol
- ASSERT(-word == reloc->r_address);
+ ASSERT(word + reloc->r_address == 0);
jumpIsland = (unsigned long)
&makeSymbolExtra(oc,
reloc->r_symbolnum,