Fix for #4318 (Linker failure on OS X 10.6)
authorgwright@antiope.com <unknown>
Tue, 26 Oct 2010 13:51:53 +0000 (13:51 +0000)
committergwright@antiope.com <unknown>
Tue, 26 Oct 2010 13:51:53 +0000 (13:51 +0000)
This patch fixes two bugs in the Mach-O linker and adds debugging statements
to the same. The bugs:

1. The test for symbol->n_value == 0 is removed and replaced by a test of the
flag field.  Checking the n_value field was just wrong; the value of a
symbol should only be examined when allocating space for a common block,
in which case the n_value field gives the size of the block.  This bug
led to an infrequently occuring linker crash.

I believe the behavior of the linker now agrees with the intent of the
sketchy Apple documentation.

2. Jump islands were being filled with garbage instead of the the location
of the referenced symbol. This caused relocations of type X86_64_RELOC_GOT and
X86_64_RELOC_GOT_LOAD to eventually lead to crashes.  The fix is simply to
look up the symbol.

Enough debug statements have been added to follow the operation of the Mach-O
linker while it loads modules.  They are not yet as informative and well
organized as for ELF.  Improving the debug statements will require some
reorganization of the code -- the Mach-O linker seems basically sound, but
is crying out for some refactoring and commenting.

rts/Linker.c

index 3741386..4b984da 100644 (file)
@@ -1135,11 +1135,16 @@ initLinker( void )
     int compileResult;
 #endif
 
+    IF_DEBUG(linker, debugBelch("initLinker: start\n"));
+
     /* Make initLinker idempotent, so we can call it
        before evey relevant operation; that means we
        don't need to initialise the linker separately */
-    if (linker_init_done == 1) { return; } else {
-      linker_init_done = 1;
+    if (linker_init_done == 1) { 
+       IF_DEBUG(linker, debugBelch("initLinker: idempotent return\n"));
+       return;
+    } else {
+        linker_init_done = 1;
     }
 
 #if defined(THREADED_RTS) && (defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO))
@@ -1152,6 +1157,7 @@ initLinker( void )
     for (sym = rtsSyms; sym->lbl != NULL; sym++) {
        ghciInsertStrHashTable("(GHCi built-in symbols)",
                                symhash, sym->lbl, sym->addr);
+       IF_DEBUG(linker, debugBelch("initLinker: inserting rts symbol %s, %p\n", sym->lbl, sym->addr));
     }
 #   if defined(OBJFORMAT_MACHO) && defined(powerpc_HOST_ARCH)
     machoInitSymbolsWithoutUnderscore();
@@ -1190,6 +1196,9 @@ initLinker( void )
     addDLL("msvcrt");
     addDLL("kernel32");
 #endif
+
+    IF_DEBUG(linker, debugBelch("initLinker: done\n"));
+    return;
 }
 
 void
@@ -1444,11 +1453,13 @@ void *
 lookupSymbol( char *lbl )
 {
     void *val;
+    IF_DEBUG(linker, debugBelch("lookupSymbol: looking up %s\n", lbl));
     initLinker() ;
     ASSERT(symhash != NULL);
     val = lookupStrHashTable(symhash, lbl);
 
     if (val == NULL) {
+       IF_DEBUG(linker, debugBelch("lookupSymbol: symbol not found\n"));
 #       if defined(OBJFORMAT_ELF)
        return dlsym(dl_prog_handle, lbl);
 #       elif defined(OBJFORMAT_MACHO)
@@ -1461,6 +1472,7 @@ lookupSymbol( char *lbl )
                  symbol name. For now, we simply strip it off here (and ONLY
                  here).
         */
+        IF_DEBUG(linker, debugBelch("lookupSymbol: looking up %s with dlsym\n", lbl));
         ASSERT(lbl[0] == '_');
         return dlsym(dl_prog_handle, lbl+1);
 #       else
@@ -1489,6 +1501,7 @@ lookupSymbol( char *lbl )
         return NULL;
 #       endif
     } else {
+       IF_DEBUG(linker, debugBelch("lookupSymbol: value of %s is %p\n", lbl, val));
        return val;
     }
 }
@@ -1980,6 +1993,7 @@ loadOc( ObjectCode* oc ) {
 
    /* loaded, but not resolved yet */
    oc->status = OBJECT_LOADED;
+   IF_DEBUG(linker, debugBelch("loadObj done.\n"));
 
    return 1;
 }
@@ -1995,6 +2009,7 @@ resolveObjs( void )
     ObjectCode *oc;
     int r;
 
+    IF_DEBUG(linker, debugBelch("resolveObjs: start\n"));
     initLinker();
 
     for (oc = objects; oc; oc = oc->next) {
@@ -2012,6 +2027,7 @@ resolveObjs( void )
            oc->status = OBJECT_RESOLVED;
        }
     }
+    IF_DEBUG(linker, debugBelch("resolveObjs: done\n"));
     return 1;
 }
 
@@ -2087,7 +2103,7 @@ static void addProddableBlock ( ObjectCode* oc, void* start, int size )
 {
    ProddableBlock* pb
       = stgMallocBytes(sizeof(ProddableBlock), "addProddableBlock");
-   /* debugBelch("aPB %p %p %d\n", oc, start, size); */
+   IF_DEBUG(linker, debugBelch("addProddableBlock %p %p %d\n", oc, start, size));
    ASSERT(size > 0);
    pb->start      = start;
    pb->size       = size;
@@ -4357,6 +4373,8 @@ static int resolveImports(
     unsigned i;
     size_t itemSize = 4;
 
+    IF_DEBUG(linker, debugBelch("resolveImports: start\n"));
+
 #if i386_HOST_ARCH
     int isJumpTable = 0;
     if(!strcmp(sect->sectname,"__jump_table"))
@@ -4374,12 +4392,16 @@ static int resolveImports(
        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))
+       IF_DEBUG(linker, debugBelch("resolveImports: resolving %s\n", nm));
+       if ((symbol->n_type & N_TYPE) == N_UNDF
+           && (symbol->n_type & N_EXT) && (symbol->n_value != 0)) {
            addr = (void*) (symbol->n_value);
-       else
+           IF_DEBUG(linker, debugBelch("resolveImports: undefined external %s has value %p\n", nm, addr));
+       } else {
            addr = lookupSymbol(nm);
-       if(!addr)
+           IF_DEBUG(linker, debugBelch("resolveImports: looking up %s, %p\n", nm, addr));
+       }
+       if (!addr)
        {
            errorBelch("\n%s: unknown symbol `%s'", oc->fileName, nm);
            return 0;
@@ -4402,6 +4424,7 @@ static int resolveImports(
         }
     }
 
+    IF_DEBUG(linker, debugBelch("resolveImports: done\n"));
     return 1;
 }
 
@@ -4412,9 +4435,11 @@ static unsigned long relocateAddress(
     unsigned long address)
 {
     int i;
-    for(i = 0; i < nSections; i++)
+    IF_DEBUG(linker, debugBelch("relocateAddress: start\n"));
+    for (i = 0; i < nSections; i++)
     {
-        if(sections[i].addr <= address
+           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
@@ -4433,7 +4458,9 @@ static int relocateSection(
     int nSections, struct section* sections, struct section *sect)
 {
     struct relocation_info *relocs;
-    int i,n;
+    int i, n;
+
+    IF_DEBUG(linker, debugBelch("relocateSection: start\n"));
 
     if(!strcmp(sect->sectname,"__la_symbol_ptr"))
        return 1;
@@ -4445,6 +4472,8 @@ static int relocateSection(
        return 1;
 
     n = sect->nreloc;
+    IF_DEBUG(linker, debugBelch("relocateSection: number of relocations: %d\n", n));
+
     relocs = (struct relocation_info*) (image + sect->reloff);
 
     for(i=0;i<n;i++)
@@ -4482,12 +4511,20 @@ static int relocateSection(
             default:
                 barf("Unknown size.");
         }
-        
-        if(type == X86_64_RELOC_GOT
+
+       IF_DEBUG(linker,
+                debugBelch("relocateSection: length = %d, thing = %d, baseValue = %p\n",
+                           reloc->r_length, thing, baseValue));
+
+        if (type == X86_64_RELOC_GOT
            || type == X86_64_RELOC_GOT_LOAD)
         {
+            struct nlist *symbol = &nlist[reloc->r_symbolnum];
+            char *nm = image + symLC->stroff + symbol->n_un.n_strx;
+
+           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);
-            value = (uint64_t) &makeSymbolExtra(oc, reloc->r_symbolnum, value)->addr;
+            value = (uint64_t) &makeSymbolExtra(oc, reloc->r_symbolnum, (unsigned long)lookupSymbol(nm))->addr;
             
             type = X86_64_RELOC_SIGNED;
         }
@@ -4495,11 +4532,21 @@ static int relocateSection(
         {
             struct nlist *symbol = &nlist[reloc->r_symbolnum];
             char *nm = image + symLC->stroff + symbol->n_un.n_strx;
-            if(symbol->n_value == 0)
-                value = (uint64_t) lookupSymbol(nm);
-            else
+
+           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 = %d\n", 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, value));
+           }
+            else {
+                value = (uint64_t) lookupSymbol(nm);
+               IF_DEBUG(linker, debugBelch("relocateSection: external symbol %s, address %p\n", nm, value));
+           }
         }
         else
         {
@@ -4507,8 +4554,10 @@ static int relocateSection(
                   - sections[reloc->r_symbolnum-1].addr
                  + (uint64_t) image;
         }
-        
-        if(type == X86_64_RELOC_BRANCH)
+      
+       IF_DEBUG(linker, debugBelch("relocateSection: value = %p\n", value));
+
+        if (type == X86_64_RELOC_BRANCH)
         {
             if((int32_t)(value - baseValue) != (int64_t)(value - baseValue))
             {
@@ -4889,6 +4938,7 @@ static int relocateSection(
        }
 #endif
     }
+    IF_DEBUG(linker, debugBelch("relocateSection: done\n"));
     return 1;
 }
 
@@ -4906,7 +4956,7 @@ static int ocGetNames_MachO(ObjectCode* oc)
     char    *commonStorage = NULL;
     unsigned long commonCounter;
 
-    IF_DEBUG(linker,debugBelch("ocGetNames_MachO\n"));
+    IF_DEBUG(linker,debugBelch("ocGetNames_MachO: start\n"));
 
     for(i=0;i<header->ncmds;i++)
     {
@@ -4926,7 +4976,8 @@ static int ocGetNames_MachO(ObjectCode* oc)
 
     for(i=0;i<segLC->nsects;i++)
     {
-        if(sections[i].size == 0)
+       IF_DEBUG(linker, debugBelch("ocGetNames_MachO: segment %d\n"));
+        if (sections[i].size == 0)
             continue;
 
         if((sections[i].flags & SECTION_TYPE) == S_ZEROFILL)
@@ -4979,6 +5030,7 @@ static int ocGetNames_MachO(ObjectCode* oc)
             }
         }
     }
+    IF_DEBUG(linker, debugBelch("ocGetNames_MachO: %d external symbols\n", oc->n_symbols));
     oc->symbols = stgMallocBytes(oc->n_symbols * sizeof(char*),
                                   "ocGetNames_MachO(oc->symbols)");
 
@@ -4993,11 +5045,13 @@ static int ocGetNames_MachO(ObjectCode* oc)
                 if(nlist[i].n_type & N_EXT)
                 {
                     char *nm = image + symLC->stroff + nlist[i].n_un.n_strx;
-                    if((nlist[i].n_desc & N_WEAK_DEF) && lookupSymbol(nm))
-                        ; // weak definition, and we already have a definition
+                    if ((nlist[i].n_desc & N_WEAK_DEF) && lookupSymbol(nm)) {
+                        // weak definition, and we already have a definition
+                       IF_DEBUG(linker, debugBelch("    weak: %s\n", nm));
+                   }
                     else
                     {
-                            IF_DEBUG(linker,debugBelch("Adding symbol 1 %s\n", nm));
+                           IF_DEBUG(linker, debugBelch("ocGetNames_MachO: inserting %s\n", nm));
                             ghciInsertStrHashTable(oc->fileName, symhash, nm,
                                                     image
                                                     + sections[nlist[i].n_sect-1].offset
@@ -5024,7 +5078,7 @@ static int ocGetNames_MachO(ObjectCode* oc)
 
                nlist[i].n_value = commonCounter;
 
-            IF_DEBUG(linker,debugBelch("Adding symbol 2 %s\n", nm));
+               IF_DEBUG(linker, debugBelch("ocGetNames_MachO: inserting common symbol: %s\n", nm));
                ghciInsertStrHashTable(oc->fileName, symhash, nm,
                                       (void*)commonCounter);
                oc->symbols[curSymbol++] = nm;
@@ -5048,7 +5102,8 @@ static int ocResolve_MachO(ObjectCode* oc)
     struct dysymtab_command *dsymLC = NULL;
     struct nlist *nlist;
 
-    for(i=0;i<header->ncmds;i++)
+    IF_DEBUG(linker, debugBelch("ocResolve_MachO: start\n"));
+    for (i = 0; i < header->ncmds; i++)
     {
        if(lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64)
            segLC = (struct segment_command*) lc;
@@ -5068,7 +5123,8 @@ static int ocResolve_MachO(ObjectCode* oc)
         unsigned long *indirectSyms
             = (unsigned long*) (image + dsymLC->indirectsymoff);
 
-        for(i=0;i<segLC->nsects;i++)
+       IF_DEBUG(linker, debugBelch("ocResolve_MachO: resolving dsymLC\n"));
+        for (i = 0; i < segLC->nsects; i++)
         {
             if(    !strcmp(sections[i].sectname,"__la_symbol_ptr")
                 || !strcmp(sections[i].sectname,"__la_sym_ptr2")
@@ -5088,12 +5144,18 @@ static int ocResolve_MachO(ObjectCode* oc)
                 if(!resolveImports(oc,image,symLC,&sections[i],indirectSyms,nlist))
                     return 0;
             }
+           else
+           {
+               IF_DEBUG(linker, debugBelch("ocResolve_MachO: unknown section\n"));
+           }
         }
     }
     
     for(i=0;i<segLC->nsects;i++)
     {
-       if(!relocateSection(oc,image,symLC,nlist,segLC->nsects,sections,&sections[i]))
+           IF_DEBUG(linker, debugBelch("ocResolve_MachO: relocating section %d\n", i));
+
+       if (!relocateSection(oc,image,symLC,nlist,segLC->nsects,sections,&sections[i]))
            return 0;
     }