X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2Frts%2FLinker.c;h=5c0ea52906c13c53ebde57fdf0d61f6923bc3b13;hb=df246f09eb5c781b7647d7ff37e94ffa6c377a9a;hp=7fbeee0648b4be5c69b853955cb98d14f18ac0b7;hpb=57f838974862a6ba2772541318d1dfdeff51cefd;p=ghc-hetmet.git diff --git a/ghc/rts/Linker.c b/ghc/rts/Linker.c index 7fbeee0..5c0ea52 100644 --- a/ghc/rts/Linker.c +++ b/ghc/rts/Linker.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------------- - * $Id: Linker.c,v 1.85 2002/03/29 20:46:50 krasimir Exp $ + * $Id: Linker.c,v 1.98 2002/07/08 14:38:26 simonpj Exp $ * * (c) The GHC Team, 2000, 2001 * @@ -7,7 +7,9 @@ * * ---------------------------------------------------------------------------*/ +#if 0 #include "PosixSource.h" +#endif #include "Rts.h" #include "RtsFlags.h" #include "HsFFI.h" @@ -30,11 +32,38 @@ #include #endif +#if defined(cygwin32_TARGET_OS) +#ifdef HAVE_DIRENT_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#include +#include +#include +#include +#include +#endif + +#if defined(ia64_TARGET_ARCH) +#define USE_MMAP +#include +#include +#endif + #if defined(linux_TARGET_OS) || defined(solaris2_TARGET_OS) || defined(freebsd_TARGET_OS) # define OBJFORMAT_ELF #elif defined(cygwin32_TARGET_OS) || defined (mingw32_TARGET_OS) # define OBJFORMAT_PEi386 # include +#elif defined(darwin_TARGET_OS) +# define OBJFORMAT_MACHO +# include +# include +# include #endif /* Hash table mapping symbol names to Symbol */ @@ -48,6 +77,10 @@ static int ocResolve_ELF ( ObjectCode* oc ); static int ocVerifyImage_PEi386 ( ObjectCode* oc ); static int ocGetNames_PEi386 ( ObjectCode* oc ); static int ocResolve_PEi386 ( ObjectCode* oc ); +#elif defined(OBJFORMAT_MACHO) +static int ocVerifyImage_MachO ( ObjectCode* oc ); +static int ocGetNames_MachO ( ObjectCode* oc ); +static int ocResolve_MachO ( ObjectCode* oc ); #endif /* ----------------------------------------------------------------------------- @@ -73,15 +106,105 @@ typedef struct _RtsSymbolVal { #endif #if !defined (mingw32_TARGET_OS) - #define RTS_POSIX_ONLY_SYMBOLS \ SymX(stg_sig_install) \ Sym(nocldstop) -#define RTS_MINGW_ONLY_SYMBOLS /**/ +#endif -#else +#if defined (cygwin32_TARGET_OS) +#define RTS_MINGW_ONLY_SYMBOLS /**/ +/* Don't have the ability to read import libs / archives, so + * we have to stupidly list a lot of what libcygwin.a + * exports; sigh. + */ +#define RTS_CYGWIN_ONLY_SYMBOLS \ + SymX(regfree) \ + SymX(regexec) \ + SymX(regerror) \ + SymX(regcomp) \ + SymX(__errno) \ + SymX(access) \ + SymX(chmod) \ + SymX(chdir) \ + SymX(close) \ + SymX(creat) \ + SymX(dup) \ + SymX(dup2) \ + SymX(fstat) \ + SymX(fcntl) \ + SymX(getcwd) \ + SymX(getenv) \ + SymX(lseek) \ + SymX(open) \ + SymX(fpathconf) \ + SymX(pathconf) \ + SymX(stat) \ + SymX(pow) \ + SymX(tanh) \ + SymX(cosh) \ + SymX(sinh) \ + SymX(atan) \ + SymX(acos) \ + SymX(asin) \ + SymX(tan) \ + SymX(cos) \ + SymX(sin) \ + SymX(exp) \ + SymX(log) \ + SymX(sqrt) \ + SymX(localtime_r) \ + SymX(gmtime_r) \ + SymX(mktime) \ + Sym(_imp___tzname) \ + SymX(gettimeofday) \ + SymX(timezone) \ + SymX(tcgetattr) \ + SymX(tcsetattr) \ + SymX(memcpy) \ + SymX(memmove) \ + SymX(realloc) \ + SymX(malloc) \ + SymX(free) \ + SymX(fork) \ + SymX(lstat) \ + SymX(isatty) \ + SymX(mkdir) \ + SymX(opendir) \ + SymX(readdir) \ + SymX(rewinddir) \ + SymX(closedir) \ + SymX(link) \ + SymX(mkfifo) \ + SymX(pipe) \ + SymX(read) \ + SymX(rename) \ + SymX(rmdir) \ + SymX(select) \ + SymX(system) \ + SymX(write) \ + SymX(strcmp) \ + SymX(strcpy) \ + SymX(strncpy) \ + SymX(strerror) \ + SymX(sigaddset) \ + SymX(sigemptyset) \ + SymX(sigprocmask) \ + SymX(umask) \ + SymX(uname) \ + SymX(unlink) \ + SymX(utime) \ + SymX(waitpid) \ + Sym(__divdi3) \ + Sym(__udivdi3) \ + Sym(__moddi3) \ + Sym(__umoddi3) -#define RTS_POSIX_ONLY_SYMBOLS +#elif !defined(mingw32_TARGET_OS) +#define RTS_MINGW_ONLY_SYMBOLS /**/ +#define RTS_CYGWIN_ONLY_SYMBOLS /**/ +#else /* defined(mingw32_TARGET_OS) */ +#define RTS_POSIX_ONLY_SYMBOLS /**/ +#define RTS_CYGWIN_ONLY_SYMBOLS /**/ /* These are statically linked from the mingw libraries into the ghc executable, so we have to employ this hack. */ @@ -166,10 +289,24 @@ typedef struct _RtsSymbolVal { Sym(init_stack) \ SymX(__stg_chk_0) \ SymX(__stg_chk_1) \ + SymX(stg_chk_2) \ + SymX(stg_chk_3) \ + SymX(stg_chk_4) \ + SymX(stg_chk_5) \ + SymX(stg_chk_6) \ + SymX(stg_chk_7) \ + SymX(stg_chk_8) \ Sym(stg_enterStackTop) \ SymX(stg_gc_d1) \ SymX(stg_gc_l1) \ SymX(__stg_gc_enter_1) \ + SymX(stg_gc_enter_2) \ + SymX(stg_gc_enter_3) \ + SymX(stg_gc_enter_4) \ + SymX(stg_gc_enter_5) \ + SymX(stg_gc_enter_6) \ + SymX(stg_gc_enter_7) \ + SymX(stg_gc_enter_8) \ SymX(stg_gc_f1) \ SymX(stg_gc_noregs) \ SymX(stg_gc_seq_1) \ @@ -216,6 +353,7 @@ typedef struct _RtsSymbolVal { SymX(divExactIntegerzh_fast) \ SymX(divModIntegerzh_fast) \ SymX(forkzh_fast) \ + SymX(forkProcesszh_fast) \ SymX(freeHaskellFunctionPtr) \ SymX(freeStablePtr) \ SymX(gcdIntegerzh_fast) \ @@ -240,6 +378,7 @@ typedef struct _RtsSymbolVal { SymX(minusIntegerzh_fast) \ SymX(mkApUpd0zh_fast) \ SymX(myThreadIdzh_fast) \ + SymX(labelThreadzh_fast) \ SymX(newArrayzh_fast) \ SymX(newBCOzh_fast) \ SymX(newByteArrayzh_fast) \ @@ -348,21 +487,41 @@ typedef struct _RtsSymbolVal { SymX(xorIntegerzh_fast) \ SymX(yieldzh_fast) -#ifndef SUPPORT_LONG_LONGS -#define RTS_LONG_LONG_SYMS /* nothing */ -#else +#ifdef SUPPORT_LONG_LONGS #define RTS_LONG_LONG_SYMS \ SymX(int64ToIntegerzh_fast) \ SymX(word64ToIntegerzh_fast) -#endif /* SUPPORT_LONG_LONGS */ +#else +#define RTS_LONG_LONG_SYMS /* nothing */ +#endif + +#ifdef ia64_TARGET_ARCH +/* force these symbols to be present */ +#define RTS_EXTRA_SYMBOLS \ + Sym(__divsf3) +#elif defined(powerpc_TARGET_ARCH) +#define RTS_EXTRA_SYMBOLS \ + Sym(__divdi3) \ + Sym(__udivdi3) \ + Sym(__moddi3) \ + Sym(__umoddi3) \ + Sym(__ashldi3) \ + Sym(__ashrdi3) \ + Sym(__lshrdi3) \ + SymX(__eprintf) +#else +#define RTS_EXTRA_SYMBOLS /* nothing */ +#endif /* entirely bogus claims about types of these symbols */ #define Sym(vvv) extern void (vvv); #define SymX(vvv) /**/ RTS_SYMBOLS RTS_LONG_LONG_SYMS +RTS_EXTRA_SYMBOLS RTS_POSIX_ONLY_SYMBOLS RTS_MINGW_ONLY_SYMBOLS +RTS_CYGWIN_ONLY_SYMBOLS #undef Sym #undef SymX @@ -379,8 +538,10 @@ RTS_MINGW_ONLY_SYMBOLS static RtsSymbolVal rtsSyms[] = { RTS_SYMBOLS RTS_LONG_LONG_SYMS + RTS_EXTRA_SYMBOLS RTS_POSIX_ONLY_SYMBOLS RTS_MINGW_ONLY_SYMBOLS + RTS_CYGWIN_ONLY_SYMBOLS { 0, 0 } /* sentinel */ }; @@ -421,7 +582,7 @@ static void ghciInsertStrHashTable ( char* obj_name, /* ----------------------------------------------------------------------------- * initialize the object linker */ -#if defined(OBJFORMAT_ELF) +#if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO) static void *dl_prog_handle; #endif @@ -437,7 +598,7 @@ initLinker( void ) ghciInsertStrHashTable("(GHCi built-in symbols)", symhash, sym->lbl, sym->addr); } -# if defined(OBJFORMAT_ELF) +# if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO) dl_prog_handle = dlopen(NULL, RTLD_LAZY); # endif } @@ -471,23 +632,14 @@ static OpenedDLL* opened_dlls = NULL; -char* -addDLL ( __attribute((unused)) char* path, char* dll_name ) +char * +addDLL( char *dll_name ) { -# if defined(OBJFORMAT_ELF) +# if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO) void *hdl; - char *buf; char *errmsg; - if (path == NULL || strlen(path) == 0) { - buf = stgMallocBytes(strlen(dll_name) + 10, "addDll"); - sprintf(buf, "lib%s.so", dll_name); - } else { - buf = stgMallocBytes(strlen(path) + 1 + strlen(dll_name) + 10, "addDll"); - sprintf(buf, "%s/lib%s.so", path, dll_name); - } - hdl = dlopen(buf, RTLD_NOW | RTLD_GLOBAL ); - free(buf); + hdl= dlopen(dll_name, RTLD_NOW | RTLD_GLOBAL); if (hdl == NULL) { /* dlopen failed; return a ptr to the error msg. */ errmsg = dlerror(); @@ -553,7 +705,7 @@ lookupSymbol( char *lbl ) val = lookupStrHashTable(symhash, lbl); if (val == NULL) { -# if defined(OBJFORMAT_ELF) +# if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO) return dlsym(dl_prog_handle, lbl); # elif defined(OBJFORMAT_PEi386) OpenedDLL* o_dll; @@ -639,6 +791,9 @@ void ghci_enquire ( char* addr ) } #endif +#ifdef ia64_TARGET_ARCH +static unsigned int PLTSize(void); +#endif /* ----------------------------------------------------------------------------- * Load an obj (populate the global symbol table, but don't resolve yet) @@ -651,7 +806,12 @@ loadObj( char *path ) ObjectCode* oc; struct stat st; int r, n; +#ifdef USE_MMAP + int fd, pagesize; + void *map_addr; +#else FILE *f; +#endif /* fprintf(stderr, "loadObj %s\n", path ); */ @@ -682,6 +842,8 @@ loadObj( char *path ) oc->formatName = "ELF"; # elif defined(OBJFORMAT_PEi386) oc->formatName = "PEi386"; +# elif defined(OBJFORMAT_MACHO) + oc->formatName = "Mach-O"; # else free(oc); barf("loadObj: not implemented on this platform"); @@ -695,7 +857,6 @@ loadObj( char *path ) strcpy(oc->fileName, path); oc->fileSize = st.st_size; - oc->image = stgMallocBytes( st.st_size, "loadObj(image)" ); oc->symbols = NULL; oc->sections = NULL; oc->lochash = allocStrHashTable(); @@ -705,22 +866,59 @@ loadObj( char *path ) oc->next = objects; objects = oc; +#ifdef USE_MMAP +#define ROUND_UP(x,size) ((x + size - 1) & ~(size - 1)) + + /* On many architectures malloc'd memory isn't executable, so we need to use mmap. */ + + fd = open(path, O_RDONLY); + if (fd == -1) + barf("loadObj: can't open `%s'", path); + + pagesize = getpagesize(); + +#ifdef ia64_TARGET_ARCH + /* The PLT needs to be right before the object */ + n = ROUND_UP(PLTSize(), pagesize); + oc->plt = mmap(NULL, n, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (oc->plt == MAP_FAILED) + barf("loadObj: can't allocate PLT"); + + oc->pltIndex = 0; + map_addr = oc->plt + n; +#endif + + n = ROUND_UP(oc->fileSize, pagesize); + oc->image = mmap(map_addr, n, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + if (oc->image == MAP_FAILED) + barf("loadObj: can't map `%s'", path); + + close(fd); + +#else /* !USE_MMAP */ + + oc->image = stgMallocBytes(oc->fileSize, "loadObj(image)"); + /* load the image into memory */ f = fopen(path, "rb"); - if (!f) { + if (!f) barf("loadObj: can't read `%s'", path); - } + n = fread ( oc->image, 1, oc->fileSize, f ); - if (n != oc->fileSize) { - fclose(f); + if (n != oc->fileSize) barf("loadObj: error whilst reading `%s'", path); - } + + fclose(f); + +#endif /* USE_MMAP */ /* verify the in-memory image */ # if defined(OBJFORMAT_ELF) r = ocVerifyImage_ELF ( oc ); # elif defined(OBJFORMAT_PEi386) r = ocVerifyImage_PEi386 ( oc ); +# elif defined(OBJFORMAT_MACHO) + r = ocVerifyImage_MachO ( oc ); # else barf("loadObj: no verify method"); # endif @@ -731,6 +929,8 @@ loadObj( char *path ) r = ocGetNames_ELF ( oc ); # elif defined(OBJFORMAT_PEi386) r = ocGetNames_PEi386 ( oc ); +# elif defined(OBJFORMAT_MACHO) + r = ocGetNames_MachO ( oc ); # else barf("loadObj: no getNames method"); # endif @@ -759,6 +959,8 @@ resolveObjs( void ) r = ocResolve_ELF ( oc ); # elif defined(OBJFORMAT_PEi386) r = ocResolve_PEi386 ( oc ); +# elif defined(OBJFORMAT_MACHO) + r = ocResolve_MachO ( oc ); # else barf("resolveObjs: not implemented on this platform"); # endif @@ -1680,30 +1882,193 @@ ocResolve_PEi386 ( ObjectCode* oc ) # define ELF_TARGET_SPARC /* Used inside */ #elif defined(i386_TARGET_ARCH) # define ELF_TARGET_386 /* Used inside */ +#elif defined (ia64_TARGET_ARCH) +# define ELF_TARGET_IA64 /* Used inside */ +# define ELF_64BIT +# define ELF_FUNCTION_DESC /* calling convention uses function descriptors */ +# define ELF_NEED_GOT /* needs Global Offset Table */ +# define ELF_NEED_PLT /* needs Procedure Linkage Tables */ #endif -/* There is a similar case for IA64 in the Solaris2 headers if this - * ever becomes relevant. - */ #include -#include + +/* + * Define a set of types which can be used for both ELF32 and ELF64 + */ + +#ifdef ELF_64BIT +#define ELFCLASS ELFCLASS64 +#define Elf_Addr Elf64_Addr +#define Elf_Word Elf64_Word +#define Elf_Sword Elf64_Sword +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Phdr Elf64_Phdr +#define Elf_Shdr Elf64_Shdr +#define Elf_Sym Elf64_Sym +#define Elf_Rel Elf64_Rel +#define Elf_Rela Elf64_Rela +#define ELF_ST_TYPE ELF64_ST_TYPE +#define ELF_ST_BIND ELF64_ST_BIND +#define ELF_R_TYPE ELF64_R_TYPE +#define ELF_R_SYM ELF64_R_SYM +#else +#define ELFCLASS ELFCLASS32 +#define Elf_Addr Elf32_Addr +#define Elf_Word Elf32_Word +#define Elf_Sword Elf32_Sword +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Phdr Elf32_Phdr +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#define ELF_ST_TYPE ELF32_ST_TYPE +#define ELF_ST_BIND ELF32_ST_BIND +#define ELF_R_TYPE ELF32_R_TYPE +#define ELF_R_SYM ELF32_R_SYM +#endif + + +/* + * Functions to allocate entries in dynamic sections. Currently we simply + * preallocate a large number, and we don't check if a entry for the given + * target already exists (a linear search is too slow). Ideally these + * entries would be associated with symbols. + */ + +/* These sizes sufficient to load HSbase + HShaskell98 + a few modules */ +#define GOT_SIZE 0x20000 +#define FUNCTION_TABLE_SIZE 0x10000 +#define PLT_SIZE 0x08000 + +#ifdef ELF_NEED_GOT +static Elf_Addr got[GOT_SIZE]; +static unsigned int gotIndex; +static Elf_Addr gp_val = (Elf_Addr)got; + +static Elf_Addr +allocateGOTEntry(Elf_Addr target) +{ + Elf_Addr *entry; + + if (gotIndex >= GOT_SIZE) + barf("Global offset table overflow"); + + entry = &got[gotIndex++]; + *entry = target; + return (Elf_Addr)entry; +} +#endif + +#ifdef ELF_FUNCTION_DESC +typedef struct { + Elf_Addr ip; + Elf_Addr gp; +} FunctionDesc; + +static FunctionDesc functionTable[FUNCTION_TABLE_SIZE]; +static unsigned int functionTableIndex; + +static Elf_Addr +allocateFunctionDesc(Elf_Addr target) +{ + FunctionDesc *entry; + + if (functionTableIndex >= FUNCTION_TABLE_SIZE) + barf("Function table overflow"); + + entry = &functionTable[functionTableIndex++]; + entry->ip = target; + entry->gp = (Elf_Addr)gp_val; + return (Elf_Addr)entry; +} + +static Elf_Addr +copyFunctionDesc(Elf_Addr target) +{ + FunctionDesc *olddesc = (FunctionDesc *)target; + FunctionDesc *newdesc; + + newdesc = (FunctionDesc *)allocateFunctionDesc(olddesc->ip); + newdesc->gp = olddesc->gp; + return (Elf_Addr)newdesc; +} +#endif + +#ifdef ELF_NEED_PLT +#ifdef ia64_TARGET_ARCH +static void ia64_reloc_gprel22(Elf_Addr target, Elf_Addr value); +static void ia64_reloc_pcrel21(Elf_Addr target, Elf_Addr value, ObjectCode *oc); + +static unsigned char plt_code[] = +{ + /* taken from binutils bfd/elfxx-ia64.c */ + 0x0b, 0x78, 0x00, 0x02, 0x00, 0x24, /* [MMI] addl r15=0,r1;; */ + 0x00, 0x41, 0x3c, 0x30, 0x28, 0xc0, /* ld8 r16=[r15],8 */ + 0x01, 0x08, 0x00, 0x84, /* mov r14=r1;; */ + 0x11, 0x08, 0x00, 0x1e, 0x18, 0x10, /* [MIB] ld8 r1=[r15] */ + 0x60, 0x80, 0x04, 0x80, 0x03, 0x00, /* mov b6=r16 */ + 0x60, 0x00, 0x80, 0x00 /* br.few b6;; */ +}; + +/* If we can't get to the function descriptor via gp, take a local copy of it */ +#define PLT_RELOC(code, target) { \ + Elf64_Sxword rel_value = target - gp_val; \ + if ((rel_value > 0x1fffff) || (rel_value < -0x1fffff)) \ + ia64_reloc_gprel22((Elf_Addr)code, copyFunctionDesc(target)); \ + else \ + ia64_reloc_gprel22((Elf_Addr)code, target); \ + } +#endif + +typedef struct { + unsigned char code[sizeof(plt_code)]; +} PLTEntry; + +static Elf_Addr +allocatePLTEntry(Elf_Addr target, ObjectCode *oc) +{ + PLTEntry *plt = (PLTEntry *)oc->plt; + PLTEntry *entry; + + if (oc->pltIndex >= PLT_SIZE) + barf("Procedure table overflow"); + + entry = &plt[oc->pltIndex++]; + memcpy(entry->code, plt_code, sizeof(entry->code)); + PLT_RELOC(entry->code, target); + return (Elf_Addr)entry; +} + +static unsigned int +PLTSize(void) +{ + return (PLT_SIZE * sizeof(PLTEntry)); +} +#endif + + +/* + * Generic ELF functions + */ static char * -findElfSection ( void* objImage, Elf32_Word sh_type ) +findElfSection ( void* objImage, Elf_Word sh_type ) { - int i; char* ehdrC = (char*)objImage; - Elf32_Ehdr* ehdr = (Elf32_Ehdr*)ehdrC; - Elf32_Shdr* shdr = (Elf32_Shdr*)(ehdrC + ehdr->e_shoff); + Elf_Ehdr* ehdr = (Elf_Ehdr*)ehdrC; + Elf_Shdr* shdr = (Elf_Shdr*)(ehdrC + ehdr->e_shoff); char* sh_strtab = ehdrC + shdr[ehdr->e_shstrndx].sh_offset; char* ptr = NULL; + int i; + for (i = 0; i < ehdr->e_shnum; i++) { if (shdr[i].sh_type == sh_type /* Ignore the section header's string table. */ && i != ehdr->e_shstrndx /* Ignore string tables named .stabstr, as they contain debugging info. */ - && 0 != strncmp(".stabstr", sh_strtab + shdr[i].sh_name, 8) + && 0 != memcmp(".stabstr", sh_strtab + shdr[i].sh_name, 8) ) { ptr = ehdrC + shdr[i].sh_offset; break; @@ -1712,35 +2077,50 @@ findElfSection ( void* objImage, Elf32_Word sh_type ) return ptr; } +#if defined(ia64_TARGET_ARCH) +static Elf_Addr +findElfSegment ( void* objImage, Elf_Addr vaddr ) +{ + char* ehdrC = (char*)objImage; + Elf_Ehdr* ehdr = (Elf_Ehdr*)ehdrC; + Elf_Phdr* phdr = (Elf_Phdr*)(ehdrC + ehdr->e_phoff); + Elf_Addr segaddr = 0; + int i; + + for (i = 0; i < ehdr->e_phnum; i++) { + segaddr = phdr[i].p_vaddr; + if ((vaddr >= segaddr) && (vaddr < segaddr + phdr[i].p_memsz)) + break; + } + return segaddr; +} +#endif static int ocVerifyImage_ELF ( ObjectCode* oc ) { - Elf32_Shdr* shdr; - Elf32_Sym* stab; + Elf_Shdr* shdr; + Elf_Sym* stab; int i, j, nent, nstrtab, nsymtabs; char* sh_strtab; char* strtab; - char* ehdrC = (char*)(oc->image); - Elf32_Ehdr* ehdr = ( Elf32_Ehdr*)ehdrC; + char* ehdrC = (char*)(oc->image); + Elf_Ehdr* ehdr = (Elf_Ehdr*)ehdrC; if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || ehdr->e_ident[EI_MAG1] != ELFMAG1 || ehdr->e_ident[EI_MAG2] != ELFMAG2 || ehdr->e_ident[EI_MAG3] != ELFMAG3) { - belch("%s: not an ELF header", oc->fileName); + belch("%s: not an ELF object", oc->fileName); return 0; } - IF_DEBUG(linker,belch( "Is an ELF header" )); - if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) { - belch("%s: not 32 bit ELF", oc->fileName); + if (ehdr->e_ident[EI_CLASS] != ELFCLASS) { + belch("%s: unsupported ELF format", oc->fileName); return 0; } - IF_DEBUG(linker,belch( "Is 32 bit ELF" )); - if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) { IF_DEBUG(linker,belch( "Is little-endian" )); } else @@ -1761,6 +2141,9 @@ ocVerifyImage_ELF ( ObjectCode* oc ) switch (ehdr->e_machine) { case EM_386: IF_DEBUG(linker,belch( "x86" )); break; case EM_SPARC: IF_DEBUG(linker,belch( "sparc" )); break; +#ifdef EM_IA_64 + case EM_IA_64: IF_DEBUG(linker,belch( "ia64" )); break; +#endif default: IF_DEBUG(linker,belch( "unknown" )); belch("%s: unknown architecture", oc->fileName); return 0; @@ -1770,9 +2153,9 @@ ocVerifyImage_ELF ( ObjectCode* oc ) "\nSection header table: start %d, n_entries %d, ent_size %d", ehdr->e_shoff, ehdr->e_shnum, ehdr->e_shentsize )); - ASSERT (ehdr->e_shentsize == sizeof(Elf32_Shdr)); + ASSERT (ehdr->e_shentsize == sizeof(Elf_Shdr)); - shdr = (Elf32_Shdr*) (ehdrC + ehdr->e_shoff); + shdr = (Elf_Shdr*) (ehdrC + ehdr->e_shoff); if (ehdr->e_shstrndx == SHN_UNDEF) { belch("%s: no section header string table", oc->fileName); @@ -1813,7 +2196,7 @@ ocVerifyImage_ELF ( ObjectCode* oc ) && i != ehdr->e_shstrndx /* Ignore string tables named .stabstr, as they contain debugging info. */ - && 0 != strncmp(".stabstr", sh_strtab + shdr[i].sh_name, 8) + && 0 != memcmp(".stabstr", sh_strtab + shdr[i].sh_name, 8) ) { IF_DEBUG(linker,belch(" section %d is a normal string table", i )); strtab = ehdrC + shdr[i].sh_offset; @@ -1831,13 +2214,13 @@ ocVerifyImage_ELF ( ObjectCode* oc ) if (shdr[i].sh_type != SHT_SYMTAB) continue; IF_DEBUG(linker,belch( "section %d is a symbol table", i )); nsymtabs++; - stab = (Elf32_Sym*) (ehdrC + shdr[i].sh_offset); - nent = shdr[i].sh_size / sizeof(Elf32_Sym); + stab = (Elf_Sym*) (ehdrC + shdr[i].sh_offset); + nent = shdr[i].sh_size / sizeof(Elf_Sym); IF_DEBUG(linker,belch( " number of entries is apparently %d (%d rem)", nent, - shdr[i].sh_size % sizeof(Elf32_Sym) + shdr[i].sh_size % sizeof(Elf_Sym) )); - if (0 != shdr[i].sh_size % sizeof(Elf32_Sym)) { + if (0 != shdr[i].sh_size % sizeof(Elf_Sym)) { belch("%s: non-integral number of symbol table entries", oc->fileName); return 0; } @@ -1849,7 +2232,7 @@ ocVerifyImage_ELF ( ObjectCode* oc ) (char*)stab[j].st_value )); IF_DEBUG(linker,fprintf(stderr, "type=" )); - switch (ELF32_ST_TYPE(stab[j].st_info)) { + switch (ELF_ST_TYPE(stab[j].st_info)) { case STT_NOTYPE: IF_DEBUG(linker,fprintf(stderr, "notype " )); break; case STT_OBJECT: IF_DEBUG(linker,fprintf(stderr, "object " )); break; case STT_FUNC : IF_DEBUG(linker,fprintf(stderr, "func " )); break; @@ -1860,7 +2243,7 @@ ocVerifyImage_ELF ( ObjectCode* oc ) IF_DEBUG(linker,fprintf(stderr, " " )); IF_DEBUG(linker,fprintf(stderr, "bind=" )); - switch (ELF32_ST_BIND(stab[j].st_info)) { + switch (ELF_ST_BIND(stab[j].st_info)) { case STB_LOCAL : IF_DEBUG(linker,fprintf(stderr, "local " )); break; case STB_GLOBAL: IF_DEBUG(linker,fprintf(stderr, "global" )); break; case STB_WEAK : IF_DEBUG(linker,fprintf(stderr, "weak " )); break; @@ -1885,12 +2268,12 @@ static int ocGetNames_ELF ( ObjectCode* oc ) { int i, j, k, nent; - Elf32_Sym* stab; + Elf_Sym* stab; - char* ehdrC = (char*)(oc->image); - Elf32_Ehdr* ehdr = (Elf32_Ehdr*)ehdrC; - char* strtab = findElfSection ( ehdrC, SHT_STRTAB ); - Elf32_Shdr* shdr = (Elf32_Shdr*) (ehdrC + ehdr->e_shoff); + char* ehdrC = (char*)(oc->image); + Elf_Ehdr* ehdr = (Elf_Ehdr*)ehdrC; + char* strtab = findElfSection ( ehdrC, SHT_STRTAB ); + Elf_Shdr* shdr = (Elf_Shdr*) (ehdrC + ehdr->e_shoff); ASSERT(symhash != NULL); @@ -1904,7 +2287,7 @@ ocGetNames_ELF ( ObjectCode* oc ) /* Figure out what kind of section it is. Logic derived from Figure 1.14 ("Special Sections") of the ELF document ("Portable Formats Specification, Version 1.1"). */ - Elf32_Shdr hdr = shdr[i]; + Elf_Shdr hdr = shdr[i]; SectionKind kind = SECTIONKIND_OTHER; int is_bss = FALSE; @@ -1956,8 +2339,8 @@ ocGetNames_ELF ( ObjectCode* oc ) if (shdr[i].sh_type != SHT_SYMTAB) continue; /* copy stuff into this module's object symbol table */ - stab = (Elf32_Sym*) (ehdrC + shdr[i].sh_offset); - nent = shdr[i].sh_size / sizeof(Elf32_Sym); + stab = (Elf_Sym*) (ehdrC + shdr[i].sh_offset); + nent = shdr[i].sh_size / sizeof(Elf_Sym); oc->n_symbols = nent; oc->symbols = stgMallocBytes(oc->n_symbols * sizeof(char*), @@ -1984,8 +2367,8 @@ ocGetNames_ELF ( ObjectCode* oc ) since the linker should never poke around in it. */ } else - if ( ( ELF32_ST_BIND(stab[j].st_info)==STB_GLOBAL - || ELF32_ST_BIND(stab[j].st_info)==STB_LOCAL + if ( ( ELF_ST_BIND(stab[j].st_info)==STB_GLOBAL + || ELF_ST_BIND(stab[j].st_info)==STB_LOCAL ) /* and not an undefined symbol */ && stab[j].st_shndx != SHN_UNDEF @@ -1993,9 +2376,9 @@ ocGetNames_ELF ( ObjectCode* oc ) && stab[j].st_shndx < SHN_LORESERVE && /* and it's a not a section or string table or anything silly */ - ( ELF32_ST_TYPE(stab[j].st_info)==STT_FUNC || - ELF32_ST_TYPE(stab[j].st_info)==STT_OBJECT || - ELF32_ST_TYPE(stab[j].st_info)==STT_NOTYPE + ( ELF_ST_TYPE(stab[j].st_info)==STT_FUNC || + ELF_ST_TYPE(stab[j].st_info)==STT_OBJECT || + ELF_ST_TYPE(stab[j].st_info)==STT_NOTYPE ) ) { /* Section 0 is the undefined section, hence > and not >=. */ @@ -2007,9 +2390,16 @@ ocGetNames_ELF ( ObjectCode* oc ) } */ ad = ehdrC + shdr[ secno ].sh_offset + stab[j].st_value; - if (ELF32_ST_BIND(stab[j].st_info)==STB_LOCAL) { + if (ELF_ST_BIND(stab[j].st_info)==STB_LOCAL) { isLocal = TRUE; } else { +#ifdef ELF_FUNCTION_DESC + /* dlsym() and the initialisation table both give us function + * descriptors, so to be consistent we store function descriptors + * in the symbol table */ + if (ELF_ST_TYPE(stab[j].st_info) == STT_FUNC) + ad = (char *)allocateFunctionDesc((Elf_Addr)ad); +#endif IF_DEBUG(linker,belch( "addOTabName(GLOB): %10p %s %s", ad, oc->fileName, nm )); isLocal = FALSE; @@ -2034,8 +2424,8 @@ ocGetNames_ELF ( ObjectCode* oc ) /* fprintf(stderr, "skipping bind = %d, type = %d, shndx = %d `%s'\n", - (int)ELF32_ST_BIND(stab[j].st_info), - (int)ELF32_ST_TYPE(stab[j].st_info), + (int)ELF_ST_BIND(stab[j].st_info), + (int)ELF_ST_TYPE(stab[j].st_info), (int)stab[j].st_shndx, strtab + stab[j].st_name ); @@ -2049,33 +2439,35 @@ ocGetNames_ELF ( ObjectCode* oc ) return 1; } - /* Do ELF relocations which lack an explicit addend. All x86-linux relocations appear to be of this form. */ static int -do_Elf32_Rel_relocations ( ObjectCode* oc, char* ehdrC, - Elf32_Shdr* shdr, int shnum, - Elf32_Sym* stab, char* strtab ) +do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC, + Elf_Shdr* shdr, int shnum, + Elf_Sym* stab, char* strtab ) { int j; char *symbol; - Elf32_Word* targ; - Elf32_Rel* rtab = (Elf32_Rel*) (ehdrC + shdr[shnum].sh_offset); - int nent = shdr[shnum].sh_size / sizeof(Elf32_Rel); + Elf_Word* targ; + Elf_Rel* rtab = (Elf_Rel*) (ehdrC + shdr[shnum].sh_offset); + int nent = shdr[shnum].sh_size / sizeof(Elf_Rel); int target_shndx = shdr[shnum].sh_info; int symtab_shndx = shdr[shnum].sh_link; - stab = (Elf32_Sym*) (ehdrC + shdr[ symtab_shndx ].sh_offset); - targ = (Elf32_Word*)(ehdrC + shdr[ target_shndx ].sh_offset); + + stab = (Elf_Sym*) (ehdrC + shdr[ symtab_shndx ].sh_offset); + targ = (Elf_Word*)(ehdrC + shdr[ target_shndx ].sh_offset); IF_DEBUG(linker,belch( "relocations for section %d using symtab %d", target_shndx, symtab_shndx )); + for (j = 0; j < nent; j++) { - Elf32_Addr offset = rtab[j].r_offset; - Elf32_Word info = rtab[j].r_info; + Elf_Addr offset = rtab[j].r_offset; + Elf_Addr info = rtab[j].r_info; - Elf32_Addr P = ((Elf32_Addr)targ) + offset; - Elf32_Word* pP = (Elf32_Word*)P; - Elf32_Addr A = *pP; - Elf32_Addr S; + Elf_Addr P = ((Elf_Addr)targ) + offset; + Elf_Word* pP = (Elf_Word*)P; + Elf_Addr A = *pP; + Elf_Addr S; + Elf_Addr value; IF_DEBUG(linker,belch( "Rel entry %3d is raw(%6p %6p)", j, (void*)offset, (void*)info )); @@ -2083,15 +2475,15 @@ do_Elf32_Rel_relocations ( ObjectCode* oc, char* ehdrC, IF_DEBUG(linker,belch( " ZERO" )); S = 0; } else { - Elf32_Sym sym = stab[ELF32_R_SYM(info)]; + Elf_Sym sym = stab[ELF_R_SYM(info)]; /* First see if it is a local symbol. */ - if (ELF32_ST_BIND(sym.st_info) == STB_LOCAL) { + if (ELF_ST_BIND(sym.st_info) == STB_LOCAL) { /* Yes, so we can get the address directly from the ELF symbol table. */ symbol = sym.st_name==0 ? "(noname)" : strtab+sym.st_name; - S = (Elf32_Addr) + S = (Elf_Addr) (ehdrC + shdr[ sym.st_shndx ].sh_offset - + stab[ELF32_R_SYM(info)].st_value); + + stab[ELF_R_SYM(info)].st_value); } else { /* No, so look up the name in our global table. */ @@ -2104,17 +2496,21 @@ do_Elf32_Rel_relocations ( ObjectCode* oc, char* ehdrC, } IF_DEBUG(linker,belch( "`%s' resolves to %p", symbol, (void*)S )); } + IF_DEBUG(linker,belch( "Reloc: P = %p S = %p A = %p", (void*)P, (void*)S, (void*)A )); checkProddableBlock ( oc, pP ); - switch (ELF32_R_TYPE(info)) { + + value = S + A; + + switch (ELF_R_TYPE(info)) { # ifdef i386_TARGET_ARCH - case R_386_32: *pP = S + A; break; - case R_386_PC32: *pP = S + A - P; break; + case R_386_32: *pP = value; break; + case R_386_PC32: *pP = value - P; break; # endif default: belch("%s: unhandled ELF relocation(Rel) type %d\n", - oc->fileName, ELF32_R_TYPE(info)); + oc->fileName, ELF_R_TYPE(info)); return 0; } @@ -2122,92 +2518,111 @@ do_Elf32_Rel_relocations ( ObjectCode* oc, char* ehdrC, return 1; } - /* Do ELF relocations for which explicit addends are supplied. sparc-solaris relocations appear to be of this form. */ static int -do_Elf32_Rela_relocations ( ObjectCode* oc, char* ehdrC, - Elf32_Shdr* shdr, int shnum, - Elf32_Sym* stab, char* strtab ) +do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC, + Elf_Shdr* shdr, int shnum, + Elf_Sym* stab, char* strtab ) { int j; char *symbol; - Elf32_Word* targ; - Elf32_Rela* rtab = (Elf32_Rela*) (ehdrC + shdr[shnum].sh_offset); - int nent = shdr[shnum].sh_size / sizeof(Elf32_Rela); + Elf_Addr targ; + Elf_Rela* rtab = (Elf_Rela*) (ehdrC + shdr[shnum].sh_offset); + int nent = shdr[shnum].sh_size / sizeof(Elf_Rela); int target_shndx = shdr[shnum].sh_info; int symtab_shndx = shdr[shnum].sh_link; - stab = (Elf32_Sym*) (ehdrC + shdr[ symtab_shndx ].sh_offset); - targ = (Elf32_Word*)(ehdrC + shdr[ target_shndx ].sh_offset); + + stab = (Elf_Sym*) (ehdrC + shdr[ symtab_shndx ].sh_offset); + targ = (Elf_Addr) (ehdrC + shdr[ target_shndx ].sh_offset); IF_DEBUG(linker,belch( "relocations for section %d using symtab %d", target_shndx, symtab_shndx )); + for (j = 0; j < nent; j++) { - Elf32_Addr offset = rtab[j].r_offset; - Elf32_Word info = rtab[j].r_info; - Elf32_Sword addend = rtab[j].r_addend; - Elf32_Addr P = ((Elf32_Addr)targ) + offset; - Elf32_Addr A = addend; /* Do not delete this; it is used on sparc. */ - Elf32_Addr S; -# if defined(sparc_TARGET_ARCH) +#if defined(DEBUG) || defined(sparc_TARGET_ARCH) || defined(ia64_TARGET_ARCH) /* This #ifdef only serves to avoid unused-var warnings. */ - Elf32_Word* pP = (Elf32_Word*)P; - Elf32_Word w1, w2; + Elf_Addr offset = rtab[j].r_offset; + Elf_Addr P = targ + offset; +#endif + Elf_Addr info = rtab[j].r_info; + Elf_Addr A = rtab[j].r_addend; + Elf_Addr S; + Elf_Addr value; +# if defined(sparc_TARGET_ARCH) + Elf_Word* pP = (Elf_Word*)P; + Elf_Word w1, w2; +# elif defined(ia64_TARGET_ARCH) + Elf64_Xword *pP = (Elf64_Xword *)P; + Elf_Addr addr; # endif IF_DEBUG(linker,belch( "Rel entry %3d is raw(%6p %6p %6p) ", j, (void*)offset, (void*)info, - (void*)addend )); + (void*)A )); if (!info) { IF_DEBUG(linker,belch( " ZERO" )); S = 0; } else { - Elf32_Sym sym = stab[ELF32_R_SYM(info)]; + Elf_Sym sym = stab[ELF_R_SYM(info)]; /* First see if it is a local symbol. */ - if (ELF32_ST_BIND(sym.st_info) == STB_LOCAL) { + if (ELF_ST_BIND(sym.st_info) == STB_LOCAL) { /* Yes, so we can get the address directly from the ELF symbol table. */ symbol = sym.st_name==0 ? "(noname)" : strtab+sym.st_name; - S = (Elf32_Addr) + S = (Elf_Addr) (ehdrC + shdr[ sym.st_shndx ].sh_offset - + stab[ELF32_R_SYM(info)].st_value); - + + stab[ELF_R_SYM(info)].st_value); +#ifdef ELF_FUNCTION_DESC + /* Make a function descriptor for this function */ + if (S && ELF_ST_TYPE(sym.st_info) == STT_FUNC) { + S = allocateFunctionDesc(S + A); + A = 0; + } +#endif } else { /* No, so look up the name in our global table. */ symbol = strtab + sym.st_name; (void*)S = lookupSymbol( symbol ); + +#ifdef ELF_FUNCTION_DESC + /* If a function, already a function descriptor - we would + have to copy it to add an offset. */ + if (S && ELF_ST_TYPE(sym.st_info) == STT_FUNC) + assert(A == 0); +#endif } if (!S) { belch("%s: unknown symbol `%s'", oc->fileName, symbol); return 0; - /* - S = 0x11223344; - fprintf ( stderr, "S %p A %p S+A %p S+A-P %p\n",S,A,S+A,S+A-P); - */ } IF_DEBUG(linker,belch( "`%s' resolves to %p", symbol, (void*)S )); } + IF_DEBUG(linker,fprintf ( stderr, "Reloc: P = %p S = %p A = %p\n", (void*)P, (void*)S, (void*)A )); - checkProddableBlock ( oc, (void*)P ); - switch (ELF32_R_TYPE(info)) { + /* checkProddableBlock ( oc, (void*)P ); */ + + value = S + A; + + switch (ELF_R_TYPE(info)) { # if defined(sparc_TARGET_ARCH) case R_SPARC_WDISP30: w1 = *pP & 0xC0000000; - w2 = (Elf32_Word)((S + A - P) >> 2); + w2 = (Elf_Word)((value - P) >> 2); ASSERT((w2 & 0xC0000000) == 0); w1 |= w2; *pP = w1; break; case R_SPARC_HI22: w1 = *pP & 0xFFC00000; - w2 = (Elf32_Word)((S + A) >> 10); + w2 = (Elf_Word)(value >> 10); ASSERT((w2 & 0xFFC00000) == 0); w1 |= w2; *pP = w1; break; case R_SPARC_LO10: w1 = *pP & ~0x3FF; - w2 = (Elf32_Word)((S + A) & 0x3FF); + w2 = (Elf_Word)(value & 0x3FF); ASSERT((w2 & ~0x3FF) == 0); w1 |= w2; *pP = w1; @@ -2224,13 +2639,33 @@ do_Elf32_Rela_relocations ( ObjectCode* oc, char* ehdrC, */ case R_SPARC_UA32: case R_SPARC_32: - w2 = (Elf32_Word)(S + A); + w2 = (Elf_Word)value; *pP = w2; break; +# elif defined(ia64_TARGET_ARCH) + case R_IA64_DIR64LSB: + case R_IA64_FPTR64LSB: + *pP = value; + break; + case R_IA64_SEGREL64LSB: + addr = findElfSegment(ehdrC, value); + *pP = value - addr; + break; + case R_IA64_GPREL22: + ia64_reloc_gprel22(P, value); + break; + case R_IA64_LTOFF22: + case R_IA64_LTOFF_FPTR22: + addr = allocateGOTEntry(value); + ia64_reloc_gprel22(P, addr); + break; + case R_IA64_PCREL21B: + ia64_reloc_pcrel21(P, S, oc); + break; # endif default: belch("%s: unhandled ELF relocation(RelA) type %d\n", - oc->fileName, ELF32_R_TYPE(info)); + oc->fileName, ELF_R_TYPE(info)); return 0; } @@ -2238,20 +2673,19 @@ do_Elf32_Rela_relocations ( ObjectCode* oc, char* ehdrC, return 1; } - static int ocResolve_ELF ( ObjectCode* oc ) { char *strtab; int shnum, ok; - Elf32_Sym* stab = NULL; - char* ehdrC = (char*)(oc->image); - Elf32_Ehdr* ehdr = (Elf32_Ehdr*) ehdrC; - Elf32_Shdr* shdr = (Elf32_Shdr*) (ehdrC + ehdr->e_shoff); - char* sh_strtab = ehdrC + shdr[ehdr->e_shstrndx].sh_offset; + Elf_Sym* stab = NULL; + char* ehdrC = (char*)(oc->image); + Elf_Ehdr* ehdr = (Elf_Ehdr*) ehdrC; + Elf_Shdr* shdr = (Elf_Shdr*) (ehdrC + ehdr->e_shoff); + char* sh_strtab = ehdrC + shdr[ehdr->e_shstrndx].sh_offset; /* first find "the" symbol table */ - stab = (Elf32_Sym*) findElfSection ( ehdrC, SHT_SYMTAB ); + stab = (Elf_Sym*) findElfSection ( ehdrC, SHT_SYMTAB ); /* also go find the string table */ strtab = findElfSection ( ehdrC, SHT_STRTAB ); @@ -2268,21 +2702,20 @@ ocResolve_ELF ( ObjectCode* oc ) relocation entries that, when done, make the stabs debugging info point at the right places. We ain't interested in all dat jazz, mun. */ - if (0 == strncmp(".rel.stab", sh_strtab + shdr[shnum].sh_name, 9)) + if (0 == memcmp(".rel.stab", sh_strtab + shdr[shnum].sh_name, 9)) continue; if (shdr[shnum].sh_type == SHT_REL ) { - ok = do_Elf32_Rel_relocations ( oc, ehdrC, shdr, - shnum, stab, strtab ); + ok = do_Elf_Rel_relocations ( oc, ehdrC, shdr, + shnum, stab, strtab ); if (!ok) return ok; } else if (shdr[shnum].sh_type == SHT_RELA) { - ok = do_Elf32_Rela_relocations ( oc, ehdrC, shdr, - shnum, stab, strtab ); + ok = do_Elf_Rela_relocations ( oc, ehdrC, shdr, + shnum, stab, strtab ); if (!ok) return ok; } - } /* Free the local symbol table; we won't need it again. */ @@ -2292,5 +2725,400 @@ ocResolve_ELF ( ObjectCode* oc ) return 1; } +/* + * IA64 specifics + * Instructions are 41 bits long, packed into 128 bit bundles with a 5-bit template + * at the front. The following utility functions pack and unpack instructions, and + * take care of the most common relocations. + */ + +#ifdef ia64_TARGET_ARCH + +static Elf64_Xword +ia64_extract_instruction(Elf64_Xword *target) +{ + Elf64_Xword w1, w2; + int slot = (Elf_Addr)target & 3; + (Elf_Addr)target &= ~3; + + w1 = *target; + w2 = *(target+1); + + switch (slot) + { + case 0: + return ((w1 >> 5) & 0x1ffffffffff); + case 1: + return (w1 >> 46) | ((w2 & 0x7fffff) << 18); + case 2: + return (w2 >> 23); + default: + barf("ia64_extract_instruction: invalid slot %p", target); + } +} + +static void +ia64_deposit_instruction(Elf64_Xword *target, Elf64_Xword value) +{ + int slot = (Elf_Addr)target & 3; + (Elf_Addr)target &= ~3; + + switch (slot) + { + case 0: + *target |= value << 5; + break; + case 1: + *target |= value << 46; + *(target+1) |= value >> 18; + break; + case 2: + *(target+1) |= value << 23; + break; + } +} + +static void +ia64_reloc_gprel22(Elf_Addr target, Elf_Addr value) +{ + Elf64_Xword instruction; + Elf64_Sxword rel_value; + + rel_value = value - gp_val; + if ((rel_value > 0x1fffff) || (rel_value < -0x1fffff)) + barf("GP-relative data out of range (address = 0x%lx, gp = 0x%lx)", value, gp_val); + + instruction = ia64_extract_instruction((Elf64_Xword *)target); + instruction |= (((rel_value >> 0) & 0x07f) << 13) /* imm7b */ + | (((rel_value >> 7) & 0x1ff) << 27) /* imm9d */ + | (((rel_value >> 16) & 0x01f) << 22) /* imm5c */ + | ((Elf64_Xword)(rel_value < 0) << 36); /* s */ + ia64_deposit_instruction((Elf64_Xword *)target, instruction); +} + +static void +ia64_reloc_pcrel21(Elf_Addr target, Elf_Addr value, ObjectCode *oc) +{ + Elf64_Xword instruction; + Elf64_Sxword rel_value; + Elf_Addr entry; + + entry = allocatePLTEntry(value, oc); + + rel_value = (entry >> 4) - (target >> 4); + if ((rel_value > 0xfffff) || (rel_value < -0xfffff)) + barf("PLT entry too far away (entry = 0x%lx, target = 0x%lx)", entry, target); + + instruction = ia64_extract_instruction((Elf64_Xword *)target); + instruction |= ((rel_value & 0xfffff) << 13) /* imm20b */ + | ((Elf64_Xword)(rel_value < 0) << 36); /* s */ + ia64_deposit_instruction((Elf64_Xword *)target, instruction); +} + +#endif /* ia64 */ #endif /* ELF */ + +/* -------------------------------------------------------------------------- + * Mach-O specifics + * ------------------------------------------------------------------------*/ + +#if defined(OBJFORMAT_MACHO) + +/* + Initial support for MachO linking on Darwin/MacOS X on PowerPC chips + by Wolfgang Thaller (wolfgang.thaller@gmx.net) + + I hereby formally apologize for the hackish nature of this code. + Things that need to be done: + *) get common symbols and .bss sections to work properly. + Haskell modules seem to work, but C modules can cause problems + *) implement ocVerifyImage_MachO + *) add more sanity checks. The current code just has to segfault if there's a + broken .o file. +*/ + +static int ocVerifyImage_MachO(ObjectCode* oc) +{ + // FIXME: do some verifying here + return 1; +} + +static void resolveImports( + ObjectCode* oc, + char *image, + struct symtab_command *symLC, + struct section *sect, // ptr to lazy or non-lazy symbol pointer section + unsigned long *indirectSyms, + struct nlist *nlist) +{ + unsigned i; + + for(i=0;i*4size;i++) + { + // according to otool, reserved1 contains the first index into the indirect symbol table + struct nlist *symbol = &nlist[indirectSyms[sect->reserved1+i]]; + char *nm = image + symLC->stroff + symbol->n_un.n_strx; + void *addr = NULL; + + if((symbol->n_type & N_TYPE) == N_UNDF + && (symbol->n_type & N_EXT) && (symbol->n_value != 0)) + addr = (void*) (symbol->n_value); + else if((addr = lookupLocalSymbol(oc,nm)) != NULL) + ; + else + addr = lookupSymbol(nm); + if(!addr) + { + fprintf(stderr, "not found: %s\n", nm); + abort(); + } + ASSERT(addr); + ((void**)(image + sect->offset))[i] = addr; + } +} + +static void relocateSection(char *image, + struct symtab_command *symLC, struct nlist *nlist, + struct section* sections, struct section *sect) +{ + struct relocation_info *relocs; + int i,n; + + if(!strcmp(sect->sectname,"__la_symbol_ptr")) + return; + else if(!strcmp(sect->sectname,"__nl_symbol_ptr")) + return; + + n = sect->nreloc; + relocs = (struct relocation_info*) (image + sect->reloff); + + for(i=0;ir_pcrel) + { + if(scat->r_length == 2 && scat->r_type == GENERIC_RELOC_VANILLA) + { + unsigned long* word = (unsigned long*) (image + sect->offset + scat->r_address); + + *word = scat->r_value + sect->offset + ((long) image); + } + } + + continue; // FIXME: I hope it's OK to ignore all the others. + } + else + { + struct relocation_info *reloc = &relocs[i]; + if(reloc->r_pcrel && !reloc->r_extern) + continue; + + if(!reloc->r_pcrel + && reloc->r_length == 2 + && reloc->r_type == GENERIC_RELOC_VANILLA) + { + unsigned long* word = (unsigned long*) (image + sect->offset + reloc->r_address); + + if(!reloc->r_extern) + { + long delta = + sections[reloc->r_symbolnum-1].offset + - sections[reloc->r_symbolnum-1].addr + + ((long) image); + + *word += delta; + } + else + { + struct nlist *symbol = &nlist[reloc->r_symbolnum]; + char *nm = image + symLC->stroff + symbol->n_un.n_strx; + *word = (unsigned long) (lookupSymbol(nm)); + ASSERT(*word); + } + continue; + } + fprintf(stderr, "unknown reloc\n"); + abort(); + ASSERT(2 + 2 == 5); + } + } +} + +static int ocGetNames_MachO(ObjectCode* oc) +{ + char *image = (char*) oc->image; + struct mach_header *header = (struct mach_header*) image; + struct load_command *lc = (struct load_command*) (image + sizeof(struct mach_header)); + unsigned i,curSymbol; + struct segment_command *segLC = NULL; + struct section *sections, *la_ptrs = NULL, *nl_ptrs = NULL; + struct symtab_command *symLC = NULL; + struct dysymtab_command *dsymLC = NULL; + struct nlist *nlist; + unsigned long commonSize = 0; + char *commonStorage = NULL; + unsigned long commonCounter; + + for(i=0;incmds;i++) + { + if(lc->cmd == LC_SEGMENT) + segLC = (struct segment_command*) lc; + else if(lc->cmd == LC_SYMTAB) + symLC = (struct symtab_command*) lc; + else if(lc->cmd == LC_DYSYMTAB) + dsymLC = (struct dysymtab_command*) lc; + lc = (struct load_command *) ( ((char*)lc) + lc->cmdsize ); + } + + sections = (struct section*) (segLC+1); + nlist = (struct nlist*) (image + symLC->symoff); + + for(i=0;insects;i++) + { + if(!strcmp(sections[i].sectname,"__la_symbol_ptr")) + la_ptrs = §ions[i]; + else if(!strcmp(sections[i].sectname,"__nl_symbol_ptr")) + nl_ptrs = §ions[i]; + + // for now, only add __text and __const to the sections table + else if(!strcmp(sections[i].sectname,"__text")) + addSection(oc, SECTIONKIND_CODE_OR_RODATA, + (void*) (image + sections[i].offset), + (void*) (image + sections[i].offset + sections[i].size)); + else if(!strcmp(sections[i].sectname,"__const")) + addSection(oc, SECTIONKIND_RWDATA, + (void*) (image + sections[i].offset), + (void*) (image + sections[i].offset + sections[i].size)); + else if(!strcmp(sections[i].sectname,"__data")) + addSection(oc, SECTIONKIND_RWDATA, + (void*) (image + sections[i].offset), + (void*) (image + sections[i].offset + sections[i].size)); + } + + // count external symbols defined here + oc->n_symbols = 0; + for(i=dsymLC->iextdefsym;iiextdefsym+dsymLC->nextdefsym;i++) + { + if((nlist[i].n_type & N_TYPE) == N_SECT) + oc->n_symbols++; + } + for(i=0;insyms;i++) + { + if((nlist[i].n_type & N_TYPE) == N_UNDF + && (nlist[i].n_type & N_EXT) && (nlist[i].n_value != 0)) + { + commonSize += nlist[i].n_value; + oc->n_symbols++; + } + } + oc->symbols = stgMallocBytes(oc->n_symbols * sizeof(char*), + "ocGetNames_MachO(oc->symbols)"); + + // insert symbols into hash table + for(i=dsymLC->iextdefsym,curSymbol=0;iiextdefsym+dsymLC->nextdefsym;i++) + { + if((nlist[i].n_type & N_TYPE) == N_SECT) + { + char *nm = image + symLC->stroff + nlist[i].n_un.n_strx; + ghciInsertStrHashTable(oc->fileName, symhash, nm, image + + sections[nlist[i].n_sect-1].offset + - sections[nlist[i].n_sect-1].addr + + nlist[i].n_value); + oc->symbols[curSymbol++] = nm; + } + } + + // insert local symbols into lochash + for(i=dsymLC->ilocalsym;iilocalsym+dsymLC->nlocalsym;i++) + { + if((nlist[i].n_type & N_TYPE) == N_SECT) + { + char *nm = image + symLC->stroff + nlist[i].n_un.n_strx; + ghciInsertStrHashTable(oc->fileName, oc->lochash, nm, image + + sections[nlist[i].n_sect-1].offset + - sections[nlist[i].n_sect-1].addr + + nlist[i].n_value); + } + } + + + commonStorage = stgCallocBytes(1,commonSize,"ocGetNames_MachO(common symbols)"); + commonCounter = (unsigned long)commonStorage; + for(i=0;insyms;i++) + { + if((nlist[i].n_type & N_TYPE) == N_UNDF + && (nlist[i].n_type & N_EXT) && (nlist[i].n_value != 0)) + { + char *nm = image + symLC->stroff + nlist[i].n_un.n_strx; + unsigned long sz = nlist[i].n_value; + + nlist[i].n_value = commonCounter; + + ghciInsertStrHashTable(oc->fileName, symhash, nm, (void*)commonCounter); + oc->symbols[curSymbol++] = nm; + + commonCounter += sz; + } + } + return 1; +} + +static int ocResolve_MachO(ObjectCode* oc) +{ + char *image = (char*) oc->image; + struct mach_header *header = (struct mach_header*) image; + struct load_command *lc = (struct load_command*) (image + sizeof(struct mach_header)); + unsigned i; + struct segment_command *segLC = NULL; + struct section *sections, *la_ptrs = NULL, *nl_ptrs = NULL; + struct symtab_command *symLC = NULL; + struct dysymtab_command *dsymLC = NULL; + struct nlist *nlist; + unsigned long *indirectSyms; + + for(i=0;incmds;i++) + { + if(lc->cmd == LC_SEGMENT) + segLC = (struct segment_command*) lc; + else if(lc->cmd == LC_SYMTAB) + symLC = (struct symtab_command*) lc; + else if(lc->cmd == LC_DYSYMTAB) + dsymLC = (struct dysymtab_command*) lc; + lc = (struct load_command *) ( ((char*)lc) + lc->cmdsize ); + } + + sections = (struct section*) (segLC+1); + nlist = (struct nlist*) (image + symLC->symoff); + + for(i=0;insects;i++) + { + if(!strcmp(sections[i].sectname,"__la_symbol_ptr")) + la_ptrs = §ions[i]; + else if(!strcmp(sections[i].sectname,"__nl_symbol_ptr")) + nl_ptrs = §ions[i]; + } + + indirectSyms = (unsigned long*) (image + dsymLC->indirectsymoff); + + if(la_ptrs) + resolveImports(oc,image,symLC,la_ptrs,indirectSyms,nlist); + if(nl_ptrs) + resolveImports(oc,image,symLC,nl_ptrs,indirectSyms,nlist); + + for(i=0;insects;i++) + { + relocateSection(image,symLC,nlist,sections,§ions[i]); + } + + /* Free the local symbol table; we won't need it again. */ + freeHashTable(oc->lochash, NULL); + oc->lochash = NULL; + return 1; +} + +#endif