diff options
author | Ben Gamari <bgamari.foss@gmail.com> | 2016-11-02 15:02:13 -0400 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2016-11-02 15:42:00 -0400 |
commit | 6ea0b4f1a6a0478eeeacb0a7be62e426d4aa58e5 (patch) | |
tree | 337c52b79b06b0b79d554096c9e4aac8ac8d1fe4 /rts | |
parent | c3446c63d64bdc5c2fa627f345c59e893ba0c176 (diff) | |
download | haskell-6ea0b4f1a6a0478eeeacb0a7be62e426d4aa58e5.tar.gz |
linker: Split PEi386 implementation into new source file
Test Plan: Validate
Reviewers: erikd, austin, simonmar
Reviewed By: simonmar
Subscribers: thomie
Differential Revision: https://phabricator.haskell.org/D2648
Diffstat (limited to 'rts')
-rw-r--r-- | rts/Linker.c | 1647 | ||||
-rw-r--r-- | rts/LinkerInternals.h | 118 | ||||
-rw-r--r-- | rts/linker/PEi386.c | 1589 | ||||
-rw-r--r-- | rts/linker/PEi386.h | 161 |
4 files changed, 1798 insertions, 1717 deletions
diff --git a/rts/Linker.c b/rts/Linker.c index d7358892ea..3a10bb39b9 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -63,10 +63,8 @@ // to use here without requiring an additional lib #elif defined (mingw32_HOST_OS) # define OBJFORMAT_PEi386 +# include "linker/PEi386.h" # include <windows.h> -# include <shfolder.h> /* SHGetFolderPathW */ -# include <math.h> -# include <wchar.h> #elif defined(darwin_HOST_OS) # define OBJFORMAT_MACHO # include <regex.h> @@ -206,35 +204,7 @@ static int ocRunInit_ELF ( ObjectCode* oc ); #if NEED_SYMBOL_EXTRAS static int ocAllocateSymbolExtras_ELF ( ObjectCode* oc ); #endif -#elif defined(OBJFORMAT_PEi386) -static int ocVerifyImage_PEi386 ( ObjectCode* oc ); -static int ocGetNames_PEi386 ( ObjectCode* oc ); -static int ocResolve_PEi386 ( ObjectCode* oc ); -static int ocRunInit_PEi386 ( ObjectCode* oc ); -static void *lookupSymbolInDLLs ( unsigned char *lbl ); -/* See Note [mingw-w64 name decoration scheme] */ -#ifndef x86_64_HOST_ARCH - static void zapTrailingAtSign ( unsigned char *sym ); -#endif - -#if defined(x86_64_HOST_ARCH) -#define USED_IF_x86_64_HOST_ARCH /* Nothing */ -#else -#define USED_IF_x86_64_HOST_ARCH STG_UNUSED -#endif -static char *allocateImageAndTrampolines ( - pathchar* arch_name, char* member_name, - FILE* f, - int size, - int isThin); -#if defined(x86_64_HOST_ARCH) -static int ocAllocateSymbolExtras_PEi386 ( ObjectCode* oc ); -static size_t makeSymbolExtra_PEi386( ObjectCode* oc, size_t, char* symbol ); -#define PEi386_IMAGE_OFFSET 4 -#else -#define PEi386_IMAGE_OFFSET 0 -#endif #elif defined(OBJFORMAT_MACHO) static int ocVerifyImage_MachO ( ObjectCode* oc ); static int ocGetNames_MachO ( ObjectCode* oc ); @@ -249,43 +219,6 @@ static void machoInitSymbolsWithoutUnderscore( void ); #endif #endif -#if defined(OBJFORMAT_PEi386) -static int checkAndLoadImportLibrary( - pathchar* arch_name, - char* member_name, - FILE* f); - -static int findAndLoadImportLibrary( - ObjectCode* oc - ); - -static UChar *myindex( - int scale, - void* base, - int index); -static UChar *cstring_from_COFF_symbol_name( - UChar* name, - UChar* strtab); -static char *cstring_from_section_name( - UChar* name, - UChar* strtab); - - -/* 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 /* OBJFORMAT_PEi386 */ - /* on x86_64 we have a problem with relocating symbol references in * code that was compiled without -fPIC. By default, the small memory * model is used, which assumes that symbol references can fit in a @@ -529,7 +462,7 @@ static int ghciInsertSymbolTable( * Returns: 0 on failure and result is not set, * nonzero on success and result set to nonzero pointer */ -static HsBool ghciLookupSymbolInfo(HashTable *table, +HsBool ghciLookupSymbolInfo(HashTable *table, const SymbolName* key, RtsSymbolInfo **result) { RtsSymbolInfo *pinfo = lookupStrHashTable(table, key); @@ -560,8 +493,6 @@ static regex_t re_realso; #ifdef THREADED_RTS static Mutex dl_mutex; // mutex to protect dlopen/dlerror critical section #endif -#elif defined(OBJFORMAT_PEi386) -void addDLLHandle(pathchar* dll_name, HINSTANCE instance); #endif void initLinker (void) @@ -626,14 +557,6 @@ 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"), @@ -670,20 +593,13 @@ initLinker_ (int retain_cafs) } #endif -#if defined(mingw32_HOST_OS) - /* - * These two libraries cause problems when added to the static link, - * but are necessary for resolving symbols in GHCi, hence we load - * them manually here. - */ - addDLL(WSTR("msvcrt")); - addDLL(WSTR("kernel32")); - addDLLHandle(WSTR("*.exe"), GetModuleHandle(NULL)); -#endif - if (RTS_LINKER_USE_MMAP) m32_allocator_init(); +#if defined(OBJFORMAT_PEi386) + initLinker_PEi386(); +#endif + IF_DEBUG(linker, debugBelch("initLinker: done\n")); return; } @@ -727,43 +643,6 @@ exitLinker( void ) { * */ -#if defined(OBJFORMAT_PEi386) -/* A record for storing handles into DLLs. */ - -typedef - struct _OpenedDLL { - pathchar* name; - struct _OpenedDLL* next; - HINSTANCE instance; - } - OpenedDLL; - -/* A list thereof. */ -static OpenedDLL* opened_dlls = NULL; - -/* A record for storing indirectly linked functions from DLLs. */ -typedef - struct _IndirectAddr { - SymbolAddr* addr; - struct _IndirectAddr* next; - } - IndirectAddr; - -/* A list thereof. */ -static IndirectAddr* indirects = NULL; - -/* Adds a DLL instance to the list of DLLs in which to search for symbols. */ -void addDLLHandle(pathchar* dll_name, HINSTANCE instance) { - OpenedDLL* o_dll; - o_dll = stgMallocBytes( sizeof(OpenedDLL), "addDLLHandle" ); - o_dll->name = dll_name ? pathdup(dll_name) : NULL; - o_dll->instance = instance; - o_dll->next = opened_dlls; - opened_dlls = o_dll; -} - -#endif - # if defined(OBJFORMAT_ELF) || defined(OBJFORMAT_MACHO) /* Suppose in ghci we load a temporary SO for a module containing @@ -946,84 +825,7 @@ addDLL( pathchar *dll_name ) return errmsg; # elif defined(OBJFORMAT_PEi386) - /* ------------------- Win32 DLL loader ------------------- */ - - pathchar* buf; - OpenedDLL* o_dll; - HINSTANCE instance; - - IF_DEBUG(linker, debugBelch("\naddDLL; dll_name = `%" PATH_FMT "'\n", dll_name)); - - /* See if we've already got it, and ignore if so. */ - for (o_dll = opened_dlls; o_dll != NULL; o_dll = o_dll->next) { - if (0 == pathcmp(o_dll->name, dll_name)) - return NULL; - } - - /* The file name has no suffix (yet) so that we can try - both foo.dll and foo.drv - - The documentation for LoadLibrary says: - If no file name extension is specified in the lpFileName - parameter, the default library extension .dll is - appended. However, the file name string can include a trailing - point character (.) to indicate that the module name has no - extension. */ - - size_t bufsize = pathlen(dll_name) + 10; - 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"%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; - int cFlag; - int flags_start = 1; // Assume we don't support the new API - - /* Detect if newer API are available, if not, skip the first flags entry */ - if (GetProcAddress((HMODULE)LoadLibraryW(L"Kernel32.DLL"), "AddDllDirectory")) { - flags_start = 0; - } - - /* Iterate through the possible flags and formats */ - for (cFlag = flags_start; cFlag < 2; cFlag++) - { - for (cFormat = 0; cFormat < 4; cFormat++) - { - snwprintf(buf, bufsize, formats[cFormat], dll_name); - instance = LoadLibraryExW(buf, NULL, flags[cFlag]); - if (instance == NULL) - { - if (GetLastError() != ERROR_MOD_NOT_FOUND) - { - goto error; - } - } - else - { - break; // We're done. DLL has been loaded. - } - } - } - - // Check if we managed to load the DLL - if (instance == NULL) { - goto error; - } - - stgFree(buf); - - addDLLHandle(dll_name, instance); - - return NULL; - -error: - stgFree(buf); - sysErrorBelch("addDLL: %" PATH_FMT " (Win32 error %lu)", dll_name, GetLastError()); - - /* LoadLibrary failed; return a ptr to the error msg. */ - return "addDLL: could not load DLL"; + return addDLL_PEi386(dll_name); # else barf("addDLL: not implemented on this platform"); @@ -1043,23 +845,7 @@ pathchar* findSystemLibrary(pathchar* dll_name) IF_DEBUG(linker, debugBelch("\nfindSystemLibrary: dll_name = `%" PATH_FMT "'\n", dll_name)); #if defined(OBJFORMAT_PEi386) - const unsigned int init_buf_size = 1024; - unsigned int bufsize = init_buf_size; - wchar_t* result = malloc(sizeof(wchar_t) * bufsize); - DWORD wResult = SearchPathW(NULL, dll_name, NULL, bufsize, result, NULL); - - if (wResult > bufsize) { - result = realloc(result, sizeof(wchar_t) * wResult); - wResult = SearchPathW(NULL, dll_name, NULL, wResult, result, NULL); - } - - - if (!wResult) { - free(result); - return NULL; - } - - return result; + return findSystemLibrary_PEi386(dll_name); #else (void)(dll_name); // Function not implemented for other platforms. return NULL; @@ -1090,69 +876,7 @@ HsPtr addLibrarySearchPath(pathchar* dll_path) IF_DEBUG(linker, debugBelch("\naddLibrarySearchPath: dll_path = `%" PATH_FMT "'\n", dll_path)); #if defined(OBJFORMAT_PEi386) - HINSTANCE hDLL = LoadLibraryW(L"Kernel32.DLL"); - LPAddDLLDirectory AddDllDirectory = (LPAddDLLDirectory)GetProcAddress((HMODULE)hDLL, "AddDllDirectory"); - - HsPtr result = NULL; - - const unsigned int init_buf_size = 4096; - int bufsize = init_buf_size; - - // Make sure the path is an absolute path - WCHAR* abs_path = malloc(sizeof(WCHAR) * init_buf_size); - DWORD wResult = GetFullPathNameW(dll_path, bufsize, abs_path, NULL); - if (!wResult){ - sysErrorBelch("addLibrarySearchPath[GetFullPathNameW]: %" PATH_FMT " (Win32 error %lu)", dll_path, GetLastError()); - } - else if (wResult > init_buf_size) { - abs_path = realloc(abs_path, sizeof(WCHAR) * wResult); - if (!GetFullPathNameW(dll_path, bufsize, abs_path, NULL)) { - sysErrorBelch("addLibrarySearchPath[GetFullPathNameW]: %" PATH_FMT " (Win32 error %lu)", dll_path, GetLastError()); - } - } - - if (AddDllDirectory) { - result = AddDllDirectory(abs_path); - } - else - { - warnMissingKBLibraryPaths(); - WCHAR* str = malloc(sizeof(WCHAR) * init_buf_size); - wResult = GetEnvironmentVariableW(L"PATH", str, bufsize); - - if (wResult > init_buf_size) { - str = realloc(str, sizeof(WCHAR) * wResult); - bufsize = wResult; - wResult = GetEnvironmentVariableW(L"PATH", str, bufsize); - if (!wResult) { - sysErrorBelch("addLibrarySearchPath[GetEnvironmentVariableW]: %" PATH_FMT " (Win32 error %lu)", dll_path, GetLastError()); - } - } - - bufsize = wResult + 2 + pathlen(abs_path); - wchar_t* newPath = malloc(sizeof(wchar_t) * bufsize); - - wcscpy(newPath, abs_path); - wcscat(newPath, L";"); - wcscat(newPath, str); - if (!SetEnvironmentVariableW(L"PATH", (LPCWSTR)newPath)) { - sysErrorBelch("addLibrarySearchPath[SetEnvironmentVariableW]: %" PATH_FMT " (Win32 error %lu)", abs_path, GetLastError()); - } - - free(newPath); - free(abs_path); - - return str; - } - - if (!result) { - sysErrorBelch("addLibrarySearchPath: %" PATH_FMT " (Win32 error %lu)", abs_path, GetLastError()); - free(abs_path); - return NULL; - } - - free(abs_path); - return result; + return addLibrarySearchPath_PEi386(dll_path); #else (void)(dll_path); // Function not implemented for other platforms. return NULL; @@ -1169,30 +893,7 @@ HsBool removeLibrarySearchPath(HsPtr dll_path_index) IF_DEBUG(linker, debugBelch("\nremoveLibrarySearchPath: ptr = `%p'\n", dll_path_index)); #if defined(OBJFORMAT_PEi386) - HsBool result = 0; - - if (dll_path_index != NULL) { - HINSTANCE hDLL = LoadLibraryW(L"Kernel32.DLL"); - LPRemoveDLLDirectory RemoveDllDirectory = (LPRemoveDLLDirectory)GetProcAddress((HMODULE)hDLL, "RemoveDllDirectory"); - - if (RemoveDllDirectory) { - result = RemoveDllDirectory(dll_path_index); - // dll_path_index is now invalid, do not use it after this point. - } - else - { - warnMissingKBLibraryPaths(); - result = SetEnvironmentVariableW(L"PATH", (LPCWSTR)dll_path_index); - free(dll_path_index); - } - - if (!result) { - sysErrorBelch("removeLibrarySearchPath: (Win32 error %lu)", GetLastError()); - return HS_BOOL_FALSE; - } - } - - return result == 0 ? HS_BOOL_TRUE : HS_BOOL_FALSE; + return removeLibrarySearchPath_PEi386(dll_path_index); #else (void)(dll_path_index); // Function not implemented for other platforms. return HS_BOOL_FALSE; @@ -1212,6 +913,14 @@ HsInt insertSymbol(pathchar* obj_name, SymbolName* key, SymbolAddr* data) /* ----------------------------------------------------------------------------- * lookup a symbol in the hash table */ +#if defined(OBJFORMAT_PEi386) +static SymbolAddr* lookupSymbol_ (SymbolName* lbl) +{ + return lookupSymbol_PEi386(lbl); +} + +#else + static SymbolAddr* lookupSymbol_ (SymbolName* lbl) { IF_DEBUG(linker, debugBelch("lookupSymbol: looking up %s\n", lbl)); @@ -1235,59 +944,44 @@ static SymbolAddr* lookupSymbol_ (SymbolName* lbl) IF_DEBUG(linker, debugBelch("lookupSymbol: looking up %s with dlsym\n", lbl)); ASSERT(lbl[0] == '_'); return internal_dlsym(lbl + 1); -# elif defined(OBJFORMAT_PEi386) - SymbolAddr* sym; - -/* See Note [mingw-w64 name decoration scheme] */ -#ifndef x86_64_HOST_ARCH - zapTrailingAtSign ( (unsigned char*)lbl ); -#endif - sym = lookupSymbolInDLLs((unsigned char*)lbl); - return sym; // might be NULL if not found # else ASSERT(2+2 == 5); return NULL; # endif } else { -#if defined(mingw32_HOST_OS) - // If Windows, perform initialization of uninitialized - // Symbols from the C runtime which was loaded above. - // We do this on lookup to prevent the hit when - // The symbol isn't being used. - if (pinfo->value == (void*)0xBAADF00D) - { - char symBuffer[50]; - sprintf(symBuffer, "_%s", lbl); - pinfo->value = GetProcAddress(GetModuleHandle("msvcrt"), symBuffer); - } -#endif - SymbolAddr* val = pinfo->value; - IF_DEBUG(linker, debugBelch("lookupSymbol: value of %s is %p\n", lbl, val)); + return loadSymbol(lbl, pinfo); + } +} +#endif /* OBJFORMAT_PEi386 */ - int r; - ObjectCode* oc = pinfo->owner; +/* + * Load and relocate the object code for a symbol as necessary. + * Symbol name only used for diagnostics output. + */ +SymbolAddr* loadSymbol(SymbolName *lbl, RtsSymbolInfo *pinfo) { + IF_DEBUG(linker, debugBelch("lookupSymbol: value of %s is %p\n", lbl, pinfo->value)); + 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 loading symbol '%s'\n", lbl)); - r = ocTryLoad(oc); + /* 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 loading symbol '%s'\n", lbl)); + int r = ocTryLoad(oc); + if (!r) { + errorBelch("Could not on-demand load symbol '%s'\n", lbl); + return NULL; + } - if (!r) { - errorBelch("Could not on-demand load symbol '%s'\n", lbl); - return NULL; - } #ifdef PROFILING - // collect any new cost centres & CCSs - // that were defined during runInit - initProfiling2(); + // collect any new cost centres & CCSs + // that were defined during runInit + initProfiling2(); #endif - } - - return val; } + + return pinfo->value; } SymbolAddr* lookupSymbol( SymbolName* lbl ) @@ -1503,18 +1197,7 @@ static void freePreloadObjectFile (ObjectCode *oc) { #if defined(mingw32_HOST_OS) - - VirtualFree(oc->image - PEi386_IMAGE_OFFSET, 0, MEM_RELEASE); - - IndirectAddr *ia, *ia_next; - ia = indirects; - while (ia != NULL) { - ia_next = ia->next; - stgFree(ia); - ia = ia_next; - } - indirects = NULL; - + freePreloadObjectFile_PEi386(oc); #else if (RTS_LINKER_USE_MMAP && oc->imageMapped) { @@ -2646,1246 +2329,6 @@ addSection (Section *s, SectionKind kind, SectionAlloc alloc, } /* -------------------------------------------------------------------------- - * PEi386(+) specifics (Win32 targets) - * ------------------------------------------------------------------------*/ - -/* The information for this linker comes from - Microsoft Portable Executable - and Common Object File Format Specification - revision 8.3 February 2013 - - It can be found online at: - - https://msdn.microsoft.com/en-us/windows/hardware/gg463119.aspx - - Things move, so if that fails, try searching for it via - - http://www.google.com/search?q=PE+COFF+specification - - The ultimate reference for the PE format is the Winnt.h - header file that comes with the Platform SDKs; as always, - implementations will drift wrt their documentation. - - A good background article on the PE format is Matt Pietrek's - March 1994 article in Microsoft System Journal (MSJ) - (Vol.9, No. 3): "Peering Inside the PE: A Tour of the - Win32 Portable Executable File Format." The info in there - has recently been updated in a two part article in - MSDN magazine, issues Feb and March 2002, - "Inside Windows: An In-Depth Look into the Win32 Portable - Executable File Format" - - John Levine's book "Linkers and Loaders" contains useful - info on PE too. - - The PE specification doesn't specify how to do the actual - relocations. For this reason, and because both PE and ELF are - based on COFF, the relocations for the PEi386+ code is based on - the ELF relocations for the equivalent relocation type. - - The ELF ABI can be found at - - http://www.x86-64.org/documentation/abi.pdf - - The current code is based on version 0.99.6 - October 2013 -*/ - - -#if defined(OBJFORMAT_PEi386) - -static int verifyCOFFHeader ( COFF_header *hdr, pathchar *filename); - -/* We assume file pointer is right at the - beginning of COFF object. - */ -static char * -allocateImageAndTrampolines ( - pathchar* arch_name, char* member_name, - FILE* f USED_IF_x86_64_HOST_ARCH, - int size, - int isThin USED_IF_x86_64_HOST_ARCH) -{ - char* image; -#if defined(x86_64_HOST_ARCH) - if (!isThin) - { - /* PeCoff contains number of symbols right in it's header, so - we can reserve the room for symbolExtras right here. */ - COFF_header hdr; - size_t n; - - n = fread(&hdr, 1, sizeof_COFF_header, f); - if (n != sizeof(COFF_header)) { - errorBelch("getNumberOfSymbols: error whilst reading `%s' header in `%S'", - member_name, arch_name); - return NULL; - } - fseek(f, -sizeof_COFF_header, SEEK_CUR); - - if (!verifyCOFFHeader(&hdr, arch_name)) { - return 0; - } - - /* We get back 8-byte aligned memory (is that guaranteed?), but - the offsets to the sections within the file are all 4 mod 8 - (is that guaranteed?). We therefore need to offset the image - by 4, so that all the pointers are 8-byte aligned, so that - pointer tagging works. */ - /* For 32-bit case we don't need this, hence we use macro PEi386_IMAGE_OFFSET, - which equals to 4 for 64-bit case and 0 for 32-bit case. */ - /* We allocate trampolines area for all symbols right behind - image data, aligned on 8. */ - size = ((PEi386_IMAGE_OFFSET + size + 0x7) & ~0x7) - + hdr.NumberOfSymbols * sizeof(SymbolExtra); - } -#endif - image = VirtualAlloc(NULL, size, - MEM_RESERVE | MEM_COMMIT, - PAGE_EXECUTE_READWRITE); - - if (image == NULL) { - errorBelch("%" PATH_FMT ": failed to allocate memory for image for %s", - arch_name, member_name); - return NULL; - } - - return image + PEi386_IMAGE_OFFSET; -} - -static int findAndLoadImportLibrary(ObjectCode* oc) -{ - int i; - - COFF_header* hdr; - COFF_section* sectab; - COFF_symbol* symtab; - UChar* strtab; - - hdr = (COFF_header*)(oc->image); - sectab = (COFF_section*)( - ((UChar*)(oc->image)) - + sizeof_COFF_header + hdr->SizeOfOptionalHeader - ); - - symtab = (COFF_symbol*)( - ((UChar*)(oc->image)) - + hdr->PointerToSymbolTable - ); - - strtab = ((UChar*)symtab) - + hdr->NumberOfSymbols * sizeof_COFF_symbol; - - for (i = 0; i < oc->n_sections; i++) - { - COFF_section* sectab_i - = (COFF_section*)myindex(sizeof_COFF_section, sectab, i); - - char *secname = cstring_from_section_name(sectab_i->Name, strtab); - - // Find the first entry containing a valid .idata$7 section. - if (strcmp(secname, ".idata$7") == 0) { - /* First load the containing DLL if not loaded. */ - Section section = oc->sections[i]; - - pathchar* dirName = pathdir(oc->fileName); - HsPtr token = addLibrarySearchPath(dirName); - stgFree(dirName); - char* dllName = (char*)section.start; - - if (strlen(dllName) == 0 || dllName[0] == ' ') - { - continue; - } - - IF_DEBUG(linker, debugBelch("lookupSymbol: on-demand '%ls' => `%s'\n", oc->fileName, dllName)); - - pathchar* dll = mkPath(dllName); - removeLibrarySearchPath(token); - - const char* result = addDLL(dll); - stgFree(dll); - - if (result != NULL) { - errorBelch("Could not load `%s'. Reason: %s\n", (char*)dllName, result); - return 0; - } - - break; - } - - stgFree(secname); - } - - return 1; -} - -static int checkAndLoadImportLibrary( pathchar* arch_name, char* member_name, FILE* f) -{ - char* image; - static HsBool load_dll_warn = HS_BOOL_FALSE; - - if (load_dll_warn) { return 0; } - - /* Based on Import Library specification. PE Spec section 7.1 */ - - COFF_import_header hdr; - size_t n; - - n = fread(&hdr, 1, sizeof_COFF_import_Header, f); - if (n != sizeof(COFF_header)) { - errorBelch("getNumberOfSymbols: error whilst reading `%s' header in `%" PATH_FMT "'\n", - member_name, arch_name); - return 0; - } - - if (hdr.Sig1 != 0x0 || hdr.Sig2 != 0xFFFF) { - fseek(f, -sizeof_COFF_import_Header, SEEK_CUR); - IF_DEBUG(linker, debugBelch("loadArchive: Object `%s` is not an import lib. Skipping...\n", member_name)); - return 0; - } - - IF_DEBUG(linker, debugBelch("loadArchive: reading %d bytes at %ld\n", hdr.SizeOfData, ftell(f))); - - image = malloc(hdr.SizeOfData); - n = fread(image, 1, hdr.SizeOfData, f); - if (n != hdr.SizeOfData) { - errorBelch("loadArchive: error whilst reading `%s' header in `%" PATH_FMT "'. Did not read enough bytes.\n", - member_name, arch_name); - } - - char* symbol = strtok(image, "\0"); - int symLen = strlen(symbol) + 1; - int nameLen = n - symLen; - char* dllName = malloc(sizeof(char) * nameLen); - dllName = strncpy(dllName, image + symLen, nameLen); - pathchar* dll = malloc(sizeof(wchar_t) * nameLen); - mbstowcs(dll, dllName, nameLen); - free(dllName); - - IF_DEBUG(linker, debugBelch("loadArchive: read symbol %s from lib `%ls'\n", symbol, dll)); - const char* result = addDLL(dll); - - free(image); - - if (result != NULL) { - errorBelch("Could not load `%ls'. Reason: %s\n", dll, result); - load_dll_warn = HS_BOOL_TRUE; - - free(dll); - fseek(f, -(n + sizeof_COFF_import_Header), SEEK_CUR); - return 0; - } - - free(dll); - return 1; -} - -/* We use myindex to calculate array addresses, rather than - simply doing the normal subscript thing. That's because - some of the above structs have sizes which are not - a whole number of words. GCC rounds their sizes up to a - whole number of words, which means that the address calcs - arising from using normal C indexing or pointer arithmetic - are just plain wrong. Sigh. -*/ -static UChar * -myindex ( int scale, void* base, int index ) -{ - return - ((UChar*)base) + scale * index; -} - - -static void -printName ( UChar* name, UChar* strtab ) -{ - if (name[0]==0 && name[1]==0 && name[2]==0 && name[3]==0) { - UInt32 strtab_offset = * (UInt32*)(name+4); - debugBelch("%s", strtab + strtab_offset ); - } else { - int i; - for (i = 0; i < 8; i++) { - if (name[i] == 0) break; - debugBelch("%c", name[i] ); - } - } -} - - -static void -copyName ( UChar* name, UChar* strtab, UChar* dst, int dstSize ) -{ - if (name[0]==0 && name[1]==0 && name[2]==0 && name[3]==0) { - UInt32 strtab_offset = * (UInt32*)(name+4); - strncpy ( (char*)dst, (char*)strtab+strtab_offset, dstSize ); - dst[dstSize-1] = 0; - } else { - int i = 0; - while (1) { - if (i >= 8) break; - if (name[i] == 0) break; - dst[i] = name[i]; - i++; - } - dst[i] = 0; - } -} - - -static UChar * -cstring_from_COFF_symbol_name ( UChar* name, UChar* strtab ) -{ - UChar* newstr; - /* If the string is longer than 8 bytes, look in the - string table for it -- this will be correctly zero terminated. - */ - if (name[0]==0 && name[1]==0 && name[2]==0 && name[3]==0) { - UInt32 strtab_offset = * (UInt32*)(name+4); - return ((UChar*)strtab) + strtab_offset; - } - /* Otherwise, if shorter than 8 bytes, return the original, - which by defn is correctly terminated. - */ - if (name[7]==0) return name; - /* The annoying case: 8 bytes. Copy into a temporary - (XXX which is never freed ...) - */ - newstr = stgMallocBytes(9, "cstring_from_COFF_symbol_name"); - ASSERT(newstr); - strncpy((char*)newstr,(char*)name,8); - newstr[8] = 0; - return newstr; -} - -/* Getting the name of a section is mildly tricky, so we make a - function for it. Sadly, in one case we have to copy the string - (when it is exactly 8 bytes long there's no trailing '\0'), so for - consistency we *always* copy the string; the caller must free it -*/ -static char * -cstring_from_section_name (UChar* name, UChar* strtab) -{ - char *newstr; - - if (name[0]=='/') { - int strtab_offset = strtol((char*)name+1,NULL,10); - int len = strlen(((char*)strtab) + strtab_offset); - - newstr = stgMallocBytes(len+1, "cstring_from_section_symbol_name"); - strcpy((char*)newstr, (char*)((UChar*)strtab) + strtab_offset); - return newstr; - } - else - { - newstr = stgMallocBytes(9, "cstring_from_section_symbol_name"); - ASSERT(newstr); - strncpy((char*)newstr,(char*)name,8); - newstr[8] = 0; - return newstr; - } -} - -/* See Note [mingw-w64 name decoration scheme] */ -#ifndef x86_64_HOST_ARCH -static void -zapTrailingAtSign ( UChar* sym ) -{ -# define my_isdigit(c) ((c) >= '0' && (c) <= '9') - int i, j; - if (sym[0] == 0) return; - i = 0; - while (sym[i] != 0) i++; - i--; - j = i; - while (j > 0 && my_isdigit(sym[j])) j--; - if (j > 0 && sym[j] == '@' && j != i) sym[j] = 0; -# undef my_isdigit -} -#endif - -/* See Note [mingw-w64 name decoration scheme] */ -#ifndef x86_64_HOST_ARCH -#define STRIP_LEADING_UNDERSCORE 1 -#else -#define STRIP_LEADING_UNDERSCORE 0 -#endif - -/* - Note [mingw-w64 name decoration scheme] - - What's going on with name decoration? Well, original code - have some crufty and ad-hocish paths related mostly to very old - mingw gcc/binutils/runtime combinations. Now mingw-w64 offers pretty - uniform and MS-compatible decoration scheme across its tools and runtime. - - The scheme is pretty straightforward: on 32 bit objects symbols are exported - with underscore prepended (and @ + stack size suffix appended for stdcall - functions), on 64 bits no underscore is prepended and no suffix is appended - because we have no stdcall convention on 64 bits. - - See #9218 -*/ - -static SymbolAddr* -lookupSymbolInDLLs ( UChar *lbl ) -{ - OpenedDLL* o_dll; - SymbolAddr* sym; - - for (o_dll = opened_dlls; o_dll != NULL; o_dll = o_dll->next) { - /* debugBelch("look in %ls for %s\n", o_dll->name, lbl); */ - - sym = GetProcAddress(o_dll->instance, (char*)(lbl+STRIP_LEADING_UNDERSCORE)); - if (sym != NULL) { - /*debugBelch("found %s in %s\n", lbl+1,o_dll->name);*/ - return sym; - } - - /* Ticket #2283. - Long description: http://support.microsoft.com/kb/132044 - tl;dr: - If C/C++ compiler sees __declspec(dllimport) ... foo ... - it generates call *__imp_foo, and __imp_foo here has exactly - the same semantics as in __imp_foo = GetProcAddress(..., "foo") - */ - if (sym == NULL && strncmp ((const char*)lbl, "__imp_", 6) == 0) { - sym = GetProcAddress(o_dll->instance, (char*)(lbl+6+STRIP_LEADING_UNDERSCORE)); - if (sym != NULL) { - IndirectAddr* ret; - ret = stgMallocBytes( sizeof(IndirectAddr), "lookupSymbolInDLLs" ); - ret->addr = sym; - ret->next = indirects; - indirects = ret; - IF_DEBUG(linker, - 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; - } - } - - sym = GetProcAddress(o_dll->instance, (char*)lbl); - if (sym != NULL) { - /*debugBelch("found %s in %s\n", lbl,o_dll->name);*/ - return sym; - } - } - return NULL; -} - -static int -verifyCOFFHeader (COFF_header *hdr, pathchar *fileName) -{ -#if defined(i386_HOST_ARCH) - if (hdr->Machine != 0x14c) { - errorBelch("%" PATH_FMT ": Not x86 PEi386", fileName); - return 0; - } -#elif defined(x86_64_HOST_ARCH) - if (hdr->Machine != 0x8664) { - errorBelch("%" PATH_FMT ": Not x86_64 PEi386", fileName); - return 0; - } -#else - errorBelch("PEi386 not supported on this arch"); -#endif - - if (hdr->SizeOfOptionalHeader != 0) { - errorBelch("%" PATH_FMT ": PEi386 with nonempty optional header", - fileName); - return 0; - } - if ( /* (hdr->Characteristics & MYIMAGE_FILE_RELOCS_STRIPPED) || */ - (hdr->Characteristics & MYIMAGE_FILE_EXECUTABLE_IMAGE) || - (hdr->Characteristics & MYIMAGE_FILE_DLL) || - (hdr->Characteristics & MYIMAGE_FILE_SYSTEM) ) { - errorBelch("%" PATH_FMT ": Not a PEi386 object file", fileName); - return 0; - } - if ( (hdr->Characteristics & MYIMAGE_FILE_BYTES_REVERSED_HI) - /* || !(hdr->Characteristics & MYIMAGE_FILE_32BIT_MACHINE) */ ) { - errorBelch("%" PATH_FMT ": Invalid PEi386 word size or endiannness: %d", - fileName, - (int)(hdr->Characteristics)); - return 0; - } - return 1; -} - -static int -ocVerifyImage_PEi386 ( ObjectCode* oc ) -{ - int i; - UInt32 j, noRelocs; - COFF_header* hdr; - COFF_section* sectab; - COFF_symbol* symtab; - UChar* strtab; - /* debugBelch("\nLOADING %s\n", oc->fileName); */ - hdr = (COFF_header*)(oc->image); - sectab = (COFF_section*) ( - ((UChar*)(oc->image)) - + sizeof_COFF_header + hdr->SizeOfOptionalHeader - ); - symtab = (COFF_symbol*) ( - ((UChar*)(oc->image)) - + hdr->PointerToSymbolTable - ); - strtab = ((UChar*)symtab) - + hdr->NumberOfSymbols * sizeof_COFF_symbol; - - if (!verifyCOFFHeader(hdr, oc->fileName)) { - return 0; - } - - /* If the string table size is way crazy, this might indicate that - there are more than 64k relocations, despite claims to the - contrary. Hence this test. */ - /* debugBelch("strtab size %d\n", * (UInt32*)strtab); */ -#if 0 - if ( (*(UInt32*)strtab) > 600000 ) { - /* Note that 600k has no special significance other than being - big enough to handle the almost-2MB-sized lumps that - constitute HSwin32*.o. */ - debugBelch("PEi386 object has suspiciously large string table; > 64k relocs?"); - return 0; - } -#endif - - /* .BSS Section is initialized in ocGetNames_PEi386 - but we need the Sections array initialized here already. */ - Section *sections; - sections = (Section*)stgCallocBytes( - sizeof(Section), - hdr->NumberOfSections + 1, /* +1 for the global BSS section see ocGetNames_PEi386 */ - "ocVerifyImage_PEi386(sections)"); - oc->sections = sections; - oc->n_sections = hdr->NumberOfSections + 1; - - /* Initialize the Sections */ - for (i = 0; i < hdr->NumberOfSections; i++) { - COFF_section* sectab_i - = (COFF_section*) - myindex(sizeof_COFF_section, sectab, i); - - /* Calculate the start of the data section */ - sections[i].start = oc->image + sectab_i->PointerToRawData; - } - - /* No further verification after this point; only debug printing. */ - i = 0; - IF_DEBUG(linker, i=1); - if (i == 0) return 1; - - debugBelch("sectab offset = %" FMT_SizeT "\n", - ((UChar*)sectab) - ((UChar*)hdr) ); - debugBelch("symtab offset = %" FMT_SizeT "\n", - ((UChar*)symtab) - ((UChar*)hdr) ); - debugBelch("strtab offset = %" FMT_SizeT "\n", - ((UChar*)strtab) - ((UChar*)hdr) ); - - debugBelch("\n" ); - debugBelch( "Machine: 0x%x\n", (UInt32)(hdr->Machine) ); - debugBelch( "# sections: %d\n", (UInt32)(hdr->NumberOfSections) ); - debugBelch( "time/date: 0x%x\n", (UInt32)(hdr->TimeDateStamp) ); - debugBelch( "symtab offset: %d\n", (UInt32)(hdr->PointerToSymbolTable) ); - debugBelch( "# symbols: %d\n", (UInt32)(hdr->NumberOfSymbols) ); - debugBelch( "sz of opt hdr: %d\n", (UInt32)(hdr->SizeOfOptionalHeader) ); - debugBelch( "characteristics: 0x%x\n", (UInt32)(hdr->Characteristics) ); - - /* Print the section table. */ - debugBelch("\n" ); - for (i = 0; i < hdr->NumberOfSections; i++) { - COFF_reloc* reltab; - COFF_section* sectab_i - = (COFF_section*) - myindex ( sizeof_COFF_section, sectab, i ); - Section section = sections[i]; - debugBelch( - "\n" - "section %d\n" - " name `", - i - ); - printName ( sectab_i->Name, strtab ); - debugBelch( - "'\n" - " vsize %d\n" - " vaddr %d\n" - " data sz %d\n" - " data off 0x%p\n" - " num rel %d\n" - " off rel %d\n" - " ptr raw 0x%x\n", - sectab_i->VirtualSize, - sectab_i->VirtualAddress, - sectab_i->SizeOfRawData, - section.start, - sectab_i->NumberOfRelocations, - sectab_i->PointerToRelocations, - sectab_i->PointerToRawData - ); - reltab = (COFF_reloc*) ( - ((UChar*)(oc->image)) + sectab_i->PointerToRelocations - ); - - if ( sectab_i->Characteristics & MYIMAGE_SCN_LNK_NRELOC_OVFL ) { - /* If the relocation field (a short) has overflowed, the - * real count can be found in the first reloc entry. - * - * See Section 4.1 (last para) of the PE spec (rev6.0). - */ - COFF_reloc* rel = (COFF_reloc*) - myindex ( sizeof_COFF_reloc, reltab, 0 ); - noRelocs = rel->VirtualAddress; - j = 1; - } else { - noRelocs = sectab_i->NumberOfRelocations; - j = 0; - } - - for (; j < noRelocs; j++) { - COFF_symbol* sym; - COFF_reloc* rel = (COFF_reloc*) - myindex ( sizeof_COFF_reloc, reltab, j ); - debugBelch( - " type 0x%-4x vaddr 0x%-8x name `", - (UInt32)rel->Type, - rel->VirtualAddress ); - sym = (COFF_symbol*) - myindex ( sizeof_COFF_symbol, symtab, rel->SymbolTableIndex ); - /* Hmm..mysterious looking offset - what's it for? SOF */ - printName ( sym->Name, strtab -10 ); - debugBelch("'\n" ); - } - - debugBelch("\n" ); - } - debugBelch("\n" ); - debugBelch("string table has size 0x%x\n", * (UInt32*)strtab ); - debugBelch("---START of string table---\n"); - for (i = 4; i < *(Int32*)strtab; i++) { - if (strtab[i] == 0) - debugBelch("\n"); else - debugBelch("%c", strtab[i] ); - } - debugBelch("--- END of string table---\n"); - - debugBelch("\n" ); - i = 0; - while (1) { - COFF_symbol* symtab_i; - if (i >= (Int32)(hdr->NumberOfSymbols)) break; - symtab_i = (COFF_symbol*) - myindex ( sizeof_COFF_symbol, symtab, i ); - debugBelch( - "symbol %d\n" - " name `", - i - ); - printName ( symtab_i->Name, strtab ); - debugBelch( - "'\n" - " value 0x%x\n" - " 1+sec# %d\n" - " type 0x%x\n" - " sclass 0x%x\n" - " nAux %d\n", - symtab_i->Value, - (Int32)(symtab_i->SectionNumber), - (UInt32)symtab_i->Type, - (UInt32)symtab_i->StorageClass, - (UInt32)symtab_i->NumberOfAuxSymbols - ); - i += symtab_i->NumberOfAuxSymbols; - i++; - } - - debugBelch("\n" ); - return 1; -} - - -static int -ocGetNames_PEi386 ( ObjectCode* oc ) -{ - COFF_header* hdr; - COFF_section* sectab; - COFF_symbol* symtab; - UChar* strtab; - - UChar* sname; - SymbolAddr* addr; - int i; - - hdr = (COFF_header*)(oc->image); - sectab = (COFF_section*) ( - ((UChar*)(oc->image)) - + sizeof_COFF_header + hdr->SizeOfOptionalHeader - ); - symtab = (COFF_symbol*) ( - ((UChar*)(oc->image)) - + hdr->PointerToSymbolTable - ); - strtab = ((UChar*)(oc->image)) - + hdr->PointerToSymbolTable - + hdr->NumberOfSymbols * sizeof_COFF_symbol; - - /* Allocate space for any (local, anonymous) .bss sections. */ - - for (i = 0; i < hdr->NumberOfSections; i++) { - UInt32 bss_sz; - UChar* zspace; - COFF_section* sectab_i - = (COFF_section*) - myindex ( sizeof_COFF_section, sectab, i ); - - char *secname = cstring_from_section_name(sectab_i->Name, strtab); - - if (0 != strcmp(secname, ".bss")) { - stgFree(secname); - continue; - } - - stgFree(secname); - - /* sof 10/05: the PE spec text isn't too clear regarding what - * the SizeOfRawData field is supposed to hold for object - * file sections containing just uninitialized data -- for executables, - * it is supposed to be zero; unclear what it's supposed to be - * for object files. However, VirtualSize is guaranteed to be - * zero for object files, which definitely suggests that SizeOfRawData - * will be non-zero (where else would the size of this .bss section be - * stored?) Looking at the COFF_section info for incoming object files, - * this certainly appears to be the case. - * - * => I suspect we've been incorrectly handling .bss sections in (relocatable) - * object files up until now. This turned out to bite us with ghc-6.4.1's use - * of gcc-3.4.x, which has started to emit initially-zeroed-out local 'static' - * variable decls into the .bss section. (The specific function in Q which - * triggered this is libraries/base/cbits/dirUtils.c:__hscore_getFolderPath()) - */ - if (sectab_i->VirtualSize == 0 && sectab_i->SizeOfRawData == 0) continue; - /* This is a non-empty .bss section. - Allocate zeroed space for it */ - bss_sz = sectab_i->VirtualSize; - if ( bss_sz < sectab_i->SizeOfRawData) { bss_sz = sectab_i->SizeOfRawData; } - zspace = stgCallocBytes(1, bss_sz, "ocGetNames_PEi386(anonymous bss)"); - oc->sections[i].start = zspace; - addProddableBlock(oc, zspace, bss_sz); - /* debugBelch("BSS anon section at 0x%x\n", zspace); */ - } - - /* Copy section information into the ObjectCode. */ - - for (i = 0; i < hdr->NumberOfSections; i++) { - UChar* start; - UChar* end; - UInt32 sz; - - /* By default consider all section as CODE or DATA, which means we want to load them. */ - SectionKind kind - = SECTIONKIND_CODE_OR_RODATA; - COFF_section* sectab_i - = (COFF_section*) - myindex ( sizeof_COFF_section, sectab, i ); - Section section = oc->sections[i]; - - char *secname = cstring_from_section_name(sectab_i->Name, strtab); - - IF_DEBUG(linker, debugBelch("section name = %s\n", secname )); - - /* The PE file section flag indicates whether the section contains code or data. */ - if (sectab_i->Characteristics & MYIMAGE_SCN_CNT_CODE || - sectab_i->Characteristics & MYIMAGE_SCN_CNT_INITIALIZED_DATA) - kind = SECTIONKIND_CODE_OR_RODATA; - - /* Check next if it contains any uninitialized data */ - if (sectab_i->Characteristics & MYIMAGE_SCN_CNT_UNINITIALIZED_DATA) - kind = SECTIONKIND_RWDATA; - - /* Finally check if it can be discarded. This will also ignore .debug sections */ - if (sectab_i->Characteristics & MYIMAGE_SCN_MEM_DISCARDABLE || - sectab_i->Characteristics & MYIMAGE_SCN_LNK_REMOVE) - kind = SECTIONKIND_OTHER; - - if (0==strcmp(".ctors", (char*)secname)) - kind = SECTIONKIND_INIT_ARRAY; - - ASSERT(sectab_i->SizeOfRawData == 0 || sectab_i->VirtualSize == 0); - sz = sectab_i->SizeOfRawData; - if (sz < sectab_i->VirtualSize) sz = sectab_i->VirtualSize; - - start = section.start; - end = start + sz - 1; - - if (kind != SECTIONKIND_OTHER && end >= start) { - addSection(&oc->sections[i], kind, SECTION_NOMEM, start, sz, 0, 0, 0); - addProddableBlock(oc, start, sz); - } - - stgFree(secname); - } - - /* Copy exported symbols into the ObjectCode. */ - - oc->n_symbols = hdr->NumberOfSymbols; - oc->symbols = stgCallocBytes(sizeof(SymbolName*), oc->n_symbols, - "ocGetNames_PEi386(oc->symbols)"); - - /* Work out the size of the global BSS section */ - StgWord globalBssSize = 0; - for (i=0; i < (int)hdr->NumberOfSymbols; i++) { - COFF_symbol* symtab_i; - symtab_i = (COFF_symbol*) - myindex ( sizeof_COFF_symbol, symtab, i ); - if (symtab_i->SectionNumber == MYIMAGE_SYM_UNDEFINED - && symtab_i->Value > 0 - && symtab_i->StorageClass != MYIMAGE_SYM_CLASS_SECTION) { - globalBssSize += symtab_i->Value; - } - i += symtab_i->NumberOfAuxSymbols; - } - - /* Allocate BSS space */ - SymbolAddr* bss = NULL; - if (globalBssSize > 0) { - bss = stgCallocBytes(1, globalBssSize, - "ocGetNames_PEi386(non-anonymous bss)"); - addSection(&oc->sections[oc->n_sections-1], - SECTIONKIND_RWDATA, SECTION_MALLOC, - bss, globalBssSize, 0, 0, 0); - IF_DEBUG(linker, debugBelch("bss @ %p %" FMT_Word "\n", bss, globalBssSize)); - addProddableBlock(oc, bss, globalBssSize); - } else { - addSection(&oc->sections[oc->n_sections-1], - SECTIONKIND_OTHER, SECTION_NOMEM, NULL, 0, 0, 0, 0); - } - - for (i = 0; i < oc->n_symbols; i++) { - COFF_symbol* symtab_i; - symtab_i = (COFF_symbol*) - myindex ( sizeof_COFF_symbol, symtab, i ); - - addr = NULL; - HsBool isWeak = HS_BOOL_FALSE; - if ( symtab_i->SectionNumber != MYIMAGE_SYM_UNDEFINED - && symtab_i->SectionNumber > 0) { - /* This symbol is global and defined, viz, exported */ - /* for MYIMAGE_SYMCLASS_EXTERNAL - && !MYIMAGE_SYM_UNDEFINED, - the address of the symbol is: - address of relevant section + offset in section - */ - COFF_section* sectabent - = (COFF_section*) myindex ( sizeof_COFF_section, - sectab, - symtab_i->SectionNumber-1 ); - if (symtab_i->StorageClass == MYIMAGE_SYM_CLASS_EXTERNAL - || ( symtab_i->StorageClass == MYIMAGE_SYM_CLASS_STATIC - && sectabent->Characteristics & MYIMAGE_SCN_LNK_COMDAT) - ) { - addr = (void*)((size_t)oc->sections[symtab_i->SectionNumber-1].start - + symtab_i->Value); - if (sectabent->Characteristics & MYIMAGE_SCN_LNK_COMDAT) { - isWeak = HS_BOOL_TRUE; - } - } - } - 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; - bss = (SymbolAddr*)((StgWord)bss + (StgWord)symtab_i->Value); - IF_DEBUG(linker, debugBelch("bss symbol @ %p %u\n", addr, symtab_i->Value)); - } - - sname = cstring_from_COFF_symbol_name(symtab_i->Name, strtab); - if (addr != NULL || isWeak == HS_BOOL_TRUE) { - - /* 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] = (SymbolName*)sname; - if (isWeak == HS_BOOL_TRUE) { - setWeakSymbol(oc, sname); - } - - if (! ghciInsertSymbolTable(oc->fileName, symhash, (SymbolName*)sname, addr, - isWeak, oc)) { - return 0; - } - } else { - /* We're skipping the symbol, but if we ever load this - object file we'll want to skip it then too. */ - oc->symbols[i] = NULL; - -# if 0 - debugBelch( - "IGNORING symbol %d\n" - " name `", - i - ); - printName ( symtab_i->Name, strtab ); - debugBelch( - "'\n" - " value 0x%x\n" - " 1+sec# %d\n" - " type 0x%x\n" - " sclass 0x%x\n" - " nAux %d\n", - symtab_i->Value, - (Int32)(symtab_i->SectionNumber), - (UInt32)symtab_i->Type, - (UInt32)symtab_i->StorageClass, - (UInt32)symtab_i->NumberOfAuxSymbols - ); -# endif - } - - i += symtab_i->NumberOfAuxSymbols; - } - - return 1; -} - -#if defined(x86_64_HOST_ARCH) - -/* We've already reserved a room for symbol extras in loadObj, - * so simply set correct pointer here. - */ -static int -ocAllocateSymbolExtras_PEi386 ( ObjectCode* oc ) -{ - oc->symbol_extras = (SymbolExtra*)(oc->image - PEi386_IMAGE_OFFSET - + ((PEi386_IMAGE_OFFSET + oc->fileSize + 0x7) & ~0x7)); - oc->first_symbol_extra = 0; - oc->n_symbol_extras = ((COFF_header*)oc->image)->NumberOfSymbols; - - return 1; -} - -static size_t -makeSymbolExtra_PEi386( ObjectCode* oc, size_t s, char* symbol ) -{ - unsigned int curr_thunk; - SymbolExtra *extra; - - curr_thunk = oc->first_symbol_extra; - if (curr_thunk >= oc->n_symbol_extras) { - barf("Can't allocate thunk for %s", symbol); - } - - extra = oc->symbol_extras + curr_thunk; - - // jmp *-14(%rip) - static uint8_t jmp[] = { 0xFF, 0x25, 0xF2, 0xFF, 0xFF, 0xFF }; - extra->addr = (uint64_t)s; - memcpy(extra->jumpIsland, jmp, 6); - - oc->first_symbol_extra++; - - return (size_t)extra->jumpIsland; -} - -#endif - -static int -ocResolve_PEi386 ( ObjectCode* oc ) -{ - COFF_header* hdr; - COFF_section* sectab; - COFF_symbol* symtab; - UChar* strtab; - - UInt32 A; - size_t S; - SymbolAddr* pP; - - int i; - UInt32 j, noRelocs; - - /* ToDo: should be variable-sized? But is at least safe in the - sense of buffer-overrun-proof. */ - UChar symbol[1000]; - /* debugBelch("resolving for %s\n", oc->fileName); */ - - hdr = (COFF_header*)(oc->image); - sectab = (COFF_section*) ( - ((UChar*)(oc->image)) - + sizeof_COFF_header + hdr->SizeOfOptionalHeader - ); - symtab = (COFF_symbol*) ( - ((UChar*)(oc->image)) - + hdr->PointerToSymbolTable - ); - strtab = ((UChar*)(oc->image)) - + hdr->PointerToSymbolTable - + hdr->NumberOfSymbols * sizeof_COFF_symbol; - - for (i = 0; i < hdr->NumberOfSections; i++) { - COFF_section* sectab_i - = (COFF_section*) - myindex ( sizeof_COFF_section, sectab, i ); - COFF_reloc* reltab - = (COFF_reloc*) ( - ((UChar*)(oc->image)) + sectab_i->PointerToRelocations - ); - Section section = oc->sections[i]; - - char *secname = cstring_from_section_name(sectab_i->Name, strtab); - - /* Ignore sections called which contain stabs debugging information. */ - if ( 0 == strcmp(".stab", (char*)secname) - || 0 == strcmp(".stabstr", (char*)secname) - || 0 == strncmp(".pdata", (char*)secname, 6) - || 0 == strncmp(".xdata", (char*)secname, 6) - || 0 == strncmp(".debug", (char*)secname, 6) - || 0 == strcmp(".rdata$zzz", (char*)secname)) { - stgFree(secname); - continue; - } - - stgFree(secname); - - if ( sectab_i->Characteristics & MYIMAGE_SCN_LNK_NRELOC_OVFL ) { - /* If the relocation field (a short) has overflowed, the - * real count can be found in the first reloc entry. - * - * See Section 4.1 (last para) of the PE spec (rev6.0). - * - * Nov2003 update: the GNU linker still doesn't correctly - * handle the generation of relocatable object files with - * overflown relocations. Hence the output to warn of potential - * troubles. - */ - COFF_reloc* rel = (COFF_reloc*) - myindex ( sizeof_COFF_reloc, reltab, 0 ); - noRelocs = rel->VirtualAddress; - - /* 10/05: we now assume (and check for) a GNU ld that is capable - * of handling object files with (>2^16) of relocs. - */ -#if 0 - debugBelch("WARNING: Overflown relocation field (# relocs found: %u)\n", - noRelocs); -#endif - j = 1; - } else { - noRelocs = sectab_i->NumberOfRelocations; - j = 0; - } - - for (; j < noRelocs; j++) { - COFF_symbol* sym; - COFF_reloc* reltab_j - = (COFF_reloc*) - myindex ( sizeof_COFF_reloc, reltab, j ); - - /* the location to patch */ - pP = (void*)( - (size_t)section.start - + reltab_j->VirtualAddress - - sectab_i->VirtualAddress - ); - /* the existing contents of pP */ - A = *(UInt32*)pP; - /* the symbol to connect to */ - sym = (COFF_symbol*) - myindex ( sizeof_COFF_symbol, - symtab, reltab_j->SymbolTableIndex ); - IF_DEBUG(linker, - debugBelch( - "reloc sec %2d num %3d: type 0x%-4x " - "vaddr 0x%-8x name `", - i, j, - (UInt32)reltab_j->Type, - reltab_j->VirtualAddress ); - printName ( sym->Name, strtab ); - debugBelch("'\n" )); - - if (sym->StorageClass == MYIMAGE_SYM_CLASS_STATIC) { - Section section = oc->sections[sym->SectionNumber-1]; - S = ((size_t)(section.start)) - + ((size_t)(sym->Value)); - } else { - copyName ( sym->Name, strtab, symbol, 1000-1 ); - S = (size_t) lookupSymbol_( (char*)symbol ); - 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); - switch (reltab_j->Type) { -#if defined(i386_HOST_ARCH) - case MYIMAGE_REL_I386_DIR32: - case MYIMAGE_REL_I386_DIR32NB: - *(UInt32 *)pP = ((UInt32)S) + A; - break; - case MYIMAGE_REL_I386_REL32: - /* Tricky. We have to insert a displacement at - pP which, when added to the PC for the _next_ - insn, gives the address of the target (S). - Problem is to know the address of the next insn - when we only know pP. We assume that this - literal field is always the last in the insn, - so that the address of the next insn is pP+4 - -- hence the constant 4. - Also I don't know if A should be added, but so - far it has always been zero. - - SOF 05/2005: 'A' (old contents of *pP) have been observed - to contain values other than zero (the 'wx' object file - that came with wxhaskell-0.9.4; dunno how it was compiled..). - So, add displacement to old value instead of asserting - A to be zero. Fixes wxhaskell-related crashes, and no other - ill effects have been observed. - - Update: the reason why we're seeing these more elaborate - relocations is due to a switch in how the NCG compiles SRTs - and offsets to them from info tables. SRTs live in .(ro)data, - while info tables live in .text, causing GAS to emit REL32/DISP32 - relocations with non-zero values. Adding the displacement is - the right thing to do. - */ - *(UInt32 *)pP = ((UInt32)S) + A - ((UInt32)(size_t)pP) - 4; - break; -#elif defined(x86_64_HOST_ARCH) - case 1: /* R_X86_64_64 (ELF constant 1) - IMAGE_REL_AMD64_ADDR64 (PE constant 1) */ - { - UInt64 A; - checkProddableBlock(oc, pP, 8); - A = *(UInt64*)pP; - *(UInt64 *)pP = ((UInt64)S) + ((UInt64)A); - break; - } - case 2: /* R_X86_64_32 (ELF constant 10) - IMAGE_REL_AMD64_ADDR32 (PE constant 2) */ - case 3: /* R_X86_64_32S (ELF constant 11) - IMAGE_REL_AMD64_ADDR32NB (PE constant 3) */ - case 17: /* R_X86_64_32S ELF constant, no PE mapping. See note [ELF constant in PE file] */ - { - size_t v; - v = S + ((size_t)A); - if (v >> 32) { - copyName ( sym->Name, strtab, symbol, 1000-1 ); - S = makeSymbolExtra_PEi386(oc, S, (char *)symbol); - /* And retry */ - v = S + ((size_t)A); - if (v >> 32) { - barf("IMAGE_REL_AMD64_ADDR32[NB]: High bits are set in %zx for %s", - v, (char *)symbol); - } - } - *(UInt32 *)pP = (UInt32)v; - break; - } - case 4: /* R_X86_64_PC32 (ELF constant 2) - IMAGE_REL_AMD64_REL32 (PE constant 4) */ - { - intptr_t v; - v = ((intptr_t)S) + ((intptr_t)(Int32)A) - ((intptr_t)pP) - 4; - if ((v >> 32) && ((-v) >> 32)) { - /* Make the trampoline then */ - copyName ( sym->Name, strtab, symbol, 1000-1 ); - S = makeSymbolExtra_PEi386(oc, S, (char *)symbol); - /* And retry */ - v = ((intptr_t)S) + ((intptr_t)(Int32)A) - ((intptr_t)pP) - 4; - if ((v >> 32) && ((-v) >> 32)) { - barf("IMAGE_REL_AMD64_REL32: High bits are set in %zx for %s", - v, (char *)symbol); - } - } - *(UInt32 *)pP = (UInt32)v; - break; - } -#endif - default: - debugBelch("%" PATH_FMT ": unhandled PEi386 relocation type %d\n", - oc->fileName, reltab_j->Type); - return 0; - } - - } - } - - IF_DEBUG(linker, debugBelch("completed %" PATH_FMT "\n", oc->fileName)); - return 1; -} - -/* - Note [ELF constant in PE file] - - For some reason, the PE files produced by GHC contain a linux - relocation constant 17 (0x11) in the object files. As far as I (Phyx-) can tell - this constant doesn't seem like it's coming from GHC, or at least I could not find - anything in the .s output that GHC produces which specifies the relocation type. - - This leads me to believe that this is a bug in GAS. However because this constant is - there we must deal with it. This is done by mapping it to the equivalent in behaviour PE - relocation constant 0x03. - - See #9907 -*/ - -static int -ocRunInit_PEi386 ( ObjectCode *oc ) -{ - COFF_header* hdr; - COFF_section* sectab; - UChar* strtab; - int i; - - hdr = (COFF_header*)(oc->image); - sectab = (COFF_section*) ( - ((UChar*)(oc->image)) - + sizeof_COFF_header + hdr->SizeOfOptionalHeader - ); - strtab = ((UChar*)(oc->image)) - + hdr->PointerToSymbolTable - + hdr->NumberOfSymbols * sizeof_COFF_symbol; - - int argc, envc; - char **argv, **envv; - - 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*) - myindex ( sizeof_COFF_section, sectab, i ); - Section section = oc->sections[i]; - char *secname = cstring_from_section_name(sectab_i->Name, strtab); - if (0 == strcmp(".ctors", (char*)secname)) { - UChar *init_startC = section.start; - init_t *init_start, *init_end, *init; - init_start = (init_t*)init_startC; - init_end = (init_t*)(init_startC + sectab_i->SizeOfRawData); - // ctors are run *backwards*! - for (init = init_end - 1; init >= init_start; init--) { - (*init)(argc, argv, envv); - } - } - } - freeProgEnvv(envc, envv); - return 1; -} - -#endif /* defined(OBJFORMAT_PEi386) */ - - -/* -------------------------------------------------------------------------- * ELF specifics * ------------------------------------------------------------------------*/ diff --git a/rts/LinkerInternals.h b/rts/LinkerInternals.h index 1d5288b310..3b6790b3e8 100644 --- a/rts/LinkerInternals.h +++ b/rts/LinkerInternals.h @@ -209,6 +209,7 @@ typedef struct _RtsSymbolInfo { void exitLinker( void ); void freeObjectCode (ObjectCode *oc); +SymbolAddr* loadSymbol(SymbolName *lbl, RtsSymbolInfo *pinfo); void *mmapForLinker (size_t bytes, uint32_t flags, int fd, int offset); @@ -220,122 +221,9 @@ void addSection (Section *s, SectionKind kind, SectionAlloc alloc, void* start, StgWord size, StgWord mapped_offset, void* mapped_start, StgWord mapped_size); -#if defined(mingw32_HOST_OS) +HsBool ghciLookupSymbolInfo(HashTable *table, + const SymbolName* key, RtsSymbolInfo **result); -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 */ /************************************************* diff --git a/rts/linker/PEi386.c b/rts/linker/PEi386.c new file mode 100644 index 0000000000..b8c5231bab --- /dev/null +++ b/rts/linker/PEi386.c @@ -0,0 +1,1589 @@ +/* -------------------------------------------------------------------------- + * PEi386(+) specifics (Win32 targets) + * ------------------------------------------------------------------------*/ + +/* The information for this linker comes from + Microsoft Portable Executable + and Common Object File Format Specification + revision 8.3 February 2013 + + It can be found online at: + + https://msdn.microsoft.com/en-us/windows/hardware/gg463119.aspx + + Things move, so if that fails, try searching for it via + + http://www.google.com/search?q=PE+COFF+specification + + The ultimate reference for the PE format is the Winnt.h + header file that comes with the Platform SDKs; as always, + implementations will drift wrt their documentation. + + A good background article on the PE format is Matt Pietrek's + March 1994 article in Microsoft System Journal (MSJ) + (Vol.9, No. 3): "Peering Inside the PE: A Tour of the + Win32 Portable Executable File Format." The info in there + has recently been updated in a two part article in + MSDN magazine, issues Feb and March 2002, + "Inside Windows: An In-Depth Look into the Win32 Portable + Executable File Format" + + John Levine's book "Linkers and Loaders" contains useful + info on PE too. + + The PE specification doesn't specify how to do the actual + relocations. For this reason, and because both PE and ELF are + based on COFF, the relocations for the PEi386+ code is based on + the ELF relocations for the equivalent relocation type. + + The ELF ABI can be found at + + http://www.x86-64.org/documentation/abi.pdf + + The current code is based on version 0.99.6 - October 2013 +*/ + +#include "Rts.h" + +#if defined(x86_64_HOST_ARCH) +#define USED_IF_x86_64_HOST_ARCH /* Nothing */ +#else +#define USED_IF_x86_64_HOST_ARCH STG_UNUSED +#endif + +#ifdef mingw32_HOST_OS + +#include "RtsUtils.h" +#include "RtsSymbolInfo.h" +#include "GetEnv.h" +#include "linker/PEi386.h" +#include "LinkerInternals.h" + +#include <windows.h> +#include <shfolder.h> /* SHGetFolderPathW */ +#include <math.h> +#include <wchar.h> + +static UChar *myindex( + int scale, + void* base, + int index); + +static UChar *cstring_from_COFF_symbol_name( + UChar* name, + UChar* strtab); +static char *cstring_from_section_name( + UChar* name, + UChar* strtab); + +static size_t makeSymbolExtra_PEi386( + ObjectCode* oc, + size_t s, + char* symbol); + +static void addDLLHandle( + pathchar* dll_name, + HINSTANCE instance); +static int verifyCOFFHeader( + COFF_header *hdr, + pathchar *filename); + +/* 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); + +void initLinker_PEi386() +{ + if (!ghciInsertSymbolTable(WSTR("(GHCi/Ld special symbols)"), + symhash, "__image_base__", __image_base, HS_BOOL_TRUE, NULL)) { + barf("ghciInsertSymbolTable failed"); + } + +#if defined(mingw32_HOST_OS) + /* + * These two libraries cause problems when added to the static link, + * but are necessary for resolving symbols in GHCi, hence we load + * them manually here. + */ + addDLL(WSTR("msvcrt")); + addDLL(WSTR("kernel32")); + addDLLHandle(WSTR("*.exe"), GetModuleHandle(NULL)); +#endif +} + +/* A record for storing handles into DLLs. */ +typedef +struct _OpenedDLL { + pathchar* name; + struct _OpenedDLL* next; + HINSTANCE instance; +} + OpenedDLL; + +/* A list thereof. */ +static OpenedDLL* opened_dlls = NULL; + +/* A record for storing indirectly linked functions from DLLs. */ +typedef +struct _IndirectAddr { + SymbolAddr* addr; + struct _IndirectAddr* next; +} + IndirectAddr; + +/* A list thereof. */ +static IndirectAddr* indirects = NULL; + +/* Adds a DLL instance to the list of DLLs in which to search for symbols. */ +static void addDLLHandle(pathchar* dll_name, HINSTANCE instance) { + OpenedDLL* o_dll; + o_dll = stgMallocBytes( sizeof(OpenedDLL), "addDLLHandle" ); + o_dll->name = dll_name ? pathdup(dll_name) : NULL; + o_dll->instance = instance; + o_dll->next = opened_dlls; + opened_dlls = o_dll; +} + +void freePreloadObjectFile_PEi386(ObjectCode *oc) +{ + VirtualFree(oc->image - PEi386_IMAGE_OFFSET, 0, MEM_RELEASE); + + IndirectAddr *ia, *ia_next; + ia = indirects; + while (ia != NULL) { + ia_next = ia->next; + stgFree(ia); + ia = ia_next; + } + indirects = NULL; +} + +const char * +addDLL_PEi386( pathchar *dll_name ) +{ + /* ------------------- Win32 DLL loader ------------------- */ + + pathchar* buf; + OpenedDLL* o_dll; + HINSTANCE instance; + + IF_DEBUG(linker, debugBelch("\naddDLL; dll_name = `%" PATH_FMT "'\n", dll_name)); + + /* See if we've already got it, and ignore if so. */ + for (o_dll = opened_dlls; o_dll != NULL; o_dll = o_dll->next) { + if (0 == pathcmp(o_dll->name, dll_name)) + return NULL; + } + + /* The file name has no suffix (yet) so that we can try + both foo.dll and foo.drv + + The documentation for LoadLibrary says: + If no file name extension is specified in the lpFileName + parameter, the default library extension .dll is + appended. However, the file name string can include a trailing + point character (.) to indicate that the module name has no + extension. */ + + size_t bufsize = pathlen(dll_name) + 10; + 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"%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; + int cFlag; + int flags_start = 1; // Assume we don't support the new API + + /* Detect if newer API are available, if not, skip the first flags entry */ + if (GetProcAddress((HMODULE)LoadLibraryW(L"Kernel32.DLL"), "AddDllDirectory")) { + flags_start = 0; + } + + /* Iterate through the possible flags and formats */ + for (cFlag = flags_start; cFlag < 2; cFlag++) + { + for (cFormat = 0; cFormat < 4; cFormat++) + { + snwprintf(buf, bufsize, formats[cFormat], dll_name); + instance = LoadLibraryExW(buf, NULL, flags[cFlag]); + if (instance == NULL) + { + if (GetLastError() != ERROR_MOD_NOT_FOUND) + { + goto error; + } + } + else + { + break; // We're done. DLL has been loaded. + } + } + } + + // Check if we managed to load the DLL + if (instance == NULL) { + goto error; + } + + stgFree(buf); + + addDLLHandle(dll_name, instance); + + return NULL; + +error: + stgFree(buf); + sysErrorBelch("addDLL: %" PATH_FMT " (Win32 error %lu)", dll_name, GetLastError()); + + /* LoadLibrary failed; return a ptr to the error msg. */ + return "addDLL: could not load DLL"; +} + +pathchar* findSystemLibrary_PEi386( pathchar* dll_name ) +{ + const unsigned int init_buf_size = 1024; + unsigned int bufsize = init_buf_size; + wchar_t* result = malloc(sizeof(wchar_t) * bufsize); + DWORD wResult = SearchPathW(NULL, dll_name, NULL, bufsize, result, NULL); + + if (wResult > bufsize) { + result = realloc(result, sizeof(wchar_t) * wResult); + wResult = SearchPathW(NULL, dll_name, NULL, wResult, result, NULL); + } + + + if (!wResult) { + free(result); + return NULL; + } + + return result; +} + +HsPtr addLibrarySearchPath_PEi386(pathchar* dll_path) +{ + HINSTANCE hDLL = LoadLibraryW(L"Kernel32.DLL"); + LPAddDLLDirectory AddDllDirectory = (LPAddDLLDirectory)GetProcAddress((HMODULE)hDLL, "AddDllDirectory"); + + HsPtr result = NULL; + + const unsigned int init_buf_size = 4096; + int bufsize = init_buf_size; + + // Make sure the path is an absolute path + WCHAR* abs_path = malloc(sizeof(WCHAR) * init_buf_size); + DWORD wResult = GetFullPathNameW(dll_path, bufsize, abs_path, NULL); + if (!wResult){ + sysErrorBelch("addLibrarySearchPath[GetFullPathNameW]: %" PATH_FMT " (Win32 error %lu)", dll_path, GetLastError()); + } + else if (wResult > init_buf_size) { + abs_path = realloc(abs_path, sizeof(WCHAR) * wResult); + if (!GetFullPathNameW(dll_path, bufsize, abs_path, NULL)) { + sysErrorBelch("addLibrarySearchPath[GetFullPathNameW]: %" PATH_FMT " (Win32 error %lu)", dll_path, GetLastError()); + } + } + + if (AddDllDirectory) { + result = AddDllDirectory(abs_path); + } + else + { + warnMissingKBLibraryPaths(); + WCHAR* str = malloc(sizeof(WCHAR) * init_buf_size); + wResult = GetEnvironmentVariableW(L"PATH", str, bufsize); + + if (wResult > init_buf_size) { + str = realloc(str, sizeof(WCHAR) * wResult); + bufsize = wResult; + wResult = GetEnvironmentVariableW(L"PATH", str, bufsize); + if (!wResult) { + sysErrorBelch("addLibrarySearchPath[GetEnvironmentVariableW]: %" PATH_FMT " (Win32 error %lu)", dll_path, GetLastError()); + } + } + + bufsize = wResult + 2 + pathlen(abs_path); + wchar_t* newPath = malloc(sizeof(wchar_t) * bufsize); + + wcscpy(newPath, abs_path); + wcscat(newPath, L";"); + wcscat(newPath, str); + if (!SetEnvironmentVariableW(L"PATH", (LPCWSTR)newPath)) { + sysErrorBelch("addLibrarySearchPath[SetEnvironmentVariableW]: %" PATH_FMT " (Win32 error %lu)", abs_path, GetLastError()); + } + + free(newPath); + free(abs_path); + + return str; + } + + if (!result) { + sysErrorBelch("addLibrarySearchPath: %" PATH_FMT " (Win32 error %lu)", abs_path, GetLastError()); + free(abs_path); + return NULL; + } + + free(abs_path); + return result; +} + +HsBool removeLibrarySearchPath_PEi386(HsPtr dll_path_index) +{ + HsBool result = 0; + + if (dll_path_index != NULL) { + HINSTANCE hDLL = LoadLibraryW(L"Kernel32.DLL"); + LPRemoveDLLDirectory RemoveDllDirectory = (LPRemoveDLLDirectory)GetProcAddress((HMODULE)hDLL, "RemoveDllDirectory"); + + if (RemoveDllDirectory) { + result = RemoveDllDirectory(dll_path_index); + // dll_path_index is now invalid, do not use it after this point. + } + else + { + warnMissingKBLibraryPaths(); + result = SetEnvironmentVariableW(L"PATH", (LPCWSTR)dll_path_index); + free(dll_path_index); + } + + if (!result) { + sysErrorBelch("removeLibrarySearchPath: (Win32 error %lu)", GetLastError()); + return HS_BOOL_FALSE; + } + } + + return result == 0 ? HS_BOOL_TRUE : HS_BOOL_FALSE; +} + + +/* We assume file pointer is right at the + beginning of COFF object. + */ +char * +allocateImageAndTrampolines ( + pathchar* arch_name, char* member_name, + FILE* f USED_IF_x86_64_HOST_ARCH, + int size, + int isThin USED_IF_x86_64_HOST_ARCH) +{ + char* image; +#if defined(x86_64_HOST_ARCH) + if (!isThin) + { + /* PeCoff contains number of symbols right in it's header, so + we can reserve the room for symbolExtras right here. */ + COFF_header hdr; + size_t n; + + n = fread(&hdr, 1, sizeof_COFF_header, f); + if (n != sizeof(COFF_header)) { + errorBelch("getNumberOfSymbols: error whilst reading `%s' header in `%S'", + member_name, arch_name); + return NULL; + } + fseek(f, -sizeof_COFF_header, SEEK_CUR); + + if (!verifyCOFFHeader(&hdr, arch_name)) { + return 0; + } + + /* We get back 8-byte aligned memory (is that guaranteed?), but + the offsets to the sections within the file are all 4 mod 8 + (is that guaranteed?). We therefore need to offset the image + by 4, so that all the pointers are 8-byte aligned, so that + pointer tagging works. */ + /* For 32-bit case we don't need this, hence we use macro PEi386_IMAGE_OFFSET, + which equals to 4 for 64-bit case and 0 for 32-bit case. */ + /* We allocate trampolines area for all symbols right behind + image data, aligned on 8. */ + size = ((PEi386_IMAGE_OFFSET + size + 0x7) & ~0x7) + + hdr.NumberOfSymbols * sizeof(SymbolExtra); + } +#endif + image = VirtualAlloc(NULL, size, + MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE); + + if (image == NULL) { + errorBelch("%" PATH_FMT ": failed to allocate memory for image for %s", + arch_name, member_name); + return NULL; + } + + return image + PEi386_IMAGE_OFFSET; +} + +int findAndLoadImportLibrary(ObjectCode* oc) +{ + int i; + + COFF_header* hdr; + COFF_section* sectab; + COFF_symbol* symtab; + UChar* strtab; + + hdr = (COFF_header*)(oc->image); + sectab = (COFF_section*)( + ((UChar*)(oc->image)) + + sizeof_COFF_header + hdr->SizeOfOptionalHeader + ); + + symtab = (COFF_symbol*)( + ((UChar*)(oc->image)) + + hdr->PointerToSymbolTable + ); + + strtab = ((UChar*)symtab) + + hdr->NumberOfSymbols * sizeof_COFF_symbol; + + for (i = 0; i < oc->n_sections; i++) + { + COFF_section* sectab_i + = (COFF_section*)myindex(sizeof_COFF_section, sectab, i); + + char *secname = cstring_from_section_name(sectab_i->Name, strtab); + + // Find the first entry containing a valid .idata$7 section. + if (strcmp(secname, ".idata$7") == 0) { + /* First load the containing DLL if not loaded. */ + Section section = oc->sections[i]; + + pathchar* dirName = pathdir(oc->fileName); + HsPtr token = addLibrarySearchPath(dirName); + stgFree(dirName); + char* dllName = (char*)section.start; + + if (strlen(dllName) == 0 || dllName[0] == ' ') + { + continue; + } + + IF_DEBUG(linker, debugBelch("lookupSymbol: on-demand '%ls' => `%s'\n", oc->fileName, dllName)); + + pathchar* dll = mkPath(dllName); + removeLibrarySearchPath(token); + + const char* result = addDLL(dll); + stgFree(dll); + + if (result != NULL) { + errorBelch("Could not load `%s'. Reason: %s\n", (char*)dllName, result); + return 0; + } + + break; + } + + stgFree(secname); + } + + return 1; +} + +int checkAndLoadImportLibrary( pathchar* arch_name, char* member_name, FILE* f) +{ + char* image; + static HsBool load_dll_warn = HS_BOOL_FALSE; + + if (load_dll_warn) { return 0; } + + /* Based on Import Library specification. PE Spec section 7.1 */ + + COFF_import_header hdr; + size_t n; + + n = fread(&hdr, 1, sizeof_COFF_import_Header, f); + if (n != sizeof(COFF_header)) { + errorBelch("getNumberOfSymbols: error whilst reading `%s' header in `%" PATH_FMT "'\n", + member_name, arch_name); + return 0; + } + + if (hdr.Sig1 != 0x0 || hdr.Sig2 != 0xFFFF) { + fseek(f, -sizeof_COFF_import_Header, SEEK_CUR); + IF_DEBUG(linker, debugBelch("loadArchive: Object `%s` is not an import lib. Skipping...\n", member_name)); + return 0; + } + + IF_DEBUG(linker, debugBelch("loadArchive: reading %d bytes at %ld\n", hdr.SizeOfData, ftell(f))); + + image = malloc(hdr.SizeOfData); + n = fread(image, 1, hdr.SizeOfData, f); + if (n != hdr.SizeOfData) { + errorBelch("loadArchive: error whilst reading `%s' header in `%" PATH_FMT "'. Did not read enough bytes.\n", + member_name, arch_name); + } + + char* symbol = strtok(image, "\0"); + int symLen = strlen(symbol) + 1; + int nameLen = n - symLen; + char* dllName = malloc(sizeof(char) * nameLen); + dllName = strncpy(dllName, image + symLen, nameLen); + pathchar* dll = malloc(sizeof(wchar_t) * nameLen); + mbstowcs(dll, dllName, nameLen); + free(dllName); + + IF_DEBUG(linker, debugBelch("loadArchive: read symbol %s from lib `%ls'\n", symbol, dll)); + const char* result = addDLL(dll); + + free(image); + + if (result != NULL) { + errorBelch("Could not load `%ls'. Reason: %s\n", dll, result); + load_dll_warn = HS_BOOL_TRUE; + + free(dll); + fseek(f, -(n + sizeof_COFF_import_Header), SEEK_CUR); + return 0; + } + + free(dll); + return 1; +} + +/* We use myindex to calculate array addresses, rather than + simply doing the normal subscript thing. That's because + some of the above structs have sizes which are not + a whole number of words. GCC rounds their sizes up to a + whole number of words, which means that the address calcs + arising from using normal C indexing or pointer arithmetic + are just plain wrong. Sigh. +*/ +static UChar * +myindex ( int scale, void* base, int index ) +{ + return + ((UChar*)base) + scale * index; +} + + +static void +printName ( UChar* name, UChar* strtab ) +{ + if (name[0]==0 && name[1]==0 && name[2]==0 && name[3]==0) { + UInt32 strtab_offset = * (UInt32*)(name+4); + debugBelch("%s", strtab + strtab_offset ); + } else { + int i; + for (i = 0; i < 8; i++) { + if (name[i] == 0) break; + debugBelch("%c", name[i] ); + } + } +} + + +static void +copyName ( UChar* name, UChar* strtab, UChar* dst, int dstSize ) +{ + if (name[0]==0 && name[1]==0 && name[2]==0 && name[3]==0) { + UInt32 strtab_offset = * (UInt32*)(name+4); + strncpy ( (char*)dst, (char*)strtab+strtab_offset, dstSize ); + dst[dstSize-1] = 0; + } else { + int i = 0; + while (1) { + if (i >= 8) break; + if (name[i] == 0) break; + dst[i] = name[i]; + i++; + } + dst[i] = 0; + } +} + + +static UChar * +cstring_from_COFF_symbol_name ( UChar* name, UChar* strtab ) +{ + UChar* newstr; + /* If the string is longer than 8 bytes, look in the + string table for it -- this will be correctly zero terminated. + */ + if (name[0]==0 && name[1]==0 && name[2]==0 && name[3]==0) { + UInt32 strtab_offset = * (UInt32*)(name+4); + return ((UChar*)strtab) + strtab_offset; + } + /* Otherwise, if shorter than 8 bytes, return the original, + which by defn is correctly terminated. + */ + if (name[7]==0) return name; + /* The annoying case: 8 bytes. Copy into a temporary + (XXX which is never freed ...) + */ + newstr = stgMallocBytes(9, "cstring_from_COFF_symbol_name"); + ASSERT(newstr); + strncpy((char*)newstr,(char*)name,8); + newstr[8] = 0; + return newstr; +} + +/* Getting the name of a section is mildly tricky, so we make a + function for it. Sadly, in one case we have to copy the string + (when it is exactly 8 bytes long there's no trailing '\0'), so for + consistency we *always* copy the string; the caller must free it +*/ +static char * +cstring_from_section_name (UChar* name, UChar* strtab) +{ + char *newstr; + + if (name[0]=='/') { + int strtab_offset = strtol((char*)name+1,NULL,10); + int len = strlen(((char*)strtab) + strtab_offset); + + newstr = stgMallocBytes(len+1, "cstring_from_section_symbol_name"); + strcpy((char*)newstr, (char*)((UChar*)strtab) + strtab_offset); + return newstr; + } + else + { + newstr = stgMallocBytes(9, "cstring_from_section_symbol_name"); + ASSERT(newstr); + strncpy((char*)newstr,(char*)name,8); + newstr[8] = 0; + return newstr; + } +} + +/* See Note [mingw-w64 name decoration scheme] */ +#ifndef x86_64_HOST_ARCH +static void +zapTrailingAtSign ( UChar* sym ) +{ +# define my_isdigit(c) ((c) >= '0' && (c) <= '9') + int i, j; + if (sym[0] == 0) return; + i = 0; + while (sym[i] != 0) i++; + i--; + j = i; + while (j > 0 && my_isdigit(sym[j])) j--; + if (j > 0 && sym[j] == '@' && j != i) sym[j] = 0; +# undef my_isdigit +} +#endif + +/* See Note [mingw-w64 name decoration scheme] */ +#ifndef x86_64_HOST_ARCH +#define STRIP_LEADING_UNDERSCORE 1 +#else +#define STRIP_LEADING_UNDERSCORE 0 +#endif + +/* + Note [mingw-w64 name decoration scheme] + + What's going on with name decoration? Well, original code + have some crufty and ad-hocish paths related mostly to very old + mingw gcc/binutils/runtime combinations. Now mingw-w64 offers pretty + uniform and MS-compatible decoration scheme across its tools and runtime. + + The scheme is pretty straightforward: on 32 bit objects symbols are exported + with underscore prepended (and @ + stack size suffix appended for stdcall + functions), on 64 bits no underscore is prepended and no suffix is appended + because we have no stdcall convention on 64 bits. + + See #9218 +*/ + +SymbolAddr* +lookupSymbolInDLLs ( UChar *lbl ) +{ + OpenedDLL* o_dll; + SymbolAddr* sym; + + for (o_dll = opened_dlls; o_dll != NULL; o_dll = o_dll->next) { + /* debugBelch("look in %ls for %s\n", o_dll->name, lbl); */ + + sym = GetProcAddress(o_dll->instance, (char*)(lbl+STRIP_LEADING_UNDERSCORE)); + if (sym != NULL) { + /*debugBelch("found %s in %s\n", lbl+1,o_dll->name);*/ + return sym; + } + + /* Ticket #2283. + Long description: http://support.microsoft.com/kb/132044 + tl;dr: + If C/C++ compiler sees __declspec(dllimport) ... foo ... + it generates call *__imp_foo, and __imp_foo here has exactly + the same semantics as in __imp_foo = GetProcAddress(..., "foo") + */ + if (sym == NULL && strncmp ((const char*)lbl, "__imp_", 6) == 0) { + sym = GetProcAddress(o_dll->instance, (char*)(lbl+6+STRIP_LEADING_UNDERSCORE)); + if (sym != NULL) { + IndirectAddr* ret; + ret = stgMallocBytes( sizeof(IndirectAddr), "lookupSymbolInDLLs" ); + ret->addr = sym; + ret->next = indirects; + indirects = ret; + IF_DEBUG(linker, + 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; + } + } + + sym = GetProcAddress(o_dll->instance, (char*)lbl); + if (sym != NULL) { + /*debugBelch("found %s in %s\n", lbl,o_dll->name);*/ + return sym; + } + } + return NULL; +} + +static int +verifyCOFFHeader (COFF_header *hdr, pathchar *fileName) +{ +#if defined(i386_HOST_ARCH) + if (hdr->Machine != 0x14c) { + errorBelch("%" PATH_FMT ": Not x86 PEi386", fileName); + return 0; + } +#elif defined(x86_64_HOST_ARCH) + if (hdr->Machine != 0x8664) { + errorBelch("%" PATH_FMT ": Not x86_64 PEi386", fileName); + return 0; + } +#else + errorBelch("PEi386 not supported on this arch"); +#endif + + if (hdr->SizeOfOptionalHeader != 0) { + errorBelch("%" PATH_FMT ": PEi386 with nonempty optional header", + fileName); + return 0; + } + if ( /* (hdr->Characteristics & MYIMAGE_FILE_RELOCS_STRIPPED) || */ + (hdr->Characteristics & MYIMAGE_FILE_EXECUTABLE_IMAGE) || + (hdr->Characteristics & MYIMAGE_FILE_DLL) || + (hdr->Characteristics & MYIMAGE_FILE_SYSTEM) ) { + errorBelch("%" PATH_FMT ": Not a PEi386 object file", fileName); + return 0; + } + if ( (hdr->Characteristics & MYIMAGE_FILE_BYTES_REVERSED_HI) + /* || !(hdr->Characteristics & MYIMAGE_FILE_32BIT_MACHINE) */ ) { + errorBelch("%" PATH_FMT ": Invalid PEi386 word size or endiannness: %d", + fileName, + (int)(hdr->Characteristics)); + return 0; + } + return 1; +} + +int +ocVerifyImage_PEi386 ( ObjectCode* oc ) +{ + int i; + UInt32 j, noRelocs; + COFF_header* hdr; + COFF_section* sectab; + COFF_symbol* symtab; + UChar* strtab; + /* debugBelch("\nLOADING %s\n", oc->fileName); */ + hdr = (COFF_header*)(oc->image); + sectab = (COFF_section*) ( + ((UChar*)(oc->image)) + + sizeof_COFF_header + hdr->SizeOfOptionalHeader + ); + symtab = (COFF_symbol*) ( + ((UChar*)(oc->image)) + + hdr->PointerToSymbolTable + ); + strtab = ((UChar*)symtab) + + hdr->NumberOfSymbols * sizeof_COFF_symbol; + + if (!verifyCOFFHeader(hdr, oc->fileName)) { + return 0; + } + + /* If the string table size is way crazy, this might indicate that + there are more than 64k relocations, despite claims to the + contrary. Hence this test. */ + /* debugBelch("strtab size %d\n", * (UInt32*)strtab); */ +#if 0 + if ( (*(UInt32*)strtab) > 600000 ) { + /* Note that 600k has no special significance other than being + big enough to handle the almost-2MB-sized lumps that + constitute HSwin32*.o. */ + debugBelch("PEi386 object has suspiciously large string table; > 64k relocs?"); + return 0; + } +#endif + + /* .BSS Section is initialized in ocGetNames_PEi386 + but we need the Sections array initialized here already. */ + Section *sections; + sections = (Section*)stgCallocBytes( + sizeof(Section), + hdr->NumberOfSections + 1, /* +1 for the global BSS section see ocGetNames_PEi386 */ + "ocVerifyImage_PEi386(sections)"); + oc->sections = sections; + oc->n_sections = hdr->NumberOfSections + 1; + + /* Initialize the Sections */ + for (i = 0; i < hdr->NumberOfSections; i++) { + COFF_section* sectab_i + = (COFF_section*) + myindex(sizeof_COFF_section, sectab, i); + + /* Calculate the start of the data section */ + sections[i].start = oc->image + sectab_i->PointerToRawData; + } + + /* No further verification after this point; only debug printing. */ + i = 0; + IF_DEBUG(linker, i=1); + if (i == 0) return 1; + + debugBelch("sectab offset = %" FMT_SizeT "\n", + ((UChar*)sectab) - ((UChar*)hdr) ); + debugBelch("symtab offset = %" FMT_SizeT "\n", + ((UChar*)symtab) - ((UChar*)hdr) ); + debugBelch("strtab offset = %" FMT_SizeT "\n", + ((UChar*)strtab) - ((UChar*)hdr) ); + + debugBelch("\n" ); + debugBelch( "Machine: 0x%x\n", (UInt32)(hdr->Machine) ); + debugBelch( "# sections: %d\n", (UInt32)(hdr->NumberOfSections) ); + debugBelch( "time/date: 0x%x\n", (UInt32)(hdr->TimeDateStamp) ); + debugBelch( "symtab offset: %d\n", (UInt32)(hdr->PointerToSymbolTable) ); + debugBelch( "# symbols: %d\n", (UInt32)(hdr->NumberOfSymbols) ); + debugBelch( "sz of opt hdr: %d\n", (UInt32)(hdr->SizeOfOptionalHeader) ); + debugBelch( "characteristics: 0x%x\n", (UInt32)(hdr->Characteristics) ); + + /* Print the section table. */ + debugBelch("\n" ); + for (i = 0; i < hdr->NumberOfSections; i++) { + COFF_reloc* reltab; + COFF_section* sectab_i + = (COFF_section*) + myindex ( sizeof_COFF_section, sectab, i ); + Section section = sections[i]; + debugBelch( + "\n" + "section %d\n" + " name `", + i + ); + printName ( sectab_i->Name, strtab ); + debugBelch( + "'\n" + " vsize %d\n" + " vaddr %d\n" + " data sz %d\n" + " data off 0x%p\n" + " num rel %d\n" + " off rel %d\n" + " ptr raw 0x%x\n", + sectab_i->VirtualSize, + sectab_i->VirtualAddress, + sectab_i->SizeOfRawData, + section.start, + sectab_i->NumberOfRelocations, + sectab_i->PointerToRelocations, + sectab_i->PointerToRawData + ); + reltab = (COFF_reloc*) ( + ((UChar*)(oc->image)) + sectab_i->PointerToRelocations + ); + + if ( sectab_i->Characteristics & MYIMAGE_SCN_LNK_NRELOC_OVFL ) { + /* If the relocation field (a short) has overflowed, the + * real count can be found in the first reloc entry. + * + * See Section 4.1 (last para) of the PE spec (rev6.0). + */ + COFF_reloc* rel = (COFF_reloc*) + myindex ( sizeof_COFF_reloc, reltab, 0 ); + noRelocs = rel->VirtualAddress; + j = 1; + } else { + noRelocs = sectab_i->NumberOfRelocations; + j = 0; + } + + for (; j < noRelocs; j++) { + COFF_symbol* sym; + COFF_reloc* rel = (COFF_reloc*) + myindex ( sizeof_COFF_reloc, reltab, j ); + debugBelch( + " type 0x%-4x vaddr 0x%-8x name `", + (UInt32)rel->Type, + rel->VirtualAddress ); + sym = (COFF_symbol*) + myindex ( sizeof_COFF_symbol, symtab, rel->SymbolTableIndex ); + /* Hmm..mysterious looking offset - what's it for? SOF */ + printName ( sym->Name, strtab -10 ); + debugBelch("'\n" ); + } + + debugBelch("\n" ); + } + debugBelch("\n" ); + debugBelch("string table has size 0x%x\n", * (UInt32*)strtab ); + debugBelch("---START of string table---\n"); + for (i = 4; i < *(Int32*)strtab; i++) { + if (strtab[i] == 0) + debugBelch("\n"); else + debugBelch("%c", strtab[i] ); + } + debugBelch("--- END of string table---\n"); + + debugBelch("\n" ); + i = 0; + while (1) { + COFF_symbol* symtab_i; + if (i >= (Int32)(hdr->NumberOfSymbols)) break; + symtab_i = (COFF_symbol*) + myindex ( sizeof_COFF_symbol, symtab, i ); + debugBelch( + "symbol %d\n" + " name `", + i + ); + printName ( symtab_i->Name, strtab ); + debugBelch( + "'\n" + " value 0x%x\n" + " 1+sec# %d\n" + " type 0x%x\n" + " sclass 0x%x\n" + " nAux %d\n", + symtab_i->Value, + (Int32)(symtab_i->SectionNumber), + (UInt32)symtab_i->Type, + (UInt32)symtab_i->StorageClass, + (UInt32)symtab_i->NumberOfAuxSymbols + ); + i += symtab_i->NumberOfAuxSymbols; + i++; + } + + debugBelch("\n" ); + return 1; +} + +int +ocGetNames_PEi386 ( ObjectCode* oc ) +{ + COFF_header* hdr; + COFF_section* sectab; + COFF_symbol* symtab; + UChar* strtab; + + UChar* sname; + SymbolAddr* addr; + int i; + + hdr = (COFF_header*)(oc->image); + sectab = (COFF_section*) ( + ((UChar*)(oc->image)) + + sizeof_COFF_header + hdr->SizeOfOptionalHeader + ); + symtab = (COFF_symbol*) ( + ((UChar*)(oc->image)) + + hdr->PointerToSymbolTable + ); + strtab = ((UChar*)(oc->image)) + + hdr->PointerToSymbolTable + + hdr->NumberOfSymbols * sizeof_COFF_symbol; + + /* Allocate space for any (local, anonymous) .bss sections. */ + + for (i = 0; i < hdr->NumberOfSections; i++) { + UInt32 bss_sz; + UChar* zspace; + COFF_section* sectab_i + = (COFF_section*) + myindex ( sizeof_COFF_section, sectab, i ); + + char *secname = cstring_from_section_name(sectab_i->Name, strtab); + + if (0 != strcmp(secname, ".bss")) { + stgFree(secname); + continue; + } + + stgFree(secname); + + /* sof 10/05: the PE spec text isn't too clear regarding what + * the SizeOfRawData field is supposed to hold for object + * file sections containing just uninitialized data -- for executables, + * it is supposed to be zero; unclear what it's supposed to be + * for object files. However, VirtualSize is guaranteed to be + * zero for object files, which definitely suggests that SizeOfRawData + * will be non-zero (where else would the size of this .bss section be + * stored?) Looking at the COFF_section info for incoming object files, + * this certainly appears to be the case. + * + * => I suspect we've been incorrectly handling .bss sections in (relocatable) + * object files up until now. This turned out to bite us with ghc-6.4.1's use + * of gcc-3.4.x, which has started to emit initially-zeroed-out local 'static' + * variable decls into the .bss section. (The specific function in Q which + * triggered this is libraries/base/cbits/dirUtils.c:__hscore_getFolderPath()) + */ + if (sectab_i->VirtualSize == 0 && sectab_i->SizeOfRawData == 0) continue; + /* This is a non-empty .bss section. + Allocate zeroed space for it */ + bss_sz = sectab_i->VirtualSize; + if ( bss_sz < sectab_i->SizeOfRawData) { bss_sz = sectab_i->SizeOfRawData; } + zspace = stgCallocBytes(1, bss_sz, "ocGetNames_PEi386(anonymous bss)"); + oc->sections[i].start = zspace; + addProddableBlock(oc, zspace, bss_sz); + /* debugBelch("BSS anon section at 0x%x\n", zspace); */ + } + + /* Copy section information into the ObjectCode. */ + + for (i = 0; i < hdr->NumberOfSections; i++) { + UChar* start; + UChar* end; + UInt32 sz; + + /* By default consider all section as CODE or DATA, which means we want to load them. */ + SectionKind kind + = SECTIONKIND_CODE_OR_RODATA; + COFF_section* sectab_i + = (COFF_section*) + myindex ( sizeof_COFF_section, sectab, i ); + Section section = oc->sections[i]; + + char *secname = cstring_from_section_name(sectab_i->Name, strtab); + + IF_DEBUG(linker, debugBelch("section name = %s\n", secname )); + + /* The PE file section flag indicates whether the section contains code or data. */ + if (sectab_i->Characteristics & MYIMAGE_SCN_CNT_CODE || + sectab_i->Characteristics & MYIMAGE_SCN_CNT_INITIALIZED_DATA) + kind = SECTIONKIND_CODE_OR_RODATA; + + /* Check next if it contains any uninitialized data */ + if (sectab_i->Characteristics & MYIMAGE_SCN_CNT_UNINITIALIZED_DATA) + kind = SECTIONKIND_RWDATA; + + /* Finally check if it can be discarded. This will also ignore .debug sections */ + if (sectab_i->Characteristics & MYIMAGE_SCN_MEM_DISCARDABLE || + sectab_i->Characteristics & MYIMAGE_SCN_LNK_REMOVE) + kind = SECTIONKIND_OTHER; + + if (0==strcmp(".ctors", (char*)secname)) + kind = SECTIONKIND_INIT_ARRAY; + + ASSERT(sectab_i->SizeOfRawData == 0 || sectab_i->VirtualSize == 0); + sz = sectab_i->SizeOfRawData; + if (sz < sectab_i->VirtualSize) sz = sectab_i->VirtualSize; + + start = section.start; + end = start + sz - 1; + + if (kind != SECTIONKIND_OTHER && end >= start) { + addSection(&oc->sections[i], kind, SECTION_NOMEM, start, sz, 0, 0, 0); + addProddableBlock(oc, start, sz); + } + + stgFree(secname); + } + + /* Copy exported symbols into the ObjectCode. */ + + oc->n_symbols = hdr->NumberOfSymbols; + oc->symbols = stgCallocBytes(sizeof(SymbolName*), oc->n_symbols, + "ocGetNames_PEi386(oc->symbols)"); + + /* Work out the size of the global BSS section */ + StgWord globalBssSize = 0; + for (i=0; i < (int)hdr->NumberOfSymbols; i++) { + COFF_symbol* symtab_i; + symtab_i = (COFF_symbol*) + myindex ( sizeof_COFF_symbol, symtab, i ); + if (symtab_i->SectionNumber == MYIMAGE_SYM_UNDEFINED + && symtab_i->Value > 0 + && symtab_i->StorageClass != MYIMAGE_SYM_CLASS_SECTION) { + globalBssSize += symtab_i->Value; + } + i += symtab_i->NumberOfAuxSymbols; + } + + /* Allocate BSS space */ + SymbolAddr* bss = NULL; + if (globalBssSize > 0) { + bss = stgCallocBytes(1, globalBssSize, + "ocGetNames_PEi386(non-anonymous bss)"); + addSection(&oc->sections[oc->n_sections-1], + SECTIONKIND_RWDATA, SECTION_MALLOC, + bss, globalBssSize, 0, 0, 0); + IF_DEBUG(linker, debugBelch("bss @ %p %" FMT_Word "\n", bss, globalBssSize)); + addProddableBlock(oc, bss, globalBssSize); + } else { + addSection(&oc->sections[oc->n_sections-1], + SECTIONKIND_OTHER, SECTION_NOMEM, NULL, 0, 0, 0, 0); + } + + for (i = 0; i < oc->n_symbols; i++) { + COFF_symbol* symtab_i; + symtab_i = (COFF_symbol*) + myindex ( sizeof_COFF_symbol, symtab, i ); + + addr = NULL; + HsBool isWeak = HS_BOOL_FALSE; + if ( symtab_i->SectionNumber != MYIMAGE_SYM_UNDEFINED + && symtab_i->SectionNumber > 0) { + /* This symbol is global and defined, viz, exported */ + /* for MYIMAGE_SYMCLASS_EXTERNAL + && !MYIMAGE_SYM_UNDEFINED, + the address of the symbol is: + address of relevant section + offset in section + */ + COFF_section* sectabent + = (COFF_section*) myindex ( sizeof_COFF_section, + sectab, + symtab_i->SectionNumber-1 ); + if (symtab_i->StorageClass == MYIMAGE_SYM_CLASS_EXTERNAL + || ( symtab_i->StorageClass == MYIMAGE_SYM_CLASS_STATIC + && sectabent->Characteristics & MYIMAGE_SCN_LNK_COMDAT) + ) { + addr = (void*)((size_t)oc->sections[symtab_i->SectionNumber-1].start + + symtab_i->Value); + if (sectabent->Characteristics & MYIMAGE_SCN_LNK_COMDAT) { + isWeak = HS_BOOL_TRUE; + } + } + } + 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; + bss = (SymbolAddr*)((StgWord)bss + (StgWord)symtab_i->Value); + IF_DEBUG(linker, debugBelch("bss symbol @ %p %u\n", addr, symtab_i->Value)); + } + + sname = cstring_from_COFF_symbol_name(symtab_i->Name, strtab); + if (addr != NULL || isWeak == HS_BOOL_TRUE) { + + /* 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] = (SymbolName*)sname; + if (isWeak == HS_BOOL_TRUE) { + setWeakSymbol(oc, sname); + } + + if (! ghciInsertSymbolTable(oc->fileName, symhash, (SymbolName*)sname, addr, + isWeak, oc)) { + return 0; + } + } else { + /* We're skipping the symbol, but if we ever load this + object file we'll want to skip it then too. */ + oc->symbols[i] = NULL; + +# if 0 + debugBelch( + "IGNORING symbol %d\n" + " name `", + i + ); + printName ( symtab_i->Name, strtab ); + debugBelch( + "'\n" + " value 0x%x\n" + " 1+sec# %d\n" + " type 0x%x\n" + " sclass 0x%x\n" + " nAux %d\n", + symtab_i->Value, + (Int32)(symtab_i->SectionNumber), + (UInt32)symtab_i->Type, + (UInt32)symtab_i->StorageClass, + (UInt32)symtab_i->NumberOfAuxSymbols + ); +# endif + } + + i += symtab_i->NumberOfAuxSymbols; + } + + return 1; +} + +#if defined(x86_64_HOST_ARCH) + +/* We've already reserved a room for symbol extras in loadObj, + * so simply set correct pointer here. + */ +int +ocAllocateSymbolExtras_PEi386 ( ObjectCode* oc ) +{ + oc->symbol_extras = (SymbolExtra*)(oc->image - PEi386_IMAGE_OFFSET + + ((PEi386_IMAGE_OFFSET + oc->fileSize + 0x7) & ~0x7)); + oc->first_symbol_extra = 0; + oc->n_symbol_extras = ((COFF_header*)oc->image)->NumberOfSymbols; + + return 1; +} + +static size_t +makeSymbolExtra_PEi386( ObjectCode* oc, size_t s, char* symbol ) +{ + unsigned int curr_thunk; + SymbolExtra *extra; + + curr_thunk = oc->first_symbol_extra; + if (curr_thunk >= oc->n_symbol_extras) { + barf("Can't allocate thunk for %s", symbol); + } + + extra = oc->symbol_extras + curr_thunk; + + // jmp *-14(%rip) + static uint8_t jmp[] = { 0xFF, 0x25, 0xF2, 0xFF, 0xFF, 0xFF }; + extra->addr = (uint64_t)s; + memcpy(extra->jumpIsland, jmp, 6); + + oc->first_symbol_extra++; + + return (size_t)extra->jumpIsland; +} + +#endif /* x86_64_HOST_ARCH */ + +int +ocResolve_PEi386 ( ObjectCode* oc ) +{ + COFF_header* hdr; + COFF_section* sectab; + COFF_symbol* symtab; + UChar* strtab; + + UInt32 A; + size_t S; + SymbolAddr* pP; + + int i; + UInt32 j, noRelocs; + + /* ToDo: should be variable-sized? But is at least safe in the + sense of buffer-overrun-proof. */ + UChar symbol[1000]; + /* debugBelch("resolving for %s\n", oc->fileName); */ + + hdr = (COFF_header*)(oc->image); + sectab = (COFF_section*) ( + ((UChar*)(oc->image)) + + sizeof_COFF_header + hdr->SizeOfOptionalHeader + ); + symtab = (COFF_symbol*) ( + ((UChar*)(oc->image)) + + hdr->PointerToSymbolTable + ); + strtab = ((UChar*)(oc->image)) + + hdr->PointerToSymbolTable + + hdr->NumberOfSymbols * sizeof_COFF_symbol; + + for (i = 0; i < hdr->NumberOfSections; i++) { + COFF_section* sectab_i + = (COFF_section*) + myindex ( sizeof_COFF_section, sectab, i ); + COFF_reloc* reltab + = (COFF_reloc*) ( + ((UChar*)(oc->image)) + sectab_i->PointerToRelocations + ); + Section section = oc->sections[i]; + + char *secname = cstring_from_section_name(sectab_i->Name, strtab); + + /* Ignore sections called which contain stabs debugging information. */ + if ( 0 == strcmp(".stab", (char*)secname) + || 0 == strcmp(".stabstr", (char*)secname) + || 0 == strncmp(".pdata", (char*)secname, 6) + || 0 == strncmp(".xdata", (char*)secname, 6) + || 0 == strncmp(".debug", (char*)secname, 6) + || 0 == strcmp(".rdata$zzz", (char*)secname)) { + stgFree(secname); + continue; + } + + stgFree(secname); + + if ( sectab_i->Characteristics & MYIMAGE_SCN_LNK_NRELOC_OVFL ) { + /* If the relocation field (a short) has overflowed, the + * real count can be found in the first reloc entry. + * + * See Section 4.1 (last para) of the PE spec (rev6.0). + * + * Nov2003 update: the GNU linker still doesn't correctly + * handle the generation of relocatable object files with + * overflown relocations. Hence the output to warn of potential + * troubles. + */ + COFF_reloc* rel = (COFF_reloc*) + myindex ( sizeof_COFF_reloc, reltab, 0 ); + noRelocs = rel->VirtualAddress; + + /* 10/05: we now assume (and check for) a GNU ld that is capable + * of handling object files with (>2^16) of relocs. + */ +#if 0 + debugBelch("WARNING: Overflown relocation field (# relocs found: %u)\n", + noRelocs); +#endif + j = 1; + } else { + noRelocs = sectab_i->NumberOfRelocations; + j = 0; + } + + for (; j < noRelocs; j++) { + COFF_symbol* sym; + COFF_reloc* reltab_j + = (COFF_reloc*) + myindex ( sizeof_COFF_reloc, reltab, j ); + + /* the location to patch */ + pP = (void*)( + (size_t)section.start + + reltab_j->VirtualAddress + - sectab_i->VirtualAddress + ); + /* the existing contents of pP */ + A = *(UInt32*)pP; + /* the symbol to connect to */ + sym = (COFF_symbol*) + myindex ( sizeof_COFF_symbol, + symtab, reltab_j->SymbolTableIndex ); + IF_DEBUG(linker, + debugBelch( + "reloc sec %2d num %3d: type 0x%-4x " + "vaddr 0x%-8x name `", + i, j, + (UInt32)reltab_j->Type, + reltab_j->VirtualAddress ); + printName ( sym->Name, strtab ); + debugBelch("'\n" )); + + if (sym->StorageClass == MYIMAGE_SYM_CLASS_STATIC) { + Section section = oc->sections[sym->SectionNumber-1]; + S = ((size_t)(section.start)) + + ((size_t)(sym->Value)); + } else { + copyName ( sym->Name, strtab, symbol, 1000-1 ); + S = (size_t) lookupSymbol_( (char*)symbol ); + 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); + switch (reltab_j->Type) { +#if defined(i386_HOST_ARCH) + case MYIMAGE_REL_I386_DIR32: + case MYIMAGE_REL_I386_DIR32NB: + *(UInt32 *)pP = ((UInt32)S) + A; + break; + case MYIMAGE_REL_I386_REL32: + /* Tricky. We have to insert a displacement at + pP which, when added to the PC for the _next_ + insn, gives the address of the target (S). + Problem is to know the address of the next insn + when we only know pP. We assume that this + literal field is always the last in the insn, + so that the address of the next insn is pP+4 + -- hence the constant 4. + Also I don't know if A should be added, but so + far it has always been zero. + + SOF 05/2005: 'A' (old contents of *pP) have been observed + to contain values other than zero (the 'wx' object file + that came with wxhaskell-0.9.4; dunno how it was compiled..). + So, add displacement to old value instead of asserting + A to be zero. Fixes wxhaskell-related crashes, and no other + ill effects have been observed. + + Update: the reason why we're seeing these more elaborate + relocations is due to a switch in how the NCG compiles SRTs + and offsets to them from info tables. SRTs live in .(ro)data, + while info tables live in .text, causing GAS to emit REL32/DISP32 + relocations with non-zero values. Adding the displacement is + the right thing to do. + */ + *(UInt32 *)pP = ((UInt32)S) + A - ((UInt32)(size_t)pP) - 4; + break; +#elif defined(x86_64_HOST_ARCH) + case 1: /* R_X86_64_64 (ELF constant 1) - IMAGE_REL_AMD64_ADDR64 (PE constant 1) */ + { + UInt64 A; + checkProddableBlock(oc, pP, 8); + A = *(UInt64*)pP; + *(UInt64 *)pP = ((UInt64)S) + ((UInt64)A); + break; + } + case 2: /* R_X86_64_32 (ELF constant 10) - IMAGE_REL_AMD64_ADDR32 (PE constant 2) */ + case 3: /* R_X86_64_32S (ELF constant 11) - IMAGE_REL_AMD64_ADDR32NB (PE constant 3) */ + case 17: /* R_X86_64_32S ELF constant, no PE mapping. See note [ELF constant in PE file] */ + { + size_t v; + v = S + ((size_t)A); + if (v >> 32) { + copyName ( sym->Name, strtab, symbol, 1000-1 ); + S = makeSymbolExtra_PEi386(oc, S, (char *)symbol); + /* And retry */ + v = S + ((size_t)A); + if (v >> 32) { + barf("IMAGE_REL_AMD64_ADDR32[NB]: High bits are set in %zx for %s", + v, (char *)symbol); + } + } + *(UInt32 *)pP = (UInt32)v; + break; + } + case 4: /* R_X86_64_PC32 (ELF constant 2) - IMAGE_REL_AMD64_REL32 (PE constant 4) */ + { + intptr_t v; + v = ((intptr_t)S) + ((intptr_t)(Int32)A) - ((intptr_t)pP) - 4; + if ((v >> 32) && ((-v) >> 32)) { + /* Make the trampoline then */ + copyName ( sym->Name, strtab, symbol, 1000-1 ); + S = makeSymbolExtra_PEi386(oc, S, (char *)symbol); + /* And retry */ + v = ((intptr_t)S) + ((intptr_t)(Int32)A) - ((intptr_t)pP) - 4; + if ((v >> 32) && ((-v) >> 32)) { + barf("IMAGE_REL_AMD64_REL32: High bits are set in %zx for %s", + v, (char *)symbol); + } + } + *(UInt32 *)pP = (UInt32)v; + break; + } +#endif + default: + debugBelch("%" PATH_FMT ": unhandled PEi386 relocation type %d\n", + oc->fileName, reltab_j->Type); + return 0; + } + + } + } + + IF_DEBUG(linker, debugBelch("completed %" PATH_FMT "\n", oc->fileName)); + return 1; +} + +/* + Note [ELF constant in PE file] + + For some reason, the PE files produced by GHC contain a linux + relocation constant 17 (0x11) in the object files. As far as I (Phyx-) can tell + this constant doesn't seem like it's coming from GHC, or at least I could not find + anything in the .s output that GHC produces which specifies the relocation type. + + This leads me to believe that this is a bug in GAS. However because this constant is + there we must deal with it. This is done by mapping it to the equivalent in behaviour PE + relocation constant 0x03. + + See #9907 +*/ + +int +ocRunInit_PEi386 ( ObjectCode *oc ) +{ + COFF_header* hdr; + COFF_section* sectab; + UChar* strtab; + int i; + + hdr = (COFF_header*)(oc->image); + sectab = (COFF_section*) ( + ((UChar*)(oc->image)) + + sizeof_COFF_header + hdr->SizeOfOptionalHeader + ); + strtab = ((UChar*)(oc->image)) + + hdr->PointerToSymbolTable + + hdr->NumberOfSymbols * sizeof_COFF_symbol; + + int argc, envc; + char **argv, **envv; + + 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*) + myindex ( sizeof_COFF_section, sectab, i ); + Section section = oc->sections[i]; + char *secname = cstring_from_section_name(sectab_i->Name, strtab); + if (0 == strcmp(".ctors", (char*)secname)) { + UChar *init_startC = section.start; + init_t *init_start, *init_end, *init; + init_start = (init_t*)init_startC; + init_end = (init_t*)(init_startC + sectab_i->SizeOfRawData); + // ctors are run *backwards*! + for (init = init_end - 1; init >= init_start; init--) { + (*init)(argc, argv, envv); + } + } + } + freeProgEnvv(envc, envv); + return 1; +} + +SymbolAddr *lookupSymbol_PEi386(SymbolName *lbl) +{ + RtsSymbolInfo *pinfo; + + if (!ghciLookupSymbolInfo(symhash, lbl, &pinfo)) { + IF_DEBUG(linker, debugBelch("lookupSymbol: symbol not found\n")); + + SymbolAddr* sym; + +/* See Note [mingw-w64 name decoration scheme] */ +#ifndef x86_64_HOST_ARCH + zapTrailingAtSign ( (unsigned char*)lbl ); +#endif + sym = lookupSymbolInDLLs((unsigned char*)lbl); + return sym; // might be NULL if not found + } else { +#if defined(mingw32_HOST_OS) + // If Windows, perform initialization of uninitialized + // Symbols from the C runtime which was loaded above. + // We do this on lookup to prevent the hit when + // The symbol isn't being used. + if (pinfo->value == (void*)0xBAADF00D) + { + char symBuffer[50]; + sprintf(symBuffer, "_%s", lbl); + pinfo->value = GetProcAddress(GetModuleHandle("msvcrt"), symBuffer); + } +#endif + return loadSymbol(lbl, pinfo); + } +} + +#endif /* mingw32_HOST_OS */ diff --git a/rts/linker/PEi386.h b/rts/linker/PEi386.h new file mode 100644 index 0000000000..020b84bfec --- /dev/null +++ b/rts/linker/PEi386.h @@ -0,0 +1,161 @@ +#ifndef LINKER_PE_I386_H +#define LINKER_PE_I386_H + +#include "Rts.h" +#include "LinkerInternals.h" +#include "PathUtils.h" + +#include "BeginPrivate.h" + +#if defined(x86_64_HOST_ARCH) +#define PEi386_IMAGE_OFFSET 4 +#else +#define PEi386_IMAGE_OFFSET 0 +#endif + +void initLinker_PEi386(void); +const char * addDLL_PEi386(pathchar *dll_name); +void freePreloadObjectFile_PEi386(ObjectCode *oc); + +int findAndLoadImportLibrary(ObjectCode* oc); +int checkAndLoadImportLibrary( pathchar* arch_name, char* member_name, FILE* f); + +pathchar* findSystemLibrary_PEi386(pathchar* dll_name); +HsPtr addLibrarySearchPath_PEi386(pathchar* dll_path); +HsBool removeLibrarySearchPath_PEi386(HsPtr dll_path_index); + +int ocResolve_PEi386 ( ObjectCode* oc ); +int ocRunInit_PEi386 ( ObjectCode *oc ); +int ocGetNames_PEi386 ( ObjectCode* oc ); +int ocVerifyImage_PEi386 ( ObjectCode* oc ); +SymbolAddr *lookupSymbol_PEi386(SymbolName *lbl); +int ocAllocateSymbolExtras_PEi386 ( ObjectCode* oc ); +void *lookupSymbolInDLLs ( unsigned char *lbl ); +/* See Note [mingw-w64 name decoration scheme] */ + +char * +allocateImageAndTrampolines ( + pathchar* arch_name, char* member_name, + FILE* f, + int size, + int isThin); + +/******************************************** + * COFF/PE headers + ********************************************/ +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 + +#include "EndPrivate.h" + +#endif /* LINKER_PE_I386_H */ |