diff options
Diffstat (limited to 'src/VBox/Runtime/testcase/tstLdr-4.cpp')
-rw-r--r-- | src/VBox/Runtime/testcase/tstLdr-4.cpp | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/src/VBox/Runtime/testcase/tstLdr-4.cpp b/src/VBox/Runtime/testcase/tstLdr-4.cpp new file mode 100644 index 00000000..c98975bf --- /dev/null +++ b/src/VBox/Runtime/testcase/tstLdr-4.cpp @@ -0,0 +1,365 @@ +/* $Id: tstLdr-4.cpp $ */ +/** @file + * IPRT - Testcase for RTLdrOpen using ldrLdrObjR0.r0. + */ + +/* + * 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/log.h> +#include <iprt/stream.h> +#include <iprt/assert.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include <iprt/test.h> + +#include <VBox/sup.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static RTTEST g_hTest; +static SUPGLOBALINFOPAGE g_MyGip = { SUPGLOBALINFOPAGE_MAGIC, SUPGLOBALINFOPAGE_VERSION, SUPGIPMODE_INVARIANT_TSC, 42 }; +static PSUPGLOBALINFOPAGE g_pMyGip = &g_MyGip; + +extern "C" DECLEXPORT(int) DisasmTest1(void); + + +static DECLCALLBACK(int) testEnumSegment(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser) +{ + uint32_t *piSeg = (uint32_t *)pvUser; + RTPrintf(" Seg#%02u: %RTptr LB %RTptr %s\n" + " link=%RTptr LB %RTptr align=%RTptr fProt=%#x offFile=%RTfoff\n" + , *piSeg, pSeg->RVA, pSeg->cbMapped, pSeg->pszName, + pSeg->LinkAddress, pSeg->cb, pSeg->Alignment, pSeg->fProt, pSeg->offFile); + + if (pSeg->RVA != NIL_RTLDRADDR) + { + RTTESTI_CHECK(pSeg->cbMapped != NIL_RTLDRADDR); + RTTESTI_CHECK(pSeg->cbMapped >= pSeg->cb); + } + else + { + RTTESTI_CHECK(pSeg->cbMapped == NIL_RTLDRADDR); + } + + /* + * Do some address conversion tests: + */ + if (pSeg->cbMapped != NIL_RTLDRADDR) + { + /* RTLdrRvaToSegOffset: */ + uint32_t iSegConv = ~(uint32_t)42; + RTLDRADDR offSegConv = ~(RTLDRADDR)22; + int rc = RTLdrRvaToSegOffset(hLdrMod, pSeg->RVA, &iSegConv, &offSegConv); + if (RT_FAILURE(rc)) + RTTestIFailed("RTLdrRvaToSegOffset failed on Seg #%u / RVA %#RTptr: %Rrc", *piSeg, pSeg->RVA, rc); + else if (iSegConv != *piSeg || offSegConv != 0) + RTTestIFailed("RTLdrRvaToSegOffset on Seg #%u / RVA %#RTptr returned: iSegConv=%#x offSegConv=%RTptr, expected %#x and 0", + *piSeg, pSeg->RVA, iSegConv, offSegConv, *piSeg); + + /* RTLdrSegOffsetToRva: */ + RTLDRADDR uRvaConv = ~(RTLDRADDR)22; + rc = RTLdrSegOffsetToRva(hLdrMod, *piSeg, 0, &uRvaConv); + if (RT_FAILURE(rc)) + RTTestIFailed("RTLdrSegOffsetToRva failed on Seg #%u / off 0: %Rrc", *piSeg, rc); + else if (uRvaConv != pSeg->RVA) + RTTestIFailed("RTLdrSegOffsetToRva on Seg #%u / off 0 returned: %RTptr, expected %RTptr", *piSeg, uRvaConv, pSeg->RVA); + + /* RTLdrLinkAddressToRva: */ + uRvaConv = ~(RTLDRADDR)22; + rc = RTLdrLinkAddressToRva(hLdrMod, pSeg->LinkAddress, &uRvaConv); + if (RT_FAILURE(rc)) + RTTestIFailed("RTLdrLinkAddressToRva failed on Seg #%u / %RTptr: %Rrc", *piSeg, pSeg->LinkAddress, rc); + else if (uRvaConv != pSeg->RVA) + RTTestIFailed("RTLdrLinkAddressToRva on Seg #%u / %RTptr returned: %RTptr, expected %RTptr", + *piSeg, pSeg->LinkAddress, uRvaConv, pSeg->RVA); + + /* RTLdrLinkAddressToSegOffset: */ + iSegConv = ~(uint32_t)42; + offSegConv = ~(RTLDRADDR)22; + rc = RTLdrLinkAddressToSegOffset(hLdrMod, pSeg->LinkAddress, &iSegConv, &offSegConv); + if (RT_FAILURE(rc)) + RTTestIFailed("RTLdrLinkAddressToSegOffset failed on Seg #%u / %#RTptr: %Rrc", *piSeg, pSeg->LinkAddress, rc); + else if (iSegConv != *piSeg || offSegConv != 0) + RTTestIFailed("RTLdrLinkAddressToSegOffset on Seg #%u / %#RTptr returned: iSegConv=%#x offSegConv=%RTptr, expected %#x and 0", + *piSeg, pSeg->LinkAddress, iSegConv, offSegConv, *piSeg); + } + + *piSeg += 1; + RT_NOREF(hLdrMod); + return VINF_SUCCESS; +} + + +/** + * 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_NOREF4(hLdrMod, pszModule, uSymbol, pvUser); + if ( !strcmp(pszSymbol, "RTAssertMsg1Weak") || !strcmp(pszSymbol, "_RTAssertMsg1Weak")) + *pValue = (uintptr_t)RTAssertMsg1Weak; + else if (!strcmp(pszSymbol, "RTAssertMsg2Weak") || !strcmp(pszSymbol, "_RTAssertMsg2Weak")) + *pValue = (uintptr_t)RTAssertMsg1Weak; + else if (!strcmp(pszSymbol, "RTAssertMsg1") || !strcmp(pszSymbol, "_RTAssertMsg1")) + *pValue = (uintptr_t)RTAssertMsg1; + else if (!strcmp(pszSymbol, "RTAssertMsg2") || !strcmp(pszSymbol, "_RTAssertMsg2")) + *pValue = (uintptr_t)RTAssertMsg2; + else if (!strcmp(pszSymbol, "RTAssertMsg2V") || !strcmp(pszSymbol, "_RTAssertMsg2V")) + *pValue = (uintptr_t)RTAssertMsg2V; + else if (!strcmp(pszSymbol, "RTAssertMayPanic") || !strcmp(pszSymbol, "_RTAssertMayPanic")) + *pValue = (uintptr_t)RTAssertMayPanic; + else if (!strcmp(pszSymbol, "RTLogDefaultInstanceEx") || !strcmp(pszSymbol, "RTLogDefaultInstanceEx")) + *pValue = (uintptr_t)RTLogDefaultInstanceEx; + else if (!strcmp(pszSymbol, "RTLogLoggerExV") || !strcmp(pszSymbol, "_RTLogLoggerExV")) + *pValue = (uintptr_t)RTLogLoggerExV; + else if (!strcmp(pszSymbol, "RTLogPrintfV") || !strcmp(pszSymbol, "_RTLogPrintfV")) + *pValue = (uintptr_t)RTLogPrintfV; + else if (!strcmp(pszSymbol, "RTR0AssertPanicSystem")|| !strcmp(pszSymbol, "_RTR0AssertPanicSystem")) + *pValue = (uintptr_t)0; + else if (!strcmp(pszSymbol, "MyPrintf") || !strcmp(pszSymbol, "_MyPrintf")) + *pValue = (uintptr_t)RTPrintf; + else if (!strcmp(pszSymbol, "SUPR0Printf") || !strcmp(pszSymbol, "_SUPR0Printf")) + *pValue = (uintptr_t)RTPrintf; + else if (!strcmp(pszSymbol, "SUPR0PrintfV") || !strcmp(pszSymbol, "_SUPR0PrintfV")) + *pValue = (uintptr_t)RTPrintfV; + else if (!strcmp(pszSymbol, "SomeImportFunction") || !strcmp(pszSymbol, "_SomeImportFunction")) + *pValue = (uintptr_t)0; + else if (!strcmp(pszSymbol, "g_pSUPGlobalInfoPage") || !strcmp(pszSymbol, "_g_pSUPGlobalInfoPage")) + *pValue = (uintptr_t)&g_pMyGip; + else if (!strcmp(pszSymbol, "g_SUPGlobalInfoPage") || !strcmp(pszSymbol, "_g_SUPGlobalInfoPage")) + *pValue = (uintptr_t)&g_MyGip; + else + { + RTPrintf("tstLdr-4: Unexpected import '%s'!\n", pszSymbol); + return VERR_SYMBOL_NOT_FOUND; + } + return VINF_SUCCESS; +} + + +/** + * One test iteration with one file. + * + * The test is very simple, we load the file three times + * into two different regions. The first two into each of the + * regions the for compare usage. The third is loaded into one + * and then relocated between the two and other locations a few times. + * + * @param pszFilename The file to load the mess with. + */ +static void testLdrOne(const char *pszFilename) +{ + RTTestSub(g_hTest, RTPathFilename(pszFilename)); + + size_t cbImage = 0; + struct Load + { + RTLDRMOD hLdrMod; + void *pvBits; + size_t cbBits; + const char *pszName; + } aLoads[6] = + { + { NULL, NULL, 0, "foo" }, + { NULL, NULL, 0, "bar" }, + { NULL, NULL, 0, "foobar" }, + }; + unsigned i; + int rc; + + /* + * Load them. + */ + for (i = 0; i < RT_ELEMENTS(aLoads); i++) + { + rc = RTLdrOpen(pszFilename, 0, RTLDRARCH_WHATEVER, &aLoads[i].hLdrMod); + if (RT_FAILURE(rc)) + { + RTTestIFailed("tstLdr-4: Failed to open '%s'/%d, rc=%Rrc. aborting test.", pszFilename, i, rc); + Assert(aLoads[i].hLdrMod == NIL_RTLDRMOD); + break; + } + + /* size it */ + size_t cb = RTLdrSize(aLoads[i].hLdrMod); + if (cbImage && cb != cbImage) + { + RTTestIFailed("tstLdr-4: Size mismatch '%s'/%d. aborting test.", pszFilename, i); + break; + } + aLoads[i].cbBits = cbImage = cb; + + /* Allocate bits. */ + aLoads[i].pvBits = RTMemPageAlloc(cb); + if (!aLoads[i].pvBits) + { + RTTestIFailed("Out of memory '%s'/%d cbImage=%d. aborting test.", pszFilename, i, cbImage); + break; + } + rc = RTMemProtect(aLoads[i].pvBits, cb, RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC); + if (RT_FAILURE(rc)) + { + RTTestIFailed("RTMemProtect/RWX '%s'/%d cbImage=%d, %Rrc. aborting test.", pszFilename, i, cbImage, rc); + break; + } + + /* Get the bits. */ + rc = RTLdrGetBits(aLoads[i].hLdrMod, aLoads[i].pvBits, (uintptr_t)aLoads[i].pvBits, testGetImport, NULL); + if (RT_FAILURE(rc)) + { + RTTestIFailed("Failed to get bits for '%s'/%d, rc=%Rrc. aborting test", pszFilename, i, rc); + break; + } + } + + /* + * Execute the code. + */ + if (!RTTestSubErrorCount(g_hTest)) + { + for (i = 0; i < RT_ELEMENTS(aLoads); i += 1) + { + /* VERR_ELF_EXE_NOT_SUPPORTED in the previous loop? */ + if (!aLoads[i].hLdrMod) + continue; + /* get the pointer. */ + RTUINTPTR Value; + rc = RTLdrGetSymbolEx(aLoads[i].hLdrMod, aLoads[i].pvBits, (uintptr_t)aLoads[i].pvBits, + UINT32_MAX, "DisasmTest1", &Value); + if (rc == VERR_SYMBOL_NOT_FOUND) + rc = RTLdrGetSymbolEx(aLoads[i].hLdrMod, aLoads[i].pvBits, (uintptr_t)aLoads[i].pvBits, + UINT32_MAX, "_DisasmTest1", &Value); + if (RT_FAILURE(rc)) + { + RTTestIFailed("Failed to get symbol \"DisasmTest1\" from load #%d: %Rrc", i, rc); + break; + } + typedef DECLCALLBACKPTR(int, PFNDISASMTEST1,(void)); + PFNDISASMTEST1 pfnDisasmTest1 = (PFNDISASMTEST1)(uintptr_t)Value; + RTPrintf("tstLdr-4: pfnDisasmTest1=%p / add-symbol-file %s %#p\n", pfnDisasmTest1, pszFilename, aLoads[i].pvBits); + uint32_t iSeg = 0; + RTLdrEnumSegments(aLoads[i].hLdrMod, testEnumSegment, &iSeg); + + /* call the test function. */ + rc = pfnDisasmTest1(); + if (rc) + RTTestIFailed("load #%d Test1 -> %#x", i, rc); + + /* While we're here, check a couple of RTLdrQueryProp calls too */ + void *pvBits = aLoads[i].pvBits; + for (unsigned iBits = 0; iBits < 2; iBits++, pvBits = NULL) + { + union + { + char szName[127]; + } uBuf; + rc = RTLdrQueryPropEx(aLoads[i].hLdrMod, RTLDRPROP_INTERNAL_NAME, aLoads[i].pvBits, + uBuf.szName, sizeof(uBuf.szName), NULL); + if (RT_SUCCESS(rc)) + RTPrintf("tstLdr-4: internal name #%d: '%s'\n", i, uBuf.szName); + else if (rc != VERR_NOT_FOUND && rc != VERR_NOT_SUPPORTED) + RTPrintf("tstLdr-4: internal name #%d failed: %Rrc\n", i, rc); + } + } + } + + /* + * Clean up. + */ + for (i = 0; i < RT_ELEMENTS(aLoads); i++) + { + if (aLoads[i].pvBits) + { + RTMemProtect(aLoads[i].pvBits, aLoads[i].cbBits, RTMEM_PROT_READ | RTMEM_PROT_WRITE); + RTMemPageFree(aLoads[i].pvBits, aLoads[i].cbBits); + } + if (aLoads[i].hLdrMod) + { + rc = RTLdrClose(aLoads[i].hLdrMod); + if (RT_FAILURE(rc)) + RTTestIFailed("Failed to close '%s' i=%d, rc=%Rrc.", pszFilename, i, rc); + } + } + +} + + + +int main() +{ + RTEXITCODE rcExit = RTTestInitAndCreate("tstLdr-4", &g_hTest); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + + /* + * Sanity check. + */ + int rc = DisasmTest1(); + if (rc == 0) + { + /* + * Execute the test. + */ + char szPath[RTPATH_MAX]; + rc = RTPathExecDir(szPath, sizeof(szPath) - sizeof("/tstLdrObjR0.r0")); + if (RT_SUCCESS(rc)) + { + strcat(szPath, "/tstLdrObjR0.r0"); + + testLdrOne(szPath); + } + else + RTTestIFailed("RTPathExecDir -> %Rrc", rc); + } + else + RTTestIFailed("FATAL ERROR - DisasmTest1 is buggy: rc=%#x", rc); + + return RTTestSummaryAndDestroy(g_hTest); +} |