Adjust section placement and relocation logic for Mach-O
authorArtem Pyanykh <artempyanykh@gmail.com>
Thu, 21 Feb 2019 09:10:38 +0000 (12:10 +0300)
committerMarge Bot <ben+marge-bot@smart-cactus.org>
Wed, 20 Mar 2019 23:52:39 +0000 (19:52 -0400)
1. Place each section on a separate page to ensure required
alignment (wastes lots ot space, needs to be improved).
2. Unwire relocation logic from macho sections (the most fiddly part
is adjusting internal relocations).

Other todos:
0. Add a test for section alignment.
1. Investigate 32bit relocations!
2. Fix memory leak in ZEROPAGE section allocation.
3. Fix creating redundant jump islands for GOT.
4. Investigate more compact section placement.

rts/Linker.c
rts/linker/MachO.c
rts/linker/MachO.h
rts/linker/SymbolExtras.c
testsuite/tests/rts/linker/Makefile [new file with mode: 0644]
testsuite/tests/rts/linker/all.T [new file with mode: 0644]
testsuite/tests/rts/linker/runner [new file with mode: 0755]
testsuite/tests/rts/linker/runner.c [new file with mode: 0644]
testsuite/tests/rts/linker/unsigned_reloc_macho_64.s [new file with mode: 0644]
testsuite/tests/rts/linker/unsigned_reloc_macho_64.stdout [new file with mode: 0644]

index b293b6a..ba78efe 100644 (file)
@@ -1419,6 +1419,8 @@ preloadObjectFile (pathchar *path)
 
 #endif /* RTS_LINKER_USE_MMAP */
 
+   IF_DEBUG(linker, debugBelch("loadObj: preloaded image at %p\n", (void *) image));
+
    oc = mkOc(path, image, fileSize, true, NULL, misalignment);
 
 #if defined(OBJFORMAT_MACHO)
@@ -1440,7 +1442,7 @@ preloadObjectFile (pathchar *path)
 static HsInt loadObj_ (pathchar *path)
 {
    ObjectCode* oc;
-   IF_DEBUG(linker, debugBelch("loadObj %" PATH_FMT "\n", path));
+   IF_DEBUG(linker, debugBelch("loadObj: %" PATH_FMT "\n", path));
 
    /* debugBelch("loadObj %s\n", path ); */
 
@@ -1611,6 +1613,8 @@ int ocTryLoad (ObjectCode* oc) {
 
     // run init/init_array/ctors/mod_init_func
 
+    IF_DEBUG(linker, debugBelch("ocTryLoad: ocRunInit start\n"));
+
     loading_obj = oc; // tells foreignExportStablePtr what to do
 #if defined(OBJFORMAT_ELF)
     r = ocRunInit_ELF ( oc );
@@ -1814,8 +1818,8 @@ void freeProddableBlocks (ObjectCode *oc)
  */
 void
 addSection (Section *s, SectionKind kind, SectionAlloc alloc,
-            void* start, StgWord size, StgWord mapped_offset,
-            void* mapped_start, StgWord mapped_size)
+            void* start, StgWord size,
+            StgWord mapped_offset, void* mapped_start, StgWord mapped_size)
 {
    s->start        = start;     /* actual start of section in memory */
    s->size         = size;      /* actual size of section in memory */
index adcfcc7..7ec3cab 100644 (file)
@@ -2,13 +2,12 @@
 
 #if defined(darwin_HOST_OS) || defined(ios_HOST_OS)
 
-#if defined(ios_HOST_OS)
-#if !RTS_LINKER_USE_MMAP
+#if defined(ios_HOST_OS) && !RTS_LINKER_USE_MMAP
 #error "ios must use mmap and mprotect!"
 #endif
+
 /* for roundUpToPage */
 #include "sm/OSMem.h"
-#endif
 
 #include "RtsUtils.h"
 #include "GetEnv.h"
@@ -680,77 +679,44 @@ relocateSectionAarch64(ObjectCode * oc, Section * section)
     }
     return 1;
 }
-#else /* non aarch64_HOST_ARCH branch -- aarch64 doesn't use relocateAddress */
-
-/*
- * Try to find the final loaded address for some addres.
- * Look through all sections, locating the section that
- * contains the address and compute the absolue address.
- */
-static unsigned long
-relocateAddress(
-                ObjectCode* oc,
-                int nSections,
-                MachOSection* sections,
-                unsigned long address)
-{
-    int i;
-    IF_DEBUG(linker, debugBelch("relocateAddress: start\n"));
-    for (i = 0; i < nSections; i++)
-    {
-        IF_DEBUG(linker, debugBelch("    relocating address in section %d\n", i));
-        if (sections[i].addr <= address
-            && address < sections[i].addr + sections[i].size)
-        {
-            return (unsigned long)oc->image
-            + sections[i].offset + address - sections[i].addr;
-        }
-    }
-    barf("Invalid Mach-O file:"
-         "Address out of bounds while relocating object file");
-    return 0;
-}
-
 #endif /* aarch64_HOST_ARCH */
 
 #if defined(x86_64_HOST_ARCH)
 static int
-relocateSection(
-    ObjectCode* oc,
-    char *image,
-    MachOSymtabCommand *symLC, MachONList *nlist,
-    int nSections, MachOSection* sections, MachOSection *sect)
+relocateSection(ObjectCode* oc, int curSection)
 {
-    MachORelocationInfo *relocs;
-    int i, n;
+    Section * sect = &oc->sections[curSection];
+    MachOSection * msect = sect->info->macho_section; // for convenience access
+    MachORelocationInfo * relocs = sect->info->relocation_info;
+    MachOSymbol * symbols = oc->info->macho_symbols;
 
-    IF_DEBUG(linker, debugBelch("relocateSection: start\n"));
+    IF_DEBUG(linker, debugBelch("relocateSection %d (%s, %s): start\n",
+                                curSection, msect->segname, msect->sectname));
 
-    if(!strcmp(sect->sectname,"__la_symbol_ptr"))
+    if(!strcmp(msect->sectname,"__la_symbol_ptr"))
         return 1;
-    else if(!strcmp(sect->sectname,"__nl_symbol_ptr"))
+    else if(!strcmp(msect->sectname,"__nl_symbol_ptr"))
         return 1;
-    else if(!strcmp(sect->sectname,"__la_sym_ptr2"))
+    else if(!strcmp(msect->sectname,"__la_sym_ptr2"))
         return 1;
-    else if(!strcmp(sect->sectname,"__la_sym_ptr3"))
+    else if(!strcmp(msect->sectname,"__la_sym_ptr3"))
         return 1;
 
-    n = sect->nreloc;
-    IF_DEBUG(linker, debugBelch("relocateSection: number of relocations: %d\n", n));
+    IF_DEBUG(linker, debugBelch("relocateSection: number of relocations: %d\n", msect->nreloc));
 
-    relocs = (MachORelocationInfo*) (image + sect->reloff);
-
-    for(i = 0; i < n; i++)
+    for(uint32_t i = 0; i < msect->nreloc; i++)
     {
         MachORelocationInfo *reloc = &relocs[i];
 
-        char    *thingPtr = image + sect->offset + reloc->r_address;
+        char    *thingPtr = (char *) sect->start + reloc->r_address;
         uint64_t thing;
         /* We shouldn't need to initialise this, but gcc on OS X 64 bit
            complains that it may be used uninitialized if we don't */
         uint64_t value = 0;
         uint64_t baseValue;
         int type = reloc->r_type;
+        int relocLenBytes;
+        int nextInstrAdj = 0;
 
         IF_DEBUG(linker, debugBelch("relocateSection: relocation %d\n", i));
         IF_DEBUG(linker, debugBelch("               : type      = %d\n", reloc->r_type));
@@ -766,27 +732,47 @@ relocateSection(
             case 0:
                 checkProddableBlock(oc,thingPtr,1);
                 thing = *(uint8_t*)thingPtr;
-                baseValue = (uint64_t)thingPtr + 1;
+                relocLenBytes = 1;
                 break;
             case 1:
                 checkProddableBlock(oc,thingPtr,2);
                 thing = *(uint16_t*)thingPtr;
-                baseValue = (uint64_t)thingPtr + 2;
+                relocLenBytes = 2;
                 break;
             case 2:
                 checkProddableBlock(oc,thingPtr,4);
                 thing = *(uint32_t*)thingPtr;
-                baseValue = (uint64_t)thingPtr + 4;
+                relocLenBytes = 4;
                 break;
             case 3:
                 checkProddableBlock(oc,thingPtr,8);
                 thing = *(uint64_t*)thingPtr;
-                baseValue = (uint64_t)thingPtr + 8;
+                relocLenBytes = 8;
                 break;
             default:
                 barf("Unknown size.");
         }
 
+        /*
+         * With SIGNED_N the relocation is not at the
+         * end of instruction and baseValue needs to be
+         * adjusted accordingly.
+         */
+        switch (type) {
+            case X86_64_RELOC_SIGNED_1:
+                nextInstrAdj = 1;
+                break;
+            case X86_64_RELOC_SIGNED_2:
+                nextInstrAdj = 2;
+                break;
+            case X86_64_RELOC_SIGNED_4:
+                nextInstrAdj = 4;
+                break;
+        }
+        baseValue = (uint64_t)thingPtr + relocLenBytes + nextInstrAdj;
+
+
+
         IF_DEBUG(linker,
                  debugBelch("relocateSection: length = %d, thing = %" PRId64 ", baseValue = %p\n",
                             reloc->r_length, thing, (char *)baseValue));
@@ -794,18 +780,19 @@ relocateSection(
         if (type == X86_64_RELOC_GOT
          || type == X86_64_RELOC_GOT_LOAD)
         {
-            MachONList *symbol = &nlist[reloc->r_symbolnum];
-            SymbolName* nm = image + symLC->stroff + symbol->n_un.n_strx;
+            MachOSymbol *symbol = &symbols[reloc->r_symbolnum];
+            SymbolName* nm = symbol->name;
             SymbolAddr* addr = NULL;
 
-            IF_DEBUG(linker, debugBelch("relocateSection: making jump island for %s, extern = %d, X86_64_RELOC_GOT\n", nm, reloc->r_extern));
+            IF_DEBUG(linker, debugBelch("relocateSection: making jump island for %s, extern = %d, X86_64_RELOC_GOT\n",
+                                        nm, reloc->r_extern));
 
-            ASSERT(reloc->r_extern);
+            ASSERT(reloc->r_extern); // fixme: redundant?
             if (reloc->r_extern == 0) {
                     errorBelch("\nrelocateSection: global offset table relocation for symbol with r_extern == 0\n");
             }
 
-            if (symbol->n_type & N_EXT) {
+            if (symbol->nlist->n_type & N_EXT) {
                     // The external bit is set, meaning the symbol is exported,
                     // and therefore can be looked up in this object module's
                     // symtab, or it is undefined, meaning dlsym must be used
@@ -829,38 +816,41 @@ relocateSection(
                     // at the location given by the section index and
                     // symbol address (symbol->n_value)
 
-                    if ((symbol->n_type & N_TYPE) == N_SECT) {
-                            addr = (void *)relocateAddress(oc, nSections, sections, symbol->n_value);
-                            IF_DEBUG(linker, debugBelch("relocateSection: calculated relocation %p of "
-                                                        "non-external X86_64_RELOC_GOT or X86_64_RELOC_GOT_LOAD\n",
-                                                        (void *)symbol->n_value));
-                            IF_DEBUG(linker, debugBelch("               : addr = %p\n", addr));
+                    if ((symbol->nlist->n_type & N_TYPE) == N_SECT) {
+                        ASSERT(symbol->addr != NULL);
+                        addr = symbol->addr;
+                        IF_DEBUG(linker, debugBelch("relocateSection: calculated relocation of "
+                                                    "non-external X86_64_RELOC_GOT or X86_64_RELOC_GOT_LOAD\n"));
+                        IF_DEBUG(linker, debugBelch("               : addr = %p\n", addr));
                     } else {
-                            errorBelch("\nrelocateSection: %s is not exported,"
-                                       " and should be defined in a section, but isn't!\n", nm);
+                        errorBelch("\nrelocateSection: %s is not exported,"
+                                   " and should be defined in a section, but isn't!\n", nm);
                     }
             }
 
+            // creates a jump island for every relocation entry for a symbol
+            // fixme: use got_addr to store the loc. of a jump island for a symbol
             value = (uint64_t) &makeSymbolExtra(oc, reloc->r_symbolnum, (unsigned long)addr)->addr;
 
             type = X86_64_RELOC_SIGNED;
         }
         else if (reloc->r_extern)
         {
-            MachONList *symbol = &nlist[reloc->r_symbolnum];
-            SymbolName* nm = image + symLC->stroff + symbol->n_un.n_strx;
+            MachOSymbol *symbol = &symbols[reloc->r_symbolnum];
+            SymbolName* nm = symbol->name;
             SymbolAddr* addr = NULL;
 
             IF_DEBUG(linker, debugBelch("relocateSection: looking up external symbol %s\n", nm));
-            IF_DEBUG(linker, debugBelch("               : type  = %d\n", symbol->n_type));
-            IF_DEBUG(linker, debugBelch("               : sect  = %d\n", symbol->n_sect));
-            IF_DEBUG(linker, debugBelch("               : desc  = %d\n", symbol->n_desc));
-            IF_DEBUG(linker, debugBelch("               : value = %p\n", (void *)symbol->n_value));
-
-            if ((symbol->n_type & N_TYPE) == N_SECT) {
-                value = relocateAddress(oc, nSections, sections,
-                                        symbol->n_value);
-                IF_DEBUG(linker, debugBelch("relocateSection, defined external symbol %s, relocated address %p\n", nm, (void *)value));
+            IF_DEBUG(linker, debugBelch("               : type  = %d\n", symbol->nlist->n_type));
+            IF_DEBUG(linker, debugBelch("               : sect  = %d\n", symbol->nlist->n_sect));
+            IF_DEBUG(linker, debugBelch("               : desc  = %d\n", symbol->nlist->n_desc));
+            IF_DEBUG(linker, debugBelch("               : value = %p\n", (void *)symbol->nlist->n_value));
+
+            if ((symbol->nlist->n_type & N_TYPE) == N_SECT) {
+                ASSERT(symbol->addr != NULL);
+                value = (uint64_t) symbol->addr;
+                IF_DEBUG(linker, debugBelch("relocateSection, defined external symbol %s, relocated address %p\n",
+                                            nm, (void *)value));
             }
             else {
                 addr = lookupSymbol_(nm);
@@ -877,15 +867,83 @@ relocateSection(
         }
         else
         {
-            // If the relocation is not through the global offset table
-            // or external, then set the value to the baseValue.  This
-            // will leave displacements into the __const section
-            // unchanged (as they ought to be).
+            /* Since the relocation is internal, r_symbolnum contains a section
+             * number relative to which the relocation is.  Depending on whether
+             * the relocation is unsigned or signed, the given displacement is
+             * relative to the image or the section respectively.
+             *
+             * For instance, in a signed case:
+             * thing = <displ. to to section r_symbolnum *in the image*> (1)
+             *       + <offset within r_symbolnum section>
+             * (1) needs to be updated due to different section layout in memory.
+             */
 
-            value = baseValue;
+            CHECKM(reloc->r_symbolnum > 0,
+                   "relocateSection: unsupported r_symbolnum = %" PRIu32 " < 1 for internal relocation",
+                   reloc->r_symbolnum);
+
+            int targetSecNum = reloc->r_symbolnum - 1; // sec numbers start with 1
+            Section * targetSec = &oc->sections[targetSecNum];
+            MachOSection * targetMacho = targetSec->info->macho_section;
+
+            IF_DEBUG(linker,
+                     debugBelch("relocateSection: internal relocation relative to section %d (%s, %s)\n",
+                                targetSecNum, targetMacho->segname, targetMacho->sectname));
+
+            switch (type) {
+            case X86_64_RELOC_UNSIGNED: {
+                CHECKM(thing >= targetMacho->addr,
+                       "relocateSection: unsigned displacement %" PRIx64 "before target section start address %" PRIx64 "\n",
+                       thing, (uint64_t) targetMacho->addr);
+
+                uint64_t thingRelativeOffset = thing - targetMacho->addr;
+                IF_DEBUG(linker, debugBelch("                 "
+                                            "unsigned displacement %" PRIx64 " with section relative offset %" PRIx64 "\n",
+                                            thing, thingRelativeOffset));
+
+                thing = (uint64_t) targetSec->start + thingRelativeOffset;
+                IF_DEBUG(linker, debugBelch("                 "
+                                            "relocated address is %p\n", (void *) thing));
+
+                /* Compared to external relocation we don't need to adjust value
+                 * any further since thing already has absolute address.
+                 */
+                value = 0;
+                break;
+            }
+            case X86_64_RELOC_SIGNED:
+            case X86_64_RELOC_SIGNED_1:
+            case X86_64_RELOC_SIGNED_2:
+            case X86_64_RELOC_SIGNED_4: {
+                uint32_t baseValueOffset = reloc->r_address + relocLenBytes + nextInstrAdj;
+                uint64_t imThingLoc = msect->addr + baseValueOffset + (int64_t) thing;
+
+                CHECKM(imThingLoc >= targetMacho->addr,
+                       "relocateSection: target location %p in image before target section start address %p\n",
+                       (void *) imThingLoc, (void *) targetMacho->addr);
+
+                int64_t thingRelativeOffset = imThingLoc - targetMacho->addr;
+                IF_DEBUG(linker,
+                     debugBelch("                 "
+                                "original displacement %" PRId64 " to %p with section relative offset %" PRIu64 "\n",
+                                thing, (void *) imThingLoc, thingRelativeOffset));
+
+                thing = (int64_t) ((uint64_t) targetSec->start + thingRelativeOffset)
+                                - ((uint64_t) sect->start + baseValueOffset);
+                value = baseValue; // so that it further cancels out with baseValue
+                IF_DEBUG(linker,
+                         debugBelch("                 "
+                                    "relocated displacement %" PRId64 " to %p\n",
+                                    (int64_t) thing, (void *) (baseValue + thing)));
+                break;
+            }
+            default:
+                barf("relocateSection: unexpected internal relocation type %d\n", type);
+                return 0;
+            }
         }
 
-        IF_DEBUG(linker, debugBelch("relocateSection: value = %p\n", (void *)value));
+        IF_DEBUG(linker, debugBelch("relocateSection: value = %p\n", (void *) value));
 
         if (type == X86_64_RELOC_BRANCH)
         {
@@ -920,6 +978,8 @@ relocateSection(
                 barf("unknown relocation");
         }
 
+        IF_DEBUG(linker, debugBelch("relocateSection: thing = %p\n", (void *) thing));
+
         switch(reloc->r_length)
         {
             case 0:
@@ -961,6 +1021,20 @@ relocateSection(
  *   addressability for relative or absolute access.
  */
 
+size_t ocGetRequiredSpace_MachO(ObjectCode* oc)
+{
+    size_t bytesNeeded = 0;
+    size_t sectionSize;
+
+    for(int i = 0; i < oc->n_sections; i++)
+    {
+        sectionSize = oc->info->macho_sections[i].size;
+        bytesNeeded += roundUpToPage(sectionSize);
+    }
+
+    return bytesNeeded;
+}
+
 int
 ocGetNames_MachO(ObjectCode* oc)
 {
@@ -982,6 +1056,19 @@ ocGetNames_MachO(ObjectCode* oc)
 
     IF_DEBUG(linker, debugBelch("ocGetNames_MachO: will load %d sections\n",
                                 oc->n_sections));
+
+#if defined(darwin_HOST_OS)
+    size_t requiredSize = ocGetRequiredSpace_MachO(oc);
+    IF_DEBUG(linker, debugBelch("ocGetNames_MachO: required mmaped space is %zu\n",
+                                requiredSize));
+
+    void* memStart = mmapForLinker(requiredSize, MAP_ANONYMOUS, -1, 0);
+    if (memStart == NULL) {
+        barf("ocGetNames_MachO: failed to mmap");
+    }
+    void* memCur = memStart;
+#endif // darwin_HOST_OS
+
     for(int i=0; i < oc->n_sections; i++)
     {
         MachOSection * section = &oc->info->macho_sections[i];
@@ -1105,9 +1192,10 @@ ocGetNames_MachO(ObjectCode* oc)
                     zeroFillArea = stgCallocBytes(1,section->size,
                                                   "ocGetNames_MachO(common symbols)");
                 }
-                section->offset = zeroFillArea - oc->image;
+                section->offset = zeroFillArea - oc->image; // fixme: fails down the road
+                                                            // when zFA < oc->image since offset is unsigned
 
-                addSection(&secArray[i], kind, SECTION_NOMEM,
+                addSection(&secArray[i], kind, SECTION_NOMEM, // fixme: why NOMEM?
                            (void *)(oc->image + section->offset),
                            section->size,
                            0, 0, 0);
@@ -1124,18 +1212,23 @@ ocGetNames_MachO(ObjectCode* oc)
                 secArray[i].info->macho_section = section;
                 secArray[i].info->relocation_info
                 = (MachORelocationInfo*)(oc->image + section->reloff);
-                FALLTHROUGH;
+                break;
             }
             default: {
-                // just set the pointer to the loaded image.
-                addSection(&secArray[i], kind, SECTION_NOMEM,
-                           (void *)(oc->image + section->offset),
-                           section->size,
-                           0, 0, 0);
+                size_t sectionRoundedSize = roundUpToPage(section->size);
 
-                addProddableBlock(oc,
-                                  (void *) (oc->image + section->offset),
-                                  section->size);
+                IF_DEBUG(linker, debugBelch("ocGetNames_MachO: copying from %p to %p a block of %" PRIu64 " bytes\n",
+                                            (void *) (oc->image + section->offset), memCur, section->size));
+                memcpy(memCur, oc->image + section->offset, section->size);
+                IF_DEBUG(linker, debugBelch("ocGetNames_MachO: copied %" PRIu64 " bytes\n",
+                                            section->size));
+
+                addSection(&secArray[i], kind, SECTION_MMAP,
+                           memCur, section->size,
+                           0, memCur, sectionRoundedSize);
+                addProddableBlock(oc, memCur, section->size);
+
+                memCur = (char*) memCur + sectionRoundedSize;
 
                 secArray[i].info->nstubs = 0;
                 secArray[i].info->stub_offset = NULL;
@@ -1156,17 +1249,25 @@ ocGetNames_MachO(ObjectCode* oc)
     for(size_t i=0; i < oc->info->n_macho_symbols; i++) {
         MachOSymbol * s = &oc->info->macho_symbols[i];
         if( N_SECT == (s->nlist->n_type & N_TYPE) ) {
-            /* section is given */
+            if( NO_SECT == s->nlist->n_sect )
+                barf("Symbol with N_SECT type, but no section.");
+
+            /* section is given, and n_sect is >0 */
             uint8_t n = s->nlist->n_sect - 1;
             if(0 == oc->info->macho_sections[n].size) {
                 continue;
             }
-            if(s->nlist->n_sect == NO_SECT)
-                barf("Symbol with N_SECT type, but no section.");
 
-            /* addr <-   offset in memory where this section resides
-             *         - address rel. to the image where this section is stored
-             *         + symbol offset in the image
+            /* addr <-   address in memory where the relocated section resides | (a)
+             *         - section's address in the image | (b)
+             *         + symbol's address in the image  | (c)
+             * (c) - (b) gives symbol's offset relative to section start
+             * (a) - (b) + (c) gives symbol's address for the relocated section
+             *
+             * (c) and (b) are not _real_ addresses and not equal
+             * to file offsets in the image.
+             * Rather they are (virtual) aligned addresses within
+             * a single segment of MH_OBJECT object file.
              */
             s->addr = (uint8_t*)oc->sections[n].start
                               - oc->info->macho_sections[n].addr
@@ -1209,13 +1310,13 @@ ocGetNames_MachO(ObjectCode* oc)
     if (oc->info->symCmd) {
         for (size_t i = 0; i < oc->info->n_macho_symbols; i++) {
             SymbolName* nm = oc->info->macho_symbols[i].name;
-            if(oc->info->nlist[i].n_type & N_STAB)
+            if (oc->info->nlist[i].n_type & N_STAB)
             {
                 IF_DEBUG(linker, debugBelch("ocGetNames_MachO: Skip STAB: %s\n", nm));
             }
-            else if((oc->info->nlist[i].n_type & N_TYPE) == N_SECT)
+            else if ((oc->info->nlist[i].n_type & N_TYPE) == N_SECT)
             {
-                if(oc->info->nlist[i].n_type & N_EXT)
+                if (oc->info->nlist[i].n_type & N_EXT)
                 {
                     if (   (oc->info->nlist[i].n_desc & N_WEAK_DEF)
                         && lookupSymbol_(nm)) {
@@ -1405,9 +1506,7 @@ ocResolve_MachO(ObjectCode* oc)
         if (!relocateSectionAarch64(oc, &oc->sections[i]))
             return 0;
 #else
-        if (!relocateSection(oc,oc->image,oc->info->symCmd,oc->info->nlist,
-                             oc->info->segCmd->nsects,oc->info->macho_sections,
-                             &oc->info->macho_sections[i]))
+        if (!relocateSection(oc, i))
             return 0;
 #endif
     }
@@ -1433,19 +1532,24 @@ ocRunInit_MachO ( ObjectCode *oc )
     getProgEnvv(&envc, &envv);
 
     for (int i = 0; i < oc->n_sections; i++) {
+        IF_DEBUG(linker, debugBelch("ocRunInit_MachO: checking section %d\n", i));
+
         // ToDo: replace this with a proper check for the S_MOD_INIT_FUNC_POINTERS
         // flag.  We should do this elsewhere in the Mach-O linker code
         // too.  Note that the system linker will *refuse* to honor
         // sections which don't have this flag, so this could cause
         // weird behavior divergence (albeit reproducible).
-        if (0 == strcmp(oc->info->macho_sections[i].sectname,
-                        "__mod_init_func")) {
+        if (0 == strcmp(oc->info->macho_sections[i].sectname, "__mod_init_func")) {
+            IF_DEBUG(linker, debugBelch("ocRunInit_MachO:     running mod init functions\n"));
 
             void *init_startC = oc->sections[i].start;
             init_t *init = (init_t*)init_startC;
             init_t *init_end = (init_t*)((uint8_t*)init_startC
                              + oc->sections[i].info->macho_section->size);
-            for (; init < init_end; init++) {
+
+            for (int pn = 0; init < init_end; init++, pn++) {
+                IF_DEBUG(linker, debugBelch("ocRunInit_MachO:     function pointer %d at %p to %p\n",
+                                            pn, (void *) init, (void *) *init));
                 (*init)(argc, argv, envv);
             }
         }
@@ -1481,6 +1585,7 @@ machoGetMisalignment( FILE * f )
     misalignment = (header.sizeofcmds + sizeof(header))
                     & 0xF;
 
+    IF_DEBUG(linker, debugBelch("mach-o misalignment %d\n", misalignment));
     return misalignment ? (16 - misalignment) : 0;
 }
 
index 5e00e2a..20cb2d0 100644 (file)
@@ -6,13 +6,14 @@
 
 #include "MachOTypes.h"
 
-void ocInit_MachO          ( ObjectCode* oc );
-void ocDeinit_MachO        ( ObjectCode* oc );
-int ocVerifyImage_MachO    ( ObjectCode* oc );
-int ocGetNames_MachO       ( ObjectCode* oc );
-int ocResolve_MachO        ( ObjectCode* oc );
-int ocRunInit_MachO        ( ObjectCode* oc );
-int machoGetMisalignment( FILE * );
-int ocAllocateExtras_MachO ( ObjectCode* oc );
+void   ocInit_MachO                 ( ObjectCode* oc );
+void   ocDeinit_MachO               ( ObjectCode* oc );
+int    ocVerifyImage_MachO          ( ObjectCode* oc );
+size_t ocGetRequiredSpace_MachO     ( ObjectCode* oc);
+int    ocGetNames_MachO             ( ObjectCode* oc );
+int    ocResolve_MachO              ( ObjectCode* oc );
+int    ocRunInit_MachO              ( ObjectCode* oc );
+int    machoGetMisalignment         ( FILE * );
+int    ocAllocateExtras_MachO       ( ObjectCode* oc );
 
 #include "EndPrivate.h"
index a9e4c37..4149522 100644 (file)
@@ -149,6 +149,7 @@ SymbolExtra* makeSymbolExtra( ObjectCode const* oc,
 #endif /* powerpc_HOST_ARCH */
 #if defined(x86_64_HOST_ARCH)
     // jmp *-14(%rip)
+    // 0xFF 25 is opcode + ModRM of near absolute indirect jump
     static uint8_t jmp[] = { 0xFF, 0x25, 0xF2, 0xFF, 0xFF, 0xFF };
     extra->addr = target;
     memcpy(extra->jumpIsland, jmp, 6);
diff --git a/testsuite/tests/rts/linker/Makefile b/testsuite/tests/rts/linker/Makefile
new file mode 100644 (file)
index 0000000..c5d80fa
--- /dev/null
@@ -0,0 +1,8 @@
+TOP=../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
+
+unsigned_reloc_macho_64:
+       as -o unsigned_reloc_macho_64.o unsigned_reloc_macho_64.s
+       '$(TEST_HC)' $(TEST_HC_OPTS_NO_RTSOPTS) -v0 --make -no-rtsopts-suggestions -no-hs-main -o runner -debug runner.c
+       ./runner unsigned_reloc_macho_64.o getAnswer
diff --git a/testsuite/tests/rts/linker/all.T b/testsuite/tests/rts/linker/all.T
new file mode 100644 (file)
index 0000000..d1b3c22
--- /dev/null
@@ -0,0 +1,9 @@
+# -*-: mode: python -*-
+
+test('unsigned_reloc_macho_64',
+     [
+         extra_files(['runner.c', 'unsigned_reloc_macho_64.s']),
+         unless(opsys('darwin'), skip),
+         unless(arch('x86_64'), skip)
+     ],
+     run_command, ['$MAKE -s --no-print-directory unsigned_reloc_macho_64'])
diff --git a/testsuite/tests/rts/linker/runner b/testsuite/tests/rts/linker/runner
new file mode 100755 (executable)
index 0000000..7771d4a
Binary files /dev/null and b/testsuite/tests/rts/linker/runner differ
diff --git a/testsuite/tests/rts/linker/runner.c b/testsuite/tests/rts/linker/runner.c
new file mode 100644 (file)
index 0000000..7c34ac2
--- /dev/null
@@ -0,0 +1,65 @@
+#include <Rts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#define MAX_SYMNAME 256
+
+typedef long (*fun_t)(void);
+
+int main(int argc, char *argv[])
+{
+    fun_t f;
+    int ok;
+    char * objpath;
+
+    hs_init(&argc, &argv);
+
+    initLinker();
+
+    if (argc < 3) {
+            errorBelch("usage: runner <objpath> <symname>");
+            exit(1);
+    }
+    objpath = argv[1];
+
+#ifdef darwin_HOST_OS
+    char symname[MAX_SYMNAME + 1];
+    symname[0] = '_';
+    strncpy(&symname[1], argv[2], MAX_SYMNAME);
+#else
+    char * symname = argv[2];
+#endif
+
+    printf("Linking: path = %s, symname = %s\n", objpath, symname);
+
+    ok = loadObj(objpath);
+    if (!ok) {
+            errorBelch("loadObj(%s) failed", objpath);
+            exit(1);
+    }
+
+    ok = resolveObjs();
+    if (!ok) {
+            errorBelch("resolveObjs failed");
+            exit(1);
+    }
+
+    f = lookupSymbol(symname);
+    if (!f) {
+            errorBelch("lookupSymbol failed");
+            exit(1);
+    }
+
+    printf("%ld\n", f());
+
+    ok = unloadObj(objpath);
+    if (!ok) {
+            errorBelch("unloadObj(%s) failed", objpath);
+            exit(1);
+    }
+
+    return 0;
+}
+
diff --git a/testsuite/tests/rts/linker/unsigned_reloc_macho_64.s b/testsuite/tests/rts/linker/unsigned_reloc_macho_64.s
new file mode 100644 (file)
index 0000000..733c986
--- /dev/null
@@ -0,0 +1,11 @@
+        .text
+        .globl _getAnswer
+
+_getAnswer:
+        mov     L2(%rip), %rdx
+        movq    (%rdx), %rax
+        ret
+
+        .data
+L1:     .quad 42
+L2:     .quad L1
diff --git a/testsuite/tests/rts/linker/unsigned_reloc_macho_64.stdout b/testsuite/tests/rts/linker/unsigned_reloc_macho_64.stdout
new file mode 100644 (file)
index 0000000..8251451
--- /dev/null
@@ -0,0 +1,2 @@
+Linking: path = unsigned_reloc_macho_64.o, symname = _getAnswer
+42
\ No newline at end of file