diff options
-rw-r--r-- | rts/CheckUnload.c | 14 | ||||
-rw-r--r-- | rts/Linker.c | 893 | ||||
-rw-r--r-- | rts/LinkerInternals.h | 40 | ||||
-rw-r--r-- | testsuite/tests/ghci/linking/all.T | 2 | ||||
-rw-r--r-- | testsuite/tests/rts/linker_error.c | 4 |
5 files changed, 689 insertions, 264 deletions
diff --git a/rts/CheckUnload.c b/rts/CheckUnload.c index 7802754445..c75673847e 100644 --- a/rts/CheckUnload.c +++ b/rts/CheckUnload.c @@ -40,15 +40,21 @@ 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; + } + } } } } diff --git a/rts/Linker.c b/rts/Linker.c index 355b33a769..be5b3b815f 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -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(§ions[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(§ions[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(§ions[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(§ions[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); diff --git a/rts/LinkerInternals.h b/rts/LinkerInternals.h index 4eab5deb45..f5f17d2380 100644 --- a/rts/LinkerInternals.h +++ b/rts/LinkerInternals.h @@ -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; diff --git a/testsuite/tests/ghci/linking/all.T b/testsuite/tests/ghci/linking/all.T index e823f88613..7404de3923 100644 --- a/testsuite/tests/ghci/linking/all.T +++ b/testsuite/tests/ghci/linking/all.T @@ -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']) diff --git a/testsuite/tests/rts/linker_error.c b/testsuite/tests/rts/linker_error.c index 715eabd184..264c95d710 100644 --- a/testsuite/tests/rts/linker_error.c +++ b/testsuite/tests/rts/linker_error.c @@ -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; } |