summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--includes/rts/Linker.h2
-rw-r--r--rts/Linker.c380
-rw-r--r--testsuite/tests/rts/Makefile50
-rw-r--r--testsuite/tests/rts/all.T17
-rw-r--r--testsuite/tests/rts/linker_error.c66
-rw-r--r--testsuite/tests/rts/linker_error2.c6
-rw-r--r--testsuite/tests/rts/linker_error3.c6
-rw-r--r--testsuite/tests/rts/linker_unload.c4
8 files changed, 388 insertions, 143 deletions
diff --git a/includes/rts/Linker.h b/includes/rts/Linker.h
index 42d316fc38..c53ad4acc5 100644
--- a/includes/rts/Linker.h
+++ b/includes/rts/Linker.h
@@ -43,7 +43,7 @@ void initLinker (void);
void initLinker_ (int retain_cafs);
/* insert a symbol in the hash table */
-void insertSymbol(pathchar* obj_name, char* key, void* data);
+HsInt insertSymbol(pathchar* obj_name, char* key, void* data);
/* lookup a symbol in the hash table */
void *lookupSymbol( char *lbl );
diff --git a/rts/Linker.c b/rts/Linker.c
index e74d647283..4ea7fd6ff7 100644
--- a/rts/Linker.c
+++ b/rts/Linker.c
@@ -1508,9 +1508,11 @@ static RtsSymbolVal rtsSyms[] = {
/* -----------------------------------------------------------------------------
* Insert symbols into hash tables, checking for duplicates.
+ *
+ * Returns: 0 on failure, nonzero on success
*/
-static void ghciInsertSymbolTable(
+static int ghciInsertSymbolTable(
pathchar* obj_name,
HashTable *table,
char* key,
@@ -1526,20 +1528,22 @@ static void ghciInsertSymbolTable(
pinfo->owner = owner;
pinfo->weak = weak;
insertStrHashTable(table, key, pinfo);
- return;
- } else if ((!pinfo->weak || pinfo->value) && weak) {
- return; /* duplicate weak symbol, throw it away */
- } else if (pinfo->weak) /* weak symbol is in the table */
+ return 1;
+ }
+ else if ((!pinfo->weak || pinfo->value) && weak)
+ {
+ return 1; /* duplicate weak symbol, throw it away */
+ }
+ else if (pinfo->weak) /* weak symbol is in the table */
{
/* override the weak definition with the non-weak one */
pinfo->value = data;
pinfo->owner = owner;
pinfo->weak = HS_BOOL_FALSE;
- return;
+ return 1;
}
debugBelch(
- "\n\n"
- "GHCi runtime linker: fatal error: I found a duplicate definition for symbol\n"
+ "GHC runtime linker: fatal error: I found a duplicate definition for symbol\n"
" %s\n"
"whilst processing object file\n"
" %" PATH_FMT "\n"
@@ -1547,13 +1551,11 @@ static void ghciInsertSymbolTable(
" * Loading two different object files which export the same symbol\n"
" * Specifying the same object file twice on the GHCi command line\n"
" * An incorrect `package.conf' entry, causing some object to be\n"
- " loaded twice.\n"
- "GHCi cannot safely continue in this situation. Exiting now. Sorry.\n"
- "\n",
+ " loaded twice.\n",
(char*)key,
obj_name
);
- stg_exit(1);
+ return 0;
}
static HsBool ghciLookupSymbolTable(HashTable *table,
@@ -1637,8 +1639,10 @@ initLinker_ (int retain_cafs)
/* populate the symbol table with stuff from the RTS */
for (sym = rtsSyms; sym->lbl != NULL; sym++) {
- ghciInsertSymbolTable(WSTR("(GHCi built-in symbols)"),
- symhash, sym->lbl, sym->addr, HS_BOOL_FALSE, NULL);
+ if (! ghciInsertSymbolTable(WSTR("(GHCi built-in symbols)"),
+ symhash, sym->lbl, sym->addr, HS_BOOL_FALSE, NULL)) {
+ barf("ghciInsertSymbolTable failed");
+ }
IF_DEBUG(linker, debugBelch("initLinker: inserting rts symbol %s, %p\n", sym->lbl, sym->addr));
}
# if defined(OBJFORMAT_MACHO) && defined(powerpc_HOST_ARCH)
@@ -1649,14 +1653,18 @@ initLinker_ (int retain_cafs)
we cannot use NULL because we use it to mean nonexistent symbols. So we
use an arbitrary (hopefully unique) address here.
*/
- ghciInsertSymbolTable(WSTR("(GHCi special symbols)"),
- symhash, "__dso_handle", (void *)0x12345687, HS_BOOL_FALSE, NULL);
+ if (! ghciInsertSymbolTable(WSTR("(GHCi special symbols)"),
+ symhash, "__dso_handle", (void *)0x12345687, HS_BOOL_FALSE, NULL)) {
+ barf("ghciInsertSymbolTable failed");
+ }
// Redurect newCAF to newDynCAF if retain_cafs is true.
- ghciInsertSymbolTable(WSTR("(GHCi built-in symbols)"), symhash,
- MAYBE_LEADING_UNDERSCORE_STR("newCAF"),
- retain_cafs ? newDynCAF : newCAF,
- HS_BOOL_FALSE, NULL);
+ if (! ghciInsertSymbolTable(WSTR("(GHCi built-in symbols)"), symhash,
+ MAYBE_LEADING_UNDERSCORE_STR("newCAF"),
+ retain_cafs ? newDynCAF : newCAF,
+ HS_BOOL_FALSE, NULL)) {
+ barf("ghciInsertSymbolTable failed");
+ }
# if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO)
# if defined(RTLD_DEFAULT)
@@ -1712,6 +1720,9 @@ exitLinker( void ) {
#endif
}
#endif
+ if (linker_init_done == 1) {
+ freeHashTable(symhash, free);
+ }
}
/* -----------------------------------------------------------------------------
@@ -1994,11 +2005,12 @@ error:
/* -----------------------------------------------------------------------------
* insert a symbol in the hash table
+ *
+ * Returns: 0 on failure, nozero on success
*/
-void
-insertSymbol(pathchar* obj_name, char* key, void* data)
+HsInt insertSymbol(pathchar* obj_name, char* key, void* data)
{
- ghciInsertSymbolTable(obj_name, symhash, key, data, HS_BOOL_FALSE, NULL);
+ return ghciInsertSymbolTable(obj_name, symhash, key, data, HS_BOOL_FALSE, NULL);
}
/* -----------------------------------------------------------------------------
@@ -2131,12 +2143,15 @@ void ghci_enquire ( char* addr )
#ifdef USE_MMAP
#define ROUND_UP(x,size) ((x + size - 1) & ~(size - 1))
-static void *
-mmapForLinker (size_t bytes, nat flags, int fd)
+//
+// Returns NULL on failure.
+//
+static void * mmapForLinker (size_t bytes, nat flags, int fd)
{
void *map_addr = NULL;
void *result;
- int pagesize, size;
+ int pagesize;
+ StgWord size;
static nat fixed = 0;
IF_DEBUG(linker, debugBelch("mmapForLinker: start\n"));
@@ -2151,15 +2166,21 @@ mmap_again:
}
#endif
- IF_DEBUG(linker, debugBelch("mmapForLinker: \tprotection %#0x\n", PROT_EXEC | PROT_READ | PROT_WRITE));
- IF_DEBUG(linker, debugBelch("mmapForLinker: \tflags %#0x\n", MAP_PRIVATE | TRY_MAP_32BIT | fixed | flags));
- result = mmap(map_addr, size, PROT_EXEC|PROT_READ|PROT_WRITE,
- MAP_PRIVATE|TRY_MAP_32BIT|fixed|flags, fd, 0);
+ IF_DEBUG(linker,
+ debugBelch("mmapForLinker: \tprotection %#0x\n",
+ PROT_EXEC | PROT_READ | PROT_WRITE));
+ IF_DEBUG(linker,
+ debugBelch("mmapForLinker: \tflags %#0x\n",
+ MAP_PRIVATE | TRY_MAP_32BIT | fixed | flags));
+
+ 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 %" FMT_Word " bytes at %p",(W_)size,map_addr);
errorBelch("Try specifying an address with +RTS -xm<addr> -RTS");
- stg_exit(EXIT_FAILURE);
+ return NULL;
}
#if !defined(ALWAYS_PIC) && defined(x86_64_HOST_ARCH)
@@ -2169,15 +2190,21 @@ mmap_again:
} else {
if ((W_)result > 0x80000000) {
// oops, we were given memory over 2Gb
-#if defined(freebsd_HOST_OS) || defined(kfreebsdgnu_HOST_OS) || defined(dragonfly_HOST_OS)
+ 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.
- 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);
+ errorBelch("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);
+ return NULL;
#endif
} else {
// hmm, we were given memory somewhere else, but it's
@@ -2190,7 +2217,9 @@ mmap_again:
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);
+ 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)
@@ -2200,15 +2229,53 @@ mmap_again:
}
#endif
- IF_DEBUG(linker, debugBelch("mmapForLinker: mapped %" FMT_Word " bytes starting at %p\n", (W_)size, result));
- IF_DEBUG(linker, debugBelch("mmapForLinker: done\n"));
+ IF_DEBUG(linker,
+ debugBelch("mmapForLinker: mapped %" FMT_Word
+ " bytes starting at %p\n", (W_)size, result));
+ IF_DEBUG(linker,
+ debugBelch("mmapForLinker: done\n"));
+
return result;
}
#endif // USE_MMAP
+static void removeOcSymbols (ObjectCode *oc)
+{
+ if (oc->symbols == NULL) return;
+
+ /* Remove all the mappings for the symbols within this object..
+ */
+ int i;
+ for (i = 0; i < oc->n_symbols; i++) {
+ if (oc->symbols[i] != NULL) {
+ ghciRemoveSymbolTable(symhash, oc->symbols[i], oc);
+ }
+ }
+}
+/*
+ * freeObjectCode() releases all the pieces of an ObjectCode. It is called by
+ * the GC when a previously unloaded ObjectCode has been determined to be
+ * unused, and when an error occurs during loadObj().
+ */
void freeObjectCode (ObjectCode *oc)
{
+ if (oc->symbols != NULL) {
+ stgFree(oc->symbols);
+ oc->symbols = NULL;
+ }
+
+ {
+ Section *s, *nexts;
+
+ for (s = oc->sections; s != NULL; s = nexts) {
+ nexts = s->next;
+ stgFree(s);
+ }
+ }
+
+ freeProddableBlocks(oc);
+
#ifdef USE_MMAP
int pagesize, size, r;
@@ -2222,7 +2289,7 @@ void freeObjectCode (ObjectCode *oc)
#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH) || defined(arm_HOST_ARCH)
#if !defined(x86_64_HOST_ARCH) || !defined(mingw32_HOST_OS)
- if (!USE_CONTIGUOUS_MMAP)
+ if (!USE_CONTIGUOUS_MMAP && oc->symbol_extras != NULL)
{
munmap(oc->symbol_extras,
ROUND_UP(sizeof(SymbolExtra) * oc->n_symbol_extras, pagesize));
@@ -2273,7 +2340,7 @@ mkOc( pathchar *path, char *image, int imageSize,
ObjectCode* oc;
IF_DEBUG(linker, debugBelch("mkOc: start\n"));
- oc = stgMallocBytes(sizeof(ObjectCode), "loadArchive(oc)");
+ oc = stgMallocBytes(sizeof(ObjectCode), "mkOc(oc)");
# if defined(OBJFORMAT_ELF)
oc->formatName = "ELF";
@@ -2302,6 +2369,7 @@ mkOc( pathchar *path, char *image, int imageSize,
oc->sections = NULL;
oc->proddables = NULL;
oc->stable_ptrs = NULL;
+ oc->symbol_extras = NULL;
#ifndef USE_MMAP
#ifdef darwin_HOST_OS
@@ -2310,8 +2378,7 @@ mkOc( pathchar *path, char *image, int imageSize,
#endif
/* chain it onto the list of objects */
- oc->next = objects;
- objects = oc;
+ oc->next = NULL;
IF_DEBUG(linker, debugBelch("mkOc: done\n"));
return oc;
@@ -2372,6 +2439,10 @@ loadArchive( pathchar *path )
#endif
#endif
+ /* TODO: don't call barf() on error, instead return an error code, freeing
+ * all resources correctly. This function is pretty complex, so it needs
+ * to be refactored to make this practical. */
+
initLinker();
IF_DEBUG(linker, debugBelch("loadArchive: start\n"));
@@ -2733,6 +2804,9 @@ loadArchive( pathchar *path )
stgFree(fileName);
fclose(f);
return 0;
+ } else {
+ oc->next = objects;
+ objects = oc;
}
}
else if (isGnuIndex) {
@@ -2849,19 +2923,23 @@ loadObj( pathchar *path )
/* coverity[toctou] */
fd = open(path, O_RDONLY);
#endif
- if (fd == -1)
- barf("loadObj: can't open `%s'", path);
+ if (fd == -1) {
+ errorBelch("loadObj: can't open `%s'", path);
+ return 0;
+ }
image = mmapForLinker(fileSize, 0, fd);
-
close(fd);
+ if (image == NULL) return 0;
#else /* !USE_MMAP */
/* load the image into memory */
/* coverity[toctou] */
f = pathopen(path, WSTR("rb"));
- if (!f)
- barf("loadObj: can't read `%" PATH_FMT "'", path);
+ if (!f) {
+ errorBelch("loadObj: can't read `%" PATH_FMT "'", path);
+ return 0;
+ }
# if defined(mingw32_HOST_OS)
// TODO: We would like to use allocateExec here, but allocateExec
@@ -2871,6 +2949,10 @@ loadObj( pathchar *path )
f, path, "itself",
#endif
fileSize);
+ if (image == NULL) {
+ fclose(f);
+ return 0;
+ }
# elif defined(darwin_HOST_OS)
// In a Mach-O .o file, all sections can and will be misaligned
// if the total size of the headers is not a multiple of the
@@ -2891,10 +2973,13 @@ loadObj( pathchar *path )
{
int n;
n = fread ( image, 1, fileSize, f );
- if (n != fileSize)
- barf("loadObj: error whilst reading `%s'", path);
+ fclose(f);
+ if (n != fileSize) {
+ errorBelch("loadObj: error whilst reading `%" PATH_FMT "'", path);
+ stgFree(image);
+ return 0;
+ }
}
- fclose(f);
#endif /* USE_MMAP */
oc = mkOc(path, image, fileSize, NULL
@@ -2905,7 +2990,16 @@ loadObj( pathchar *path )
#endif
);
- return loadOc(oc);
+ if (! loadOc(oc)) {
+ // failed; free everything we've allocated
+ removeOcSymbols(oc);
+ freeObjectCode(oc);
+ return 0;
+ }
+
+ oc->next = objects;
+ objects = oc;
+ return 1;
}
static HsInt
@@ -2914,6 +3008,21 @@ loadOc( ObjectCode* oc ) {
IF_DEBUG(linker, debugBelch("loadOc: start\n"));
+ /* verify the in-memory image */
+# if defined(OBJFORMAT_ELF)
+ r = ocVerifyImage_ELF ( oc );
+# elif defined(OBJFORMAT_PEi386)
+ r = ocVerifyImage_PEi386 ( oc );
+# elif defined(OBJFORMAT_MACHO)
+ r = ocVerifyImage_MachO ( oc );
+# else
+ barf("loadObj: no verify method");
+# endif
+ if (!r) {
+ IF_DEBUG(linker, debugBelch("loadOc: ocVerifyImage_* failed\n"));
+ return r;
+ }
+
# if defined(OBJFORMAT_MACHO) && (defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH))
r = ocAllocateSymbolExtras_MachO ( oc );
if (!r) {
@@ -2930,21 +3039,6 @@ loadOc( ObjectCode* oc ) {
ocAllocateSymbolExtras_PEi386 ( oc );
#endif
- /* verify the in-memory image */
-# if defined(OBJFORMAT_ELF)
- r = ocVerifyImage_ELF ( oc );
-# elif defined(OBJFORMAT_PEi386)
- r = ocVerifyImage_PEi386 ( oc );
-# elif defined(OBJFORMAT_MACHO)
- r = ocVerifyImage_MachO ( oc );
-# else
- barf("loadObj: no verify method");
-# endif
- if (!r) {
- IF_DEBUG(linker, debugBelch("loadOc: ocVerifyImage_* failed\n"));
- return r;
- }
-
/* build the symbol list for this image */
# if defined(OBJFORMAT_ELF)
r = ocGetNames_ELF ( oc );
@@ -3039,17 +3133,7 @@ unloadObj( pathchar *path )
if (!pathcmp(oc->fileName,path)) {
- /* Remove all the mappings for the symbols within this
- * object..
- */
- {
- int i;
- for (i = 0; i < oc->n_symbols; i++) {
- if (oc->symbols[i] != NULL) {
- ghciRemoveSymbolTable(symhash, oc->symbols[i], oc);
- }
- }
- }
+ removeOcSymbols(oc);
if (prev == NULL) {
objects = oc->next;
@@ -3059,23 +3143,6 @@ unloadObj( pathchar *path )
oc->next = unloaded_objects;
unloaded_objects = oc;
- // The data itself and a few other bits (oc->fileName,
- // oc->archiveMemberName) are kept until freeObjectCode(),
- // which is only called when it has been determined that
- // it is safe to unload the object.
- stgFree(oc->symbols);
-
- {
- Section *s, *nexts;
-
- for (s = oc->sections; s != NULL; s = nexts) {
- nexts = s->next;
- stgFree(s);
- }
- }
-
- freeProddableBlocks(oc);
-
// Release any StablePtrs that were created when this
// object module was initialized.
{
@@ -3242,13 +3309,16 @@ static int ocAllocateSymbolExtras( ObjectCode* oc, int count, int first )
oc->fileSize = n + (sizeof(SymbolExtra) * count);
oc->symbol_extras = (SymbolExtra *) (oc->image + n);
}
- else
+ else {
oc->symbol_extras = NULL;
+ return 0;
+ }
}
else
{
oc->symbol_extras = mmapForLinker(sizeof(SymbolExtra) * count,
MAP_ANONYMOUS, -1);
+ if (oc->symbol_extras == NULL) return 0;
}
}
else
@@ -3584,6 +3654,8 @@ typedef
#define MYIMAGE_REL_I386_DIR32 0x0006
#define MYIMAGE_REL_I386_REL32 0x0014
+static int verifyCOFFHeader ( COFF_header *hdr, pathchar *filename);
+
/* We assume file pointer is right at the
beginning of COFF object.
*/
@@ -3602,11 +3674,17 @@ allocateImageAndTrampolines (
size_t n;
n = fread ( &hdr, 1, sizeof_COFF_header, f );
- if (n != sizeof( COFF_header ))
- barf("getNumberOfSymbols: error whilst reading `%s' header in `%S'",
- member_name, arch_name);
+ if (n != sizeof( COFF_header )) {
+ errorBelch("getNumberOfSymbols: error whilst reading `%s' header in `%S'",
+ member_name, arch_name);
+ return NULL;
+ }
fseek( f, -sizeof_COFF_header, SEEK_CUR );
+ if (!verifyCOFFHeader(&hdr, arch_name)) {
+ return 0;
+ }
+
/* We get back 8-byte aligned memory (is that guaranteed?), but
the offsets to the sections within the file are all 4 mod 8
(is that guaranteed?). We therefore need to offset the image
@@ -3622,6 +3700,13 @@ allocateImageAndTrampolines (
image = VirtualAlloc(NULL, size,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
+
+ if (image == NULL) {
+ errorBelch("%" PATH_FMT ": failed to allocate memory for image",
+ arch_name);
+ return NULL;
+ }
+
return image + PEi386_IMAGE_OFFSET;
}
@@ -3853,37 +3938,17 @@ lookupSymbolInDLLs ( UChar *lbl )
return NULL;
}
-
static int
-ocVerifyImage_PEi386 ( ObjectCode* oc )
+verifyCOFFHeader (COFF_header *hdr, pathchar *fileName)
{
- int i;
- UInt32 j, noRelocs;
- COFF_header* hdr;
- COFF_section* sectab;
- COFF_symbol* symtab;
- UChar* strtab;
- /* debugBelch("\nLOADING %s\n", oc->fileName); */
- hdr = (COFF_header*)(oc->image);
- sectab = (COFF_section*) (
- ((UChar*)(oc->image))
- + sizeof_COFF_header + hdr->SizeOfOptionalHeader
- );
- symtab = (COFF_symbol*) (
- ((UChar*)(oc->image))
- + hdr->PointerToSymbolTable
- );
- strtab = ((UChar*)symtab)
- + hdr->NumberOfSymbols * sizeof_COFF_symbol;
-
#if defined(i386_HOST_ARCH)
if (hdr->Machine != 0x14c) {
- errorBelch("%" PATH_FMT ": Not x86 PEi386", oc->fileName);
+ errorBelch("%" PATH_FMT ": Not x86 PEi386", fileName);
return 0;
}
#elif defined(x86_64_HOST_ARCH)
if (hdr->Machine != 0x8664) {
- errorBelch("%" PATH_FMT ": Not x86_64 PEi386", oc->fileName);
+ errorBelch("%" PATH_FMT ": Not x86_64 PEi386", fileName);
return 0;
}
#else
@@ -3891,23 +3956,53 @@ ocVerifyImage_PEi386 ( ObjectCode* oc )
#endif
if (hdr->SizeOfOptionalHeader != 0) {
- errorBelch("%" PATH_FMT ": PEi386 with nonempty optional header", oc->fileName);
+ errorBelch("%" PATH_FMT ": PEi386 with nonempty optional header",
+ fileName);
return 0;
}
if ( /* (hdr->Characteristics & MYIMAGE_FILE_RELOCS_STRIPPED) || */
(hdr->Characteristics & MYIMAGE_FILE_EXECUTABLE_IMAGE) ||
(hdr->Characteristics & MYIMAGE_FILE_DLL) ||
(hdr->Characteristics & MYIMAGE_FILE_SYSTEM) ) {
- errorBelch("%" PATH_FMT ": Not a PEi386 object file", oc->fileName);
+ errorBelch("%" PATH_FMT ": Not a PEi386 object file", fileName);
return 0;
}
if ( (hdr->Characteristics & MYIMAGE_FILE_BYTES_REVERSED_HI)
/* || !(hdr->Characteristics & MYIMAGE_FILE_32BIT_MACHINE) */ ) {
errorBelch("%" PATH_FMT ": Invalid PEi386 word size or endiannness: %d",
- oc->fileName,
+ fileName,
(int)(hdr->Characteristics));
return 0;
}
+ return 1;
+}
+
+static int
+ocVerifyImage_PEi386 ( ObjectCode* oc )
+{
+ int i;
+ UInt32 j, noRelocs;
+ COFF_header* hdr;
+ COFF_section* sectab;
+ COFF_symbol* symtab;
+ UChar* strtab;
+ /* debugBelch("\nLOADING %s\n", oc->fileName); */
+ hdr = (COFF_header*)(oc->image);
+ sectab = (COFF_section*) (
+ ((UChar*)(oc->image))
+ + sizeof_COFF_header + hdr->SizeOfOptionalHeader
+ );
+ symtab = (COFF_symbol*) (
+ ((UChar*)(oc->image))
+ + hdr->PointerToSymbolTable
+ );
+ strtab = ((UChar*)symtab)
+ + hdr->NumberOfSymbols * sizeof_COFF_symbol;
+
+ if (!verifyCOFFHeader(hdr, oc->fileName)) {
+ return 0;
+ }
+
/* If the string table size is way crazy, this might indicate that
there are more than 64k relocations, despite claims to the
contrary. Hence this test. */
@@ -4199,7 +4294,9 @@ ocGetNames_PEi386 ( ObjectCode* oc )
if (kind != SECTIONKIND_OTHER && end >= start) {
if ((((size_t)(start)) % 4) != 0) {
- barf("Misaligned section %s: %p", (char*)secname, start);
+ errorBelch("Misaligned section %s: %p", (char*)secname, start);
+ stgFree(secname);
+ return 0;
}
addSection(oc, kind, start, end);
@@ -4263,8 +4360,10 @@ ocGetNames_PEi386 ( ObjectCode* oc )
ASSERT(i >= 0 && i < oc->n_symbols);
/* cstring_from_COFF_symbol_name always succeeds. */
oc->symbols[i] = (char*)sname;
- ghciInsertSymbolTable(oc->fileName, symhash, (char*)sname, addr,
- HS_BOOL_FALSE, oc);
+ if (! ghciInsertSymbolTable(oc->fileName, symhash, (char*)sname, addr,
+ HS_BOOL_FALSE, oc)) {
+ return 0;
+ }
} else {
# if 0
debugBelch(
@@ -5150,8 +5249,11 @@ ocGetNames_ELF ( ObjectCode* oc )
nent = shdr[i].sh_size / sizeof(Elf_Sym);
oc->n_symbols = nent;
- oc->symbols = stgMallocBytes(oc->n_symbols * sizeof(char*),
+ oc->symbols = stgCallocBytes(oc->n_symbols, sizeof(char*),
"ocGetNames_ELF(oc->symbols)");
+ // Note calloc: if we fail partway through initializing symbols, we need
+ // to undo the additions to the symbol table so far. We know which ones
+ // have been added by whether the entry is NULL or not.
//TODO: we ignore local symbols anyway right? So we can use the
// shdr[i].sh_info to get the index of the first non-local symbol
@@ -5224,12 +5326,15 @@ ocGetNames_ELF ( ObjectCode* oc )
if (ad != NULL) {
ASSERT(nm != NULL);
- oc->symbols[j] = nm;
/* Acquire! */
if (isLocal) {
/* Ignore entirely. */
} else {
- ghciInsertSymbolTable(oc->fileName, symhash, nm, ad, isWeak, oc);
+ if (! ghciInsertSymbolTable(oc->fileName, symhash,
+ nm, ad, isWeak, oc)) {
+ return 0;
+ }
+ oc->symbols[j] = nm;
}
} else {
/* Skip. */
@@ -5323,6 +5428,7 @@ do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC,
} else {
symbol = strtab + sym.st_name;
S_tmp = lookupSymbol( symbol );
+ if (S_tmp == NULL) return 0;
S = (Elf_Addr)S_tmp;
}
if (!S) {
@@ -5764,8 +5870,9 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
-> jumpIsland;
off = pltAddress + A - P;
#else
- barf("R_X86_64_PC32 relocation out of range: %s = %p\nRecompile %s with -fPIC.",
- symbol, off, oc->fileName );
+ errorBelch("R_X86_64_PC32 relocation out of range: %s = %p\nRecompile %s with -fPIC.",
+ symbol, off, oc->fileName );
+ return 0;
#endif
}
*(Elf64_Word *)P = (Elf64_Word)off;
@@ -5790,8 +5897,9 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
-> jumpIsland;
value = pltAddress + A;
#else
- barf("R_X86_64_32 relocation out of range: %s = %p\nRecompile %s with -fPIC.",
- symbol, value, oc->fileName );
+ errorBelch("R_X86_64_32 relocation out of range: %s = %p\nRecompile %s with -fPIC.",
+ symbol, value, oc->fileName );
+ return 0;
#endif
}
*(Elf64_Word *)P = (Elf64_Word)value;
@@ -5808,8 +5916,9 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
-> jumpIsland;
value = pltAddress + A;
#else
- barf("R_X86_64_32S relocation out of range: %s = %p\nRecompile %s with -fPIC.",
- symbol, value, oc->fileName );
+ errorBelch("R_X86_64_32S relocation out of range: %s = %p\nRecompile %s with -fPIC.",
+ symbol, value, oc->fileName );
+ return 0;
#endif
}
*(Elf64_Sword *)P = (Elf64_Sword)value;
@@ -6849,6 +6958,7 @@ ocGetNames_MachO(ObjectCode* oc)
{
#ifdef USE_MMAP
char * zeroFillArea = mmapForLinker(sections[i].size, MAP_ANONYMOUS, -1);
+ if (zeroFillArea == NULL) return 0;
memset(zeroFillArea, 0, sections[i].size);
#else
char * zeroFillArea = stgCallocBytes(1,sections[i].size,
diff --git a/testsuite/tests/rts/Makefile b/testsuite/tests/rts/Makefile
index 02a50a4644..8a7cb8af02 100644
--- a/testsuite/tests/rts/Makefile
+++ b/testsuite/tests/rts/Makefile
@@ -104,15 +104,15 @@ T7040_ghci_setup :
LOCAL_GHC_PKG = '$(GHC_PKG)' --no-user-package-db
-BASE_DIR = $(shell $(LOCAL_GHC_PKG) field base library-dirs | sed 's/^.*: *//')
-BASE_LIB = $(shell $(LOCAL_GHC_PKG) field base hs-libraries | sed 's/^.*: *//')
-GHC_PRIM_DIR = $(shell $(LOCAL_GHC_PKG) field ghc-prim library-dirs | sed 's/^.*: *//')
-GHC_PRIM_LIB = $(shell $(LOCAL_GHC_PKG) field ghc-prim hs-libraries | sed 's/^.*: *//')
+BASE_DIR = $(shell $(LOCAL_GHC_PKG) field base library-dirs | sed 's/^[^:]*: *//')
+BASE_LIB = $(shell $(LOCAL_GHC_PKG) field base hs-libraries | sed 's/^[^:]*: *//')
+GHC_PRIM_DIR = $(shell $(LOCAL_GHC_PKG) field ghc-prim library-dirs | sed 's/^[^:]*: *//')
+GHC_PRIM_LIB = $(shell $(LOCAL_GHC_PKG) field ghc-prim hs-libraries | sed 's/^[^:]*: *//')
# We need to get first library directory here in order to get rid of
# system gmp library directory installation when ghc is configured
# with --with-gmp-libraries=<dir> parameter
INTEGER_GMP_DIR = $(shell $(LOCAL_GHC_PKG) field integer-gmp library-dirs \
- | sed 's/^.*: *//' | head -1)
+ | sed 's/^[^:]*: *//' | head -1)
INTEGER_GMP_LIB = $(shell $(LOCAL_GHC_PKG) field integer-gmp hs-libraries | sed 's/^.*: *//')
BASE = $(BASE_DIR)/lib$(BASE_LIB).a
@@ -124,5 +124,43 @@ linker_unload:
$(RM) Test.o Test.hi
"$(TEST_HC)" $(TEST_HC_OPTS) -c Test.hs -v0
# -rtsopts causes a warning
- "$(TEST_HC)" $(filter-out -rtsopts, $(TEST_HC_OPTS)) linker_unload.c -o linker_unload -no-hs-main -optc-Werror
+ "$(TEST_HC)" $(filter-out -rtsopts, $(TEST_HC_OPTS)) linker_unload.c -o linker_unload -no-hs-main -optc-Werror -debug -optc-g
./linker_unload $(BASE) $(GHC_PRIM) $(INTEGER_GMP)
+
+# -----------------------------------------------------------------------------
+# Testing failures in the RTS linker. We should be able to repeatedly
+# load bogus object files of various kinds without crashing and
+# without any memory leaks.
+#
+# Check for memory leaks manually by running e.g.
+#
+# make linker_error1
+# valgrind --leak-check=full --show-reachable=yes ./linker_error1 linker_error1_o.o
+
+# linker_error1: not a valid object file
+
+.PHONY: linker_error1
+linker_error1:
+ "$(TEST_HC)" -c linker_error.c -o linker_error1.o
+ "$(TEST_HC)" linker_error1.o -o linker_error1 -no-hs-main -optc-g -debug
+ ./linker_error1 linker_error.c
+
+# linker_error2: the object file has an unknown symbol (fails in
+# resolveObjs())
+
+.PHONY: linker_error2
+linker_error2:
+ "$(TEST_HC)" -c linker_error.c -o linker_error2.o
+ "$(TEST_HC)" -c linker_error2.c -o linker_error2_o.o
+ "$(TEST_HC)" linker_error2.o -o linker_error2 -no-hs-main -optc-g -debug
+ ./linker_error2 linker_error2_o.o
+
+# linker_error3: the object file duplicates an existing symbol (fails
+# in loadObj())
+
+.PHONY: linker_error3
+linker_error3:
+ "$(TEST_HC)" -c linker_error.c -o linker_error3.o
+ "$(TEST_HC)" -c linker_error3.c -o linker_error3_o.o
+ "$(TEST_HC)" linker_error3.o -o linker_error3 -no-hs-main -optc-g -debug
+ ./linker_error3 linker_error3_o.o
diff --git a/testsuite/tests/rts/all.T b/testsuite/tests/rts/all.T
index d4948727e3..015a9c7f90 100644
--- a/testsuite/tests/rts/all.T
+++ b/testsuite/tests/rts/all.T
@@ -244,3 +244,20 @@ test('rdynamic', [ unless(opsys('linux') or opsys('mingw32'), skip)
test('overflow1', [ exit_code(251) ], compile_and_run, [''])
test('overflow2', [ exit_code(251) ], compile_and_run, [''])
test('overflow3', [ exit_code(251) ], compile_and_run, [''])
+
+test('linker_error1',
+ [ extra_clean(['linker_error1.o','linker_error1']), ignore_output ],
+ run_command,
+ ['$MAKE -s --no-print-directory linker_error1'])
+
+test('linker_error2',
+ [ extra_clean(['linker_error2.o','linker_error2_c.o', 'linker_error2']),
+ ignore_output ],
+ run_command,
+ ['$MAKE -s --no-print-directory linker_error2'])
+
+test('linker_error3',
+ [ extra_clean(['linker_error3.o','linker_error3_c.o', 'linker_error3']),
+ ignore_output ],
+ run_command,
+ ['$MAKE -s --no-print-directory linker_error3'])
diff --git a/testsuite/tests/rts/linker_error.c b/testsuite/tests/rts/linker_error.c
new file mode 100644
index 0000000000..60d24a5aca
--- /dev/null
+++ b/testsuite/tests/rts/linker_error.c
@@ -0,0 +1,66 @@
+#include "ghcconfig.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "Rts.h"
+
+#define ITERATIONS 10
+
+typedef int testfun(int);
+
+int main (int argc, char *argv[])
+{
+ testfun *f;
+ int i, r;
+#if defined(mingw32_HOST_OS)
+ wchar_t *obj;
+#else
+ char *obj;
+#endif
+
+ hs_init(&argc, &argv);
+
+ initLinker_(0);
+
+ // Load object file argv[1] repeatedly
+
+ if (argc != 2) {
+ errorBelch("syntax: linker_error <object-file>");
+ exit(1);
+ }
+
+#if defined(mingw32_HOST_OS)
+ size_t len = mbstowcs(NULL, argv[1], 0) + 1;
+ if (len == -1) {
+ errorBelch("invalid multibyte sequence in argument %d: %s", i, argv[i]);
+ exit(1);
+ }
+ wchar_t *buf = (wchar_t*)_alloca(len * sizeof(wchar_t));
+ size_t len2 = mbstowcs(buf, argv[1], len);
+ if (len != len2 + 1) {
+ errorBelch("something fishy is going on in argument %d: %s", i, argv[i]);
+ exit(1);
+ }
+ obj = buf;
+#else
+ obj = argv[1];
+#endif
+
+ for (i=0; i < ITERATIONS; i++) {
+ r = loadObj(obj);
+ if (!r) {
+ debugBelch("loadObj(%s) failed", obj);
+ continue;
+ }
+ r = resolveObjs();
+ if (!r) {
+ debugBelch("resolveObjs failed");
+ unloadObj(obj);
+ continue;
+ }
+ errorBelch("loading succeeded");
+ exit(1);
+ }
+
+ hs_exit();
+ return 0;
+}
diff --git a/testsuite/tests/rts/linker_error2.c b/testsuite/tests/rts/linker_error2.c
new file mode 100644
index 0000000000..2687833ff5
--- /dev/null
+++ b/testsuite/tests/rts/linker_error2.c
@@ -0,0 +1,6 @@
+extern int bar;
+
+int foo(void)
+{
+ return bar;
+}
diff --git a/testsuite/tests/rts/linker_error3.c b/testsuite/tests/rts/linker_error3.c
new file mode 100644
index 0000000000..00faa741c7
--- /dev/null
+++ b/testsuite/tests/rts/linker_error3.c
@@ -0,0 +1,6 @@
+extern int bar;
+
+int stg_upd_frame_info(void)
+{
+ return bar;
+}
diff --git a/testsuite/tests/rts/linker_unload.c b/testsuite/tests/rts/linker_unload.c
index f1cc891df1..4980eeb47f 100644
--- a/testsuite/tests/rts/linker_unload.c
+++ b/testsuite/tests/rts/linker_unload.c
@@ -29,7 +29,9 @@ int main (int argc, char *argv[])
testfun *f;
int i, r;
- hs_init(&argc, &argv);
+ RtsConfig conf = defaultRtsConfig;
+ conf.rts_opts_enabled = RtsOptsAll;
+ hs_init_ghc(&argc, &argv, conf);
initLinker_(0);