PDMLdr.cpp revision 5162728a15dd63b25b1fdbce492fa21fa90ecb1d
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * PDM - Pluggable Device Manager, module loader.
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * Copyright (C) 2006-2007 Oracle Corporation
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * available from http://www.virtualbox.org. This file is free software;
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * you can redistribute it and/or modify it under the terms of the GNU
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * General Public License (GPL) as published by the Free Software
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
0d12c7f9423f2745f8e282523d0930f91bff03b3vboxsync//#define PDMLDR_FAKE_MODE
0d12c7f9423f2745f8e282523d0930f91bff03b3vboxsync/*******************************************************************************
0d12c7f9423f2745f8e282523d0930f91bff03b3vboxsync* Header Files *
0d12c7f9423f2745f8e282523d0930f91bff03b3vboxsync*******************************************************************************/
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync/*******************************************************************************
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync* Structures and Typedefs *
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync*******************************************************************************/
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * Structure which the user argument of the RTLdrGetBits() callback points to.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * @internal
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync/*******************************************************************************
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync* Internal Functions *
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync*******************************************************************************/
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsyncstatic DECLCALLBACK(int) pdmR3GetImportRC(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser);
044af0d1e6474076366759db86f101778c5f20ccvboxsyncstatic int pdmR3LoadR0U(PUVM pUVM, const char *pszFilename, const char *pszName);
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsyncstatic char * pdmR3File(const char *pszFile, const char *pszDefaultExt, bool fShared);
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * Loads the VMMR0.r0 module early in the init process.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * @returns VBox status code.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * @param pUVM Pointer to the user mode VM structure.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync return pdmR3LoadR0U(pUVM, NULL, VMMR0_MAIN_MODULE_NAME);
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * Init the module loader part of PDM.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * This routine will load the Host Context Ring-0 and Guest
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * Context VMM modules.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * @returns VBox stutus code.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * @param pUVM Pointer to the user mode VM structure.
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * @param pvVMMR0Mod The opqaue returned by PDMR3LdrLoadVMMR0.
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync#if defined(PDMLDR_FAKE_MODE) || !defined(VBOX_WITH_RAW_MODE)
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * Load the mandatory GC module, the VMMR0.r0 is loaded before VM creation.
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync return PDMR3LdrLoadRC(pUVM->pVM, NULL, VMMGC_MAIN_MODULE_NAME);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * Terminate the module loader part of PDM.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * This will unload and free all modules.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * @param pVM The VM handle.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * @remarks This is normally called twice during termination.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * Free the modules.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync /* free loader item. */
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync /* free bits. */
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync int rc2 = SUPR3FreeModule((void *)(uintptr_t)pModule->ImageBase);
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync /* MM will free this memory for us - it's alloc only memory. :-) */
ca3da10d05961c339b5180fbd40a54587d6bad35vboxsync * Applies relocations to GC modules.
ca3da10d05961c339b5180fbd40a54587d6bad35vboxsync * This must be done very early in the relocation
2bb12e589d2c280ad042e4e70635ae7224c7eceevboxsync * process so that components can resolve GC symbols during relocation.
2bb12e589d2c280ad042e4e70635ae7224c7eceevboxsync * @param pUVM Pointer to the user mode VM structure.
2bb12e589d2c280ad042e4e70635ae7224c7eceevboxsync * @param offDelta Relocation delta relative to old location.
2bb12e589d2c280ad042e4e70635ae7224c7eceevboxsyncVMMR3DECL(void) PDMR3LdrRelocateU(PUVM pUVM, RTGCINTPTR offDelta)
2bb12e589d2c280ad042e4e70635ae7224c7eceevboxsync LogFlow(("PDMR3LdrRelocate: offDelta=%RGv\n", offDelta));
044af0d1e6474076366759db86f101778c5f20ccvboxsync * GC Modules.
044af0d1e6474076366759db86f101778c5f20ccvboxsync * The relocation have to be done in two passes so imports
044af0d1e6474076366759db86f101778c5f20ccvboxsync * can be correctely resolved. The first pass will update
044af0d1e6474076366759db86f101778c5f20ccvboxsync * the ImageBase saving the current value in OldImageBase.
044af0d1e6474076366759db86f101778c5f20ccvboxsync * The second pass will do the actual relocation.
044af0d1e6474076366759db86f101778c5f20ccvboxsync /* pass 1 */
044af0d1e6474076366759db86f101778c5f20ccvboxsync for (pCur = pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext)
044af0d1e6474076366759db86f101778c5f20ccvboxsync pCur->ImageBase = MMHyperR3ToRC(pUVM->pVM, pCur->pvBits);
044af0d1e6474076366759db86f101778c5f20ccvboxsync /* pass 2 */
044af0d1e6474076366759db86f101778c5f20ccvboxsync for (pCur = pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext)
044af0d1e6474076366759db86f101778c5f20ccvboxsync int rc = RTLdrRelocate(pCur->hLdrMod, pCur->pvBits, pCur->ImageBase, pCur->OldImageBase,
044af0d1e6474076366759db86f101778c5f20ccvboxsync AssertFatalMsgRC(rc, ("RTLdrRelocate failed, rc=%d\n", rc));
044af0d1e6474076366759db86f101778c5f20ccvboxsync DBGFR3ModuleRelocate(pUVM->pVM, pCur->OldImageBase, pCur->ImageBase, RTLdrSize(pCur->hLdrMod),
044af0d1e6474076366759db86f101778c5f20ccvboxsync * Loads a module into the host context ring-3.
ca3da10d05961c339b5180fbd40a54587d6bad35vboxsync * This is used by the driver and device init functions to load modules
ca3da10d05961c339b5180fbd40a54587d6bad35vboxsync * containing the drivers and devices. The function can be extended to
172ae196da38208e5f1e3485715a89f2d53c6880vboxsync * load modules which are not native to the environment we're running in,
044af0d1e6474076366759db86f101778c5f20ccvboxsync * but at the moment this is not required.
172ae196da38208e5f1e3485715a89f2d53c6880vboxsync * No reference counting is kept, since we don't implement any facilities
172ae196da38208e5f1e3485715a89f2d53c6880vboxsync * for unloading the module. But the module will naturally be released
172ae196da38208e5f1e3485715a89f2d53c6880vboxsync * when the VM terminates.
172ae196da38208e5f1e3485715a89f2d53c6880vboxsync * @returns VBox status code.
172ae196da38208e5f1e3485715a89f2d53c6880vboxsync * @param pUVM Pointer to the user mode VM structure.
ca3da10d05961c339b5180fbd40a54587d6bad35vboxsync * @param pszFilename Filename of the module binary.
88acfa6629a7976c0583c1712d2b5b22a87a5121vboxsync * @param pszName Module name. Case sensitive and the length is limited!
044af0d1e6474076366759db86f101778c5f20ccvboxsyncint pdmR3LoadR3U(PUVM pUVM, const char *pszFilename, const char *pszName)
ca3da10d05961c339b5180fbd40a54587d6bad35vboxsync * Validate input.
044af0d1e6474076366759db86f101778c5f20ccvboxsync AssertMsg(pUVM->pVM->pdm.s.offVM, ("bad init order!\n"));
044af0d1e6474076366759db86f101778c5f20ccvboxsync AssertMsgFailed(("Name is too long, cchName=%d pszName='%s'\n", cchName, pszName));
044af0d1e6474076366759db86f101778c5f20ccvboxsync * Try lookup the name and see if the module exists.
044af0d1e6474076366759db86f101778c5f20ccvboxsync for (pCur = pUVM->pdm.s.pModules; pCur; pCur = pCur->pNext)
a1df400bbe9d64aad400442e56eb637019300a5evboxsync AssertMsgRC(rc, ("We've already got a module '%s' loaded!\n", pszName));
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * Allocate the module list node and initialize it.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync size_t cchSuff = RTPathHaveExt(pszFilename) ? 0 : strlen(pszSuff);
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync PPDMMOD pModule = (PPDMMOD)RTMemAllocZ(RT_OFFSETOF(PDMMOD, szFilename[cchFilename + cchSuff + 1]));
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync memcpy(pModule->szName, pszName, cchName); /* memory is zero'ed, no need to copy terminator :-) */
a1df400bbe9d64aad400442e56eb637019300a5evboxsync memcpy(pModule->szFilename, pszFilename, cchFilename);
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync memcpy(&pModule->szFilename[cchFilename], pszSuff, cchSuff);
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * Load the loader item.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync rc = SUPR3HardenedVerifyFile(pModule->szFilename, "pdmR3LoadR3U", NULL);
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync rc = RTLdrLoad(pModule->szFilename, &pModule->hLdrMod);
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync /* Something went wrong, most likely module not found. Don't consider other unlikely errors */
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync rc = VMSetError(pUVM->pVM, rc, RT_SRC_POS, N_("Unable to load R3 module %s (%s)"), pModule->szFilename, pszName);
ca3da10d05961c339b5180fbd40a54587d6bad35vboxsync * Resolve an external symbol during RTLdrGetBits() of a RC module.
ca3da10d05961c339b5180fbd40a54587d6bad35vboxsync * @returns VBox status code.
88acfa6629a7976c0583c1712d2b5b22a87a5121vboxsync * @param hLdrMod The loader module handle.
ca3da10d05961c339b5180fbd40a54587d6bad35vboxsync * @param pszModule Module name.
ca3da10d05961c339b5180fbd40a54587d6bad35vboxsync * @param pszSymbol Symbol name, NULL if uSymbol should be used.
044af0d1e6474076366759db86f101778c5f20ccvboxsync * @param uSymbol Symbol ordinal, ~0 if pszSymbol should be used.
044af0d1e6474076366759db86f101778c5f20ccvboxsync * @param pValue Where to store the symbol value (address).
044af0d1e6474076366759db86f101778c5f20ccvboxsync * @param pvUser User argument.
044af0d1e6474076366759db86f101778c5f20ccvboxsyncstatic DECLCALLBACK(int) pdmR3GetImportRC(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser)
044af0d1e6474076366759db86f101778c5f20ccvboxsync PPDMMOD pModule = ((PPDMGETIMPORTARGS)pvUser)->pModule;
044af0d1e6474076366759db86f101778c5f20ccvboxsync * Adjust input.
044af0d1e6474076366759db86f101778c5f20ccvboxsync * Builtin module.
044af0d1e6474076366759db86f101778c5f20ccvboxsync if (!pszModule || !strcmp(pszModule, "VMMGCBuiltin.gc"))
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync AssertMsg(!pszModule, ("Unknown builtin symbol '%s' for module '%s'!\n", pszSymbol, pModule->szName)); NOREF(pModule);
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * Search for module.
ae83ea510012552d3286d67b41abddf158ca6654vboxsync /* Search for the symbol. */
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync int rc = RTLdrGetSymbolEx(pCur->hLdrMod, pCur->pvBits, pCur->ImageBase, pszSymbol, pValue);
c0e27f622f9bd6d9e77d2d959aab71d69dabf0d3vboxsync AssertMsg(*pValue - pCur->ImageBase < RTLdrSize(pCur->hLdrMod),
c0e27f622f9bd6d9e77d2d959aab71d69dabf0d3vboxsync (RTRCPTR)(pCur->ImageBase + RTLdrSize(pCur->hLdrMod) - 1),
172ae196da38208e5f1e3485715a89f2d53c6880vboxsync AssertMsgFailed(("Couldn't find symbol '%s' in module '%s'!\n", pszSymbol, pszModule));
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync LogRel(("PDMLdr: Couldn't find symbol '%s' in module '%s'!\n", pszSymbol, pszModule));
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync AssertMsgFailed(("Couldn't find module '%s' for resolving symbol '%s'!\n", pszModule, pszSymbol));
c0e27f622f9bd6d9e77d2d959aab71d69dabf0d3vboxsync * Loads a module into the guest context (i.e. into the Hypervisor memory region).
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * @returns VBox status code.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * @param pVM The VM to load it into.
590bfe12ce22cd3716448fbb9f4dc51664bfe5e2vboxsync * @param pszFilename Filename of the module binary.
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsync * @param pszName Module name. Case sensitive and the length is limited!
3a4a6501d0ccd629d9951b644d380c7bb2d46086vboxsyncVMMR3DECL(int) PDMR3LdrLoadRC(PVM pVM, const char *pszFilename, const char *pszName)
while (pCur)
return VERR_PDM_MODULE_NAME_CLASH;
if (!pszFilename)
if (!pModule)
return VERR_NO_MEMORY;
("pazName is too long (%d chars) max is %d chars.\n", strlen(pszName), sizeof(pModule->szName) - 1));
if (paPages)
return VINF_SUCCESS;
/* Don't consider VERR_PDM_MODULE_NAME_CLASH and VERR_NO_MEMORY above as these are very unlikely. */
return rc;
while (pCur)
return VERR_PDM_MODULE_NAME_CLASH;
if (!pszFilename)
if (!pModule)
return VERR_NO_MEMORY;
("pazName is too long (%d chars) max is %d chars.\n", strlen(pszName), sizeof(pModule->szName) - 1));
void *pvImageBase;
return VINF_SUCCESS;
/* Don't consider VERR_PDM_MODULE_NAME_CLASH and VERR_NO_MEMORY above as these are very unlikely. */
return rc;
VMMR3DECL(int) PDMR3LdrGetSymbolR3(PVM pVM, const char *pszModule, const char *pszSymbol, void **ppvValue)
int rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, pszSymbol, &Value);
AssertMsg(rc, ("Couldn't symbol '%u' in module '%s'\n", (unsigned)(uintptr_t)pszSymbol, pszModule));
return rc;
return VERR_SYMBOL_NOT_FOUND;
VMMR3DECL(int) PDMR3LdrGetSymbolR0(PVM pVM, const char *pszModule, const char *pszSymbol, PRTR0PTR ppvValue)
#ifdef PDMLDR_FAKE_MODE
return VINF_SUCCESS;
if (!pszModule)
return rc;
return VERR_SYMBOL_NOT_FOUND;
VMMR3DECL(int) PDMR3LdrGetSymbolR0Lazy(PVM pVM, const char *pszModule, const char *pszSymbol, PRTR0PTR ppvValue)
#ifdef PDMLDR_FAKE_MODE
return VINF_SUCCESS;
if (pszModule)
AssertMsgReturn(!strpbrk(pszModule, "/\\:\n\r\t"), ("pszModule=%s\n", pszModule), VERR_INVALID_PARAMETER);
if (!pModule)
VMMR3DECL(int) PDMR3LdrGetSymbolRC(PVM pVM, const char *pszModule, const char *pszSymbol, PRTRCPTR pRCPtrValue)
return VINF_SUCCESS;
if (!pszModule)
int rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, pszSymbol, &Value);
AssertMsg(rc, ("Couldn't symbol '%u' in module '%s'\n", (unsigned)(uintptr_t)pszSymbol, pszModule));
return rc;
return VERR_SYMBOL_NOT_FOUND;
VMMR3DECL(int) PDMR3LdrGetSymbolRCLazy(PVM pVM, const char *pszModule, const char *pszSymbol, PRTRCPTR pRCPtrValue)
return VINF_SUCCESS;
if (pszModule)
AssertMsgReturn(!strpbrk(pszModule, "/\\:\n\r\t"), ("pszModule=%s\n", pszModule), VERR_INVALID_PARAMETER);
if (!pModule)
cchDefaultExt = 0;
if (cchDefaultExt)
return pszRet;
* search in the private directory (/usr/lib/virtualbox on Unix).
int rc;
return NULL;
typedef struct QMFEIPARG
char *pszNearSym1;
char *pszNearSym2;
static DECLCALLBACK(int) pdmR3QueryModFromEIPEnumSymbols(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser)
if (pszSymbol)
if (pszSymbol)
return VINF_SUCCESS;
if (pMod)
return rc;
if (pMod)
if (pNearSym1)
if (pNearSym2)
return rc;
if (pMod)
if (pNearSym1)
if (pNearSym2)
return rc;
pvArg);
return rc;
return pModule;
if (fLazy)
switch (enmType)
#ifdef VBOX_WITH_RAW_MODE
case PDMMOD_TYPE_RC:
if (pszFilename)
case PDMMOD_TYPE_R0:
AssertFailed();
return NULL;
if (pModule)
while (pszCur)
if (pszNext)
pszNext++;
if (pszColon)
AssertMsgFailedBreakStmt(("Invalid skip instruction %.*s (prefix=%s)\n", cchSym, pszCur, pszSymPrefix),
if (fRing0)
void *pvValue;
return rc;