diff -rN -u old-ghc//rts/Linker.c new-ghc//rts/Linker.c
--- old-ghc//rts/Linker.c	2011-04-03 22:08:35.000000000 +0100
+++ new-ghc//rts/Linker.c	2011-04-03 22:08:35.000000000 +0100
@@ -4569,22 +4642,41 @@
     return 1;
 }
 
-static unsigned long relocateAddress(
+
+#ifndef x86_64_HOST_ARCH
+
+static unsigned long
+relocateAddress(
     ObjectCode* oc,
     int nSections,
     struct section* sections,
     unsigned long address)
 {
     int i;
+    unsigned long relocated;
+
     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_DEBUG(linker, debugBelch("relocateAddress: \ttrying to relocate address %#lx in section %d\n", address, i));
+
+        // check if the address to be relocated is in section 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;
+            relocated = (unsigned long)oc->image + sections[i].offset + address - sections[i].addr;
+
+            IF_DEBUG(linker, debugBelch("relocateAddress: \tin section %d, relocated address is %#lx\n", i, relocated));
+            IF_DEBUG(linker, debugBelch("relocateAddress: \toc->image = %lu (%#lx)\n", (unsigned long)oc->image,
+                                        (unsigned long)oc->image));
+            IF_DEBUG(linker, debugBelch("relocateAddress: \tsections[%d].offset = %u (%#x)\n", i, sections[i].offset,
+                                        sections[i].offset));
+            IF_DEBUG(linker, debugBelch("relocateAddress: \taddress = %ld (%#lx)\n", address, address));
+
+            IF_DEBUG(linker, debugBelch("relocateAddress: \tsections[%d].addr = %llu (%#llx)\n", i, sections[i].addr,
+                                        sections[i].addr));
+            IF_DEBUG(linker, debugBelch("relocateAddress: done\n"));
+            return relocated;
         }
     }
     barf("Invalid Mach-O file:"
@@ -4592,175 +4684,534 @@
     return 0;
 }
 
-static int relocateSection(
+#endif
+
+
+static void
+checkProddableBlock_MachO (ObjectCode *oc, void *addr, int ll)
+{
+    ProddableBlock* pb;
+    int len;
+
+    // For x86_64, ll is the base 2 logarithm of the relocation
+    // length.
+    //
+    // We need to check the actual relocation length instead of
+    // the maximum length, otherwise we might incorrectly conclude
+    // that the last relocation in a ProddableBlock runs off the end.
+
+    if (ll >= 0 && ll <= 3) {
+        len = 2^ll;
+    } else {
+        barf("checkProddableBlock_MachO: invalid relocation length, %d\n", ll);
+    }
+
+    for (pb = oc->proddables; pb != NULL; pb = pb->next) {
+        char *s = (char *)(pb->start);
+        char *e = s + pb->size - 1;
+        char *a = (char *)addr;
+
+        if (a >= s && (a + (len - 1)) <= e) {
+            return;
+        }
+    }
+
+    barf("checkProddableBlock_MachO: invalid fixup in runtime linker at %p, length %d", addr, len);
+}
+
+
+// Look up a symbol, either defined in the symbol table as an exportable
+// reference in the current image.
+
+static uint64_t
+findExternalSymbol(
+    struct nlist *symbol,
+    char *symbolName,
+    ObjectCode *oc,
+    struct section *sections)
+{
+    uint64_t addr;
+
+    // If this is an exported external symbol, i.e., one that is
+    // defined in this object file, then the N_SECT bit is set. 
+    // We just look up its address in the nlist. If not, look
+    // up the address in the symbol table.
+
+    IF_DEBUG(linker, debugBelch("findSymbolAddress: looking for symbol %s\n", symbolName));
+    if ((symbol->n_type & N_TYPE) == N_SECT) {
+        IF_DEBUG(linker, debugBelch("findSymbolAddress: \t%s is defined in this object file\n", symbolName));
+
+        int sect = symbol->n_sect - 1;
+        addr = (uint64_t) (oc->image + sections[sect].offset - sections[sect].addr + symbol->n_value);
+
+        IF_DEBUG(linker, debugBelch("findSymbolAddress: \toc->image = %p\n", oc->image));
+        IF_DEBUG(linker, debugBelch("findSymbolAddress: \tsection[%d].offset = %#0x\n", sect, sections[sect].offset));
+        IF_DEBUG(linker, debugBelch("findSymbolAddress: \tsection[%d].addr = %#0llx\n", sect, (unsigned long long)sections[sect].addr));
+        IF_DEBUG(linker, debugBelch("findSymbolAddress: \tsymbol->n_value = %#0llx\n", (unsigned long long)symbol->n_value));
+    } else {
+        IF_DEBUG(linker, debugBelch("findSymbolAddress: \tsearching for %s in symbol table\n", symbolName));
+
+        addr = (uint64_t) lookupSymbol(symbolName);
+        if (!addr) {
+            barf("findExternalSymbol: failed to find %s in the symbol table", symbolName);
+        } else {
+            IF_DEBUG(linker, debugBelch("findExternalSymbol: symbol %s is at %p\n", symbolName, (void *) addr));
+        }
+    }
+
+    return addr;
+}
+
+
+// Look up an internal reference.  In this case, the fixup value initially
+// read from the relocation entry contains the offset into the target section.
+// We just need to find the installed memory address of the start of the
+// target section.
+
+static uint64_t
+findInternalReference(
+    struct relocation_info *reloc,
+    ObjectCode *oc,
+    struct section *sections)
+{
+    uint64_t addr;
+
+    // If reloc->r_symbolnum equals R_ABS (which is numerically zero),
+    // the symbol is not relocatable and zero is returned, indicating that the
+    // fixup should be written back unchanged.  Othewise, look up the
+    // file offset of section number (reloc->r_symbolnum - 1) and add it
+    // to the base address of the memory image.
+
+    if (reloc->r_symbolnum == R_ABS) {
+        addr = 0;
+        IF_DEBUG(linker, debugBelch("relocateSection: internal symbol is absolute (no relocation)\n"));
+    } else {
+        int sect = reloc->r_symbolnum - 1;
+        addr = (uint64_t) (oc->image + sections[sect].offset);
+        IF_DEBUG(linker, debugBelch("relocateSection: internal symbol is in section %d at %0llx\n", sect, addr));
+    }
+
+    return addr;
+}
+
+
+// Limits for pc relative offsets:
+
+#define MAX_8_BIT_FORWARD_DISPL 127
+#define MIN_8_BIT_BACKWARD_DISPL -128
+#define MAX_32_BIT_FORWARD_DISPL 2147483647
+#define MIN_32_BIT_BACKWARD_DISPL -2147483648
+
+
+// Perform all of the relocations in a section.  This is the core
+// of the relocation operation.
+
+static int
+relocateSection(
     ObjectCode* oc,
     char *image,
-    struct symtab_command *symLC, struct nlist *nlist,
-    int nSections, struct section* sections, struct section *sect)
+    struct symtab_command *symLC,
+    struct nlist *nlist,
+    int nSections,
+    struct section *sections,
+    struct section *sect)
 {
     struct relocation_info *relocs;
     int i, n;
 
     IF_DEBUG(linker, debugBelch("relocateSection: start\n"));
 
-    if(!strcmp(sect->sectname,"__la_symbol_ptr"))
+    // Certain sections are only used for external references (references
+    // to symbols defined in other files).  Symbols in these sections
+    // have to be resolved to the installed address, but don't contain
+    // relocations, so they are skipped.
+
+    if (strcmp(sect->sectname,"__la_symbol_ptr") == 0) {
+        IF_DEBUG(linker, debugBelch("relocateSection: __la_symbol_ptr section, skipping\n")); 
         return 1;
-    else if(!strcmp(sect->sectname,"__nl_symbol_ptr"))
+    }
+    else if (strcmp(sect->sectname,"__nl_symbol_ptr") == 0) {
+        IF_DEBUG(linker, debugBelch("relocateSection: __nl_symbol_ptr section, skipping\n")); 
         return 1;
-    else if(!strcmp(sect->sectname,"__la_sym_ptr2"))
+    }
+    else if (strcmp(sect->sectname,"__la_sym_ptr2") == 0) {
+        IF_DEBUG(linker, debugBelch("relocateSection: __la_sym_ptr2 section, skipping\n")); 
         return 1;
-    else if(!strcmp(sect->sectname,"__la_sym_ptr3"))
+    }
+    else if (strcmp(sect->sectname,"__la_sym_ptr3") == 0) {
+        IF_DEBUG(linker, debugBelch("relocateSection: __la_sym_ptr3 section, skipping\n")); 
         return 1;
+    }
+
+    // If we reach this point, we have a section that may need relocations.
 
     n = sect->nreloc;
-    IF_DEBUG(linker, debugBelch("relocateSection: number of relocations: %d\n", n));
+    relocs = (struct relocation_info *) (image + sect->reloff);
 
-    relocs = (struct relocation_info*) (image + sect->reloff);
+    IF_DEBUG(linker, debugBelch("relocateSection: section %s has %d relocations\n", sect->sectname, n));
+
+    for (i = 0; i < n; i++) {
 
-    for(i=0;i<n;i++)
-    {
 #ifdef x86_64_HOST_ARCH
+
         struct relocation_info *reloc = &relocs[i];
 
-        char    *thingPtr = image + sect->offset + 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;
+        // The object that may require relocation (commonly, a "fixup") is
+        // located at offset reloc->r_address from the start of the section.
+        // The start of the section is at memory address image + sect->offset.
+        // The location of the fixup is therefore given by the expression
+        // for fixupPtr below.
+        //
+        // The variable fixup will contain the final fixed-up value written
+        // to the location given by fixupPtr.
+
+        void    *fixupPtr = image + sect->offset + reloc->r_address;
+        uint64_t fixup;
+
+        uint64_t addr;
+        uint64_t pc;
+
+        struct nlist *symbol = NULL;
+        char *symbolName     = NULL;
+        
         int type = reloc->r_type;
 
-        checkProddableBlock(oc,thingPtr);
+        if (nSections > 255) {
+                barf("relocateSection: nSections > 255");
+        }
+
+        // Check that we can write to the location that fixupPtr points to.
+        // If we can't, checkProddableBlock_MachO will barf.
+        
+        checkProddableBlock_MachO(oc, fixupPtr, reloc->r_length);
+
+        // Read the object to be relocated (the "fixup") from the fixupPtr.
+        // Then calculate the notional program counter ("pc") used for
+        // relative relocations. The notional pc is address of the next
+        // instruction after the one containing the relocation.
+
         switch(reloc->r_length)
         {
             case 0:
-                thing = *(uint8_t*)thingPtr;
-                baseValue = (uint64_t)thingPtr + 1;
+                fixup = *(uint8_t *)fixupPtr;
+                pc    = (uint64_t)fixupPtr + 1;
                 break;
             case 1:
-                thing = *(uint16_t*)thingPtr;
-                baseValue = (uint64_t)thingPtr + 2;
+                fixup = *(uint16_t *)fixupPtr;
+                pc    = (uint64_t)fixupPtr + 2;
                 break;
             case 2:
-                thing = *(uint32_t*)thingPtr;
-                baseValue = (uint64_t)thingPtr + 4;
+                fixup = *(uint32_t *)fixupPtr;
+                pc    = (uint64_t)fixupPtr + 4;
                 break;
             case 3:
-                thing = *(uint64_t*)thingPtr;
-                baseValue = (uint64_t)thingPtr + 8;
+                fixup = *(uint64_t *)fixupPtr;
+                pc    = (uint64_t)fixupPtr + 8;
                 break;
             default:
-                barf("Unknown size.");
+                barf("relocateSection: Unknown relocation length %d", reloc->r_length);
         }
 
-        IF_DEBUG(linker,
-                 debugBelch("relocateSection: length = %d, thing = %" PRId64 ", baseValue = %p\n",
-                            reloc->r_length, thing, (char *)baseValue));
+        IF_DEBUG(linker, debugBelch("relocateSection: relocation %d has\n", i));
+        IF_DEBUG(linker, debugBelch("relocateSection: \tr_address   = %d (%#x)\n",
+                                    reloc->r_address,
+                                    reloc->r_address));
+        IF_DEBUG(linker, debugBelch("relocateSection: \tr_symbolnum = %u (%#x)\n",
+                                    reloc->r_symbolnum,
+                                    reloc->r_symbolnum));
+        IF_DEBUG(linker, debugBelch("relocateSection: \tr_pcrel     = %d\n",
+                                    reloc->r_pcrel));
+        IF_DEBUG(linker, debugBelch("relocateSection: \tr_length    = %d\n",
+                                    reloc->r_length));
+        IF_DEBUG(linker, debugBelch("relocateSection: \tr_extern    = %d\n",
+                                    reloc->r_extern));
+        IF_DEBUG(linker, debugBelch("relocateSection: \tr_type      = %d (%#x)\n",
+                                    reloc->r_type, reloc->r_type));
+        IF_DEBUG(linker, debugBelch("relocateSection: \tfixupPtr = %p, fixup = 0x%lx, pc = %p\n",
+                                    fixupPtr,
+                                    (unsigned long)fixup,
+                                    (void *)pc));
+
+        // For external relocations, get the symbol table entry and the
+        // symbol name.
+
+        if (reloc->r_extern) {
+            symbol     = &nlist[reloc->r_symbolnum];
+            symbolName = image + symLC->stroff + symbol->n_un.n_strx;
+        }
+
+        // Do the relocation.  This is not the shortest possible way
+        // to do this, but by considering each type of relocation and
+        // whether the relocation is external and the length of the relocated
+        // object, all invalid cases can be caught.
 
-        if (type == X86_64_RELOC_GOT
-           || type == X86_64_RELOC_GOT_LOAD)
+        switch (type)
         {
-            struct nlist *symbol = &nlist[reloc->r_symbolnum];
-            char *nm = image + symLC->stroff + symbol->n_un.n_strx;
+        case X86_64_RELOC_GOT:
+        case X86_64_RELOC_GOT_LOAD:
+
+            ASSERT(reloc->r_pcrel);          // GOT and GOT_LOAD are always pc-relative,
+            ASSERT(reloc->r_extern);         // always external,
+            ASSERT(reloc->r_length == 2);    // and always 32 bit.
+
+            // Create a Global Offset Table (GOT) entry and install the
+            // address of the symbol in the table.  The address of the
+            // GOT entry is target of the final pc relative relocation.
+
+            addr = (uint64_t) &makeSymbolExtra(oc, reloc->r_symbolnum, (unsigned long)lookupSymbol(symbolName))->addr;
+
+            IF_DEBUG(linker, debugBelch("relocateSection: type is X64_64_RELOC_GOT or X86_64_RELOC_GOT_LOAD\n"));
+            IF_DEBUG(linker, debugBelch("relocateSection: GOT entry for %s is at %0llx\n", symbolName, addr));
+
+            fixup += (addr - pc);
+            break;
+
+        case X86_64_RELOC_UNSIGNED:
 
-            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, (unsigned long)lookupSymbol(nm))->addr;
+            ASSERT(!reloc->r_pcrel);          // UNSIGNED relocs are always absolute addresses
+            ASSERT(reloc->r_length >= 2);     // and always 32 or 64 bits.
 
-            type = X86_64_RELOC_SIGNED;
+            IF_DEBUG(linker, debugBelch("relocateSection: type is X86_64_RELOC_UNSIGNED\n"));
+
+            if (reloc->r_extern) {
+                addr = findExternalSymbol(symbol, symbolName, oc, sections);
+                IF_DEBUG(linker, debugBelch("relocateSection: external symbol %s found at %0llx\n", symbolName, addr));
+            }
+            else {
+                // Non-external, UNSIGNED relocation: the fixup contains the address
+                // of the target from the start of the memory image.  So just add
+                // the memory address of the begining of the mmap'ed object code.
+
+                int s = reloc->r_symbolnum - 1;
+                addr = (uint64_t)(oc->image + sections[s].offset - sections[s].addr);
+                IF_DEBUG(linker, debugBelch("relocateSection: start target section is at %0llx\n", addr));
+            }
+
+            fixup += addr;
+
+            break;
+
+        case X86_64_RELOC_BRANCH:
+
+            ASSERT(reloc->r_pcrel);           // BRANCH relocs are always pc-relative,
+            ASSERT(reloc->r_length == 0 ||    // and either 8 or 32 bits.
+                   reloc->r_length == 2);
+
+            IF_DEBUG(linker, debugBelch("relocateSection: type is X86_64_RELOC_BRANCH\n"));
+
+            int64_t displ;
+
+            if (reloc->r_length == 0) {
+                if (reloc->r_extern) {
+                    addr = findExternalSymbol(symbol, symbolName, oc, sections);
+                    IF_DEBUG(linker, debugBelch("relocateSection: external symbol %s found at %0llx\n", symbolName, addr));
+                }
+                else {
+                    addr = findInternalReference(reloc, oc, sections);
+                    IF_DEBUG(linker, debugBelch("relocateSection: internal reference found at %0llx\n", addr));
+                }
+
+                if (addr == 0) {
+                    barf("relocateSection: non-relocatable symbol found in 8 bit BRANCH relocation\n");
+                } 
+
+                displ = addr - pc;
+
+                if (displ > 0) {
+                    if (displ > MAX_8_BIT_FORWARD_DISPL) {
+                        barf("relocateSection: required 8 bit displacement too far forward");
+                    }
+                } else {
+                    if (displ < MIN_8_BIT_BACKWARD_DISPL) {
+                        barf("relocateSection: required 8 bit displacement too far backward");
         }
-        else if(reloc->r_extern)
-        {
-            struct nlist *symbol = &nlist[reloc->r_symbolnum];
-            char *nm = image + symLC->stroff + symbol->n_un.n_strx;
+                }
+
+                IF_DEBUG(linker, debugBelch("relocateSection: 8 bit BRANCH displacement = %lld (%#0llx)\n", displ, (unsigned long long) displ));
 
-            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));
+            } else {
+                if (reloc->r_extern) {
+                    addr = findExternalSymbol(symbol, symbolName, oc, sections);
+                    IF_DEBUG(linker, debugBelch("relocateSection: external symbol %s found at %0llx\n", symbolName, addr));
             }
             else {
-                value = (uint64_t) lookupSymbol(nm);
-                IF_DEBUG(linker, debugBelch("relocateSection: external symbol %s, address %p\n", nm, (void *)value));
+                    addr = findInternalReference(reloc, oc, sections);
+                    IF_DEBUG(linker, debugBelch("relocateSection: internal reference found at %0llx\n", addr));
+                }
+
+                if (addr == 0) {
+                    barf("relocateSection: non-relocatable symbol found in 32 bit BRANCH relocation\n");
+                } 
+
+                displ = addr - pc;
+
+                IF_DEBUG(linker, debugBelch("relocateSection: 32 bit BRANCH displacement = %lld (%#0llx)\n", displ, (unsigned long long) displ));
+                
+                if (((displ > 0) && (displ > MAX_32_BIT_FORWARD_DISPL))
+                 || ((displ < 0) && (displ < MIN_32_BIT_BACKWARD_DISPL))) {
+                    addr = (uint64_t) &makeSymbolExtra(oc, reloc->r_symbolnum, addr)->jumpIsland;
+                    IF_DEBUG(linker, debugBelch("relocateSection: making jump island for 32 bit BRANCH relocation: %#0llx\n", addr));
+                    displ = addr - pc;
+
+                    IF_DEBUG(linker, debugBelch("relocateSection: 32 bit BRANCH displacement to jump island= %lld (%#0llx)\n", displ, (unsigned long long) displ));
+                }
             }
+
+            fixup += displ;
+
+            break;
+
+        case X86_64_RELOC_SUBTRACTOR:
+
+            IF_DEBUG(linker, debugBelch("relocateSection: type is X86_64_RELOC_SUBTRACTOR\n"));
+
+            // An X86_64_RELOC_SUBTRACTOR can never be the last relocation
+            // in a section, since it must be followed by an X86_64_RELOC_UNSIGNED.
+            // Barf if this isn't true.
+
+            if (i == (n - 1)) {
+                barf("X86_64_RELOC_SUBTRACTOR is the last relocation in section %s", sect->sectname);
+            }
+
+            struct relocation_info *nextReloc =  &relocs[i + 1];
+
+            ASSERT(!reloc->r_pcrel);                              // SUBTRACTOR relocs must not be pc relative,
+            ASSERT(reloc->r_length == 2 ||                        // must be 32 or 64 bits,
+                   reloc->r_length == 3);                         //
+            ASSERT(reloc->r_extern);                              // must be external,
+            ASSERT(nextReloc->r_type == X86_64_RELOC_UNSIGNED);   // must be followed by a X86_64_RELOC_UNSIGNED,
+            ASSERT(nextReloc->r_length == nextReloc->r_length);   // with the same length as the SUBTRACTOR reloc,
+            ASSERT(!nextReloc->r_pcrel);                          // and must also not be pc relative. Got it?
+
+            if (nextReloc->r_type != X86_64_RELOC_UNSIGNED) {
+                barf("relocateSection: SUBTRACTOR must be followed by UNSIGNED");
         }
-        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).
 
-	    value = baseValue;
+            if (reloc->r_length == 2) {
+                    addr = findExternalSymbol(symbol, symbolName, oc, sections);
+                    IF_DEBUG(linker, debugBelch("relocateSection: external symbol %s found at %0llx\n", symbolName, addr));
         }
+            else {
+                    addr = findExternalSymbol(symbol, symbolName, oc, sections);
+                    IF_DEBUG(linker, debugBelch("relocateSection: external symbol %s found at %0llx\n", symbolName, addr));
+            }
+
+            // Subtract the symbol address from the fixup.  The subsequent
+            // UNSIGNED relocation will add the address of another external
+            // symbol, so the final relocation will be the difference of the
+            // symbol addresses, plus any offset originally saved in the
+            // fixup location.
+
+            fixup -= addr;
+
+            break;
+
+        case X86_64_RELOC_SIGNED:
+        case X86_64_RELOC_SIGNED_1:
+        case X86_64_RELOC_SIGNED_2:
+        case X86_64_RELOC_SIGNED_4:
+
+            IF_DEBUG(linker, debugBelch("relocateSection: type is X86_64_RELOC_SIGNED/1/2/4\n"));
 
-        IF_DEBUG(linker, debugBelch("relocateSection: value = %p\n", (void *)value));
+            uint64_t addend = 0;
 
-        if (type == X86_64_RELOC_BRANCH)
+            ASSERT(reloc->r_pcrel);           // SIGNED relocs are always pc relative
+            ASSERT(reloc->r_length == 2);     // and 32 bits long.
+
+            if (reloc->r_extern) {
+                switch (reloc->r_type)
         {
-            if((int32_t)(value - baseValue) != (int64_t)(value - baseValue))
-            {
-                ASSERT(reloc->r_extern);
-                value = (uint64_t) &makeSymbolExtra(oc, reloc->r_symbolnum, value)
-                                        -> jumpIsland;
+                case X86_64_RELOC_SIGNED:
+                    addend = 0;
+                    break;
+                case X86_64_RELOC_SIGNED_1:
+                    addend = 1;
+                    break;
+                case X86_64_RELOC_SIGNED_2:
+                    addend = 2;
+                    break;
+                case X86_64_RELOC_SIGNED_4:
+                    addend = 4;
+                    break;
             }
-            ASSERT((int32_t)(value - baseValue) == (int64_t)(value - baseValue));
-            type = X86_64_RELOC_SIGNED;
+
+                addr = findExternalSymbol(symbol, symbolName, oc, sections);
+                IF_DEBUG(linker, debugBelch("relocateSection: external symbol %s found at %0llx\n", symbolName, addr));
         }
+            else {
+
+                // Relocations that are both SIGNED and not external
+                // have compiler computed offsets to the target address
+                // in the object file. We needn't do anything at all with these.
 
-        switch(type)
+                switch (reloc->r_type)
         {
-            case X86_64_RELOC_UNSIGNED:
-                ASSERT(!reloc->r_pcrel);
-                thing += value;
+                case X86_64_RELOC_SIGNED:
+                    addend = 0;
                 break;
-            case X86_64_RELOC_SIGNED:
             case X86_64_RELOC_SIGNED_1:
+                    addend = 1;
+                    break;
             case X86_64_RELOC_SIGNED_2:
+                    addend = 2;
+                    break;
             case X86_64_RELOC_SIGNED_4:
-                ASSERT(reloc->r_pcrel);
-                thing += value - baseValue;
+                    addend = 4;
                 break;
-            case X86_64_RELOC_SUBTRACTOR:
-                ASSERT(!reloc->r_pcrel);
-                thing -= value;
+                }
+
+                addr = pc;
+                IF_DEBUG(linker, debugBelch("relocateSection: not external, leaving fixup unchanged.\n"));
+            }
+
+            fixup += (addr - pc) + addend;
+
                 break;
+
             default:
-                barf("unkown relocation");
+            barf("relocateSection: unknown relocation type");
+
         }
 
+        IF_DEBUG(linker, debugBelch("relocateSection: FINAL: reloc = %d, addr = %0lx, fixup = %0llx\n", i, (unsigned long)reloc->r_address, fixup));
+        // Write the fixed-up value back to the image.
+
         switch(reloc->r_length)
         {
             case 0:
-                *(uint8_t*)thingPtr = thing;
+                IF_DEBUG(linker, debugBelch("relocateSection: 8 bit reloc, fixup %#0x written to %p\n",
+                                            (uint8_t)fixup, fixupPtr));
+                *(uint8_t *)fixupPtr = fixup;
                 break;
             case 1:
-                *(uint16_t*)thingPtr = thing;
+                IF_DEBUG(linker, debugBelch("relocateSection: 16 bit reloc, fixup %#0x written to %p\n",
+                                            (uint16_t)fixup, fixupPtr));
+                *(uint16_t *)fixupPtr = fixup;
                 break;
             case 2:
-                *(uint32_t*)thingPtr = thing;
+                IF_DEBUG(linker, debugBelch("relocateSection: 32 bit reloc, fixup %#0x written to %p\n",
+                                            (uint32_t)fixup, fixupPtr));
+                *(uint32_t *)fixupPtr = fixup;
                 break;
             case 3:
-                *(uint64_t*)thingPtr = thing;
+                IF_DEBUG(linker, debugBelch("relocateSection: 64 bit reloc, fixup %#0llx written to %p\n",
+                                            (uint64_t)fixup, fixupPtr));
+                *(uint64_t *)fixupPtr = fixup;
                 break;
         }
-#else
-        if(relocs[i].r_address & R_SCATTERED)
-        {
+
+#else // x86 and powerpc architectures start here
+
+        if (relocs[i].r_address & R_SCATTERED) {
             struct scattered_relocation_info *scat =
                 (struct scattered_relocation_info*) &relocs[i];
 
-            if(!scat->r_pcrel)
-            {
-                if(scat->r_length == 2)
-                {
+            if (!scat->r_pcrel) {
+                if (scat->r_length == 2) {
+
                     unsigned long word = 0;
                     unsigned long* wordPtr = (unsigned long*) (image + sect->offset + scat->r_address);
+
                     checkProddableBlock(oc,wordPtr);
 
                     // Note on relocation types:
@@ -5169,29 +5637,32 @@
                     commonSize += nlist[i].n_value;
                     oc->n_symbols++;
                 }
-                else if((nlist[i].n_type & N_TYPE) == N_SECT)
+                else if ((nlist[i].n_type & N_TYPE) == N_SECT) {
                     oc->n_symbols++;
             }
         }
     }
+    }
+
     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)");
 
-    if(symLC)
-    {
-        for(i=0;i<symLC->nsyms;i++)
-        {
-            if(nlist[i].n_type & N_STAB)
+    if (symLC) {
+        for (i = 0; i < symLC->nsyms; i++) {
+            IF_DEBUG(linker, debugBelch("ocGetNames_MachO: symbol type is %#04x\n", nlist[i].n_type));
+            if (nlist[i].n_type & N_STAB) {
                 ;
+            }
             else if((nlist[i].n_type & N_TYPE) == N_SECT)
             {
-                if(nlist[i].n_type & N_EXT)
+                if ((nlist[i].n_type & N_EXT) && !(nlist[i].n_type & N_PEXT))
                 {
                     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_DEBUG(linker, debugBelch("    weak: %s\n", nm));
+                        IF_DEBUG(linker, debugBelch("ocGetNames_MachO: \tweak: %s\n", nm));
                     }
                     else
                     {
@@ -5262,51 +5751,72 @@
     nlist = symLC ? (struct nlist*) (image + symLC->symoff)
                   : NULL;
 
-    if(dsymLC)
-    {
+    if (dsymLC) {
         unsigned long *indirectSyms
             = (unsigned long*) (image + dsymLC->indirectsymoff);
 
-        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")
-                || !strcmp(sections[i].sectname,"__la_sym_ptr3"))
+        IF_DEBUG(linker, debugBelch("ocResolve_MachO: trying to resolve dynamic symbols\n"));
+        for (i = 0; i < segLC->nsects; i++) {
+            if (    strcmp(sections[i].sectname,"__la_symbol_ptr") == 0
+                 || strcmp(sections[i].sectname,"__la_sym_ptr2")   == 0
+                 || strcmp(sections[i].sectname,"__la_sym_ptr3")   == 0)
             {
+                IF_DEBUG(linker, debugBelch("ocResolve_machO: resolving __la_symbol_ptr, __la_sym_ptr2 or __la_sym_ptr3 section\n"));
                 if(!resolveImports(oc,image,symLC,&sections[i],indirectSyms,nlist))
                     return 0;
             }
-            else if(!strcmp(sections[i].sectname,"__nl_symbol_ptr")
-                ||  !strcmp(sections[i].sectname,"__pointers"))
+            else if (strcmp(sections[i].sectname,"__nl_symbol_ptr") == 0
+                 ||  strcmp(sections[i].sectname,"__pointers")      == 0)
             {
+                IF_DEBUG(linker, debugBelch("ocResolve_machO: resolving __nl_symbol_ptr or __pointers section\n"));
                 if(!resolveImports(oc,image,symLC,&sections[i],indirectSyms,nlist))
                     return 0;
             }
-            else if(!strcmp(sections[i].sectname,"__jump_table"))
+            else if (strcmp(sections[i].sectname,"__jump_table") == 0)
             {
+                IF_DEBUG(linker, debugBelch("ocResolve_MachO: resolving jump table\n"));
                 if(!resolveImports(oc,image,symLC,&sections[i],indirectSyms,nlist))
                     return 0;
             }
             else
             {
-                IF_DEBUG(linker, debugBelch("ocResolve_MachO: unknown section\n"));
+                    IF_DEBUG(linker, debugBelch("ocResolve_MachO: %s is not used by the dynamic linker, skipping\n", sections[i].sectname));
             }
         }
+        IF_DEBUG(linker, debugBelch("ocResolve_MachO: done resolving dynamic symbols\n"));
     }
 
-    for(i=0;i<segLC->nsects;i++)
-    {
-            IF_DEBUG(linker, debugBelch("ocResolve_MachO: relocating section %d\n", i));
+    IF_DEBUG(linker, debugBelch("ocResolve_MachO: beginning relocations\n"));
+
+    IF_DEBUG(linker, 
+    for (i = 0; i < segLC->nsects; i++) {
+        debugBelch("ocResolve_MachO: \tsection[%d].sectname = %s\n", i, sections[i].sectname);
+        debugBelch("ocResolve_MachO: \tsection[%d].segname  = %s\n", i, sections[i].segname);
+        debugBelch("ocResolve_MachO: \tsection[%d].addr     = %#0llx\n", i, (unsigned long long)sections[i].addr);
+        debugBelch("ocResolve_MachO: \tsection[%d].size     = %#0llx\n", i, (unsigned long long)sections[i].size);
+        debugBelch("ocResolve_MachO: \tsection[%d].offset   = %#0lx\n", i, (unsigned long)sections[i].offset);
+        debugBelch("ocResolve_MachO: \tsection[%d].align    = %#0lx\n", i, (unsigned long)sections[i].align);
+        debugBelch("ocResolve_MachO: \tsection[%d].reloff   = %#0lx\n", i, (unsigned long)sections[i].reloff);
+        debugBelch("ocResolve_MachO: \tsection[%d].nreloc   = %#0lx\n", i, (unsigned long)sections[i].nreloc);
+        debugBelch("ocResolve_MachO: \tsection[%d].flags    = %#0lx\n", i, (unsigned long)sections[i].flags);
+        debugBelch("ocResolve_MachO: \n");
+    });
+
+    for (i = 0; i < segLC->nsects; i++) {
+
+        IF_DEBUG(linker, debugBelch("ocResolve_MachO: relocating section %d (%s)\n", i, sections[i].sectname));
 
         if (!relocateSection(oc,image,symLC,nlist,segLC->nsects,sections,&sections[i]))
             return 0;
     }
 
+    IF_DEBUG(linker, debugBelch("ocResolve_MachO: finished relocating\n"));
+
 #if defined (powerpc_HOST_ARCH)
     ocFlushInstructionCache( oc );
 #endif
 
+    IF_DEBUG(linker, debugBelch("ocResolve_MachO: done\n"));
     return 1;
 }
 
