VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PDMLdr.cpp@ 69111

Last change on this file since 69111 was 69111, checked in by vboxsync, 7 years ago

(C) year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 62.5 KB
Line 
1/* $Id: PDMLdr.cpp 69111 2017-10-17 14:26:02Z vboxsync $ */
2/** @file
3 * PDM - Pluggable Device Manager, module loader.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18//#define PDMLDR_FAKE_MODE
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#define LOG_GROUP LOG_GROUP_PDM_LDR
25#include "PDMInternal.h"
26#include <VBox/vmm/pdm.h>
27#include <VBox/vmm/mm.h>
28#include <VBox/vmm/trpm.h>
29#include <VBox/vmm/vmm.h>
30#include <VBox/vmm/vm.h>
31#include <VBox/vmm/uvm.h>
32#include <VBox/sup.h>
33#include <VBox/param.h>
34#include <VBox/err.h>
35#include <VBox/vmm/hm.h>
36#include <VBox/VBoxTpG.h>
37
38#include <VBox/log.h>
39#include <iprt/assert.h>
40#include <iprt/ctype.h>
41#include <iprt/file.h>
42#include <iprt/ldr.h>
43#include <iprt/mem.h>
44#include <iprt/path.h>
45#include <iprt/string.h>
46
47#include <limits.h>
48
49
50/*********************************************************************************************************************************
51* Structures and Typedefs *
52*********************************************************************************************************************************/
53/**
54 * Structure which the user argument of the RTLdrGetBits() callback points to.
55 * @internal
56 */
57typedef struct PDMGETIMPORTARGS
58{
59 PVM pVM;
60 PPDMMOD pModule;
61} PDMGETIMPORTARGS, *PPDMGETIMPORTARGS;
62
63
64/*********************************************************************************************************************************
65* Internal Functions *
66*********************************************************************************************************************************/
67#ifdef VBOX_WITH_RAW_MODE
68static DECLCALLBACK(int) pdmR3GetImportRC(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser);
69static char *pdmR3FileRC(const char *pszFile, const char *pszSearchPath);
70#endif
71static int pdmR3LoadR0U(PUVM pUVM, const char *pszFilename, const char *pszName, const char *pszSearchPath);
72static char *pdmR3FileR0(const char *pszFile, const char *pszSearchPath);
73static char *pdmR3File(const char *pszFile, const char *pszDefaultExt, const char *pszSearchPath, bool fShared);
74
75
76
77/**
78 * Loads the VMMR0.r0 module early in the init process.
79 *
80 * @returns VBox status code.
81 * @param pUVM Pointer to the user mode VM structure.
82 */
83VMMR3_INT_DECL(int) PDMR3LdrLoadVMMR0U(PUVM pUVM)
84{
85 return pdmR3LoadR0U(pUVM, NULL, VMMR0_MAIN_MODULE_NAME, NULL);
86}
87
88
89/**
90 * Init the module loader part of PDM.
91 *
92 * This routine will load the Host Context Ring-0 and Guest
93 * Context VMM modules.
94 *
95 * @returns VBox status code.
96 * @param pUVM The user mode VM structure.
97 */
98int pdmR3LdrInitU(PUVM pUVM)
99{
100#if !defined(PDMLDR_FAKE_MODE) && defined(VBOX_WITH_RAW_MODE)
101 /*
102 * Load the mandatory RC module, the VMMR0.r0 is loaded before VM creation.
103 */
104 PVM pVM = pUVM->pVM; AssertPtr(pVM);
105 if (!HMIsEnabled(pVM))
106 {
107 int rc = PDMR3LdrLoadRC(pVM, NULL, VMMRC_MAIN_MODULE_NAME);
108 if (RT_FAILURE(rc))
109 return rc;
110 }
111#else
112 RT_NOREF(pUVM);
113#endif
114 return VINF_SUCCESS;
115}
116
117
118/**
119 * Terminate the module loader part of PDM.
120 *
121 * This will unload and free all modules.
122 *
123 * @param pUVM The user mode VM structure.
124 *
125 * @remarks This is normally called twice during termination.
126 */
127void pdmR3LdrTermU(PUVM pUVM)
128{
129 /*
130 * Free the modules.
131 */
132 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
133 PPDMMOD pModule = pUVM->pdm.s.pModules;
134 pUVM->pdm.s.pModules = NULL;
135 while (pModule)
136 {
137 /* free loader item. */
138 if (pModule->hLdrMod != NIL_RTLDRMOD)
139 {
140 int rc2 = RTLdrClose(pModule->hLdrMod);
141 AssertRC(rc2);
142 pModule->hLdrMod = NIL_RTLDRMOD;
143 }
144
145 /* free bits. */
146 switch (pModule->eType)
147 {
148 case PDMMOD_TYPE_R0:
149 {
150 Assert(pModule->ImageBase);
151 int rc2 = SUPR3FreeModule((void *)(uintptr_t)pModule->ImageBase);
152 AssertRC(rc2);
153 pModule->ImageBase = 0;
154 break;
155 }
156
157#ifdef VBOX_WITH_RAW_MODE
158 case PDMMOD_TYPE_RC:
159#endif
160 case PDMMOD_TYPE_R3:
161 /* MM will free this memory for us - it's alloc only memory. :-) */
162 break;
163
164 default:
165 AssertMsgFailed(("eType=%d\n", pModule->eType));
166 break;
167 }
168 pModule->pvBits = NULL;
169
170 void *pvFree = pModule;
171 pModule = pModule->pNext;
172 RTMemFree(pvFree);
173 }
174 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
175}
176
177
178/**
179 * Applies relocations to RC modules.
180 *
181 * This must be done very early in the relocation
182 * process so that components can resolve RC symbols during relocation.
183 *
184 * @param pUVM Pointer to the user mode VM structure.
185 * @param offDelta Relocation delta relative to old location.
186 */
187VMMR3_INT_DECL(void) PDMR3LdrRelocateU(PUVM pUVM, RTGCINTPTR offDelta)
188{
189#ifdef VBOX_WITH_RAW_MODE
190 LogFlow(("PDMR3LdrRelocate: offDelta=%RGv\n", offDelta));
191 RT_NOREF1(offDelta);
192
193 /*
194 * RC Modules.
195 */
196 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
197 if (pUVM->pdm.s.pModules)
198 {
199 /*
200 * The relocation have to be done in two passes so imports
201 * can be correctly resolved. The first pass will update
202 * the ImageBase saving the current value in OldImageBase.
203 * The second pass will do the actual relocation.
204 */
205 /* pass 1 */
206 PPDMMOD pCur;
207 for (pCur = pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext)
208 {
209 if (pCur->eType == PDMMOD_TYPE_RC)
210 {
211 pCur->OldImageBase = pCur->ImageBase;
212 pCur->ImageBase = MMHyperR3ToRC(pUVM->pVM, pCur->pvBits);
213 }
214 }
215
216 /* pass 2 */
217 for (pCur = pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext)
218 {
219 if (pCur->eType == PDMMOD_TYPE_RC)
220 {
221 PDMGETIMPORTARGS Args;
222 Args.pVM = pUVM->pVM;
223 Args.pModule = pCur;
224 int rc = RTLdrRelocate(pCur->hLdrMod, pCur->pvBits, pCur->ImageBase, pCur->OldImageBase,
225 pdmR3GetImportRC, &Args);
226 AssertFatalMsgRC(rc, ("RTLdrRelocate failed, rc=%d\n", rc));
227 }
228 }
229 }
230 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
231#else
232 RT_NOREF2(pUVM, offDelta);
233#endif
234}
235
236
237/**
238 * Loads a module into the host context ring-3.
239 *
240 * This is used by the driver and device init functions to load modules
241 * containing the drivers and devices. The function can be extended to
242 * load modules which are not native to the environment we're running in,
243 * but at the moment this is not required.
244 *
245 * No reference counting is kept, since we don't implement any facilities
246 * for unloading the module. But the module will naturally be released
247 * when the VM terminates.
248 *
249 * @returns VBox status code.
250 * @param pUVM Pointer to the user mode VM structure.
251 * @param pszFilename Filename of the module binary.
252 * @param pszName Module name. Case sensitive and the length is limited!
253 */
254int pdmR3LoadR3U(PUVM pUVM, const char *pszFilename, const char *pszName)
255{
256 /*
257 * Validate input.
258 */
259 AssertMsg(RTCritSectIsInitialized(&pUVM->pdm.s.ListCritSect), ("bad init order!\n"));
260 Assert(pszFilename);
261 size_t cchFilename = strlen(pszFilename);
262 Assert(pszName);
263 size_t cchName = strlen(pszName);
264 PPDMMOD pCur;
265 if (cchName >= sizeof(pCur->szName))
266 {
267 AssertMsgFailed(("Name is too long, cchName=%d pszName='%s'\n", cchName, pszName));
268 return VERR_INVALID_PARAMETER;
269 }
270
271 /*
272 * Try lookup the name and see if the module exists.
273 */
274 int rc;
275 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
276 for (pCur = pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext)
277 {
278 if (!strcmp(pCur->szName, pszName))
279 {
280 if (pCur->eType == PDMMOD_TYPE_R3)
281 rc = VINF_PDM_ALREADY_LOADED;
282 else
283 rc = VERR_PDM_MODULE_NAME_CLASH;
284 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
285
286 AssertMsgRC(rc, ("We've already got a module '%s' loaded!\n", pszName));
287 return rc;
288 }
289 }
290
291 /*
292 * Allocate the module list node and initialize it.
293 */
294 const char *pszSuff = RTLdrGetSuff();
295 size_t cchSuff = RTPathHasSuffix(pszFilename) ? 0 : strlen(pszSuff);
296 PPDMMOD pModule = (PPDMMOD)RTMemAllocZ(RT_OFFSETOF(PDMMOD, szFilename[cchFilename + cchSuff + 1]));
297 if (pModule)
298 {
299 pModule->eType = PDMMOD_TYPE_R3;
300 memcpy(pModule->szName, pszName, cchName); /* memory is zero'd, no need to copy terminator :-) */
301 memcpy(pModule->szFilename, pszFilename, cchFilename);
302 memcpy(&pModule->szFilename[cchFilename], pszSuff, cchSuff);
303
304 /*
305 * Load the loader item.
306 */
307 RTERRINFOSTATIC ErrInfo;
308 RTErrInfoInitStatic(&ErrInfo);
309 rc = SUPR3HardenedLdrLoadPlugIn(pModule->szFilename, &pModule->hLdrMod, &ErrInfo.Core);
310 if (RT_SUCCESS(rc))
311 {
312 pModule->pNext = pUVM->pdm.s.pModules;
313 pUVM->pdm.s.pModules = pModule;
314 }
315 else
316 {
317 /* Something went wrong, most likely module not found. Don't consider other unlikely errors */
318 rc = VMSetError(pUVM->pVM, rc, RT_SRC_POS,
319 N_("Unable to load R3 module %s (%s): %s"), pModule->szFilename, pszName, ErrInfo.Core.pszMsg);
320 RTMemFree(pModule);
321 }
322 }
323 else
324 rc = VERR_NO_MEMORY;
325
326 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
327 return rc;
328}
329
330#ifdef VBOX_WITH_RAW_MODE
331
332/**
333 * Resolve an external symbol during RTLdrGetBits() of a RC module.
334 *
335 * @returns VBox status code.
336 * @param hLdrMod The loader module handle.
337 * @param pszModule Module name.
338 * @param pszSymbol Symbol name, NULL if uSymbol should be used.
339 * @param uSymbol Symbol ordinal, ~0 if pszSymbol should be used.
340 * @param pValue Where to store the symbol value (address).
341 * @param pvUser User argument.
342 */
343static DECLCALLBACK(int) pdmR3GetImportRC(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol,
344 RTUINTPTR *pValue, void *pvUser)
345{
346 PVM pVM = ((PPDMGETIMPORTARGS)pvUser)->pVM;
347 PPDMMOD pModule = ((PPDMGETIMPORTARGS)pvUser)->pModule;
348 NOREF(hLdrMod); NOREF(uSymbol);
349
350 /*
351 * Adjust input.
352 */
353 if (pszModule && !*pszModule)
354 pszModule = NULL;
355
356 /*
357 * Builtin module.
358 */
359 if (!pszModule || !strcmp(pszModule, "VMMRCBuiltin.rc"))
360 {
361 int rc = VINF_SUCCESS;
362 if (!strcmp(pszSymbol, "g_VM"))
363 *pValue = pVM->pVMRC;
364 else if (!strcmp(pszSymbol, "g_CPUM"))
365 *pValue = VM_RC_ADDR(pVM, &pVM->cpum);
366 else if ( !strncmp(pszSymbol, "g_TRPM", 6)
367 || !strncmp(pszSymbol, "g_trpm", 6)
368 || !strncmp(pszSymbol, "TRPM", 4))
369 {
370 RTRCPTR RCPtr = 0;
371 rc = TRPMR3GetImportRC(pVM, pszSymbol, &RCPtr);
372 if (RT_SUCCESS(rc))
373 *pValue = RCPtr;
374 }
375 else if ( !strncmp(pszSymbol, "VMM", 3)
376 || !strcmp(pszSymbol, "g_Logger")
377 || !strcmp(pszSymbol, "g_RelLogger"))
378 {
379 RTRCPTR RCPtr = 0;
380 rc = VMMR3GetImportRC(pVM, pszSymbol, &RCPtr);
381 if (RT_SUCCESS(rc))
382 *pValue = RCPtr;
383 }
384 else if ( !strncmp(pszSymbol, "TM", 2)
385 || !strcmp(pszSymbol, "g_pSUPGlobalInfoPage"))
386 {
387 RTRCPTR RCPtr = 0;
388 rc = TMR3GetImportRC(pVM, pszSymbol, &RCPtr);
389 if (RT_SUCCESS(rc))
390 *pValue = RCPtr;
391 }
392 else
393 {
394 AssertMsg(!pszModule, ("Unknown builtin symbol '%s' for module '%s'!\n", pszSymbol, pModule->szName)); NOREF(pModule);
395 rc = VERR_SYMBOL_NOT_FOUND;
396 }
397 if (RT_SUCCESS(rc) || pszModule)
398 {
399 if (RT_FAILURE(rc))
400 LogRel(("PDMLdr: Couldn't find symbol '%s' in module '%s'!\n", pszSymbol, pszModule));
401 return rc;
402 }
403 }
404
405 /*
406 * Search for module.
407 */
408 PUVM pUVM = pVM->pUVM;
409 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
410 PPDMMOD pCur = pUVM->pdm.s.pModules;
411 while (pCur)
412 {
413 if ( pCur->eType == PDMMOD_TYPE_RC
414 && ( !pszModule
415 || !strcmp(pCur->szName, pszModule))
416 )
417 {
418 /* Search for the symbol. */
419 int rc = RTLdrGetSymbolEx(pCur->hLdrMod, pCur->pvBits, pCur->ImageBase, UINT32_MAX, pszSymbol, pValue);
420 if (RT_SUCCESS(rc))
421 {
422 AssertMsg(*pValue - pCur->ImageBase < RTLdrSize(pCur->hLdrMod),
423 ("%RRv-%RRv %s %RRv\n", (RTRCPTR)pCur->ImageBase,
424 (RTRCPTR)(pCur->ImageBase + RTLdrSize(pCur->hLdrMod) - 1),
425 pszSymbol, (RTRCPTR)*pValue));
426 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
427 return rc;
428 }
429 if (pszModule)
430 {
431 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
432 AssertLogRelMsgFailed(("PDMLdr: Couldn't find symbol '%s' in module '%s'!\n", pszSymbol, pszModule));
433 return VERR_SYMBOL_NOT_FOUND;
434 }
435 }
436
437 /* next */
438 pCur = pCur->pNext;
439 }
440
441 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
442 AssertLogRelMsgFailed(("Couldn't find module '%s' for resolving symbol '%s'!\n", pszModule, pszSymbol));
443 return VERR_SYMBOL_NOT_FOUND;
444}
445
446
447/**
448 * Loads a module into the raw-mode context (i.e. into the Hypervisor memory
449 * region).
450 *
451 * @returns VBox status code.
452 * @retval VINF_PDM_ALREADY_LOADED if the module is already loaded (name +
453 * filename match).
454 * @retval VERR_PDM_MODULE_NAME_CLASH if a different file has already been
455 * loaded with the name module name.
456 *
457 * @param pVM The cross context VM structure.
458 * @param pszFilename Filename of the module binary.
459 * @param pszName Module name. Case sensitive and the length is limited!
460 */
461VMMR3DECL(int) PDMR3LdrLoadRC(PVM pVM, const char *pszFilename, const char *pszName)
462{
463 /*
464 * Validate input.
465 */
466 AssertMsg(MMR3IsInitialized(pVM), ("bad init order!\n"));
467 AssertReturn(!HMIsEnabled(pVM), VERR_PDM_HM_IPE);
468
469 /*
470 * Find the file if not specified.
471 */
472 char *pszFile = NULL;
473 if (!pszFilename)
474 pszFilename = pszFile = pdmR3FileRC(pszName, NULL);
475
476 /*
477 * Check if a module by that name is already loaded.
478 */
479 int rc;
480 PUVM pUVM = pVM->pUVM;
481 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
482 PPDMMOD pCur = pUVM->pdm.s.pModules;
483 while (pCur)
484 {
485 if (!strcmp(pCur->szName, pszName))
486 {
487 /* Name clash. Hopefully due to it being the same file. */
488 if (!strcmp(pCur->szFilename, pszFilename))
489 rc = VINF_PDM_ALREADY_LOADED;
490 else
491 {
492 rc = VERR_PDM_MODULE_NAME_CLASH;
493 AssertMsgFailed(("We've already got a module '%s' loaded!\n", pszName));
494 }
495 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
496 RTMemTmpFree(pszFile);
497 return rc;
498 }
499 /* next */
500 pCur = pCur->pNext;
501 }
502
503 /*
504 * Allocate the module list node.
505 */
506 PPDMMOD pModule = (PPDMMOD)RTMemAllocZ(sizeof(*pModule) + strlen(pszFilename));
507 if (!pModule)
508 {
509 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
510 RTMemTmpFree(pszFile);
511 return VERR_NO_MEMORY;
512 }
513 AssertMsg(strlen(pszName) + 1 < sizeof(pModule->szName),
514 ("pazName is too long (%d chars) max is %d chars.\n", strlen(pszName), sizeof(pModule->szName) - 1));
515 strcpy(pModule->szName, pszName);
516 pModule->eType = PDMMOD_TYPE_RC;
517 strcpy(pModule->szFilename, pszFilename);
518
519
520 /*
521 * Open the loader item.
522 */
523 RTERRINFOSTATIC ErrInfo;
524 RTErrInfoInitStatic(&ErrInfo);
525 rc = SUPR3HardenedVerifyPlugIn(pszFilename, &ErrInfo.Core);
526 if (RT_SUCCESS(rc))
527 {
528 RTErrInfoClear(&ErrInfo.Core);
529 rc = RTLdrOpen(pszFilename, 0, RTLDRARCH_X86_32, &pModule->hLdrMod);
530 }
531 if (RT_SUCCESS(rc))
532 {
533 /*
534 * Allocate space in the hypervisor.
535 */
536 size_t cb = RTLdrSize(pModule->hLdrMod);
537 cb = RT_ALIGN_Z(cb, PAGE_SIZE);
538 uint32_t cPages = (uint32_t)(cb >> PAGE_SHIFT);
539 if (((size_t)cPages << PAGE_SHIFT) == cb)
540 {
541 PSUPPAGE paPages = (PSUPPAGE)RTMemTmpAlloc(cPages * sizeof(paPages[0]));
542 if (paPages)
543 {
544 rc = SUPR3PageAllocEx(cPages, 0 /*fFlags*/, &pModule->pvBits, NULL /*pR0Ptr*/, paPages);
545 if (RT_SUCCESS(rc))
546 {
547 RTGCPTR GCPtr;
548 rc = MMR3HyperMapPages(pVM, pModule->pvBits, NIL_RTR0PTR,
549 cPages, paPages, pModule->szName, &GCPtr);
550 if (RT_SUCCESS(rc))
551 {
552 MMR3HyperReserve(pVM, PAGE_SIZE, "fence", NULL);
553
554 /*
555 * Get relocated image bits.
556 */
557 Assert(MMHyperR3ToRC(pVM, pModule->pvBits) == GCPtr);
558 pModule->ImageBase = GCPtr;
559 PDMGETIMPORTARGS Args;
560 Args.pVM = pVM;
561 Args.pModule = pModule;
562 rc = RTLdrGetBits(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, pdmR3GetImportRC, &Args);
563 if (RT_SUCCESS(rc))
564 {
565#ifdef VBOX_WITH_DTRACE_RC
566 /*
567 * Register the tracer bits if present.
568 */
569 RTLDRADDR uValue;
570 rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, UINT32_MAX,
571 "g_VTGObjHeader", &uValue);
572 if (RT_SUCCESS(rc))
573 {
574 PVTGOBJHDR pVtgHdr = (PVTGOBJHDR)MMHyperRCToCC(pVM, (RTRCPTR)uValue);
575 if ( pVtgHdr
576 && !memcmp(pVtgHdr->szMagic, VTGOBJHDR_MAGIC, sizeof(pVtgHdr->szMagic)))
577 rc = SUPR3TracerRegisterModule(~(uintptr_t)0, pModule->szName, pVtgHdr, uValue,
578 SUP_TRACER_UMOD_FLAGS_SHARED);
579 else
580 rc = pVtgHdr ? VERR_INVALID_MAGIC : VERR_INVALID_POINTER;
581 if (RT_FAILURE(rc))
582 LogRel(("PDMLdr: Failed to register tracepoints for '%s': %Rrc\n", pModule->szName, rc));
583 }
584#endif
585
586 /*
587 * Insert the module.
588 */
589 if (pUVM->pdm.s.pModules)
590 {
591 /* we don't expect this list to be very long, so rather save the tail pointer. */
592 pCur = pUVM->pdm.s.pModules;
593 while (pCur->pNext)
594 pCur = pCur->pNext;
595 pCur->pNext = pModule;
596 }
597 else
598 pUVM->pdm.s.pModules = pModule; /* (pNext is zeroed by alloc) */
599 Log(("PDM: RC Module at %RRv %s (%s)\n", (RTRCPTR)pModule->ImageBase, pszName, pszFilename));
600
601 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
602 RTMemTmpFree(pszFile);
603 RTMemTmpFree(paPages);
604
605 return VINF_SUCCESS;
606 }
607 }
608 else
609 {
610 AssertRC(rc);
611 SUPR3PageFreeEx(pModule->pvBits, cPages);
612 }
613 }
614 else
615 AssertMsgFailed(("SUPR3PageAlloc(%d,) -> %Rrc\n", cPages, rc));
616 RTMemTmpFree(paPages);
617 }
618 else
619 rc = VERR_NO_TMP_MEMORY;
620 }
621 else
622 rc = VERR_OUT_OF_RANGE;
623 int rc2 = RTLdrClose(pModule->hLdrMod);
624 AssertRC(rc2);
625 }
626 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
627
628 /* Don't consider VERR_PDM_MODULE_NAME_CLASH and VERR_NO_MEMORY above as these are very unlikely. */
629 if (RT_FAILURE(rc) && RTErrInfoIsSet(&ErrInfo.Core))
630 rc = VMSetError(pVM, rc, RT_SRC_POS, N_("Cannot load RC module %s: %s"), pszFilename, ErrInfo.Core.pszMsg);
631 else if (RT_FAILURE(rc))
632 rc = VMSetError(pVM, rc, RT_SRC_POS, N_("Cannot load RC module %s"), pszFilename);
633
634 RTMemFree(pModule);
635 RTMemTmpFree(pszFile);
636 return rc;
637}
638
639#endif /* VBOX_WITH_RAW_MODE */
640
641/**
642 * Loads a module into the ring-0 context.
643 *
644 * @returns VBox status code.
645 * @param pUVM Pointer to the user mode VM structure.
646 * @param pszFilename Filename of the module binary.
647 * @param pszName Module name. Case sensitive and the length is limited!
648 * @param pszSearchPath List of directories to search if @a pszFilename is
649 * not specified. Can be NULL, in which case the arch
650 * dependent install dir is searched.
651 */
652static int pdmR3LoadR0U(PUVM pUVM, const char *pszFilename, const char *pszName, const char *pszSearchPath)
653{
654 /*
655 * Validate input.
656 */
657 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
658 PPDMMOD pCur = pUVM->pdm.s.pModules;
659 while (pCur)
660 {
661 if (!strcmp(pCur->szName, pszName))
662 {
663 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
664 AssertMsgFailed(("We've already got a module '%s' loaded!\n", pszName));
665 return VERR_PDM_MODULE_NAME_CLASH;
666 }
667 /* next */
668 pCur = pCur->pNext;
669 }
670
671 /*
672 * Find the file if not specified.
673 */
674 char *pszFile = NULL;
675 if (!pszFilename)
676 pszFilename = pszFile = pdmR3FileR0(pszName, pszSearchPath);
677
678 /*
679 * Allocate the module list node.
680 */
681 PPDMMOD pModule = (PPDMMOD)RTMemAllocZ(sizeof(*pModule) + strlen(pszFilename));
682 if (!pModule)
683 {
684 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
685 RTMemTmpFree(pszFile);
686 return VERR_NO_MEMORY;
687 }
688 AssertMsg(strlen(pszName) + 1 < sizeof(pModule->szName),
689 ("pazName is too long (%d chars) max is %d chars.\n", strlen(pszName), sizeof(pModule->szName) - 1));
690 strcpy(pModule->szName, pszName);
691 pModule->eType = PDMMOD_TYPE_R0;
692 strcpy(pModule->szFilename, pszFilename);
693
694 /*
695 * Ask the support library to load it.
696 */
697 void *pvImageBase;
698 RTERRINFOSTATIC ErrInfo;
699 RTErrInfoInitStatic(&ErrInfo);
700 int rc = SUPR3LoadModule(pszFilename, pszName, &pvImageBase, &ErrInfo.Core);
701 if (RT_SUCCESS(rc))
702 {
703 pModule->hLdrMod = NIL_RTLDRMOD;
704 pModule->ImageBase = (uintptr_t)pvImageBase;
705
706 /*
707 * Insert the module.
708 */
709 if (pUVM->pdm.s.pModules)
710 {
711 /* we don't expect this list to be very long, so rather save the tail pointer. */
712 pCur = pUVM->pdm.s.pModules;
713 while (pCur->pNext)
714 pCur = pCur->pNext;
715 pCur->pNext = pModule;
716 }
717 else
718 pUVM->pdm.s.pModules = pModule; /* (pNext is zeroed by alloc) */
719 Log(("PDM: R0 Module at %RHv %s (%s)\n", (RTR0PTR)pModule->ImageBase, pszName, pszFilename));
720 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
721 RTMemTmpFree(pszFile);
722 return VINF_SUCCESS;
723 }
724
725 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
726 RTMemFree(pModule);
727 LogRel(("PDMLdr: pdmR3LoadR0U: pszName=\"%s\" rc=%Rrc szErr=\"%s\"\n", pszName, rc, ErrInfo.Core.pszMsg));
728
729 /* Don't consider VERR_PDM_MODULE_NAME_CLASH and VERR_NO_MEMORY above as these are very unlikely. */
730 if (RT_FAILURE(rc))
731 rc = VMR3SetError(pUVM, rc, RT_SRC_POS, N_("Failed to load R0 module %s: %s"), pszFilename, ErrInfo.Core.pszMsg);
732
733 RTMemTmpFree(pszFile); /* might be reference thru pszFilename in the above VMSetError call. */
734 return rc;
735}
736
737
738
739/**
740 * Get the address of a symbol in a given HC ring 3 module.
741 *
742 * @returns VBox status code.
743 * @param pVM The cross context VM structure.
744 * @param pszModule Module name.
745 * @param pszSymbol Symbol name. If it's value is less than 64k it's treated like a
746 * ordinal value rather than a string pointer.
747 * @param ppvValue Where to store the symbol value.
748 */
749VMMR3_INT_DECL(int) PDMR3LdrGetSymbolR3(PVM pVM, const char *pszModule, const char *pszSymbol, void **ppvValue)
750{
751 /*
752 * Validate input.
753 */
754 AssertPtr(pVM);
755 AssertPtr(pszModule);
756 AssertPtr(ppvValue);
757 PUVM pUVM = pVM->pUVM;
758 AssertMsg(RTCritSectIsInitialized(&pUVM->pdm.s.ListCritSect), ("bad init order!\n"));
759
760 /*
761 * Find the module.
762 */
763 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
764 for (PPDMMOD pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext)
765 {
766 if ( pModule->eType == PDMMOD_TYPE_R3
767 && !strcmp(pModule->szName, pszModule))
768 {
769 RTUINTPTR Value = 0;
770 int rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, UINT32_MAX, pszSymbol, &Value);
771 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
772 if (RT_SUCCESS(rc))
773 {
774 *ppvValue = (void *)(uintptr_t)Value;
775 Assert((uintptr_t)*ppvValue == Value);
776 }
777 else
778 {
779 if ((uintptr_t)pszSymbol < 0x10000)
780 AssertMsg(rc, ("Couldn't symbol '%u' in module '%s'\n", (unsigned)(uintptr_t)pszSymbol, pszModule));
781 else
782 AssertMsg(rc, ("Couldn't symbol '%s' in module '%s'\n", pszSymbol, pszModule));
783 }
784 return rc;
785 }
786 }
787 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
788 AssertMsgFailed(("Couldn't locate module '%s'\n", pszModule));
789 return VERR_SYMBOL_NOT_FOUND;
790}
791
792
793/**
794 * Get the address of a symbol in a given HC ring 0 module.
795 *
796 * @returns VBox status code.
797 * @param pVM The cross context VM structure.
798 * @param pszModule Module name. If NULL the main R0 module (VMMR0.r0) is assumes.
799 * @param pszSymbol Symbol name. If it's value is less than 64k it's treated like a
800 * ordinal value rather than a string pointer.
801 * @param ppvValue Where to store the symbol value.
802 */
803VMMR3DECL(int) PDMR3LdrGetSymbolR0(PVM pVM, const char *pszModule, const char *pszSymbol, PRTR0PTR ppvValue)
804{
805#ifdef PDMLDR_FAKE_MODE
806 *ppvValue = 0xdeadbeef;
807 return VINF_SUCCESS;
808
809#else
810 /*
811 * Validate input.
812 */
813 AssertPtr(pVM);
814 AssertPtrNull(pszModule);
815 AssertPtr(ppvValue);
816 PUVM pUVM = pVM->pUVM;
817 AssertMsg(RTCritSectIsInitialized(&pUVM->pdm.s.ListCritSect), ("bad init order!\n"));
818
819 if (!pszModule)
820 pszModule = VMMR0_MAIN_MODULE_NAME;
821
822 /*
823 * Find the module.
824 */
825 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
826 for (PPDMMOD pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext)
827 {
828 if ( pModule->eType == PDMMOD_TYPE_R0
829 && !strcmp(pModule->szName, pszModule))
830 {
831 int rc = SUPR3GetSymbolR0((void *)(uintptr_t)pModule->ImageBase, pszSymbol, (void **)ppvValue);
832 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
833 if (RT_FAILURE(rc))
834 {
835 AssertMsgRC(rc, ("Couldn't find symbol '%s' in module '%s'\n", pszSymbol, pszModule));
836 LogRel(("PDMLdr: PDMGetSymbol: Couldn't find symbol '%s' in module '%s'\n", pszSymbol, pszModule));
837 }
838 return rc;
839 }
840 }
841 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
842 AssertMsgFailed(("Couldn't locate module '%s'\n", pszModule));
843 return VERR_SYMBOL_NOT_FOUND;
844#endif
845}
846
847
848/**
849 * Same as PDMR3LdrGetSymbolR0 except that the module will be attempted loaded if not found.
850 *
851 * @returns VBox status code.
852 * @param pVM The cross context VM structure.
853 * @param pszModule Module name. If NULL the main R0 module (VMMR0.r0) is assumed.
854 * @param pszSearchPath List of directories to search if @a pszFile is
855 * not qualified with a path. Can be NULL, in which
856 * case the arch dependent install dir is searched.
857 * @param pszSymbol Symbol name. If it's value is less than 64k it's treated like a
858 * ordinal value rather than a string pointer.
859 * @param ppvValue Where to store the symbol value.
860 */
861VMMR3DECL(int) PDMR3LdrGetSymbolR0Lazy(PVM pVM, const char *pszModule, const char *pszSearchPath, const char *pszSymbol,
862 PRTR0PTR ppvValue)
863{
864#ifdef PDMLDR_FAKE_MODE
865 *ppvValue = 0xdeadbeef;
866 return VINF_SUCCESS;
867
868#else
869 AssertPtr(pVM);
870 AssertPtrNull(pszModule);
871 AssertPtr(ppvValue);
872 PUVM pUVM = pVM->pUVM;
873 AssertMsg(RTCritSectIsInitialized(&pUVM->pdm.s.ListCritSect), ("bad init order!\n"));
874
875 if (pszModule) /* (We don't lazy load the main R0 module.) */
876 {
877 /*
878 * Since we're lazy, we'll only check if the module is present
879 * and hand it over to PDMR3LdrGetSymbolR0 when that's done.
880 */
881 AssertMsgReturn(!strpbrk(pszModule, "/\\:\n\r\t"), ("pszModule=%s\n", pszModule), VERR_INVALID_PARAMETER);
882 PPDMMOD pModule;
883 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
884 for (pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext)
885 if ( pModule->eType == PDMMOD_TYPE_R0
886 && !strcmp(pModule->szName, pszModule))
887 break;
888 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
889 if (!pModule)
890 {
891 int rc = pdmR3LoadR0U(pUVM, NULL, pszModule, pszSearchPath);
892 AssertMsgRCReturn(rc, ("pszModule=%s rc=%Rrc\n", pszModule, rc), VERR_MODULE_NOT_FOUND);
893 }
894 }
895
896 return PDMR3LdrGetSymbolR0(pVM, pszModule, pszSymbol, ppvValue);
897#endif
898}
899
900
901/**
902 * Get the address of a symbol in a given RC module.
903 *
904 * @returns VBox status code.
905 * @param pVM The cross context VM structure.
906 * @param pszModule Module name. If NULL the main R0 module (VMMRC.rc)
907 * is assumes.
908 * @param pszSymbol Symbol name. If it's value is less than 64k it's
909 * treated like a ordinal value rather than a string
910 * pointer.
911 * @param pRCPtrValue Where to store the symbol value.
912 */
913VMMR3DECL(int) PDMR3LdrGetSymbolRC(PVM pVM, const char *pszModule, const char *pszSymbol, PRTRCPTR pRCPtrValue)
914{
915#if defined(PDMLDR_FAKE_MODE) || !defined(VBOX_WITH_RAW_MODE)
916 RT_NOREF(pVM, pszModule, pszSymbol);
917 Assert(!HMIsEnabled(pVM));
918 *pRCPtrValue = NIL_RTRCPTR;
919 return VINF_SUCCESS;
920
921#else
922 /*
923 * Validate input.
924 */
925 AssertPtr(pVM);
926 AssertPtrNull(pszModule);
927 AssertPtr(pRCPtrValue);
928 AssertMsg(MMR3IsInitialized(pVM), ("bad init order!\n"));
929
930 if (!pszModule)
931 pszModule = VMMRC_MAIN_MODULE_NAME;
932
933 /*
934 * Find the module.
935 */
936 PUVM pUVM = pVM->pUVM;
937 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
938 for (PPDMMOD pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext)
939 {
940 if ( pModule->eType == PDMMOD_TYPE_RC
941 && !strcmp(pModule->szName, pszModule))
942 {
943 RTUINTPTR Value;
944 int rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, UINT32_MAX, pszSymbol, &Value);
945 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
946 if (RT_SUCCESS(rc))
947 {
948 *pRCPtrValue = (RTGCPTR)Value;
949 Assert(*pRCPtrValue == Value);
950 }
951 else
952 {
953 if ((uintptr_t)pszSymbol < 0x10000)
954 AssertMsg(rc, ("Couldn't symbol '%u' in module '%s'\n", (unsigned)(uintptr_t)pszSymbol, pszModule));
955 else
956 AssertMsg(rc, ("Couldn't symbol '%s' in module '%s'\n", pszSymbol, pszModule));
957 }
958 return rc;
959 }
960 }
961 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
962 AssertMsgFailed(("Couldn't locate module '%s'\n", pszModule));
963 return VERR_SYMBOL_NOT_FOUND;
964#endif
965}
966
967
968/**
969 * Same as PDMR3LdrGetSymbolRC except that the module will be attempted loaded if not found.
970 *
971 * @returns VBox status code.
972 * @param pVM The cross context VM structure.
973 * @param pszModule Module name. If NULL the main RC module (VMMRC.rc)
974 * is assumed.
975 * @param pszSearchPath List of directories to search if @a pszFile is
976 * not qualified with a path. Can be NULL, in which
977 * case the arch dependent install dir is searched.
978 * @param pszSymbol Symbol name. If it's value is less than 64k it's treated like a
979 * ordinal value rather than a string pointer.
980 * @param pRCPtrValue Where to store the symbol value.
981 */
982VMMR3DECL(int) PDMR3LdrGetSymbolRCLazy(PVM pVM, const char *pszModule, const char *pszSearchPath, const char *pszSymbol,
983 PRTRCPTR pRCPtrValue)
984{
985#if defined(PDMLDR_FAKE_MODE) || !defined(VBOX_WITH_RAW_MODE)
986 RT_NOREF(pVM, pszModule, pszSearchPath, pszSymbol);
987 Assert(!HMIsEnabled(pVM));
988 *pRCPtrValue = NIL_RTRCPTR;
989 return VINF_SUCCESS;
990
991#else
992 AssertPtr(pVM);
993 if (!pszModule)
994 pszModule = VMMRC_MAIN_MODULE_NAME;
995 AssertPtr(pszModule);
996 AssertPtr(pRCPtrValue);
997 AssertMsg(MMR3IsInitialized(pVM), ("bad init order!\n"));
998
999 /*
1000 * Since we're lazy, we'll only check if the module is present
1001 * and hand it over to PDMR3LdrGetSymbolRC when that's done.
1002 */
1003 AssertMsgReturn(!strpbrk(pszModule, "/\\:\n\r\t"), ("pszModule=%s\n", pszModule), VERR_INVALID_PARAMETER);
1004 PUVM pUVM = pVM->pUVM;
1005 PPDMMOD pModule;
1006 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
1007 for (pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext)
1008 if ( pModule->eType == PDMMOD_TYPE_RC
1009 && !strcmp(pModule->szName, pszModule))
1010 break;
1011 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1012 if (!pModule)
1013 {
1014 char *pszFilename = pdmR3FileRC(pszModule, pszSearchPath);
1015 AssertMsgReturn(pszFilename, ("pszModule=%s\n", pszModule), VERR_MODULE_NOT_FOUND);
1016 int rc = PDMR3LdrLoadRC(pVM, pszFilename, pszModule);
1017 RTMemTmpFree(pszFilename);
1018 AssertMsgRCReturn(rc, ("pszModule=%s rc=%Rrc\n", pszModule, rc), VERR_MODULE_NOT_FOUND);
1019 }
1020
1021 return PDMR3LdrGetSymbolRC(pVM, pszModule, pszSymbol, pRCPtrValue);
1022#endif
1023}
1024
1025
1026/**
1027 * Constructs the full filename for a R3 image file.
1028 *
1029 * @returns Pointer to temporary memory containing the filename.
1030 * Caller must free this using RTMemTmpFree().
1031 * @returns NULL on failure.
1032 *
1033 * @param pszFile File name (no path).
1034 * @param fShared If true, search in the shared directory (/usr/lib on Unix), else
1035 * search in the private directory (/usr/lib/virtualbox on Unix).
1036 * Ignored if VBOX_PATH_SHARED_LIBS is not defined.
1037 */
1038char *pdmR3FileR3(const char *pszFile, bool fShared)
1039{
1040 return pdmR3File(pszFile, NULL, NULL, fShared);
1041}
1042
1043
1044/**
1045 * Constructs the full filename for a R0 image file.
1046 *
1047 * @returns Pointer to temporary memory containing the filename.
1048 * Caller must free this using RTMemTmpFree().
1049 * @returns NULL on failure.
1050 *
1051 * @param pszFile File name (no path).
1052 * @param pszSearchPath List of directories to search if @a pszFile is
1053 * not qualified with a path. Can be NULL, in which
1054 * case the arch dependent install dir is searched.
1055 */
1056char *pdmR3FileR0(const char *pszFile, const char *pszSearchPath)
1057{
1058 return pdmR3File(pszFile, NULL, pszSearchPath, /*fShared=*/false);
1059}
1060
1061
1062/**
1063 * Constructs the full filename for a RC image file.
1064 *
1065 * @returns Pointer to temporary memory containing the filename.
1066 * Caller must free this using RTMemTmpFree().
1067 * @returns NULL on failure.
1068 *
1069 * @param pszFile File name (no path).
1070 * @param pszSearchPath List of directories to search if @a pszFile is
1071 * not qualified with a path. Can be NULL, in which
1072 * case the arch dependent install dir is searched.
1073 */
1074char *pdmR3FileRC(const char *pszFile, const char *pszSearchPath)
1075{
1076 return pdmR3File(pszFile, NULL, pszSearchPath, /*fShared=*/false);
1077}
1078
1079
1080/**
1081 * Worker for pdmR3File().
1082 *
1083 * @returns Pointer to temporary memory containing the filename.
1084 * Caller must free this using RTMemTmpFree().
1085 * @returns NULL on failure.
1086 *
1087 * @param pszDir Directory part
1088 * @param pszFile File name part
1089 * @param pszDefaultExt Extension part
1090 */
1091static char *pdmR3FileConstruct(const char *pszDir, const char *pszFile, const char *pszDefaultExt)
1092{
1093 /*
1094 * Allocate temp memory for return buffer.
1095 */
1096 size_t cchDir = strlen(pszDir);
1097 size_t cchFile = strlen(pszFile);
1098 size_t cchDefaultExt;
1099
1100 /*
1101 * Default extention?
1102 */
1103 if (!pszDefaultExt || strchr(pszFile, '.'))
1104 cchDefaultExt = 0;
1105 else
1106 cchDefaultExt = strlen(pszDefaultExt);
1107
1108 size_t cchPath = cchDir + 1 + cchFile + cchDefaultExt + 1;
1109 AssertMsgReturn(cchPath <= RTPATH_MAX, ("Path too long!\n"), NULL);
1110
1111 char *pszRet = (char *)RTMemTmpAlloc(cchDir + 1 + cchFile + cchDefaultExt + 1);
1112 AssertMsgReturn(pszRet, ("Out of temporary memory!\n"), NULL);
1113
1114 /*
1115 * Construct the filename.
1116 */
1117 memcpy(pszRet, pszDir, cchDir);
1118 pszRet[cchDir++] = '/'; /* this works everywhere */
1119 memcpy(pszRet + cchDir, pszFile, cchFile + 1);
1120 if (cchDefaultExt)
1121 memcpy(pszRet + cchDir + cchFile, pszDefaultExt, cchDefaultExt + 1);
1122
1123 return pszRet;
1124}
1125
1126
1127/**
1128 * Worker for pdmR3FileRC(), pdmR3FileR0() and pdmR3FileR3().
1129 *
1130 * @returns Pointer to temporary memory containing the filename.
1131 * Caller must free this using RTMemTmpFree().
1132 * @returns NULL on failure.
1133 * @param pszFile File name (no path).
1134 * @param pszDefaultExt The default extention, NULL if none.
1135 * @param pszSearchPath List of directories to search if @a pszFile is
1136 * not qualified with a path. Can be NULL, in which
1137 * case the arch dependent install dir is searched.
1138 * @param fShared If true, search in the shared directory (/usr/lib on Unix), else
1139 * search in the private directory (/usr/lib/virtualbox on Unix).
1140 * Ignored if VBOX_PATH_SHARED_LIBS is not defined.
1141 * @todo We'll have this elsewhere than in the root later!
1142 * @todo Remove the fShared hack again once we don't need to link against VBoxDD anymore!
1143 */
1144static char *pdmR3File(const char *pszFile, const char *pszDefaultExt, const char *pszSearchPath, bool fShared)
1145{
1146 char szPath[RTPATH_MAX];
1147 int rc;
1148
1149 AssertLogRelReturn(!fShared || !pszSearchPath, NULL);
1150 Assert(!RTPathHavePath(pszFile));
1151
1152 /*
1153 * If there is a path, search it.
1154 */
1155 if ( pszSearchPath
1156 && *pszSearchPath)
1157 {
1158 /* Check the filename length. */
1159 size_t const cchFile = strlen(pszFile);
1160 if (cchFile >= sizeof(szPath))
1161 return NULL;
1162
1163 /*
1164 * Walk the search path.
1165 */
1166 const char *psz = pszSearchPath;
1167 while (*psz)
1168 {
1169 /* Skip leading blanks - no directories with leading spaces, thank you. */
1170 while (RT_C_IS_BLANK(*psz))
1171 psz++;
1172
1173 /* Find the end of this element. */
1174 const char *pszNext;
1175 const char *pszEnd = strchr(psz, ';');
1176 if (!pszEnd)
1177 pszEnd = pszNext = strchr(psz, '\0');
1178 else
1179 pszNext = pszEnd + 1;
1180 if (pszEnd != psz)
1181 {
1182 rc = RTPathJoinEx(szPath, sizeof(szPath), psz, pszEnd - psz, pszFile, cchFile);
1183 if (RT_SUCCESS(rc))
1184 {
1185 if (RTFileExists(szPath))
1186 {
1187 size_t cchPath = strlen(szPath) + 1;
1188 char *pszRet = (char *)RTMemTmpAlloc(cchPath);
1189 if (pszRet)
1190 memcpy(pszRet, szPath, cchPath);
1191 return pszRet;
1192 }
1193 }
1194 }
1195
1196 /* advance */
1197 psz = pszNext;
1198 }
1199 }
1200
1201 /*
1202 * Use the default location.
1203 */
1204 rc = fShared
1205 ? RTPathSharedLibs( szPath, sizeof(szPath))
1206 : RTPathAppPrivateArch(szPath, sizeof(szPath));
1207 if (!RT_SUCCESS(rc))
1208 {
1209 AssertMsgFailed(("RTPath[SharedLibs|AppPrivateArch](,%d) failed rc=%d!\n", sizeof(szPath), rc));
1210 return NULL;
1211 }
1212
1213 return pdmR3FileConstruct(szPath, pszFile, pszDefaultExt);
1214}
1215
1216
1217/** @internal */
1218typedef struct QMFEIPARG
1219{
1220 RTINTPTR uPC;
1221
1222 char *pszNearSym1;
1223 size_t cchNearSym1;
1224 RTINTPTR offNearSym1;
1225
1226 char *pszNearSym2;
1227 size_t cchNearSym2;
1228 RTINTPTR offNearSym2;
1229} QMFEIPARG, *PQMFEIPARG;
1230
1231
1232/**
1233 * Enumeration callback function used by RTLdrEnumSymbols().
1234 *
1235 * @returns VBox status code. Failure will stop the enumeration.
1236 * @param hLdrMod The loader module handle.
1237 * @param pszSymbol Symbol name. NULL if ordinal only.
1238 * @param uSymbol Symbol ordinal, ~0 if not used.
1239 * @param Value Symbol value.
1240 * @param pvUser The user argument specified to RTLdrEnumSymbols().
1241 */
1242static DECLCALLBACK(int) pdmR3QueryModFromEIPEnumSymbols(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol,
1243 RTUINTPTR Value, void *pvUser)
1244{
1245 PQMFEIPARG pArgs = (PQMFEIPARG)pvUser;
1246 NOREF(hLdrMod);
1247
1248 RTINTPTR off = Value - pArgs->uPC;
1249 if (off <= 0) /* near1 is before or at same location. */
1250 {
1251 if (off > pArgs->offNearSym1)
1252 {
1253 pArgs->offNearSym1 = off;
1254 if (pArgs->pszNearSym1 && pArgs->cchNearSym1)
1255 {
1256 *pArgs->pszNearSym1 = '\0';
1257 if (pszSymbol)
1258 strncat(pArgs->pszNearSym1, pszSymbol, pArgs->cchNearSym1);
1259 else
1260 {
1261 char szOrd[32];
1262 RTStrPrintf(szOrd, sizeof(szOrd), "#%#x", uSymbol);
1263 strncat(pArgs->pszNearSym1, szOrd, pArgs->cchNearSym1);
1264 }
1265 }
1266 }
1267 }
1268 else /* near2 is after */
1269 {
1270 if (off < pArgs->offNearSym2)
1271 {
1272 pArgs->offNearSym2 = off;
1273 if (pArgs->pszNearSym2 && pArgs->cchNearSym2)
1274 {
1275 *pArgs->pszNearSym2 = '\0';
1276 if (pszSymbol)
1277 strncat(pArgs->pszNearSym2, pszSymbol, pArgs->cchNearSym2);
1278 else
1279 {
1280 char szOrd[32];
1281 RTStrPrintf(szOrd, sizeof(szOrd), "#%#x", uSymbol);
1282 strncat(pArgs->pszNearSym2, szOrd, pArgs->cchNearSym2);
1283 }
1284 }
1285 }
1286 }
1287
1288 return VINF_SUCCESS;
1289}
1290
1291
1292/**
1293 * Internal worker for PDMR3LdrQueryRCModFromPC and PDMR3LdrQueryR0ModFromPC.
1294 *
1295 * @returns VBox status code.
1296 *
1297 * @param pVM The cross context VM structure.
1298 * @param uPC The program counter (eip/rip) to locate the module for.
1299 * @param enmType The module type.
1300 * @param pszModName Where to store the module name.
1301 * @param cchModName Size of the module name buffer.
1302 * @param pMod Base address of the module.
1303 * @param pszNearSym1 Name of the closes symbol from below.
1304 * @param cchNearSym1 Size of the buffer pointed to by pszNearSym1.
1305 * @param pNearSym1 The address of pszNearSym1.
1306 * @param pszNearSym2 Name of the closes symbol from below.
1307 * @param cchNearSym2 Size of the buffer pointed to by pszNearSym2.
1308 * @param pNearSym2 The address of pszNearSym2.
1309 */
1310static int pdmR3LdrQueryModFromPC(PVM pVM, RTUINTPTR uPC, PDMMODTYPE enmType,
1311 char *pszModName, size_t cchModName, PRTUINTPTR pMod,
1312 char *pszNearSym1, size_t cchNearSym1, PRTUINTPTR pNearSym1,
1313 char *pszNearSym2, size_t cchNearSym2, PRTUINTPTR pNearSym2)
1314{
1315 PUVM pUVM = pVM->pUVM;
1316 int rc = VERR_MODULE_NOT_FOUND;
1317 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
1318 for (PPDMMOD pCur= pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext)
1319 {
1320 if (pCur->eType != enmType)
1321 continue;
1322
1323 /* The following RTLdrOpen call is a dirty hack to get ring-0 module information. */
1324 RTLDRMOD hLdrMod = pCur->hLdrMod;
1325 if (hLdrMod == NIL_RTLDRMOD && uPC >= pCur->ImageBase)
1326 {
1327 int rc2 = RTLdrOpen(pCur->szFilename, 0 /*fFlags*/, RTLDRARCH_HOST, &hLdrMod);
1328 if (RT_FAILURE(rc2))
1329 hLdrMod = NIL_RTLDRMOD;
1330 }
1331
1332 if ( hLdrMod != NIL_RTLDRMOD
1333 && uPC - pCur->ImageBase < RTLdrSize(hLdrMod))
1334 {
1335 if (pMod)
1336 *pMod = pCur->ImageBase;
1337 if (pszModName && cchModName)
1338 {
1339 *pszModName = '\0';
1340 strncat(pszModName, pCur->szName, cchModName);
1341 }
1342 if (pNearSym1) *pNearSym1 = 0;
1343 if (pNearSym2) *pNearSym2 = 0;
1344 if (pszNearSym1) *pszNearSym1 = '\0';
1345 if (pszNearSym2) *pszNearSym2 = '\0';
1346
1347 /*
1348 * Locate the nearest symbols.
1349 */
1350 QMFEIPARG Args;
1351 Args.uPC = uPC;
1352 Args.pszNearSym1 = pszNearSym1;
1353 Args.cchNearSym1 = cchNearSym1;
1354 Args.offNearSym1 = RTINTPTR_MIN;
1355 Args.pszNearSym2 = pszNearSym2;
1356 Args.cchNearSym2 = cchNearSym2;
1357 Args.offNearSym2 = RTINTPTR_MAX;
1358
1359 rc = RTLdrEnumSymbols(hLdrMod, RTLDR_ENUM_SYMBOL_FLAGS_ALL, pCur->pvBits, pCur->ImageBase,
1360 pdmR3QueryModFromEIPEnumSymbols, &Args);
1361 if (pNearSym1 && Args.offNearSym1 != RTINTPTR_MIN)
1362 *pNearSym1 = Args.offNearSym1 + uPC;
1363 if (pNearSym2 && Args.offNearSym2 != RTINTPTR_MAX)
1364 *pNearSym2 = Args.offNearSym2 + uPC;
1365
1366 rc = VINF_SUCCESS;
1367 }
1368
1369 if (hLdrMod != pCur->hLdrMod && hLdrMod != NIL_RTLDRMOD)
1370 RTLdrClose(hLdrMod);
1371
1372 if (RT_SUCCESS(rc))
1373 break;
1374 }
1375 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1376 return rc;
1377}
1378
1379
1380/**
1381 * Queries raw-mode context module information from an PC (eip/rip).
1382 *
1383 * This is typically used to locate a crash address.
1384 *
1385 * @returns VBox status code.
1386 *
1387 * @param pVM The cross context VM structure.
1388 * @param uPC The program counter (eip/rip) to locate the module for.
1389 * @param pszModName Where to store the module name.
1390 * @param cchModName Size of the module name buffer.
1391 * @param pMod Base address of the module.
1392 * @param pszNearSym1 Name of the closes symbol from below.
1393 * @param cchNearSym1 Size of the buffer pointed to by pszNearSym1.
1394 * @param pNearSym1 The address of pszNearSym1.
1395 * @param pszNearSym2 Name of the closes symbol from below.
1396 * @param cchNearSym2 Size of the buffer pointed to by pszNearSym2.
1397 * @param pNearSym2 The address of pszNearSym2.
1398 */
1399VMMR3_INT_DECL(int) PDMR3LdrQueryRCModFromPC(PVM pVM, RTRCPTR uPC,
1400 char *pszModName, size_t cchModName, PRTRCPTR pMod,
1401 char *pszNearSym1, size_t cchNearSym1, PRTRCPTR pNearSym1,
1402 char *pszNearSym2, size_t cchNearSym2, PRTRCPTR pNearSym2)
1403{
1404 RTUINTPTR AddrMod = 0;
1405 RTUINTPTR AddrNear1 = 0;
1406 RTUINTPTR AddrNear2 = 0;
1407 int rc = pdmR3LdrQueryModFromPC(pVM, uPC, PDMMOD_TYPE_RC,
1408 pszModName, cchModName, &AddrMod,
1409 pszNearSym1, cchNearSym1, &AddrNear1,
1410 pszNearSym2, cchNearSym2, &AddrNear2);
1411 if (RT_SUCCESS(rc))
1412 {
1413 if (pMod)
1414 *pMod = (RTRCPTR)AddrMod;
1415 if (pNearSym1)
1416 *pNearSym1 = (RTRCPTR)AddrNear1;
1417 if (pNearSym2)
1418 *pNearSym2 = (RTRCPTR)AddrNear2;
1419 }
1420 return rc;
1421}
1422
1423
1424/**
1425 * Queries ring-0 context module information from an PC (eip/rip).
1426 *
1427 * This is typically used to locate a crash address.
1428 *
1429 * @returns VBox status code.
1430 *
1431 * @param pVM The cross context VM structure.
1432 * @param uPC The program counter (eip/rip) to locate the module for.
1433 * @param pszModName Where to store the module name.
1434 * @param cchModName Size of the module name buffer.
1435 * @param pMod Base address of the module.
1436 * @param pszNearSym1 Name of the closes symbol from below.
1437 * @param cchNearSym1 Size of the buffer pointed to by pszNearSym1.
1438 * @param pNearSym1 The address of pszNearSym1.
1439 * @param pszNearSym2 Name of the closes symbol from below.
1440 * @param cchNearSym2 Size of the buffer pointed to by pszNearSym2. Optional.
1441 * @param pNearSym2 The address of pszNearSym2. Optional.
1442 */
1443VMMR3_INT_DECL(int) PDMR3LdrQueryR0ModFromPC(PVM pVM, RTR0PTR uPC,
1444 char *pszModName, size_t cchModName, PRTR0PTR pMod,
1445 char *pszNearSym1, size_t cchNearSym1, PRTR0PTR pNearSym1,
1446 char *pszNearSym2, size_t cchNearSym2, PRTR0PTR pNearSym2)
1447{
1448 RTUINTPTR AddrMod = 0;
1449 RTUINTPTR AddrNear1 = 0;
1450 RTUINTPTR AddrNear2 = 0;
1451 int rc = pdmR3LdrQueryModFromPC(pVM, uPC, PDMMOD_TYPE_R0,
1452 pszModName, cchModName, &AddrMod,
1453 pszNearSym1, cchNearSym1, &AddrNear1,
1454 pszNearSym2, cchNearSym2, &AddrNear2);
1455 if (RT_SUCCESS(rc))
1456 {
1457 if (pMod)
1458 *pMod = (RTR0PTR)AddrMod;
1459 if (pNearSym1)
1460 *pNearSym1 = (RTR0PTR)AddrNear1;
1461 if (pNearSym2)
1462 *pNearSym2 = (RTR0PTR)AddrNear2;
1463 }
1464 return rc;
1465}
1466
1467
1468/**
1469 * Enumerate all PDM modules.
1470 *
1471 * @returns VBox status code.
1472 * @param pVM The cross context VM structure.
1473 * @param pfnCallback Function to call back for each of the modules.
1474 * @param pvArg User argument.
1475 */
1476VMMR3DECL(int) PDMR3LdrEnumModules(PVM pVM, PFNPDMR3ENUM pfnCallback, void *pvArg)
1477{
1478 PUVM pUVM = pVM->pUVM;
1479 int rc = VINF_SUCCESS;
1480 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
1481 for (PPDMMOD pCur = pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext)
1482 {
1483 rc = pfnCallback(pVM,
1484 pCur->szFilename,
1485 pCur->szName,
1486 pCur->ImageBase,
1487 pCur->eType == PDMMOD_TYPE_RC ? RTLdrSize(pCur->hLdrMod) : 0,
1488 pCur->eType == PDMMOD_TYPE_RC ? PDMLDRCTX_RAW_MODE
1489 : pCur->eType == PDMMOD_TYPE_R0 ? PDMLDRCTX_RING_0
1490 : pCur->eType == PDMMOD_TYPE_R3 ? PDMLDRCTX_RING_3
1491 : PDMLDRCTX_INVALID,
1492 pvArg);
1493 if (RT_FAILURE(rc))
1494 break;
1495 }
1496 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1497 return rc;
1498}
1499
1500
1501/**
1502 * Locates a module.
1503 *
1504 * @returns Pointer to the module if found.
1505 * @param pUVM Pointer to the user mode VM structure.
1506 * @param pszModule The module name.
1507 * @param enmType The module type.
1508 * @param fLazy Lazy loading the module if set.
1509 * @param pszSearchPath Search path for use when lazy loading.
1510 */
1511static PPDMMOD pdmR3LdrFindModule(PUVM pUVM, const char *pszModule, PDMMODTYPE enmType,
1512 bool fLazy, const char *pszSearchPath)
1513{
1514 RTCritSectEnter(&pUVM->pdm.s.ListCritSect);
1515 for (PPDMMOD pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext)
1516 if ( pModule->eType == enmType
1517 && !strcmp(pModule->szName, pszModule))
1518 {
1519 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1520 return pModule;
1521 }
1522 RTCritSectLeave(&pUVM->pdm.s.ListCritSect);
1523 if (fLazy)
1524 {
1525 switch (enmType)
1526 {
1527#ifdef VBOX_WITH_RAW_MODE
1528 case PDMMOD_TYPE_RC:
1529 {
1530 char *pszFilename = pdmR3FileRC(pszModule, pszSearchPath);
1531 if (pszFilename)
1532 {
1533 int rc = PDMR3LdrLoadRC(pUVM->pVM, pszFilename, pszModule);
1534 RTMemTmpFree(pszFilename);
1535 if (RT_SUCCESS(rc))
1536 return pdmR3LdrFindModule(pUVM, pszModule, enmType, false, NULL);
1537 }
1538 break;
1539 }
1540#endif
1541
1542 case PDMMOD_TYPE_R0:
1543 {
1544 int rc = pdmR3LoadR0U(pUVM, NULL, pszModule, pszSearchPath);
1545 if (RT_SUCCESS(rc))
1546 return pdmR3LdrFindModule(pUVM, pszModule, enmType, false, NULL);
1547 break;
1548 }
1549
1550 default:
1551 AssertFailed();
1552 }
1553 }
1554 return NULL;
1555}
1556
1557
1558/**
1559 * Resolves a ring-0 or raw-mode context interface.
1560 *
1561 * @returns VBox status code.
1562 * @param pVM The cross context VM structure.
1563 * @param pvInterface Pointer to the interface structure. The symbol list
1564 * describes the layout.
1565 * @param cbInterface The size of the structure pvInterface is pointing
1566 * to. For bounds checking.
1567 * @param pszModule The module name. If NULL we assume it's the default
1568 * R0 or RC module (@a fRing0OrRC). We'll attempt to
1569 * load the module if it isn't found in the module
1570 * list.
1571 * @param pszSearchPath The module search path. If NULL, search the
1572 * architecture dependent install directory.
1573 * @param pszSymPrefix What to prefix the symbols in the list with. The
1574 * idea is that you define a list that goes with an
1575 * interface (INTERFACE_SYM_LIST) and reuse it with
1576 * each implementation.
1577 * @param pszSymList The symbol list for the interface. This is a
1578 * semi-colon separated list of symbol base names. As
1579 * mentioned above, each is prefixed with @a
1580 * pszSymPrefix before resolving. There are a couple
1581 * of special symbol names that will cause us to skip
1582 * ahead a little bit:
1583 * - U8:whatever,
1584 * - U16:whatever,
1585 * - U32:whatever,
1586 * - U64:whatever,
1587 * - RCPTR:whatever,
1588 * - R3PTR:whatever,
1589 * - R0PTR:whatever,
1590 * - GCPHYS:whatever,
1591 * - HCPHYS:whatever.
1592 * @param fRing0 Set if it's a ring-0 context interface, clear if
1593 * it's raw-mode context interface.
1594 */
1595VMMR3_INT_DECL(int) PDMR3LdrGetInterfaceSymbols(PVM pVM, void *pvInterface, size_t cbInterface,
1596 const char *pszModule, const char *pszSearchPath,
1597 const char *pszSymPrefix, const char *pszSymList,
1598 bool fRing0)
1599{
1600 bool const fNullRun = !fRing0 && HMIsEnabled(pVM);
1601
1602 /*
1603 * Find the module.
1604 */
1605 int rc = VINF_SUCCESS;
1606 PPDMMOD pModule = NULL;
1607 if (!fNullRun)
1608 pModule = pdmR3LdrFindModule(pVM->pUVM,
1609 pszModule ? pszModule : fRing0 ? "VMMR0.r0" : "VMMRC.rc",
1610 fRing0 ? PDMMOD_TYPE_R0 : PDMMOD_TYPE_RC,
1611 true /*fLazy*/, pszSearchPath);
1612 if (pModule || fNullRun)
1613 {
1614 /* Prep the symbol name. */
1615 char szSymbol[256];
1616 size_t const cchSymPrefix = strlen(pszSymPrefix);
1617 AssertReturn(cchSymPrefix + 5 < sizeof(szSymbol), VERR_SYMBOL_NOT_FOUND);
1618 memcpy(szSymbol, pszSymPrefix, cchSymPrefix);
1619
1620 /*
1621 * Iterate the symbol list.
1622 */
1623 uint32_t offInterface = 0;
1624 const char *pszCur = pszSymList;
1625 while (pszCur)
1626 {
1627 /*
1628 * Find the end of the current symbol name.
1629 */
1630 size_t cchSym;
1631 const char *pszNext = strchr(pszCur, ';');
1632 if (pszNext)
1633 {
1634 cchSym = pszNext - pszCur;
1635 pszNext++;
1636 }
1637 else
1638 cchSym = strlen(pszCur);
1639 AssertBreakStmt(cchSym > 0, rc = VERR_INVALID_PARAMETER);
1640
1641 /* Is it a skip instruction? */
1642 const char *pszColon = (const char *)memchr(pszCur, ':', cchSym);
1643 if (pszColon)
1644 {
1645 /*
1646 * String switch on the instruction and execute it, checking
1647 * that we didn't overshoot the interface structure.
1648 */
1649#define IS_SKIP_INSTR(szInstr) \
1650 ( cchSkip == sizeof(szInstr) - 1 \
1651 && !memcmp(pszCur, szInstr, sizeof(szInstr) - 1) )
1652
1653 size_t const cchSkip = pszColon - pszCur;
1654 if (IS_SKIP_INSTR("U8"))
1655 offInterface += sizeof(uint8_t);
1656 else if (IS_SKIP_INSTR("U16"))
1657 offInterface += sizeof(uint16_t);
1658 else if (IS_SKIP_INSTR("U32"))
1659 offInterface += sizeof(uint32_t);
1660 else if (IS_SKIP_INSTR("U64"))
1661 offInterface += sizeof(uint64_t);
1662 else if (IS_SKIP_INSTR("RCPTR"))
1663 offInterface += sizeof(RTRCPTR);
1664 else if (IS_SKIP_INSTR("R3PTR"))
1665 offInterface += sizeof(RTR3PTR);
1666 else if (IS_SKIP_INSTR("R0PTR"))
1667 offInterface += sizeof(RTR0PTR);
1668 else if (IS_SKIP_INSTR("HCPHYS"))
1669 offInterface += sizeof(RTHCPHYS);
1670 else if (IS_SKIP_INSTR("GCPHYS"))
1671 offInterface += sizeof(RTGCPHYS);
1672 else
1673 AssertMsgFailedBreakStmt(("Invalid skip instruction %.*s (prefix=%s)\n", cchSym, pszCur, pszSymPrefix),
1674 rc = VERR_INVALID_PARAMETER);
1675 AssertMsgBreakStmt(offInterface <= cbInterface,
1676 ("off=%#x cb=%#x (sym=%.*s prefix=%s)\n", offInterface, cbInterface, cchSym, pszCur, pszSymPrefix),
1677 rc = VERR_BUFFER_OVERFLOW);
1678#undef IS_SKIP_INSTR
1679 }
1680 else
1681 {
1682 /*
1683 * Construct the symbol name, get its value, store it and
1684 * advance the interface cursor.
1685 */
1686 AssertReturn(cchSymPrefix + cchSym < sizeof(szSymbol), VERR_SYMBOL_NOT_FOUND);
1687 memcpy(&szSymbol[cchSymPrefix], pszCur, cchSym);
1688 szSymbol[cchSymPrefix + cchSym] = '\0';
1689
1690 if (fRing0)
1691 {
1692 void *pvValue = NULL;
1693 if (!fNullRun)
1694 {
1695 rc = SUPR3GetSymbolR0((void *)(RTR0PTR)pModule->ImageBase, szSymbol, &pvValue);
1696 AssertMsgRCBreak(rc, ("Couldn't find symbol '%s' in module '%s'\n", szSymbol, pModule->szName));
1697 }
1698
1699 PRTR0PTR pValue = (PRTR0PTR)((uintptr_t)pvInterface + offInterface);
1700 AssertMsgBreakStmt(offInterface + sizeof(*pValue) <= cbInterface,
1701 ("off=%#x cb=%#x sym=%s\n", offInterface, cbInterface, szSymbol),
1702 rc = VERR_BUFFER_OVERFLOW);
1703 *pValue = (RTR0PTR)pvValue;
1704 Assert((void *)*pValue == pvValue);
1705 offInterface += sizeof(*pValue);
1706 }
1707 else
1708 {
1709 RTUINTPTR Value = 0;
1710 if (!fNullRun)
1711 {
1712 rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, UINT32_MAX, szSymbol, &Value);
1713 AssertMsgRCBreak(rc, ("Couldn't find symbol '%s' in module '%s'\n", szSymbol, pModule->szName));
1714 }
1715
1716 PRTRCPTR pValue = (PRTRCPTR)((uintptr_t)pvInterface + offInterface);
1717 AssertMsgBreakStmt(offInterface + sizeof(*pValue) <= cbInterface,
1718 ("off=%#x cb=%#x sym=%s\n", offInterface, cbInterface, szSymbol),
1719 rc = VERR_BUFFER_OVERFLOW);
1720 *pValue = (RTRCPTR)Value;
1721 Assert(*pValue == Value);
1722 offInterface += sizeof(*pValue);
1723 }
1724 }
1725
1726 /* advance */
1727 pszCur = pszNext;
1728 }
1729
1730 }
1731 else
1732 rc = VERR_MODULE_NOT_FOUND;
1733 return rc;
1734}
1735
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