summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTamar Christina <tamar@zhox.com>2016-04-11 00:38:42 +0200
committerBen Gamari <ben@smart-cactus.org>2016-04-11 01:44:41 +0200
commit90538d86af579595987826cd893828d6f379f35a (patch)
tree263e787c80664e36ac9dbc895ceff71273b1a03f
parent8987ce067d187878f82005293f6b215dec66df48 (diff)
downloadhaskell-90538d86af579595987826cd893828d6f379f35a.tar.gz
Change runtime linker to perform lazy loading of symbols/sections
The Runtime Linker is currently eagerly loading all object files on all platforms which do not use the system linker for `GHCi`. The problem with this approach is that it requires all symbols to be found. Even those of functions never used/called. This makes the number of libraries required to link things like `mingwex` quite high. To work around this the `rts` was relying on a trick. It itself was compiled with `MingW64-w`'s `GCC`. So it was already linked against `mingwex`. As such, it re-exported the symbols from itself. While this worked it made it impossible to link against `mingwex` in user libraries. And with this means no `C99` code could ever run in `GHCi` on Windows without having the required symbols re-exported from the rts. Consequently this rules out a large number of packages on Windows. SDL2, HMatrix etc. After talking with @rwbarton I have taken the approach of loading entire object files when a symbol is needed instead of doing the dependency tracking on a per symbol basis. This is a lot less fragile and a lot less complicated to implement. The changes come down to the following steps: 1) modify the linker to and introduce a new state for ObjectCode: `Needed`. A Needed object is one that is required for the linking to succeed. The initial set consists of all Object files passed as arguments to the link. 2) Change `ObjectCode`'s to be indexed but not initialized or resolved. This means we know where we would load the symbols, but haven't actually done so. 3) Mark any `ObjectCode` belonging to `.o` passed as argument as required: ObjectState `NEEDED`. 4) During `Resolve` object calls, mark all `ObjectCode` containing the required symbols as `NEEDED` 5) During `lookupSymbol` lookups, (which is called from `linkExpr` and `linkDecl` in `GHCI.hs`) is the symbol is in a not-yet-loaded `ObjectCode` then load the `ObjectCode` on demand and return the address of the symbol. Otherwise produce an unresolved symbols error as expected. 6) On `unloadObj` we then change the state of the object and remove it's symbols from the `reqSymHash` table so it can be reloaded. This change affects all platforms and OSes which use the runtime linker. It seems there are no real perf tests for `GHCi`, but performance shouldn't be impacted much. We gain a lot of time not loading all `obj` files, and we lose some time in `lookupSymbol` when we're finding sections that have to be loaded. The actual finding itself is O(1) (Assuming the hashtnl is perfect) It also consumes slighly more memory as instead of storing just the address of a symbol I also store some other information, like if the symbol is weak or not. This change will break any packages relying on renamed POSIX functions that were re-named and re-exported by the rts. Any packages following the proper naming for functions as found on MSDN will work fine. Test Plan: ./validate on all platforms which use the Runtime linker. Reviewers: thomie, rwbarton, simonmar, erikd, bgamari, austin, hvr Reviewed By: erikd Subscribers: kgardas, gridaphobe, RyanGlScott, simonmar, rwbarton, #ghc_windows_task_force Differential Revision: https://phabricator.haskell.org/D1805 GHC Trac Issues: #11223
-rw-r--r--compiler/main/SysTools.hs19
-rw-r--r--configure.ac3
-rw-r--r--docs/users_guide/8.0.1-notes.rst9
-rw-r--r--libraries/base/System/Posix/Internals.hs131
-rw-r--r--libraries/base/base.cabal10
-rw-r--r--libraries/base/include/HsBase.h2
-rw-r--r--libraries/ghc-prim/ghc-prim.cabal14
-rw-r--r--rts/Linker.c445
-rw-r--r--rts/LinkerInternals.h23
-rw-r--r--rts/RtsSymbols.c319
-rw-r--r--testsuite/tests/ghci/linking/dyn/all.T2
-rw-r--r--testsuite/tests/rts/T11223/Makefile126
-rw-r--r--testsuite/tests/rts/T11223/T11223_link_order_a_b_2_fail.stderr25
-rw-r--r--testsuite/tests/rts/T11223/T11223_link_order_a_b_2_fail.stderr-mingw3225
-rw-r--r--testsuite/tests/rts/T11223/T11223_link_order_a_b_succeed.stdout1
-rw-r--r--testsuite/tests/rts/T11223/T11223_link_order_b_a_2_succeed.stdout1
-rw-r--r--testsuite/tests/rts/T11223/T11223_link_order_b_a_succeed.stdout1
-rw-r--r--testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr25
-rw-r--r--testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr-mingw3225
-rw-r--r--testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr.normalised-mingw3224
-rw-r--r--testsuite/tests/rts/T11223/T11223_simple_link.stdout1
-rw-r--r--testsuite/tests/rts/T11223/T11223_simple_link_lib.stdout1
-rw-r--r--testsuite/tests/rts/T11223/T11223_simple_unused_duplicate_lib.stdout1
-rw-r--r--testsuite/tests/rts/T11223/all.T93
-rw-r--r--testsuite/tests/rts/T11223/bar.c4
-rw-r--r--testsuite/tests/rts/T11223/foo.c14
-rw-r--r--testsuite/tests/rts/T11223/foo.hs5
-rw-r--r--testsuite/tests/rts/T11223/foo2.hs5
-rw-r--r--testsuite/tests/rts/T11223/foo3.hs6
-rw-r--r--testsuite/tests/rts/T11223/power.c10
-rw-r--r--testsuite/tests/rts/T11223/power.hs5
-rw-r--r--testsuite/tests/rts/T11223/power3.hs5
-rw-r--r--testsuite/tests/rts/T11223/power_slow.c14
33 files changed, 943 insertions, 451 deletions
diff --git a/compiler/main/SysTools.hs b/compiler/main/SysTools.hs
index 930ba9ebba..4afb199b84 100644
--- a/compiler/main/SysTools.hs
+++ b/compiler/main/SysTools.hs
@@ -934,9 +934,24 @@ runLink dflags args = do
let (p,args0) = pgm_l dflags
args1 = map Option (getOpts dflags opt_l)
args2 = args0 ++ linkargs ++ args1 ++ args
- mb_env <- getGccEnv args2
- runSomethingResponseFile dflags ld_filter "Linker" p args2 mb_env
+ args3 = argFixup args2 []
+ mb_env <- getGccEnv args3
+ runSomethingResponseFile dflags ld_filter "Linker" p args3 mb_env
where
+ testLib lib = "-l" `isPrefixOf` lib || ".a" `isSuffixOf` lib
+ {- GHC is just blindly appending linker arguments from libraries and
+ the commandline together. This results in very problematic link orders
+ which will cause incorrect linking. Since we're changing the link
+ arguments anyway, let's just make sure libraries are last.
+ This functions moves libraries on the link all the way back
+ but keeps the order amongst them the same. -}
+ argFixup [] r = [] ++ r
+ argFixup (o@(Option opt):xs) r = if testLib opt
+ then argFixup xs (r ++ [o])
+ else o:argFixup xs r
+ argFixup (o@(FileOption _ opt):xs) r = if testLib opt
+ then argFixup xs (r ++ [o])
+ else o:argFixup xs r
ld_filter = case (platformOS (targetPlatform dflags)) of
OSSolaris2 -> sunos_ld_filter
_ -> id
diff --git a/configure.ac b/configure.ac
index a645aec9c0..ff717a1010 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1001,9 +1001,6 @@ AC_COMPILE_IFELSE(
AC_DEFINE([CC_SUPPORTS_TLS],[0],[Define to 1 if __thread is supported])
])
-
-AC_CHECK_FUNCS(__mingw_vfprintf)
-
dnl large address space support (see includes/rts/storage/MBlock.h)
dnl
dnl Darwin has vm_allocate/vm_protect
diff --git a/docs/users_guide/8.0.1-notes.rst b/docs/users_guide/8.0.1-notes.rst
index 2e22f44e08..d79fdf2a90 100644
--- a/docs/users_guide/8.0.1-notes.rst
+++ b/docs/users_guide/8.0.1-notes.rst
@@ -72,6 +72,9 @@ The highlights, since the 7.10 branch, are:
- Support for Windows XP and earlier has been dropped.
+- GHC RTS No longer re-exports POSIX functions under their deprecated
+ names on Windows.
+
Full details
------------
@@ -458,6 +461,12 @@ Runtime system
choose to use at most ⟨x⟩ capabilities, limited by the number of processors
as :rts-flag:`-N` is.
+- The runtime linker is no longer greedy and will load only the needed objects
+ from archives. This means particularly on Windows packages requiring e.g. C99
+ support will now function properly. As part of this the RTS on Windows
+ no longer re-exports deprecated posix functions under the undeprecated names
+ (see :ghc-ticket:`11223`).
+
Build system
~~~~~~~~~~~~
diff --git a/libraries/base/System/Posix/Internals.hs b/libraries/base/System/Posix/Internals.hs
index ceac5ffabc..66e9304d56 100644
--- a/libraries/base/System/Posix/Internals.hs
+++ b/libraries/base/System/Posix/Internals.hs
@@ -352,49 +352,115 @@ type CFilePath = CString
type CFilePath = CWString
#endif
-foreign import ccall unsafe "HsBase.h access"
+foreign import ccall unsafe "HsBase.h __hscore_open"
+ c_open :: CFilePath -> CInt -> CMode -> IO CInt
+
+foreign import ccall safe "HsBase.h __hscore_open"
+ c_safe_open :: CFilePath -> CInt -> CMode -> IO CInt
+
+foreign import ccall unsafe "HsBase.h __hscore_fstat"
+ c_fstat :: CInt -> Ptr CStat -> IO CInt
+
+foreign import ccall unsafe "HsBase.h __hscore_lstat"
+ lstat :: CFilePath -> Ptr CStat -> IO CInt
+
+{- Note: Win32 POSIX functions
+Functions that are not part of the POSIX standards were
+at some point deprecated by Microsoft. This deprecation
+was performed by renaming the functions according to the
+C++ ABI Section 17.6.4.3.2b. This was done to free up the
+namespace of normal Windows programs since Windows isn't
+POSIX compliant anyway.
+
+These were working before since the RTS was re-exporting
+these symbols under the undeprecated names. This is no longer
+being done. See #11223
+
+See https://msdn.microsoft.com/en-us/library/ms235384.aspx
+for more.
+-}
+#if defined(mingw32_HOST_OS)
+foreign import ccall unsafe "io.h _lseeki64"
+ c_lseek :: CInt -> Int64 -> CInt -> IO Int64
+
+foreign import ccall unsafe "HsBase.h _access"
c_access :: CString -> CInt -> IO CInt
-foreign import ccall unsafe "HsBase.h chmod"
+foreign import ccall unsafe "HsBase.h _chmod"
c_chmod :: CString -> CMode -> IO CInt
-foreign import ccall unsafe "HsBase.h close"
+foreign import ccall unsafe "HsBase.h _close"
c_close :: CInt -> IO CInt
-foreign import ccall unsafe "HsBase.h creat"
+foreign import ccall unsafe "HsBase.h _creat"
c_creat :: CString -> CMode -> IO CInt
-foreign import ccall unsafe "HsBase.h dup"
+foreign import ccall unsafe "HsBase.h _dup"
c_dup :: CInt -> IO CInt
-foreign import ccall unsafe "HsBase.h dup2"
+foreign import ccall unsafe "HsBase.h _dup2"
c_dup2 :: CInt -> CInt -> IO CInt
-foreign import ccall unsafe "HsBase.h __hscore_fstat"
- c_fstat :: CInt -> Ptr CStat -> IO CInt
-
-foreign import ccall unsafe "HsBase.h isatty"
+foreign import ccall unsafe "HsBase.h _isatty"
c_isatty :: CInt -> IO CInt
-#if defined(mingw32_HOST_OS)
-foreign import ccall unsafe "io.h _lseeki64"
- c_lseek :: CInt -> Int64 -> CInt -> IO Int64
+-- See Note: CSsize
+foreign import capi unsafe "HsBase.h _read"
+ c_read :: CInt -> Ptr Word8 -> CSize -> IO CSsize
+
+-- See Note: CSsize
+foreign import capi safe "HsBase.h _read"
+ c_safe_read :: CInt -> Ptr Word8 -> CSize -> IO CSsize
+
+foreign import ccall unsafe "HsBase.h _umask"
+ c_umask :: CMode -> IO CMode
+
+-- See Note: CSsize
+foreign import capi unsafe "HsBase.h _write"
+ c_write :: CInt -> Ptr Word8 -> CSize -> IO CSsize
+
+-- See Note: CSsize
+foreign import capi safe "HsBase.h _write"
+ c_safe_write :: CInt -> Ptr Word8 -> CSize -> IO CSsize
+
+foreign import ccall unsafe "HsBase.h _unlink"
+ c_unlink :: CString -> IO CInt
+
+foreign import ccall unsafe "HsBase.h _pipe"
+ c_pipe :: Ptr CInt -> IO CInt
+
+foreign import capi unsafe "HsBase.h _utime"
+ c_utime :: CString -> Ptr CUtimbuf -> IO CInt
+
+foreign import ccall unsafe "HsBase.h _getpid"
+ c_getpid :: IO CPid
#else
-- We use CAPI as on some OSs (eg. Linux) this is wrapped by a macro
-- which redirects to the 64-bit-off_t versions when large file
-- support is enabled.
foreign import capi unsafe "unistd.h lseek"
c_lseek :: CInt -> COff -> CInt -> IO COff
-#endif
-foreign import ccall unsafe "HsBase.h __hscore_lstat"
- lstat :: CFilePath -> Ptr CStat -> IO CInt
+foreign import ccall unsafe "HsBase.h access"
+ c_access :: CString -> CInt -> IO CInt
-foreign import ccall unsafe "HsBase.h __hscore_open"
- c_open :: CFilePath -> CInt -> CMode -> IO CInt
+foreign import ccall unsafe "HsBase.h chmod"
+ c_chmod :: CString -> CMode -> IO CInt
-foreign import ccall safe "HsBase.h __hscore_open"
- c_safe_open :: CFilePath -> CInt -> CMode -> IO CInt
+foreign import ccall unsafe "HsBase.h close"
+ c_close :: CInt -> IO CInt
+
+foreign import ccall unsafe "HsBase.h creat"
+ c_creat :: CString -> CMode -> IO CInt
+
+foreign import ccall unsafe "HsBase.h dup"
+ c_dup :: CInt -> IO CInt
+
+foreign import ccall unsafe "HsBase.h dup2"
+ c_dup2 :: CInt -> CInt -> IO CInt
+
+foreign import ccall unsafe "HsBase.h isatty"
+ c_isatty :: CInt -> IO CInt
-- See Note: CSsize
foreign import capi unsafe "HsBase.h read"
@@ -404,9 +470,6 @@ foreign import capi unsafe "HsBase.h read"
foreign import capi safe "HsBase.h read"
c_safe_read :: CInt -> Ptr Word8 -> CSize -> IO CSsize
-foreign import ccall unsafe "HsBase.h __hscore_stat"
- c_stat :: CFilePath -> Ptr CStat -> IO CInt
-
foreign import ccall unsafe "HsBase.h umask"
c_umask :: CMode -> IO CMode
@@ -418,14 +481,24 @@ foreign import capi unsafe "HsBase.h write"
foreign import capi safe "HsBase.h write"
c_safe_write :: CInt -> Ptr Word8 -> CSize -> IO CSsize
-foreign import ccall unsafe "HsBase.h __hscore_ftruncate"
- c_ftruncate :: CInt -> COff -> IO CInt
-
foreign import ccall unsafe "HsBase.h unlink"
c_unlink :: CString -> IO CInt
+foreign import ccall unsafe "HsBase.h pipe"
+ c_pipe :: Ptr CInt -> IO CInt
+
+foreign import capi unsafe "HsBase.h utime"
+ c_utime :: CString -> Ptr CUtimbuf -> IO CInt
+
foreign import ccall unsafe "HsBase.h getpid"
c_getpid :: IO CPid
+#endif
+
+foreign import ccall unsafe "HsBase.h __hscore_stat"
+ c_stat :: CFilePath -> Ptr CStat -> IO CInt
+
+foreign import ccall unsafe "HsBase.h __hscore_ftruncate"
+ c_ftruncate :: CInt -> COff -> IO CInt
#if !defined(mingw32_HOST_OS)
foreign import capi unsafe "HsBase.h fcntl"
@@ -447,9 +520,6 @@ foreign import ccall unsafe "HsBase.h link"
foreign import capi unsafe "HsBase.h mkfifo"
c_mkfifo :: CString -> CMode -> IO CInt
-foreign import ccall unsafe "HsBase.h pipe"
- c_pipe :: Ptr CInt -> IO CInt
-
foreign import capi unsafe "signal.h sigemptyset"
c_sigemptyset :: Ptr CSigset -> IO CInt
@@ -467,9 +537,6 @@ foreign import capi unsafe "HsBase.h tcgetattr"
foreign import capi unsafe "HsBase.h tcsetattr"
c_tcsetattr :: CInt -> CInt -> Ptr CTermios -> IO CInt
-foreign import capi unsafe "HsBase.h utime"
- c_utime :: CString -> Ptr CUtimbuf -> IO CInt
-
foreign import ccall unsafe "HsBase.h waitpid"
c_waitpid :: CPid -> Ptr CInt -> CInt -> IO CPid
#endif
diff --git a/libraries/base/base.cabal b/libraries/base/base.cabal
index fd3c6081fb..2d1a998c95 100644
--- a/libraries/base/base.cabal
+++ b/libraries/base/base.cabal
@@ -336,7 +336,15 @@ Library
-- OS Specific
if os(windows)
- extra-libraries: wsock32, user32, shell32
+ -- Windows requires some extra libraries for linking because the RTS
+ -- is no longer re-exporting them.
+ -- msvcrt: standard C library. The RTS will automatically include this,
+ -- but is added for completeness.
+ -- mingwex: provides C99 compatibility. libm is a stub on MingW.
+ -- mingw32: Unfortunately required because of a resource leak between
+ -- mingwex and mingw32. the __math_err symbol is defined in
+ -- mingw32 which is required by mingwex.
+ extra-libraries: wsock32, user32, shell32, msvcrt, mingw32, mingwex
exposed-modules:
GHC.IO.Encoding.CodePage.API
GHC.IO.Encoding.CodePage.Table
diff --git a/libraries/base/include/HsBase.h b/libraries/base/include/HsBase.h
index 3fe514e555..cfe046426d 100644
--- a/libraries/base/include/HsBase.h
+++ b/libraries/base/include/HsBase.h
@@ -299,7 +299,7 @@ INLINE int
__hscore_setmode( int fd, HsBool toBin )
{
#if defined(_WIN32)
- return setmode(fd,(toBin == HS_BOOL_TRUE) ? _O_BINARY : _O_TEXT);
+ return _setmode(fd,(toBin == HS_BOOL_TRUE) ? _O_BINARY : _O_TEXT);
#else
return 0;
#endif
diff --git a/libraries/ghc-prim/ghc-prim.cabal b/libraries/ghc-prim/ghc-prim.cabal
index 2077e6d799..60f229c602 100644
--- a/libraries/ghc-prim/ghc-prim.cabal
+++ b/libraries/ghc-prim/ghc-prim.cabal
@@ -53,6 +53,20 @@ Library
GHC.Tuple
GHC.Types
+ -- OS Specific
+ if os(windows)
+ -- Windows requires some extra libraries for linking because the RTS
+ -- is no longer re-exporting them (see #11223)
+ -- msvcrt: standard C library. The RTS will automatically include this,
+ -- but is added for completeness.
+ -- mingwex: provides C99 compatibility. libm is a stub on MingW.
+ -- mingw32: Unfortunately required because of a resource leak between
+ -- mingwex and mingw32. the __math_err symbol is defined in
+ -- mingw32 which is required by mingwex.
+ -- user32: provides access to apis to modify user components (UI etc)
+ -- on Windows. Required because of mingw32.
+ extra-libraries: user32, mingw32, mingwex
+
if flag(include-ghc-prim)
exposed-modules: GHC.Prim
diff --git a/rts/Linker.c b/rts/Linker.c
index be484841d2..a296afe1bb 100644
--- a/rts/Linker.c
+++ b/rts/Linker.c
@@ -130,13 +130,85 @@
#include <sys/tls.h>
#endif
+/* SymbolInfo tracks a symbol's address, the object code from which
+ it originated, and whether or not it's weak.
+
+ Refactoring idea: For the sake of memory efficiency it might be worthwhile
+ dropping the `weak` field, instead keeping a list of weak symbols in
+ ObjectCode. This is task #11816.
+*/
typedef struct _RtsSymbolInfo {
void *value;
- const ObjectCode *owner;
+ ObjectCode *owner;
HsBool weak;
} RtsSymbolInfo;
-/* Hash table mapping symbol names to RtsSymbolInfo */
+/* `symhash` is a Hash table mapping symbol names to RtsSymbolInfo.
+ This hashtable will contain information on all symbols
+ that we know of, however the .o they are in may not be loaded.
+
+ Until the ObjectCode the symbol belongs to is actually
+ loaded this symbol may be replaced. So do not rely on
+ addresses of unloaded symbols.
+
+ Note [runtime-linker-phases]
+ --------------------------------------
+ Broadly the behavior of the runtime linker can be
+ split into the following four phases:
+
+ - Indexing (e.g. ocVerifyImage and ocGetNames)
+ - Initialization (e.g. ocResolve and ocRunInit)
+ - Resolve (e.g. resolveObjs())
+ - Lookup (e.g. lookupSymbol)
+
+ This is to enable lazy loading of symbols. Eager loading is problematic
+ as it means that all symbols must be available, even those which we will
+ never use. This is especially painful of Windows, where the number of
+ libraries required to link things like mingwex grows to be quite high.
+
+ We proceed through these stages as follows,
+
+ * During Indexing we verify and open the ObjectCode and
+ perform a quick scan/indexing of the ObjectCode. All the work
+ required to actually load the ObjectCode is done.
+
+ All symbols from the ObjectCode is also inserted into
+ `symhash`, where possible duplicates are handled via the semantics
+ described in `ghciInsertSymbolTable`.
+
+ This phase will produce ObjectCode with status `OBJECT_LOADED` or `OBJECT_NEEDED`
+ depending on whether they are an archive members or not.
+
+ * During initialization we load ObjectCode, perform relocations, execute
+ static constructors etc. This phase may trigger other ObjectCodes to
+ be loaded because of the calls to lookupSymbol.
+
+ This phase will produce ObjectCode with status `OBJECT_NEEDED` if the
+ previous status was `OBJECT_LOADED`.
+
+ * During resolve we attempt to resolve all the symbols needed for the
+ initial link. This essentially means, that for any ObjectCode given
+ directly to the command-line we perform lookupSymbols on the required
+ symbols. lookupSymbols may trigger the loading of additional ObjectCode
+ if required.
+
+ This phase will produce ObjectCode with status `OBJECT_RESOLVED` if
+ the previous status was `OBJECT_NEEDED`.
+
+ * Lookup symbols is used to lookup any symbols required, both during initial
+ link and during statement and expression compilations in the REPL.
+ Declaration of e.g. an foreign import, will eventually call lookupSymbol
+ which will either fail (symbol unknown) or succeed (and possibly triggered a
+ load).
+
+ This phase may transition an ObjectCode from `OBJECT_LOADED` to `OBJECT_RESOLVED`
+
+ When a new scope is introduced (e.g. a new module imported) GHCi does a full re-link
+ by calling unloadObj and starting over.
+ When a new declaration or statement is performed ultimately lookupSymbol is called
+ without doing a re-link.
+
+ */
static /*Str*/HashTable *symhash;
/* List of currently loaded objects */
@@ -193,7 +265,7 @@ static pathchar* pathdup(pathchar *path)
ret = wcsdup(path);
#else
/* sigh, strdup() isn't a POSIX function, so do it the long way */
- ret = stgMallocBytes( strlen(path)+1, "loadObj" );
+ ret = stgMallocBytes( strlen(path)+1, "pathdup" );
strcpy(ret, path);
#endif
return ret;
@@ -215,6 +287,9 @@ static pathchar* mkPath(char* path)
#endif
}
+/* Generic wrapper function to try and Resolve and RunInit oc files */
+int ocTryLoad( ObjectCode* oc );
+
#if defined(OBJFORMAT_ELF)
static int ocVerifyImage_ELF ( ObjectCode* oc );
static int ocGetNames_ELF ( ObjectCode* oc );
@@ -264,10 +339,21 @@ static void machoInitSymbolsWithoutUnderscore( void );
#endif
#if defined(OBJFORMAT_PEi386)
+
+/* Add ld symbol for PE image base. */
+#if defined(__GNUC__)
+#define __ImageBase __MINGW_LSYMBOL(_image_base__)
+#endif
+
+/* Get the base of the module. */
+/* This symbol is defined by ld. */
+extern IMAGE_DOS_HEADER __ImageBase;
+#define __image_base (void*)((HINSTANCE)&__ImageBase)
+
// MingW-w64 is missing these from the implementation. So we have to look them up
typedef DLL_DIRECTORY_COOKIE(WINAPI *LPAddDLLDirectory)(PCWSTR NewDirectory);
typedef WINBOOL(WINAPI *LPRemoveDLLDirectory)(DLL_DIRECTORY_COOKIE Cookie);
-#endif
+#endif /* OBJFORMAT_PEi386 */
static void freeProddableBlocks (ObjectCode *oc);
@@ -405,12 +491,36 @@ static void *mmap_32bit_base = (void *)MMAP_32BIT_BASE_DEFAULT;
#define MAP_ANONYMOUS MAP_ANON
#endif
+static void ghciRemoveSymbolTable(HashTable *table, const char *key,
+ ObjectCode *owner)
+{
+ RtsSymbolInfo *pinfo = lookupStrHashTable(table, key);
+ if (!pinfo || owner != pinfo->owner) return;
+ removeStrHashTable(table, key, NULL);
+ stgFree(pinfo);
+}
+
/* -----------------------------------------------------------------------------
* Insert symbols into hash tables, checking for duplicates.
*
* Returns: 0 on failure, nonzero on success
*/
-
+/*
+ Note [weak-symbols-support]
+ -------------------------------------
+ While ghciInsertSymbolTable does implement extensive
+ logic for weak symbol support, weak symbols are not currently
+ fully supported by the RTS. This code is mostly here for COMDAT
+ support which uses the weak symbols support.
+
+ Linking weak symbols defined purely in C code with other C code
+ should also work, probably. Observing weak symbols in Haskell
+ won't.
+
+ Some test have been written for weak symbols but have been disabled
+ mostly because it's unsure how the weak symbols support should look.
+ See Trac #11223
+ */
static int ghciInsertSymbolTable(
pathchar* obj_name,
HashTable *table,
@@ -429,11 +539,24 @@ static int ghciInsertSymbolTable(
insertStrHashTable(table, key, pinfo);
return 1;
}
- else if ((!pinfo->weak || pinfo->value) && weak)
+ else if (weak && data && pinfo->weak && !pinfo->value)
{
- return 1; /* duplicate weak symbol, throw it away */
+ /* The existing symbol is weak with a zero value; replace it with the new symbol. */
+ pinfo->value = data;
+ pinfo->owner = owner;
+ return 1;
+ }
+ else if (weak)
+ {
+ return 1; /* weak symbol, because the symbol is weak, data = 0 and we
+ already know of another copy throw this one away.
+
+ or both weak symbols have a nonzero value. Keep the existing one.
+
+ This also preserves the semantics of linking against
+ the first symbol we find. */
}
- else if (pinfo->weak) /* weak symbol is in the table */
+ else if (pinfo->weak && !weak) /* weak symbol is in the table */
{
/* override the weak definition with the non-weak one */
pinfo->value = data;
@@ -441,6 +564,42 @@ static int ghciInsertSymbolTable(
pinfo->weak = HS_BOOL_FALSE;
return 1;
}
+ else if ( pinfo->owner
+ && pinfo->owner->status != OBJECT_RESOLVED
+ && pinfo->owner->status != OBJECT_NEEDED)
+ {
+ /* If the other symbol hasn't been loaded or will be loaded and we want to
+ explicitly load the new one, we can just swap it out and load the one
+ that has been requested. If not, just keep the first one encountered.
+
+ Because the `symHash' table consists symbols we've also not loaded but
+ found during the initial scan this is safe to do. If however the existing
+ symbol has been loaded then it means we have a duplicate.
+
+ This is essentially emulating the behavior of a linker wherein it will always
+ link in object files that are .o file arguments, but only take object files
+ from archives as needed. */
+ if (owner && (owner->status == OBJECT_NEEDED || owner->status == OBJECT_RESOLVED)) {
+ pinfo->value = data;
+ pinfo->owner = owner;
+ pinfo->weak = weak;
+ }
+
+ return 1;
+ }
+ else if (pinfo->owner == owner)
+ {
+ /* If it's the same symbol, ignore. This makes ghciInsertSymbolTable idempotent */
+ return 1;
+ }
+ else if (owner && owner->status == OBJECT_LOADED)
+ {
+ /* If the duplicate symbol is just in state OBJECT_LOADED it means we're in discovery of an
+ member. It's not a real duplicate yet. If the Oc Becomes OBJECT_NEEDED then ocTryLoad will
+ call this function again to trigger the duplicate error. */
+ return 1;
+ }
+
pathchar* archiveName = NULL;
debugBelch(
"GHC runtime linker: fatal error: I found a duplicate definition for symbol\n"
@@ -469,8 +628,14 @@ static int ghciInsertSymbolTable(
return 0;
}
-static HsBool ghciLookupSymbolTable(HashTable *table,
- const char *key, void **result)
+/* -----------------------------------------------------------------------------
+* Looks up symbols into hash tables.
+*
+* Returns: 0 on failure and result is not set,
+* nonzero on success and result set to nonzero pointer
+*/
+static HsBool ghciLookupSymbolInfo(HashTable *table,
+ const char *key, RtsSymbolInfo **result)
{
RtsSymbolInfo *pinfo = lookupStrHashTable(table, key);
if (!pinfo) {
@@ -478,22 +643,14 @@ static HsBool ghciLookupSymbolTable(HashTable *table,
return HS_BOOL_FALSE;
}
if (pinfo->weak)
- IF_DEBUG(linker, debugBelch("lookup: promoting %s\n", key));
+ IF_DEBUG(linker, debugBelch("lookupSymbolInfo: promoting %s\n", key));
/* Once it's looked up, it can no longer be overridden */
pinfo->weak = HS_BOOL_FALSE;
- *result = pinfo->value;
+ *result = pinfo;
return HS_BOOL_TRUE;
}
-static void ghciRemoveSymbolTable(HashTable *table, const char *key,
- ObjectCode *owner)
-{
- RtsSymbolInfo *pinfo = lookupStrHashTable(table, key);
- if (!pinfo || owner != pinfo->owner) return;
- removeStrHashTable(table, key, NULL);
- stgFree(pinfo);
-}
/* -----------------------------------------------------------------------------
* initialize the object linker
*/
@@ -573,6 +730,14 @@ initLinker_ (int retain_cafs)
barf("ghciInsertSymbolTable failed");
}
+#if defined(OBJFORMAT_PEi386)
+ if (!ghciInsertSymbolTable(WSTR("(GHCi/Ld special symbols)"),
+ symhash, "__image_base__", __image_base, HS_BOOL_TRUE, NULL)) {
+ barf("ghciInsertSymbolTable failed");
+ }
+#endif /* OBJFORMAT_PEi386 */
+
+
// Redirect newCAF to newRetainedCAF if retain_cafs is true.
if (! ghciInsertSymbolTable(WSTR("(GHCi built-in symbols)"), symhash,
MAYBE_LEADING_UNDERSCORE_STR("newCAF"),
@@ -914,7 +1079,7 @@ addDLL( pathchar *dll_name )
buf = stgMallocBytes(bufsize * sizeof(wchar_t), "addDLL");
/* These are ordered by probability of success and order we'd like them */
- const wchar_t *formats[] = { L"%s.DLL", L"%s.DRV", L"lib%s.DLL", L"%s" };
+ const wchar_t *formats[] = { L"%ls.DLL", L"%ls.DRV", L"lib%ls.DLL", L"%ls" };
const DWORD flags[] = { LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, 0 };
int cFormat;
@@ -1154,13 +1319,14 @@ HsInt insertSymbol(pathchar* obj_name, char* key, void* data)
*/
static void* lookupSymbol_ (char *lbl)
{
- void *val;
IF_DEBUG(linker, debugBelch("lookupSymbol: looking up %s\n", lbl));
ASSERT(symhash != NULL);
+ RtsSymbolInfo *pinfo;
- if (!ghciLookupSymbolTable(symhash, lbl, &val)) {
+ if (!ghciLookupSymbolInfo(symhash, lbl, &pinfo)) {
IF_DEBUG(linker, debugBelch("lookupSymbol: symbol not found\n"));
+
# if defined(OBJFORMAT_ELF)
return internal_dlsym(lbl);
# elif defined(OBJFORMAT_MACHO)
@@ -1189,7 +1355,25 @@ static void* lookupSymbol_ (char *lbl)
return NULL;
# endif
} else {
+ void *val = pinfo->value;
IF_DEBUG(linker, debugBelch("lookupSymbol: value of %s is %p\n", lbl, val));
+
+ int r;
+ ObjectCode* oc = pinfo->owner;
+
+ /* Symbol can be found during linking, but hasn't been relocated. Do so now.
+ See Note [runtime-linker-phases] */
+ if (oc && oc->status == OBJECT_LOADED) {
+ oc->status = OBJECT_NEEDED;
+ IF_DEBUG(linker, debugBelch("lookupSymbol: on-demand loaded symbol '%s'\n", lbl));
+ r = ocTryLoad(oc);
+
+ if (!r) {
+ errorBelch("Could not on-demand load symbol '%s'\n", lbl);
+ return NULL;
+ }
+ }
+
return val;
}
}
@@ -1243,24 +1427,26 @@ void ghci_enquire ( char* addr );
void ghci_enquire ( char* addr )
{
int i;
- char* sym;
- char* a;
+ SymbolInfo sym;
+ RtsSymbolInfo* a;
const int DELTA = 64;
ObjectCode* oc;
for (oc = objects; oc; oc = oc->next) {
for (i = 0; i < oc->n_symbols; i++) {
sym = oc->symbols[i];
- if (sym == NULL) continue;
+ if (sym.name == NULL) continue;
a = NULL;
if (a == NULL) {
- ghciLookupSymbolTable(symhash, sym, (void **)&a);
+ ghciLookupSymbolInfo(symhash, sym.name, &a);
}
if (a == NULL) {
// debugBelch("ghci_enquire: can't find %s\n", sym);
}
- else if (addr-DELTA <= a && a <= addr+DELTA) {
- debugBelch("%p + %3d == `%s'\n", addr, (int)(a - addr), sym);
+ else if ( a->value
+ && addr-DELTA <= (char*)a->value
+ && (char*)a->value <= addr+DELTA) {
+ debugBelch("%p + %3d == `%s'\n", addr, (int)((char*)a->value - addr), sym.name);
}
}
}
@@ -1621,8 +1807,8 @@ static void removeOcSymbols (ObjectCode *oc)
// 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);
+ if (oc->symbols[i].name != NULL) {
+ ghciRemoveSymbolTable(symhash, oc->symbols[i].name, oc);
}
}
@@ -1742,6 +1928,17 @@ void freeObjectCode (ObjectCode *oc)
stgFree(oc);
}
+/* -----------------------------------------------------------------------------
+* Sets the initial status of a fresh ObjectCode
+*/
+static void setOcInitialStatus(ObjectCode* oc) {
+ if (oc->archiveMemberName == NULL) {
+ oc->status = OBJECT_NEEDED;
+ }
+ else {
+ oc->status = OBJECT_LOADED;
+ }
+}
static ObjectCode*
mkOc( pathchar *path, char *image, int imageSize,
@@ -1768,11 +1965,12 @@ mkOc( pathchar *path, char *image, int imageSize,
if (archiveMemberName) {
oc->archiveMemberName = stgMallocBytes( strlen(archiveMemberName)+1, "loadObj" );
strcpy(oc->archiveMemberName, archiveMemberName);
- }
- else {
+ } else {
oc->archiveMemberName = NULL;
}
+ setOcInitialStatus( oc );
+
oc->fileSize = imageSize;
oc->symbols = NULL;
oc->n_sections = 0;
@@ -2487,55 +2685,93 @@ static HsInt loadOc (ObjectCode* oc)
return r;
}
- /* loaded, but not resolved yet */
- oc->status = OBJECT_LOADED;
+ /* loaded, but not resolved yet, ensure the OC is in a consistent state */
+ setOcInitialStatus( oc );
IF_DEBUG(linker, debugBelch("loadOc: done.\n"));
return 1;
}
/* -----------------------------------------------------------------------------
- * resolve all the currently unlinked objects in memory
- *
- * Returns: 1 if ok, 0 on error.
- */
-static HsInt resolveObjs_ (void)
-{
- ObjectCode *oc;
+* try to load and initialize an ObjectCode into memory
+*
+* Returns: 1 if ok, 0 on error.
+*/
+int ocTryLoad (ObjectCode* oc) {
int r;
- IF_DEBUG(linker, debugBelch("resolveObjs: start\n"));
+ if (oc->status != OBJECT_NEEDED) {
+ return 1;
+ }
+
+ /* Check for duplicate symbols by looking into `symhash`.
+ Duplicate symbols are any symbols which exist
+ in different ObjectCodes that have both been loaded, or
+ are to be loaded by this call.
+
+ This call is intended to have no side-effects when a non-duplicate
+ symbol is re-inserted.
+ */
+ int x;
+ SymbolInfo symbol;
+ for (x = 0; x < oc->n_symbols; x++) {
+ symbol = oc->symbols[x];
+ if ( symbol.name
+ && symbol.addr
+ && !ghciInsertSymbolTable(oc->fileName, symhash, symbol.name, symbol.addr, symbol.isWeak, oc)){
+ return 0;
+ }
+ }
- for (oc = objects; oc; oc = oc->next) {
- if (oc->status != OBJECT_RESOLVED) {
# if defined(OBJFORMAT_ELF)
- r = ocResolve_ELF ( oc );
+ r = ocResolve_ELF ( oc );
# elif defined(OBJFORMAT_PEi386)
- r = ocResolve_PEi386 ( oc );
+ r = ocResolve_PEi386 ( oc );
# elif defined(OBJFORMAT_MACHO)
- r = ocResolve_MachO ( oc );
+ r = ocResolve_MachO ( oc );
# else
- barf("resolveObjs: not implemented on this platform");
+ barf("ocTryLoad: not implemented on this platform");
# endif
- if (!r) { return r; }
+ if (!r) { return r; }
- // run init/init_array/ctors/mod_init_func
+ // run init/init_array/ctors/mod_init_func
- loading_obj = oc; // tells foreignExportStablePtr what to do
+ loading_obj = oc; // tells foreignExportStablePtr what to do
#if defined(OBJFORMAT_ELF)
- r = ocRunInit_ELF ( oc );
+ r = ocRunInit_ELF ( oc );
#elif defined(OBJFORMAT_PEi386)
- r = ocRunInit_PEi386 ( oc );
+ r = ocRunInit_PEi386 ( oc );
#elif defined(OBJFORMAT_MACHO)
- r = ocRunInit_MachO ( oc );
+ r = ocRunInit_MachO ( oc );
#else
- barf("resolveObjs: initializers not implemented on this platform");
+ barf("ocTryLoad: initializers not implemented on this platform");
#endif
- loading_obj = NULL;
+ loading_obj = NULL;
+
+ if (!r) { return r; }
+
+ oc->status = OBJECT_RESOLVED;
+
+ return 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * resolve all the currently unlinked objects in memory
+ *
+ * Returns: 1 if ok, 0 on error.
+ */
+static HsInt resolveObjs_ (void)
+{
+ ObjectCode *oc;
+ int r;
- if (!r) { return r; }
+ IF_DEBUG(linker, debugBelch("resolveObjs: start\n"));
- oc->status = OBJECT_RESOLVED;
+ for (oc = objects; oc; oc = oc->next) {
+ r = ocTryLoad(oc);
+ if (!r)
+ {
+ return r;
}
}
@@ -3103,6 +3339,7 @@ typedef
#define MYIMAGE_SYM_CLASS_EXTERNAL 2
#define MYIMAGE_SYM_CLASS_STATIC 3
#define MYIMAGE_SYM_UNDEFINED 0
+#define MYIMAGE_SYM_CLASS_WEAK_EXTERNAL 105
/* From PE spec doc, section 3.1 */
#define MYIMAGE_SCN_CNT_CODE 0x00000020
@@ -3351,7 +3588,7 @@ lookupSymbolInDLLs ( UChar *lbl )
ret->next = indirects;
indirects = ret;
IF_DEBUG(linker,
- debugBelch("warning: %s from %S is linked instead of %s",
+ debugBelch("warning: %s from %S is linked instead of %s\n",
(char*)(lbl+6+STRIP_LEADING_UNDERSCORE), o_dll->name, (char*)lbl));
return (void*) & ret->addr;
}
@@ -3707,7 +3944,7 @@ ocGetNames_PEi386 ( ObjectCode* oc )
/* Copy exported symbols into the ObjectCode. */
oc->n_symbols = hdr->NumberOfSymbols;
- oc->symbols = stgCallocBytes(sizeof(char*), oc->n_symbols,
+ oc->symbols = stgCallocBytes(sizeof(SymbolInfo), oc->n_symbols,
"ocGetNames_PEi386(oc->symbols)");
/* Work out the size of the global BSS section */
@@ -3744,7 +3981,6 @@ ocGetNames_PEi386 ( ObjectCode* oc )
myindex ( sizeof_COFF_symbol, symtab, i );
addr = NULL;
-
HsBool isWeak = HS_BOOL_FALSE;
if (symtab_i->SectionNumber != MYIMAGE_SYM_UNDEFINED) {
/* This symbol is global and defined, viz, exported */
@@ -3757,7 +3993,7 @@ ocGetNames_PEi386 ( ObjectCode* oc )
= (COFF_section*) myindex ( sizeof_COFF_section,
sectab,
symtab_i->SectionNumber-1 );
- if (symtab_i->StorageClass == MYIMAGE_SYM_CLASS_EXTERNAL
+ if ( symtab_i->StorageClass == MYIMAGE_SYM_CLASS_EXTERNAL
|| ( symtab_i->StorageClass == MYIMAGE_SYM_CLASS_STATIC
&& sectabent->Characteristics & MYIMAGE_SCN_LNK_COMDAT)
) {
@@ -3769,9 +4005,11 @@ ocGetNames_PEi386 ( ObjectCode* oc )
}
}
}
- else
- if (symtab_i->SectionNumber == MYIMAGE_SYM_UNDEFINED
- && symtab_i->Value > 0) {
+ else if (symtab_i->StorageClass == MYIMAGE_SYM_CLASS_WEAK_EXTERNAL) {
+ isWeak = HS_BOOL_TRUE;
+ }
+ else if ( symtab_i->SectionNumber == MYIMAGE_SYM_UNDEFINED
+ && symtab_i->Value > 0) {
/* This symbol isn't in any section at all, ie, global bss.
Allocate zeroed space for it from the BSS section */
addr = bss;
@@ -3779,13 +4017,16 @@ ocGetNames_PEi386 ( ObjectCode* oc )
IF_DEBUG(linker, debugBelch("bss symbol @ %p %u\n", addr, symtab_i->Value));
}
- if (addr != NULL ) {
- sname = cstring_from_COFF_symbol_name ( symtab_i->Name, strtab );
- /* debugBelch("addSymbol %p `%s \n", addr,sname); */
+ if (addr != NULL || isWeak == HS_BOOL_TRUE) {
+ sname = cstring_from_COFF_symbol_name(symtab_i->Name, strtab);
+
+ /* debugBelch("addSymbol %p `%s' Weak:%lld \n", addr, sname, isWeak); */
IF_DEBUG(linker, debugBelch("addSymbol %p `%s'\n", addr,sname);)
ASSERT(i >= 0 && i < oc->n_symbols);
/* cstring_from_COFF_symbol_name always succeeds. */
- oc->symbols[i] = (char*)sname;
+ oc->symbols[i].name = (char*)sname;
+ oc->symbols[i].addr = addr;
+ oc->symbols[i].isWeak = isWeak;
if (! ghciInsertSymbolTable(oc->fileName, symhash, (char*)sname, addr,
isWeak, oc)) {
return 0;
@@ -3984,10 +4225,11 @@ ocResolve_PEi386 ( ObjectCode* oc )
} else {
copyName ( sym->Name, strtab, symbol, 1000-1 );
S = (size_t) lookupSymbol_( (char*)symbol );
- if ((void*)S != NULL) goto foundit;
- errorBelch("%" PATH_FMT ": unknown symbol `%s'", oc->fileName, symbol);
- return 0;
- foundit:;
+ if ((void*)S == NULL) {
+
+ errorBelch("%" PATH_FMT ": unknown symbol `%s'\n", oc->fileName, symbol);
+ return 0;
+ }
}
/* All supported relocations write at least 4 bytes */
checkProddableBlock(oc, pP, 4);
@@ -4080,7 +4322,7 @@ ocResolve_PEi386 ( ObjectCode* oc )
}
}
- IF_DEBUG(linker, debugBelch("completed %" PATH_FMT, oc->fileName));
+ IF_DEBUG(linker, debugBelch("completed %" PATH_FMT "\n", oc->fileName));
return 1;
}
@@ -4122,6 +4364,12 @@ ocRunInit_PEi386 ( ObjectCode *oc )
getProgArgv(&argc, &argv);
getProgEnvv(&envc, &envv);
+ /* TODO: This part is just looking for .ctors section. This can be optimized
+ and should for objects compiled with function sections as these produce a
+ large amount of sections.
+
+ This can be done by saving the index of the .ctor section in the ObjectCode
+ from ocGetNames. Then this loop isn't needed. */
for (i = 0; i < hdr->NumberOfSections; i++) {
COFF_section* sectab_i
= (COFF_section*)
@@ -4830,7 +5078,7 @@ ocGetNames_ELF ( ObjectCode* oc )
nent = shdr[i].sh_size / sizeof(Elf_Sym);
oc->n_symbols = nent;
- oc->symbols = stgCallocBytes(oc->n_symbols, sizeof(char*),
+ oc->symbols = stgCallocBytes(oc->n_symbols, sizeof(SymbolInfo),
"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
@@ -4841,10 +5089,10 @@ ocGetNames_ELF ( ObjectCode* oc )
// ie we should use j = shdr[i].sh_info
for (j = 0; j < nent; j++) {
- char isLocal = FALSE; /* avoids uninit-var warning */
- HsBool isWeak = HS_BOOL_FALSE;
- char* ad = NULL;
- char* nm = strtab + stab[j].st_name;
+ char isLocal = FALSE; /* avoids uninit-var warning */
+ HsBool isWeak = HS_BOOL_FALSE;
+ unsigned char* ad = NULL;
+ char* nm = strtab + stab[j].st_name;
unsigned short shndx = stab[j].st_shndx;
Elf_Word secno;
@@ -4923,6 +5171,7 @@ ocGetNames_ELF ( ObjectCode* oc )
/* And the decision is ... */
+ oc->symbols[j].name = nm;
if (ad != NULL) {
ASSERT(nm != NULL);
/* Acquire! */
@@ -4933,7 +5182,8 @@ ocGetNames_ELF ( ObjectCode* oc )
nm, ad, isWeak, oc)) {
goto fail;
}
- oc->symbols[j] = nm;
+ oc->symbols[j].addr = ad;
+ oc->symbols[j].isWeak = isWeak;
}
} else {
/* Skip. */
@@ -4948,7 +5198,7 @@ ocGetNames_ELF ( ObjectCode* oc )
strtab + stab[j].st_name
);
*/
- oc->symbols[j] = NULL;
+ oc->symbols[j].addr = NULL;
}
}
@@ -6691,7 +6941,7 @@ ocGetNames_MachO(ObjectCode* oc)
}
}
IF_DEBUG(linker, debugBelch("ocGetNames_MachO: %d external symbols\n", oc->n_symbols));
- oc->symbols = stgMallocBytes(oc->n_symbols * sizeof(char*),
+ oc->symbols = stgMallocBytes(oc->n_symbols * sizeof(SymbolInfo),
"ocGetNames_MachO(oc->symbols)");
if(symLC)
@@ -6712,14 +6962,22 @@ ocGetNames_MachO(ObjectCode* oc)
else
{
IF_DEBUG(linker, debugBelch("ocGetNames_MachO: inserting %s\n", nm));
- ghciInsertSymbolTable(oc->fileName, symhash, nm,
- image
- + sections[nlist[i].n_sect-1].offset
- - sections[nlist[i].n_sect-1].addr
- + nlist[i].n_value,
- HS_BOOL_FALSE,
- oc);
- oc->symbols[curSymbol++] = nm;
+ char* addr = image
+ + sections[nlist[i].n_sect - 1].offset
+ - sections[nlist[i].n_sect - 1].addr
+ + nlist[i].n_value;
+
+ ghciInsertSymbolTable( oc->fileName
+ , symhash
+ , nm
+ , addr
+ , HS_BOOL_FALSE
+ , oc);
+
+ oc->symbols[curSymbol].name = nm;
+ oc->symbols[curSymbol].addr = addr;
+ oc->symbols[curSymbol].isWeak = HS_BOOL_FALSE;
+ curSymbol++;
}
}
else
@@ -6751,7 +7009,10 @@ ocGetNames_MachO(ObjectCode* oc)
IF_DEBUG(linker, debugBelch("ocGetNames_MachO: inserting common symbol: %s\n", nm));
ghciInsertSymbolTable(oc->fileName, symhash, nm,
(void*)commonCounter, HS_BOOL_FALSE, oc);
- oc->symbols[curSymbol++] = nm;
+ oc->symbols[curSymbol].name = nm;
+ oc->symbols[curSymbol].addr = (void*)commonCounter;
+ oc->symbols[curSymbol].isWeak = HS_BOOL_FALSE;
+ curSymbol++;
commonCounter += sz;
}
diff --git a/rts/LinkerInternals.h b/rts/LinkerInternals.h
index c972fa2ad3..4ff6e997c9 100644
--- a/rts/LinkerInternals.h
+++ b/rts/LinkerInternals.h
@@ -9,8 +9,12 @@
#ifndef LINKERINTERNALS_H
#define LINKERINTERNALS_H
+#include "Rts.h"
+
+/* See Linker.c Note [runtime-linker-phases] */
typedef enum {
OBJECT_LOADED,
+ OBJECT_NEEDED,
OBJECT_RESOLVED,
OBJECT_UNLOADED
} OStatus;
@@ -93,6 +97,21 @@ typedef struct {
#endif
} SymbolExtra;
+
+/* Top-level structure for an symbols in object module. One of these is allocated
+* for each symbol in an object in use.
+*/
+typedef struct _SymbolInfo {
+ /* The name of the symbol. */
+ char* name;
+
+ /* The address of the symbol. */
+ unsigned char* addr;
+
+ /* Indicates if the symbol is weak */
+ HsBool isWeak;
+} SymbolInfo;
+
/* Top-level structure for an object module. One of these is allocated
* for each object file in use.
*/
@@ -111,8 +130,8 @@ typedef struct _ObjectCode {
this object into the global symbol hash table. This is so that
we know which parts of the latter mapping to nuke when this
object is removed from the system. */
- char** symbols;
- int n_symbols;
+ SymbolInfo* symbols;
+ int n_symbols;
/* ptr to mem containing the object file image */
char* image;
diff --git a/rts/RtsSymbols.c b/rts/RtsSymbols.c
index 395a5f10b0..3e82cdffa1 100644
--- a/rts/RtsSymbols.c
+++ b/rts/RtsSymbols.c
@@ -58,18 +58,6 @@
#if defined(mingw32_HOST_OS)
#define RTS_POSIX_ONLY_SYMBOLS /**/
-#if HAVE_GETTIMEOFDAY
-#define RTS_MINGW_GETTIMEOFDAY_SYM SymI_NeedsProto(gettimeofday)
-#else
-#define RTS_MINGW_GETTIMEOFDAY_SYM /**/
-#endif
-
-#if HAVE___MINGW_VFPRINTF
-#define RTS___MINGW_VFPRINTF_SYM SymI_HasProto(__mingw_vfprintf)
-#else
-#define RTS___MINGW_VFPRINTF_SYM /**/
-#endif
-
#if defined(i386_HOST_ARCH)
#define RTS_WIN32_ONLY(X) X
#else
@@ -82,324 +70,21 @@
#define RTS_WIN64_ONLY(X) /**/
#endif
-/* These are statically linked from the mingw libraries into the ghc
- executable, so we have to employ this hack. */
#define RTS_MINGW_ONLY_SYMBOLS \
SymI_HasProto(stg_asyncReadzh) \
SymI_HasProto(stg_asyncWritezh) \
SymI_HasProto(stg_asyncDoProczh) \
SymI_HasProto(getWin32ProgArgv) \
SymI_HasProto(setWin32ProgArgv) \
- SymI_HasProto(memset) \
- SymI_HasProto(inet_ntoa) \
- SymI_HasProto(inet_addr) \
- SymI_HasProto(htonl) \
- SymI_HasProto(recvfrom) \
- SymI_HasProto(listen) \
- SymI_HasProto(bind) \
- SymI_HasProto(shutdown) \
- SymI_HasProto(connect) \
- SymI_HasProto(htons) \
- SymI_HasProto(ntohs) \
- SymI_HasProto(getservbyname) \
- SymI_HasProto(getservbyport) \
- SymI_HasProto(getprotobynumber) \
- SymI_HasProto(getprotobyname) \
- SymI_HasProto(gethostbyname) \
- SymI_HasProto(gethostbyaddr) \
- SymI_HasProto(gethostname) \
- SymI_HasProto(strcpy) \
- SymI_HasProto(strncpy) \
- SymI_HasProto(abort) \
- SymI_HasProto(isxdigit) \
- SymI_HasProto(isupper) \
- SymI_HasProto(ispunct) \
- SymI_HasProto(islower) \
- SymI_HasProto(isspace) \
- SymI_HasProto(isprint) \
- SymI_HasProto(isdigit) \
- SymI_HasProto(iscntrl) \
- SymI_HasProto(isalpha) \
- SymI_HasProto(isalnum) \
- SymI_HasProto(isascii) \
- RTS___MINGW_VFPRINTF_SYM \
- SymI_HasProto(strcmp) \
- SymI_HasProto(memmove) \
- SymI_HasProto(realloc) \
- SymI_HasProto(malloc) \
- SymI_HasProto(pow) \
- SymI_HasProto(tanh) \
- SymI_HasProto(cosh) \
- SymI_HasProto(sinh) \
- SymI_HasProto(atan) \
- SymI_HasProto(acos) \
- SymI_HasProto(asin) \
- SymI_HasProto(tan) \
- SymI_HasProto(cos) \
- SymI_HasProto(sin) \
- SymI_HasProto(exp) \
- SymI_HasProto(log) \
- SymI_HasProto(sqrt) \
- SymI_HasProto(powf) \
- SymI_HasProto(tanhf) \
- SymI_HasProto(coshf) \
- SymI_HasProto(sinhf) \
- SymI_HasProto(atanf) \
- SymI_HasProto(acosf) \
- SymI_HasProto(asinf) \
- SymI_HasProto(tanf) \
- SymI_HasProto(cosf) \
- SymI_HasProto(sinf) \
- SymI_HasProto(expf) \
- SymI_HasProto(logf) \
- SymI_HasProto(sqrtf) \
- SymI_HasProto(erf) \
- SymI_HasProto(erfc) \
- SymI_HasProto(erff) \
- SymI_HasProto(erfcf) \
- SymI_HasProto(expm1) \
- SymI_HasProto(expm1f) \
- SymI_HasProto(log1p) \
- SymI_HasProto(log1pf) \
- SymI_HasProto(memcpy) \
SymI_HasProto(rts_InstallConsoleEvent) \
SymI_HasProto(rts_ConsoleHandlerDone) \
- SymI_NeedsProto(mktime) \
- SymI_NeedsProto(localtime) \
- SymI_NeedsProto(gmtime) \
- SymI_NeedsProto(opendir) \
- SymI_NeedsProto(readdir) \
- SymI_NeedsProto(rewinddir) \
+ SymI_HasProto(atexit) \
RTS_WIN32_ONLY(SymI_NeedsProto(__chkstk_ms)) \
RTS_WIN64_ONLY(SymI_NeedsProto(___chkstk_ms)) \
- SymI_NeedsProto(localeconv) \
- SymI_HasProto(close) \
- SymI_HasProto(read) \
- SymI_HasProto(dup) \
- SymI_HasProto(dup2) \
- SymI_HasProto(write) \
- SymI_NeedsProto(getpid) \
- SymI_HasProto(access) \
- SymI_HasProto(chmod) \
- SymI_HasProto(creat) \
- SymI_HasProto(umask) \
- SymI_HasProto(unlink) \
- SymI_HasProto(_errno) \
- SymI_NeedsProto(ftruncate64) \
- SymI_HasProto(setmode) \
- SymI_HasProto(_wstat64) \
- SymI_HasProto(_fstat64) \
- SymI_HasProto(_wsopen) \
RTS_WIN32_ONLY(SymI_HasProto(_imp___environ)) \
RTS_WIN64_ONLY(SymI_HasProto(__imp__environ)) \
RTS_WIN32_ONLY(SymI_HasProto(_imp___iob)) \
- RTS_WIN64_ONLY(SymI_HasProto(__iob_func)) \
- SymI_HasProto(GetFileAttributesA) \
- SymI_HasProto(GetFileInformationByHandle) \
- SymI_HasProto(GetFileType) \
- SymI_HasProto(GetLastError) \
- SymI_HasProto(QueryPerformanceFrequency) \
- SymI_HasProto(QueryPerformanceCounter) \
- SymI_HasProto(GetTickCount) \
- SymI_HasProto(WaitForSingleObject) \
- SymI_HasProto(PeekConsoleInputA) \
- SymI_HasProto(ReadConsoleInputA) \
- SymI_HasProto(PeekNamedPipe) \
- SymI_HasProto(select) \
- SymI_HasProto(isatty) \
- SymI_HasProto(_get_osfhandle) \
- SymI_HasProto(GetConsoleMode) \
- SymI_HasProto(SetConsoleMode) \
- SymI_HasProto(FlushConsoleInputBuffer) \
- SymI_HasProto(free) \
- SymI_NeedsProto(raise) \
- SymI_NeedsProto(_getpid) \
- SymI_HasProto(getc) \
- SymI_HasProto(ungetc) \
- SymI_HasProto(puts) \
- SymI_HasProto(putc) \
- SymI_HasProto(putchar) \
- SymI_HasProto(fputc) \
- SymI_HasProto(fread) \
- SymI_HasProto(fwrite) \
- SymI_HasProto(ferror) \
- SymI_HasProto(printf) \
- SymI_HasProto(fprintf) \
- SymI_HasProto(sprintf) \
- SymI_HasProto(vsprintf) \
- SymI_HasProto(sscanf) \
- SymI_HasProto(ldexp) \
- SymI_HasProto(strlen) \
- SymI_HasProto(strnlen) \
- SymI_HasProto(strchr) \
- SymI_HasProto(strtol) \
- SymI_HasProto(strerror) \
- SymI_HasProto(memchr) \
- SymI_HasProto(memcmp) \
- SymI_HasProto(wcscpy) \
- SymI_HasProto(wcslen) \
- SymI_HasProto(_lseeki64) \
- SymI_HasProto(_wchmod) \
- SymI_HasProto(closesocket) \
- SymI_HasProto(send) \
- SymI_HasProto(recv) \
- SymI_HasProto(bsearch) \
- SymI_HasProto(CommandLineToArgvW) \
- SymI_HasProto(CreateBitmap) \
- SymI_HasProto(CreateBitmapIndirect) \
- SymI_HasProto(CreateCompatibleBitmap) \
- SymI_HasProto(CreateDIBPatternBrushPt) \
- SymI_HasProto(CreateDIBitmap) \
- SymI_HasProto(SetBitmapDimensionEx) \
- SymI_HasProto(GetBitmapDimensionEx) \
- SymI_HasProto(GetStockObject) \
- SymI_HasProto(GetObjectW) \
- SymI_HasProto(DeleteObject) \
- SymI_HasProto(SetDIBits) \
- SymI_HasProto(GetDIBits) \
- SymI_HasProto(CreateSolidBrush) \
- SymI_HasProto(CreateHatchBrush) \
- SymI_HasProto(CreatePatternBrush) \
- SymI_HasProto(CreateFontW) \
- SymI_HasProto(AngleArc) \
- SymI_HasProto(Arc) \
- SymI_HasProto(ArcTo) \
- SymI_HasProto(BeginPath) \
- SymI_HasProto(BitBlt) \
- SymI_HasProto(CancelDC) \
- SymI_HasProto(Chord) \
- SymI_HasProto(CloseFigure) \
- SymI_HasProto(CombineRgn) \
- SymI_HasProto(CreateCompatibleDC) \
- SymI_HasProto(CreateEllipticRgn) \
- SymI_HasProto(CreateEllipticRgnIndirect) \
- SymI_HasProto(CreatePen) \
- SymI_HasProto(CreatePolygonRgn) \
- SymI_HasProto(CreateRectRgn) \
- SymI_HasProto(CreateRectRgnIndirect) \
- SymI_HasProto(CreateRoundRectRgn) \
- SymI_HasProto(DeleteDC) \
- SymI_HasProto(Ellipse) \
- SymI_HasProto(EndPath) \
- SymI_HasProto(EqualRgn) \
- SymI_HasProto(ExtSelectClipRgn) \
- SymI_HasProto(FillPath) \
- SymI_HasProto(FillRgn) \
- SymI_HasProto(FlattenPath) \
- SymI_HasProto(FrameRgn) \
- SymI_HasProto(GetArcDirection) \
- SymI_HasProto(GetBkColor) \
- SymI_HasProto(GetBkMode) \
- SymI_HasProto(GetBrushOrgEx) \
- SymI_HasProto(GetCurrentObject) \
- SymI_HasProto(GetDCOrgEx) \
- SymI_HasProto(GetGraphicsMode) \
- SymI_HasProto(GetMiterLimit) \
- SymI_HasProto(GetPolyFillMode) \
- SymI_HasProto(GetRgnBox) \
- SymI_HasProto(GetStretchBltMode) \
- SymI_HasProto(GetTextAlign) \
- SymI_HasProto(GetTextCharacterExtra) \
- SymI_HasProto(GetTextColor) \
- SymI_HasProto(GetTextExtentPoint32W) \
- SymI_HasProto(InvertRgn) \
- SymI_HasProto(LineTo) \
- SymI_HasProto(MaskBlt) \
- SymI_HasProto(MoveToEx) \
- SymI_HasProto(OffsetRgn) \
- SymI_HasProto(PaintRgn) \
- SymI_HasProto(PathToRegion) \
- SymI_HasProto(Pie) \
- SymI_HasProto(PlgBlt) \
- SymI_HasProto(PolyBezier) \
- SymI_HasProto(PolyBezierTo) \
- SymI_HasProto(Polygon) \
- SymI_HasProto(Polyline) \
- SymI_HasProto(PolylineTo) \
- SymI_HasProto(PtInRegion) \
- SymI_HasProto(Rectangle) \
- SymI_HasProto(RectInRegion) \
- SymI_HasProto(RestoreDC) \
- SymI_HasProto(RoundRect) \
- SymI_HasProto(SaveDC) \
- SymI_HasProto(SelectClipPath) \
- SymI_HasProto(SelectClipRgn) \
- SymI_HasProto(SelectObject) \
- SymI_HasProto(SelectPalette) \
- SymI_HasProto(SetArcDirection) \
- SymI_HasProto(SetBkColor) \
- SymI_HasProto(SetBkMode) \
- SymI_HasProto(SetBrushOrgEx) \
- SymI_HasProto(SetGraphicsMode) \
- SymI_HasProto(SetMiterLimit) \
- SymI_HasProto(SetPolyFillMode) \
- SymI_HasProto(SetStretchBltMode) \
- SymI_HasProto(SetTextAlign) \
- SymI_HasProto(SetTextCharacterExtra) \
- SymI_HasProto(SetTextColor) \
- SymI_HasProto(StretchBlt) \
- SymI_HasProto(StrokeAndFillPath) \
- SymI_HasProto(StrokePath) \
- SymI_HasProto(TextOutW) \
- SymI_HasProto(timeGetTime) \
- SymI_HasProto(WidenPath) \
- SymI_HasProto(GetFileSecurityW) \
- SymI_HasProto(RegCloseKey) \
- SymI_HasProto(RegConnectRegistryW) \
- SymI_HasProto(RegCreateKeyExW) \
- SymI_HasProto(RegCreateKeyW) \
- SymI_HasProto(RegDeleteKeyW) \
- SymI_HasProto(RegDeleteValueW) \
- SymI_HasProto(RegEnumKeyW) \
- SymI_HasProto(RegEnumValueW) \
- SymI_HasProto(RegFlushKey) \
- SymI_HasProto(RegLoadKeyW) \
- SymI_HasProto(RegNotifyChangeKeyValue) \
- SymI_HasProto(RegOpenKeyExW) \
- SymI_HasProto(RegOpenKeyW) \
- SymI_HasProto(RegQueryInfoKeyW) \
- SymI_HasProto(RegQueryValueExW) \
- SymI_HasProto(RegQueryValueW) \
- SymI_HasProto(RegReplaceKeyW) \
- SymI_HasProto(RegRestoreKeyW) \
- SymI_HasProto(RegSaveKeyW) \
- SymI_HasProto(RegSetValueExW) \
- SymI_HasProto(RegSetValueW) \
- SymI_HasProto(RegUnLoadKeyW) \
- SymI_HasProto(SHGetFolderPathW) \
- RTS_WIN32_ONLY(SymI_HasProto(SetWindowLongW)) \
- RTS_WIN32_ONLY(SymI_HasProto(GetWindowLongW)) \
- RTS_WIN64_ONLY(SymI_HasProto(SetWindowLongPtrW)) \
- RTS_WIN64_ONLY(SymI_HasProto(GetWindowLongPtrW)) \
- SymI_HasProto(MenuItemFromPoint) \
- SymI_HasProto(ChildWindowFromPoint) \
- SymI_HasProto(ChildWindowFromPointEx) \
- SymI_HasProto(UnmapViewOfFile) \
- SymI_HasProto(CloseHandle) \
- SymI_HasProto(FreeLibrary) \
- SymI_HasProto(GetMessageW) \
- SymI_HasProto(TranslateMessage) \
- SymI_HasProto(DispatchMessageW) \
- SymI_HasProto(DefWindowProcW) \
- SymI_HasProto(GlobalAlloc) \
- SymI_HasProto(GlobalFree) \
- SymI_HasProto(CreateFileW) \
- SymI_HasProto(WriteFile) \
- SymI_HasProto(FormatMessageW) \
- SymI_NeedsProto(_localtime64) \
- SymI_NeedsProto(_tzname) \
- SymI_NeedsProto(_timezone) \
- SymI_HasProto(CreatePipe) \
- SymI_HasProto(SetHandleInformation) \
- SymI_HasProto(GetStdHandle) \
- SymI_HasProto(GetCurrentProcess) \
- SymI_HasProto(DuplicateHandle) \
- SymI_HasProto(CreateProcessW) \
- SymI_HasProto(TerminateProcess) \
- SymI_HasProto(_open_osfhandle) \
- SymI_HasProto(GetExitCodeProcess) \
- RTS_MINGW_GETTIMEOFDAY_SYM \
- SymI_NeedsProto(closedir)
+ RTS_WIN64_ONLY(SymI_HasProto(__iob_func))
#else
#define RTS_MINGW_ONLY_SYMBOLS /**/
diff --git a/testsuite/tests/ghci/linking/dyn/all.T b/testsuite/tests/ghci/linking/dyn/all.T
index eb044fc44d..e1e0fd289e 100644
--- a/testsuite/tests/ghci/linking/dyn/all.T
+++ b/testsuite/tests/ghci/linking/dyn/all.T
@@ -30,7 +30,7 @@ test('T10955dyn',
test('T10458',
[unless(doing_ghci, skip),
- extra_clean(['libAS.*']),
+ extra_clean(['T10458dir/libAS.*']),
pre_cmd('$MAKE -s --no-print-directory compile_libT10458'),
extra_hc_opts('-L$PWD/T10458dir -lAS')],
ghci_script, ['T10458.script'])
diff --git a/testsuite/tests/rts/T11223/Makefile b/testsuite/tests/rts/T11223/Makefile
new file mode 100644
index 0000000000..af07c32bd8
--- /dev/null
+++ b/testsuite/tests/rts/T11223/Makefile
@@ -0,0 +1,126 @@
+TOP=../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
+
+# -----------------------------------------------------------------------------
+# Testing RTS linker object resolution
+#
+
+GCC=gcc
+
+.PHONY: t_11223_simple_link
+t_11223_simple_link:
+ $(RM) -f foo_simple.o foo.hi foo.o
+ "$(CC)" -c foo.c -o foo_simple.o
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) foo_simple.o foo.hs
+
+.PHONY: t_11223_simple_link_lib
+t_11223_simple_link_lib:
+ $(RM) -f foo_lib.o foo.hi foo.o libfoo_lib.a
+ "$(CC)" -c foo.c -o foo_lib.o
+ "$(AR)" rs libfoo_lib.a foo_lib.o 2> /dev/null
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) foo.hs -lfoo_lib -L"$(PWD)"
+
+.PHONY: t_11223_simple_duplicate
+t_11223_simple_duplicate:
+ $(RM) -f foo_dup.o bar_dup.o foo.hi foo.o
+ "$(CC)" -c foo.c -o foo_dup.o
+ "$(CC)" -c bar.c -o bar_dup.o
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) foo_dup.o bar_dup.o foo.hs
+
+.PHONY: t_11223_simple_duplicate_lib
+t_11223_simple_duplicate_lib:
+ $(RM) -f foo_dup_lib.o bar_dup_lib.o foo.hi foo.o libfoo_dup_lib.a
+ "$(CC)" -c foo.c -o foo_dup_lib.o
+ "$(CC)" -c bar.c -o bar_dup_lib.o
+ "$(AR)" rs libfoo_dup_lib.a foo_dup_lib.o 2> /dev/null
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) bar_dup.o foo.hs -lfoo_dup_lib -L"$(PWD)"
+
+.PHONY: t_11223_simple_unused_duplicate_lib
+t_11223_simple_unused_duplicate_lib:
+ $(RM) -f foo_dup_lib.o bar_dup_lib.o foo.hi foo.o libbar_dup_lib.a
+ "$(CC)" -c foo.c -o foo_dup_lib.o
+ "$(CC)" -c bar.c -o bar_dup_lib.o
+ "$(AR)" rs libbar_dup_lib.a bar_dup_lib.o 2> /dev/null
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) foo_dup.o foo.hs -lbar_dup_lib -L"$(PWD)"
+
+.PHONY: t_11223_link_order_a_b_succeed
+t_11223_link_order_a_b_succeed:
+ $(RM) -f foo_link_lib_1.o bar_link_lib_1.o foo.hi foo.o libbar_link_lib_1.a libfoo_link_lib_1.a
+ "$(CC)" -c foo.c -o foo_link_lib_1.o
+ "$(CC)" -c bar.c -o bar_link_lib_1.o
+ "$(AR)" rs libbar_link_lib_1.a bar_link_lib_1.o 2> /dev/null
+ "$(AR)" rs libfoo_link_lib_1.a foo_link_lib_1.o 2> /dev/null
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) foo2.hs -lbar_link_lib_1 -lfoo_link_lib_1 -L"$(PWD)"
+
+.PHONY: t_11223_link_order_b_a_succeed
+t_11223_link_order_b_a_succeed:
+ $(RM) -f foo_link_lib_2.o bar_link_lib_2.o foo.hi foo.o libbar_link_lib_2.a libfoo_link_lib_2.a
+ "$(CC)" -c foo.c -o foo_link_lib_2.o
+ "$(CC)" -c bar.c -o bar_link_lib_2.o
+ "$(AR)" rs libbar_link_lib_2.a bar_link_lib_2.o 2> /dev/null
+ "$(AR)" rs libfoo_link_lib_2.a foo_link_lib_2.o 2> /dev/null
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) foo2.hs -lfoo_link_lib_2 -lbar_link_lib_2 -L"$(PWD)"
+
+.PHONY: t_11223_link_order_a_b_2_fail
+t_11223_link_order_a_b_2_fail:
+ $(RM) -f foo_link_lib_3.o bar_link_lib_3.o foo.hi foo.o libbar_link_lib_3.a libfoo_link_lib_3.a
+ "$(CC)" -c foo.c -o foo_link_lib_3.o
+ "$(CC)" -c bar.c -o bar_link_lib_3.o
+ "$(AR)" rs libbar_link_lib_3.a bar_link_lib_3.o 2> /dev/null
+ "$(AR)" rs libfoo_link_lib_3.a foo_link_lib_3.o 2> /dev/null
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) foo3.hs -lbar_link_lib_3 -lfoo_link_lib_3 -L"$(PWD)"
+
+.PHONY: t_11223_link_order_b_a_2_succeed
+t_11223_link_order_b_a_2_succeed:
+ $(RM) -f foo_link_lib_4.o bar_link_lib_4.o foo.hi foo.o libbar_link_lib_4.a libfoo_link_lib_4.a
+ "$(CC)" -c foo.c -o foo_link_lib_4.o
+ "$(CC)" -c bar.c -o bar_link_lib_4.o
+ "$(AR)" rs libbar_link_lib_4.a bar_link_lib_4.o 2> /dev/null
+ "$(AR)" rs libfoo_link_lib_4.a foo_link_lib_4.o 2> /dev/null
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) foo3.hs -lfoo_link_lib_4 -lbar_link_lib_4 -L"$(PWD)"
+
+# -----------------------------------------------------------------------------
+# Testing RTS weak symbols resolution
+#
+
+.PHONY: t_11223_weak_only_link_fail
+t_11223_weak_only_link_fail:
+ $(RM) -f power_w1.o power.hi power.o
+ "$(GCC)" -c power.c -DWEAK -o power_w1.o
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) power.hs power_w1.o
+
+.PHONY: t_11223_weak_only_link_succeed
+t_11223_weak_only_link_succeed:
+ $(RM) -f power_w2.o power3.hi power3.o
+ "$(GCC)" -c power_slow.c -DWEAK -o power_w2.o
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) power3.hs power_w2.o
+
+.PHONY: t_11223_weak_both_link_order_a_b_succeed
+t_11223_weak_both_link_order_a_b_succeed:
+ $(RM) -f fast_power_w3.o slow_power_w3.o power3.hi power3.o
+ "$(GCC)" -c power_slow.c -DWEAK -o slow_power_w3.o
+ "$(GCC)" -c power.c -DWEAK -o fast_power_w3.o
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) power3.hs fast_power_w3.o slow_power_w3.o
+
+.PHONY: t_11223_weak_both_link_order_b_a_succeed
+t_11223_weak_both_link_order_b_a_succeed:
+ $(RM) -f fast_power_w4.o slow_power_w4.o power3.hi power3.o
+ "$(GCC)" -c power_slow.c -DWEAK -o slow_power_w4.o
+ "$(GCC)" -c power.c -DWEAK -o fast_power_w4.o
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) power3.hs slow_power_w4.o fast_power_w4.o
+
+.PHONY: t_11223_weak_single_link_order_a_b_succeed
+t_11223_weak_single_link_order_a_b_succeed:
+ $(RM) -f fast_power_w5.o slow_power_w5.o power3.hi power3.o
+ "$(GCC)" -c power_slow.c -o slow_power_w5.o
+ "$(GCC)" -c power.c -DWEAK -o fast_power_w5.o
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) power3.hs fast_power_w5.o slow_power_w5.o
+
+.PHONY: t_11223_weak_single_link_order_b_a_succeed
+t_11223_weak_single_link_order_b_a_succeed:
+ $(RM) -f fast_power_w6.o slow_power_w6.o power3.hi power3.o
+ "$(GCC)" -c power_slow.c -DWEAK -o slow_power_w6.o
+ "$(GCC)" -c power.c -o fast_power_w6.o
+ echo main | "$(TEST_HC)" --interactive -v0 $(filter-out -rtsopts, $(TEST_HC_OPTS)) power3.hs slow_power_w6.o fast_power_w6.o
+
diff --git a/testsuite/tests/rts/T11223/T11223_link_order_a_b_2_fail.stderr b/testsuite/tests/rts/T11223/T11223_link_order_a_b_2_fail.stderr
new file mode 100644
index 0000000000..b7079a6352
--- /dev/null
+++ b/testsuite/tests/rts/T11223/T11223_link_order_a_b_2_fail.stderr
@@ -0,0 +1,25 @@
+GHC runtime linker: fatal error: I found a duplicate definition for symbol
+ a
+whilst processing object file
+ /home/phyx/Documents/ghc/testsuite/tests/rts/T11223/libfoo_link_lib_3.a
+The symbol was previously defined in
+ /home/phyx/Documents/ghc/testsuite/tests/rts/T11223/libbar_link_lib_3.a(bar_link_lib_3.o)
+This could be caused by:
+ * Loading two different object files which export the same symbol
+ * Specifying the same object file twice on the GHCi command line
+ * An incorrect `package.conf' entry, causing some object to be
+ loaded twice.
+ghc-stage2: Could not on-demand load symbol 'c'
+
+
+ByteCodeLink: can't find label
+During interactive linking, GHCi couldn't find the following symbol:
+ c
+This may be due to you not asking GHCi to load extra object files,
+archives or DLLs needed by your current session. Restart GHCi, specifying
+the missing library using the -L/path/to/object/dir and -lmissinglibname
+flags, or simply by naming the relevant files on the GHCi command line.
+Alternatively, this link failure might indicate a bug in GHCi.
+If you suspect the latter, please send a bug report to:
+ glasgow-haskell-bugs@haskell.org
+
diff --git a/testsuite/tests/rts/T11223/T11223_link_order_a_b_2_fail.stderr-mingw32 b/testsuite/tests/rts/T11223/T11223_link_order_a_b_2_fail.stderr-mingw32
new file mode 100644
index 0000000000..51d4db5dc0
--- /dev/null
+++ b/testsuite/tests/rts/T11223/T11223_link_order_a_b_2_fail.stderr-mingw32
@@ -0,0 +1,25 @@
+GHC runtime linker: fatal error: I found a duplicate definition for symbol
+ a
+whilst processing object file
+ E:/msys64/home/Tamar/ghc2/testsuite/tests/rts/T11223\libfoo_link_lib_3.a
+The symbol was previously defined in
+ E:/msys64/home/Tamar/ghc2/testsuite/tests/rts/T11223\libbar_link_lib_3.a(bar_link_lib_3.o)
+This could be caused by:
+ * Loading two different object files which export the same symbol
+ * Specifying the same object file twice on the GHCi command line
+ * An incorrect `package.conf' entry, causing some object to be
+ loaded twice.
+
+ByteCodeLink: can't find label
+During interactive linking, GHCi couldn't find the following symbol:
+ c
+This may be due to you not asking GHCi to load extra object files,
+archives or DLLs needed by your current session. Restart GHCi, specifying
+the missing library using the -L/path/to/object/dir and -lmissinglibname
+flags, or simply by naming the relevant files on the GHCi command line.
+Alternatively, this link failure might indicate a bug in GHCi.
+If you suspect the latter, please send a bug report to:
+ glasgow-haskell-bugs@haskell.org
+
+ghc-stage2.exe: Could not on-demand load symbol 'c'
+
diff --git a/testsuite/tests/rts/T11223/T11223_link_order_a_b_succeed.stdout b/testsuite/tests/rts/T11223/T11223_link_order_a_b_succeed.stdout
new file mode 100644
index 0000000000..78c6baefdd
--- /dev/null
+++ b/testsuite/tests/rts/T11223/T11223_link_order_a_b_succeed.stdout
@@ -0,0 +1 @@
+2
diff --git a/testsuite/tests/rts/T11223/T11223_link_order_b_a_2_succeed.stdout b/testsuite/tests/rts/T11223/T11223_link_order_b_a_2_succeed.stdout
new file mode 100644
index 0000000000..110fbf1042
--- /dev/null
+++ b/testsuite/tests/rts/T11223/T11223_link_order_b_a_2_succeed.stdout
@@ -0,0 +1 @@
+256
diff --git a/testsuite/tests/rts/T11223/T11223_link_order_b_a_succeed.stdout b/testsuite/tests/rts/T11223/T11223_link_order_b_a_succeed.stdout
new file mode 100644
index 0000000000..e0e1028e60
--- /dev/null
+++ b/testsuite/tests/rts/T11223/T11223_link_order_b_a_succeed.stdout
@@ -0,0 +1 @@
+4
diff --git a/testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr b/testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr
new file mode 100644
index 0000000000..06da96ff27
--- /dev/null
+++ b/testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr
@@ -0,0 +1,25 @@
+GHC runtime linker: fatal error: I found a duplicate definition for symbol
+ a
+whilst processing object file
+ /home/phyx/Documents/ghc/testsuite/tests/rts/T11223/libfoo_dup_lib.a
+The symbol was previously defined in
+ bar_dup.o
+This could be caused by:
+ * Loading two different object files which export the same symbol
+ * Specifying the same object file twice on the GHCi command line
+ * An incorrect `package.conf' entry, causing some object to be
+ loaded twice.
+ghc-stage2: Could not on-demand load symbol 'c'
+
+
+ByteCodeLink: can't find label
+During interactive linking, GHCi couldn't find the following symbol:
+ c
+This may be due to you not asking GHCi to load extra object files,
+archives or DLLs needed by your current session. Restart GHCi, specifying
+the missing library using the -L/path/to/object/dir and -lmissinglibname
+flags, or simply by naming the relevant files on the GHCi command line.
+Alternatively, this link failure might indicate a bug in GHCi.
+If you suspect the latter, please send a bug report to:
+ glasgow-haskell-bugs@haskell.org
+
diff --git a/testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr-mingw32 b/testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr-mingw32
new file mode 100644
index 0000000000..af8ff1b1e8
--- /dev/null
+++ b/testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr-mingw32
@@ -0,0 +1,25 @@
+GHC runtime linker: fatal error: I found a duplicate definition for symbol
+ a
+whilst processing object file
+ E:/msys64/home/Tamar/ghc2/testsuite/tests/rts/T11223\libfoo_dup_lib.a
+The symbol was previously defined in
+ bar_dup.o
+This could be caused by:
+ * Loading two different object files which export the same symbol
+ * Specifying the same object file twice on the GHCi command line
+ * An incorrect `package.conf' entry, causing some object to be
+ loaded twice.
+
+ByteCodeLink: can't find label
+During interactive linking, GHCi couldn't find the following symbol:
+ c
+This may be due to you not asking GHCi to load extra object files,
+archives or DLLs needed by your current session. Restart GHCi, specifying
+the missing library using the -L/path/to/object/dir and -lmissinglibname
+flags, or simply by naming the relevant files on the GHCi command line.
+Alternatively, this link failure might indicate a bug in GHCi.
+If you suspect the latter, please send a bug report to:
+ glasgow-haskell-bugs@haskell.org
+
+ghc-stage2.exe: Could not on-demand load symbol 'c'
+
diff --git a/testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr.normalised-mingw32 b/testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr.normalised-mingw32
new file mode 100644
index 0000000000..69fc0d4cca
--- /dev/null
+++ b/testsuite/tests/rts/T11223/T11223_simple_duplicate_lib.stderr.normalised-mingw32
@@ -0,0 +1,24 @@
+GHC runtime linker: fatal I found a duplicate definition for symbol
+ a
+whilst processing object file
+ libfoo_dup_lib.a
+The symbol was previously defined in
+ bar_dup.o
+This could be caused by:
+ * Loading two different object files which export the same symbol
+ * Specifying the same object file twice on the GHCi command line
+ * An incorrect `package.conf' entry, causing some object to be
+ loaded twice.
+
+ByteCodeLink: can't find label
+During interactive linking, GHCi couldn't find the following symbol:
+ c
+This may be due to you not asking GHCi to load extra object files,
+archives or DLLs needed by your current session. Restart GHCi, specifying
+the missing library using the -L dir and -lmissinglibname
+flags, or simply by naming the relevant files on the GHCi command line.
+Alternatively, this link failure might indicate a bug in GHCi.
+If you suspect the latter, please send a bug report to:
+ glasgow-haskell-bugs@haskell.org
+
+ghc: Could not on-demand load symbol 'c' \ No newline at end of file
diff --git a/testsuite/tests/rts/T11223/T11223_simple_link.stdout b/testsuite/tests/rts/T11223/T11223_simple_link.stdout
new file mode 100644
index 0000000000..78c5a0adb2
--- /dev/null
+++ b/testsuite/tests/rts/T11223/T11223_simple_link.stdout
@@ -0,0 +1 @@
+64
diff --git a/testsuite/tests/rts/T11223/T11223_simple_link_lib.stdout b/testsuite/tests/rts/T11223/T11223_simple_link_lib.stdout
new file mode 100644
index 0000000000..78c5a0adb2
--- /dev/null
+++ b/testsuite/tests/rts/T11223/T11223_simple_link_lib.stdout
@@ -0,0 +1 @@
+64
diff --git a/testsuite/tests/rts/T11223/T11223_simple_unused_duplicate_lib.stdout b/testsuite/tests/rts/T11223/T11223_simple_unused_duplicate_lib.stdout
new file mode 100644
index 0000000000..78c5a0adb2
--- /dev/null
+++ b/testsuite/tests/rts/T11223/T11223_simple_unused_duplicate_lib.stdout
@@ -0,0 +1 @@
+64
diff --git a/testsuite/tests/rts/T11223/all.T b/testsuite/tests/rts/T11223/all.T
new file mode 100644
index 0000000000..872cf2b93a
--- /dev/null
+++ b/testsuite/tests/rts/T11223/all.T
@@ -0,0 +1,93 @@
+import string
+import re
+
+#--------------------------------------
+# Python normalization functions
+#--------------------------------------
+def normalise_duplicate_errmsg( msg ):
+ return re.sub(r"((?:[a-z, A-Z]+):|)[\/\\\\]+(?:.+[\/\\\\]+?)?(.+?)[\/\\\\]+", " ",
+ re.sub(r"version\s(\d+\.\d+\.\d+)\sfor\s[a-z,A-Z,0-9,_,-]+(?=\))", r"", msg))
+
+#--------------------------------------
+# Test functions
+#--------------------------------------
+
+test('T11223_simple_link',
+ [when(ghc_dynamic(), skip)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_simple_link'])
+
+test('T11223_simple_link_lib',
+ [when(ghc_dynamic(), skip)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_simple_link_lib'])
+
+# I'm ignoring the output since for this particular invocation normalise_errmsg
+# isn't being called and I can't figure out why not.
+test('T11223_simple_duplicate',
+ [when(ghc_dynamic(), skip), ignore_output, exit_code(2), normalise_errmsg_fun(normalise_duplicate_errmsg)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_simple_duplicate'])
+
+test('T11223_simple_duplicate_lib',
+ [when(ghc_dynamic(), skip), normalise_errmsg_fun(normalise_duplicate_errmsg)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_simple_duplicate_lib'])
+
+test('T11223_simple_unused_duplicate_lib',
+ [when(ghc_dynamic(), skip)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_simple_unused_duplicate_lib'])
+
+test('T11223_link_order_a_b_succeed',
+ [when(ghc_dynamic(), skip)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_link_order_a_b_succeed'])
+
+test('T11223_link_order_b_a_succeed',
+ [when(ghc_dynamic(), skip)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_link_order_b_a_succeed'])
+
+test('T11223_link_order_a_b_2_fail',
+ [when(ghc_dynamic(), skip), normalise_errmsg_fun(normalise_duplicate_errmsg)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_link_order_a_b_2_fail'])
+
+test('T11223_link_order_b_a_2_succeed',
+ [when(ghc_dynamic(), skip)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_link_order_b_a_2_succeed'])
+
+# Weak Symbols are not currently implemented. So Disable all the tests
+# See Note [weak-symbols-support] in Linker.c
+
+test('T11223_weak_only_link_fail',
+ [when(ghc_dynamic(), skip), expect_broken(11817)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_weak_only_link_fail'])
+
+test('T11223_weak_only_link_succeed',
+ [when(ghc_dynamic(), skip), expect_broken(11817)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_weak_only_link_succeed'])
+
+test('T11223_weak_both_link_order_a_b_succeed',
+ [when(ghc_dynamic(), skip), expect_broken(11817)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_weak_both_link_order_a_b_succeed'])
+
+test('T11223_weak_both_link_order_b_a_succeed',
+ [when(ghc_dynamic(), skip), expect_broken(11817)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_weak_both_link_order_b_a_succeed'])
+
+test('T11223_weak_single_link_order_a_b_succeed',
+ [when(ghc_dynamic(), skip), expect_broken(11817)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_weak_single_link_order_a_b_succeed'])
+
+test('T11223_weak_single_link_order_b_a_succeed',
+ [when(ghc_dynamic(), skip), expect_broken(11817)],
+ run_command,
+ ['$MAKE -s --no-print-directory t_11223_weak_single_link_order_b_a_succeed'])
diff --git a/testsuite/tests/rts/T11223/bar.c b/testsuite/tests/rts/T11223/bar.c
new file mode 100644
index 0000000000..de5702061a
--- /dev/null
+++ b/testsuite/tests/rts/T11223/bar.c
@@ -0,0 +1,4 @@
+int a()
+{
+ return 2;
+}
diff --git a/testsuite/tests/rts/T11223/foo.c b/testsuite/tests/rts/T11223/foo.c
new file mode 100644
index 0000000000..cd6e4aca4c
--- /dev/null
+++ b/testsuite/tests/rts/T11223/foo.c
@@ -0,0 +1,14 @@
+int a()
+{
+ return 4;
+}
+
+int b()
+{
+ return a()*a();
+}
+
+int c()
+{
+ return a()*b();
+}
diff --git a/testsuite/tests/rts/T11223/foo.hs b/testsuite/tests/rts/T11223/foo.hs
new file mode 100644
index 0000000000..ff7aa58ba4
--- /dev/null
+++ b/testsuite/tests/rts/T11223/foo.hs
@@ -0,0 +1,5 @@
+module Main where
+
+foreign import ccall "c" c_exp :: Int
+
+main = print c_exp
diff --git a/testsuite/tests/rts/T11223/foo2.hs b/testsuite/tests/rts/T11223/foo2.hs
new file mode 100644
index 0000000000..ecaed41282
--- /dev/null
+++ b/testsuite/tests/rts/T11223/foo2.hs
@@ -0,0 +1,5 @@
+module Main where
+
+foreign import ccall "a" c_exp :: Int
+
+main = print c_exp
diff --git a/testsuite/tests/rts/T11223/foo3.hs b/testsuite/tests/rts/T11223/foo3.hs
new file mode 100644
index 0000000000..642de2b528
--- /dev/null
+++ b/testsuite/tests/rts/T11223/foo3.hs
@@ -0,0 +1,6 @@
+module Main where
+
+foreign import ccall "a" a_exp :: Int
+foreign import ccall "c" c_exp :: Int
+
+main = print $ c_exp * a_exp
diff --git a/testsuite/tests/rts/T11223/power.c b/testsuite/tests/rts/T11223/power.c
new file mode 100644
index 0000000000..f94c7e7749
--- /dev/null
+++ b/testsuite/tests/rts/T11223/power.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+#ifdef WEAK
+int power2(int x) __attribute__((weak));
+#endif
+int power2(int x)
+{
+ fprintf(stderr, "fast power2()\n");
+ return x*x;
+}
diff --git a/testsuite/tests/rts/T11223/power.hs b/testsuite/tests/rts/T11223/power.hs
new file mode 100644
index 0000000000..c7951c3fed
--- /dev/null
+++ b/testsuite/tests/rts/T11223/power.hs
@@ -0,0 +1,5 @@
+module Main where
+
+foreign import ccall "power2" power2 :: Int -> Int
+
+main = print $ power2 4
diff --git a/testsuite/tests/rts/T11223/power3.hs b/testsuite/tests/rts/T11223/power3.hs
new file mode 100644
index 0000000000..fcc2b1336f
--- /dev/null
+++ b/testsuite/tests/rts/T11223/power3.hs
@@ -0,0 +1,5 @@
+module Main where
+
+foreign import ccall "power3" power3 :: Int -> Int
+
+main = print $ power3 4
diff --git a/testsuite/tests/rts/T11223/power_slow.c b/testsuite/tests/rts/T11223/power_slow.c
new file mode 100644
index 0000000000..a1ae8d1e61
--- /dev/null
+++ b/testsuite/tests/rts/T11223/power_slow.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#ifdef WEAK
+int power2(int x) __attribute__((weak));
+#endif
+int power2(int x)
+{
+ fprintf(stderr, "slow power2()\n");
+ return x*x;
+}
+
+int power3(int x)
+{
+ return power2(x)*x;
+}