VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/ldrNative-win.cpp@ 98278

Last change on this file since 98278 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.0 KB
Line 
1/* $Id: ldrNative-win.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Binary Image Loader, Win32 native.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_LDR
42#include <iprt/nt/nt-and-windows.h>
43
44#include <iprt/ldr.h>
45#include "internal/iprt.h"
46
47#include <iprt/alloca.h>
48#include <iprt/assert.h>
49#include <iprt/ctype.h>
50#include <iprt/err.h>
51#include <iprt/file.h>
52#include <iprt/log.h>
53#include <iprt/once.h>
54#include <iprt/path.h>
55#include <iprt/string.h>
56#include <iprt/utf16.h>
57
58#include "internal/ldr.h"
59#include "internal-r3-win.h"
60
61#ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
62# define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR UINT32_C(0x100)
63# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR UINT32_C(0x200)
64# define LOAD_LIBRARY_SEARCH_SYSTEM32 UINT32_C(0x800)
65#endif
66
67
68DECLHIDDEN(int) rtldrNativeLoad(const char *pszFilename, uintptr_t *phHandle, uint32_t fFlags, PRTERRINFO pErrInfo)
69{
70 Assert(sizeof(*phHandle) >= sizeof(HMODULE));
71 AssertReturn(!(fFlags & RTLDRLOAD_FLAGS_GLOBAL), VERR_INVALID_FLAGS);
72 AssertLogRelMsgReturn(RTPathStartsWithRoot(pszFilename), /* Relative names will still be applied to the search path. */
73 ("pszFilename='%s'\n", pszFilename),
74 VERR_INTERNAL_ERROR_2);
75 AssertReleaseMsg(g_hModKernel32,
76 ("rtldrNativeLoad(%s,,) is called before IPRT has configured the windows loader!\n", pszFilename));
77
78 /*
79 * Convert to UTF-16 and make sure it got a .DLL suffix.
80 */
81 /** @todo Implement long path support for native DLL loading on windows. @bugref{9248} */
82 int rc;
83 RTUTF16 *pwszNative = NULL;
84 if (RTPathHasSuffix(pszFilename) || (fFlags & RTLDRLOAD_FLAGS_NO_SUFFIX))
85 rc = RTStrToUtf16(pszFilename, &pwszNative);
86 else
87 {
88 size_t cwcAlloc;
89 rc = RTStrCalcUtf16LenEx(pszFilename, RTSTR_MAX, &cwcAlloc);
90 if (RT_SUCCESS(rc))
91 {
92 cwcAlloc += sizeof(".DLL");
93 pwszNative = RTUtf16Alloc(cwcAlloc * sizeof(RTUTF16));
94 if (pwszNative)
95 {
96 size_t cwcNative;
97 rc = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwszNative, cwcAlloc, &cwcNative);
98 if (RT_SUCCESS(rc))
99 rc = RTUtf16CopyAscii(&pwszNative[cwcNative], cwcAlloc - cwcNative, ".DLL");
100 }
101 else
102 rc = VERR_NO_UTF16_MEMORY;
103 }
104 }
105 if (RT_SUCCESS(rc))
106 {
107 /* Convert slashes just to be on the safe side. */
108 for (size_t off = 0;; off++)
109 {
110 RTUTF16 wc = pwszNative[off];
111 if (wc == '/')
112 pwszNative[off] = '\\';
113 else if (!wc)
114 break;
115 }
116
117 /*
118 * Attempt load.
119 */
120 HMODULE hmod;
121 static int s_iSearchDllLoadDirSupported = 0;
122 if ( !(fFlags & RTLDRLOAD_FLAGS_NT_SEARCH_DLL_LOAD_DIR)
123 || s_iSearchDllLoadDirSupported < 0)
124 hmod = LoadLibraryExW(pwszNative, NULL, 0);
125 else
126 {
127 hmod = LoadLibraryExW(pwszNative, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32
128 | LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
129 if (s_iSearchDllLoadDirSupported == 0)
130 {
131 if (hmod != NULL || GetLastError() != ERROR_INVALID_PARAMETER)
132 s_iSearchDllLoadDirSupported = 1;
133 else
134 {
135 s_iSearchDllLoadDirSupported = -1;
136 hmod = LoadLibraryExW(pwszNative, NULL, 0);
137 }
138 }
139 }
140 if (hmod)
141 {
142 *phHandle = (uintptr_t)hmod;
143 RTUtf16Free(pwszNative);
144 return VINF_SUCCESS;
145 }
146
147 /*
148 * Try figure why it failed to load.
149 */
150 DWORD dwErr = GetLastError();
151 rc = RTErrConvertFromWin32(dwErr);
152 rc = RTErrInfoSetF(pErrInfo, rc, "GetLastError=%u", dwErr);
153 }
154 else
155 rc = RTErrInfoSetF(pErrInfo, rc, "Error converting UTF-8 to UTF-16 string.");
156 RTUtf16Free(pwszNative);
157 return rc;
158}
159
160
161DECLCALLBACK(int) rtldrNativeGetSymbol(PRTLDRMODINTERNAL pMod, const char *pszSymbol, void **ppvValue)
162{
163 PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod;
164 FARPROC pfn = GetProcAddress((HMODULE)pModNative->hNative, pszSymbol);
165 if (pfn)
166 {
167 *ppvValue = (void *)pfn;
168 return VINF_SUCCESS;
169 }
170 *ppvValue = NULL;
171 return RTErrConvertFromWin32(GetLastError());
172}
173
174
175DECLCALLBACK(int) rtldrNativeClose(PRTLDRMODINTERNAL pMod)
176{
177 PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod;
178 if ( (pModNative->fFlags & RTLDRLOAD_FLAGS_NO_UNLOAD)
179 || FreeLibrary((HMODULE)pModNative->hNative))
180 {
181 pModNative->hNative = (uintptr_t)INVALID_HANDLE_VALUE;
182 return VINF_SUCCESS;
183 }
184 return RTErrConvertFromWin32(GetLastError());
185}
186
187
188DECLHIDDEN(int) rtldrNativeLoadSystem(const char *pszFilename, const char *pszExt, uint32_t fFlags, PRTLDRMOD phLdrMod)
189{
190 AssertReleaseMsg(g_hModKernel32,
191 ("rtldrNativeLoadSystem(%s,,) is called before IPRT has configured the windows loader!\n", pszFilename));
192
193 /*
194 * Resolve side-by-side resolver API.
195 */
196 static bool volatile s_fInitialized = false;
197 static decltype(RtlDosApplyFileIsolationRedirection_Ustr) *s_pfnApplyRedir = NULL;
198 if (!s_fInitialized)
199 {
200 s_pfnApplyRedir = (decltype(s_pfnApplyRedir))GetProcAddress(g_hModNtDll,
201 "RtlDosApplyFileIsolationRedirection_Ustr");
202 ASMCompilerBarrier();
203 s_fInitialized = true;
204 }
205
206 /*
207 * We try WinSxS via undocumented NTDLL API and flal back on the System32
208 * directory. No other locations are supported.
209 */
210 int rc = VERR_TRY_AGAIN;
211 char szPath[RTPATH_MAX];
212 char *pszPath = szPath;
213
214 /* Get the windows system32 directory so we can sanity check the WinSxS result. */
215 WCHAR wszSysDir[MAX_PATH];
216 UINT cwcSysDir = GetSystemDirectoryW(wszSysDir, MAX_PATH);
217 if (cwcSysDir >= MAX_PATH)
218 return VERR_FILENAME_TOO_LONG;
219
220 /* Try side-by-side first (see COMCTL32.DLL). */
221 if (s_pfnApplyRedir)
222 {
223 size_t cwcName = 0;
224 RTUTF16 wszName[MAX_PATH];
225 PRTUTF16 pwszName = wszName;
226 int rc2 = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwszName, RT_ELEMENTS(wszName), &cwcName);
227 if (RT_SUCCESS(rc2))
228 {
229 static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll");
230 WCHAR wszPath[MAX_PATH];
231 UNICODE_STRING UniStrStatic = { 0, (USHORT)sizeof(wszPath) - sizeof(WCHAR), wszPath };
232 UNICODE_STRING UniStrDynamic = { 0, 0, NULL };
233 PUNICODE_STRING pUniStrResult = NULL;
234 UNICODE_STRING UniStrName =
235 { (USHORT)(cwcName * sizeof(RTUTF16)), (USHORT)((cwcName + 1) * sizeof(RTUTF16)), wszName };
236
237 NTSTATUS rcNt = s_pfnApplyRedir(1 /*fFlags*/,
238 &UniStrName,
239 (PUNICODE_STRING)&s_DefaultSuffix,
240 &UniStrStatic,
241 &UniStrDynamic,
242 &pUniStrResult,
243 NULL /*pNewFlags*/,
244 NULL /*pcbFilename*/,
245 NULL /*pcbNeeded*/);
246 if (NT_SUCCESS(rcNt))
247 {
248 /*
249 * Check that the resolved path has similarities to the
250 * system directory.
251 *
252 * ASSUMES the windows directory is a root directory and
253 * that both System32 and are on the same level. So, we'll
254 * have 2 matching components (or more if the resolver
255 * returns a system32 path for some reason).
256 */
257 unsigned cMatchingComponents = 0;
258 size_t off = 0;
259 while (off < pUniStrResult->Length)
260 {
261 RTUTF16 wc1 = wszSysDir[off];
262 RTUTF16 wc2 = pUniStrResult->Buffer[off];
263 if (!RTPATH_IS_SLASH(wc1))
264 {
265 if (wc1 == wc2)
266 off++;
267 else if ( wc1 < 127
268 && wc2 < 127
269 && RT_C_TO_LOWER(wc1) == RT_C_TO_LOWER(wc2) )
270 off++;
271 else
272 break;
273 }
274 else if (RTPATH_IS_SLASH(wc2))
275 {
276 cMatchingComponents += off > 0;
277 do
278 off++;
279 while ( off < pUniStrResult->Length
280 && RTPATH_IS_SLASH(wszSysDir[off])
281 && RTPATH_IS_SLASH(pUniStrResult->Buffer[off]));
282 }
283 else
284 break;
285 }
286 if (cMatchingComponents >= 2)
287 {
288 pszPath = szPath;
289 rc2 = RTUtf16ToUtf8Ex(pUniStrResult->Buffer, pUniStrResult->Length / sizeof(RTUTF16),
290 &pszPath, sizeof(szPath), NULL);
291 if (RT_SUCCESS(rc2))
292 rc = VINF_SUCCESS;
293 }
294 else
295 AssertMsgFailed(("%s -> '%*.ls'\n", pszFilename, pUniStrResult->Length, pUniStrResult->Buffer));
296 RtlFreeUnicodeString(&UniStrDynamic);
297 }
298 }
299 else
300 AssertMsgFailed(("%Rrc\n", rc));
301 }
302
303 /* If the above didn't succeed, create a system32 path. */
304 if (RT_FAILURE(rc))
305 {
306 rc = RTUtf16ToUtf8Ex(wszSysDir, RTSTR_MAX, &pszPath, sizeof(szPath), NULL);
307 if (RT_SUCCESS(rc))
308 {
309 rc = RTPathAppend(szPath, sizeof(szPath), pszFilename);
310 if (pszExt && RT_SUCCESS(rc))
311 rc = RTStrCat(szPath, sizeof(szPath), pszExt);
312 }
313 }
314
315 /* Do the actual loading, if we were successful constructing a name. */
316 if (RT_SUCCESS(rc))
317 {
318 if (RTFileExists(szPath))
319 rc = RTLdrLoadEx(szPath, phLdrMod, fFlags, NULL);
320 else
321 rc = VERR_MODULE_NOT_FOUND;
322 }
323
324 return rc;
325}
326
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette