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 “<literal>failed to mmap() memory below 2Gb</literal>”. 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>
#include "Sparks.h"
#include "RtsTypeable.h"
#include "Timer.h"
+#include "Trace.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
*/
#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
*/
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
}
/* -----------------------------------------------------------------------------
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)
*
int r, n;
#ifdef USE_MMAP
int fd, pagesize;
- void *map_addr = NULL;
#else
FILE *f;
#endif
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);
*/
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
{
RtsFlags.ConcFlags.ctxtSwitchTime = 20; /* In milliseconds */
RtsFlags.MiscFlags.install_signal_handlers = rtsTrue;
+ RtsFlags.MiscFlags.linkerMemBase = 0;
#ifdef THREADED_RTS
RtsFlags.ParFlags.nNodes = 1;
#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:",
}
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;
);