diff options
Diffstat (limited to 'src/VBox/VMM/VMMR3/PDMLdr.cpp')
-rw-r--r-- | src/VBox/VMM/VMMR3/PDMLdr.cpp | 1782 |
1 files changed, 1782 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR3/PDMLdr.cpp b/src/VBox/VMM/VMMR3/PDMLdr.cpp new file mode 100644 index 00000000..4ea695f9 --- /dev/null +++ b/src/VBox/VMM/VMMR3/PDMLdr.cpp @@ -0,0 +1,1782 @@ +/* $Id: PDMLdr.cpp $ */ +/** @file + * PDM - Pluggable Device Manager, module loader. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +//#define PDMLDR_FAKE_MODE + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_PDM_LDR +#include "PDMInternal.h" +#include <VBox/vmm/pdm.h> +#include <VBox/vmm/mm.h> +#include <VBox/vmm/trpm.h> +#include <VBox/vmm/vmm.h> +#include <VBox/vmm/vm.h> +#include <VBox/vmm/uvm.h> +#include <VBox/sup.h> +#include <VBox/param.h> +#include <VBox/err.h> +#include <VBox/vmm/hm.h> +#include <VBox/VBoxTpG.h> + +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/ctype.h> +#include <iprt/file.h> +#include <iprt/ldr.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/string.h> + +#include <limits.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Structure which the user argument of the RTLdrGetBits() callback points to. + * @internal + */ +typedef struct PDMGETIMPORTARGS +{ + PVM pVM; + PPDMMOD pModule; +} PDMGETIMPORTARGS, *PPDMGETIMPORTARGS; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +#ifdef VBOX_WITH_RAW_MODE_KEEP +static DECLCALLBACK(int) pdmR3GetImportRC(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser); +static char *pdmR3FileRC(const char *pszFile, const char *pszSearchPath); +#endif +static int pdmR3LoadR0U(PUVM pUVM, const char *pszFilename, const char *pszName, const char *pszSearchPath); +static char *pdmR3FileR0(const char *pszFile, const char *pszSearchPath); +static char *pdmR3File(const char *pszFile, const char *pszDefaultExt, const char *pszSearchPath, bool fShared); + + + +/** + * Loads the VMMR0.r0 module early in the init process. + * + * @returns VBox status code. + * @param pUVM Pointer to the user mode VM structure. + */ +VMMR3_INT_DECL(int) PDMR3LdrLoadVMMR0U(PUVM pUVM) +{ + return pdmR3LoadR0U(pUVM, NULL, VMMR0_MAIN_MODULE_NAME, NULL); +} + + +/** + * Init the module loader part of PDM. + * + * This routine will load the Host Context Ring-0 and Guest + * Context VMM modules. + * + * @returns VBox status code. + * @param pUVM The user mode VM structure. + */ +int pdmR3LdrInitU(PUVM pUVM) +{ +#if !defined(PDMLDR_FAKE_MODE) && defined(VBOX_WITH_RAW_MODE_KEEP) + /* + * Load the mandatory RC module, the VMMR0.r0 is loaded before VM creation. + */ + PVM pVM = pUVM->pVM; AssertPtr(pVM); + if (VM_IS_RAW_MODE_ENABLED(pVM)) + { + int rc = PDMR3LdrLoadRC(pVM, NULL, VMMRC_MAIN_MODULE_NAME); + if (RT_FAILURE(rc)) + return rc; + } +#else + RT_NOREF(pUVM); +#endif + return VINF_SUCCESS; +} + + +/** + * Terminate the module loader part of PDM. + * + * This will unload and free all modules. + * + * @param pUVM The user mode VM structure. + * @param fFinal This is clear when in the PDMR3Term/vmR3Destroy call + * chain, and set when called from PDMR3TermUVM. + * + * @remarks This is normally called twice during termination. + */ +void pdmR3LdrTermU(PUVM pUVM, bool fFinal) +{ + /* + * Free the modules. + */ + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + PPDMMOD pModule = pUVM->pdm.s.pModules; + pUVM->pdm.s.pModules = NULL; + PPDMMOD *ppNext = &pUVM->pdm.s.pModules; + while (pModule) + { + /* free loader item. */ + if (pModule->hLdrMod != NIL_RTLDRMOD) + { + int rc2 = RTLdrClose(pModule->hLdrMod); + AssertRC(rc2); + pModule->hLdrMod = NIL_RTLDRMOD; + } + + /* free bits. */ + switch (pModule->eType) + { + case PDMMOD_TYPE_R0: + { + if (fFinal) + { + Assert(pModule->ImageBase); + int rc2 = SUPR3FreeModule((void *)(uintptr_t)pModule->ImageBase); + AssertRC(rc2); + pModule->ImageBase = 0; + break; + } + + /* Postpone ring-0 module till the PDMR3TermUVM() phase as VMMR0.r0 is still + busy when we're called the first time very very early in vmR3Destroy(). */ + PPDMMOD pNextModule = pModule->pNext; + + pModule->pNext = NULL; + *ppNext = pModule; + ppNext = &pModule->pNext; + + pModule = pNextModule; + continue; + } + +#ifdef VBOX_WITH_RAW_MODE_KEEP + case PDMMOD_TYPE_RC: +#endif + case PDMMOD_TYPE_R3: + /* MM will free this memory for us - it's alloc only memory. :-) */ + break; + + default: + AssertMsgFailed(("eType=%d\n", pModule->eType)); + break; + } + pModule->pvBits = NULL; + + void *pvFree = pModule; + pModule = pModule->pNext; + RTMemFree(pvFree); + } + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); +} + + +/** + * Applies relocations to RC modules. + * + * This must be done very early in the relocation + * process so that components can resolve RC symbols during relocation. + * + * @param pUVM Pointer to the user mode VM structure. + * @param offDelta Relocation delta relative to old location. + */ +VMMR3_INT_DECL(void) PDMR3LdrRelocateU(PUVM pUVM, RTGCINTPTR offDelta) +{ +#ifdef VBOX_WITH_RAW_MODE_KEEP + LogFlow(("PDMR3LdrRelocate: offDelta=%RGv\n", offDelta)); + RT_NOREF1(offDelta); + + /* + * RC Modules. + */ + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + if (pUVM->pdm.s.pModules) + { + /* + * The relocation have to be done in two passes so imports + * can be correctly resolved. The first pass will update + * the ImageBase saving the current value in OldImageBase. + * The second pass will do the actual relocation. + */ + /* pass 1 */ + PPDMMOD pCur; + for (pCur = pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext) + { + if (pCur->eType == PDMMOD_TYPE_RC) + { + pCur->OldImageBase = pCur->ImageBase; + pCur->ImageBase = MMHyperR3ToRC(pUVM->pVM, pCur->pvBits); + } + } + + /* pass 2 */ + for (pCur = pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext) + { + if (pCur->eType == PDMMOD_TYPE_RC) + { + PDMGETIMPORTARGS Args; + Args.pVM = pUVM->pVM; + Args.pModule = pCur; + int rc = RTLdrRelocate(pCur->hLdrMod, pCur->pvBits, pCur->ImageBase, pCur->OldImageBase, + pdmR3GetImportRC, &Args); + AssertFatalMsgRC(rc, ("RTLdrRelocate failed, rc=%d\n", rc)); + } + } + } + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); +#else + RT_NOREF2(pUVM, offDelta); +#endif +} + + +/** + * Loads a module into the host context ring-3. + * + * This is used by the driver and device init functions to load modules + * containing the drivers and devices. The function can be extended to + * load modules which are not native to the environment we're running in, + * but at the moment this is not required. + * + * No reference counting is kept, since we don't implement any facilities + * for unloading the module. But the module will naturally be released + * when the VM terminates. + * + * @returns VBox status code. + * @param pUVM Pointer to the user mode VM structure. + * @param pszFilename Filename of the module binary. + * @param pszName Module name. Case sensitive and the length is limited! + */ +int pdmR3LoadR3U(PUVM pUVM, const char *pszFilename, const char *pszName) +{ + /* + * Validate input. + */ + AssertMsg(RTCritSectIsInitialized(&pUVM->pdm.s.ListCritSect), ("bad init order!\n")); + Assert(pszFilename); + size_t cchFilename = strlen(pszFilename); + Assert(pszName); + size_t cchName = strlen(pszName); + PPDMMOD pCur; + if (cchName >= sizeof(pCur->szName)) + { + AssertMsgFailed(("Name is too long, cchName=%d pszName='%s'\n", cchName, pszName)); + return VERR_INVALID_PARAMETER; + } + + /* + * Try lookup the name and see if the module exists. + */ + int rc; + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + for (pCur = pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext) + { + if (!strcmp(pCur->szName, pszName)) + { + if (pCur->eType == PDMMOD_TYPE_R3) + rc = VINF_PDM_ALREADY_LOADED; + else + rc = VERR_PDM_MODULE_NAME_CLASH; + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + + AssertMsgRC(rc, ("We've already got a module '%s' loaded!\n", pszName)); + return rc; + } + } + + /* + * Allocate the module list node and initialize it. + */ + const char *pszSuff = RTLdrGetSuff(); + size_t cchSuff = RTPathHasSuffix(pszFilename) ? 0 : strlen(pszSuff); + PPDMMOD pModule = (PPDMMOD)RTMemAllocZ(RT_UOFFSETOF_DYN(PDMMOD, szFilename[cchFilename + cchSuff + 1])); + if (pModule) + { + pModule->eType = PDMMOD_TYPE_R3; + memcpy(pModule->szName, pszName, cchName); /* memory is zero'd, no need to copy terminator :-) */ + memcpy(pModule->szFilename, pszFilename, cchFilename); + memcpy(&pModule->szFilename[cchFilename], pszSuff, cchSuff); + + /* + * Load the loader item. + */ + RTERRINFOSTATIC ErrInfo; + RTErrInfoInitStatic(&ErrInfo); + rc = SUPR3HardenedLdrLoadPlugIn(pModule->szFilename, &pModule->hLdrMod, &ErrInfo.Core); + if (RT_SUCCESS(rc)) + { + pModule->pNext = pUVM->pdm.s.pModules; + pUVM->pdm.s.pModules = pModule; + } + else + { + /* Something went wrong, most likely module not found. Don't consider other unlikely errors */ + rc = VMSetError(pUVM->pVM, rc, RT_SRC_POS, + N_("Unable to load R3 module %s (%s): %s"), pModule->szFilename, pszName, ErrInfo.Core.pszMsg); + RTMemFree(pModule); + } + } + else + rc = VERR_NO_MEMORY; + + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + return rc; +} + +#ifdef VBOX_WITH_RAW_MODE_KEEP + +/** + * Resolve an external symbol during RTLdrGetBits() of a RC module. + * + * @returns VBox status code. + * @param hLdrMod The loader module handle. + * @param pszModule Module name. + * @param pszSymbol Symbol name, NULL if uSymbol should be used. + * @param uSymbol Symbol ordinal, ~0 if pszSymbol should be used. + * @param pValue Where to store the symbol value (address). + * @param pvUser User argument. + */ +static DECLCALLBACK(int) pdmR3GetImportRC(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol, + RTUINTPTR *pValue, void *pvUser) +{ + PVM pVM = ((PPDMGETIMPORTARGS)pvUser)->pVM; + PPDMMOD pModule = ((PPDMGETIMPORTARGS)pvUser)->pModule; + NOREF(hLdrMod); NOREF(uSymbol); + + /* + * Adjust input. + */ + if (pszModule && !*pszModule) + pszModule = NULL; + + /* + * Builtin module. + */ + if (!pszModule || !strcmp(pszModule, "VMMRCBuiltin.rc")) + { + int rc = VINF_SUCCESS; + if (!strcmp(pszSymbol, "g_VM")) + *pValue = pVM->pVMRC; + else if (!strcmp(pszSymbol, "g_VCpu0")) + *pValue = pVM->pVMRC + pVM->offVMCPU; + else if (!strcmp(pszSymbol, "g_CPUM")) + *pValue = VM_RC_ADDR(pVM, &pVM->cpum); + else if ( !strncmp(pszSymbol, "g_TRPM", 6) + || !strncmp(pszSymbol, "g_trpm", 6) + || !strncmp(pszSymbol, "TRPM", 4)) + { + RTRCPTR RCPtr = 0; + rc = TRPMR3GetImportRC(pVM, pszSymbol, &RCPtr); + if (RT_SUCCESS(rc)) + *pValue = RCPtr; + } + else if ( !strncmp(pszSymbol, "VMM", 3) + || !strcmp(pszSymbol, "g_Logger") + || !strcmp(pszSymbol, "g_RelLogger")) + { + RTRCPTR RCPtr = 0; + rc = VMMR3GetImportRC(pVM, pszSymbol, &RCPtr); + if (RT_SUCCESS(rc)) + *pValue = RCPtr; + } + else + { + AssertMsg(!pszModule, ("Unknown builtin symbol '%s' for module '%s'!\n", pszSymbol, pModule->szName)); NOREF(pModule); + rc = VERR_SYMBOL_NOT_FOUND; + } + if (RT_SUCCESS(rc) || pszModule) + { + if (RT_FAILURE(rc)) + LogRel(("PDMLdr: Couldn't find symbol '%s' in module '%s'!\n", pszSymbol, pszModule)); + return rc; + } + } + + /* + * Search for module. + */ + PUVM pUVM = pVM->pUVM; + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + PPDMMOD pCur = pUVM->pdm.s.pModules; + while (pCur) + { + if ( pCur->eType == PDMMOD_TYPE_RC + && ( !pszModule + || !strcmp(pCur->szName, pszModule)) + ) + { + /* Search for the symbol. */ + int rc = RTLdrGetSymbolEx(pCur->hLdrMod, pCur->pvBits, pCur->ImageBase, UINT32_MAX, pszSymbol, pValue); + if (RT_SUCCESS(rc)) + { + AssertMsg(*pValue - pCur->ImageBase < RTLdrSize(pCur->hLdrMod), + ("%RRv-%RRv %s %RRv\n", (RTRCPTR)pCur->ImageBase, + (RTRCPTR)(pCur->ImageBase + RTLdrSize(pCur->hLdrMod) - 1), + pszSymbol, (RTRCPTR)*pValue)); + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + return rc; + } + if (pszModule) + { + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + AssertLogRelMsgFailed(("PDMLdr: Couldn't find symbol '%s' in module '%s'!\n", pszSymbol, pszModule)); + return VERR_SYMBOL_NOT_FOUND; + } + } + + /* next */ + pCur = pCur->pNext; + } + + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + AssertLogRelMsgFailed(("Couldn't find module '%s' for resolving symbol '%s'!\n", pszModule, pszSymbol)); + return VERR_SYMBOL_NOT_FOUND; +} + + +/** + * Loads a module into the raw-mode context (i.e. into the Hypervisor memory + * region). + * + * @returns VBox status code. + * @retval VINF_PDM_ALREADY_LOADED if the module is already loaded (name + + * filename match). + * @retval VERR_PDM_MODULE_NAME_CLASH if a different file has already been + * loaded with the name module name. + * + * @param pVM The cross context VM structure. + * @param pszFilename Filename of the module binary. + * @param pszName Module name. Case sensitive and the length is limited! + */ +VMMR3DECL(int) PDMR3LdrLoadRC(PVM pVM, const char *pszFilename, const char *pszName) +{ + /* + * Validate input. + */ + AssertReturn(VM_IS_RAW_MODE_ENABLED(pVM), VERR_PDM_HM_IPE); + + /* + * Find the file if not specified. + */ + char *pszFile = NULL; + if (!pszFilename) + pszFilename = pszFile = pdmR3FileRC(pszName, NULL); + + /* + * Check if a module by that name is already loaded. + */ + int rc; + PUVM pUVM = pVM->pUVM; + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + PPDMMOD pCur = pUVM->pdm.s.pModules; + while (pCur) + { + if (!strcmp(pCur->szName, pszName)) + { + /* Name clash. Hopefully due to it being the same file. */ + if (!strcmp(pCur->szFilename, pszFilename)) + rc = VINF_PDM_ALREADY_LOADED; + else + { + rc = VERR_PDM_MODULE_NAME_CLASH; + AssertMsgFailed(("We've already got a module '%s' loaded!\n", pszName)); + } + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + RTMemTmpFree(pszFile); + return rc; + } + /* next */ + pCur = pCur->pNext; + } + + /* + * Allocate the module list node. + */ + PPDMMOD pModule = (PPDMMOD)RTMemAllocZ(sizeof(*pModule) + strlen(pszFilename)); + if (!pModule) + { + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + RTMemTmpFree(pszFile); + return VERR_NO_MEMORY; + } + AssertMsg(strlen(pszName) + 1 < sizeof(pModule->szName), + ("pazName is too long (%d chars) max is %d chars.\n", strlen(pszName), sizeof(pModule->szName) - 1)); + strcpy(pModule->szName, pszName); + pModule->eType = PDMMOD_TYPE_RC; + strcpy(pModule->szFilename, pszFilename); + + + /* + * Open the loader item. + */ + RTERRINFOSTATIC ErrInfo; + RTErrInfoInitStatic(&ErrInfo); + rc = SUPR3HardenedVerifyPlugIn(pszFilename, &ErrInfo.Core); + if (RT_SUCCESS(rc)) + { + RTErrInfoClear(&ErrInfo.Core); + rc = RTLdrOpen(pszFilename, 0, RTLDRARCH_X86_32, &pModule->hLdrMod); + } + if (RT_SUCCESS(rc)) + { + /* + * Allocate space in the hypervisor. + */ + size_t cb = RTLdrSize(pModule->hLdrMod); + cb = RT_ALIGN_Z(cb, RT_MAX(GUEST_PAGE_SIZE, HOST_PAGE_SIZE)); + uint32_t cPages = (uint32_t)(cb >> HOST_PAGE_SHIFT); + if (((size_t)cPages << HOST_PAGE_SHIFT) == cb) + { + PSUPPAGE paPages = (PSUPPAGE)RTMemTmpAlloc(cPages * sizeof(paPages[0])); + if (paPages) + { + rc = SUPR3PageAllocEx(cPages, 0 /*fFlags*/, &pModule->pvBits, NULL /*pR0Ptr*/, paPages); + if (RT_SUCCESS(rc)) + { + RTGCPTR GCPtr; + rc = VERR_NOT_IMPLEMENTED; //MMR3HyperMapPages(pVM, pModule->pvBits, NIL_RTR0PTR, cPages, paPages, pModule->szName, &GCPtr); + if (RT_SUCCESS(rc)) + { + //MMR3HyperReserveFence(pVM); + + /* + * Get relocated image bits. + */ + Assert(MMHyperR3ToRC(pVM, pModule->pvBits) == GCPtr); + pModule->ImageBase = GCPtr; + PDMGETIMPORTARGS Args; + Args.pVM = pVM; + Args.pModule = pModule; + rc = RTLdrGetBits(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, pdmR3GetImportRC, &Args); + if (RT_SUCCESS(rc)) + { +#ifdef VBOX_WITH_DTRACE_RC + /* + * Register the tracer bits if present. + */ + RTLDRADDR uValue; + rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, UINT32_MAX, + "g_VTGObjHeader", &uValue); + if (RT_SUCCESS(rc)) + { + PVTGOBJHDR pVtgHdr = (PVTGOBJHDR)MMHyperRCToCC(pVM, (RTRCPTR)uValue); + if ( pVtgHdr + && !memcmp(pVtgHdr->szMagic, VTGOBJHDR_MAGIC, sizeof(pVtgHdr->szMagic))) + rc = SUPR3TracerRegisterModule(~(uintptr_t)0, pModule->szName, pVtgHdr, uValue, + SUP_TRACER_UMOD_FLAGS_SHARED); + else + rc = pVtgHdr ? VERR_INVALID_MAGIC : VERR_INVALID_POINTER; + if (RT_FAILURE(rc)) + LogRel(("PDMLdr: Failed to register tracepoints for '%s': %Rrc\n", pModule->szName, rc)); + } +#endif + + /* + * Insert the module. + */ + if (pUVM->pdm.s.pModules) + { + /* we don't expect this list to be very long, so rather save the tail pointer. */ + pCur = pUVM->pdm.s.pModules; + while (pCur->pNext) + pCur = pCur->pNext; + pCur->pNext = pModule; + } + else + pUVM->pdm.s.pModules = pModule; /* (pNext is zeroed by alloc) */ + Log(("PDM: RC Module at %RRv %s (%s)\n", (RTRCPTR)pModule->ImageBase, pszName, pszFilename)); + + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + RTMemTmpFree(pszFile); + RTMemTmpFree(paPages); + + return VINF_SUCCESS; + } + } + else + { + AssertRC(rc); + SUPR3PageFreeEx(pModule->pvBits, cPages); + } + } + else + AssertMsgFailed(("SUPR3PageAlloc(%d,) -> %Rrc\n", cPages, rc)); + RTMemTmpFree(paPages); + } + else + rc = VERR_NO_TMP_MEMORY; + } + else + rc = VERR_OUT_OF_RANGE; + int rc2 = RTLdrClose(pModule->hLdrMod); + AssertRC(rc2); + } + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + + /* Don't consider VERR_PDM_MODULE_NAME_CLASH and VERR_NO_MEMORY above as these are very unlikely. */ + if (RT_FAILURE(rc) && RTErrInfoIsSet(&ErrInfo.Core)) + rc = VMSetError(pVM, rc, RT_SRC_POS, N_("Cannot load RC module %s: %s"), pszFilename, ErrInfo.Core.pszMsg); + else if (RT_FAILURE(rc)) + rc = VMSetError(pVM, rc, RT_SRC_POS, N_("Cannot load RC module %s"), pszFilename); + + RTMemFree(pModule); + RTMemTmpFree(pszFile); + return rc; +} + +#endif /* VBOX_WITH_RAW_MODE_KEEP */ + +/** + * Loads a module into the ring-0 context. + * + * @returns VBox status code. + * @param pUVM Pointer to the user mode VM structure. + * @param pszFilename Filename of the module binary. + * @param pszName Module name. Case sensitive and the length is limited! + * @param pszSearchPath List of directories to search if @a pszFilename is + * not specified. Can be NULL, in which case the arch + * dependent install dir is searched. + */ +static int pdmR3LoadR0U(PUVM pUVM, const char *pszFilename, const char *pszName, const char *pszSearchPath) +{ + /* + * Validate input. + */ + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + PPDMMOD pCur = pUVM->pdm.s.pModules; + while (pCur) + { + if (!strcmp(pCur->szName, pszName)) + { + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + AssertMsgFailed(("We've already got a module '%s' loaded!\n", pszName)); + return VERR_PDM_MODULE_NAME_CLASH; + } + /* next */ + pCur = pCur->pNext; + } + + /* + * Find the file if not specified. + */ + char *pszFile = NULL; + if (!pszFilename) + pszFilename = pszFile = pdmR3FileR0(pszName, pszSearchPath); + + /* + * Allocate the module list node. + */ + PPDMMOD pModule = (PPDMMOD)RTMemAllocZ(sizeof(*pModule) + strlen(pszFilename)); + if (!pModule) + { + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + RTMemTmpFree(pszFile); + return VERR_NO_MEMORY; + } + AssertMsg(strlen(pszName) + 1 < sizeof(pModule->szName), + ("pazName is too long (%d chars) max is %d chars.\n", strlen(pszName), sizeof(pModule->szName) - 1)); + strcpy(pModule->szName, pszName); + pModule->eType = PDMMOD_TYPE_R0; + strcpy(pModule->szFilename, pszFilename); + + /* + * Ask the support library to load it. + */ + void *pvImageBase; + RTERRINFOSTATIC ErrInfo; + RTErrInfoInitStatic(&ErrInfo); + int rc = SUPR3LoadModule(pszFilename, pszName, &pvImageBase, &ErrInfo.Core); + if (RT_SUCCESS(rc)) + { + pModule->hLdrMod = NIL_RTLDRMOD; + pModule->ImageBase = (uintptr_t)pvImageBase; + + /* + * Insert the module. + */ + if (pUVM->pdm.s.pModules) + { + /* we don't expect this list to be very long, so rather save the tail pointer. */ + pCur = pUVM->pdm.s.pModules; + while (pCur->pNext) + pCur = pCur->pNext; + pCur->pNext = pModule; + } + else + pUVM->pdm.s.pModules = pModule; /* (pNext is zeroed by alloc) */ + Log(("PDM: R0 Module at %RHv %s (%s)\n", (RTR0PTR)pModule->ImageBase, pszName, pszFilename)); + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + RTMemTmpFree(pszFile); + return VINF_SUCCESS; + } + + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + RTMemFree(pModule); + LogRel(("PDMLdr: pdmR3LoadR0U: pszName=\"%s\" rc=%Rrc szErr=\"%s\"\n", pszName, rc, ErrInfo.Core.pszMsg)); + + /* Don't consider VERR_PDM_MODULE_NAME_CLASH and VERR_NO_MEMORY above as these are very unlikely. */ + if (RT_FAILURE(rc)) + rc = VMR3SetError(pUVM, rc, RT_SRC_POS, N_("Failed to load R0 module %s: %s"), pszFilename, ErrInfo.Core.pszMsg); + + RTMemTmpFree(pszFile); /* might be reference thru pszFilename in the above VMSetError call. */ + return rc; +} + + +/** + * Makes sure a ring-0 module is loaded. + * + * @returns VBox status code. + * @param pUVM Pointer to the user mode VM structure. + * @param pszModule Module name (no path). + * @param pszSearchPath List of directories to search for the module + * (assumes @a pszModule is also a filename). + */ +VMMR3_INT_DECL(int) PDMR3LdrLoadR0(PUVM pUVM, const char *pszModule, const char *pszSearchPath) +{ + /* + * Find the module. + */ + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + for (PPDMMOD pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext) + { + if ( pModule->eType == PDMMOD_TYPE_R0 + && !strcmp(pModule->szName, pszModule)) + { + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + return VINF_SUCCESS; + } + } + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + + /* + * Okay, load it. + */ + return pdmR3LoadR0U(pUVM, NULL, pszModule, pszSearchPath); +} + + +/** + * Get the address of a symbol in a given HC ring 3 module. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pszModule Module name. + * @param pszSymbol Symbol name. If it's value is less than 64k it's treated like a + * ordinal value rather than a string pointer. + * @param ppvValue Where to store the symbol value. + */ +VMMR3_INT_DECL(int) PDMR3LdrGetSymbolR3(PVM pVM, const char *pszModule, const char *pszSymbol, void **ppvValue) +{ + /* + * Validate input. + */ + AssertPtr(pVM); + AssertPtr(pszModule); + AssertPtr(ppvValue); + PUVM pUVM = pVM->pUVM; + AssertMsg(RTCritSectIsInitialized(&pUVM->pdm.s.ListCritSect), ("bad init order!\n")); + + /* + * Find the module. + */ + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + for (PPDMMOD pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext) + { + if ( pModule->eType == PDMMOD_TYPE_R3 + && !strcmp(pModule->szName, pszModule)) + { + RTUINTPTR Value = 0; + int rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, UINT32_MAX, pszSymbol, &Value); + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + if (RT_SUCCESS(rc)) + { + *ppvValue = (void *)(uintptr_t)Value; + Assert((uintptr_t)*ppvValue == Value); + } + else + { + if ((uintptr_t)pszSymbol < 0x10000) + AssertMsg(rc, ("Couldn't symbol '%u' in module '%s'\n", (unsigned)(uintptr_t)pszSymbol, pszModule)); + else + AssertMsg(rc, ("Couldn't symbol '%s' in module '%s'\n", pszSymbol, pszModule)); + } + return rc; + } + } + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + AssertMsgFailed(("Couldn't locate module '%s'\n", pszModule)); + return VERR_SYMBOL_NOT_FOUND; +} + + +/** + * Get the address of a symbol in a given HC ring 0 module. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pszModule Module name. If NULL the main R0 module (VMMR0.r0) is assumes. + * @param pszSymbol Symbol name. If it's value is less than 64k it's treated like a + * ordinal value rather than a string pointer. + * @param ppvValue Where to store the symbol value. + */ +VMMR3DECL(int) PDMR3LdrGetSymbolR0(PVM pVM, const char *pszModule, const char *pszSymbol, PRTR0PTR ppvValue) +{ +#ifdef PDMLDR_FAKE_MODE + *ppvValue = 0xdeadbeef; + return VINF_SUCCESS; + +#else + /* + * Validate input. + */ + AssertPtr(pVM); + AssertPtrNull(pszModule); + AssertPtr(ppvValue); + PUVM pUVM = pVM->pUVM; + AssertMsg(RTCritSectIsInitialized(&pUVM->pdm.s.ListCritSect), ("bad init order!\n")); + + if (!pszModule) + pszModule = VMMR0_MAIN_MODULE_NAME; + + /* + * Find the module. + */ + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + for (PPDMMOD pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext) + { + if ( pModule->eType == PDMMOD_TYPE_R0 + && !strcmp(pModule->szName, pszModule)) + { + int rc = SUPR3GetSymbolR0((void *)(uintptr_t)pModule->ImageBase, pszSymbol, (void **)ppvValue); + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + if (RT_FAILURE(rc)) + { + AssertMsgRC(rc, ("Couldn't find symbol '%s' in module '%s'\n", pszSymbol, pszModule)); + LogRel(("PDMLdr: PDMGetSymbol: Couldn't find symbol '%s' in module '%s'\n", pszSymbol, pszModule)); + } + return rc; + } + } + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + AssertMsgFailed(("Couldn't locate module '%s'\n", pszModule)); + return VERR_SYMBOL_NOT_FOUND; +#endif +} + + +/** + * Same as PDMR3LdrGetSymbolR0 except that the module will be attempted loaded if not found. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pszModule Module name. If NULL the main R0 module (VMMR0.r0) is assumed. + * @param pszSearchPath List of directories to search if @a pszFile is + * not qualified with a path. Can be NULL, in which + * case the arch dependent install dir is searched. + * @param pszSymbol Symbol name. If it's value is less than 64k it's treated like a + * ordinal value rather than a string pointer. + * @param ppvValue Where to store the symbol value. + */ +VMMR3DECL(int) PDMR3LdrGetSymbolR0Lazy(PVM pVM, const char *pszModule, const char *pszSearchPath, const char *pszSymbol, + PRTR0PTR ppvValue) +{ +#ifdef PDMLDR_FAKE_MODE + *ppvValue = 0xdeadbeef; + return VINF_SUCCESS; + +#else + AssertPtr(pVM); + AssertPtrNull(pszModule); + AssertPtr(ppvValue); + PUVM pUVM = pVM->pUVM; + AssertMsg(RTCritSectIsInitialized(&pUVM->pdm.s.ListCritSect), ("bad init order!\n")); + + if (pszModule) /* (We don't lazy load the main R0 module.) */ + { + /* + * Since we're lazy, we'll only check if the module is present + * and hand it over to PDMR3LdrGetSymbolR0 when that's done. + */ + AssertMsgReturn(!strpbrk(pszModule, "/\\:\n\r\t"), ("pszModule=%s\n", pszModule), VERR_INVALID_PARAMETER); + PPDMMOD pModule; + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + for (pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext) + if ( pModule->eType == PDMMOD_TYPE_R0 + && !strcmp(pModule->szName, pszModule)) + break; + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + if (!pModule) + { + int rc = pdmR3LoadR0U(pUVM, NULL, pszModule, pszSearchPath); + AssertMsgRCReturn(rc, ("pszModule=%s rc=%Rrc\n", pszModule, rc), VERR_MODULE_NOT_FOUND); + } + } + + return PDMR3LdrGetSymbolR0(pVM, pszModule, pszSymbol, ppvValue); +#endif +} + + +/** + * Get the address of a symbol in a given RC module. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pszModule Module name. If NULL the main R0 module (VMMRC.rc) + * is assumes. + * @param pszSymbol Symbol name. If it's value is less than 64k it's + * treated like a ordinal value rather than a string + * pointer. + * @param pRCPtrValue Where to store the symbol value. + */ +VMMR3DECL(int) PDMR3LdrGetSymbolRC(PVM pVM, const char *pszModule, const char *pszSymbol, PRTRCPTR pRCPtrValue) +{ +#if defined(PDMLDR_FAKE_MODE) || !defined(VBOX_WITH_RAW_MODE_KEEP) + RT_NOREF(pVM, pszModule, pszSymbol); + *pRCPtrValue = NIL_RTRCPTR; + return VINF_SUCCESS; + +#else + /* + * Validate input. + */ + AssertPtr(pVM); + AssertPtrNull(pszModule); + AssertPtr(pRCPtrValue); + + if (!pszModule) + pszModule = VMMRC_MAIN_MODULE_NAME; + + /* + * Find the module. + */ + PUVM pUVM = pVM->pUVM; + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + for (PPDMMOD pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext) + { + if ( pModule->eType == PDMMOD_TYPE_RC + && !strcmp(pModule->szName, pszModule)) + { + RTUINTPTR Value; + int rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, UINT32_MAX, pszSymbol, &Value); + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + if (RT_SUCCESS(rc)) + { + *pRCPtrValue = (RTGCPTR)Value; + Assert(*pRCPtrValue == Value); + } + else + { + if ((uintptr_t)pszSymbol < 0x10000) + AssertMsg(rc, ("Couldn't symbol '%u' in module '%s'\n", (unsigned)(uintptr_t)pszSymbol, pszModule)); + else + AssertMsg(rc, ("Couldn't symbol '%s' in module '%s'\n", pszSymbol, pszModule)); + } + return rc; + } + } + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + AssertMsgFailed(("Couldn't locate module '%s'\n", pszModule)); + return VERR_SYMBOL_NOT_FOUND; +#endif +} + + +/** + * Same as PDMR3LdrGetSymbolRC except that the module will be attempted loaded if not found. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pszModule Module name. If NULL the main RC module (VMMRC.rc) + * is assumed. + * @param pszSearchPath List of directories to search if @a pszFile is + * not qualified with a path. Can be NULL, in which + * case the arch dependent install dir is searched. + * @param pszSymbol Symbol name. If it's value is less than 64k it's treated like a + * ordinal value rather than a string pointer. + * @param pRCPtrValue Where to store the symbol value. + */ +VMMR3DECL(int) PDMR3LdrGetSymbolRCLazy(PVM pVM, const char *pszModule, const char *pszSearchPath, const char *pszSymbol, + PRTRCPTR pRCPtrValue) +{ +#if defined(PDMLDR_FAKE_MODE) || !defined(VBOX_WITH_RAW_MODE_KEEP) + RT_NOREF(pVM, pszModule, pszSearchPath, pszSymbol); + *pRCPtrValue = NIL_RTRCPTR; + return VINF_SUCCESS; + +#else + AssertPtr(pVM); + if (!pszModule) + pszModule = VMMRC_MAIN_MODULE_NAME; + AssertPtr(pszModule); + AssertPtr(pRCPtrValue); + + /* + * Since we're lazy, we'll only check if the module is present + * and hand it over to PDMR3LdrGetSymbolRC when that's done. + */ + AssertMsgReturn(!strpbrk(pszModule, "/\\:\n\r\t"), ("pszModule=%s\n", pszModule), VERR_INVALID_PARAMETER); + PUVM pUVM = pVM->pUVM; + PPDMMOD pModule; + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + for (pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext) + if ( pModule->eType == PDMMOD_TYPE_RC + && !strcmp(pModule->szName, pszModule)) + break; + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + if (!pModule) + { + char *pszFilename = pdmR3FileRC(pszModule, pszSearchPath); + AssertMsgReturn(pszFilename, ("pszModule=%s\n", pszModule), VERR_MODULE_NOT_FOUND); + int rc = PDMR3LdrLoadRC(pVM, pszFilename, pszModule); + RTMemTmpFree(pszFilename); + AssertMsgRCReturn(rc, ("pszModule=%s rc=%Rrc\n", pszModule, rc), VERR_MODULE_NOT_FOUND); + } + + return PDMR3LdrGetSymbolRC(pVM, pszModule, pszSymbol, pRCPtrValue); +#endif +} + + +/** + * Constructs the full filename for a R3 image file. + * + * @returns Pointer to temporary memory containing the filename. + * Caller must free this using RTMemTmpFree(). + * @returns NULL on failure. + * + * @param pszFile File name (no path). + * @param fShared If true, search in the shared directory (/usr/lib on Unix), else + * search in the private directory (/usr/lib/virtualbox on Unix). + * Ignored if VBOX_PATH_SHARED_LIBS is not defined. + */ +char *pdmR3FileR3(const char *pszFile, bool fShared) +{ + return pdmR3File(pszFile, NULL, NULL, fShared); +} + + +/** + * Constructs the full filename for a R0 image file. + * + * @returns Pointer to temporary memory containing the filename. + * Caller must free this using RTMemTmpFree(). + * @returns NULL on failure. + * + * @param pszFile File name (no path). + * @param pszSearchPath List of directories to search if @a pszFile is + * not qualified with a path. Can be NULL, in which + * case the arch dependent install dir is searched. + */ +char *pdmR3FileR0(const char *pszFile, const char *pszSearchPath) +{ + return pdmR3File(pszFile, NULL, pszSearchPath, /*fShared=*/false); +} + + +/** + * Constructs the full filename for a RC image file. + * + * @returns Pointer to temporary memory containing the filename. + * Caller must free this using RTMemTmpFree(). + * @returns NULL on failure. + * + * @param pszFile File name (no path). + * @param pszSearchPath List of directories to search if @a pszFile is + * not qualified with a path. Can be NULL, in which + * case the arch dependent install dir is searched. + */ +char *pdmR3FileRC(const char *pszFile, const char *pszSearchPath) +{ + return pdmR3File(pszFile, NULL, pszSearchPath, /*fShared=*/false); +} + + +/** + * Worker for pdmR3File(). + * + * @returns Pointer to temporary memory containing the filename. + * Caller must free this using RTMemTmpFree(). + * @returns NULL on failure. + * + * @param pszDir Directory part + * @param pszFile File name part + * @param pszDefaultExt Extension part + */ +static char *pdmR3FileConstruct(const char *pszDir, const char *pszFile, const char *pszDefaultExt) +{ + /* + * Allocate temp memory for return buffer. + */ + size_t cchDir = strlen(pszDir); + size_t cchFile = strlen(pszFile); + size_t cchDefaultExt; + + /* + * Default extention? + */ + if (!pszDefaultExt || strchr(pszFile, '.')) + cchDefaultExt = 0; + else + cchDefaultExt = strlen(pszDefaultExt); + + size_t cchPath = cchDir + 1 + cchFile + cchDefaultExt + 1; + AssertMsgReturn(cchPath <= RTPATH_MAX, ("Path too long!\n"), NULL); + + char *pszRet = (char *)RTMemTmpAlloc(cchDir + 1 + cchFile + cchDefaultExt + 1); + AssertMsgReturn(pszRet, ("Out of temporary memory!\n"), NULL); + + /* + * Construct the filename. + */ + memcpy(pszRet, pszDir, cchDir); + pszRet[cchDir++] = '/'; /* this works everywhere */ + memcpy(pszRet + cchDir, pszFile, cchFile + 1); + if (cchDefaultExt) + memcpy(pszRet + cchDir + cchFile, pszDefaultExt, cchDefaultExt + 1); + + return pszRet; +} + + +/** + * Worker for pdmR3FileRC(), pdmR3FileR0() and pdmR3FileR3(). + * + * @returns Pointer to temporary memory containing the filename. + * Caller must free this using RTMemTmpFree(). + * @returns NULL on failure. + * @param pszFile File name (no path). + * @param pszDefaultExt The default extention, NULL if none. + * @param pszSearchPath List of directories to search if @a pszFile is + * not qualified with a path. Can be NULL, in which + * case the arch dependent install dir is searched. + * @param fShared If true, search in the shared directory (/usr/lib on Unix), else + * search in the private directory (/usr/lib/virtualbox on Unix). + * Ignored if VBOX_PATH_SHARED_LIBS is not defined. + * @todo We'll have this elsewhere than in the root later! + * @todo Remove the fShared hack again once we don't need to link against VBoxDD anymore! + */ +static char *pdmR3File(const char *pszFile, const char *pszDefaultExt, const char *pszSearchPath, bool fShared) +{ + char szPath[RTPATH_MAX]; + int rc; + + AssertLogRelReturn(!fShared || !pszSearchPath, NULL); + Assert(!RTPathHavePath(pszFile)); + + /* + * If there is a path, search it. + */ + if ( pszSearchPath + && *pszSearchPath) + { + /* Check the filename length. */ + size_t const cchFile = strlen(pszFile); + if (cchFile >= sizeof(szPath)) + return NULL; + + /* + * Walk the search path. + */ + const char *psz = pszSearchPath; + while (*psz) + { + /* Skip leading blanks - no directories with leading spaces, thank you. */ + while (RT_C_IS_BLANK(*psz)) + psz++; + + /* Find the end of this element. */ + const char *pszNext; + const char *pszEnd = strchr(psz, ';'); + if (!pszEnd) + pszEnd = pszNext = strchr(psz, '\0'); + else + pszNext = pszEnd + 1; + if (pszEnd != psz) + { + rc = RTPathJoinEx(szPath, sizeof(szPath), psz, pszEnd - psz, pszFile, cchFile, RTPATH_STR_F_STYLE_HOST); + if (RT_SUCCESS(rc)) + { + if (RTFileExists(szPath)) + { + size_t cchPath = strlen(szPath) + 1; + char *pszRet = (char *)RTMemTmpAlloc(cchPath); + if (pszRet) + memcpy(pszRet, szPath, cchPath); + return pszRet; + } + } + } + + /* advance */ + psz = pszNext; + } + } + + /* + * Use the default location. + */ + rc = fShared + ? RTPathSharedLibs( szPath, sizeof(szPath)) + : RTPathAppPrivateArch(szPath, sizeof(szPath)); + if (!RT_SUCCESS(rc)) + { + AssertMsgFailed(("RTPath[SharedLibs|AppPrivateArch](,%d) failed rc=%d!\n", sizeof(szPath), rc)); + return NULL; + } + + return pdmR3FileConstruct(szPath, pszFile, pszDefaultExt); +} + + +/** @internal */ +typedef struct QMFEIPARG +{ + RTINTPTR uPC; + + char *pszNearSym1; + size_t cchNearSym1; + RTINTPTR offNearSym1; + + char *pszNearSym2; + size_t cchNearSym2; + RTINTPTR offNearSym2; +} QMFEIPARG, *PQMFEIPARG; + + +/** + * Enumeration callback function used by RTLdrEnumSymbols(). + * + * @returns VBox status code. Failure will stop the enumeration. + * @param hLdrMod The loader module handle. + * @param pszSymbol Symbol name. NULL if ordinal only. + * @param uSymbol Symbol ordinal, ~0 if not used. + * @param Value Symbol value. + * @param pvUser The user argument specified to RTLdrEnumSymbols(). + */ +static DECLCALLBACK(int) pdmR3QueryModFromEIPEnumSymbols(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, + RTUINTPTR Value, void *pvUser) +{ + PQMFEIPARG pArgs = (PQMFEIPARG)pvUser; + NOREF(hLdrMod); + + RTINTPTR off = Value - pArgs->uPC; + if (off <= 0) /* near1 is before or at same location. */ + { + if (off > pArgs->offNearSym1) + { + pArgs->offNearSym1 = off; + if (pArgs->pszNearSym1 && pArgs->cchNearSym1) + { + *pArgs->pszNearSym1 = '\0'; + if (pszSymbol) + strncat(pArgs->pszNearSym1, pszSymbol, pArgs->cchNearSym1); + else + { + char szOrd[32]; + RTStrPrintf(szOrd, sizeof(szOrd), "#%#x", uSymbol); + strncat(pArgs->pszNearSym1, szOrd, pArgs->cchNearSym1); + } + } + } + } + else /* near2 is after */ + { + if (off < pArgs->offNearSym2) + { + pArgs->offNearSym2 = off; + if (pArgs->pszNearSym2 && pArgs->cchNearSym2) + { + *pArgs->pszNearSym2 = '\0'; + if (pszSymbol) + strncat(pArgs->pszNearSym2, pszSymbol, pArgs->cchNearSym2); + else + { + char szOrd[32]; + RTStrPrintf(szOrd, sizeof(szOrd), "#%#x", uSymbol); + strncat(pArgs->pszNearSym2, szOrd, pArgs->cchNearSym2); + } + } + } + } + + return VINF_SUCCESS; +} + + +/** + * Internal worker for PDMR3LdrQueryRCModFromPC and PDMR3LdrQueryR0ModFromPC. + * + * @returns VBox status code. + * + * @param pVM The cross context VM structure. + * @param uPC The program counter (eip/rip) to locate the module for. + * @param enmType The module type. + * @param pszModName Where to store the module name. + * @param cchModName Size of the module name buffer. + * @param pMod Base address of the module. + * @param pszNearSym1 Name of the closes symbol from below. + * @param cchNearSym1 Size of the buffer pointed to by pszNearSym1. + * @param pNearSym1 The address of pszNearSym1. + * @param pszNearSym2 Name of the closes symbol from below. + * @param cchNearSym2 Size of the buffer pointed to by pszNearSym2. + * @param pNearSym2 The address of pszNearSym2. + */ +static int pdmR3LdrQueryModFromPC(PVM pVM, RTUINTPTR uPC, PDMMODTYPE enmType, + char *pszModName, size_t cchModName, PRTUINTPTR pMod, + char *pszNearSym1, size_t cchNearSym1, PRTUINTPTR pNearSym1, + char *pszNearSym2, size_t cchNearSym2, PRTUINTPTR pNearSym2) +{ + PUVM pUVM = pVM->pUVM; + int rc = VERR_MODULE_NOT_FOUND; + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + for (PPDMMOD pCur= pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext) + { + if (pCur->eType != enmType) + continue; + + /* The following RTLdrOpen call is a dirty hack to get ring-0 module information. */ + RTLDRMOD hLdrMod = pCur->hLdrMod; + if (hLdrMod == NIL_RTLDRMOD && uPC >= pCur->ImageBase) + { + int rc2 = RTLdrOpen(pCur->szFilename, 0 /*fFlags*/, RTLDRARCH_HOST, &hLdrMod); + if (RT_FAILURE(rc2)) + hLdrMod = NIL_RTLDRMOD; + } + + if ( hLdrMod != NIL_RTLDRMOD + && uPC - pCur->ImageBase < RTLdrSize(hLdrMod)) + { + if (pMod) + *pMod = pCur->ImageBase; + if (pszModName && cchModName) + { + *pszModName = '\0'; + strncat(pszModName, pCur->szName, cchModName); + } + if (pNearSym1) *pNearSym1 = 0; + if (pNearSym2) *pNearSym2 = 0; + if (pszNearSym1) *pszNearSym1 = '\0'; + if (pszNearSym2) *pszNearSym2 = '\0'; + + /* + * Locate the nearest symbols. + */ + QMFEIPARG Args; + Args.uPC = uPC; + Args.pszNearSym1 = pszNearSym1; + Args.cchNearSym1 = cchNearSym1; + Args.offNearSym1 = RTINTPTR_MIN; + Args.pszNearSym2 = pszNearSym2; + Args.cchNearSym2 = cchNearSym2; + Args.offNearSym2 = RTINTPTR_MAX; + + rc = RTLdrEnumSymbols(hLdrMod, RTLDR_ENUM_SYMBOL_FLAGS_ALL, pCur->pvBits, pCur->ImageBase, + pdmR3QueryModFromEIPEnumSymbols, &Args); + if (pNearSym1 && Args.offNearSym1 != RTINTPTR_MIN) + *pNearSym1 = Args.offNearSym1 + uPC; + if (pNearSym2 && Args.offNearSym2 != RTINTPTR_MAX) + *pNearSym2 = Args.offNearSym2 + uPC; + + rc = VINF_SUCCESS; + } + + if (hLdrMod != pCur->hLdrMod && hLdrMod != NIL_RTLDRMOD) + RTLdrClose(hLdrMod); + + if (RT_SUCCESS(rc)) + break; + } + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + return rc; +} + + +/** + * Queries raw-mode context module information from an PC (eip/rip). + * + * This is typically used to locate a crash address. + * + * @returns VBox status code. + * + * @param pVM The cross context VM structure. + * @param uPC The program counter (eip/rip) to locate the module for. + * @param pszModName Where to store the module name. + * @param cchModName Size of the module name buffer. + * @param pMod Base address of the module. + * @param pszNearSym1 Name of the closes symbol from below. + * @param cchNearSym1 Size of the buffer pointed to by pszNearSym1. + * @param pNearSym1 The address of pszNearSym1. + * @param pszNearSym2 Name of the closes symbol from below. + * @param cchNearSym2 Size of the buffer pointed to by pszNearSym2. + * @param pNearSym2 The address of pszNearSym2. + */ +VMMR3_INT_DECL(int) PDMR3LdrQueryRCModFromPC(PVM pVM, RTRCPTR uPC, + char *pszModName, size_t cchModName, PRTRCPTR pMod, + char *pszNearSym1, size_t cchNearSym1, PRTRCPTR pNearSym1, + char *pszNearSym2, size_t cchNearSym2, PRTRCPTR pNearSym2) +{ + RTUINTPTR AddrMod = 0; + RTUINTPTR AddrNear1 = 0; + RTUINTPTR AddrNear2 = 0; + int rc = pdmR3LdrQueryModFromPC(pVM, uPC, PDMMOD_TYPE_RC, + pszModName, cchModName, &AddrMod, + pszNearSym1, cchNearSym1, &AddrNear1, + pszNearSym2, cchNearSym2, &AddrNear2); + if (RT_SUCCESS(rc)) + { + if (pMod) + *pMod = (RTRCPTR)AddrMod; + if (pNearSym1) + *pNearSym1 = (RTRCPTR)AddrNear1; + if (pNearSym2) + *pNearSym2 = (RTRCPTR)AddrNear2; + } + return rc; +} + + +/** + * Queries ring-0 context module information from an PC (eip/rip). + * + * This is typically used to locate a crash address. + * + * @returns VBox status code. + * + * @param pVM The cross context VM structure. + * @param uPC The program counter (eip/rip) to locate the module for. + * @param pszModName Where to store the module name. + * @param cchModName Size of the module name buffer. + * @param pMod Base address of the module. + * @param pszNearSym1 Name of the closes symbol from below. + * @param cchNearSym1 Size of the buffer pointed to by pszNearSym1. + * @param pNearSym1 The address of pszNearSym1. + * @param pszNearSym2 Name of the closes symbol from below. + * @param cchNearSym2 Size of the buffer pointed to by pszNearSym2. Optional. + * @param pNearSym2 The address of pszNearSym2. Optional. + */ +VMMR3_INT_DECL(int) PDMR3LdrQueryR0ModFromPC(PVM pVM, RTR0PTR uPC, + char *pszModName, size_t cchModName, PRTR0PTR pMod, + char *pszNearSym1, size_t cchNearSym1, PRTR0PTR pNearSym1, + char *pszNearSym2, size_t cchNearSym2, PRTR0PTR pNearSym2) +{ + RTUINTPTR AddrMod = 0; + RTUINTPTR AddrNear1 = 0; + RTUINTPTR AddrNear2 = 0; + int rc = pdmR3LdrQueryModFromPC(pVM, uPC, PDMMOD_TYPE_R0, + pszModName, cchModName, &AddrMod, + pszNearSym1, cchNearSym1, &AddrNear1, + pszNearSym2, cchNearSym2, &AddrNear2); + if (RT_SUCCESS(rc)) + { + if (pMod) + *pMod = (RTR0PTR)AddrMod; + if (pNearSym1) + *pNearSym1 = (RTR0PTR)AddrNear1; + if (pNearSym2) + *pNearSym2 = (RTR0PTR)AddrNear2; + } + return rc; +} + + +/** + * Enumerate all PDM modules. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pfnCallback Function to call back for each of the modules. + * @param pvArg User argument. + */ +VMMR3DECL(int) PDMR3LdrEnumModules(PVM pVM, PFNPDMR3ENUM pfnCallback, void *pvArg) +{ + PUVM pUVM = pVM->pUVM; + int rc = VINF_SUCCESS; + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + for (PPDMMOD pCur = pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext) + { + rc = pfnCallback(pVM, + pCur->szFilename, + pCur->szName, + pCur->ImageBase, + pCur->eType == PDMMOD_TYPE_RC ? RTLdrSize(pCur->hLdrMod) : 0, + pCur->eType == PDMMOD_TYPE_RC ? PDMLDRCTX_RAW_MODE + : pCur->eType == PDMMOD_TYPE_R0 ? PDMLDRCTX_RING_0 + : pCur->eType == PDMMOD_TYPE_R3 ? PDMLDRCTX_RING_3 + : PDMLDRCTX_INVALID, + pvArg); + if (RT_FAILURE(rc)) + break; + } + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + return rc; +} + + +/** + * Locates a module. + * + * @returns Pointer to the module if found. + * @param pUVM Pointer to the user mode VM structure. + * @param pszModule The module name. + * @param enmType The module type. + * @param fLazy Lazy loading the module if set. + * @param pszSearchPath Search path for use when lazy loading. + */ +static PPDMMOD pdmR3LdrFindModule(PUVM pUVM, const char *pszModule, PDMMODTYPE enmType, + bool fLazy, const char *pszSearchPath) +{ + RTCritSectEnter(&pUVM->pdm.s.ListCritSect); + for (PPDMMOD pModule = pUVM->pdm.s.pModules; pModule; pModule = pModule->pNext) + if ( pModule->eType == enmType + && !strcmp(pModule->szName, pszModule)) + { + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + return pModule; + } + RTCritSectLeave(&pUVM->pdm.s.ListCritSect); + if (fLazy) + { + switch (enmType) + { +#ifdef VBOX_WITH_RAW_MODE_KEEP + case PDMMOD_TYPE_RC: + { + char *pszFilename = pdmR3FileRC(pszModule, pszSearchPath); + if (pszFilename) + { + int rc = PDMR3LdrLoadRC(pUVM->pVM, pszFilename, pszModule); + RTMemTmpFree(pszFilename); + if (RT_SUCCESS(rc)) + return pdmR3LdrFindModule(pUVM, pszModule, enmType, false, NULL); + } + break; + } +#endif + + case PDMMOD_TYPE_R0: + { + int rc = pdmR3LoadR0U(pUVM, NULL, pszModule, pszSearchPath); + if (RT_SUCCESS(rc)) + return pdmR3LdrFindModule(pUVM, pszModule, enmType, false, NULL); + break; + } + + default: + AssertFailed(); + } + } + return NULL; +} + + +/** + * Resolves a ring-0 or raw-mode context interface. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pvInterface Pointer to the interface structure. The symbol list + * describes the layout. + * @param cbInterface The size of the structure pvInterface is pointing + * to. For bounds checking. + * @param pszModule The module name. If NULL we assume it's the default + * R0 or RC module (@a fRing0OrRC). We'll attempt to + * load the module if it isn't found in the module + * list. + * @param pszSearchPath The module search path. If NULL, search the + * architecture dependent install directory. + * @param pszSymPrefix What to prefix the symbols in the list with. The + * idea is that you define a list that goes with an + * interface (INTERFACE_SYM_LIST) and reuse it with + * each implementation. + * @param pszSymList The symbol list for the interface. This is a + * semi-colon separated list of symbol base names. As + * mentioned above, each is prefixed with @a + * pszSymPrefix before resolving. There are a couple + * of special symbol names that will cause us to skip + * ahead a little bit: + * - U8:whatever, + * - U16:whatever, + * - U32:whatever, + * - U64:whatever, + * - RCPTR:whatever, + * - R3PTR:whatever, + * - R0PTR:whatever, + * - GCPHYS:whatever, + * - HCPHYS:whatever. + * @param fRing0 Set if it's a ring-0 context interface, clear if + * it's raw-mode context interface. + */ +VMMR3_INT_DECL(int) PDMR3LdrGetInterfaceSymbols(PVM pVM, void *pvInterface, size_t cbInterface, + const char *pszModule, const char *pszSearchPath, + const char *pszSymPrefix, const char *pszSymList, + bool fRing0) +{ + bool const fNullRun = !fRing0; + + /* + * Find the module. + */ + int rc = VINF_SUCCESS; + PPDMMOD pModule = NULL; + if (!fNullRun) + pModule = pdmR3LdrFindModule(pVM->pUVM, + pszModule ? pszModule : fRing0 ? "VMMR0.r0" : "VMMRC.rc", + fRing0 ? PDMMOD_TYPE_R0 : PDMMOD_TYPE_RC, + true /*fLazy*/, pszSearchPath); + if (pModule || fNullRun) + { + /* Prep the symbol name. */ + char szSymbol[256]; + size_t const cchSymPrefix = strlen(pszSymPrefix); + AssertReturn(cchSymPrefix + 5 < sizeof(szSymbol), VERR_SYMBOL_NOT_FOUND); + memcpy(szSymbol, pszSymPrefix, cchSymPrefix); + + /* + * Iterate the symbol list. + */ + uint32_t offInterface = 0; + const char *pszCur = pszSymList; + while (pszCur) + { + /* + * Find the end of the current symbol name. + */ + size_t cchSym; + const char *pszNext = strchr(pszCur, ';'); + if (pszNext) + { + cchSym = pszNext - pszCur; + pszNext++; + } + else + cchSym = strlen(pszCur); + AssertBreakStmt(cchSym > 0, rc = VERR_INVALID_PARAMETER); + + /* Is it a skip instruction? */ + const char *pszColon = (const char *)memchr(pszCur, ':', cchSym); + if (pszColon) + { + /* + * String switch on the instruction and execute it, checking + * that we didn't overshoot the interface structure. + */ +#define IS_SKIP_INSTR(szInstr) \ + ( cchSkip == sizeof(szInstr) - 1 \ + && !memcmp(pszCur, szInstr, sizeof(szInstr) - 1) ) + + size_t const cchSkip = pszColon - pszCur; + if (IS_SKIP_INSTR("U8")) + offInterface += sizeof(uint8_t); + else if (IS_SKIP_INSTR("U16")) + offInterface += sizeof(uint16_t); + else if (IS_SKIP_INSTR("U32")) + offInterface += sizeof(uint32_t); + else if (IS_SKIP_INSTR("U64")) + offInterface += sizeof(uint64_t); + else if (IS_SKIP_INSTR("RCPTR")) + offInterface += sizeof(RTRCPTR); + else if (IS_SKIP_INSTR("R3PTR")) + offInterface += sizeof(RTR3PTR); + else if (IS_SKIP_INSTR("R0PTR")) + offInterface += sizeof(RTR0PTR); + else if (IS_SKIP_INSTR("HCPHYS")) + offInterface += sizeof(RTHCPHYS); + else if (IS_SKIP_INSTR("GCPHYS")) + offInterface += sizeof(RTGCPHYS); + else + AssertMsgFailedBreakStmt(("Invalid skip instruction %.*s (prefix=%s)\n", cchSym, pszCur, pszSymPrefix), + rc = VERR_INVALID_PARAMETER); + AssertMsgBreakStmt(offInterface <= cbInterface, + ("off=%#x cb=%#x (sym=%.*s prefix=%s)\n", offInterface, cbInterface, cchSym, pszCur, pszSymPrefix), + rc = VERR_BUFFER_OVERFLOW); +#undef IS_SKIP_INSTR + } + else + { + /* + * Construct the symbol name, get its value, store it and + * advance the interface cursor. + */ + AssertReturn(cchSymPrefix + cchSym < sizeof(szSymbol), VERR_SYMBOL_NOT_FOUND); + memcpy(&szSymbol[cchSymPrefix], pszCur, cchSym); + szSymbol[cchSymPrefix + cchSym] = '\0'; + + if (fRing0) + { + void *pvValue = NULL; + if (!fNullRun) + { + rc = SUPR3GetSymbolR0((void *)(RTR0PTR)pModule->ImageBase, szSymbol, &pvValue); + AssertMsgRCBreak(rc, ("Couldn't find symbol '%s' in module '%s'\n", szSymbol, pModule->szName)); + } + + PRTR0PTR pValue = (PRTR0PTR)((uintptr_t)pvInterface + offInterface); + AssertMsgBreakStmt(offInterface + sizeof(*pValue) <= cbInterface, + ("off=%#x cb=%#x sym=%s\n", offInterface, cbInterface, szSymbol), + rc = VERR_BUFFER_OVERFLOW); + *pValue = (RTR0PTR)pvValue; + Assert((void *)*pValue == pvValue); + offInterface += sizeof(*pValue); + } + else + { + RTUINTPTR Value = 0; + if (!fNullRun) + { + rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, UINT32_MAX, szSymbol, &Value); + AssertMsgRCBreak(rc, ("Couldn't find symbol '%s' in module '%s'\n", szSymbol, pModule->szName)); + } + + PRTRCPTR pValue = (PRTRCPTR)((uintptr_t)pvInterface + offInterface); + AssertMsgBreakStmt(offInterface + sizeof(*pValue) <= cbInterface, + ("off=%#x cb=%#x sym=%s\n", offInterface, cbInterface, szSymbol), + rc = VERR_BUFFER_OVERFLOW); + *pValue = (RTRCPTR)Value; + Assert(*pValue == Value); + offInterface += sizeof(*pValue); + } + } + + /* advance */ + pszCur = pszNext; + } + + } + else + rc = VERR_MODULE_NOT_FOUND; + return rc; +} + |