/** * Changes an alias to point to a new address space. * * Not all the aliases can be changed, currently it's only DBGF_AS_GLOBAL * and DBGF_AS_KERNEL. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param hAlias The alias to change. * @param hAliasFor The address space hAlias should be an alias for. This * can be an alias. The caller's reference to this address * space will NOT be consumed. */ VMMR3DECL(int) DBGFR3AsSetAlias(PUVM pUVM, RTDBGAS hAlias, RTDBGAS hAliasFor) { /* * Input validation. */ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); AssertMsgReturn(DBGF_AS_IS_ALIAS(hAlias), ("%p\n", hAlias), VERR_INVALID_PARAMETER); AssertMsgReturn(!DBGF_AS_IS_FIXED_ALIAS(hAlias), ("%p\n", hAlias), VERR_INVALID_PARAMETER); RTDBGAS hRealAliasFor = DBGFR3AsResolveAndRetain(pUVM, hAliasFor); if (hRealAliasFor == NIL_RTDBGAS) return VERR_INVALID_HANDLE; /* * Make sure the handle has is already in the database. */ int rc = VERR_NOT_FOUND; DBGF_AS_DB_LOCK_WRITE(pUVM); if (RTAvlPVGet(&pUVM->dbgf.s.AsHandleTree, hRealAliasFor)) { /* * Update the alias table and release the current address space. */ RTDBGAS hAsOld; ASMAtomicXchgHandle(&pUVM->dbgf.s.ahAsAliases[DBGF_AS_ALIAS_2_INDEX(hAlias)], hRealAliasFor, &hAsOld); uint32_t cRefs = RTDbgAsRelease(hAsOld); Assert(cRefs > 0); Assert(cRefs != UINT32_MAX); NOREF(cRefs); rc = VINF_SUCCESS; } DBGF_AS_DB_UNLOCK_WRITE(pUVM); return rc; }
/** * Load symbols from an executable module into the specified address space. * * If an module exist at the specified address it will be replaced by this * call, otherwise a new module is created. * * @returns VBox status code. * * @param pUVM The user mode VM handle. * @param hDbgAs The address space. * @param pszFilename The filename of the executable module. * @param pszModName The module name. If NULL, then then the file name * base is used (no extension or nothing). * @param enmArch The desired architecture, use RTLDRARCH_WHATEVER if * it's not relevant or known. * @param pModAddress The load address of the module. * @param iModSeg The segment to load, pass NIL_RTDBGSEGIDX to load * the whole image. * @param fFlags Flags reserved for future extensions, must be 0. */ VMMR3DECL(int) DBGFR3AsLoadImage(PUVM pUVM, RTDBGAS hDbgAs, const char *pszFilename, const char *pszModName, RTLDRARCH enmArch, PCDBGFADDRESS pModAddress, RTDBGSEGIDX iModSeg, uint32_t fFlags) { /* * Validate input */ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); AssertReturn(*pszFilename, VERR_INVALID_PARAMETER); AssertReturn(DBGFR3AddrIsValid(pUVM, pModAddress), VERR_INVALID_PARAMETER); AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); RTDBGAS hRealAS = DBGFR3AsResolveAndRetain(pUVM, hDbgAs); if (hRealAS == NIL_RTDBGAS) return VERR_INVALID_HANDLE; RTDBGMOD hDbgMod; int rc = RTDbgModCreateFromImage(&hDbgMod, pszFilename, pszModName, enmArch, pUVM->dbgf.s.hDbgCfg); if (RT_SUCCESS(rc)) { rc = DBGFR3AsLinkModule(pUVM, hRealAS, hDbgMod, pModAddress, iModSeg, 0); if (RT_FAILURE(rc)) RTDbgModRelease(hDbgMod); } RTDbgAsRelease(hRealAS); return rc; }
VMMR3DECL(int) DBGFR3AsLineByAddr(PUVM pUVM, RTDBGAS hDbgAs, PCDBGFADDRESS pAddress, PRTGCINTPTR poffDisp, PRTDBGLINE pLine, PRTDBGMOD phMod) { /* * Implement the special address space aliases the lazy way. */ if (hDbgAs == DBGF_AS_RC_AND_GC_GLOBAL) { int rc = DBGFR3AsLineByAddr(pUVM, DBGF_AS_RC, pAddress, poffDisp, pLine, phMod); if (RT_FAILURE(rc)) rc = DBGFR3AsLineByAddr(pUVM, DBGF_AS_GLOBAL, pAddress, poffDisp, pLine, phMod); return rc; } /* * Input validation. */ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); AssertReturn(DBGFR3AddrIsValid(pUVM, pAddress), VERR_INVALID_PARAMETER); AssertPtrNullReturn(poffDisp, VERR_INVALID_POINTER); AssertPtrReturn(pLine, VERR_INVALID_POINTER); AssertPtrNullReturn(phMod, VERR_INVALID_POINTER); if (poffDisp) *poffDisp = 0; if (phMod) *phMod = NIL_RTDBGMOD; RTDBGAS hRealAS = DBGFR3AsResolveAndRetain(pUVM, hDbgAs); if (hRealAS == NIL_RTDBGAS) return VERR_INVALID_HANDLE; /* * Do the lookup. */ return RTDbgAsLineByAddr(hRealAS, pAddress->FlatPtr, poffDisp, pLine, phMod); }
/** * Query a symbol by name. * * The symbol can be prefixed by a module name pattern to scope the search. The * pattern is a simple string pattern with '*' and '?' as wild chars. See * RTStrSimplePatternMatch(). * * @returns VBox status code. See RTDbgAsSymbolByAddr. * * @param pUVM The user mode VM handle. * @param hDbgAs The address space handle. * @param pszSymbol The symbol to search for, maybe prefixed by a * module pattern. * @param pSymbol Where to return the symbol information. * The returned symbol name will be prefixed by * the module name as far as space allows. * @param phMod Where to return the module handle. Optional. */ VMMR3DECL(int) DBGFR3AsSymbolByName(PUVM pUVM, RTDBGAS hDbgAs, const char *pszSymbol, PRTDBGSYMBOL pSymbol, PRTDBGMOD phMod) { /* * Implement the special address space aliases the lazy way. */ if (hDbgAs == DBGF_AS_RC_AND_GC_GLOBAL) { int rc = DBGFR3AsSymbolByName(pUVM, DBGF_AS_RC, pszSymbol, pSymbol, phMod); if (RT_FAILURE(rc)) rc = DBGFR3AsSymbolByName(pUVM, DBGF_AS_GLOBAL, pszSymbol, pSymbol, phMod); return rc; } /* * Input validation. */ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); AssertPtrReturn(pSymbol, VERR_INVALID_POINTER); AssertPtrNullReturn(phMod, VERR_INVALID_POINTER); if (phMod) *phMod = NIL_RTDBGMOD; RTDBGAS hRealAS = DBGFR3AsResolveAndRetain(pUVM, hDbgAs); if (hRealAS == NIL_RTDBGAS) return VERR_INVALID_HANDLE; /* * Do the lookup. */ RTDBGMOD hMod; int rc = RTDbgAsSymbolByName(hRealAS, pszSymbol, pSymbol, &hMod); if (RT_SUCCESS(rc)) { dbgfR3AsSymbolJoinNames(pSymbol, hMod); if (!phMod) RTDbgModRelease(hMod); } /* Temporary conversion. */ else if (hDbgAs == DBGF_AS_GLOBAL) { DBGFSYMBOL DbgfSym; rc = DBGFR3SymbolByName(pUVM->pVM, pszSymbol, &DbgfSym); if (RT_SUCCESS(rc)) dbgfR3AsSymbolConvert(pSymbol, &DbgfSym); } return rc; }
/** * Query a symbol by address. * * The returned symbol is the one we consider closes to the specified address. * * @returns VBox status code. See RTDbgAsSymbolByAddr. * * @param pUVM The user mode VM handle. * @param hDbgAs The address space handle. * @param pAddress The address to lookup. * @param fFlags One of the RTDBGSYMADDR_FLAGS_XXX flags. * @param poffDisp Where to return the distance between the returned * symbol and pAddress. Optional. * @param pSymbol Where to return the symbol information. The returned * symbol name will be prefixed by the module name as * far as space allows. * @param phMod Where to return the module handle. Optional. */ VMMR3DECL(int) DBGFR3AsSymbolByAddr(PUVM pUVM, RTDBGAS hDbgAs, PCDBGFADDRESS pAddress, uint32_t fFlags, PRTGCINTPTR poffDisp, PRTDBGSYMBOL pSymbol, PRTDBGMOD phMod) { /* * Implement the special address space aliases the lazy way. */ if (hDbgAs == DBGF_AS_RC_AND_GC_GLOBAL) { int rc = DBGFR3AsSymbolByAddr(pUVM, DBGF_AS_RC, pAddress, fFlags, poffDisp, pSymbol, phMod); if (RT_FAILURE(rc)) rc = DBGFR3AsSymbolByAddr(pUVM, DBGF_AS_GLOBAL, pAddress, fFlags, poffDisp, pSymbol, phMod); return rc; } /* * Input validation. */ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); AssertReturn(DBGFR3AddrIsValid(pUVM, pAddress), VERR_INVALID_PARAMETER); AssertPtrNullReturn(poffDisp, VERR_INVALID_POINTER); AssertPtrReturn(pSymbol, VERR_INVALID_POINTER); AssertPtrNullReturn(phMod, VERR_INVALID_POINTER); if (poffDisp) *poffDisp = 0; if (phMod) *phMod = NIL_RTDBGMOD; RTDBGAS hRealAS = DBGFR3AsResolveAndRetain(pUVM, hDbgAs); if (hRealAS == NIL_RTDBGAS) return VERR_INVALID_HANDLE; /* * Do the lookup. */ RTDBGMOD hMod; int rc = RTDbgAsSymbolByAddr(hRealAS, pAddress->FlatPtr, fFlags, poffDisp, pSymbol, &hMod); if (RT_SUCCESS(rc)) { dbgfR3AsSymbolJoinNames(pSymbol, hMod); if (!phMod) RTDbgModRelease(hMod); } return rc; }
/** * Load symbols from a map file into a module at the specified address space. * * If an module exist at the specified address it will be replaced by this * call, otherwise a new module is created. * * @returns VBox status code. * * @param pUVM The user mode VM handle. * @param hDbgAs The address space. * @param pszFilename The map file. * @param pszModName The module name. If NULL, then then the file name * base is used (no extension or nothing). * @param pModAddress The load address of the module. * @param iModSeg The segment to load, pass NIL_RTDBGSEGIDX to load * the whole image. * @param uSubtrahend Value to to subtract from the symbols in the map * file. This is useful for the linux System.map and * /proc/kallsyms. * @param fFlags Flags reserved for future extensions, must be 0. */ VMMR3DECL(int) DBGFR3AsLoadMap(PUVM pUVM, RTDBGAS hDbgAs, const char *pszFilename, const char *pszModName, PCDBGFADDRESS pModAddress, RTDBGSEGIDX iModSeg, RTGCUINTPTR uSubtrahend, uint32_t fFlags) { /* * Validate input */ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); AssertReturn(*pszFilename, VERR_INVALID_PARAMETER); AssertReturn(DBGFR3AddrIsValid(pUVM, pModAddress), VERR_INVALID_PARAMETER); AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); RTDBGAS hRealAS = DBGFR3AsResolveAndRetain(pUVM, hDbgAs); if (hRealAS == NIL_RTDBGAS) return VERR_INVALID_HANDLE; /* * Do the work. */ DBGFR3ASLOADOPENDATA Data; Data.pszModName = pszModName; Data.uSubtrahend = uSubtrahend; Data.fFlags = 0; Data.hMod = NIL_RTDBGMOD; int rc = dbgfR3AsSearchCfgPath(pUVM, pszFilename, "MapPath", dbgfR3AsLoadMapOpen, &Data); if (RT_FAILURE(rc)) rc = dbgfR3AsSearchEnvPath(pszFilename, "VBOXDBG_MAP_PATH", dbgfR3AsLoadMapOpen, &Data); if (RT_FAILURE(rc)) rc = dbgfR3AsSearchCfgPath(pUVM, pszFilename, "Path", dbgfR3AsLoadMapOpen, &Data); if (RT_FAILURE(rc)) rc = dbgfR3AsSearchEnvPath(pszFilename, "VBOXDBG_PATH", dbgfR3AsLoadMapOpen, &Data); if (RT_SUCCESS(rc)) { rc = DBGFR3AsLinkModule(pUVM, hRealAS, Data.hMod, pModAddress, iModSeg, 0); if (RT_FAILURE(rc)) RTDbgModRelease(Data.hMod); } RTDbgAsRelease(hRealAS); return rc; }
/** * Wrapper around RTDbgAsModuleLink, RTDbgAsModuleLinkSeg and DBGFR3AsResolve. * * @returns VBox status code. * @param pVM Pointer to the VM. * @param hDbgAs The address space handle. * @param hMod The module handle. * @param pModAddress The link address. * @param iModSeg The segment to link, NIL_RTDBGSEGIDX for the entire image. * @param fFlags Flags to pass to the link functions, see RTDBGASLINK_FLAGS_*. */ VMMR3DECL(int) DBGFR3AsLinkModule(PVM pVM, RTDBGAS hDbgAs, RTDBGMOD hMod, PCDBGFADDRESS pModAddress, RTDBGSEGIDX iModSeg, uint32_t fFlags) { /* * Input validation. */ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertReturn(DBGFR3AddrIsValid(pVM, pModAddress), VERR_INVALID_PARAMETER); RTDBGAS hRealAS = DBGFR3AsResolveAndRetain(pVM, hDbgAs); if (hRealAS == NIL_RTDBGAS) return VERR_INVALID_HANDLE; /* * Do the job. */ int rc; if (iModSeg == NIL_RTDBGSEGIDX) rc = RTDbgAsModuleLink(hRealAS, hMod, pModAddress->FlatPtr, fFlags); else rc = RTDbgAsModuleLinkSeg(hRealAS, hMod, iModSeg, pModAddress->FlatPtr, fFlags); RTDbgAsRelease(hRealAS); return rc; }
/** * Wrapper around RTDbgAsModuleByName and RTDbgAsModuleUnlink. * * Unlinks all mappings matching the given module name. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param hDbgAs The address space handle. * @param pszModName The name of the module to unlink. */ VMMR3DECL(int) DBGFR3AsUnlinkModuleByName(PUVM pUVM, RTDBGAS hDbgAs, const char *pszModName) { /* * Input validation. */ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); RTDBGAS hRealAS = DBGFR3AsResolveAndRetain(pUVM, hDbgAs); if (hRealAS == NIL_RTDBGAS) return VERR_INVALID_HANDLE; /* * Do the job. */ RTDBGMOD hMod; int rc = RTDbgAsModuleByName(hRealAS, pszModName, 0, &hMod); if (RT_SUCCESS(rc)) { for (;;) { rc = RTDbgAsModuleUnlink(hRealAS, hMod); RTDbgModRelease(hMod); if (RT_FAILURE(rc)) break; rc = RTDbgAsModuleByName(hRealAS, pszModName, 0, &hMod); if (RT_FAILURE_NP(rc)) { if (rc == VERR_NOT_FOUND) rc = VINF_SUCCESS; break; } } } RTDbgAsRelease(hRealAS); return rc; }
/** * Query a symbol by address. * * The returned symbol is the one we consider closes to the specified address. * * @returns VBox status code. See RTDbgAsSymbolByAddr. * * @param pUVM The user mode VM handle. * @param hDbgAs The address space handle. * @param pAddress The address to lookup. * @param poffDisp Where to return the distance between the returned * symbol and pAddress. Optional. * @param pSymbol Where to return the symbol information. The returned * symbol name will be prefixed by the module name as * far as space allows. * @param phMod Where to return the module handle. Optional. */ VMMR3DECL(int) DBGFR3AsSymbolByAddr(PUVM pUVM, RTDBGAS hDbgAs, PCDBGFADDRESS pAddress, PRTGCINTPTR poffDisp, PRTDBGSYMBOL pSymbol, PRTDBGMOD phMod) { /* * Implement the special address space aliases the lazy way. */ if (hDbgAs == DBGF_AS_RC_AND_GC_GLOBAL) { int rc = DBGFR3AsSymbolByAddr(pUVM, DBGF_AS_RC, pAddress, poffDisp, pSymbol, phMod); if (RT_FAILURE(rc)) rc = DBGFR3AsSymbolByAddr(pUVM, DBGF_AS_GLOBAL, pAddress, poffDisp, pSymbol, phMod); return rc; } /* * Input validation. */ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); AssertReturn(DBGFR3AddrIsValid(pUVM, pAddress), VERR_INVALID_PARAMETER); AssertPtrNullReturn(poffDisp, VERR_INVALID_POINTER); AssertPtrReturn(pSymbol, VERR_INVALID_POINTER); AssertPtrNullReturn(phMod, VERR_INVALID_POINTER); if (poffDisp) *poffDisp = 0; if (phMod) *phMod = NIL_RTDBGMOD; RTDBGAS hRealAS = DBGFR3AsResolveAndRetain(pUVM, hDbgAs); if (hRealAS == NIL_RTDBGAS) return VERR_INVALID_HANDLE; /* * Do the lookup. */ RTDBGMOD hMod; int rc = RTDbgAsSymbolByAddr(hRealAS, pAddress->FlatPtr, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL, poffDisp, pSymbol, &hMod); if (RT_SUCCESS(rc)) { dbgfR3AsSymbolJoinNames(pSymbol, hMod); if (!phMod) RTDbgModRelease(hMod); } /* Temporary conversions. */ else if (hDbgAs == DBGF_AS_GLOBAL) { DBGFSYMBOL DbgfSym; rc = DBGFR3SymbolByAddr(pUVM->pVM, pAddress->FlatPtr, poffDisp, &DbgfSym); if (RT_SUCCESS(rc)) dbgfR3AsSymbolConvert(pSymbol, &DbgfSym); } else if (hDbgAs == DBGF_AS_R0) { RTR0PTR R0PtrMod; char szNearSym[260]; RTR0PTR R0PtrNearSym; RTR0PTR R0PtrNearSym2; VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE); rc = PDMR3LdrQueryR0ModFromPC(pUVM->pVM, pAddress->FlatPtr, pSymbol->szName, sizeof(pSymbol->szName) / 2, &R0PtrMod, &szNearSym[0], sizeof(szNearSym), &R0PtrNearSym, NULL, 0, &R0PtrNearSym2); if (RT_SUCCESS(rc)) { pSymbol->offSeg = pSymbol->Value = R0PtrNearSym; pSymbol->cb = R0PtrNearSym2 > R0PtrNearSym ? R0PtrNearSym2 - R0PtrNearSym : 0; pSymbol->iSeg = 0; pSymbol->fFlags = 0; pSymbol->iOrdinal = UINT32_MAX; size_t offName = strlen(pSymbol->szName); pSymbol->szName[offName++] = '!'; size_t cchNearSym = strlen(szNearSym); if (cchNearSym + offName >= sizeof(pSymbol->szName)) cchNearSym = sizeof(pSymbol->szName) - offName - 1; strncpy(&pSymbol->szName[offName], szNearSym, cchNearSym); pSymbol->szName[offName + cchNearSym] = '\0'; if (poffDisp) *poffDisp = pAddress->FlatPtr - pSymbol->Value; } } return rc; }
/** * Loads the kernel symbols from the kallsyms tables. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param pThis The Linux digger data. */ static int dbgDiggerLinuxLoadKernelSymbols(PUVM pUVM, PDBGDIGGERLINUX pThis) { /* * Allocate memory for temporary table copies, reading the tables as we go. */ uint32_t const cbGuestAddr = pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t); void *pvAddresses = RTMemAllocZ(pThis->cKernelSymbols * cbGuestAddr); int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelAddresses, pvAddresses, pThis->cKernelSymbols * cbGuestAddr); if (RT_SUCCESS(rc)) { uint8_t *pbNames = (uint8_t *)RTMemAllocZ(pThis->cbKernelNames); rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelNames, pbNames, pThis->cbKernelNames); if (RT_SUCCESS(rc)) { char *pszzTokens = (char *)RTMemAllocZ(pThis->cbKernelTokenTable); rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelTokenTable, pszzTokens, pThis->cbKernelTokenTable); if (RT_SUCCESS(rc)) { uint16_t *paoffTokens = (uint16_t *)RTMemAllocZ(256 * sizeof(uint16_t)); rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelTokenIndex, paoffTokens, 256 * sizeof(uint16_t)); if (RT_SUCCESS(rc)) { /* * Figure out the kernel start and end. */ RTGCUINTPTR uKernelStart = pThis->AddrKernelAddresses.FlatPtr; RTGCUINTPTR uKernelEnd = pThis->AddrKernelTokenIndex.FlatPtr + 256 * sizeof(uint16_t); uint32_t i; if (cbGuestAddr == sizeof(uint64_t)) { uint64_t *pauAddrs = (uint64_t *)pvAddresses; for (i = 0; i < pThis->cKernelSymbols; i++) if ( pauAddrs[i] < uKernelStart && LNX64_VALID_ADDRESS(pauAddrs[i]) && uKernelStart - pauAddrs[i] < LNX_MAX_KERNEL_SIZE) uKernelStart = pauAddrs[i]; for (i = pThis->cKernelSymbols - 1; i > 0; i--) if ( pauAddrs[i] > uKernelEnd && LNX64_VALID_ADDRESS(pauAddrs[i]) && pauAddrs[i] - uKernelEnd < LNX_MAX_KERNEL_SIZE) uKernelEnd = pauAddrs[i]; } else { uint32_t *pauAddrs = (uint32_t *)pvAddresses; for (i = 0; i < pThis->cKernelSymbols; i++) if ( pauAddrs[i] < uKernelStart && LNX32_VALID_ADDRESS(pauAddrs[i]) && uKernelStart - pauAddrs[i] < LNX_MAX_KERNEL_SIZE) uKernelStart = pauAddrs[i]; for (i = pThis->cKernelSymbols - 1; i > 0; i--) if ( pauAddrs[i] > uKernelEnd && LNX32_VALID_ADDRESS(pauAddrs[i]) && pauAddrs[i] - uKernelEnd < LNX_MAX_KERNEL_SIZE) uKernelEnd = pauAddrs[i]; } RTGCUINTPTR cbKernel = uKernelEnd - uKernelStart; pThis->cbKernel = (uint32_t)cbKernel; DBGFR3AddrFromFlat(pUVM, &pThis->AddrKernelBase, uKernelStart); Log(("dbgDiggerLinuxLoadKernelSymbols: uKernelStart=%RGv cbKernel=%#x\n", uKernelStart, cbKernel)); /* * Create a module for the kernel. */ RTDBGMOD hMod; rc = RTDbgModCreate(&hMod, "vmlinux", cbKernel, 0 /*fFlags*/); if (RT_SUCCESS(rc)) { rc = RTDbgModSetTag(hMod, DIG_LNX_MOD_TAG); AssertRC(rc); rc = VINF_SUCCESS; /* * Enumerate the symbols. */ uint8_t const *pbCurAddr = (uint8_t const *)pvAddresses; uint32_t offName = 0; uint32_t cLeft = pThis->cKernelSymbols; while (cLeft-- > 0 && RT_SUCCESS(rc)) { /* Decode the symbol name first. */ if (RT_LIKELY(offName < pThis->cbKernelNames)) { uint8_t cbName = pbNames[offName++]; if (RT_LIKELY(offName + cbName <= pThis->cbKernelNames)) { char szSymbol[4096]; uint32_t offSymbol = 0; while (cbName-- > 0) { uint8_t bEnc = pbNames[offName++]; uint16_t offToken = paoffTokens[bEnc]; if (RT_LIKELY(offToken < pThis->cbKernelTokenTable)) { const char *pszToken = &pszzTokens[offToken]; char ch; while ((ch = *pszToken++) != '\0') if (offSymbol < sizeof(szSymbol) - 1) szSymbol[offSymbol++] = ch; } else { rc = VERR_INVALID_UTF8_ENCODING; break; } } szSymbol[offSymbol < sizeof(szSymbol) ? offSymbol : sizeof(szSymbol) - 1] = '\0'; /* The address. */ RTGCUINTPTR uSymAddr = cbGuestAddr == sizeof(uint64_t) ? *(uint64_t *)pbCurAddr : *(uint32_t *)pbCurAddr; pbCurAddr += cbGuestAddr; /* Add it without the type char. */ if (uSymAddr - uKernelStart <= cbKernel) { rc = RTDbgModSymbolAdd(hMod, &szSymbol[1], RTDBGSEGIDX_RVA, uSymAddr - uKernelStart, 0 /*cb*/, 0 /*fFlags*/, NULL); if (RT_FAILURE(rc)) { if ( rc == VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE || rc == VERR_DBG_INVALID_RVA || rc == VERR_DBG_ADDRESS_CONFLICT || rc == VERR_DBG_DUPLICATE_SYMBOL) { Log2(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc (ignored)\n", szSymbol, rc)); rc = VINF_SUCCESS; } else Log(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc\n", szSymbol, rc)); } } } else { rc = VERR_END_OF_STRING; Log(("dbgDiggerLinuxLoadKernelSymbols: offName=%#x cLeft=%#x cbName=%#x cbKernelNames=%#x\n", offName, cLeft, cbName, pThis->cbKernelNames)); } } else { rc = VERR_END_OF_STRING; Log(("dbgDiggerLinuxLoadKernelSymbols: offName=%#x cLeft=%#x cbKernelNames=%#x\n", offName, cLeft, pThis->cbKernelNames)); } } /* * Link the module into the address space. */ if (RT_SUCCESS(rc)) { RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL); if (hAs != NIL_RTDBGAS) rc = RTDbgAsModuleLink(hAs, hMod, uKernelStart, RTDBGASLINK_FLAGS_REPLACE); else rc = VERR_INTERNAL_ERROR; RTDbgAsRelease(hAs); } else Log(("dbgDiggerLinuxFindTokenIndex: Failed: %Rrc\n", rc)); RTDbgModRelease(hMod); } else Log(("dbgDiggerLinuxFindTokenIndex: RTDbgModCreate failed: %Rrc\n", rc)); } else Log(("dbgDiggerLinuxFindTokenIndex: Reading token index at %RGv failed: %Rrc\n", pThis->AddrKernelTokenIndex.FlatPtr, rc)); RTMemFree(paoffTokens); } else Log(("dbgDiggerLinuxFindTokenIndex: Reading token table at %RGv failed: %Rrc\n", pThis->AddrKernelTokenTable.FlatPtr, rc)); RTMemFree(pszzTokens); } else Log(("dbgDiggerLinuxFindTokenIndex: Reading encoded names at %RGv failed: %Rrc\n", pThis->AddrKernelNames.FlatPtr, rc)); RTMemFree(pbNames); } else Log(("dbgDiggerLinuxFindTokenIndex: Reading symbol addresses at %RGv failed: %Rrc\n", pThis->AddrKernelAddresses.FlatPtr, rc)); RTMemFree(pvAddresses); return rc; }
/** * @interface_method_impl{DBGFOSIDMESG,pfnQueryKernelLog} */ static DECLCALLBACK(int) dbgDiggerLinuxIDmsg_QueryKernelLog(PDBGFOSIDMESG pThis, PUVM pUVM, uint32_t fFlags, uint32_t cMessages, char *pszBuf, size_t cbBuf, size_t *pcbActual) { PDBGDIGGERLINUX pData = RT_FROM_MEMBER(pThis, DBGDIGGERLINUX, IDmesg); if (cMessages < 1) return VERR_INVALID_PARAMETER; /* * Resolve the symbols we need and read their values. */ RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL); RTDBGMOD hMod; int rc = RTDbgAsModuleByName(hAs, "vmlinux", 0, &hMod); if (RT_FAILURE(rc)) return VERR_NOT_FOUND; RTDbgAsRelease(hAs); RTGCPTR GCPtrLogBuf; uint32_t cbLogBuf; uint32_t idxFirst; uint32_t idxNext; struct { void *pvVar; size_t cbHost, cbGuest; const char *pszSymbol; } aSymbols[] = { { &GCPtrLogBuf, sizeof(GCPtrLogBuf), pData->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t), "log_buf" }, { &cbLogBuf, sizeof(cbLogBuf), sizeof(cbLogBuf), "log_buf_len" }, { &idxFirst, sizeof(idxFirst), sizeof(idxFirst), "log_first_idx" }, { &idxNext, sizeof(idxNext), sizeof(idxNext), "log_next_idx" }, }; for (uint32_t i = 0; i < RT_ELEMENTS(aSymbols); i++) { RTDBGSYMBOL SymInfo; rc = RTDbgModSymbolByName(hMod, aSymbols[i].pszSymbol, &SymInfo); if (RT_SUCCESS(rc)) { RT_BZERO(aSymbols[i].pvVar, aSymbols[i].cbHost); Assert(aSymbols[i].cbHost >= aSymbols[i].cbGuest); DBGFADDRESS Addr; rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, (RTGCPTR)SymInfo.Value + pData->AddrKernelBase.FlatPtr), aSymbols[i].pvVar, aSymbols[i].cbGuest); if (RT_SUCCESS(rc)) continue; Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Reading '%s' at %RGv: %Rrc\n", aSymbols[i].pszSymbol, Addr.FlatPtr, rc)); } else Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error looking up '%s': %Rrc\n", aSymbols[i].pszSymbol, rc)); RTDbgModRelease(hMod); return VERR_NOT_FOUND; } /* * Check if the values make sense. */ if (pData->f64Bit ? !LNX64_VALID_ADDRESS(GCPtrLogBuf) : !LNX32_VALID_ADDRESS(GCPtrLogBuf)) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf' value %RGv is not valid.\n", GCPtrLogBuf)); return VERR_NOT_FOUND; } if ( cbLogBuf < 4096 || !RT_IS_POWER_OF_TWO(cbLogBuf) || cbLogBuf > 16*_1M) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf_len' value %#x is not valid.\n", cbLogBuf)); return VERR_NOT_FOUND; } uint32_t const cbLogAlign = 4; if ( idxFirst > cbLogBuf - sizeof(LNXPRINTKHDR) || (idxFirst & (cbLogAlign - 1)) != 0) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_first_idx' value %#x is not valid.\n", idxFirst)); return VERR_NOT_FOUND; } if ( idxNext > cbLogBuf - sizeof(LNXPRINTKHDR) || (idxNext & (cbLogAlign - 1)) != 0) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_next_idx' value %#x is not valid.\n", idxNext)); return VERR_NOT_FOUND; } /* * Read the whole log buffer. */ uint8_t *pbLogBuf = (uint8_t *)RTMemAlloc(cbLogBuf); if (!pbLogBuf) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Failed to allocate %#x bytes for log buffer\n", cbLogBuf)); return VERR_NO_MEMORY; } DBGFADDRESS Addr; rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, GCPtrLogBuf), pbLogBuf, cbLogBuf); if (RT_FAILURE(rc)) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error reading %#x bytes of log buffer at %RGv: %Rrc\n", cbLogBuf, Addr.FlatPtr, rc)); RTMemFree(pbLogBuf); return VERR_NOT_FOUND; } /* * Count the messages in the buffer while doing some basic validation. */ uint32_t const cbUsed = idxFirst == idxNext ? cbLogBuf /* could be empty... */ : idxFirst < idxNext ? idxNext - idxFirst : cbLogBuf - idxFirst + idxNext; uint32_t cbLeft = cbUsed; uint32_t offCur = idxFirst; uint32_t cLogMsgs = 0; while (cbLeft > 0) { PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; if (!pHdr->cbTotal) { /* Wrap around packet, most likely... */ if (cbLogBuf - offCur >= cbLeft) break; offCur = 0; pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; } if (RT_UNLIKELY( pHdr->cbTotal > cbLogBuf - sizeof(*pHdr) - offCur || pHdr->cbTotal > cbLeft || (pHdr->cbTotal & (cbLogAlign - 1)) != 0 || pHdr->cbTotal < (uint32_t)pHdr->cbText + (uint32_t)pHdr->cbDict + sizeof(*pHdr) )) { Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Invalid printk_log record at %#x: cbTotal=%#x cbText=%#x cbDict=%#x cbLogBuf=%#x cbLeft=%#x\n", offCur, pHdr->cbTotal, pHdr->cbText, pHdr->cbDict, cbLogBuf, cbLeft)); rc = VERR_INVALID_STATE; break; } if (pHdr->cbText > 0) cLogMsgs++; /* next */ offCur += pHdr->cbTotal; cbLeft -= pHdr->cbTotal; } if (RT_FAILURE(rc)) { RTMemFree(pbLogBuf); return rc; } /* * Copy the messages into the output buffer. */ offCur = idxFirst; cbLeft = cbUsed; /* Skip messages that the caller doesn't want. */ if (cMessages < cLogMsgs) { uint32_t cToSkip = cLogMsgs - cMessages; while (cToSkip > 0) { PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; if (!pHdr->cbTotal) { offCur = 0; pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; } if (pHdr->cbText > 0) cToSkip--; /* next */ offCur += pHdr->cbTotal; cbLeft -= pHdr->cbTotal; } } /* Now copy the messages. */ size_t offDst = 0; while (cbLeft > 0) { PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; if (!pHdr->cbTotal) { if (cbLogBuf - offCur >= cbLeft) break; offCur = 0; pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur]; } if (pHdr->cbText > 0) { char *pchText = (char *)(pHdr + 1); size_t cchText = RTStrNLen(pchText, pHdr->cbText); if (offDst + cchText < cbBuf) { memcpy(&pszBuf[offDst], pHdr + 1, cchText); pszBuf[offDst + cchText] = '\n'; } else if (offDst < cbBuf) memcpy(&pszBuf[offDst], pHdr + 1, cbBuf - offDst); offDst += cchText + 1; } /* next */ offCur += pHdr->cbTotal; cbLeft -= pHdr->cbTotal; } /* Done with the buffer. */ RTMemFree(pbLogBuf); /* Make sure we've reserved a char for the terminator. */ if (!offDst) offDst = 1; /* Set return size value. */ if (pcbActual) *pcbActual = offDst; /* * All VBox strings are UTF-8 and bad things may in theory happen if we * pass bad UTF-8 to code which assumes it's all valid. So, we enforce * UTF-8 upon the guest kernel messages here even if they (probably) have * no defined code set in reality. */ if (offDst <= cbBuf) { pszBuf[offDst - 1] = '\0'; RTStrPurgeEncoding(pszBuf); return VINF_SUCCESS; } if (cbBuf) { pszBuf[cbBuf - 1] = '\0'; RTStrPurgeEncoding(pszBuf); } return VERR_BUFFER_OVERFLOW; }