Commit 11f6f411 authored by Simon Marlow's avatar Simon Marlow

Attempt to fix #2512 and #2063; add +RTS -xm<address> -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.
parent cb905327
......@@ -130,6 +130,35 @@
own signal handlers.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-xm<replaceable>address</replaceable></option>
<indexterm><primary><option>-xm</option></primary><secondary>RTS
option</secondary></indexterm></term>
<listitem>
<para>
WARNING: this option is for working around memory
allocation problems only. Do not use unless GHCi fails
with a message like &ldquo;<literal>failed to mmap() memory below 2Gb</literal>&rdquo;. If you need to use this option to get GHCi working
on your machine, please file a bug.
</para>
<para>
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, <literal>+RTS -xm20000000
-RTS</literal> 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. <literal>mmap</literal>
with <literal>MAP_32BIT</literal> on Linux), or
otherwise <literal>-xm40000000</literal>.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
......
......@@ -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
......
......@@ -28,6 +28,7 @@
#include "Sparks.h"
#include "RtsTypeable.h"
#include "Timer.h"
#include "Trace.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
......@@ -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<addr> -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
{
......
......@@ -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<file> 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;
);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment