diff options
Diffstat (limited to 'rts/Linker.c')
| -rw-r--r-- | rts/Linker.c | 199 |
1 files changed, 182 insertions, 17 deletions
diff --git a/rts/Linker.c b/rts/Linker.c index 0507c9c268..35227c866b 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -104,6 +104,7 @@ # include <windows.h> # include <shfolder.h> /* SHGetFolderPathW */ # include <math.h> +# include <wchar.h> #elif defined(darwin_HOST_OS) # define OBJFORMAT_MACHO # include <regex.h> @@ -246,6 +247,12 @@ static void machoInitSymbolsWithoutUnderscore( void ); #endif #endif +#if defined(OBJFORMAT_PEi386) +// MingW-w64 is missing these from the implementation. So we have to look them up +typedef DLL_DIRECTORY_COOKIE(*LPAddDLLDirectory)(PCWSTR NewDirectory); +typedef WINBOOL(*LPRemoveDLLDirectory)(DLL_DIRECTORY_COOKIE Cookie); +#endif + static void freeProddableBlocks (ObjectCode *oc); #if USE_MMAP @@ -832,7 +839,7 @@ addDLL( pathchar *dll_name ) OpenedDLL* o_dll; HINSTANCE instance; - /* debugBelch("\naddDLL; dll_name = `%s'\n", dll_name); */ + 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) { @@ -852,23 +859,46 @@ addDLL( pathchar *dll_name ) size_t bufsize = pathlen(dll_name) + 10; buf = stgMallocBytes(bufsize * sizeof(wchar_t), "addDLL"); - snwprintf(buf, bufsize, L"%s.DLL", dll_name); - instance = LoadLibraryW(buf); - if (instance == NULL) { - if (GetLastError() != ERROR_MOD_NOT_FOUND) goto error; - // KAA: allow loading of drivers (like winspool.drv) - snwprintf(buf, bufsize, L"%s.DRV", dll_name); - instance = LoadLibraryW(buf); - if (instance == NULL) { - if (GetLastError() != ERROR_MOD_NOT_FOUND) goto error; - // #1883: allow loading of unix-style libfoo.dll DLLs - snwprintf(buf, bufsize, L"lib%s.DLL", dll_name); - instance = LoadLibraryW(buf); - if (instance == NULL) { - goto error; + + /* These are ordered by probability of success and order we'd like them */ + const wchar_t *formats[] = { L"%s.DLL", L"%s.DRV", L"lib%s.DLL", L"%s" }; + const 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); @@ -877,7 +907,7 @@ addDLL( pathchar *dll_name ) error: stgFree(buf); - sysErrorBelch("%" PATH_FMT, dll_name); + 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"; @@ -887,6 +917,142 @@ error: # endif } + +/* ----------------------------------------------------------------------------- +* Emits a warning determining that the system is missing a required security +* update that we need to get access to the proper APIs +*/ +void warnMissingKBLibraryPaths( void ) +{ + static HsBool missing_update_warn = HS_BOOL_FALSE; + if (!missing_update_warn) { + debugBelch("Warning: If linking fails, consider installing KB2533623.\n"); + missing_update_warn = HS_BOOL_TRUE; + } +} + +/* ----------------------------------------------------------------------------- +* appends a directory to the process DLL Load path so LoadLibrary can find it +* +* Returns: NULL on failure, or pointer to be passed to removeLibrarySearchPath to +* restore the search path to what it was before this call. +*/ +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; +#else + (void)(dll_path); // Function not implemented for other platforms. + return NULL; +#endif +} + +/* ----------------------------------------------------------------------------- +* removes a directory from the process DLL Load path +* +* Returns: HS_BOOL_TRUE on success, otherwise HS_BOOL_FALSE +*/ +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; +#else + (void)(dll_path_index); // Function not implemented for other platforms. + return HS_BOOL_FALSE; +#endif +} + /* ----------------------------------------------------------------------------- * insert a symbol in the hash table * @@ -2806,7 +2972,6 @@ typedef #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 |
