/**
 * 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;
}
/**
 * Called by PATMR3Term.
 *
 * @param   pVM         The cross context VM structure.
 */
void patmR3DbgTerm(PVM pVM)
{
    if (pVM->patm.s.hDbgModPatchMem != NIL_RTDBGMOD)
    {
        RTDbgModRelease(pVM->patm.s.hDbgModPatchMem);
        pVM->patm.s.hDbgModPatchMem = NIL_RTDBGMOD;
    }
}
예제 #3
0
/**
 * 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;
}
예제 #5
0
/**
 * 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 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;
}
/**
 * Relocates the RC address space.
 *
 * @param   pUVM        The user mode VM handle.
 * @param   offDelta    The relocation delta.
 */
void dbgfR3AsRelocate(PUVM pUVM, RTGCUINTPTR offDelta)
{
    /*
     * We will relocate the raw-mode context modules by offDelta if they have
     * been injected into the the DBGF_AS_RC map.
     */
    if (   pUVM->dbgf.s.afAsAliasPopuplated[DBGF_AS_ALIAS_2_INDEX(DBGF_AS_RC)]
        && offDelta != 0)
    {
        RTDBGAS hAs = pUVM->dbgf.s.ahAsAliases[DBGF_AS_ALIAS_2_INDEX(DBGF_AS_RC)];

        /* Take a snapshot of the modules as we might have overlapping
           addresses between the previous and new mapping. */
        RTDbgAsLockExcl(hAs);
        uint32_t cModules = RTDbgAsModuleCount(hAs);
        if (cModules > 0 && cModules < _4K)
        {
            struct DBGFASRELOCENTRY
            {
                RTDBGMOD    hDbgMod;
                RTRCPTR     uOldAddr;
            } *paEntries = (struct DBGFASRELOCENTRY *)RTMemTmpAllocZ(sizeof(paEntries[0]) * cModules);
            if (paEntries)
            {
                /* Snapshot. */
                for (uint32_t i = 0; i < cModules; i++)
                {
                    paEntries[i].hDbgMod = RTDbgAsModuleByIndex(hAs, i);
                    AssertLogRelMsg(paEntries[i].hDbgMod != NIL_RTDBGMOD, ("iModule=%#x\n", i));

                    RTDBGASMAPINFO  aMappings[1] = { { 0, 0 } };
                    uint32_t        cMappings = 1;
                    int rc = RTDbgAsModuleQueryMapByIndex(hAs, i, &aMappings[0], &cMappings, 0 /*fFlags*/);
                    if (RT_SUCCESS(rc) && cMappings == 1 && aMappings[0].iSeg == NIL_RTDBGSEGIDX)
                        paEntries[i].uOldAddr = (RTRCPTR)aMappings[0].Address;
                    else
                        AssertLogRelMsgFailed(("iModule=%#x rc=%Rrc cMappings=%#x.\n", i, rc, cMappings));
                }

                /* Unlink them. */
                for (uint32_t i = 0; i < cModules; i++)
                {
                    int rc = RTDbgAsModuleUnlink(hAs, paEntries[i].hDbgMod);
                    AssertLogRelMsg(RT_SUCCESS(rc), ("iModule=%#x rc=%Rrc hDbgMod=%p\n", i, rc, paEntries[i].hDbgMod));
                }

                /* Link them at the new locations. */
                for (uint32_t i = 0; i < cModules; i++)
                {
                    RTRCPTR uNewAddr = paEntries[i].uOldAddr + offDelta;
                    int rc = RTDbgAsModuleLink(hAs, paEntries[i].hDbgMod, uNewAddr,
                                               RTDBGASLINK_FLAGS_REPLACE);
                    AssertLogRelMsg(RT_SUCCESS(rc),
                                    ("iModule=%#x rc=%Rrc hDbgMod=%p %RRv -> %RRv\n", i, rc, paEntries[i].hDbgMod,
                                     paEntries[i].uOldAddr, uNewAddr));
                    RTDbgModRelease(paEntries[i].hDbgMod);
                }

                RTMemTmpFree(paEntries);
            }
            else
                AssertLogRelMsgFailed(("No memory for %#x modules.\n", cModules));
        }
        else
            AssertLogRelMsgFailed(("cModules=%#x\n", cModules));
        RTDbgAsUnlockExcl(hAs);
    }
}
예제 #8
0
int main(int argc, char **argv)
{
    int rc = RTR3InitExe(argc, &argv, 0);
    if (RT_FAILURE(rc))
        return RTMsgInitFailure(rc);

    /*
     * Create an empty address space that we can load modules and stuff into
     * as we parse the parameters.
     */
    RTDBGAS hDbgAs;
    rc = RTDbgAsCreate(&hDbgAs, 0, RTUINTPTR_MAX, "");
    if (RT_FAILURE(rc))
        return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDBgAsCreate -> %Rrc", rc);

    /*
     * Create a debugging configuration instance to work with so that we can
     * make use of (i.e. test) path searching and such.
     */
    RTDBGCFG hDbgCfg;
    rc = RTDbgCfgCreate(&hDbgCfg, "IPRT", true /*fNativePaths*/);
    if (RT_FAILURE(rc))
        return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgCfgCreate -> %Rrc", rc);

    /*
     * Parse arguments.
     */
    static const RTGETOPTDEF s_aOptions[] =
    {
        { "--input",        'i', RTGETOPT_REQ_STRING },
        { "--local-file",   'l', RTGETOPT_REQ_NOTHING },
        { "--cache-file",   'c', RTGETOPT_REQ_NOTHING },
        { "--pe-image",     'p', RTGETOPT_REQ_NOTHING },
        { "--verbose",      'v', RTGETOPT_REQ_NOTHING },
        { "--x86",          '8', RTGETOPT_REQ_NOTHING },
        { "--amd64",        '6', RTGETOPT_REQ_NOTHING },
        { "--whatever",     '*', RTGETOPT_REQ_NOTHING },
    };

    PRTSTREAM       pInput          = g_pStdIn;
    PRTSTREAM       pOutput         = g_pStdOut;
    unsigned        cVerbosityLevel = 0;
    enum {
        kOpenMethod_FromImage,
        kOpenMethod_FromPeImage
    }               enmOpenMethod   = kOpenMethod_FromImage;
    bool            fCacheFile      = false;
    RTLDRARCH       enmArch         = RTLDRARCH_WHATEVER;

    RTGETOPTUNION   ValueUnion;
    RTGETOPTSTATE   GetState;
    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
    while ((rc = RTGetOpt(&GetState, &ValueUnion)))
    {
        switch (rc)
        {
            case 'i':
                rc = RTStrmOpen(ValueUnion.psz, "r", &pInput);
                if (RT_FAILURE(rc))
                    return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open '%s' for reading: %Rrc", ValueUnion.psz, rc);
                break;

            case 'c':
                fCacheFile = true;
                break;

            case 'l':
                fCacheFile = false;
                break;

            case 'p':
                enmOpenMethod = kOpenMethod_FromPeImage;
                break;

            case 'v':
                cVerbosityLevel++;
                break;

            case '8':
                enmArch = RTLDRARCH_X86_32;
                break;

            case '6':
                enmArch = RTLDRARCH_AMD64;
                break;

            case '*':
                enmArch = RTLDRARCH_WHATEVER;
                break;

            case 'h':
                RTPrintf("Usage: %s [options] <module> <address> [<module> <address> [..]]\n"
                         "\n"
                         "Options:\n"
                         "  -i,--input=file\n"
                         "      Specify a input file instead of standard input.\n"
                         "  --pe-image\n"
                         "      Use RTDbgModCreateFromPeImage to open the file."
                         "  -v, --verbose\n"
                         "      Display the address space before doing the filtering.\n"
                         "  --amd64,--x86,--whatever\n"
                         "      Selects the desired architecture.\n"
                         "  -h, -?, --help\n"
                         "      Display this help text and exit successfully.\n"
                         "  -V, --version\n"
                         "      Display the revision and exit successfully.\n"
                         , RTPathFilename(argv[0]));
                return RTEXITCODE_SUCCESS;

            case 'V':
                RTPrintf("$Revision$\n");
                return RTEXITCODE_SUCCESS;

            case VINF_GETOPT_NOT_OPTION:
            {
                /* <module> <address> */
                const char *pszModule = ValueUnion.psz;

                rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX);
                if (RT_FAILURE(rc))
                    return RTGetOptPrintError(rc, &ValueUnion);
                uint64_t u64Address = ValueUnion.u64;

                uint32_t cbImage    = 0;
                uint32_t uTimestamp = 0;
                if (fCacheFile)
                {
                    rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX);
                    if (RT_FAILURE(rc))
                        return RTGetOptPrintError(rc, &ValueUnion);
                    cbImage = ValueUnion.u32;

                    rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX);
                    if (RT_FAILURE(rc))
                        return RTGetOptPrintError(rc, &ValueUnion);
                    uTimestamp = ValueUnion.u32;
                }

                RTDBGMOD hMod;
                if (enmOpenMethod == kOpenMethod_FromImage)
                    rc = RTDbgModCreateFromImage(&hMod, pszModule, NULL, enmArch, hDbgCfg);
                else
                    rc = RTDbgModCreateFromPeImage(&hMod, pszModule, NULL, NIL_RTLDRMOD, cbImage, uTimestamp, hDbgCfg);
                if (RT_FAILURE(rc))
                    return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgModCreateFromImage(,%s,,) -> %Rrc", pszModule, rc);

                rc = RTDbgAsModuleLink(hDbgAs, hMod, u64Address, 0 /* fFlags */);
                if (RT_FAILURE(rc))
                    return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgAsModuleLink(,%s,%llx,) -> %Rrc", pszModule, u64Address, rc);
                break;
            }

            default:
                return RTGetOptPrintError(rc, &ValueUnion);
        }
    }

    /*
     * Display the address space.
     */
    if (cVerbosityLevel)
    {
        RTPrintf("*** Address Space Dump ***\n");
        uint32_t cModules = RTDbgAsModuleCount(hDbgAs);
        for (uint32_t iModule = 0; iModule < cModules; iModule++)
        {
            RTDBGMOD        hDbgMod = RTDbgAsModuleByIndex(hDbgAs, iModule);
            RTPrintf("Module #%u: %s\n", iModule, RTDbgModName(hDbgMod));

            RTDBGASMAPINFO  aMappings[128];
            uint32_t        cMappings = RT_ELEMENTS(aMappings);
            rc = RTDbgAsModuleQueryMapByIndex(hDbgAs, iModule, &aMappings[0], &cMappings, 0 /*fFlags*/);
            if (RT_SUCCESS(rc))
            {
                for (uint32_t iMapping = 0; iMapping < cMappings; iMapping++)
                {
                    if (aMappings[iMapping].iSeg == NIL_RTDBGSEGIDX)
                        RTPrintf("  mapping #%u: %RTptr-%RTptr\n",
                                 iMapping,
                                 aMappings[iMapping].Address,
                                 aMappings[iMapping].Address + RTDbgModImageSize(hDbgMod) - 1);
                    else
                    {
                        RTDBGSEGMENT SegInfo;
                        rc = RTDbgModSegmentByIndex(hDbgMod, aMappings[iMapping].iSeg, &SegInfo);
                        if (RT_SUCCESS(rc))
                            RTPrintf("  mapping #%u: %RTptr-%RTptr (segment #%u - '%s')",
                                     iMapping,
                                     aMappings[iMapping].Address,
                                     aMappings[iMapping].Address + SegInfo.cb,
                                     SegInfo.iSeg, SegInfo.szName);
                        else
                            RTPrintf("  mapping #%u: %RTptr-???????? (segment #%u)", iMapping, aMappings[iMapping].Address);
                    }

                    if (cVerbosityLevel > 1)
                    {
                        uint32_t cSymbols = RTDbgModSymbolCount(hDbgMod);
                        RTPrintf("    %u symbols\n", cSymbols);
                        for (uint32_t iSymbol = 0; iSymbol < cSymbols; iSymbol++)
                        {
                            RTDBGSYMBOL SymInfo;
                            rc = RTDbgModSymbolByOrdinal(hDbgMod, iSymbol, &SymInfo);
                            if (RT_SUCCESS(rc))
                                RTPrintf("    #%04u at %08x:%RTptr %05llx %s\n",
                                         SymInfo.iOrdinal, SymInfo.iSeg, SymInfo.offSeg,
                                         (uint64_t)SymInfo.cb, SymInfo.szName);
                        }
                    }
                }
            }
            else
                RTMsgError("RTDbgAsModuleQueryMapByIndex failed: %Rrc", rc);
            RTDbgModRelease(hDbgMod);
        }
        RTPrintf("*** End of Address Space Dump ***\n");
    }

    /*
     * Read text from standard input and see if there is anything we can translate.
     */
    for (;;)
    {
        /* Get a line. */
        char szLine[_64K];
        rc = RTStrmGetLine(pInput, szLine, sizeof(szLine));
        if (rc == VERR_EOF)
            break;
        if (RT_FAILURE(rc))
            return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTStrmGetLine() -> %Rrc\n", rc);

        /*
         * Search the line for potential addresses and replace them with
         * symbols+offset.
         */
        const char *pszStart = szLine;
        const char *psz      = szLine;
        char        ch;
        while ((ch = *psz) != '\0')
        {
            size_t      cchAddress;
            uint64_t    u64Address;

            if (   (   ch == '0'
                    && (psz[1] == 'x' || psz[1] == 'X')
                    && TryParseAddress(psz, &cchAddress, &u64Address))
                || (   RT_C_IS_XDIGIT(ch)
                    && TryParseAddress(psz, &cchAddress, &u64Address))
               )
            {
                /* Print. */
                psz += cchAddress;
                if (pszStart != psz)
                    RTStrmWrite(pOutput, pszStart, psz - pszStart);
                pszStart = psz;

                /* Try get the module. */
                RTUINTPTR   uAddr;
                RTDBGSEGIDX iSeg;
                RTDBGMOD    hDbgMod;
                rc = RTDbgAsModuleByAddr(hDbgAs, u64Address, &hDbgMod, &uAddr, &iSeg);
                if (RT_SUCCESS(rc))
                {
                    if (iSeg != UINT32_MAX)
                        RTStrmPrintf(pOutput, "=[%s:%u", RTDbgModName(hDbgMod), iSeg);
                    else
                        RTStrmPrintf(pOutput, "=[%s", RTDbgModName(hDbgMod), iSeg);

                    /*
                     * Do we have symbols?
                     */
                    RTDBGSYMBOL Symbol;
                    RTINTPTR    offSym;
                    rc = RTDbgAsSymbolByAddr(hDbgAs, u64Address, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL, &offSym, &Symbol, NULL);
                    if (RT_SUCCESS(rc))
                    {
                        if (!offSym)
                            RTStrmPrintf(pOutput, "!%s", Symbol.szName);
                        else if (offSym > 0)
                            RTStrmPrintf(pOutput, "!%s+%#llx", Symbol.szName, offSym);
                        else
                            RTStrmPrintf(pOutput, "!%s-%#llx", Symbol.szName, -offSym);
                    }
                    else
                        RTStrmPrintf(pOutput, "+%#llx", u64Address - uAddr);

                    /*
                     * Do we have line numbers?
                     */
                    RTDBGLINE   Line;
                    RTINTPTR    offLine;
                    rc = RTDbgAsLineByAddr(hDbgAs, u64Address, &offLine, &Line, NULL);
                    if (RT_SUCCESS(rc))
                        RTStrmPrintf(pOutput, " %Rbn(%u)", Line.szFilename, Line.uLineNo);

                    RTStrmPrintf(pOutput, "]");
                    RTDbgModRelease(hDbgMod);
                }
            }
            else
                psz++;
        }

        if (pszStart != psz)
            RTStrmWrite(pOutput, pszStart, psz - pszStart);
        RTStrmPutCh(pOutput, '\n');

    }

    return RTEXITCODE_SUCCESS;
}
예제 #9
0
/**
 * 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;
}
/**
 * Populate DBGF_AS_RC with PATM symbols.
 *
 * Called by dbgfR3AsLazyPopulate when DBGF_AS_RC or DBGF_AS_RC_AND_GC_GLOBAL is
 * accessed for the first time.
 *
 * @param   pVM         The cross context VM structure.
 * @param   hDbgAs      The DBGF_AS_RC address space handle.
 */
VMMR3_INT_DECL(void) PATMR3DbgPopulateAddrSpace(PVM pVM, RTDBGAS hDbgAs)
{
    AssertReturnVoid(VM_IS_RAW_MODE_ENABLED(pVM));

    /*
     * Add a fake debug module for the PATMGCSTATE structure.
     */
    RTDBGMOD hDbgMod;
    int rc = RTDbgModCreate(&hDbgMod, "patmgcstate", sizeof(PATMGCSTATE), 0 /*fFlags*/);
    if (RT_SUCCESS(rc))
    {
        ADD_MEMBER(hDbgMod, PATMGCSTATE, uVMFlags,                  "uVMFlags");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, uPendingAction,            "uPendingAction");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, uPatchCalls,               "uPatchCalls");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, uScratch,                  "uScratch");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, uIretEFlags,               "uIretEFlags");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, uIretCS,                   "uIretCS");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, uIretEIP,                  "uIretEIP");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, Psp,                       "Psp");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, fPIF,                      "fPIF");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, GCPtrInhibitInterrupts,    "GCPtrInhibitInterrupts");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, GCCallPatchTargetAddr,     "GCCallPatchTargetAddr");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, GCCallReturnAddr,          "GCCallReturnAddr");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, Restore.uEAX,              "Restore.uEAX");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, Restore.uECX,              "Restore.uECX");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, Restore.uEDI,              "Restore.uEDI");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, Restore.eFlags,            "Restore.eFlags");
        ADD_MEMBER(hDbgMod, PATMGCSTATE, Restore.uFlags,            "Restore.uFlags");

        rc = RTDbgAsModuleLink(hDbgAs, hDbgMod, pVM->patm.s.pGCStateGC, 0 /*fFlags*/);
        AssertLogRelRC(rc);
        RTDbgModRelease(hDbgMod);
    }

    /*
     * Add something for the stats so we get some kind of symbols for
     * references to them while disassembling patches.
     */
    rc = RTDbgModCreate(&hDbgMod, "patmstats", PATM_STAT_MEMSIZE, 0 /*fFlags*/);
    if (RT_SUCCESS(rc))
    {
        ADD_FUNC(hDbgMod, pVM->patm.s.pStatsGC, pVM->patm.s.pStatsGC, PATM_STAT_MEMSIZE, "PATMMemStatsStart");

        rc = RTDbgAsModuleLink(hDbgAs, hDbgMod, pVM->patm.s.pStatsGC, 0 /*fFlags*/);
        AssertLogRelRC(rc);
        RTDbgModRelease(hDbgMod);
    }

    /*
     * Add a fake debug module for the patches and stack.
     */
    rc = RTDbgModCreate(&hDbgMod, "patches", pVM->patm.s.cbPatchMem + PATM_STACK_TOTAL_SIZE + PAGE_SIZE, 0 /*fFlags*/);
    if (RT_SUCCESS(rc))
    {
        pVM->patm.s.hDbgModPatchMem = hDbgMod;
        patmR3DbgAddPatches(pVM, hDbgMod);

        rc = RTDbgAsModuleLink(hDbgAs, hDbgMod, pVM->patm.s.pPatchMemGC, 0 /*fFlags*/);
        AssertLogRelRC(rc);
    }
}
예제 #11
0
/**
 * 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;
}
예제 #12
0
/**
 * @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;
}