PDMLdr.cpp revision 806e576dde72d3c00c37226d6142ac520d91779e
/* $Id$ */
/** @file
* PDM - Pluggable Device Manager, module loader.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
//#define PDMLDR_FAKE_MODE
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_PDM_LDR
#include "PDMInternal.h"
#include <limits.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Structure which the user argument of the RTLdrGetBits() callback points to.
* @internal
*/
typedef struct PDMGETIMPORTARGS
{
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static DECLCALLBACK(int) pdmR3GetImportGC(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser);
static char * pdmR3FileGC(const char *pszFile);
static char * pdmR3FileR0(const char *pszFile);
static DECLCALLBACK(int) pdmR3QueryModFromEIPEnumSymbols(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser);
/**
* Loads the VMMR0.r0 module early in the init process.
*
* @returns VBox status code.
* @param pUVM Pointer to the user mode VM structure.
*/
{
}
/**
* Init the module loader part of PDM.
*
* This routine will load the Host Context Ring-0 and Guest
* Context VMM modules.
*
* @returns VBox stutus code.
* @param pUVM Pointer to the user mode VM structure.
* @param pvVMMR0Mod The opqaue returned by PDMR3LdrLoadVMMR0.
*/
{
#ifdef PDMLDR_FAKE_MODE
return VINF_SUCCESS;
#else
/*
* Load the mandatory GC module, the VMMR0.r0 is loaded before VM creation.
*/
#endif
}
/**
* Terminate the module loader part of PDM.
*
* This will unload and free all modules.
*
* @param pVM The VM handle.
*
* @remarks This is normally called twice during termination.
*/
{
/*
* Free the modules.
*/
while (pModule)
{
/* free loader item. */
{
}
/* free bits. */
{
case PDMMOD_TYPE_R0:
{
break;
}
case PDMMOD_TYPE_GC:
case PDMMOD_TYPE_R3:
/* MM will free this memory for us - it's alloc only memory. :-) */
break;
default:
break;
}
}
}
/**
* Applies relocations to GC modules.
*
* This must be done very early in the relocation
* process so that components can resolve GC symbols during relocation.
*
* @param pUVM Pointer to the user mode VM structure.
* @param offDelta Relocation delta relative to old location.
*/
{
/*
* GC Modules.
*/
{
/*
* The relocation have to be done in two passes so imports
* can be correctely resolved. The first pass will update
* the ImageBase saving the current value in OldImageBase.
* The second pass will do the actual relocation.
*/
/* pass 1 */
{
{
}
}
/* pass 2 */
{
{
pdmR3GetImportGC, &Args);
}
}
}
}
/**
* 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!
*/
{
/*
* Validate input.
*/
{
return VERR_INVALID_PARAMETER;
}
/*
* Try lookup the name and see if the module exists.
*/
{
{
return VINF_PDM_ALREADY_LOADED;
return VERR_PDM_MODULE_NAME_CLASH;
}
}
/*
* Allocate the module list node and initialize it.
*/
if (!pModule)
return VERR_NO_MEMORY;
/*
* Load the loader item.
*/
if (VBOX_SUCCESS(rc))
{
return rc;
}
/* Something went wrong, most likely module not found. Don't consider other unlikely errors */
}
/**
* Resolve an external symbol during RTLdrGetBits() of a GC 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) pdmR3GetImportGC(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, unsigned uSymbol, RTUINTPTR *pValue, void *pvUser)
{
/*
* Adjust input.
*/
/*
* Builtin module.
*/
{
int rc = VINF_SUCCESS;
{
if (VBOX_SUCCESS(rc))
}
{
if (VBOX_SUCCESS(rc))
}
else
{
AssertMsg(!pszModule, ("Unknown builtin symbol '%s' for module '%s'!\n", pszSymbol, pModule->szName)); NOREF(pModule);
}
return rc;
}
/*
* Search for module.
*/
while (pCur)
{
&& ( !pszModule
)
{
/* Search for the symbol. */
if (VBOX_SUCCESS(rc))
{
return rc;
}
if (pszModule)
{
return VERR_SYMBOL_NOT_FOUND;
}
}
/* next */
}
return VERR_SYMBOL_NOT_FOUND;
}
/**
* Loads a module into the guest context (i.e. into the Hypervisor memory region).
*
* The external (to PDM) use of this interface is to load VMMGC.gc.
*
* @returns VBox status code.
* @param pVM The VM to load it into.
* @param pszFilename Filename of the module binary.
* @param pszName Module name. Case sensitive and the length is limited!
*/
{
/*
* Validate input.
*/
while (pCur)
{
{
return VERR_PDM_MODULE_NAME_CLASH;
}
/* next */
}
/*
* Find the file if not specified.
*/
if (!pszFilename)
/*
* Allocate the module list node.
*/
if (!pModule)
{
return VERR_NO_MEMORY;
}
("pazName is too long (%d chars) max is %d chars.\n", strlen(pszName), sizeof(pModule->szName) - 1));
/*
* Open the loader item.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Allocate space in the hypervisor.
*/
if (VBOX_SUCCESS(rc))
{
if (VBOX_SUCCESS(rc))
{
/*
* Get relocated image bits.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Insert the module.
*/
{
/* we don't expect this list to be very long, so rather save the tail pointer. */
}
else
return VINF_SUCCESS;
}
}
else
{
}
}
else
}
/* Don't consider VERR_PDM_MODULE_NAME_CLASH and VERR_NO_MEMORY above as these are very unlikely. */
if (VBOX_FAILURE(rc))
return rc;
}
/**
* 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!
*/
{
/*
* Validate input.
*/
while (pCur)
{
{
return VERR_PDM_MODULE_NAME_CLASH;
}
/* next */
}
/*
* Find the file if not specified.
*/
if (!pszFilename)
/*
* Allocate the module list node.
*/
if (!pModule)
{
return VERR_NO_MEMORY;
}
("pazName is too long (%d chars) max is %d chars.\n", strlen(pszName), sizeof(pModule->szName) - 1));
/*
* Ask the support library to load it.
*/
void *pvImageBase;
if (VBOX_SUCCESS(rc))
{
/*
* Insert the module.
*/
{
/* we don't expect this list to be very long, so rather save the tail pointer. */
}
else
return VINF_SUCCESS;
}
/* Don't consider VERR_PDM_MODULE_NAME_CLASH and VERR_NO_MEMORY above as these are very unlikely. */
return rc;
}
/**
* Get the address of a symbol in a given HC ring 3 module.
*
* @returns VBox status code.
* @param pVM VM handle.
* @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.
*/
PDMR3DECL(int) PDMR3GetSymbolR3(PVM pVM, const char *pszModule, const char *pszSymbol, void **ppvValue)
{
/*
* Validate input.
*/
/*
* Find the module.
*/
{
{
int rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, pszSymbol, &Value);
if (VBOX_SUCCESS(rc))
{
}
else
{
if (pszSymbol < (const char*)(void*)0x10000)
AssertMsg(rc, ("Couldn't symbol '%u' in module '%s'\n", (unsigned)(uintptr_t)pszSymbol, pszModule));
else
}
return rc;
}
}
return VERR_SYMBOL_NOT_FOUND;
}
/**
* Get the address of a symbol in a given HC ring 0 module.
*
* @returns VBox status code.
* @param pVM VM handle.
* @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.
*/
PDMR3DECL(int) PDMR3GetSymbolR0(PVM pVM, const char *pszModule, const char *pszSymbol, PRTR0PTR ppvValue)
{
#ifdef PDMLDR_FAKE_MODE
*ppvValue = 0xdeadbeef;
return VINF_SUCCESS;
#else
/*
* Validate input.
*/
if (!pszModule)
pszModule = "VMMR0.r0";
/*
* Find the module.
*/
{
{
if (VBOX_FAILURE(rc))
{
}
return rc;
}
}
return VERR_SYMBOL_NOT_FOUND;
#endif
}
/**
* Same as PDMR3GetSymbolR0 except that the module will be attempted loaded if not found.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param pszModule Module name. If NULL the main R0 module (VMMR0.r0) is assumed.
* @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.
*/
PDMR3DECL(int) PDMR3GetSymbolR0Lazy(PVM pVM, const char *pszModule, const char *pszSymbol, PRTR0PTR ppvValue)
{
#ifdef PDMLDR_FAKE_MODE
*ppvValue = 0xdeadbeef;
return VINF_SUCCESS;
#else
/*
* Since we're lazy, we'll only check if the module is present
* and hand it over to PDMR3GetSymbolR0 when that's done.
*/
if (pszModule)
{
AssertMsgReturn(!strpbrk(pszModule, "/\\:\n\r\t"), ("pszModule=%s\n", pszModule), VERR_INVALID_PARAMETER);
break;
if (!pModule)
{
}
}
#endif
}
/**
* Get the address of a symbol in a given GC module.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param pszModule Module name. If NULL the main R0 module (VMMGC.gc) 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 pGCPtrValue Where to store the symbol value.
*/
PDMR3DECL(int) PDMR3GetSymbolGC(PVM pVM, const char *pszModule, const char *pszSymbol, PRTGCPTR32 pGCPtrValue)
{
#ifdef PDMLDR_FAKE_MODE
*pGCPtrValue = 0xfeedf00d;
return VINF_SUCCESS;
#else
/*
* Validate input.
*/
if (!pszModule)
pszModule = "VMMGC.gc";
/*
* Find the module.
*/
{
{
int rc = RTLdrGetSymbolEx(pModule->hLdrMod, pModule->pvBits, pModule->ImageBase, pszSymbol, &Value);
if (VBOX_SUCCESS(rc))
{
}
else
{
if (pszSymbol < (const char*)(void*)0x10000)
AssertMsg(rc, ("Couldn't symbol '%u' in module '%s'\n", (unsigned)(uintptr_t)pszSymbol, pszModule));
else
}
return rc;
}
}
return VERR_SYMBOL_NOT_FOUND;
#endif
}
/**
* Same as PDMR3GetSymbolGC except that the module will be attempted loaded if not found.
*
* @returns VBox status code.
* @param pVM VM handle.
* @param pszModule Module name. If NULL the main R0 module (VMMGC.gc) 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 pGCPtrValue Where to store the symbol value.
*/
PDMR3DECL(int) PDMR3GetSymbolGCLazy(PVM pVM, const char *pszModule, const char *pszSymbol, PRTGCPTR32 pGCPtrValue)
{
#ifdef PDMLDR_FAKE_MODE
*pGCPtrValue = 0xfeedf00d;
return VINF_SUCCESS;
#else
/*
* Since we're lazy, we'll only check if the module is present
* and hand it over to PDMR3GetSymbolGC when that's done.
*/
if (pszModule)
{
AssertMsgReturn(!strpbrk(pszModule, "/\\:\n\r\t"), ("pszModule=%s\n", pszModule), VERR_INVALID_PARAMETER);
break;
if (!pModule)
{
}
}
#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).
* @todo We'll have this elsewhere than in the root later!
*/
{
}
/**
* 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).
* @todo We'll have this elsewhere than in the root later!
*/
char * pdmR3FileR0(const char *pszFile)
{
}
/**
* Constructs the full filename for a GC 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).
* @todo We'll have this elsewhere than in the root later!
*/
char * pdmR3FileGC(const char *pszFile)
{
}
/**
* 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.
*/
unsigned cchDefaultExt;
/*
* Default extention?
*/
cchDefaultExt = 0;
else
if (cchPath > RTPATH_MAX)
{
AssertMsgFailed(("Path too long!\n"));
return NULL;
}
if (!pszRet)
{
AssertMsgFailed(("Out of temporary memory!\n"));
return NULL;
}
/*
* Construct the filename.
*/
if (cchDefaultExt)
return pszRet;
}
/**
* Worker for pdmR3FileGC(), 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.
* 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!
*/
{
char szPath[RTPATH_MAX];
int rc;
if (!VBOX_SUCCESS(rc))
{
return NULL;
}
}
/** @internal */
typedef struct QMFEIPARG
{
char *pszNearSym1;
unsigned cchNearSym1;
char *pszNearSym2;
unsigned cchNearSym2;
} QMFEIPARG, *PQMFEIPARG;
/**
* Queries module information from an EIP.
*
* This is typically used to locate a crash address.
*
* @returns VBox status code.
* @param pVM VM handle
* @param uEIP EIP to locate.
* @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.
*/
{
int rc = VERR_MODULE_NOT_FOUND;
{
/* Skip anything which isn't in GC. */
continue;
{
if (pMod)
if (pszModName && cchModName)
{
*pszModName = '\0';
}
/*
* Locate the nearest symbols.
*/
rc = VINF_SUCCESS;
break;
}
}
return rc;
}
/**
* 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)
{
if (off <= 0) /* near1 is before or at same location. */
{
{
{
if (pszSymbol)
else
{
char szOrd[32];
}
}
}
}
else /* near2 is after */
{
{
{
if (pszSymbol)
else
{
char szOrd[32];
}
}
}
}
return VINF_SUCCESS;
}
/**
* Enumerate all PDM modules.
*
* @returns VBox status.
* @param pVM VM Handle.
* @param pfnCallback Function to call back for each of the modules.
* @param pvArg User argument.
*/
{
{
if (VBOX_FAILURE(rc))
return rc;
}
return VINF_SUCCESS;
}