From 11f6f411b4a15b333423715b41b498f5f7745933 Mon Sep 17 00:00:00 2001 From: Simon Marlow Date: Mon, 17 Nov 2008 12:05:56 +0000 Subject: [PATCH] Attempt to fix #2512 and #2063; add +RTS -xm
-RTS option On x86_64, the RTS needs to allocate memory in the low 2Gb of the address space. On Linux we can do this with MAP_32BIT, but sometimes this doesn't work (#2512) and other OSs don't support it at all (#2063). So to work around this: - Try MAP_32BIT first, if available. - Otherwise, try allocating memory from a fixed address (by default 1Gb) - We now provide an option to configure the address to allocate from. This allows a workaround on machines where the default breaks, and also provides a way for people to test workarounds that we can incorporate in future releases. --- docs/users_guide/runtime_control.xml | 29 +++++++ includes/RtsFlags.h | 2 + rts/Linker.c | 148 ++++++++++++++++++++++++++-------- rts/RtsFlags.c | 22 ++++- 4 files changed, 166 insertions(+), 35 deletions(-) diff --git a/docs/users_guide/runtime_control.xml b/docs/users_guide/runtime_control.xml index 5039526..e0dd420 100644 --- a/docs/users_guide/runtime_control.xml +++ b/docs/users_guide/runtime_control.xml @@ -130,6 +130,35 @@ own signal handlers. + + + + RTS + option + + + WARNING: this option is for working around memory + allocation problems only. Do not use unless GHCi fails + with a message like “failed to mmap() memory below 2Gb”. If you need to use this option to get GHCi working + on your machine, please file a bug. + + + + On 64-bit machines, the RTS needs to allocate memory in the + low 2Gb of the address space. Support for this across + different operating systems is patchy, and sometimes fails. + This option is there to give the RTS a hint about where it + should be able to allocate memory in the low 2Gb of the + address space. For example, +RTS -xm20000000 + -RTS would hint that the RTS should allocate + starting at the 0.5Gb mark. The default is to use the OS's + built-in support for allocating memory in the low 2Gb if + available (e.g. mmap + with MAP_32BIT on Linux), or + otherwise -xm40000000. + + + diff --git a/includes/RtsFlags.h b/includes/RtsFlags.h index 11133ef..55b00bb 100644 --- a/includes/RtsFlags.h +++ b/includes/RtsFlags.h @@ -121,6 +121,8 @@ struct CONCURRENT_FLAGS { struct MISC_FLAGS { int tickInterval; /* in milliseconds */ rtsBool install_signal_handlers; + StgWord linkerMemBase; /* address to ask the OS for memory + * for the linker, NULL ==> off */ }; #ifdef PAR diff --git a/rts/Linker.c b/rts/Linker.c index cdeb569..357023d 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -28,6 +28,7 @@ #include "Sparks.h" #include "RtsTypeable.h" #include "Timer.h" +#include "Trace.h" #ifdef HAVE_SYS_TYPES_H #include @@ -168,6 +169,50 @@ static void machoInitSymbolsWithoutUnderscore( void ); */ #define X86_64_ELF_NONPIC_HACK 1 +/* Link objects into the lower 2Gb on x86_64. GHC assumes the + * small memory model on this architecture (see gcc docs, + * -mcmodel=small). + * + * MAP_32BIT not available on OpenBSD/amd64 + */ +#if defined(x86_64_HOST_ARCH) && defined(MAP_32BIT) +#define TRY_MAP_32BIT MAP_32BIT +#else +#define TRY_MAP_32BIT 0 +#endif + +/* + * Due to the small memory model (see above), on x86_64 we have to map + * all our non-PIC object files into the low 2Gb of the address space + * (why 2Gb and not 4Gb? Because all addresses must be reachable + * using a 32-bit signed PC-relative offset). On Linux we can do this + * using the MAP_32BIT flag to mmap(), however on other OSs + * (e.g. *BSD, see #2063, and also on Linux inside Xen, see #2512), we + * can't do this. So on these systems, we have to pick a base address + * in the low 2Gb of the address space and try to allocate memory from + * there. + * + * We pick a default address based on the OS, but also make this + * configurable via an RTS flag (+RTS -xm) + */ +#if defined(x86_64_HOST_ARCH) + +#if defined(MAP_32BIT) +// Try to use MAP_32BIT +#define MMAP_32BIT_BASE_DEFAULT 0 +#else +// A guess: 1Gb. +#define MMAP_32BIT_BASE_DEFAULT 0x40000000 +#endif + +static void *mmap_32bit_base = MMAP_32BIT_BASE_DEFAULT; +#endif + +/* MAP_ANONYMOUS is MAP_ANON on some systems, e.g. OpenBSD */ +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif + /* ----------------------------------------------------------------------------- * Built-in symbols from the RTS */ @@ -994,6 +1039,13 @@ initLinker( void ) dl_prog_handle = dlopen(NULL, RTLD_LAZY); # endif /* RTLD_DEFAULT */ # endif + +#if defined(x86_64_HOST_ARCH) + if (RtsFlags.MiscFlags.linkerMemBase != 0) { + // User-override for mmap_32bit_base + mmap_32bit_base = (void*)RtsFlags.MiscFlags.linkerMemBase; + } +#endif } /* ----------------------------------------------------------------------------- @@ -1240,6 +1292,61 @@ void ghci_enquire ( char* addr ) static unsigned int PLTSize(void); #endif +static void * +mmapForLinker (size_t bytes, nat flags, int fd) +{ + void *map_addr = NULL; + void *result; + +mmap_again: + +#if defined(x86_64_HOST_ARCH) + if (mmap_32bit_base != 0) { + map_addr = mmap_32bit_base; + } +#endif + + result = mmap(map_addr, bytes, PROT_EXEC|PROT_READ|PROT_WRITE, + MAP_PRIVATE|TRY_MAP_32BIT|flags, fd, 0); + + if (result == MAP_FAILED) { + sysErrorBelch("mmap"); + stg_exit(EXIT_FAILURE); + } + +#if defined(x86_64_HOST_ARCH) + if (mmap_32bit_base != 0) { + if (result == map_addr) { + mmap_32bit_base = map_addr + bytes; + } else { + if ((W_)result > 0x80000000) { + // oops, we were given memory over 2Gb + // ... try allocating memory somewhere else?; + barf("loadObj: failed to mmap() memory below 2Gb; asked for %lu bytes at 0x%p, got 0x%p. Try specifying an address with +RTS -xm -RTS", bytes, map_addr, result); + } else { + // hmm, we were given memory somewhere else, but it's + // still under 2Gb so we can use it. Next time, ask + // for memory right after the place we just got some + mmap_32bit_base = (void*)result + bytes; + } + } + } 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, bytes); + + // Set a base address and try again... (guess: 1Gb) + mmap_32bit_base = (void*)0x40000000; + goto mmap_again; + } + } +#endif + + return result; +} + /* ----------------------------------------------------------------------------- * Load an obj (populate the global symbol table, but don't resolve yet) * @@ -1253,7 +1360,6 @@ loadObj( char *path ) int r, n; #ifdef USE_MMAP int fd, pagesize; - void *map_addr = NULL; #else FILE *f; #endif @@ -1340,27 +1446,14 @@ loadObj( char *path ) n = ROUND_UP(oc->fileSize, pagesize); - /* Link objects into the lower 2Gb on x86_64. GHC assumes the - * small memory model on this architecture (see gcc docs, - * -mcmodel=small). - * - * MAP_32BIT not available on OpenBSD/amd64 - */ -#if defined(x86_64_HOST_ARCH) && defined(MAP_32BIT) -#define EXTRA_MAP_FLAGS MAP_32BIT -#else -#define EXTRA_MAP_FLAGS 0 -#endif - - /* MAP_ANONYMOUS is MAP_ANON on some systems, e.g. OpenBSD */ -#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) -#define MAP_ANONYMOUS MAP_ANON -#endif - +#ifdef ia64_HOST_ARCH oc->image = mmap(map_addr, n, PROT_EXEC|PROT_READ|PROT_WRITE, - MAP_PRIVATE|EXTRA_MAP_FLAGS, fd, 0); + MAP_PRIVATE|TRY_MAP_32BIT, fd, 0); if (oc->image == MAP_FAILED) barf("loadObj: can't map `%s'", path); +#else + oc->image = mmapForLinker(n, 0, fd); +#endif close(fd); @@ -1632,21 +1725,8 @@ static int ocAllocateSymbolExtras( ObjectCode* oc, int count, int first ) */ if( m > n ) // we need to allocate more pages { - oc->symbol_extras = mmap (NULL, sizeof(SymbolExtra) * count, - PROT_EXEC|PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS|EXTRA_MAP_FLAGS, - 0, 0); - if (oc->symbol_extras == MAP_FAILED) - { - errorBelch( "Unable to mmap() for jump islands\n" ); - return 0; - } -#ifdef x86_64_HOST_ARCH - if ((StgWord)oc->symbol_extras > 0x80000000) - { - barf("mmap() returned memory outside 2Gb"); - } -#endif + oc->symbol_extras = mmapForLinker(sizeof(SymbolExtra) * count, + MAP_ANONYMOUS, 0); } else { diff --git a/rts/RtsFlags.c b/rts/RtsFlags.c index 497cd88..1cbd569 100644 --- a/rts/RtsFlags.c +++ b/rts/RtsFlags.c @@ -208,6 +208,7 @@ void initRtsFlagsDefaults(void) RtsFlags.ConcFlags.ctxtSwitchTime = 20; /* In milliseconds */ RtsFlags.MiscFlags.install_signal_handlers = rtsTrue; + RtsFlags.MiscFlags.linkerMemBase = 0; #ifdef THREADED_RTS RtsFlags.ParFlags.nNodes = 1; @@ -476,6 +477,10 @@ usage_text[] = { #if defined(GRAN) /* ToDo: fill in decent Docu here */ " -b... All GranSim options start with -b; see GranSim User's Guide for details", #endif +#if defined(x86_64_HOST_ARCH) +" -xm Base address to mmap memory in the GHCi linker", +" (hex; must be <80000000)", +#endif #if defined(USE_PAPI) " -aX CPU performance counter measurements using PAPI", " (use with the -s option). X is one of:", @@ -1259,7 +1264,22 @@ error = rtsTrue; } break; - case 'c': /* Debugging tool: show current cost centre on an exception */ +#if defined(x86_64_HOST_ARCH) + case 'm': /* linkerMemBase */ + if (rts_argv[arg][3] != '\0') { + RtsFlags.MiscFlags.linkerMemBase + = strtol(rts_argv[arg]+3, (char **) NULL, 16); + if (RtsFlags.MiscFlags.linkerMemBase > 0x80000000) { + errorBelch("-xm: value must be <80000000"); + error = rtsTrue; + } + } else { + RtsFlags.MiscFlags.linkerMemBase = 0; + } + break; +#endif + + case 'c': /* Debugging tool: show current cost centre on an exception */ PROFILING_BUILD_ONLY( RtsFlags.ProfFlags.showCCSOnException = rtsTrue; ); -- 1.7.10.4