Revert "Batch merge"
[ghc.git] / rts / linker / Elf.c
index c1caf9a..9ea10d4 100644 (file)
 #  include <elf_abi.h>
 #endif
 
+#if defined(arm_HOST_ARCH) || defined(aarch64_HOST_ARCH)
+#  define NEED_GOT
+#  define NEED_PLT
+#  include "elf_got.h"
+#  include "elf_plt.h"
+#  include "elf_reloc.h"
+#endif
+
 /*
 
    Note [Many ELF Sections]
@@ -161,8 +169,8 @@ get_shndx_table(Elf_Ehdr* ehdr)
 void
 ocInit_ELF(ObjectCode * oc)
 {
-    oc->info = (ObjectCodeFormatInfo*)stgCallocBytes(
-            1, sizeof(ObjectCodeFormatInfo),
+    oc->info = (struct ObjectCodeFormatInfo*)stgCallocBytes(
+            1, sizeof *oc->info,
             "ocInit_Elf(ObjectCodeFormatInfo)");
     // TODO: fill info
     oc->info->elfHeader = (Elf_Ehdr *)oc->image;
@@ -170,6 +178,8 @@ ocInit_ELF(ObjectCode * oc)
                                             + oc->info->elfHeader->e_phoff);
     oc->info->sectionHeader = (Elf_Shdr *) ((uint8_t*)oc->image
                                             + oc->info->elfHeader->e_shoff);
+    oc->info->sectionHeaderStrtab = (char*)((uint8_t*)oc->image +
+            oc->info->sectionHeader[oc->info->elfHeader->e_shstrndx].sh_offset);
 
     oc->n_sections = elf_shnum(oc->info->elfHeader);
 
@@ -277,6 +287,9 @@ ocDeinit_ELF(ObjectCode * oc)
      * ElfSymbols
      */
     if(oc->info != NULL) {
+#if defined(NEED_GOT)
+        freeGot(oc);
+#endif
         ElfSymbolTable * last = oc->info->symbolTables;
 
         while(last != NULL) {
@@ -378,7 +391,10 @@ ocVerifyImage_ELF ( ObjectCode* oc )
 #elif defined(EM_AMD64)
       case EM_AMD64: IF_DEBUG(linker,debugBelch( "amd64" )); break;
 #endif
-      default:       IF_DEBUG(linker,debugBelch( "unknown" ));
+#if defined(EM_AARCH64)
+      case EM_AARCH64: IF_DEBUG(linker,debugBelch( "aarch64" )); break;
+#endif
+       default:       IF_DEBUG(linker,debugBelch( "unknown" ));
                      errorBelch("%s: unknown architecture (e_machine == %d)"
                                 , oc->fileName, ehdr->e_machine);
                      return 0;
@@ -423,7 +439,7 @@ ocVerifyImage_ELF ( ObjectCode* oc )
           if (!SECTION_INDEX_VALID(shdr[i].sh_link)) {
             if (shdr[i].sh_link == SHN_UNDEF)
               errorBelch("\n%s: relocation section #%d has no symbol table\n"
-                         "This object file has probably been fully striped. "
+                         "This object file has probably been fully stripped. "
                          "Such files cannot be linked.\n",
                          oc->archiveMemberName ? oc->archiveMemberName : oc->fileName, i);
             else
@@ -598,6 +614,8 @@ static int getSectionKind_ELF( Elf_Shdr *hdr, int *is_bss )
     return SECTIONKIND_OTHER;
 }
 
+#if !defined(NEED_PLT)
+
 static void *
 mapObjectFileSection (int fd, Elf_Word offset, Elf_Word size,
                       void **mapped_start, StgWord *mapped_size,
@@ -615,17 +633,17 @@ mapObjectFileSection (int fd, Elf_Word offset, Elf_Word size,
     *mapped_start = p;
     return (void*)((StgWord)p + offset - pageOffset);
 }
+#endif
 
 int
 ocGetNames_ELF ( ObjectCode* oc )
 {
    Elf_Word i;
-   int j, nent, result, fd = -1;
-   Elf_Sym* stab;
+   int result, fd = -1;
 
    char*     ehdrC    = (char*)(oc->image);
    Elf_Ehdr* ehdr     = (Elf_Ehdr*)ehdrC;
-   char*     strtab;
+
    Elf_Shdr* shdr     = (Elf_Shdr*) (ehdrC + ehdr->e_shoff);
    Section * sections;
 #if defined(SHN_XINDEX)
@@ -640,7 +658,6 @@ ocGetNames_ELF ( ObjectCode* oc )
    oc->sections = sections;
    oc->n_sections = shnum;
 
-
    if (oc->imageMapped) {
 #if defined(openbsd_HOST_OS)
        fd = open(oc->fileName, O_RDONLY, S_IRUSR);
@@ -666,16 +683,77 @@ ocGetNames_ELF ( ObjectCode* oc )
          /* This is a non-empty .bss section.  Allocate zeroed space for
             it, and set its .sh_offset field such that
             ehdrC + .sh_offset == addr_of_zeroed_space.  */
+#if defined(NEED_GOT)
+          /* always use mmap if we use GOT slots.  Otherwise the malloced
+           * address might be out of range for sections that are mmaped.
+           */
+          alloc = SECTION_MMAP;
+          start = mmap(NULL, size,
+                       PROT_READ | PROT_WRITE | PROT_EXEC,
+                       MAP_ANON | MAP_PRIVATE,
+                       -1, 0);
+          mapped_start = start;
+          mapped_offset = 0;
+          mapped_size = roundUpToPage(size);
+#else
           alloc = SECTION_MALLOC;
           start = stgCallocBytes(1, size, "ocGetNames_ELF(BSS)");
           mapped_start = start;
+#endif
          /*
          debugBelch("BSS section at 0x%x, size %d\n",
                          zspace, shdr[i].sh_size);
          */
-      }
+          addSection(&sections[i], kind, alloc, start, size,
+                     mapped_offset, mapped_start, mapped_size);
+
+          oc->sections[i].info->nstubs = 0;
+          oc->sections[i].info->stub_offset = NULL;
+          oc->sections[i].info->stub_size = 0;
+          oc->sections[i].info->stubs = NULL;
+      } else if (kind != SECTIONKIND_OTHER && size > 0) {
+
+#if defined(NEED_PLT)
+          /* To support stubs next to sections, we will use the following
+           * layout:
+           *
+           * .--------------.
+           * | Section data |
+           * |--------------|
+           * | Stub space   |
+           * '--------------'
+           *
+           * This ensures that the plt stubs are in range for the section data,
+           * Unless the section data exceeds the size for relative jump, in
+           * which case I wouldn't know how to solve this, without starting to
+           * break up the section itself.
+           */
+
+          unsigned nstubs = numberOfStubsForSection(oc, i);
+          unsigned stub_space = STUB_SIZE * nstubs;
+
+          void * mem = mmap(NULL, size+stub_space,
+                            PROT_READ | PROT_WRITE | PROT_EXEC,
+                            MAP_ANON | MAP_PRIVATE,
+                            -1, 0);
+
+          if( mem == MAP_FAILED ) {
+              barf("failed to mmap allocated memory to load section %d. "
+                   "errno = %d", i, errno);
+          }
 
-      else if (kind != SECTIONKIND_OTHER && size > 0) {
+          /* copy only the image part over; we don't want to copy data
+           * into the stub part.
+           */
+          memcpy( mem, oc->image + offset, size );
+
+          alloc = SECTION_MMAP;
+
+          mapped_offset = 0;
+          mapped_size = roundUpToPage(size+stub_space);
+          start = mem;
+          mapped_start = mem;
+#else
           if (USE_CONTIGUOUS_MMAP) {
               // already mapped.
               start = oc->image + offset;
@@ -696,155 +774,190 @@ ocGetNames_ELF ( ObjectCode* oc )
               if (start == NULL) goto fail;
               alloc = SECTION_MMAP;
           }
+#endif
+          addSection(&sections[i], kind, alloc, start, size,
+                     mapped_offset, mapped_start, mapped_size);
+
+#if defined(NEED_PLT)
+          oc->sections[i].info->nstubs = 0;
+          oc->sections[i].info->stub_offset = (uint8_t*)mem + size;
+          oc->sections[i].info->stub_size = stub_space;
+          oc->sections[i].info->stubs = NULL;
+#else
+          oc->sections[i].info->nstubs = 0;
+          oc->sections[i].info->stub_offset = NULL;
+          oc->sections[i].info->stub_size = 0;
+          oc->sections[i].info->stubs = NULL;
+#endif
+
           addProddableBlock(oc, start, size);
+      } else {
+          addSection(&oc->sections[i], kind, alloc, oc->image+offset, size,
+                     0, 0, 0);
+          oc->sections[i].info->nstubs = 0;
+          oc->sections[i].info->stub_offset = NULL;
+          oc->sections[i].info->stub_size = 0;
+          oc->sections[i].info->stubs = NULL;
       }
+      oc->sections[i].info->name          = oc->info->sectionHeaderStrtab
+                                            + shdr[i].sh_name;
+      oc->sections[i].info->sectionHeader = &shdr[i];
+
+
 
-      addSection(&sections[i], kind, alloc, start, size,
-                 mapped_offset, mapped_start, mapped_size);
 
       if (shdr[i].sh_type != SHT_SYMTAB) continue;
 
       /* copy stuff into this module's object symbol table */
-      stab = (Elf_Sym*) (ehdrC + offset);
-      strtab = ehdrC + shdr[shdr[i].sh_link].sh_offset;
-      nent = shdr[i].sh_size / sizeof(Elf_Sym);
 
-      oc->n_symbols = nent;
-      oc->symbols = stgCallocBytes(oc->n_symbols, sizeof(SymbolName*),
+      oc->n_symbols = 0;
+      for(ElfSymbolTable *symTab = oc->info->symbolTables;
+          symTab != NULL; symTab = symTab->next) {
+          oc->n_symbols += symTab->n_symbols;
+      }
+
+      oc->symbols = stgCallocBytes(oc->n_symbols, sizeof(Symbol_t),
                                    "ocGetNames_ELF(oc->symbols)");
       // Note calloc: if we fail partway through initializing symbols, we need
       // to undo the additions to the symbol table so far. We know which ones
       // have been added by whether the entry is NULL or not.
 
+      unsigned curSymbol = 0;
+
       //TODO: we ignore local symbols anyway right? So we can use the
       //      shdr[i].sh_info to get the index of the first non-local symbol
       // ie we should use j = shdr[i].sh_info
-      for (j = 0; j < nent; j++) {
+       for(ElfSymbolTable *symTab = oc->info->symbolTables;
+           symTab != NULL; symTab = symTab->next) {
+           for (size_t j = 0; j < symTab->n_symbols; j++) {
 
-         char  isLocal  = false; /* avoids uninit-var warning */
-         HsBool isWeak  = HS_BOOL_FALSE;
-         SymbolAddr* ad  = NULL;
-         SymbolName* nm  = strtab + stab[j].st_name;
-         unsigned short shndx = stab[j].st_shndx;
-         Elf_Word secno;
+               char isLocal = false; /* avoids uninit-var warning */
+               HsBool isWeak = HS_BOOL_FALSE;
+               SymbolName *nm = symTab->symbols[j].name;
+               unsigned short shndx = symTab->symbols[j].elf_sym->st_shndx;
 
-         /* See Note [Many ELF Sections] */
-         /* Note that future checks for special SHN_* numbers should check the
-          * shndx variable, not the section number in secno. Sections with the
-          * real number in the SHN_LORESERVE..HIRESERVE range will have shndx
-          * SHN_XINDEX and a secno with one of the reserved values. */
-         secno = shndx;
+               ElfSymbol *symbol = &symTab->symbols[j];
+
+               Elf_Word secno;
+
+
+               /* See Note [Many ELF Sections] */
+               /* Note that future checks for special SHN_* numbers should check
+                * the shndx variable, not the section number in secno. Sections
+                * with the real number in the SHN_LORESERVE..HIRESERVE range
+                * will have shndx SHN_XINDEX and a secno with one of the
+                * reserved values. */
+               secno = shndx;
 #if defined(SHN_XINDEX)
-         if (shndx == SHN_XINDEX) {
-            ASSERT(shndxTable);
-            secno = shndxTable[j];
-         }
+               if (shndx == SHN_XINDEX) {
+                  ASSERT(shndxTable);
+                  secno = shndxTable[j];
+               }
 #endif
-         /* Figure out if we want to add it; if so, set ad to its
-            address.  Otherwise leave ad == NULL. */
-
-         if (shndx == SHN_COMMON) {
-            isLocal = false;
-            ad = stgCallocBytes(1, stab[j].st_size, "ocGetNames_ELF(COMMON)");
-            /*
-            debugBelch("COMMON symbol, size %d name %s\n",
-                            stab[j].st_size, nm);
-            */
-            /* Pointless to do addProddableBlock() for this area,
-               since the linker should never poke around in it. */
-         }
-         else
-         if ( ( ELF_ST_BIND(stab[j].st_info)==STB_GLOBAL
-                || ELF_ST_BIND(stab[j].st_info)==STB_LOCAL
-                || ELF_ST_BIND(stab[j].st_info)==STB_WEAK
-              )
-              /* and not an undefined symbol */
-              && shndx != SHN_UNDEF
-              /* and not in a "special section" */
-              && (shndx < SHN_LORESERVE
+               /* Figure out if we want to add it; if so, set ad to its
+                  address.  Otherwise leave ad == NULL. */
+
+               if (shndx == SHN_COMMON) {
+                   isLocal = false;
+                   symbol->addr = stgCallocBytes(1, symbol->elf_sym->st_size,
+                                       "ocGetNames_ELF(COMMON)");
+                   /*
+                   debugBelch("COMMON symbol, size %d name %s\n",
+                                   stab[j].st_size, nm);
+                   */
+                   /* Pointless to do addProddableBlock() for this area,
+                      since the linker should never poke around in it. */
+               } else if ((ELF_ST_BIND(symbol->elf_sym->st_info) == STB_GLOBAL
+                           || ELF_ST_BIND(symbol->elf_sym->st_info) == STB_LOCAL
+                           || ELF_ST_BIND(symbol->elf_sym->st_info) == STB_WEAK
+                                                                  )
+                          /* and not an undefined symbol */
+                          && shndx != SHN_UNDEF
+                          /* and not in a "special section" */
+                          && (shndx < SHN_LORESERVE
 #if defined(SHN_XINDEX)
-                  || shndx == SHN_XINDEX
-#endif
-                 )
-              &&
-              /* and it's a not a section or string table or anything silly */
-              ( 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 >=. */
-            ASSERT(secno > 0 && secno < shnum);
-            /*
-            if (shdr[secno].sh_type == SHT_NOBITS) {
-               debugBelch("   BSS symbol, size %d off %d name %s\n",
-                               stab[j].st_size, stab[j].st_value, nm);
-            }
-            */
-            ad = (SymbolAddr*)((intptr_t)sections[secno].start +
-                         (intptr_t)stab[j].st_value);
-            if (ELF_ST_BIND(stab[j].st_info)==STB_LOCAL) {
-               isLocal = true;
-               isWeak = false;
-            } else { /* STB_GLOBAL or STB_WEAK */
-#if defined(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 = (SymbolAddr*)allocateFunctionDesc((Elf_Addr)ad);
+                                  || shndx == SHN_XINDEX
 #endif
-               IF_DEBUG(linker,debugBelch( "addOTabName(GLOB): %10p  %s %s\n",
-                                      ad, oc->fileName, nm ));
-               isLocal = false;
-               isWeak = (ELF_ST_BIND(stab[j].st_info)==STB_WEAK);
-            }
-         }
-
-         /* And the decision is ... */
-
-         oc->symbols[j] = nm;
-
-         if (ad != NULL) {
-            ASSERT(nm != NULL);
-            /* Acquire! */
-            if (isLocal) {
-                /* Ignore entirely. */
-                oc->symbols[j] = NULL;
-            } else {
-
-                if (isWeak == HS_BOOL_TRUE) {
-                    setWeakSymbol(oc, nm);
-                }
+                          )
+                          &&
+                          /* and it's a not a section or string table or
+                           * anything silly */
+                          (ELF_ST_TYPE(symbol->elf_sym->st_info) == STT_FUNC
+                          || ELF_ST_TYPE(symbol->elf_sym->st_info) == STT_OBJECT
+                          || ELF_ST_TYPE(symbol->elf_sym->st_info) == STT_NOTYPE
+                          )
+                       ) {
+                   /* Section 0 is the undefined section, hence > and not >=. */
+                   ASSERT(secno > 0 && secno < shnum);
+                   /*
+                   if (shdr[secno].sh_type == SHT_NOBITS) {
+                      debugBelch("   BSS symbol, size %d off %d name %s\n",
+                                      stab[j].st_size, stab[j].st_value, nm);
+                   }
+                   */
+                   symbol->addr = (SymbolAddr*)(
+                           (intptr_t) oc->sections[secno].start +
+                           (intptr_t) symbol->elf_sym->st_value);
+
+                   if (ELF_ST_BIND(symbol->elf_sym->st_info) == STB_LOCAL) {
+                       isLocal = true;
+                       isWeak = false;
+                   } else { /* STB_GLOBAL or STB_WEAK */
+                       IF_DEBUG(linker,
+                                debugBelch("addOTabName(GLOB): %10p  %s %s\n",
+                                           symbol->addr, oc->fileName, nm));
+                       isLocal = false;
+                       isWeak = ELF_ST_BIND(symbol->elf_sym->st_info)
+                                == STB_WEAK;
+                   }
+               }
 
-                if (! ghciInsertSymbolTable(oc->fileName, symhash,
-                                            nm, ad, isWeak, oc)) {
-                    goto fail;
-                }
-            }
-         } else {
-            /* Skip. */
-            IF_DEBUG(linker,debugBelch( "skipping `%s'\n",
-                                   nm ));
-
-            /* We're skipping the symbol, but if we ever load this
-               object file we'll want to skip it then too. */
-            oc->symbols[j] = NULL;
-
-            /*
-            debugBelch(
-                    "skipping   bind = %d,  type = %d,  secno = %d   `%s'\n",
-                    (int)ELF_ST_BIND(stab[j].st_info),
-                    (int)ELF_ST_TYPE(stab[j].st_info),
-                    (int)secno,
-                    nm
+               /* And the decision is ... */
+
+               if (symbol->addr != NULL) {
+                   ASSERT(nm != NULL);
+                   /* Acquire! */
+                   if (!isLocal) {
+
+                       if (isWeak == HS_BOOL_TRUE) {
+                           setWeakSymbol(oc, nm);
+                       }
+                       if (!ghciInsertSymbolTable(oc->fileName, symhash,
+                                                  nm, symbol->addr, isWeak, oc)
+                           ) {
+                           goto fail;
+                       }
+                       oc->symbols[curSymbol++].name = nm;
+                       oc->symbols[curSymbol].addr = symbol->addr;
+                   }
+               } else {
+                   /* Skip. */
+                   IF_DEBUG(linker,
+                            debugBelch("skipping `%s'\n",
+                                               nm)
                    );
-            */
-         }
 
+                   /*
+                   debugBelch(
+                      "skipping   bind = %d,  type = %d,  secno = %d   `%s'\n",
+                      (int)ELF_ST_BIND(stab[j].st_info),
+                      (int)ELF_ST_TYPE(stab[j].st_info),
+                      (int)secno,
+                      nm
+                   );
+                   */
+               }
+           }
       }
    }
 
+#if defined(NEED_GOT)
+   if(makeGot( oc ))
+       errorBelch("Failed to create GOT for %s",
+                  oc->archiveMemberName
+                  ? oc->archiveMemberName
+                  : oc->fileName);
+#endif
    result = 1;
    goto end;
 
@@ -857,6 +970,10 @@ end:
    return result;
 }
 
+// the aarch64 linker uses relocacteObjectCodeAarch64,
+// see elf_reloc_aarch64.{h,c}
+#if !defined(aarch64_HOST_ARCH)
+
 /* Do ELF relocations which lack an explicit addend.  All x86-linux
    and arm-linux relocations appear to be of this form. */
 static int
@@ -864,347 +981,382 @@ do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC,
                          Elf_Shdr* shdr, int shnum )
 {
    int j;
-   SymbolName* symbol;
+
    Elf_Word* targ;
    Elf_Rel*  rtab = (Elf_Rel*) (ehdrC + shdr[shnum].sh_offset);
-   Elf_Sym*  stab;
-   char*     strtab;
+
    int         nent = shdr[shnum].sh_size / sizeof(Elf_Rel);
    int target_shndx = shdr[shnum].sh_info;
    int symtab_shndx = shdr[shnum].sh_link;
-   int strtab_shndx = shdr[symtab_shndx].sh_link;
-#if defined(SHN_XINDEX)
-   Elf_Word* shndx_table = get_shndx_table((Elf_Ehdr*)ehdrC);
-#endif
 
-   stab  = (Elf_Sym*) (ehdrC + shdr[ symtab_shndx ].sh_offset);
-   strtab= (char*)    (ehdrC + shdr[ strtab_shndx ].sh_offset);
+   ElfSymbolTable *stab = NULL;
+   for(ElfSymbolTable * st = oc->info->symbolTables;
+       st != NULL; st = st->next) {
+       if((int)st->index == symtab_shndx) {
+           stab = st;
+           break;
+       }
+   }
+   ASSERT(stab != NULL);
+
    targ  = (Elf_Word*)oc->sections[target_shndx].start;
-   IF_DEBUG(linker,debugBelch( "relocations for section %d using symtab %d and strtab %d\n",
-                          target_shndx, symtab_shndx, strtab_shndx ));
+   IF_DEBUG(linker,debugBelch(
+                "relocations for section %d using symtab %d\n",
+                target_shndx, symtab_shndx));
 
    /* Skip sections that we're not interested in. */
    if (oc->sections[target_shndx].kind == SECTIONKIND_OTHER) {
-           IF_DEBUG(linker,debugBelch( "skipping (target section not loaded)"));
-           return 1;
+       IF_DEBUG(linker,debugBelch( "skipping (target section not loaded)"));
+       return 1;
    }
 
    for (j = 0; j < nent; j++) {
-      Elf_Addr offset = rtab[j].r_offset;
-      Elf_Addr info   = rtab[j].r_info;
+       Elf_Addr offset = rtab[j].r_offset;
+       Elf_Addr info   = rtab[j].r_info;
 
-      Elf_Addr  P  = ((Elf_Addr)targ) + offset;
-      Elf_Word* pP = (Elf_Word*)P;
+       Elf_Addr  P  = ((Elf_Addr)targ) + offset;
+       Elf_Word* pP = (Elf_Word*)P;
 #if defined(i386_HOST_ARCH) || defined(DEBUG)
-      Elf_Addr  A  = *pP;
+       Elf_Addr  A  = *pP;
 #endif
-      Elf_Addr  S;
-      void*     S_tmp;
+       Elf_Addr  S;
+       void*     S_tmp;
 #if defined(i386_HOST_ARCH)
-      Elf_Addr  value;
+       Elf_Addr  value;
 #endif
 #if defined(arm_HOST_ARCH)
-      int is_target_thm=0, T=0;
+       int is_target_thm=0, T=0;
 #endif
 
-      IF_DEBUG(linker,debugBelch( "Rel entry %3d is raw(%6p %6p): ",
-                             j, (void*)offset, (void*)info ));
-      if (!info) {
-         IF_DEBUG(linker,debugBelch( " ZERO" ));
-         S = 0;
-      } else {
-         Elf_Sym sym = stab[ELF_R_SYM(info)];
-         /* First see if it is a local symbol. */
-         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;
-            /* See Note [Many ELF Sections] */
-            Elf_Word secno = sym.st_shndx;
-#if defined(SHN_XINDEX)
-            if (secno == SHN_XINDEX) {
-               ASSERT(shndx_table);
-               secno = shndx_table[ELF_R_SYM(info)];
-            }
-#endif
-            S = (Elf_Addr)oc->sections[ secno ].start +
-                stab[ELF_R_SYM(info)].st_value;
-         } else {
-            symbol = strtab + sym.st_name;
-            S_tmp = lookupSymbol_( symbol );
-            S = (Elf_Addr)S_tmp;
-         }
-         if (!S) {
-            errorBelch("%s: unknown symbol `%s'", oc->fileName, symbol);
-            return 0;
-         }
-         IF_DEBUG(linker,debugBelch( "`%s' resolves to %p\n", symbol, (void*)S ));
+       ElfSymbol * symbol = NULL;
+
+       IF_DEBUG(linker,debugBelch( "Rel entry %3d is raw(%6p %6p): ",
+                                   j, (void*)offset, (void*)info ));
+       if (!info) {
+           IF_DEBUG(linker,debugBelch( " ZERO" ));
+           S = 0;
+       } else {
+           symbol = &stab->symbols[ELF_R_SYM(info)];
+           /* First see if it is a local symbol. */
+           if (ELF_ST_BIND(symbol->elf_sym->st_info) == STB_LOCAL) {
+               S = (Elf_Addr)symbol->addr;
+           } else {
+               S_tmp = lookupSymbol_( symbol->name );
+               S = (Elf_Addr)S_tmp;
+           }
+           if (!S) {
+               errorBelch("%s: unknown symbol `%s'",
+                          oc->fileName, symbol->name);
+               return 0;
+           }
+           IF_DEBUG(linker,debugBelch( "`%s' resolves to %p\n", symbol->name,
+                                       (void*)S ));
 
 #if defined(arm_HOST_ARCH)
-          /*
-           * 4.5.3 Symbol Values
-           *
-           * In addition to the normal rules for symbol values the following
-           * rules shall also apply to symbols of type STT_FUNC:
-           * - If the symbol addresses an ARM instruction, its value is the
-           *   address of the instruction (in a relocatable object, the
-           *   offset of the instruction from the start of the section
-           *   containing it).
-           * - If the symbol addresses a Thumb instruction, its value is the
-           *   address of the instruction with bit zero set (in a relocatable
-           *   object, the section offset with bit zero set).
-           * - For the purposes of relocation the value used shall be the
-           *   address of the instruction (st_value & ~1).
-           *
-           *  Note: This allows a linker to distinguish ARM and Thumb code
-           *        symbols without having to refer to the map. An ARM symbol
-           *        will always have an even value, while a Thumb symbol will
-           *        always have an odd value. However, a linker should strip
-           *        the discriminating bit from the value before using it for
-           *        relocation.
-           *
-           * (source: ELF for the ARM Architecture
-           *          ARM IHI 0044F, current through ABI release 2.10
-           *          24th November 2015)
-           */
-          if(ELF_ST_TYPE(sym.st_info) == STT_FUNC) {
-              is_target_thm = S & 0x1;
-              T = is_target_thm;
-              S &= ~1;
-          }
+           /*
+            * 4.5.3 Symbol Values
+            *
+            * In addition to the normal rules for symbol values the following
+            * rules shall also apply to symbols of type STT_FUNC:
+            * - If the symbol addresses an ARM instruction, its value is the
+            *   address of the instruction (in a relocatable object, the
+            *   offset of the instruction from the start of the section
+            *   containing it).
+            * - If the symbol addresses a Thumb instruction, its value is the
+            *   address of the instruction with bit zero set (in a relocatable
+            *   object, the section offset with bit zero set).
+            * - For the purposes of relocation the value used shall be the
+            *   address of the instruction (st_value & ~1).
+            *
+            *  Note: This allows a linker to distinguish ARM and Thumb code
+            *        symbols without having to refer to the map. An ARM symbol
+            *        will always have an even value, while a Thumb symbol will
+            *        always have an odd value. However, a linker should strip
+            *        the discriminating bit from the value before using it for
+            *        relocation.
+            *
+            * (source: ELF for the ARM Architecture
+            *          ARM IHI 0044F, current through ABI release 2.10
+            *          24th November 2015)
+            */
+           if(ELF_ST_TYPE(symbol->elf_sym->st_info) == STT_FUNC) {
+               is_target_thm = S & 0x1;
+               T = is_target_thm;
+               S &= ~1;
+           }
 #endif
-      }
+       }
 
-      int reloc_type = ELF_R_TYPE(info);
-      IF_DEBUG(linker,debugBelch( "Reloc: P = %p   S = %p   A = %p   type=%d\n",
-                             (void*)P, (void*)S, (void*)A, reloc_type ));
-      checkProddableBlock ( oc, pP, sizeof(Elf_Word) );
+       int reloc_type = ELF_R_TYPE(info);
+       IF_DEBUG(linker,debugBelch("Reloc: P = %p   S = %p   A = %p   type=%d\n",
+                                  (void*)P, (void*)S, (void*)A, reloc_type ));
+       checkProddableBlock ( oc, pP, sizeof(Elf_Word) );
 
 #if defined(i386_HOST_ARCH)
-      value = S + A;
+       value = S + A;
 #endif
 
-      switch (reloc_type) {
+       switch (reloc_type) {
 #        ifdef i386_HOST_ARCH
-         case COMPAT_R_386_32:   *pP = value;     break;
-         case COMPAT_R_386_PC32: *pP = value - P; break;
+       case COMPAT_R_386_NONE:                  break;
+       case COMPAT_R_386_32:   *pP = value;     break;
+       case COMPAT_R_386_PC32: *pP = value - P; break;
 #        endif
 
 #        ifdef arm_HOST_ARCH
-         case COMPAT_R_ARM_ABS32:
-         // Specified by Linux ARM ABI to be equivalent to ABS32
-         case COMPAT_R_ARM_TARGET1:
-            *(Elf32_Word *)P += S;
-            *(Elf32_Word *)P |= T;
-            break;
-
-         case COMPAT_R_ARM_REL32:
-            *(Elf32_Word *)P += S;
-            *(Elf32_Word *)P |= T;
-            *(Elf32_Word *)P -= P;
-            break;
-
-         case COMPAT_R_ARM_CALL:
-         case COMPAT_R_ARM_JUMP24:
-         {
-            // N.B. LLVM's LLD linker's relocation implement is a fantastic
-            // resource
-            StgWord32 *word = (StgWord32 *)P;
-            StgInt32 imm = (*word & ((1<<24)-1)) << 2;
-
-            const StgBool is_blx = (*word & 0xf0000000) == 0xf0000000;
-            const StgWord32 hBit = is_blx ? ((*word >> 24) & 1) : 0;
-            imm |= hBit << 1;
+       case COMPAT_R_ARM_ABS32:
+           // Specified by Linux ARM ABI to be equivalent to ABS32
+       case COMPAT_R_ARM_TARGET1:
+           *(Elf32_Word *)P += S;
+           *(Elf32_Word *)P |= T;
+           break;
+
+       case COMPAT_R_ARM_REL32:
+           *(Elf32_Word *)P += S;
+           *(Elf32_Word *)P |= T;
+           *(Elf32_Word *)P -= P;
+           break;
+
+       case COMPAT_R_ARM_CALL:
+       case COMPAT_R_ARM_JUMP24:
+       {
+           // N.B. LLVM's LLD linker's relocation implementation is a fantastic
+           // resource
+           StgWord32 *word = (StgWord32 *)P;
+           StgInt32 imm = (*word & ((1<<24)-1)) << 2;
+
+           const StgBool is_blx = (*word & 0xf0000000) == 0xf0000000;
+           const StgWord32 hBit = is_blx ? ((*word >> 24) & 1) : 0;
+           imm |= hBit << 1;
+
+           // Sign extend to 32 bits
+           // I would have thought this would be 24 bits but LLD uses 26 here.
+           // Hmm.
+           int32_t A = signExtend32(26, imm);
+
+           S = S + A; A = 0;
+
+           StgWord32 result = ((S + A) | T) - P;
+
+           const StgBool overflow = !isInt(26, (StgInt32) result);
+           // Handle overflow and Thumb interworking
+           const StgBool needs_veneer =
+               (is_target_thm && ELF_R_TYPE(info) == COMPAT_R_ARM_JUMP24)
+               || overflow;
+
+           if(needs_veneer) { /* overflow or thum interworking */
+               // Note [PC bias]
+               // From the ELF for the ARM Architecture documentation:
+               // > 4.6.1.1 Addends and PC-bias compensation
+               // > A binary file may use REL or RELA relocations or a mixture
+               // > of the two (but multiple relocations for the same address
+               // > must use only one type).
+               // > If the relocation is pc-relative then compensation for the
+               // > PC bias (the PC value is 8 bytes ahead of the executing
+               // > instruction in ARM state and 4 bytes in Thumb state) must
+               // > be encoded in the relocation by the object producer.
+               int32_t bias = 8;
+
+               S += bias;
+               /* try to locate an existing stub for this target */
+               if(findStub(&oc->sections[target_shndx], (void**)&S, 0)) {
+                   /* didn't find any. Need to create one */
+                   if(makeStub(&oc->sections[target_shndx], (void**)&S, 0)) {
+                       errorBelch("Unable to create veneer for ARM_CALL\n");
+                       return 0;
+                   }
+               }
+               S -= bias;
 
-            // Sign extend to 32 bits
-            // I would have thought this would be 24 bits but LLD uses 26 here.
-            // Hmm.
-            imm = sign_extend32(26, imm);
+               result = ((S + A) | T) - P;
+               result &= ~1; // Clear thumb indicator bit
 
-            StgWord32 result = ((S + imm) | T) - P;
+               ASSERT(isInt(26, result)); /* X in range */
+           }
 
-            const StgBool overflow = !is_int(26, (StgInt32) result);
-            // Handle overflow and Thumb interworking
-            const StgBool needs_veneer =
-                (is_target_thm && ELF_R_TYPE(info) == COMPAT_R_ARM_JUMP24)
-                || overflow;
+           // Update the branch target
+           const StgWord32 imm24 = (result & 0x03fffffc) >> 2;
+           *word = (*word & ~0x00ffffff)
+                 | (imm24 & 0x00ffffff);
 
-            if (needs_veneer) {
-               // Generate veneer
-               // The +8 below is to undo the PC-bias compensation done by the
-               // object producer
-               SymbolExtra *extra = makeArmSymbolExtra(oc, ELF_R_SYM(info),
-                                                       S+imm+8, 0,
-                                                       is_target_thm);
-               // The -8 below is to compensate for PC bias
-               result = (StgWord32) ((StgInt32) extra->jumpIsland - P - 8);
-               result &= ~1; // Clear thumb indicator bit
-               if (!is_int(26, (StgInt32) result)) {
-                  errorBelch("Unable to fixup overflow'd R_ARM_CALL: "
-                             "jump island=%p, reloc=%p\n",
-                             (void*) extra->jumpIsland, (void*) P);
-                  return 0;
-               }
-            }
-            // Update the branch target
-            const StgWord32 imm24 = (result & 0x03fffffc) >> 2;
-            *word = (*word & ~0x00ffffff)
-                  | (imm24 & 0x00ffffff);
-
-            // Change the relocated branch into a BLX if necessary
-            const StgBool switch_mode =
-                is_target_thm && (reloc_type == COMPAT_R_ARM_CALL);
-            if (!needs_veneer && switch_mode) {
+           // Change the relocated branch into a BLX if necessary
+           const StgBool switch_mode =
+               is_target_thm && (reloc_type == COMPAT_R_ARM_CALL);
+           if (!needs_veneer && switch_mode) {
                const StgWord32 hBit = (result & 0x2) >> 1;
                // Change instruction to BLX
                *word = (*word & ~0xFF000000) | ((0xfa | hBit) << 24);
                IF_DEBUG(linker, debugBelch("Changed BL to BLX at %p\n", word));
-            }
-            break;
-         }
+           }
+           break;
+       }
 
-         case COMPAT_R_ARM_MOVT_ABS:
-         case COMPAT_R_ARM_MOVW_ABS_NC:
-         {
-            StgWord32 *word = (StgWord32 *)P;
-            StgWord32 imm12 = *word & 0xfff;
-            StgWord32 imm4 = (*word >> 16) & 0xf;
-            StgInt32 offset = imm4 << 12 | imm12;
-            StgWord32 result = (S + offset) | T;
-
-            if (reloc_type == COMPAT_R_ARM_MOVT_ABS)
-                result = (result & 0xffff0000) >> 16;
-
-            StgWord32 result12 = result & 0xfff;
-            StgWord32 result4 = (result >> 12) & 0xf;
-            *word = (*word & ~0xf0fff) | (result4 << 16) | result12;
-            break;
-         }
+       case COMPAT_R_ARM_MOVT_ABS:
+       case COMPAT_R_ARM_MOVW_ABS_NC:
+       {
+           StgWord32 *word = (StgWord32 *)P;
+           StgWord32 imm12 = *word & 0xfff;
+           StgWord32 imm4 = (*word >> 16) & 0xf;
+           StgInt32 offset = imm4 << 12 | imm12;
+           StgWord32 result = (S + offset) | T;
+
+           if (reloc_type == COMPAT_R_ARM_MOVT_ABS)
+               result = (result & 0xffff0000) >> 16;
+
+           StgWord32 result12 = result & 0xfff;
+           StgWord32 result4 = (result >> 12) & 0xf;
+           *word = (*word & ~0xf0fff) | (result4 << 16) | result12;
+           break;
+       }
+
+       case COMPAT_R_ARM_THM_CALL:
+       case COMPAT_R_ARM_THM_JUMP24:
+       {
+           StgWord16 *upper = (StgWord16 *)P;
+           StgWord16 *lower = (StgWord16 *)(P + 2);
+
+           int overflow;
+           int to_thm = (*lower >> 12) & 1;
+           int sign = (*upper >> 10) & 1;
+           int j1, j2, i1, i2;
 
-         case COMPAT_R_ARM_THM_CALL:
-         case COMPAT_R_ARM_THM_JUMP24:
-         {
-            StgWord16 *upper = (StgWord16 *)P;
-            StgWord16 *lower = (StgWord16 *)(P + 2);
-
-            int overflow;
-            int to_thm = (*lower >> 12) & 1;
-            int sign = (*upper >> 10) & 1;
-            int j1, j2, i1, i2;
-
-            // Decode immediate value
-            j1 = (*lower >> 13) & 1; i1 = ~(j1 ^ sign) & 1;
-            j2 = (*lower >> 11) & 1; i2 = ~(j2 ^ sign) & 1;
-            StgInt32 imm = (sign << 24)
-                         | (i1 << 23)
-                         | (i2 << 22)
-                         | ((*upper & 0x03ff) << 12)
-                         | ((*lower & 0x07ff) << 1);
+           // Decode immediate value
+           j1 = (*lower >> 13) & 1; i1 = ~(j1 ^ sign) & 1;
+           j2 = (*lower >> 11) & 1; i2 = ~(j2 ^ sign) & 1;
+
+           StgInt32 A = (sign << 24)
+                        | (i1 << 23)
+                        | (i2 << 22)
+                        | ((*upper & 0x03ff) << 12)
+                        | ((*lower & 0x07ff) << 1);
 
             // Sign extend 25 to 32 bits
-            if (imm & 0x01000000)
-               imm -= 0x02000000;
+           if (A & 0x01000000)
+               A -= 0x02000000;
+
+           S = S + A; A = 0;
 
-            offset = ((imm + S) | T) - P;
-            overflow = offset <= (StgWord32)0xff000000
-                    || offset >= (StgWord32)0x01000000;
+           offset = ((S + A) | T) - P;
+           overflow = offset <= (StgWord32)0xff000000
+                   || offset >= (StgWord32)0x01000000;
 
-            if ((!is_target_thm && ELF_R_TYPE(info) == COMPAT_R_ARM_THM_JUMP24)
-                || overflow) {
+           if ((!is_target_thm && ELF_R_TYPE(info) == COMPAT_R_ARM_THM_JUMP24)
+               || overflow) {
                // Generate veneer
-               SymbolExtra *extra = makeArmSymbolExtra(oc, ELF_R_SYM(info),
-                                                       S+imm+4, 1,
-                                                       is_target_thm);
-               offset = (StgWord32) &extra->jumpIsland - P - 4;
+
+               // see [PC bias] above.
+               int32_t bias = 4;
+               S += bias;
+               // set the Thumb indicator to S, the final address should
+               // carry the correct thumb indicator.
+               S |= T;
+               /* try to locate an existing stub for this target */
+               if(findStub(&oc->sections[target_shndx], (void**)&S, 1)) {
+                   /* didn't find any. Need to create one */
+                   if(makeStub(&oc->sections[target_shndx], (void**)&S, 1)) {
+                       errorBelch("Unable to create veneer for ARM_THM_CALL\n");
+                       return 0;
+                   }
+               }
+               S -= bias;
+
+               offset = ((S + A) | T) - P;
+
                sign = offset >> 31;
                to_thm = 1;
-            } else if (!is_target_thm
-                       && ELF_R_TYPE(info) == COMPAT_R_ARM_THM_CALL) {
+           } else if (!is_target_thm
+                      && ELF_R_TYPE(info) == COMPAT_R_ARM_THM_CALL) {
                offset &= ~0x3;
                to_thm = 0;
-            }
-
-            // Reencode instruction
-            i1 = ~(offset >> 23) & 1; j1 = sign ^ i1;
-            i2 = ~(offset >> 22) & 1; j2 = sign ^ i2;
-            *upper = ( (*upper & 0xf800)
-                   | (sign << 10)
-                   | ((offset >> 12) & 0x03ff) );
-            *lower = ( (*lower & 0xd000)
-                   | (j1 << 13)
-                   | (to_thm << 12)
-                   | (j2 << 11)
-                   | ((offset >> 1) & 0x07ff) );
-            break;
-         }
+           }
+
+           // Reencode instruction
+           i1 = ~(offset >> 23) & 1; j1 = sign ^ i1;
+           i2 = ~(offset >> 22) & 1; j2 = sign ^ i2;
+           *upper = ( (*upper & 0xf800)
+                  | (sign << 10)
+                  | ((offset >> 12) & 0x03ff) );
+           *lower = ( (*lower & 0xd000)
+                  | (j1 << 13)
+                  | (to_thm << 12)
+                  | (j2 << 11)
+                  | ((offset >> 1) & 0x07ff) );
+           break;
+       }
 
-         case COMPAT_R_ARM_THM_MOVT_ABS:
-         case COMPAT_R_ARM_THM_MOVW_ABS_NC:
-         {
-            StgWord16 *upper = (StgWord16 *)P;
-            StgWord16 *lower = (StgWord16 *)(P + 2);
-            StgInt32 offset = ((*upper & 0x000f) << 12)
-                            | ((*upper & 0x0400) << 1)
-                            | ((*lower & 0x7000) >> 4)
-                            | (*lower & 0x00ff);
-
-            offset = (offset ^ 0x8000) - 0x8000; // Sign extend
-            offset += S;
-            if (ELF_R_TYPE(info) == COMPAT_R_ARM_THM_MOVW_ABS_NC)
-                   offset |= T;
-            else if (ELF_R_TYPE(info) == COMPAT_R_ARM_THM_MOVT_ABS)
-                   offset >>= 16;
-
-            *upper = ( (*upper & 0xfbf0)
-                   | ((offset & 0xf000) >> 12)
-                   | ((offset & 0x0800) >> 1) );
-            *lower = ( (*lower & 0x8f00)
-                   | ((offset & 0x0700) << 4)
-                   | (offset & 0x00ff) );
-            break;
-         }
+       case COMPAT_R_ARM_THM_MOVT_ABS:
+       case COMPAT_R_ARM_THM_MOVW_ABS_NC:
+       {
+           StgWord16 *upper = (StgWord16 *)P;
+           StgWord16 *lower = (StgWord16 *)(P + 2);
+           StgInt32 offset = ((*upper & 0x000f) << 12)
+                           | ((*upper & 0x0400) << 1)
+                           | ((*lower & 0x7000) >> 4)
+                           | (*lower & 0x00ff);
+
+           offset = (offset ^ 0x8000) - 0x8000; // Sign extend
+           offset += S;
+           if (ELF_R_TYPE(info) == COMPAT_R_ARM_THM_MOVW_ABS_NC)
+               offset |= T;
+           else if (ELF_R_TYPE(info) == COMPAT_R_ARM_THM_MOVT_ABS)
+               offset >>= 16;
+
+           *upper = ( (*upper & 0xfbf0)
+                  | ((offset & 0xf000) >> 12)
+                  | ((offset & 0x0800) >> 1) );
+           *lower = ( (*lower & 0x8f00)
+                  | ((offset & 0x0700) << 4)
+                  | (offset & 0x00ff) );
+           break;
+       }
 
-         case COMPAT_R_ARM_THM_JUMP8:
-         {
-            StgWord16 *word = (StgWord16 *)P;
-            StgWord offset = *word & 0x01fe;
-            offset += S - P;
-            if (!is_target_thm) {
+       case COMPAT_R_ARM_THM_JUMP8:
+       {
+           StgWord16 *word = (StgWord16 *)P;
+           StgWord offset = *word & 0x01fe;
+           offset += S - P;
+           if (!is_target_thm) {
                errorBelch("%s: Thumb to ARM transition with JUMP8 relocation "
                           "not supported\n",
-                     oc->fileName);
+                          oc->fileName);
                return 0;
-            }
+           }
 
-            *word = (*word & ~0x01fe)
-                  | (offset & 0x01fe);
-            break;
-         }
+           *word = (*word & ~0x01fe)
+                 | (offset & 0x01fe);
+           break;
+       }
 
-         case COMPAT_R_ARM_THM_JUMP11:
-         {
-            StgWord16 *word = (StgWord16 *)P;
-            StgWord offset = *word & 0x0ffe;
-            offset += S - P;
-            if (!is_target_thm) {
+       case COMPAT_R_ARM_THM_JUMP11:
+       {
+           StgWord16 *word = (StgWord16 *)P;
+           StgWord offset = *word & 0x0ffe;
+           offset += S - P;
+           if (!is_target_thm) {
                errorBelch("%s: Thumb to ARM transition with JUMP11 relocation "
                           "not supported\n",
-                     oc->fileName);
+                          oc->fileName);
                return 0;
-            }
-
-            *word = (*word & ~0x0ffe)
-                  | (offset & 0x0ffe);
-            break;
-         }
+           }
 
+           *word = (*word & ~0x0ffe)
+                 | (offset & 0x0ffe);
+           break;
+       }
+       case COMPAT_R_ARM_GOT_PREL: {
+              int32_t A = *pP;
+              void* GOT_S = symbol->got_addr;
+              ASSERT(GOT_S);
+              *(uint32_t *)P = (uint32_t) GOT_S + A - P;
+              break;
+       }
 #        endif // arm_HOST_ARCH
 
-         default:
-            errorBelch("%s: unhandled ELF relocation(Rel) type %" FMT_Word "\n",
-                  oc->fileName, (W_)ELF_R_TYPE(info));
-            return 0;
-      }
+       default:
+           errorBelch("%s: unhandled ELF relocation(Rel) type %" FMT_Word "\n",
+                      oc->fileName, (W_)ELF_R_TYPE(info));
+           return 0;
+       }
 
    }
    return 1;
@@ -1300,7 +1452,7 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
            errorBelch("%s: unknown symbol `%s'", oc->fileName, symbol);
            return 0;
          }
-         IF_DEBUG(linker,debugBelch( "`%s' resolves to %p\n", symbol, (void*)S ));
+         IF_DEBUG(linker,debugBelch("`%s' resolves to %p\n", symbol, (void*)S));
       }
 
 #if defined(DEBUG) || defined(sparc_HOST_ARCH) || defined(powerpc_HOST_ARCH) \
@@ -1385,13 +1537,13 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
 
          case R_PPC_PLTREL24:
             value -= 0x8000; /* See Note [.LCTOC1 in PPC PIC code] */
-            /* fallthrough */
+            FALLTHROUGH;
          case R_PPC_REL24:
             delta = value - P;
 
             if( delta << 6 >> 6 != delta )
             {
-               value = (Elf_Addr) (&makeSymbolExtra( oc, ELF_R_SYM(info), value )
+               value = (Elf_Addr)(&makeSymbolExtra( oc, ELF_R_SYM(info), value )
                                         ->jumpIsland);
                delta = value - P;
 
@@ -1421,9 +1573,15 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
 #        endif
 
 #if defined(x86_64_HOST_ARCH)
+      case COMPAT_R_X86_64_NONE:
+          break;
+
       case COMPAT_R_X86_64_64:
-          *(Elf64_Xword *)P = value;
+      {
+          Elf64_Xword payload = value;
+          memcpy((void*)P, &payload, sizeof(payload));
           break;
+      }
 
       case COMPAT_R_X86_64_PC32:
       {
@@ -1431,79 +1589,93 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
           barf("R_X86_64_PC32 relocation, but ALWAYS_PIC.");
 #else
           StgInt64 off = value - P;
-          if (off >= 0x7fffffffL || off < -0x80000000L) {
-              if (X86_64_ELF_NONPIC_HACK) {
-                  StgInt64 pltAddress =
-                      (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)
-                                                -> jumpIsland;
-                  off = pltAddress + A - P;
-              } else {
-                  errorBelch("R_X86_64_PC32 relocation out of range: %s = %"
-                             PRId64 "d\nRecompile %s with -fPIC.",
-                             symbol, off, oc->fileName );
-                  return 0;
-              }
+          if (off != (Elf64_Sword)off && X86_64_ELF_NONPIC_HACK) {
+              StgInt64 pltAddress =
+                  (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)
+                                            -> jumpIsland;
+              off = pltAddress + A - P;
           }
-          *(Elf64_Word *)P = (Elf64_Word)off;
+          if (off != (Elf64_Sword)off) {
+              errorBelch(
+                  "R_X86_64_PC32 relocation out of range: %s = %" PRIx64
+                  "\nRecompile %s with -fPIC -fexternal-dynamic-refs.",
+                  symbol, off, oc->fileName);
+              return 0;
+          }
+          Elf64_Sword payload = off;
+          memcpy((void*)P, &payload, sizeof(payload));
 #endif
           break;
       }
 
       case COMPAT_R_X86_64_PC64:
       {
-          StgInt64 off = value - P;
-          *(Elf64_Word *)P = (Elf64_Word)off;
+          Elf64_Sxword payload = value - P;
+          memcpy((void*)P, &payload, sizeof(payload));
           break;
       }
 
       case COMPAT_R_X86_64_32:
+      {
 #if defined(ALWAYS_PIC)
           barf("R_X86_64_32 relocation, but ALWAYS_PIC.");
 #else
-          if (value >= 0x7fffffffL) {
-              if (X86_64_ELF_NONPIC_HACK) {
-                  StgInt64 pltAddress =
-                      (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)
-                                                -> jumpIsland;
-                  value = pltAddress + A;
-              } else {
-                  errorBelch("R_X86_64_32 relocation out of range: %s = %"
-                         PRId64 "d\nRecompile %s with -fPIC.",
-                         symbol, value, oc->fileName );
-                  return 0;
-              }
+          if (value != (Elf64_Word)value && X86_64_ELF_NONPIC_HACK) {
+              StgInt64 pltAddress =
+                  (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)
+                                            -> jumpIsland;
+              value = pltAddress + A;
           }
-          *(Elf64_Word *)P = (Elf64_Word)value;
+          if (value != (Elf64_Word)value) {
+              errorBelch(
+                  "R_X86_64_32 relocation out of range: %s = %" PRIx64
+                  "\nRecompile %s with -fPIC -fexternal-dynamic-refs.",
+                  symbol, value, oc->fileName);
+              return 0;
+          }
+          Elf64_Word payload = value;
+          memcpy((void*)P, &payload, sizeof(payload));
 #endif
           break;
+      }
 
       case COMPAT_R_X86_64_32S:
+      {
 #if defined(ALWAYS_PIC)
           barf("R_X86_64_32S relocation, but ALWAYS_PIC.");
 #else
-          if ((StgInt64)value > 0x7fffffffL || (StgInt64)value < -0x80000000L) {
-              if (X86_64_ELF_NONPIC_HACK) {
-                  StgInt64 pltAddress =
-                      (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)
-                                                -> jumpIsland;
-                  value = pltAddress + A;
-              } else {
-                  errorBelch("R_X86_64_32S relocation out of range: %s = %"
-                         PRId64 "d\nRecompile %s with -fPIC.",
-                         symbol, value, oc->fileName );
-                  return 0;
-              }
+          if ((StgInt64)value != (Elf64_Sword)value && X86_64_ELF_NONPIC_HACK) {
+              StgInt64 pltAddress =
+                  (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)
+                                            -> jumpIsland;
+              value = pltAddress + A;
+          }
+          if ((StgInt64)value != (Elf64_Sword)value) {
+              errorBelch(
+                  "R_X86_64_32S relocation out of range: %s = %" PRIx64
+                  "\nRecompile %s with -fPIC -fexternal-dynamic-refs.",
+                  symbol, value, oc->fileName);
+              return 0;
           }
-          *(Elf64_Sword *)P = (Elf64_Sword)value;
+          Elf64_Sword payload = value;
+          memcpy((void*)P, &payload, sizeof(payload));
 #endif
           break;
+      }
       case COMPAT_R_X86_64_REX_GOTPCRELX:
       case COMPAT_R_X86_64_GOTPCRELX:
       case COMPAT_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;
+          if (off != (Elf64_Sword)off) {
+              barf(
+                  "COMPAT_R_X86_64_GOTPCREL relocation out of range: "
+                  "%s = %" PRIx64 " in %s.",
+                  symbol, off, oc->fileName);
+          }
+          Elf64_Sword payload = off;
+          memcpy((void*)P, &payload, sizeof(payload));
           break;
       }
 #if defined(dragonfly_HOST_OS)
@@ -1520,7 +1692,15 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
           /* make entry in GOT that contains said offset */
           StgInt64 gotEntry = (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info),
                                          (S - (Elf64_Addr)(ti.base)))->addr;
-          *(Elf64_Word *)P = gotEntry + A - P;
+          StgInt64 off = gotEntry + A - P;
+          if (off != (Elf64_Sword)off) {
+              barf(
+                  "COMPAT_R_X86_64_GOTTPOFF relocation out of range: "
+                  "%s = %" PRIx64 " in %s.",
+                  symbol, off, oc->fileName);
+          }
+          Elf64_SWord payload = off;
+          memcpy((void*)P, &payload, sizeof(payload));
 #endif
           break;
       }
@@ -1532,19 +1712,26 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
           barf("R_X86_64_PLT32 relocation, but ALWAYS_PIC.");
 #else
           StgInt64 off = value - P;
-          if (off >= 0x7fffffffL || off < -0x80000000L) {
+          if (off != (Elf64_Sword)off) {
               StgInt64 pltAddress = (StgInt64) &makeSymbolExtra(oc, ELF_R_SYM(info), S)
                                                     -> jumpIsland;
               off = pltAddress + A - P;
           }
-          *(Elf64_Word *)P = (Elf64_Word)off;
+          if (off != (Elf64_Sword)off) {
+              barf(
+                  "R_X86_64_PLT32 relocation out of range: "
+                  "%s = %" PRIx64 " in %s.",
+                  symbol, off, oc->fileName);
+          }
+          Elf64_Sword payload = off;
+          memcpy((void*)P, &payload, sizeof(payload));
 #endif
           break;
       }
 #endif
 
          default:
-            errorBelch("%s: unhandled ELF relocation(RelA) type %" FMT_Word "\n",
+            barf("%s: unhandled ELF relocation(RelA) type %" FMT_Word "\n",
                   oc->fileName, (W_)ELF_R_TYPE(info));
             return 0;
       }
@@ -1552,35 +1739,96 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
    }
    return 1;
 }
+#endif /* !aarch64_HOST_ARCH */
+
 
 int
 ocResolve_ELF ( ObjectCode* oc )
 {
-   int       ok;
-   Elf_Word  i;
    char*     ehdrC = (char*)(oc->image);
    Elf_Ehdr* ehdr  = (Elf_Ehdr*) ehdrC;
    Elf_Shdr* shdr  = (Elf_Shdr*) (ehdrC + ehdr->e_shoff);
    const Elf_Word shnum = elf_shnum(ehdr);
 
-   /* Process the relocation sections. */
-   for (i = 0; i < shnum; i++) {
-      if (shdr[i].sh_type == SHT_REL) {
-         ok = do_Elf_Rel_relocations ( oc, ehdrC, shdr, i );
-         if (!ok) return ok;
-      }
-      else
-      if (shdr[i].sh_type == SHT_RELA) {
-         ok = do_Elf_Rela_relocations ( oc, ehdrC, shdr, i );
-         if (!ok) return ok;
-      }
-   }
+#if defined(SHN_XINDEX)
+    Elf_Word* shndxTable = get_shndx_table(ehdr);
+#endif
 
-#if defined(powerpc_HOST_ARCH) || defined(arm_HOST_ARCH)
-   ocFlushInstructionCache( oc );
+    /* resolve section symbols
+     * these are special symbols that point to sections, and have no name.
+     * Usually there should be one symbol for each text and data section.
+     *
+     * We need to resolve (assign addresses) to them, to be able to use them
+     * during relocation.
+     */
+    for(ElfSymbolTable *symTab = oc->info->symbolTables;
+        symTab != NULL; symTab = symTab->next) {
+        for (size_t i = 0; i < symTab->n_symbols; i++) {
+            ElfSymbol *symbol = &symTab->symbols[i];
+            if(STT_SECTION == ELF_ST_TYPE(symbol->elf_sym->st_info)) {
+                /* NOTE: We assume that oc->sections corresponds to the
+                 *       sections in the object file.  This is currently true,
+                 *       and will stay true, unless we start to compress
+                 *       oc->sections by not having an entry for sections we
+                 *       are not interested in.
+                 */
+
+
+                /* See Note [Many ELF Sections] */
+                /* Note that future checks for special SHN_* numbers should
+                 * check the shndx variable, not the section number in secno.
+                 * Sections with the real number in the SHN_LORESERVE..HIRESERVE
+                 * range will have shndx SHN_XINDEX and a secno with one of the
+                 * reserved values.
+                 */
+                Elf_Word secno = symbol->elf_sym->st_shndx;
+#if defined(SHN_XINDEX)
+                if (secno == SHN_XINDEX) {
+                    ASSERT(shndxTable);
+                    secno = shndxTable[i];
+                }
 #endif
+                ASSERT(symbol->elf_sym->st_name == 0);
+                ASSERT(symbol->elf_sym->st_value == 0);
+                symbol->addr = oc->sections[ secno ].start;
+            }
+        }
+    }
 
-   return 1;
+#if defined(NEED_GOT)
+    if(fillGot( oc ))
+        return 0;
+    /* silence warnings */
+    (void) shnum;
+    (void) shdr;
+#endif /* NEED_GOT */
+
+#if defined(aarch64_HOST_ARCH)
+    /* use new relocation design */
+    if(relocateObjectCode( oc ))
+        return 0;
+#else
+    /* Process the relocation sections. */
+    for (Elf_Word i = 0; i < shnum; i++) {
+        if (shdr[i].sh_type == SHT_REL) {
+          bool ok = do_Elf_Rel_relocations ( oc, ehdrC, shdr, i );
+          if (!ok)
+              return ok;
+        }
+        else
+        if (shdr[i].sh_type == SHT_RELA) {
+          bool ok = do_Elf_Rela_relocations ( oc, ehdrC, shdr, i );
+          if (!ok)
+              return ok;
+        }
+    }
+#endif
+
+#if defined(powerpc_HOST_ARCH)
+    ocFlushInstructionCache( oc );
+#endif
+
+    return 1;
 }
 
 int ocRunInit_ELF( ObjectCode *oc )