diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/Runtime/r3/win/ldrNative-win.cpp | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/win/ldrNative-win.cpp b/src/VBox/Runtime/r3/win/ldrNative-win.cpp new file mode 100644 index 00000000..5175a03f --- /dev/null +++ b/src/VBox/Runtime/r3/win/ldrNative-win.cpp @@ -0,0 +1,316 @@ +/* $Id: ldrNative-win.cpp $ */ +/** @file + * IPRT - Binary Image Loader, Win32 native. + */ + +/* + * Copyright (C) 2006-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_LDR +#include <iprt/nt/nt-and-windows.h> + +#include <iprt/ldr.h> +#include "internal/iprt.h" + +#include <iprt/alloca.h> +#include <iprt/assert.h> +#include <iprt/ctype.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/log.h> +#include <iprt/once.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + +#include "internal/ldr.h" +#include "internal-r3-win.h" + +#ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR +# define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR UINT32_C(0x100) +# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR UINT32_C(0x200) +# define LOAD_LIBRARY_SEARCH_SYSTEM32 UINT32_C(0x800) +#endif + + +DECLHIDDEN(int) rtldrNativeLoad(const char *pszFilename, uintptr_t *phHandle, uint32_t fFlags, PRTERRINFO pErrInfo) +{ + Assert(sizeof(*phHandle) >= sizeof(HMODULE)); + AssertReturn(!(fFlags & RTLDRLOAD_FLAGS_GLOBAL), VERR_INVALID_FLAGS); + AssertLogRelMsgReturn(RTPathStartsWithRoot(pszFilename), /* Relative names will still be applied to the search path. */ + ("pszFilename='%s'\n", pszFilename), + VERR_INTERNAL_ERROR_2); + AssertReleaseMsg(g_hModKernel32, + ("rtldrNativeLoad(%s,,) is called before IPRT has configured the windows loader!\n", pszFilename)); + + /* + * Convert to UTF-16 and make sure it got a .DLL suffix. + */ + /** @todo Implement long path support for native DLL loading on windows. @bugref{9248} */ + int rc; + RTUTF16 *pwszNative = NULL; + if (RTPathHasSuffix(pszFilename) || (fFlags & RTLDRLOAD_FLAGS_NO_SUFFIX)) + rc = RTStrToUtf16(pszFilename, &pwszNative); + else + { + size_t cwcAlloc; + rc = RTStrCalcUtf16LenEx(pszFilename, RTSTR_MAX, &cwcAlloc); + if (RT_SUCCESS(rc)) + { + cwcAlloc += sizeof(".DLL"); + pwszNative = RTUtf16Alloc(cwcAlloc * sizeof(RTUTF16)); + if (pwszNative) + { + size_t cwcNative; + rc = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwszNative, cwcAlloc, &cwcNative); + if (RT_SUCCESS(rc)) + rc = RTUtf16CopyAscii(&pwszNative[cwcNative], cwcAlloc - cwcNative, ".DLL"); + } + else + rc = VERR_NO_UTF16_MEMORY; + } + } + if (RT_SUCCESS(rc)) + { + /* Convert slashes just to be on the safe side. */ + for (size_t off = 0;; off++) + { + RTUTF16 wc = pwszNative[off]; + if (wc == '/') + pwszNative[off] = '\\'; + else if (!wc) + break; + } + + /* + * Attempt load. + */ + HMODULE hmod; + static int s_iSearchDllLoadDirSupported = 0; + if ( !(fFlags & RTLDRLOAD_FLAGS_NT_SEARCH_DLL_LOAD_DIR) + || s_iSearchDllLoadDirSupported < 0) + hmod = LoadLibraryExW(pwszNative, NULL, 0); + else + { + hmod = LoadLibraryExW(pwszNative, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32 + | LOAD_LIBRARY_SEARCH_APPLICATION_DIR); + if (s_iSearchDllLoadDirSupported == 0) + { + if (hmod != NULL || GetLastError() != ERROR_INVALID_PARAMETER) + s_iSearchDllLoadDirSupported = 1; + else + { + s_iSearchDllLoadDirSupported = -1; + hmod = LoadLibraryExW(pwszNative, NULL, 0); + } + } + } + if (hmod) + { + *phHandle = (uintptr_t)hmod; + RTUtf16Free(pwszNative); + return VINF_SUCCESS; + } + + /* + * Try figure why it failed to load. + */ + DWORD dwErr = GetLastError(); + rc = RTErrConvertFromWin32(dwErr); + rc = RTErrInfoSetF(pErrInfo, rc, "GetLastError=%u", dwErr); + } + else + rc = RTErrInfoSetF(pErrInfo, rc, "Error converting UTF-8 to UTF-16 string."); + RTUtf16Free(pwszNative); + return rc; +} + + +DECLCALLBACK(int) rtldrNativeGetSymbol(PRTLDRMODINTERNAL pMod, const char *pszSymbol, void **ppvValue) +{ + PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod; + FARPROC pfn = GetProcAddress((HMODULE)pModNative->hNative, pszSymbol); + if (pfn) + { + *ppvValue = (void *)pfn; + return VINF_SUCCESS; + } + *ppvValue = NULL; + return RTErrConvertFromWin32(GetLastError()); +} + + +DECLCALLBACK(int) rtldrNativeClose(PRTLDRMODINTERNAL pMod) +{ + PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod; + if ( (pModNative->fFlags & RTLDRLOAD_FLAGS_NO_UNLOAD) + || FreeLibrary((HMODULE)pModNative->hNative)) + { + pModNative->hNative = (uintptr_t)INVALID_HANDLE_VALUE; + return VINF_SUCCESS; + } + return RTErrConvertFromWin32(GetLastError()); +} + + +DECLHIDDEN(int) rtldrNativeLoadSystem(const char *pszFilename, const char *pszExt, uint32_t fFlags, PRTLDRMOD phLdrMod) +{ + AssertReleaseMsg(g_hModKernel32, + ("rtldrNativeLoadSystem(%s,,) is called before IPRT has configured the windows loader!\n", pszFilename)); + + /* + * Resolve side-by-side resolver API. + */ + static bool volatile s_fInitialized = false; + static decltype(RtlDosApplyFileIsolationRedirection_Ustr) *s_pfnApplyRedir = NULL; + if (!s_fInitialized) + { + s_pfnApplyRedir = (decltype(s_pfnApplyRedir))GetProcAddress(g_hModNtDll, + "RtlDosApplyFileIsolationRedirection_Ustr"); + ASMCompilerBarrier(); + s_fInitialized = true; + } + + /* + * We try WinSxS via undocumented NTDLL API and flal back on the System32 + * directory. No other locations are supported. + */ + int rc = VERR_TRY_AGAIN; + char szPath[RTPATH_MAX]; + char *pszPath = szPath; + + /* Get the windows system32 directory so we can sanity check the WinSxS result. */ + WCHAR wszSysDir[MAX_PATH]; + UINT cwcSysDir = GetSystemDirectoryW(wszSysDir, MAX_PATH); + if (cwcSysDir >= MAX_PATH) + return VERR_FILENAME_TOO_LONG; + + /* Try side-by-side first (see COMCTL32.DLL). */ + if (s_pfnApplyRedir) + { + size_t cwcName = 0; + RTUTF16 wszName[MAX_PATH]; + PRTUTF16 pwszName = wszName; + int rc2 = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwszName, RT_ELEMENTS(wszName), &cwcName); + if (RT_SUCCESS(rc2)) + { + static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll"); + WCHAR wszPath[MAX_PATH]; + UNICODE_STRING UniStrStatic = { 0, (USHORT)sizeof(wszPath) - sizeof(WCHAR), wszPath }; + UNICODE_STRING UniStrDynamic = { 0, 0, NULL }; + PUNICODE_STRING pUniStrResult = NULL; + UNICODE_STRING UniStrName = + { (USHORT)(cwcName * sizeof(RTUTF16)), (USHORT)((cwcName + 1) * sizeof(RTUTF16)), wszName }; + + NTSTATUS rcNt = s_pfnApplyRedir(1 /*fFlags*/, + &UniStrName, + (PUNICODE_STRING)&s_DefaultSuffix, + &UniStrStatic, + &UniStrDynamic, + &pUniStrResult, + NULL /*pNewFlags*/, + NULL /*pcbFilename*/, + NULL /*pcbNeeded*/); + if (NT_SUCCESS(rcNt)) + { + /* + * Check that the resolved path has similarities to the + * system directory. + * + * ASSUMES the windows directory is a root directory and + * that both System32 and are on the same level. So, we'll + * have 2 matching components (or more if the resolver + * returns a system32 path for some reason). + */ + unsigned cMatchingComponents = 0; + size_t off = 0; + while (off < pUniStrResult->Length) + { + RTUTF16 wc1 = wszSysDir[off]; + RTUTF16 wc2 = pUniStrResult->Buffer[off]; + if (!RTPATH_IS_SLASH(wc1)) + { + if (wc1 == wc2) + off++; + else if ( wc1 < 127 + && wc2 < 127 + && RT_C_TO_LOWER(wc1) == RT_C_TO_LOWER(wc2) ) + off++; + else + break; + } + else if (RTPATH_IS_SLASH(wc2)) + { + cMatchingComponents += off > 0; + do + off++; + while ( off < pUniStrResult->Length + && RTPATH_IS_SLASH(wszSysDir[off]) + && RTPATH_IS_SLASH(pUniStrResult->Buffer[off])); + } + else + break; + } + if (cMatchingComponents >= 2) + { + pszPath = szPath; + rc2 = RTUtf16ToUtf8Ex(pUniStrResult->Buffer, pUniStrResult->Length / sizeof(RTUTF16), + &pszPath, sizeof(szPath), NULL); + if (RT_SUCCESS(rc2)) + rc = VINF_SUCCESS; + } + else + AssertMsgFailed(("%s -> '%*.ls'\n", pszFilename, pUniStrResult->Length, pUniStrResult->Buffer)); + RtlFreeUnicodeString(&UniStrDynamic); + } + } + else + AssertMsgFailed(("%Rrc\n", rc)); + } + + /* If the above didn't succeed, create a system32 path. */ + if (RT_FAILURE(rc)) + { + rc = RTUtf16ToUtf8Ex(wszSysDir, RTSTR_MAX, &pszPath, sizeof(szPath), NULL); + if (RT_SUCCESS(rc)) + { + rc = RTPathAppend(szPath, sizeof(szPath), pszFilename); + if (pszExt && RT_SUCCESS(rc)) + rc = RTStrCat(szPath, sizeof(szPath), pszExt); + } + } + + /* Do the actual loading, if we were successful constructing a name. */ + if (RT_SUCCESS(rc)) + { + if (RTFileExists(szPath)) + rc = RTLdrLoadEx(szPath, phLdrMod, fFlags, NULL); + else + rc = VERR_MODULE_NOT_FOUND; + } + + return rc; +} + |