Commit 97f2b164 authored by Tamar Christina's avatar Tamar Christina

Add Windows import library support to the Runtime Linker

Summary:
Import libraries are files ending in `.dll.a` and `.lib` depending on which
compiler creates them (GCC, vs MSVC).

Import Libraries are standard `archive` files that contain object files.
These object files can have two different formats:

1) The normal COFF Object format for object files
    (contains all ascii data and very little program code, so do not
     try to execute.)
2) "short import" format which just contains a symbol name and
   the dll in which the symbol can be found.

Import Libraries are useful for two things:

1) Allowing applications that don't support dynamic linking to
   link against the import lib (non-short format) which then
   makes calls into the DLL by loading it at runtime.

2) Allow linking of mutually recursive dlls. if `A.DLL` requires
   `B.DLL` and vice versa, import libs can be used to break the cycle
   as they can be created from the expected exports of the DLLs.

A side effect of having these two capabilities is that Import libs are often
used to hide specific versions of DLLs behind a non-versioned import lib.

e.g. GCC_S.a (non-conventional import lib) will point to the correct
`libGCC` DLL. With this support Windows Haskell files can now just link
to `-lGCC_S` and not have to worry about what the actual name of libGCC is.

Also third party libraries such as `icuuc` use import libs to forward to
versioned DLLs. e.g. `icuuc.lib` points to `icuuc51.dll` etc.

Test Plan:
./validate

Two new tests added T11072gcc T11072msvc

Two binary files have been added to the test folder because the "short"
import library format doesn't seem to be creatable via `dlltool`
and requires Microsoft's `lib.exe`.

Reviewers: bgamari, RyanGlScott, erikd, goldfire, austin, hvr

Reviewed By: RyanGlScott, erikd

Subscribers: thomie

Differential Revision: https://phabricator.haskell.org/D1696

GHC Trac Issues: #11072
parent 4cbae1bd
......@@ -1227,9 +1227,6 @@ linkPackage hsc_env pkg
mapM_ (load_dyn hsc_env)
(known_dlls ++ map (mkSOName platform) dlls)
-- DLLs are loaded, reset the search paths
mapM_ (removeLibrarySearchPath hsc_env) $ reverse pathCache
-- After loading all the DLLs, we can load the static objects.
-- Ordering isn't important here, because we do one final link
-- step to resolve everything.
......@@ -1238,6 +1235,13 @@ linkPackage hsc_env pkg
maybePutStr dflags "linking ... "
ok <- resolveObjs hsc_env
-- DLLs are loaded, reset the search paths
-- Import libraries will be loaded via loadArchive so only
-- reset the DLL search path after all archives are loaded
-- as well.
mapM_ (removeLibrarySearchPath hsc_env) $ reverse pathCache
if succeeded ok
then maybePutStrLn dflags "done."
else let errmsg = "unable to load package `"
......@@ -1281,9 +1285,10 @@ locateLib hsc_env is_hs dirs lib
-- For non-Haskell libraries (e.g. gmp, iconv):
-- first look in library-dirs for a dynamic library (libfoo.so)
-- then look in library-dirs for a static library (libfoo.a)
-- first look in library-dirs and inplace GCC for a dynamic library (libfoo.so)
-- then look in library-dirs and inplace GCC for a dynamic library (libfoo.so)
-- then check for system dynamic libraries (e.g. kernel32.dll on windows)
-- then try "gcc --print-file-name" to search gcc's search path
-- then try looking for import libraries on Windows (.dll.a, .lib)
-- then look in library-dirs and inplace GCC for a static library (libfoo.a)
-- for a dynamic library (#5289)
-- otherwise, assume loadDLL can find it
......@@ -1291,6 +1296,7 @@ locateLib hsc_env is_hs dirs lib
= findDll `orElse`
findSysDll `orElse`
tryGcc `orElse`
tryImpLib `orElse`
findArchive `orElse`
assumeDll
......@@ -1321,31 +1327,43 @@ locateLib hsc_env is_hs dirs lib
loading_profiled_hs_libs = interpreterProfiled dflags
loading_dynamic_hs_libs = interpreterDynamic dflags
import_libs = [lib <.> "lib", "lib" ++ lib <.> "lib", "lib" ++ lib <.> "dll.a"]
hs_dyn_lib_name = lib ++ '-':programName dflags ++ projectVersion dflags
hs_dyn_lib_file = mkHsSOName platform hs_dyn_lib_name
so_name = mkSOName platform lib
so_name = mkSOName platform lib
lib_so_name = "lib" ++ so_name
dyn_lib_file = case (arch, os) of
(ArchX86_64, OSSolaris2) -> "64" </> so_name
_ -> so_name
findObject = liftM (fmap Object) $ findFile dirs obj_file
findDynObject = liftM (fmap Object) $ findFile dirs dyn_obj_file
findArchive = let local = liftM (fmap Archive) $ findFile dirs arch_file
linked = liftM (fmap Archive) $ searchForLibUsingGcc dflags arch_file dirs
in liftM2 (<|>) local linked
findHSDll = liftM (fmap DLLPath) $ findFile dirs hs_dyn_lib_file
findDll = liftM (fmap DLLPath) $ findFile dirs dyn_lib_file
findSysDll = fmap (fmap $ DLL . takeFileName) $ findSystemLibrary hsc_env so_name
tryGcc = let short = liftM (fmap DLLPath) $ searchForLibUsingGcc dflags so_name dirs
full = liftM (fmap DLLPath) $ searchForLibUsingGcc dflags lib_so_name dirs
in liftM2 (<|>) short full
findObject = liftM (fmap Object) $ findFile dirs obj_file
findDynObject = liftM (fmap Object) $ findFile dirs dyn_obj_file
findArchive = let local = liftM (fmap Archive) $ findFile dirs arch_file
linked = liftM (fmap Archive) $ searchForLibUsingGcc dflags arch_file dirs
in liftM2 (<|>) local linked
findHSDll = liftM (fmap DLLPath) $ findFile dirs hs_dyn_lib_file
findDll = liftM (fmap DLLPath) $ findFile dirs dyn_lib_file
findSysDll = fmap (fmap $ DLL . takeFileName) $ findSystemLibrary hsc_env so_name
tryGcc = let short = liftM (fmap DLLPath) $ searchForLibUsingGcc dflags so_name dirs
full = liftM (fmap DLLPath) $ searchForLibUsingGcc dflags lib_so_name dirs
in liftM2 (<|>) short full
tryImpLib = case os of
OSMinGW32 -> let check name = liftM (fmap Archive) $ searchForLibUsingGcc dflags name dirs
in apply (map check import_libs)
_ -> return Nothing
assumeDll = return (DLL lib)
infixr `orElse`
f `orElse` g = f >>= maybe g return
apply [] = return Nothing
apply (x:xs) = do x' <- x
if isJust x'
then return x'
else apply xs
platform = targetPlatform dflags
arch = platformArch platform
os = platformOS platform
......
This diff is collapsed.
......@@ -16,7 +16,8 @@ typedef enum {
OBJECT_LOADED,
OBJECT_NEEDED,
OBJECT_RESOLVED,
OBJECT_UNLOADED
OBJECT_UNLOADED,
OBJECT_DONT_RESOLVE
} OStatus;
/* Indication of section kinds for loaded objects. Needed by
......@@ -172,6 +173,11 @@ typedef struct _ObjectCode {
ForeignExportStablePtr *stable_ptrs;
/* Indicates whether if the .o file comes from
an import library. In which case we shouldn't
execute code from it. */
HsBool isImportLib;
} ObjectCode;
#define OC_INFORMATIVE_FILENAME(OC) \
......@@ -192,4 +198,121 @@ void exitLinker( void );
void freeObjectCode (ObjectCode *oc);
#if defined(mingw32_HOST_OS)
typedef unsigned char UChar;
typedef unsigned short UInt16;
typedef short Int16;
typedef unsigned int UInt32;
typedef int Int32;
typedef unsigned long long int UInt64;
typedef
struct {
UInt16 Machine;
UInt16 NumberOfSections;
UInt32 TimeDateStamp;
UInt32 PointerToSymbolTable;
UInt32 NumberOfSymbols;
UInt16 SizeOfOptionalHeader;
UInt16 Characteristics;
}
COFF_header;
#define sizeof_COFF_header 20
/* Section 7.1 PE Specification */
typedef
struct {
UInt16 Sig1;
UInt16 Sig2;
UInt16 Version;
UInt16 Machine;
UInt32 TimeDateStamp;
UInt32 SizeOfData;
UInt16 Ordinal;
UInt16 Type_NameType_Reserved;
}
COFF_import_header;
#define sizeof_COFF_import_Header 20
typedef
struct {
UChar Name[8];
UInt32 VirtualSize;
UInt32 VirtualAddress;
UInt32 SizeOfRawData;
UInt32 PointerToRawData;
UInt32 PointerToRelocations;
UInt32 PointerToLinenumbers;
UInt16 NumberOfRelocations;
UInt16 NumberOfLineNumbers;
UInt32 Characteristics;
}
COFF_section;
#define sizeof_COFF_section 40
typedef
struct {
UChar Name[8];
UInt32 Value;
Int16 SectionNumber;
UInt16 Type;
UChar StorageClass;
UChar NumberOfAuxSymbols;
}
COFF_symbol;
#define sizeof_COFF_symbol 18
typedef
struct {
UInt32 VirtualAddress;
UInt32 SymbolTableIndex;
UInt16 Type;
}
COFF_reloc;
#define sizeof_COFF_reloc 10
/* From PE spec doc, section 3.3.2 */
/* Note use of MYIMAGE_* since IMAGE_* are already defined in
windows.h -- for the same purpose, but I want to know what I'm
getting, here. */
#define MYIMAGE_FILE_RELOCS_STRIPPED 0x0001
#define MYIMAGE_FILE_EXECUTABLE_IMAGE 0x0002
#define MYIMAGE_FILE_DLL 0x2000
#define MYIMAGE_FILE_SYSTEM 0x1000
#define MYIMAGE_FILE_BYTES_REVERSED_HI 0x8000
#define MYIMAGE_FILE_BYTES_REVERSED_LO 0x0080
#define MYIMAGE_FILE_32BIT_MACHINE 0x0100
/* From PE spec doc, section 5.4.2 and 5.4.4 */
#define MYIMAGE_SYM_CLASS_EXTERNAL 2
#define MYIMAGE_SYM_CLASS_STATIC 3
#define MYIMAGE_SYM_UNDEFINED 0
#define MYIMAGE_SYM_CLASS_SECTION 104
#define MYIMAGE_SYM_CLASS_WEAK_EXTERNAL 105
/* From PE spec doc, section 3.1 */
#define MYIMAGE_SCN_CNT_CODE 0x00000020
#define MYIMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
#define MYIMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080
#define MYIMAGE_SCN_LNK_COMDAT 0x00001000
#define MYIMAGE_SCN_LNK_NRELOC_OVFL 0x01000000
#define MYIMAGE_SCN_LNK_REMOVE 0x00000800
#define MYIMAGE_SCN_MEM_DISCARDABLE 0x02000000
/* From PE spec doc, section 5.2.1 */
#define MYIMAGE_REL_I386_DIR32 0x0006
#define MYIMAGE_REL_I386_DIR32NB 0x0007
#define MYIMAGE_REL_I386_REL32 0x0014
#endif /* OBJFORMAT_PEi386 */
#endif /* LINKERINTERNALS_H */
......@@ -64,6 +64,22 @@ compile_libAB_dyn:
'$(TEST_HC)' $(TEST_HC_OPTS) -ignore-dot-ghci -v0 -o "bin_dyn/$(call EXE,T10955dyn)" -L./bin_dyn -lB -lA T10955dyn.hs -v0
LD_LIBRARY_PATH=./bin_dyn ./bin_dyn/$(call EXE,T10955dyn)
.PHONY: compile_libAS_impl_gcc
compile_libAS_impl_gcc:
rm -rf bin_impl_gcc
mkdir bin_impl_gcc
'$(TEST_HC)' $(MY_TEST_HC_OPTS) -odir "bin_impl_gcc" -shared A.c -o "bin_impl_gcc/$(call DLL,ASimpL)"
mv bin_impl_gcc/libASimpL.dll.a bin_impl_gcc/libASx.dll.a
echo "main" | '$(TEST_HC)' $(TEST_HC_OPTS) --interactive -ignore-dot-ghci -v0 T11072.hs -lASx -L./bin_impl_gcc
.PHONY: compile_libAS_impl_msvc
compile_libAS_impl_msvc:
rm -rf bin_impl_msvc
mkdir bin_impl_msvc
'$(TEST_HC)' $(MY_TEST_HC_OPTS) -odir "bin_impl_msvc" -shared A.c -o "bin_impl_msvc/$(call DLL,ASimpL)"
rm -f bin_impl_msvc/libAS*.a
echo "main" | '$(TEST_HC)' $(TEST_HC_OPTS) --interactive -ignore-dot-ghci -v0 T11072.hs -lAS -L./bin_impl_msvc -L"./$(shell uname -m)"
.PHONY: T1407
T1407:
cat T1407.script | LD_LIBRARY_PATH=. "$(TEST_HC)" $(TEST_HC_OPTS) -ignore-dot-ghci -v0 --interactive -L.
......
module Main where
import Foreign
import Foreign.C.Types
foreign import ccall "foo" dle :: IO CInt
main = dle >>= print
......@@ -34,3 +34,13 @@ test('T10458',
pre_cmd('$MAKE -s --no-print-directory compile_libT10458'),
extra_hc_opts('-L$PWD/T10458dir -lAS')],
ghci_script, ['T10458.script'])
test('T11072gcc',
[unless(doing_ghci, skip), unless(opsys('mingw32'), skip), extra_clean(['bin_impl_gcc/*', 'bin_impl_gcc'])],
run_command,
['$MAKE -s --no-print-directory compile_libAS_impl_gcc'])
test('T11072msvc',
[unless(doing_ghci, skip), unless(opsys('mingw32'), skip), extra_clean(['bin_impl_msvc/*', 'bin_impl_msvc'])],
run_command,
['$MAKE -s --no-print-directory compile_libAS_impl_msvc'])
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