ELF/x86_64: map object file sections separately into the low 2GB
authorSimon Marlow <marlowsd@gmail.com>
Thu, 15 Oct 2015 15:17:06 +0000 (16:17 +0100)
committerSimon Marlow <marlowsd@gmail.com>
Thu, 15 Oct 2015 15:19:45 +0000 (16:19 +0100)
On 64-bit ELF we need to link object files into the low 2GB due to the
small memory model.  Previously we would map the entire object file
using MAP_32BIT, but the object file can consist of 75% or more
symbols, which only need to be present during linking, so this is
wasteful.  In our particular application, we're already running out of
space here.

This patch changes the way we load object files on ELF platforms so
that the object is first mapped above the 2GB boundary, parsed, and
then the important sections are re-mapped into the low 2GB area.

Test Plan:
validate
(also needs testing on OS X & Windows, preferably 32 & 64 bit)

Reviewers: Phyx, trommler, bgamari, austin

Subscribers: hsyl20, thomie, bgamari

Differential Revision: https://phabricator.haskell.org/D975

rts/CheckUnload.c
rts/Linker.c
rts/LinkerInternals.h
testsuite/tests/ghci/linking/all.T
testsuite/tests/rts/linker_error.c

index 7802754..c756738 100644 (file)
 static void checkAddress (HashTable *addrs, void *addr)
 {
     ObjectCode *oc;
+    int i;
 
     if (!lookupHashTable(addrs, (W_)addr)) {
         insertHashTable(addrs, (W_)addr, addr);
 
         for (oc = unloaded_objects; oc; oc = oc->next) {
-            if ((W_)addr >= (W_)oc->image &&
-                (W_)addr <  (W_)oc->image + oc->fileSize) {
-                oc->referenced = 1;
-                break;
+            for (i = 0; i < oc->n_sections; i++) {
+                if (oc->sections[i].kind != SECTIONKIND_OTHER) {
+                    if ((W_)addr >= (W_)oc->sections[i].start &&
+                        (W_)addr <  (W_)oc->sections[i].start
+                                    + oc->sections[i].size) {
+                        oc->referenced = 1;
+                        return;
+                    }
+                }
             }
         }
     }
index 355b33a..be5b3b8 100644 (file)
@@ -174,7 +174,7 @@ typedef void (*init_t) (int argc, char **argv, char **env);
 static HsInt isAlreadyLoaded( pathchar *path );
 static HsInt loadOc( ObjectCode* oc );
 static ObjectCode* mkOc( pathchar *path, char *image, int imageSize,
-                         char *archiveMemberName
+                         rtsBool mapped, char *archiveMemberName
 #ifndef USE_MMAP
 #ifdef darwin_HOST_OS
                        , int misalignment
@@ -219,7 +219,7 @@ static int ocVerifyImage_ELF    ( ObjectCode* oc );
 static int ocGetNames_ELF       ( ObjectCode* oc );
 static int ocResolve_ELF        ( ObjectCode* oc );
 static int ocRunInit_ELF        ( ObjectCode* oc );
-#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH) || defined(arm_HOST_ARCH)
+#if NEED_SYMBOL_EXTRAS
 static int ocAllocateSymbolExtras_ELF ( ObjectCode* oc );
 #endif
 #elif defined(OBJFORMAT_PEi386)
@@ -254,7 +254,7 @@ static int ocRunInit_MachO        ( ObjectCode* oc );
 #ifndef USE_MMAP
 static int machoGetMisalignment( FILE * );
 #endif
-#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
+#if NEED_SYMBOL_EXTRAS
 static int ocAllocateSymbolExtras_MachO ( ObjectCode* oc );
 #endif
 #ifdef powerpc_HOST_ARCH
@@ -264,6 +264,34 @@ static void machoInitSymbolsWithoutUnderscore( void );
 
 static void freeProddableBlocks (ObjectCode *oc);
 
+#ifdef USE_MMAP
+/**
+ * An allocated page being filled by the allocator
+ */
+struct m32_alloc_t {
+   void * base_addr;             // Page address
+   unsigned int current_size;    // Number of bytes already reserved
+};
+
+#define M32_MAX_PAGES 32
+
+/**
+ * Allocator
+ *
+ * Currently an allocator is just a set of pages being filled. The maximum
+ * number of pages can be configured with M32_MAX_PAGES.
+ */
+typedef struct m32_allocator_t {
+   struct m32_alloc_t pages[M32_MAX_PAGES];
+} * m32_allocator;
+
+// We use a global memory allocator
+static struct m32_allocator_t allocator;
+
+struct m32_allocator_t;
+static void m32_allocator_init(struct m32_allocator_t *m32);
+#endif
+
 /* on x86_64 we have a problem with relocating symbol references in
  * code that was compiled without -fPIC.  By default, the small memory
  * model is used, which assumes that symbol references can fit in a
@@ -1730,6 +1758,8 @@ initLinker_ (int retain_cafs)
     addDLLHandle(WSTR("*.exe"), GetModuleHandle(NULL));
 #endif
 
+    m32_allocator_init(&allocator);
+
     IF_DEBUG(linker, debugBelch("initLinker: done\n"));
     return;
 }
@@ -2194,21 +2224,42 @@ void ghci_enquire ( char* addr )
 
 #ifdef USE_MMAP
 #define ROUND_UP(x,size) ((x + size - 1) & ~(size - 1))
+#define ROUND_DOWN(x,size) (x & ~(size - 1))
+
+static StgWord getPageSize(void)
+{
+    static StgWord pagesize = 0;
+    if (pagesize == 0) {
+        return sysconf(_SC_PAGESIZE);
+    } else {
+        return pagesize;
+    }
+}
+
+static StgWord roundUpToPage (StgWord size)
+{
+    return ROUND_UP(size, getPageSize());
+}
+
+#ifdef OBJFORMAT_ELF
+static StgWord roundDownToPage (StgWord size)
+{
+    return ROUND_DOWN(size, getPageSize());
+}
+#endif
 
 //
 // Returns NULL on failure.
 //
-static void * mmapForLinker (size_t bytes, nat flags, int fd)
+static void * mmapForLinker (size_t bytes, nat flags, int fd, int offset)
 {
    void *map_addr = NULL;
    void *result;
-   int pagesize;
    StgWord size;
    static nat fixed = 0;
 
    IF_DEBUG(linker, debugBelch("mmapForLinker: start\n"));
-   pagesize = getpagesize();
-   size = ROUND_UP(bytes, pagesize);
+   size = roundUpToPage(bytes);
 
 #if !defined(ALWAYS_PIC) && defined(x86_64_HOST_ARCH)
 mmap_again:
@@ -2227,7 +2278,7 @@ mmap_again:
 
    result = mmap(map_addr, size,
                  PROT_EXEC|PROT_READ|PROT_WRITE,
-                 MAP_PRIVATE|TRY_MAP_32BIT|fixed|flags, fd, 0);
+                 MAP_PRIVATE|TRY_MAP_32BIT|fixed|flags, fd, offset);
 
    if (result == MAP_FAILED) {
        sysErrorBelch("mmap %" FMT_Word " bytes at %p",(W_)size,map_addr);
@@ -2289,6 +2340,211 @@ mmap_again:
 
    return result;
 }
+
+/*
+
+Note [M32 Allocator]
+~~~~~~~~~~~~~~~~~~~~
+
+A memory allocator that allocates only pages in the 32-bit range (lower 2GB).
+This is useful on 64-bit platforms to ensure that addresses of allocated
+objects can be referenced with a 32-bit relative offset.
+
+Initially, the linker used `mmap` to allocate a page per object. Hence it
+wasted a lot of space for small objects (see #9314). With this allocator, we
+try to fill pages as much as we can for small objects.
+
+How does it work?
+-----------------
+
+For small objects, a Word64 counter is added at the beginning of the page they
+are stored in. It indicates the number of objects that are still alive in the
+page. When the counter drops down to zero, the page is freed. The counter is
+atomically decremented, hence the deallocation is thread-safe.
+
+During the allocation phase, the allocator keeps track of some pages that are
+not totally filled: the number of pages in the "filling" list is configurable
+with M32_MAX_PAGES. Allocation consists in finding some place in one of these
+pages or starting a new one, then increasing the page counter. If none of the
+pages in the "filling" list has enough free space, the most filled one is
+flushed (see below) and a new one is allocated.
+
+The allocator holds a reference on pages in the "filling" list: the counter in
+these pages is 1+n where n is the current number of objects allocated in the
+page. Hence allocated objects can be freed while the allocator is using
+(filling) the page. Flushing a page consists in decreasing its counter and
+removing it from the "filling" list. By extension, flushing the allocator
+consists in flushing all the pages in the "filling" list.  Don't forget to
+flush the allocator at the end of the allocation phase in order to avoid space
+leaks!
+
+Large objects are objects that are larger than a page (minus the bytes required
+for the counter and the optional padding). These objects are allocated into
+their own set of pages.  We can differentiate large and small objects from
+their address: large objects are aligned on page size while small objects never
+are (because of the space reserved for the page's object counter).
+
+For large objects, the remaining space at the end of the last page is left
+unused by the allocator. It can be used with care as it will be freed with the
+associated large object. GHC linker uses this feature/hack, hence changing the
+implementation of the M32 allocator must be done with care (i.e. do not try to
+improve the allocator to avoid wasting this space without modifying the linker
+code accordingly).
+
+Object allocation is *not* thread-safe (however it could be done easily with a
+lock in the allocator structure). Object deallocation is thread-safe.
+
+*/
+
+/****************************************************************************
+ * M32 ALLOCATOR (see Note [M32 Allocator]
+ ***************************************************************************/
+
+/**
+ * Wrapper for `unmap` that handles error cases.
+ */
+static void munmapForLinker (void * addr, size_t size)
+{
+   int r = munmap(addr,size);
+   if (r == -1) {
+      // Should we abort here?
+      sysErrorBelch("munmap");
+   }
+}
+
+/**
+ * Initialize the allocator structure
+ */
+static void m32_allocator_init(m32_allocator m32) {
+   memset(m32, 0, sizeof(struct m32_allocator_t));
+}
+
+/**
+ * Atomically decrement the object counter on the given page and release the
+ * page if necessary. The given address must be the *base address* of the page.
+ *
+ * You shouldn't have to use this method. Use `m32_free` instead.
+ */
+static void m32_free_internal(void * addr) {
+   uint64_t c = __sync_sub_and_fetch((uint64_t*)addr, 1);
+   if (c == 0) {
+      munmapForLinker(addr, getPageSize());
+   }
+}
+
+/**
+ * Release the allocator's reference to pages on the "filling" list. This
+ * should be called when it is believed that no more allocations will be needed
+ * from the allocator to ensure that empty pages waiting to be filled aren't
+ * unnecessarily held.
+ */
+static void m32_allocator_flush(m32_allocator m32) {
+   int i;
+   for (i=0; i<M32_MAX_PAGES; i++) {
+      void * addr =  __sync_fetch_and_and(&m32->pages[i].base_addr, 0x0);
+      if (addr != 0) {
+         m32_free_internal(addr);
+      }
+   }
+}
+
+// Return true if the object has its own dedicated set of pages
+#define m32_is_large_object(size,alignment) \
+   (size >= getPageSize() - ROUND_UP(8,alignment))
+
+// Return true if the object has its own dedicated set of pages
+#define m32_is_large_object_addr(addr) \
+   ((uintptr_t) addr % getPageSize() == 0)
+
+/**
+ * Free the memory associated with an object.
+ *
+ * If the object is "small", the object counter of the page it is allocated in
+ * is decremented and the page is not freed until all of its objects are freed.
+ */
+static void m32_free(void *addr, unsigned int size) {
+   uintptr_t m = (uintptr_t) addr % getPageSize();
+
+   if (m == 0) {
+      // large object
+      munmapForLinker(addr,ROUND_UP(size,getPageSize()));
+   }
+   else {
+      // small object
+      void * page_addr = (void*)((uintptr_t)addr - m);
+      m32_free_internal(page_addr);
+   }
+}
+
+/**
+ * Allocate `size` bytes of memory with the given alignment
+ */
+static void *
+m32_alloc(m32_allocator m32, unsigned int size,
+          unsigned int alignment) {
+
+   unsigned int pgsz = (unsigned int)getPageSize();
+
+   if (m32_is_large_object(size,alignment)) {
+       // large object
+       return mmapForLinker(size,MAP_ANONYMOUS,-1,0);
+   }
+   else {
+      // small object
+      // Try to find a page that can contain it
+      int empty = -1;
+      int most_filled = -1;
+      int i;
+      for (i=0; i<M32_MAX_PAGES; i++) {
+         // empty page
+         if (m32->pages[i].base_addr == 0) {
+            empty = empty == -1 ? i : empty;
+            continue;
+         }
+         // page can contain the buffer?
+         unsigned int alsize = ROUND_UP(m32->pages[i].current_size, alignment);
+         if (size <= pgsz - alsize) {
+            void * addr = (char*)m32->pages[i].base_addr + alsize;
+            m32->pages[i].current_size = alsize + size;
+            // increment the counter atomically
+            __sync_fetch_and_add((uint64_t*)m32->pages[i].base_addr, 1);
+            return addr;
+         }
+         // most filled?
+         if (most_filled == -1
+          || m32->pages[most_filled].current_size < m32->pages[i].current_size)
+         {
+            most_filled = i;
+         }
+      }
+
+      // If we haven't found an empty page, flush the most filled one
+      if (empty == -1) {
+         m32_free_internal(m32->pages[most_filled].base_addr);
+         m32->pages[most_filled].base_addr    = 0;
+         m32->pages[most_filled].current_size = 0;
+         empty = most_filled;
+      }
+
+      // Allocate a new page
+      void * addr = mmapForLinker(pgsz,MAP_ANONYMOUS,-1,0);
+      if (addr == NULL) {
+         return NULL;
+      }
+      m32->pages[empty].base_addr    = addr;
+      // Add 8 bytes for the counter + padding
+      m32->pages[empty].current_size = size+ROUND_UP(8,alignment);
+      // Initialize the counter:
+      // 1 for the allocator + 1 for the returned allocated memory
+      *((uint64_t*)addr)             = 2;
+      return (char*)addr + ROUND_UP(8,alignment);
+   }
+}
+
+/****************************************************************************
+ * END (M32 ALLOCATOR)
+ ***************************************************************************/
+
 #endif // USE_MMAP
 
 /*
@@ -2329,6 +2585,39 @@ static void freeOcStablePtrs (ObjectCode *oc)
     oc->stable_ptrs = NULL;
 }
 
+static void
+freePreloadObjectFile (ObjectCode *oc)
+{
+#ifdef USE_MMAP
+
+    if (oc->imageMapped) {
+        munmap(oc->image, oc->fileSize);
+    } else {
+        stgFree(oc->image);
+    }
+
+#elif defined(mingw32_HOST_OS)
+
+    VirtualFree(oc->image - PEi386_IMAGE_OFFSET, 0, MEM_RELEASE);
+
+    IndirectAddr *ia, *ia_next;
+    ia = indirects;
+    while (ia != NULL) {
+      ia_next = ia->next;
+      stgFree(ia);
+      ia = ia_next;
+    }
+    indirects = NULL;
+
+#else
+
+    stgFree(oc->image);
+
+#endif
+
+    oc->image = NULL;
+    oc->fileSize = 0;
+}
 
 /*
  * freeObjectCode() releases all the pieces of an ObjectCode.  It is called by
@@ -2337,69 +2626,54 @@ static void freeOcStablePtrs (ObjectCode *oc)
  */
 void freeObjectCode (ObjectCode *oc)
 {
+    freePreloadObjectFile(oc);
+
     if (oc->symbols != NULL) {
         stgFree(oc->symbols);
         oc->symbols = NULL;
     }
 
-    {
-        Section *s, *nexts;
-
-        for (s = oc->sections; s != NULL; s = nexts) {
-            nexts = s->next;
-            stgFree(s);
+    if (oc->sections != NULL) {
+        int i;
+        for (i=0; i < oc->n_sections; i++) {
+            if (oc->sections[i].start != NULL) {
+                switch(oc->sections[i].alloc){
+#ifdef USE_MMAP
+                case SECTION_MMAP:
+                    munmap(oc->sections[i].mapped_start,
+                           oc->sections[i].mapped_size);
+                    break;
+                case SECTION_M32:
+                    m32_free(oc->sections[i].start,
+                             oc->sections[i].size);
+                    break;
+#endif
+                case SECTION_MALLOC:
+                    stgFree(oc->sections[i].start);
+                    break;
+                default:
+                    break;
+                }
+            }
         }
+        stgFree(oc->sections);
     }
 
     freeProddableBlocks(oc);
 
+    /* Free symbol_extras.  On x86_64 Windows, symbol_extras are allocated
+     * alongside the image, so we don't need to free. */
+#if NEED_SYMBOL_EXTRAS && (!defined(x86_64_HOST_ARCH) || !defined(mingw32_HOST_OS))
 #ifdef USE_MMAP
-    int pagesize, size, r;
-
-    pagesize = getpagesize();
-    size = ROUND_UP(oc->fileSize, pagesize);
-
-    r = munmap(oc->image, size);
-    if (r == -1) {
-        sysErrorBelch("munmap");
-    }
-
-#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH) || defined(arm_HOST_ARCH)
-#if !defined(x86_64_HOST_ARCH) || !defined(mingw32_HOST_OS)
     if (!USE_CONTIGUOUS_MMAP && oc->symbol_extras != NULL)
     {
-        munmap(oc->symbol_extras,
-               ROUND_UP(sizeof(SymbolExtra) * oc->n_symbol_extras, pagesize));
+        m32_free(oc->symbol_extras, sizeof(SymbolExtra) * oc->n_symbol_extras);
     }
-#endif
-#endif
-
-#else
-
-#ifndef mingw32_HOST_OS
-    stgFree(oc->image);
-#else
-    VirtualFree(oc->image - PEi386_IMAGE_OFFSET, 0, MEM_RELEASE);
-
-    IndirectAddr *ia, *ia_next;
-    ia = indirects;
-    while (ia != NULL) {
-      ia_next = ia->next;
-      stgFree(ia);
-      ia = ia_next;
-    }
-    indirects = NULL;
-
-#endif
-
-#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH) || defined(arm_HOST_ARCH)
-#if !defined(x86_64_HOST_ARCH) || !defined(mingw32_HOST_OS)
+#else // !USE_MMAP
     stgFree(oc->symbol_extras);
 #endif
 #endif
 
-#endif
-
     stgFree(oc->fileName);
     stgFree(oc->archiveMemberName);
     stgFree(oc);
@@ -2408,7 +2682,7 @@ void freeObjectCode (ObjectCode *oc)
 
 static ObjectCode*
 mkOc( pathchar *path, char *image, int imageSize,
-      char *archiveMemberName
+      rtsBool mapped, char *archiveMemberName
 #ifndef USE_MMAP
 #ifdef darwin_HOST_OS
     , int misalignment
@@ -2444,12 +2718,14 @@ mkOc( pathchar *path, char *image, int imageSize,
 
    oc->fileSize          = imageSize;
    oc->symbols           = NULL;
+   oc->n_sections        = 0;
    oc->sections          = NULL;
    oc->proddables        = NULL;
    oc->stable_ptrs       = NULL;
-#if powerpc_HOST_ARCH || x86_64_HOST_ARCH || arm_HOST_ARCH
+#if NEED_SYMBOL_EXTRAS
    oc->symbol_extras     = NULL;
 #endif
+   oc->imageMapped       = mapped;
 
 #ifndef USE_MMAP
 #ifdef darwin_HOST_OS
@@ -2788,16 +3064,7 @@ static HsInt loadArchive_ (pathchar *path)
 
             IF_DEBUG(linker, debugBelch("loadArchive: Member is an object file...loading...\n"));
 
-            /* We can't mmap from the archive directly, as object
-               files need to be 8-byte aligned but files in .ar
-               archives are 2-byte aligned. When possible we use mmap
-               to get some anonymous memory, as on 64-bit platforms if
-               we use malloc then we can be given memory above 2^32.
-               In the mmap case we're probably wasting lots of space;
-               we could do better. */
-#if defined(USE_MMAP)
-            image = mmapForLinker(memberSize, MAP_ANONYMOUS, -1);
-#elif defined(mingw32_HOST_OS)
+#if defined(mingw32_HOST_OS)
         // TODO: We would like to use allocateExec here, but allocateExec
         //       cannot currently allocate blocks large enough.
             image = allocateImageAndTrampolines(path, fileName,
@@ -2806,11 +3073,16 @@ static HsInt loadArchive_ (pathchar *path)
 #endif
                memberSize);
 #elif defined(darwin_HOST_OS)
+#if defined(USE_MMAP)
+            image = mmapForLinker(memberSize, MAP_ANONYMOUS, -1, 0);
+#else
             /* See loadObj() */
             misalignment = machoGetMisalignment(f);
             image = stgMallocBytes(memberSize + misalignment, "loadArchive(image)");
             image += misalignment;
-#else
+#endif // USE_MMAP
+
+#else // not windows or darwin
             image = stgMallocBytes(memberSize, "loadArchive(image)");
 #endif
 
@@ -2867,12 +3139,10 @@ static HsInt loadArchive_ (pathchar *path)
             sprintf(archiveMemberName, "%" PATH_FMT "(%.*s)",
                     path, (int)thisFileNameSize, fileName);
 
-            oc = mkOc(path, image, memberSize, archiveMemberName
-#ifndef USE_MMAP
-#ifdef darwin_HOST_OS
+            oc = mkOc(path, image, memberSize, rtsFalse, archiveMemberName
+#if !defined(USE_MMAP) && defined(darwin_HOST_OS)
                      , misalignment
 #endif
-#endif
                      );
 
             stgFree(archiveMemberName);
@@ -2892,7 +3162,7 @@ static HsInt loadArchive_ (pathchar *path)
             }
             IF_DEBUG(linker, debugBelch("loadArchive: Found GNU-variant file index\n"));
 #ifdef USE_MMAP
-            gnuFileIndex = mmapForLinker(memberSize + 1, MAP_ANONYMOUS, -1);
+            gnuFileIndex = mmapForLinker(memberSize + 1, MAP_ANONYMOUS, -1, 0);
 #else
             gnuFileIndex = stgMallocBytes(memberSize + 1, "loadArchive(image)");
 #endif
@@ -2942,6 +3212,10 @@ static HsInt loadArchive_ (pathchar *path)
 #endif
     }
 
+#ifdef USE_MMAP
+    m32_allocator_flush(&allocator);
+#endif
+
     IF_DEBUG(linker, debugBelch("loadArchive: done\n"));
     return 1;
 }
@@ -2954,78 +3228,65 @@ HsInt loadArchive (pathchar *path)
    return r;
 }
 
-/* -----------------------------------------------------------------------------
- * Load an obj (populate the global symbol table, but don't resolve yet)
- *
- * Returns: 1 if ok, 0 on error.
- */
-static HsInt loadObj_ (pathchar *path)
+//
+// Load the object file into memory.  This will not be its final resting place,
+// as on 64-bit platforms we need to map its segments into the low 2Gb of the
+// address space, properly aligned.
+//
+static ObjectCode *
+preloadObjectFile (pathchar *path)
 {
-   ObjectCode* oc;
-   char *image;
    int fileSize;
    struct_stat st;
    int r;
-#ifdef USE_MMAP
-   int fd;
-#else
-   FILE *f;
-#  if defined(darwin_HOST_OS)
+   void *image;
+   ObjectCode *oc;
+#if !defined(USE_MMAP) && defined(darwin_HOST_OS)
    int misalignment;
-#  endif
 #endif
-   IF_DEBUG(linker, debugBelch("loadObj %" PATH_FMT "\n", path));
-
-   /* debugBelch("loadObj %s\n", path ); */
-
-   /* Check that we haven't already loaded this object.
-      Ignore requests to load multiple times */
-
-   if (isAlreadyLoaded(path)) {
-       IF_DEBUG(linker,
-                debugBelch("ignoring repeated load of %" PATH_FMT "\n", path));
-       return 1; /* success */
-   }
 
    r = pathstat(path, &st);
    if (r == -1) {
-       IF_DEBUG(linker, debugBelch("File doesn't exist\n"));
-       return 0;
+       errorBelch("loadObj: %" PATH_FMT ": file doesn't exist", path);
+       return NULL;
    }
 
    fileSize = st.st_size;
 
 #ifdef USE_MMAP
-   /* On many architectures malloc'd memory isn't executable, so we need to use mmap. */
+   int fd;
+
+   /* On many architectures malloc'd memory isn't executable, so we need to use
+    * mmap. */
 
 #if defined(openbsd_HOST_OS)
-   /* coverity[toctou] */
    fd = open(path, O_RDONLY, S_IRUSR);
 #else
-   /* coverity[toctou] */
    fd = open(path, O_RDONLY);
 #endif
    if (fd == -1) {
-      errorBelch("loadObj: can't open `%s'", path);
-      return 0;
+      errorBelch("loadObj: can't open %s", path);
+      return NULL;
    }
 
-   image = mmapForLinker(fileSize, 0, fd);
+   image = mmap(NULL, fileSize, PROT_READ|PROT_WRITE|PROT_EXEC,
+                MAP_PRIVATE, fd, 0);
+       // not 32-bit yet, we'll remap later
    close(fd);
-   if (image == NULL) {
-       return 0;
-   }
 
 #else /* !USE_MMAP */
+   FILE *f;
+
    /* load the image into memory */
    /* coverity[toctou] */
    f = pathopen(path, WSTR("rb"));
    if (!f) {
        errorBelch("loadObj: can't read `%" PATH_FMT "'", path);
-       return 0;
+       return NULL;
    }
 
-#   if defined(mingw32_HOST_OS)
+#  if defined(mingw32_HOST_OS)
+
         // TODO: We would like to use allocateExec here, but allocateExec
         //       cannot currently allocate blocks large enough.
     image = allocateImageAndTrampolines(path, "itself",
@@ -3035,9 +3296,11 @@ static HsInt loadObj_ (pathchar *path)
        fileSize);
     if (image == NULL) {
         fclose(f);
-        return 0;
+        return NULL;
     }
+
 #   elif defined(darwin_HOST_OS)
+
     // In a Mach-O .o file, all sections can and will be misaligned
     // if the total size of the headers is not a multiple of the
     // desired alignment. This is fine for .o files that only serve
@@ -3050,30 +3313,57 @@ static HsInt loadObj_ (pathchar *path)
    misalignment = machoGetMisalignment(f);
    image = stgMallocBytes(fileSize + misalignment, "loadObj(image)");
    image += misalignment;
-#  else
+
+# else /* !defined(mingw32_HOST_OS) */
+
    image = stgMallocBytes(fileSize, "loadObj(image)");
-#  endif
 
-   {
-       int n;
-       n = fread ( image, 1, fileSize, f );
-       fclose(f);
-       if (n != fileSize) {
-           errorBelch("loadObj: error whilst reading `%" PATH_FMT "'", path);
-           stgFree(image);
-           return 0;
-       }
+#endif
+
+   int n;
+   n = fread ( image, 1, fileSize, f );
+   fclose(f);
+   if (n != fileSize) {
+       errorBelch("loadObj: error whilst reading `%" PATH_FMT "'", path);
+       stgFree(image);
+       return NULL;
    }
+
 #endif /* USE_MMAP */
 
-   oc = mkOc(path, image, fileSize, NULL
-#ifndef USE_MMAP
-#ifdef darwin_HOST_OS
+   oc = mkOc(path, image, fileSize, rtsTrue, NULL
+#if !defined(USE_MMAP) && defined(darwin_HOST_OS)
             , misalignment
 #endif
-#endif
             );
 
+   return oc;
+}
+
+/* -----------------------------------------------------------------------------
+ * Load an obj (populate the global symbol table, but don't resolve yet)
+ *
+ * Returns: 1 if ok, 0 on error.
+ */
+static HsInt loadObj_ (pathchar *path)
+{
+   ObjectCode* oc;
+   IF_DEBUG(linker, debugBelch("loadObj %" PATH_FMT "\n", path));
+
+   /* debugBelch("loadObj %s\n", path ); */
+
+   /* Check that we haven't already loaded this object.
+      Ignore requests to load multiple times */
+
+   if (isAlreadyLoaded(path)) {
+       IF_DEBUG(linker,
+                debugBelch("ignoring repeated load of %" PATH_FMT "\n", path));
+       return 1; /* success */
+   }
+
+   oc = preloadObjectFile(path);
+   if (oc == NULL) return 0;
+
    if (! loadOc(oc)) {
        // failed; free everything we've allocated
        removeOcSymbols(oc);
@@ -3095,8 +3385,8 @@ HsInt loadObj (pathchar *path)
    return r;
 }
 
-static HsInt
-loadOc( ObjectCode* oc ) {
+static HsInt loadOc (ObjectCode* oc)
+{
    int r;
 
    IF_DEBUG(linker, debugBelch("loadOc: start\n"));
@@ -3116,20 +3406,22 @@ loadOc( ObjectCode* oc ) {
        return r;
    }
 
-#  if defined(OBJFORMAT_MACHO) && (defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH))
+#if NEED_SYMBOL_EXTRAS
+#  if defined(OBJFORMAT_MACHO)
    r = ocAllocateSymbolExtras_MachO ( oc );
    if (!r) {
        IF_DEBUG(linker, debugBelch("loadOc: ocAllocateSymbolExtras_MachO failed\n"));
        return r;
    }
-#  elif defined(OBJFORMAT_ELF) && (defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH) || defined(arm_HOST_ARCH))
+#  elif defined(OBJFORMAT_ELF)
    r = ocAllocateSymbolExtras_ELF ( oc );
    if (!r) {
        IF_DEBUG(linker, debugBelch("loadOc: ocAllocateSymbolExtras_ELF failed\n"));
        return r;
    }
-#  elif defined(OBJFORMAT_PEi386) && defined(x86_64_HOST_ARCH)
+#  elif defined(OBJFORMAT_PEi386)
    ocAllocateSymbolExtras_PEi386 ( oc );
+#  endif
 #endif
 
    /* build the symbol list for this image */
@@ -3332,18 +3624,23 @@ static void freeProddableBlocks (ObjectCode *oc)
  * Section management.
  */
 static void
-addSection ( ObjectCode* oc, SectionKind kind,
-                         void* start, void* end )
+addSection (Section *s, SectionKind kind, SectionAlloc alloc,
+            void* start, StgWord size, StgWord mapped_offset,
+            void* mapped_start, StgWord mapped_size)
 {
-   Section* s   = stgMallocBytes(sizeof(Section), "addSection");
-   s->start     = start;
-   s->end       = end;
-   s->kind      = kind;
-   s->next      = oc->sections;
-   oc->sections = s;
-
-   IF_DEBUG(linker, debugBelch("addSection: %p-%p (size %lld), kind %d\n",
-                               start, ((char*)end)-1, ((long long)(size_t)end) - ((long long)(size_t)start) + 1, kind ));
+   s->start        = start;     /* actual start of section in memory */
+   s->size         = size;      /* actual size of section in memory */
+   s->kind         = kind;
+   s->alloc        = alloc;
+   s->mapped_offset = mapped_offset; /* offset from the image of mapped_start */
+
+   s->mapped_start = mapped_start; /* start of mmap() block */
+   s->mapped_size  = mapped_size;  /* size of mmap() block */
+
+   IF_DEBUG(linker,
+            debugBelch("addSection: %p-%p (size %" FMT_Word "), kind %d\n",
+                       start, (void*)((StgWord)start + size),
+                       size, kind ));
 }
 
 
@@ -3355,7 +3652,7 @@ addSection ( ObjectCode* oc, SectionKind kind,
  * them right next to the object code itself.
  */
 
-#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH) || defined(arm_HOST_ARCH)
+#if NEED_SYMBOL_EXTRAS
 #if !defined(x86_64_HOST_ARCH) || !defined(mingw32_HOST_OS)
 
 /*
@@ -3379,63 +3676,55 @@ addSection ( ObjectCode* oc, SectionKind kind,
 
 static int ocAllocateSymbolExtras( ObjectCode* oc, int count, int first )
 {
-#ifdef USE_MMAP
-  int pagesize, n, m;
-#endif
-  int aligned;
+  StgWord n;
 #ifndef USE_MMAP
   int misalignment = 0;
 #ifdef darwin_HOST_OS
-  misalignment = oc->misalignment;
+  int aligned;
 #endif
 #endif
 
+#ifdef USE_MMAP
+  if (USE_CONTIGUOUS_MMAP)
+  {
+      n = roundUpToPage(oc->fileSize);
+
+      /* Keep image and symbol_extras contiguous */
+      void *new = mmapForLinker(n + (sizeof(SymbolExtra) * count),
+                                MAP_ANONYMOUS, -1, 0);
+      if (new)
+      {
+          memcpy(new, oc->image, oc->fileSize);
+          if (oc->imageMapped) {
+              munmap(oc->image, n);
+          }
+          oc->image = new;
+          oc->imageMapped = rtsTrue;
+          oc->fileSize = n + (sizeof(SymbolExtra) * count);
+          oc->symbol_extras = (SymbolExtra *) (oc->image + n);
+      }
+      else {
+          oc->symbol_extras = NULL;
+          return 0;
+      }
+  }
+  else
+#endif
+
   if( count > 0 )
   {
+#ifdef USE_MMAP
+    n = roundUpToPage(oc->fileSize);
+
+    oc->symbol_extras = m32_alloc(&allocator,
+                                  sizeof(SymbolExtra) * count, 8);
+    if (oc->symbol_extras == NULL) return 0;
+#else
     // round up to the nearest 4
     aligned = (oc->fileSize + 3) & ~3;
 
-#ifdef USE_MMAP
-    pagesize = getpagesize();
-    n = ROUND_UP( oc->fileSize, pagesize );
-    m = ROUND_UP( aligned + sizeof (SymbolExtra) * count, pagesize );
+    misalignment = oc->misalignment;
 
-    /* we try to use spare space at the end of the last page of the
-     * image for the jump islands, but if there isn't enough space
-     * then we have to map some (anonymously, remembering MAP_32BIT).
-     */
-    if( m > n ) // we need to allocate more pages
-    {
-        if (USE_CONTIGUOUS_MMAP)
-        {
-            /* Keep image and symbol_extras contiguous */
-            void *new = mmapForLinker(n + (sizeof(SymbolExtra) * count),
-                                  MAP_ANONYMOUS, -1);
-            if (new)
-            {
-                memcpy(new, oc->image, oc->fileSize);
-                munmap(oc->image, n);
-                oc->image = new;
-                oc->fileSize = n + (sizeof(SymbolExtra) * count);
-                oc->symbol_extras = (SymbolExtra *) (oc->image + n);
-            }
-            else {
-                oc->symbol_extras = NULL;
-                return 0;
-            }
-        }
-        else
-        {
-            oc->symbol_extras = mmapForLinker(sizeof(SymbolExtra) * count,
-                                          MAP_ANONYMOUS, -1);
-            if (oc->symbol_extras == NULL) return 0;
-        }
-    }
-    else
-    {
-        oc->symbol_extras = (SymbolExtra *) (oc->image + aligned);
-    }
-#else
     oc->image -= misalignment;
     oc->image = stgReallocBytes( oc->image,
                                  misalignment +
@@ -3445,11 +3734,11 @@ static int ocAllocateSymbolExtras( ObjectCode* oc, int count, int first )
 
     oc->symbol_extras = (SymbolExtra *) (oc->image + aligned);
 #endif /* USE_MMAP */
+  }
 
-    memset( oc->symbol_extras, 0, sizeof (SymbolExtra) * count );
+  if (oc->symbol_extras != NULL) {
+      memset( oc->symbol_extras, 0, sizeof (SymbolExtra) * count );
   }
-  else
-    oc->symbol_extras = NULL;
 
   oc->first_symbol_extra = first;
   oc->n_symbol_extras = count;
@@ -3458,7 +3747,7 @@ static int ocAllocateSymbolExtras( ObjectCode* oc, int count, int first )
 }
 
 #endif
-#endif // defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH) || defined(arm_HOST_ARCH)
+#endif // NEED_SYMBOL_EXTRAS
 
 #if defined(arm_HOST_ARCH)
 
@@ -4360,6 +4649,14 @@ ocGetNames_PEi386 ( ObjectCode* oc )
       /* debugBelch("BSS anon section at 0x%x\n", zspace); */
    }
 
+   Section *sections;
+   sections = (Section*)stgCallocBytes(
+       sizeof(Section),
+       hdr->NumberOfSections + 1, /* +1 for the global BSS section see below */
+       "ocGetNames_ELF(sections)");
+   oc->sections = sections;
+   oc->n_sections = hdr->NumberOfSections + 1;
+
    /* Copy section information into the ObjectCode. */
 
    for (i = 0; i < hdr->NumberOfSections; i++) {
@@ -4403,8 +4700,8 @@ ocGetNames_PEi386 ( ObjectCode* oc )
       end   = start + sz - 1;
 
       if (kind != SECTIONKIND_OTHER && end >= start) {
-         addSection(oc, kind, start, end);
-         addProddableBlock(oc, start, end - start + 1);
+          addSection(&sections[i], kind, SECTION_NOMEM, start, sz, 0, 0, 0);
+          addProddableBlock(oc, start, end - start + 1);
       }
 
       stgFree(secname);
@@ -4413,11 +4710,36 @@ ocGetNames_PEi386 ( ObjectCode* oc )
    /* Copy exported symbols into the ObjectCode. */
 
    oc->n_symbols = hdr->NumberOfSymbols;
-   oc->symbols   = stgMallocBytes(oc->n_symbols * sizeof(char*),
+   oc->symbols   = stgCallocBytes(sizeof(char*), oc->n_symbols,
                                   "ocGetNames_PEi386(oc->symbols)");
-   /* Call me paranoid; I don't care. */
-   for (i = 0; i < oc->n_symbols; i++)
-      oc->symbols[i] = NULL;
+
+   /* Work out the size of the global BSS section */
+   StgWord globalBssSize = 0;
+   for (i=0; i < (int)hdr->NumberOfSymbols; i++) {
+      COFF_symbol* symtab_i;
+       symtab_i = (COFF_symbol*)
+           myindex ( sizeof_COFF_symbol, symtab, i );
+       if (symtab_i->SectionNumber == MYIMAGE_SYM_UNDEFINED
+           && symtab_i->Value > 0) {
+           globalBssSize += symtab_i->Value;
+       }
+       i += symtab_i->NumberOfAuxSymbols;
+   }
+
+   /* Allocate BSS space */
+   void *bss = NULL;
+   if (globalBssSize > 0) {
+       bss = stgCallocBytes(1, globalBssSize,
+                            "ocGetNames_PEi386(non-anonymous bss)");
+       addSection(&sections[oc->n_sections-1],
+                  SECTIONKIND_RWDATA, SECTION_MALLOC,
+                  bss, globalBssSize, 0, 0, 0);
+       debugBelch("bss @ %p %" FMT_Word "\n", bss, globalBssSize);
+       addProddableBlock(oc, bss, globalBssSize);
+   } else {
+       addSection(&sections[oc->n_sections-1],
+                  SECTIONKIND_OTHER, SECTION_NOMEM, NULL, 0, 0, 0, 0);
+   }
 
    for (i = 0; i < oc->n_symbols; i++) {
       COFF_symbol* symtab_i;
@@ -4454,13 +4776,10 @@ ocGetNames_PEi386 ( ObjectCode* oc )
       if (symtab_i->SectionNumber == MYIMAGE_SYM_UNDEFINED
           && symtab_i->Value > 0) {
          /* This symbol isn't in any section at all, ie, global bss.
-            Allocate zeroed space for it. */
-         addr = stgCallocBytes(1, symtab_i->Value,
-                               "ocGetNames_PEi386(non-anonymous bss)");
-         addSection(oc, SECTIONKIND_RWDATA, addr,
-                        ((UChar*)addr) + symtab_i->Value - 1);
-         addProddableBlock(oc, addr, symtab_i->Value);
-         /* debugBelch("BSS      section at 0x%x\n", addr); */
+            Allocate zeroed space for it from the BSS section */
+          addr = bss;
+          bss = (void *)((StgWord)bss + (StgWord)symtab_i->Value);
+          debugBelch("bss symbol @ %p %u\n", addr, symtab_i->Value);
       }
 
       if (addr != NULL ) {
@@ -5056,7 +5375,6 @@ PLTSize(void)
 }
 #endif
 
-
 /*
  * Generic ELF functions
  */
@@ -5286,6 +5604,9 @@ ocVerifyImage_ELF ( ObjectCode* oc )
    return 1;
 }
 
+/* Figure out what kind of section it is.  Logic derived from
+   Figure 1.14 ("Special Sections") of the ELF document
+   ("Portable Formats Specification, Version 1.1"). */
 static int getSectionKind_ELF( Elf_Shdr *hdr, int *is_bss )
 {
     *is_bss = FALSE;
@@ -5324,51 +5645,109 @@ static int getSectionKind_ELF( Elf_Shdr *hdr, int *is_bss )
     return SECTIONKIND_OTHER;
 }
 
+static void *
+mapObjectFileSection (int fd, Elf_Word offset, Elf_Word size,
+                      void **mapped_start, StgWord *mapped_size,
+                      StgWord *mapped_offset)
+{
+    void *p;
+    StgWord pageOffset, pageSize;
+
+    pageOffset = roundDownToPage(offset);
+    pageSize = roundUpToPage(offset-pageOffset+size);
+    p = mmapForLinker(pageSize, 0, fd, pageOffset);
+    if (p == NULL) return NULL;
+    *mapped_size = pageSize;
+    *mapped_offset = pageOffset;
+    *mapped_start = p;
+    return (void*)((StgWord)p + offset - pageOffset);
+}
 
 static int
 ocGetNames_ELF ( ObjectCode* oc )
 {
-   int i, j, nent;
+   int i, j, nent, result, fd = -1;
    Elf_Sym* stab;
 
    char*     ehdrC    = (char*)(oc->image);
    Elf_Ehdr* ehdr     = (Elf_Ehdr*)ehdrC;
    char*     strtab;
    Elf_Shdr* shdr     = (Elf_Shdr*) (ehdrC + ehdr->e_shoff);
+   Section * sections;
 
    ASSERT(symhash != NULL);
 
+   sections = (Section*)stgCallocBytes(sizeof(Section), ehdr->e_shnum,
+                                       "ocGetNames_ELF(sections)");
+   oc->sections = sections;
+   oc->n_sections = ehdr->e_shnum;
+
+
+   if (oc->imageMapped) {
+#if defined(openbsd_HOST_OS)
+       fd = open(oc->fileName, O_RDONLY, S_IRUSR);
+#else
+       fd = open(oc->fileName, O_RDONLY);
+#endif
+       if (fd == -1) {
+           errorBelch("loadObj: can't open %" PATH_FMT, oc->fileName);
+           return 0;
+       }
+   }
+
    for (i = 0; i < ehdr->e_shnum; i++) {
-      /* Figure out what kind of section it is.  Logic derived from
-         Figure 1.14 ("Special Sections") of the ELF document
-         ("Portable Formats Specification, Version 1.1"). */
       int         is_bss = FALSE;
       SectionKind kind   = getSectionKind_ELF(&shdr[i], &is_bss);
+      SectionAlloc alloc = SECTION_NOMEM;
+      void *start = NULL, *mapped_start = NULL;
+      StgWord mapped_size = 0, mapped_offset = 0;
+      StgWord size = shdr[i].sh_size;
+      StgWord offset = shdr[i].sh_offset;
 
-      if (is_bss && shdr[i].sh_size > 0) {
+      if (is_bss && size > 0) {
          /* 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.  */
-         char* zspace = stgCallocBytes(1, shdr[i].sh_size,
-                                       "ocGetNames_ELF(BSS)");
-         shdr[i].sh_offset = ((char*)zspace) - ((char*)ehdrC);
+          alloc = SECTION_MALLOC;
+          start = stgCallocBytes(1, size, "ocGetNames_ELF(BSS)");
+          mapped_start = start;
          /*
          debugBelch("BSS section at 0x%x, size %d\n",
                          zspace, shdr[i].sh_size);
          */
       }
 
-      /* fill in the section info */
-      if (kind != SECTIONKIND_OTHER && shdr[i].sh_size > 0) {
-         addProddableBlock(oc, ehdrC + shdr[i].sh_offset, shdr[i].sh_size);
-         addSection(oc, kind, ehdrC + shdr[i].sh_offset,
-                        ehdrC + shdr[i].sh_offset + shdr[i].sh_size - 1);
+      else if (kind != SECTIONKIND_OTHER && size > 0) {
+          if (USE_CONTIGUOUS_MMAP) {
+              // already mapped.
+              start = oc->image + offset;
+              alloc = SECTION_NOMEM;
+          }
+          // use the m32 allocator if either the image is not mapped
+          // (i.e. we cannot map the secions separately), or if the section
+          // size is small.
+          else if (!oc->imageMapped || size < getPageSize() / 3) {
+              start = m32_alloc(&allocator, size, 8);
+              if (start == NULL) goto fail;
+              memcpy(start, oc->image + offset, size);
+              alloc = SECTION_M32;
+          } else {
+              start = mapObjectFileSection(fd, offset, size,
+                                           &mapped_start, &mapped_size,
+                                           &mapped_offset);
+              if (start == NULL) goto fail;
+              alloc = SECTION_MMAP;
+          }
+          addProddableBlock(oc, ehdrC + offset, size);
       }
 
+      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 + shdr[i].sh_offset);
+      stab = (Elf_Sym*) (ehdrC + offset);
       strtab = ehdrC + shdr[shdr[i].sh_link].sh_offset;
       nent = shdr[i].sh_size / sizeof(Elf_Sym);
 
@@ -5427,7 +5806,8 @@ ocGetNames_ELF ( ObjectCode* oc )
                                stab[j].st_size, stab[j].st_value, nm);
             }
             */
-            ad = ehdrC + shdr[ secno ].sh_offset + stab[j].st_value;
+            ad = (void*)((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;
@@ -5456,7 +5836,7 @@ ocGetNames_ELF ( ObjectCode* oc )
             } else {
                 if (! ghciInsertSymbolTable(oc->fileName, symhash,
                                             nm, ad, isWeak, oc)) {
-                    return 0;
+                    goto fail;
                 }
                 oc->symbols[j] = nm;
             }
@@ -5479,7 +5859,16 @@ ocGetNames_ELF ( ObjectCode* oc )
       }
    }
 
-   return 1;
+   result = 1;
+   goto end;
+
+fail:
+   result = 0;
+   goto end;
+
+end:
+   if (fd >= 0) close(fd);
+   return result;
 }
 
 /* Do ELF relocations which lack an explicit addend.  All x86-linux
@@ -5501,18 +5890,14 @@ do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC,
 
    stab  = (Elf_Sym*) (ehdrC + shdr[ symtab_shndx ].sh_offset);
    strtab= (char*)    (ehdrC + shdr[ strtab_shndx ].sh_offset);
-   targ  = (Elf_Word*)(ehdrC + shdr[ target_shndx ].sh_offset);
+   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 ));
 
    /* Skip sections that we're not interested in. */
-   {
-       int is_bss;
-       SectionKind kind = getSectionKind_ELF(&shdr[target_shndx], &is_bss);
-       if (kind == SECTIONKIND_OTHER) {
+   if (oc->sections[target_shndx].kind == SECTIONKIND_OTHER) {
            IF_DEBUG(linker,debugBelch( "skipping (target section not loaded)"));
            return 1;
-       }
    }
 
    for (j = 0; j < nent; j++) {
@@ -5545,9 +5930,8 @@ do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC,
             /* Yes, so we can get the address directly from the ELF symbol
                table. */
             symbol = sym.st_name==0 ? "(noname)" : strtab+sym.st_name;
-            S = (Elf_Addr)
-                (ehdrC + shdr[ sym.st_shndx ].sh_offset
-                       + stab[ELF_R_SYM(info)].st_value);
+            S = (Elf_Addr)oc->sections[sym.st_shndx].start +
+                stab[ELF_R_SYM(info)].st_value;
 
          } else {
             symbol = strtab + sym.st_name;
@@ -5816,11 +6200,17 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
    strtab= (char*)    (ehdrC + shdr[ strtab_shndx ].sh_offset);
 #if defined(DEBUG) || defined(sparc_HOST_ARCH) || defined(ia64_HOST_ARCH) || defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
    /* This #ifdef only serves to avoid set-but-not-used warnings */
-   targ  = (Elf_Addr) (ehdrC + shdr[ target_shndx ].sh_offset);
+   targ  = (Elf_Addr) oc->sections[target_shndx].start;
 #endif
    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;
+   }
+
    for (j = 0; j < nent; j++) {
 #if defined(DEBUG) || defined(sparc_HOST_ARCH) || defined(ia64_HOST_ARCH) || defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
       /* This #ifdef only serves to avoid unused-var warnings. */
@@ -5854,9 +6244,8 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
             /* Yes, so we can get the address directly from the ELF symbol
                table. */
             symbol = sym.st_name==0 ? "(noname)" : strtab+sym.st_name;
-            S = (Elf_Addr)
-                (ehdrC + shdr[ sym.st_shndx ].sh_offset
-                       + stab[ELF_R_SYM(info)].st_value);
+            S = (Elf_Addr)oc->sections[sym.st_shndx].start
+                + stab[ELF_R_SYM(info)].st_value;
 #ifdef ELF_FUNCTION_DESC
             /* Make a function descriptor for this function */
             if (S && ELF_ST_TYPE(sym.st_info) == STT_FUNC) {
@@ -6176,12 +6565,12 @@ static int ocRunInit_ELF( ObjectCode *oc )
       SectionKind kind = getSectionKind_ELF(&shdr[i], &is_bss);
       if (kind == SECTIONKIND_CODE_OR_RODATA
        && 0 == memcmp(".init", sh_strtab + shdr[i].sh_name, 5)) {
-         init_t init_f = (init_t)(ehdrC + shdr[i].sh_offset);
-         init_f(argc, argv, envv);
+          init_t init_f = (init_t)(oc->sections[i].start);
+          init_f(argc, argv, envv);
       }
 
       if (kind == SECTIONKIND_INIT_ARRAY) {
-         char *init_startC = ehdrC + shdr[i].sh_offset;
+          char *init_startC = oc->sections[i].start;
          init_start = (init_t*)init_startC;
          init_end = (init_t*)(init_startC + shdr[i].sh_size);
          for (init = init_start; init < init_end; init++) {
@@ -6193,7 +6582,7 @@ static int ocRunInit_ELF( ObjectCode *oc )
       // SECTIONKIND_RWDATA; but allowing RODATA seems harmless enough.
       if ((kind == SECTIONKIND_RWDATA || kind == SECTIONKIND_CODE_OR_RODATA)
        && 0 == memcmp(".ctors", sh_strtab + shdr[i].sh_name, 6)) {
-         char *init_startC = ehdrC + shdr[i].sh_offset;
+          char *init_startC = oc->sections[i].start;
          init_start = (init_t*)init_startC;
          init_end = (init_t*)(init_startC + shdr[i].sh_size);
          // ctors run in reverse
@@ -6211,7 +6600,7 @@ static int ocRunInit_ELF( ObjectCode *oc )
  * PowerPC & X86_64 ELF specifics
  */
 
-#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH) || defined(arm_HOST_ARCH)
+#if NEED_SYMBOL_EXTRAS
 
 static int ocAllocateSymbolExtras_ELF( ObjectCode *oc )
 {
@@ -6247,7 +6636,7 @@ static int ocAllocateSymbolExtras_ELF( ObjectCode *oc )
   return ocAllocateSymbolExtras( oc, shdr[i].sh_size / sizeof( Elf_Sym ), 0 );
 }
 
-#endif /* powerpc */
+#endif /* NEED_SYMBOL_EXTRAS */
 
 #endif /* ELF */
 
@@ -7088,6 +7477,14 @@ ocGetNames_MachO(ObjectCode* oc)
         barf("ocGetNames_MachO: no segment load command");
     }
 
+    Section *secArray;
+    secArray = (Section*)stgCallocBytes(
+         sizeof(Section),
+         segLC->nsects,
+         "ocGetNames_MachO(sections)");
+   oc->sections = secArray;
+   oc->n_sections = segLC->nsects;
+
     IF_DEBUG(linker, debugBelch("ocGetNames_MachO: will load %d sections\n", segLC->nsects));
     for(i=0;i<segLC->nsects;i++)
     {
@@ -7101,7 +7498,7 @@ ocGetNames_MachO(ObjectCode* oc)
         if((sections[i].flags & SECTION_TYPE) == S_ZEROFILL)
         {
 #ifdef USE_MMAP
-            char * zeroFillArea = mmapForLinker(sections[i].size, MAP_ANONYMOUS, -1);
+            char * zeroFillArea = mmapForLinker(sections[i].size, MAP_ANONYMOUS, -1, 0);
             if (zeroFillArea == NULL) return 0;
             memset(zeroFillArea, 0, sections[i].size);
 #else
@@ -7124,11 +7521,11 @@ ocGetNames_MachO(ObjectCode* oc)
             kind = SECTIONKIND_RWDATA;
         }
 
-        if (kind != SECTIONKIND_OTHER) {
-            addSection(oc, kind,
-                (void*) (image + sections[i].offset),
-                (void*) (image + sections[i].offset + sections[i].size));
-        }
+        addSection(&secArray[i], kind, SECTION_NOMEM,
+                   (void *)(image + sections[i].offset),
+                   sections[i].size,
+                   0, 0, 0);
+
         addProddableBlock(oc,
                           (void *) (image + sections[i].offset),
                                         sections[i].size);
index 4eab5de..f5f17d2 100644 (file)
@@ -27,16 +27,31 @@ typedef
           SECTIONKIND_NOINFOAVAIL } 
    SectionKind;
 
-typedef 
+typedef
+   enum { SECTION_NOMEM,
+          SECTION_M32,
+          SECTION_MMAP,
+          SECTION_MALLOC,
+        }
+   SectionAlloc;
+
+typedef
    struct _Section { 
-      void* start; 
-      void* end; 
+      void* start;                /* actual start of section in memory */
+      StgWord size;               /* actual size of section in memory */
       SectionKind kind;
-      struct _Section* next;
-   } 
+      SectionAlloc alloc;
+
+      /*
+       * The following fields are relevant for SECTION_MMAP sections only
+       */
+      StgWord mapped_offset;      /* offset from the image of mapped_start */
+      void* mapped_start;         /* start of mmap() block */
+      StgWord mapped_size;        /* size of mmap() block */
+   }
    Section;
 
-typedef 
+typedef
    struct _ProddableBlock {
       void* start;
       int   size;
@@ -55,6 +70,10 @@ typedef struct ForeignExportStablePtr_ {
     struct ForeignExportStablePtr_ *next;
 } ForeignExportStablePtr;
 
+#if powerpc_HOST_ARCH || x86_64_HOST_ARCH || arm_HOST_ARCH
+#define NEED_SYMBOL_EXTRAS 1
+#endif
+
 /* Jump Islands are sniplets of machine code required for relative
  * address relocations on the PowerPC, x86_64 and ARM.
  */
@@ -80,7 +99,7 @@ typedef struct {
 typedef struct _ObjectCode {
     OStatus    status;
     pathchar  *fileName;
-    int        fileSize;
+    int        fileSize;     /* also mapped image size when using mmap() */
     char*      formatName;            /* eg "ELF32", "DLL", "COFF", etc. */
 
     /* If this object is a member of an archive, archiveMemberName is
@@ -95,8 +114,10 @@ typedef struct _ObjectCode {
     char**     symbols;
     int        n_symbols;
 
-    /* ptr to malloc'd lump of memory holding the obj file */
+    /* ptr to mem containing the object file image */
     char*      image;
+    /* non-zero if the object file was mmap'd, otherwise malloc'd */
+    int        imageMapped;
 
     /* flag used when deciding whether to unload an object file */
     int        referenced;
@@ -109,6 +130,7 @@ typedef struct _ObjectCode {
 
     /* The section-kind entries for this object module.  Linked
        list. */
+    int n_sections;
     Section* sections;
 
     /* Allow a chain of these things */
@@ -125,7 +147,7 @@ typedef struct _ObjectCode {
     unsigned int pltIndex;
 #endif
 
-#if powerpc_HOST_ARCH || x86_64_HOST_ARCH || arm_HOST_ARCH
+#if NEED_SYMBOL_EXTRAS
     SymbolExtra    *symbol_extras;
     unsigned long   first_symbol_extra;
     unsigned long   n_symbol_extras;
index e823f88..7404de3 100644 (file)
@@ -43,7 +43,7 @@ test('ghcilink006',
 test('T3333',
      [extra_clean(['T3333.o']),
      unless(doing_ghci, skip),
-     unless(opsys('linux') or ghci_dynamic(), expect_broken(3333))],
+     unless(opsys('linux') or opsys('darwin') or ghci_dynamic(), expect_broken(3333))],
      run_command,
      ['$MAKE -s --no-print-directory T3333'])
 
index 715eabd..264c95d 100644 (file)
@@ -51,12 +51,12 @@ int main (int argc, char *argv[])
     for (i=0; i < ITERATIONS; i++) {
         r = loadObj(obj);
         if (!r) {
-            debugBelch("loadObj(%s) failed", obj);
+            debugBelch("loadObj(%s) failed\n", obj);
             continue;
         }
         r = resolveObjs();
         if (!r) {
-            debugBelch("resolveObjs failed");
+            debugBelch("resolveObjs failed\n");
             unloadObj(obj);
             continue;
         }