diff options
Diffstat (limited to 'src/VBox/Runtime/testcase/tstLdr-3.cpp')
-rw-r--r-- | src/VBox/Runtime/testcase/tstLdr-3.cpp | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/src/VBox/Runtime/testcase/tstLdr-3.cpp b/src/VBox/Runtime/testcase/tstLdr-3.cpp new file mode 100644 index 00000000..90983605 --- /dev/null +++ b/src/VBox/Runtime/testcase/tstLdr-3.cpp @@ -0,0 +1,471 @@ +/* $Id: tstLdr-3.cpp $ */ +/** @file + * IPRT - Testcase for parts of RTLdr*, manual inspection. + */ + +/* + * Copyright (C) 2006-2023 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>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/ldr.h> +#include <iprt/alloc.h> +#include <iprt/stream.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include <VBox/dis.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static RTUINTPTR g_uLoadAddr; +static RTLDRMOD g_hLdrMod; +static void *g_pvBits; +static uint8_t g_cBits; +static uint8_t g_fNearImports; + +/** + * Current nearest symbol. + */ +typedef struct TESTNEARSYM +{ + RTUINTPTR Addr; + struct TESTSYM + { + RTUINTPTR Value; + unsigned uSymbol; + char szName[512]; + } aSyms[2]; +} TESTNEARSYM, *PTESTNEARSYM; + +/** + * Enumeration callback function used by RTLdrEnumSymbols(). + * + * @returns iprt 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) testEnumSymbol2(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser) +{ + RT_NOREF1(hLdrMod); + PTESTNEARSYM pSym = (PTESTNEARSYM)pvUser; + + /* less or equal */ + if ( Value <= pSym->Addr + && ( Value > pSym->aSyms[0].Value + || ( Value == pSym->aSyms[0].Value + && !pSym->aSyms[0].szName[0] + && pszSymbol + && *pszSymbol + ) + ) + ) + { + pSym->aSyms[0].Value = Value; + pSym->aSyms[0].uSymbol = uSymbol; + pSym->aSyms[0].szName[0] = '\0'; + if (pszSymbol) + strncat(pSym->aSyms[0].szName, pszSymbol, sizeof(pSym->aSyms[0].szName)-1); + } + + /* above */ + if ( Value > pSym->Addr + && ( Value < pSym->aSyms[1].Value + || ( Value == pSym->aSyms[1].Value + && !pSym->aSyms[1].szName[1] + && pszSymbol + && *pszSymbol + ) + ) + ) + { + pSym->aSyms[1].Value = Value; + pSym->aSyms[1].uSymbol = uSymbol; + pSym->aSyms[1].szName[0] = '\0'; + if (pszSymbol) + strncat(pSym->aSyms[1].szName, pszSymbol, sizeof(pSym->aSyms[1].szName)-1); + } + + return VINF_SUCCESS; +} + +static int FindNearSymbol(RTUINTPTR uAddr, PTESTNEARSYM pNearSym) +{ + RT_ZERO(*pNearSym); + pNearSym->Addr = (RTUINTPTR)uAddr; + pNearSym->aSyms[1].Value = ~(RTUINTPTR)0; + int rc = RTLdrEnumSymbols(g_hLdrMod, RTLDR_ENUM_SYMBOL_FLAGS_ALL, g_pvBits, g_uLoadAddr, testEnumSymbol2, pNearSym); + if (RT_FAILURE(rc)) + RTPrintf("tstLdr-3: Failed to enumerate symbols: %Rra\n", rc); + return rc; +} + +static DECLCALLBACK(int) MyGetSymbol(PCDISCPUSTATE pCpu, uint32_t u32Sel, RTUINTPTR uAddress, + char *pszBuf, size_t cchBuf, RTINTPTR *poff, + void *pvUser) +{ + RT_NOREF3(pCpu, u32Sel, pvUser); + + if ( uAddress > RTLdrSize(g_hLdrMod) + g_uLoadAddr + || uAddress < g_uLoadAddr) + return VERR_SYMBOL_NOT_FOUND; + + TESTNEARSYM NearSym; + int rc = FindNearSymbol(uAddress, &NearSym); + if (RT_FAILURE(rc)) + return rc; + + RTStrCopy(pszBuf, cchBuf, NearSym.aSyms[0].szName); + *poff = uAddress - NearSym.aSyms[0].Value; + return VINF_SUCCESS; +} + + +/** + * @callback_method_impl{FNDISREADBYTES} + */ +static DECLCALLBACK(int) MyReadBytes(PDISCPUSTATE pDis, uint8_t offInstr, uint8_t cbMinRead, uint8_t cbMaxRead) +{ + RT_NOREF1(cbMaxRead); + uint8_t const *pbSrc = (uint8_t const *)((uintptr_t)pDis->uInstrAddr + (uintptr_t)pDis->pvUser + offInstr); + memcpy(&pDis->abInstr[offInstr], pbSrc, cbMinRead); + pDis->cbCachedInstr = offInstr + cbMinRead; + return VINF_SUCCESS; +} + + +static bool MyDisBlock(DISCPUMODE enmCpuMode, RTHCUINTPTR pvCodeBlock, int32_t cbMax, RTUINTPTR off, + RTUINTPTR uNearAddr, RTUINTPTR uSearchAddr) +{ + DISCPUSTATE Cpu; + int32_t i = 0; + while (i < cbMax) + { + bool fQuiet = RTAssertSetQuiet(true); + bool fMayPanic = RTAssertSetMayPanic(false); + char szOutput[256]; + unsigned cbInstr; + int rc = DISInstrWithReader(uNearAddr + i, enmCpuMode, + MyReadBytes, (uint8_t *)pvCodeBlock - (uintptr_t)uNearAddr, + &Cpu, &cbInstr); + RTAssertSetMayPanic(fMayPanic); + RTAssertSetQuiet(fQuiet); + if (RT_FAILURE(rc)) + return false; + + TESTNEARSYM NearSym; + rc = FindNearSymbol(uNearAddr + i, &NearSym); + if (RT_SUCCESS(rc) && NearSym.aSyms[0].Value == NearSym.Addr) + RTPrintf("%s:\n", NearSym.aSyms[0].szName); + + DISFormatYasmEx(&Cpu, szOutput, sizeof(szOutput), + DIS_FMT_FLAGS_RELATIVE_BRANCH | DIS_FMT_FLAGS_BYTES_RIGHT | DIS_FMT_FLAGS_ADDR_LEFT | DIS_FMT_FLAGS_BYTES_SPACED, + MyGetSymbol, NULL); + + RTPrintf("%s\n", szOutput); + if (pvCodeBlock + i + off == uSearchAddr) + RTPrintf("^^^^^^^^\n"); + + /* next */ + i += cbInstr; + } + return true; +} + + + +/** + * Resolve an external symbol during RTLdrGetBits(). + * + * @returns iprt 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) testGetImport(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol, + unsigned uSymbol, RTUINTPTR *pValue, void *pvUser) +{ + RT_NOREF5(hLdrMod, pszModule, pszSymbol, uSymbol, pvUser); + RTUINTPTR BaseAddr = *(PCRTUINTPTR)pvUser; + if (g_fNearImports) + *pValue = BaseAddr + UINT32_C(0x604020f0); + else if ( BaseAddr < UINT64_C(0xffffff7f820df000) - _4G + || BaseAddr > UINT64_C(0xffffff7f820df000) + _4G) + *pValue = UINT64_C(0xffffff7f820df000); + else + *pValue = UINT64_C(0xffffff7c820df000); + if (g_cBits == 32) + *pValue &= UINT32_MAX; + return VINF_SUCCESS; +} + +static uint32_t g_iSegNo = 0; +static DECLCALLBACK(int) testEnumSegment1(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser) +{ + if (hLdrMod != g_hLdrMod || pvUser != NULL) + return VERR_INTERNAL_ERROR_3; + RTPrintf("Seg#%02u: %RTptr LB %RTptr %s\n" + " link=%RTptr LB %RTptr align=%RTptr fProt=%#x offFile=%RTfoff\n" + , g_iSegNo++, pSeg->RVA, pSeg->cbMapped, pSeg->pszName, + pSeg->LinkAddress, pSeg->cb, pSeg->Alignment, pSeg->fProt, pSeg->offFile); + + return VINF_SUCCESS; +} + + +/** + * Enumeration callback function used by RTLdrEnumSymbols(). + * + * @returns iprt 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) testEnumSymbol1(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, RTUINTPTR Value, void *pvUser) +{ + if (hLdrMod != g_hLdrMod || pvUser != NULL) + return VERR_INTERNAL_ERROR_3; + RTPrintf(" %RTptr %s (%d)\n", Value, pszSymbol, uSymbol); + return VINF_SUCCESS; +} + + +static int testDisasNear(uint64_t uAddr) +{ + TESTNEARSYM NearSym; + int rc = FindNearSymbol(uAddr, &NearSym); + if (RT_FAILURE(rc)) + return rc; + + RTPrintf("tstLdr-3: Addr=%RTptr\n" + "%RTptr %s (%d) - %RTptr %s (%d)\n", + NearSym.Addr, + NearSym.aSyms[0].Value, NearSym.aSyms[0].szName, NearSym.aSyms[0].uSymbol, + NearSym.aSyms[1].Value, NearSym.aSyms[1].szName, NearSym.aSyms[1].uSymbol); + if (NearSym.Addr - NearSym.aSyms[0].Value < 0x10000) + { + DISCPUMODE enmDisCpuMode = g_cBits == 32 ? DISCPUMODE_32BIT : DISCPUMODE_64BIT; + uint8_t *pbCode = (uint8_t *)g_pvBits + (NearSym.aSyms[0].Value - g_uLoadAddr); + MyDisBlock(enmDisCpuMode, (uintptr_t)pbCode, + RT_MAX(NearSym.aSyms[1].Value - NearSym.aSyms[0].Value, 0x20000), + NearSym.aSyms[0].Value - (uintptr_t)pbCode, + NearSym.aSyms[0].Value, + NearSym.Addr); + } + + return VINF_SUCCESS; +} + +int main(int argc, char **argv) +{ + RTR3InitExe(argc, &argv, 0); + + /* + * Module & code bitness (optional). + */ + g_cBits = ARCH_BITS; +#if !defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN) + g_fNearImports = false; +#else + g_fNearImports = true; +#endif + while (argc > 1) + { + if (!strcmp(argv[1], "--32")) + g_cBits = 32; + else if (!strcmp(argv[1], "--64")) + g_cBits = 64; + else if (!strcmp(argv[1], "--near-imports")) + g_fNearImports = true; + else if (!strcmp(argv[1], "--wide-imports")) + g_fNearImports = false; + else + break; + argc--; + argv++; + } + + int rcRet = 0; + if (argc <= 2) + { + RTPrintf("usage: %s [--32|--64] [--<near|wide>-imports] <load-addr> <module> [addr1 []]\n", argv[0]); + return 1; + } + + /* + * Load the module. + */ + RTERRINFOSTATIC ErrInfo; + g_uLoadAddr = (RTUINTPTR)RTStrToUInt64(argv[1]); + int rc = RTLdrOpenEx(argv[2], 0, RTLDRARCH_WHATEVER, &g_hLdrMod, RTErrInfoInitStatic(&ErrInfo)); + if (RT_FAILURE(rc)) + { + RTPrintf("tstLdr-3: Failed to open '%s': %Rra\n", argv[2], rc); + if (ErrInfo.szMsg[0]) + RTPrintf("tstLdr-3: %s\n", ErrInfo.szMsg); + return 1; + } + + g_pvBits = RTMemAlloc(RTLdrSize(g_hLdrMod)); + rc = RTLdrGetBits(g_hLdrMod, g_pvBits, g_uLoadAddr, testGetImport, &g_uLoadAddr); + if (RT_SUCCESS(rc)) + { + if ( argc == 4 + && argv[3][0] == '*') + { + /* + * Wildcard address mode. + */ + uint64_t uWild = RTStrToUInt64(&argv[3][1]); + uint64_t uIncrements = strchr(argv[3], '/') ? RTStrToUInt64(strchr(argv[3], '/') + 1) : 0x1000; + if (!uIncrements) + uIncrements = 0x1000; + uint64_t uMax = RTLdrSize(g_hLdrMod) + g_uLoadAddr; + for (uint64_t uCur = g_uLoadAddr + uWild; uCur < uMax; uCur += uIncrements) + testDisasNear(uCur); + } + else if (argc > 3) + { + /* + * User specified addresses within the module. + */ + for (int i = 3; i < argc; i++) + { + rc = testDisasNear(RTStrToUInt64(argv[i])); + if (RT_FAILURE(rc)) + rcRet++; + } + } + else + { + /* + * Enumerate symbols. + */ + rc = RTLdrEnumSymbols(g_hLdrMod, RTLDR_ENUM_SYMBOL_FLAGS_ALL, g_pvBits, g_uLoadAddr, testEnumSymbol1, NULL); + if (RT_FAILURE(rc)) + { + RTPrintf("tstLdr-3: Failed to enumerate symbols: %Rra\n", rc); + rcRet++; + } + + /* + * Query various properties. + */ + union + { + char szName[256]; + uint32_t iImpModule; + RTUUID Uuid; + } uBuf; + rc = RTLdrQueryProp(g_hLdrMod, RTLDRPROP_INTERNAL_NAME, &uBuf, sizeof(uBuf)); + if (RT_SUCCESS(rc)) + RTPrintf("tstLdr-3: Internal name: %s\n", uBuf.szName); + else if (rc != VERR_NOT_FOUND && rc != VERR_NOT_SUPPORTED) + { + RTPrintf("tstLdr-3: Internal name: failed - %Rrc\n", rc); + rcRet++; + } + + uint32_t cImports = 0; + rc = RTLdrQueryProp(g_hLdrMod, RTLDRPROP_IMPORT_COUNT, &cImports, sizeof(cImports)); + if (RT_SUCCESS(rc)) + { + RTPrintf("tstLdr-3: Import count: %u\n", cImports); + for (uint32_t i = 0; i < cImports; i++) + { + uBuf.iImpModule = i; + rc = RTLdrQueryProp(g_hLdrMod, RTLDRPROP_IMPORT_MODULE, &uBuf, sizeof(uBuf)); + if (RT_SUCCESS(rc)) + RTPrintf("tstLdr-3: Import module #%u: %s\n", i, uBuf.szName); + else + { + RTPrintf("tstLdr-3: Import module #%u: failed - %Rrc\n", i, rc); + rcRet++; + } + } + } + else if (rc != VERR_NOT_FOUND && rc != VERR_NOT_SUPPORTED) + { + RTPrintf("tstLdr-3: Import count: failed - %Rrc\n", rc); + rcRet++; + } + + rc = RTLdrQueryProp(g_hLdrMod, RTLDRPROP_UUID, &uBuf.Uuid, sizeof(uBuf.Uuid)); + if (RT_SUCCESS(rc)) + RTPrintf("tstLdr-3: UUID: %RTuuid\n", uBuf.Uuid); + else if (rc != VERR_NOT_FOUND && rc != VERR_NOT_SUPPORTED) + { + RTPrintf("tstLdr-3: UUID: failed - %Rrc\n", rc); + rcRet++; + } + + /* + * Enumerate segments. + */ + RTPrintf("tstLdr-3: Segments:\n"); + rc = RTLdrEnumSegments(g_hLdrMod, testEnumSegment1, NULL); + if (RT_FAILURE(rc)) + { + RTPrintf("tstLdr-3: Failed to enumerate symbols: %Rra\n", rc); + rcRet++; + } + } + } + else + { + RTPrintf("tstLdr-3: Failed to get bits for '%s' at %RTptr: %Rra\n", argv[2], g_uLoadAddr, rc); + rcRet++; + } + RTMemFree(g_pvBits); + RTLdrClose(g_hLdrMod); + + /* + * Test result summary. + */ + if (!rcRet) + RTPrintf("tstLdr-3: SUCCESS\n"); + else + RTPrintf("tstLdr-3: FAILURE - %d errors\n", rcRet); + return !!rcRet; +} |