Commit 5300099e authored by Simon Marlow's avatar Simon Marlow

Make the linker more robust to errors

When linking fails because there was a problem with the supplied
object file, then we should not barf() or exit, we should emit a
suitable error message and return an error code to the caller.  We
should also free all memory that might have been allocated during
linking, and generally not do any damage.  This patch fixes most
common instances of this problem.

Test Plan: validate

Reviewers: rwbarton, austin, ezyang

Reviewed By: ezyang

Subscribers: simonmar, ezyang, carter, thomie

Differential Revision:
parent 0ed9a277
......@@ -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 );
This diff is collapsed.
......@@ -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/^.*: *//')
......@@ -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
"$(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
"$(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
"$(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
......@@ -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, [''])
[ extra_clean(['linker_error1.o','linker_error1']), ignore_output ],
['$MAKE -s --no-print-directory linker_error1'])
[ extra_clean(['linker_error2.o','linker_error2_c.o', 'linker_error2']),
ignore_output ],
['$MAKE -s --no-print-directory linker_error2'])
[ extra_clean(['linker_error3.o','linker_error3_c.o', 'linker_error3']),
ignore_output ],
['$MAKE -s --no-print-directory linker_error3'])
#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;
char *obj;
hs_init(&argc, &argv);
// Load object file argv[1] repeatedly
if (argc != 2) {
errorBelch("syntax: linker_error <object-file>");
#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]);
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]);
obj = buf;
obj = argv[1];
for (i=0; i < ITERATIONS; i++) {
r = loadObj(obj);
if (!r) {
debugBelch("loadObj(%s) failed", obj);
r = resolveObjs();
if (!r) {
debugBelch("resolveObjs failed");
errorBelch("loading succeeded");
return 0;
extern int bar;
int foo(void)
return bar;
extern int bar;
int stg_upd_frame_info(void)
return bar;
......@@ -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);
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