+#ifdef USE_MMAP
+#define ROUND_UP(x,size) ((x + size - 1) & ~(size - 1))
+
+static void *
+mmapForLinker (size_t bytes, nat flags, int fd)
+{
+ void *map_addr = NULL;
+ void *result;
+ int pagesize, size;
+ static nat fixed = 0;
+
+ pagesize = getpagesize();
+ size = ROUND_UP(bytes, pagesize);
+
+#if !defined(ALWAYS_PIC) && defined(x86_64_HOST_ARCH)
+mmap_again:
+
+ if (mmap_32bit_base != 0) {
+ map_addr = mmap_32bit_base;
+ }
+#endif
+
+ result = mmap(map_addr, size, PROT_EXEC|PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|TRY_MAP_32BIT|fixed|flags, fd, 0);
+
+ if (result == MAP_FAILED) {
+ sysErrorBelch("mmap %lu bytes at %p",(lnat)size,map_addr);
+ errorBelch("Try specifying an address with +RTS -xm<addr> -RTS");
+ stg_exit(EXIT_FAILURE);
+ }
+
+#if !defined(ALWAYS_PIC) && defined(x86_64_HOST_ARCH)
+ if (mmap_32bit_base != 0) {
+ if (result == map_addr) {
+ mmap_32bit_base = (StgWord8*)map_addr + size;
+ } else {
+ if ((W_)result > 0x80000000) {
+ // oops, we were given memory over 2Gb
+#if defined(freebsd_HOST_OS) || defined(dragonfly_HOST_OS)
+ // Some platforms require MAP_FIXED. This is normally
+ // a bad idea, because MAP_FIXED will overwrite
+ // existing mappings.
+ munmap(result,size);
+ fixed = MAP_FIXED;
+ goto mmap_again;
+#else
+ barf("loadObj: failed to mmap() memory below 2Gb; asked for %lu bytes at %p. Try specifying an address with +RTS -xm<addr> -RTS", size, map_addr, result);
+#endif
+ } 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 = (StgWord8*)result + size;
+ }
+ }
+ } 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;
+ }
+ }
+#endif
+
+ return result;
+}
+#endif // USE_MMAP
+
+static ObjectCode*
+mkOc( char *path, char *image, int imageSize
+#ifndef USE_MMAP
+#ifdef darwin_HOST_OS
+ , int misalignment
+#endif
+#endif
+ ) {
+ ObjectCode* oc;
+
+ oc = stgMallocBytes(sizeof(ObjectCode), "loadArchive(oc)");
+
+# if defined(OBJFORMAT_ELF)
+ oc->formatName = "ELF";
+# elif defined(OBJFORMAT_PEi386)
+ oc->formatName = "PEi386";
+# elif defined(OBJFORMAT_MACHO)
+ oc->formatName = "Mach-O";
+# else
+ stgFree(oc);
+ barf("loadObj: not implemented on this platform");
+# endif
+
+ oc->image = image;
+ /* sigh, strdup() isn't a POSIX function, so do it the long way */
+ /* XXX What should this be for an archive? */
+ oc->fileName = stgMallocBytes( strlen(path)+1, "loadObj" );
+ strcpy(oc->fileName, path);
+
+ oc->fileSize = imageSize;
+ oc->symbols = NULL;
+ oc->sections = NULL;
+ oc->proddables = NULL;
+
+#ifndef USE_MMAP
+#ifdef darwin_HOST_OS
+ oc->misalignment = misalignment;
+#endif
+#endif
+
+ /* chain it onto the list of objects */
+ oc->next = objects;
+ objects = oc;
+
+ return oc;
+}
+
+#if defined(USE_ARCHIVES_FOR_GHCI)
+HsInt
+loadArchive( char *path )
+{
+ ObjectCode* oc;
+ char *image;
+ int imageSize;
+ FILE *f;
+ int n;
+ size_t fileNameSize;
+ char *file;
+ size_t fileSize;
+ int isObject;
+ char tmp[12];
+
+ IF_DEBUG(linker, debugBelch("loadArchive `%s'\n", path));
+
+ fileSize = 32;
+ file = stgMallocBytes(fileSize, "loadArchive(file)");
+
+ f = fopen(path, "rb");
+ if (!f)
+ barf("loadObj: can't read `%s'", path);
+
+ n = fread ( tmp, 1, 8, f );
+ if (strncmp(tmp, "!<arch>\n", 8) != 0)
+ barf("loadArchive: Not an archive: `%s'", path);
+
+ while(1) {
+ n = fread ( file, 1, 16, f );
+ if (n != 16) {
+ if (feof(f)) {
+ break;
+ }
+ else {
+ barf("loadArchive: Failed reading file name from `%s'", path);
+ }
+ }
+ n = fread ( tmp, 1, 12, f );
+ if (n != 12)
+ barf("loadArchive: Failed reading mod time from `%s'", path);
+ n = fread ( tmp, 1, 6, f );
+ if (n != 6)
+ barf("loadArchive: Failed reading owner from `%s'", path);
+ n = fread ( tmp, 1, 6, f );
+ if (n != 6)
+ barf("loadArchive: Failed reading group from `%s'", path);
+ n = fread ( tmp, 1, 8, f );
+ if (n != 8)
+ barf("loadArchive: Failed reading mode from `%s'", path);
+ n = fread ( tmp, 1, 10, f );
+ if (n != 10)
+ barf("loadArchive: Failed reading size from `%s'", path);
+ tmp[10] = '\0';
+ for (n = 0; isdigit(tmp[n]); n++);
+ tmp[n] = '\0';
+ imageSize = atoi(tmp);
+ n = fread ( tmp, 1, 2, f );
+ if (strncmp(tmp, "\x60\x0A", 2) != 0)
+ barf("loadArchive: Failed reading magic from `%s' at %ld. Got %c%c", path, ftell(f), tmp[0], tmp[1]);
+
+ /* Check for BSD-variant large filenames */
+ if (0 == strncmp(file, "#1/", 3)) {
+ file[16] = '\0';
+ for (n = 3; isdigit(file[n]); n++);
+ file[n] = '\0';
+ fileNameSize = atoi(file + 3);
+ imageSize -= fileNameSize;
+ if (fileNameSize > fileSize) {
+ /* Double it to avoid potentially continually
+ increasing it by 1 */
+ fileSize = fileNameSize * 2;
+ file = stgReallocBytes(file, fileSize, "loadArchive(file)");
+ }
+ n = fread ( file, 1, fileNameSize, f );
+ if (n != (int)fileNameSize)
+ barf("loadArchive: Failed reading filename from `%s'", path);
+ }
+ else {
+ fileNameSize = 16;
+ }
+
+ isObject = 0;
+ for (n = 0; n < (int)fileNameSize - 1; n++) {
+ if ((file[n] == '.') && (file[n + 1] == 'o')) {
+ isObject = 1;
+ break;
+ }
+ }
+
+ if (isObject) {
+ /* 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, and if we malloc the
+ memory then we can be given memory above 2^32, so we
+ mmap some anonymous memory and use that. We could
+ do better here. */
+ image = mmapForLinker(imageSize, MAP_ANONYMOUS, -1);
+ n = fread ( image, 1, imageSize, f );
+ if (n != imageSize)
+ barf("loadObj: error whilst reading `%s'", path);
+ oc = mkOc(path, image, imageSize
+#ifndef USE_MMAP
+#ifdef darwin_HOST_OS
+ , 0
+#endif
+#endif
+ );
+ if (0 == loadOc(oc)) {
+ stgFree(file);
+ return 0;
+ }
+ }
+ else {
+ n = fseek(f, imageSize, SEEK_CUR);
+ if (n != 0)
+ barf("loadArchive: error whilst seeking by %d in `%s'",
+ imageSize, path);
+ }
+ /* .ar files are 2-byte aligned */
+ if (imageSize % 2) {
+ n = fread ( tmp, 1, 1, f );
+ if (n != 1) {
+ if (feof(f)) {
+ break;
+ }
+ else {
+ barf("loadArchive: Failed reading padding from `%s'", path);
+ }
+ }
+ }
+ }
+
+ fclose(f);
+
+ stgFree(file);
+ return 1;
+}
+#else
+HsInt GNU_ATTRIBUTE(__noreturn__)
+loadArchive( char *path STG_UNUSED ) {
+ barf("loadArchive: not enabled");
+}