diff options
author | Ben Gamari <ben@smart-cactus.org> | 2022-02-07 16:21:50 -0500 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2022-02-09 20:43:39 -0500 |
commit | e219ac826b05db833531028e0663f62f12eff010 (patch) | |
tree | c7b133cc689d1068066d356de12a6d1ae57c5e16 /rts/Linker.c | |
parent | 3df06922f03191310ebee0547de1782eeb6bda67 (diff) | |
download | haskell-e219ac826b05db833531028e0663f62f12eff010.tar.gz |
rts: Move mmapForLinker and friends to linker/MMap.c
They are not particularly related to linking.
Diffstat (limited to 'rts/Linker.c')
-rw-r--r-- | rts/Linker.c | 252 |
1 files changed, 1 insertions, 251 deletions
diff --git a/rts/Linker.c b/rts/Linker.c index 8ac529465b..fcba191249 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -31,6 +31,7 @@ #include "linker/M32Alloc.h" #include "linker/CacheFlush.h" #include "linker/SymbolExtras.h" +#include "linker/MMap.h" #include "PathUtils.h" #include "CheckUnload.h" // createOCSectionIndices #include "ReportMemoryMap.h" @@ -172,8 +173,6 @@ Mutex linker_mutex; /* Generic wrapper function to try and Resolve and RunInit oc files */ int ocTryLoad( ObjectCode* oc ); -static void *mmap_32bit_base = LINKER_LOAD_BASE; - static void ghciRemoveSymbolTable(StrHashTable *table, const SymbolName* key, ObjectCode *owner) { @@ -1009,255 +1008,6 @@ resolveSymbolAddr (pathchar* buffer, int size, #endif /* OBJFORMAT_PEi386 */ } -static const char *memoryAccessDescription(MemoryAccess mode) -{ - switch (mode) { - case MEM_NO_ACCESS: return "no-access"; - case MEM_READ_ONLY: return "read-only"; - case MEM_READ_WRITE: return "read-write"; - case MEM_READ_EXECUTE: return "read-execute"; - default: barf("invalid MemoryAccess"); - } -} - -#if defined(mingw32_HOST_OS) - -// -// Returns NULL on failure. -// -void * -mmapAnonForLinker (size_t bytes) -{ - return VirtualAlloc(NULL, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); -} - -void -munmapForLinker (void *addr, size_t bytes, const char *caller) -{ - if (VirtualFree(addr, 0, MEM_RELEASE) == 0) { - sysErrorBelch("munmapForLinker: %s: Failed to unmap %zd bytes at %p", - caller, bytes, addr); - } -} - -/** - * Change the allowed access modes of a region of memory previously allocated - * with mmapAnonForLinker. - */ -void -mprotectForLinker(void *start, size_t len, MemoryAccess mode) -{ - DWORD old; - if (len == 0) { - return; - } - DWORD prot; - switch (mode) { - case MEM_NO_ACCESS: prot = PAGE_NOACCESS; break; - case MEM_READ_ONLY: prot = PAGE_READONLY; break; - case MEM_READ_WRITE: prot = PAGE_READWRITE; break; - case MEM_READ_EXECUTE: prot = PAGE_EXECUTE_READ; break; - default: barf("invalid MemoryAccess"); - } - - if (VirtualProtect(start, len, prot, &old) == 0) { - sysErrorBelch("mprotectForLinker: failed to protect %zd bytes at %p as %s", - len, start, memoryAccessDescription(mode)); - ASSERT(false); - } -} - -#elif RTS_LINKER_USE_MMAP -// -// Returns NULL on failure. -// -void * -mmapForLinker (size_t bytes, uint32_t prot, uint32_t flags, int fd, int offset) -{ - void *map_addr = NULL; - void *result; - size_t size; - uint32_t tryMap32Bit = RtsFlags.MiscFlags.linkerAlwaysPic - ? 0 - : TRY_MAP_32BIT; - static uint32_t fixed = 0; - - IF_DEBUG(linker_verbose, debugBelch("mmapForLinker: start\n")); - size = roundUpToPage(bytes); - -#if defined(MAP_LOW_MEM) -mmap_again: -#endif - - if (mmap_32bit_base != NULL) { - map_addr = mmap_32bit_base; - } - - IF_DEBUG(linker_verbose, - debugBelch("mmapForLinker: \tprotection %#0x\n", prot)); - IF_DEBUG(linker_verbose, - debugBelch("mmapForLinker: \tflags %#0x\n", - MAP_PRIVATE | tryMap32Bit | fixed | flags)); - IF_DEBUG(linker_verbose, - debugBelch("mmapForLinker: \tsize %#0zx\n", bytes)); - IF_DEBUG(linker_verbose, - debugBelch("mmapForLinker: \tmap_addr %p\n", map_addr)); - - result = mmap(map_addr, size, prot, - MAP_PRIVATE|tryMap32Bit|fixed|flags, fd, offset); - - if (result == MAP_FAILED) { - reportMemoryMap(); - sysErrorBelch("mmap %" FMT_Word " bytes at %p",(W_)size,map_addr); - errorBelch("Try specifying an address with +RTS -xm<addr> -RTS"); - return NULL; - } - -#if defined(MAP_LOW_MEM) - if (RtsFlags.MiscFlags.linkerAlwaysPic) { - /* make no attempt at mapping low memory if we are assuming PIC */ - } else if (mmap_32bit_base != NULL) { - if (result != map_addr) { - if ((W_)result > 0x80000000) { - // oops, we were given memory over 2Gb - munmap(result,size); -#if defined(freebsd_HOST_OS) || \ - defined(kfreebsdgnu_HOST_OS) || \ - defined(dragonfly_HOST_OS) - // Some platforms require MAP_FIXED. This is normally - // a bad idea, because MAP_FIXED will overwrite - // existing mappings. - fixed = MAP_FIXED; - goto mmap_again; -#else - reportMemoryMap(); - errorBelch("mmapForLinker: failed to mmap() memory below 2Gb; " - "asked for %lu bytes at %p. " - "Try specifying an address with +RTS -xm<addr> -RTS", - size, map_addr); - return NULL; -#endif - } else { - // hmm, we were given memory somewhere else, but it's - // still under 2Gb so we can use it. - } - } - } else { - if ((W_)result > 0x80000000) { - // oops, we were given memory over 2Gb - // ... try allocating memory somewhere else?; - debugTrace(DEBUG_linker, - "MAP_32BIT didn't work; gave us %lu bytes at 0x%p", - bytes, result); - munmap(result, size); - - // Set a base address and try again... (guess: 1Gb) - mmap_32bit_base = (void*)0x40000000; - goto mmap_again; - } - } -#elif (defined(aarch64_TARGET_ARCH) || defined(aarch64_HOST_ARCH)) - // for aarch64 we need to make sure we stay within 4GB of the - // mmap_32bit_base, and we also do not want to update it. - if (result != map_addr) { - // upper limit 4GB - size of the object file - 1mb wiggle room. - if(llabs((uintptr_t)result - (uintptr_t)&stg_upd_frame_info) > (2<<32) - size - (2<<20)) { - // not within range :( - debugTrace(DEBUG_linker, - "MAP_32BIT didn't work; gave us %lu bytes at 0x%p", - bytes, result); - munmap(result, size); - // TODO: some abort/mmap_32bit_base recomputation based on - // if mmap_32bit_base is changed, or still at stg_upd_frame_info - goto mmap_again; - } - } -#endif - - if (mmap_32bit_base != NULL) { - // Next time, ask for memory right after our new mapping to maximize the - // chance that we get low memory. - mmap_32bit_base = (void*) ((uintptr_t)result + size); - } - - IF_DEBUG(linker_verbose, - debugBelch("mmapForLinker: mapped %" FMT_Word - " bytes starting at %p\n", (W_)size, result)); - IF_DEBUG(linker_verbose, - debugBelch("mmapForLinker: done\n")); - - return result; -} - -/* - * Map read/write pages in low memory. Returns NULL on failure. - */ -void * -mmapAnonForLinker (size_t bytes) -{ - return mmapForLinker (bytes, PROT_READ|PROT_WRITE, MAP_ANONYMOUS, -1, 0); -} - -void munmapForLinker (void *addr, size_t bytes, const char *caller) -{ - int r = munmap(addr, bytes); - if (r == -1) { - // Should we abort here? - sysErrorBelch("munmap: %s", caller); - } -} - -/* Note [Memory protection in the linker] - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * For many years the linker would simply map all of its memory - * with PROT_READ|PROT_WRITE|PROT_EXEC. However operating systems have been - * becoming increasingly reluctant to accept this practice (e.g. #17353, - * #12657) and for good reason: writable code is ripe for exploitation. - * - * Consequently mmapForLinker now maps its memory with PROT_READ|PROT_WRITE. - * After the linker has finished filling/relocating the mapping it must then - * call mprotectForLinker on the sections of the mapping which - * contain executable code. - * - * Note that the m32 allocator handles protection of its allocations. For this - * reason the caller to m32_alloc() must tell the allocator whether the - * allocation needs to be executable. The caller must then ensure that they - * call m32_allocator_flush() after they are finished filling the region, which - * will cause the allocator to change the protection bits to - * PROT_READ|PROT_EXEC. - * - */ - -/* - * Mark an portion of a mapping previously reserved by mmapForLinker - * as executable (but not writable). - */ -void mprotectForLinker(void *start, size_t len, MemoryAccess mode) -{ - if (len == 0) { - return; - } - IF_DEBUG(linker_verbose, - debugBelch("mprotectForLinker: protecting %" FMT_Word - " bytes starting at %p as %s\n", - (W_)len, start, memoryAccessDescription(mode))); - - int prot; - switch (mode) { - case MEM_NO_ACCESS: prot = 0; break; - case MEM_READ_ONLY: prot = PROT_READ; break; - case MEM_READ_WRITE: prot = PROT_READ | PROT_WRITE; break; - case MEM_READ_EXECUTE: prot = PROT_READ | PROT_EXEC; break; - default: barf("invalid MemoryAccess"); - } - - if (mprotect(start, len, prot) == -1) { - sysErrorBelch("mprotectForLinker: failed to protect %zd bytes at %p as %s", - len, start, memoryAccessDescription(mode)); - } -} -#endif - /* * Remove symbols from the symbol table, and free oc->symbols. * This operation is idempotent. |