/* $Id: DBGFAddrSpace.cpp $ */
/** @file
 * DBGF - Debugger Facility, Address Space Management.
 */

/*
 * Copyright (C) 2008-2020 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 */


/** @page pg_dbgf_addr_space     DBGFAddrSpace - Address Space Management
 *
 * What's an address space? It's mainly a convenient way of stuffing
 * module segments and ad-hoc symbols together. It will also help out
 * when the debugger gets extended to deal with user processes later.
 *
 * There are two standard address spaces that will always be present:
 *   - The physical address space.
 *   - The global virtual address space.
 *
 * Additional address spaces will be added and removed at runtime for
 * guest processes. The global virtual address space will be used to
 * track the kernel parts of the OS, or at least the bits of the kernel
 * that is part of all address spaces (mac os x and 4G/4G patched linux).
 *
 */


/*********************************************************************************************************************************
*   Header Files                                                                                                                 *
*********************************************************************************************************************************/
#define LOG_GROUP LOG_GROUP_DBGF
#include <VBox/vmm/dbgf.h>
#include <VBox/vmm/hm.h>
#include <VBox/vmm/pdmapi.h>
#include <VBox/vmm/mm.h>
#include "DBGFInternal.h"
#include <VBox/vmm/uvm.h>
#include <VBox/vmm/vm.h>
#include <VBox/err.h>
#include <VBox/log.h>

#include <iprt/asm.h>
#include <iprt/assert.h>
#include <iprt/ctype.h>
#include <iprt/env.h>
#include <iprt/mem.h>
#include <iprt/path.h>
#include <iprt/param.h>


/*********************************************************************************************************************************
*   Structures and Typedefs                                                                                                      *
*********************************************************************************************************************************/
/**
 * Address space database node.
 */
typedef struct DBGFASDBNODE
{
    /** The node core for DBGF::AsHandleTree, the key is the address space handle. */
    AVLPVNODECORE   HandleCore;
    /** The node core for DBGF::AsPidTree, the key is the process id. */
    AVLU32NODECORE  PidCore;
    /** The node core for DBGF::AsNameSpace, the string is the address space name. */
    RTSTRSPACECORE  NameCore;

} DBGFASDBNODE;
/** Pointer to an address space database node. */
typedef DBGFASDBNODE *PDBGFASDBNODE;


/**
 * For dbgfR3AsLoadImageOpenData and dbgfR3AsLoadMapOpenData.
 */
typedef struct DBGFR3ASLOADOPENDATA
{
    const char     *pszModName;
    RTGCUINTPTR     uSubtrahend;
    uint32_t        fFlags;
    RTDBGMOD        hMod;
} DBGFR3ASLOADOPENDATA;

#if 0 /* unused */
/**
 * Callback for dbgfR3AsSearchPath and dbgfR3AsSearchEnvPath.
 *
 * @returns VBox status code. If success, then the search is completed.
 * @param   pszFilename     The file name under evaluation.
 * @param   pvUser          The user argument.
 */
typedef int FNDBGFR3ASSEARCHOPEN(const char *pszFilename, void *pvUser);
/** Pointer to a FNDBGFR3ASSEARCHOPEN. */
typedef FNDBGFR3ASSEARCHOPEN *PFNDBGFR3ASSEARCHOPEN;
#endif


/*********************************************************************************************************************************
*   Defined Constants And Macros                                                                                                 *
*********************************************************************************************************************************/
/** Locks the address space database for writing. */
#define DBGF_AS_DB_LOCK_WRITE(pUVM) \
    do { \
        int rcSem = RTSemRWRequestWrite((pUVM)->dbgf.s.hAsDbLock, RT_INDEFINITE_WAIT); \
        AssertRC(rcSem); \
    } while (0)

/** Unlocks the address space database after writing. */
#define DBGF_AS_DB_UNLOCK_WRITE(pUVM) \
    do { \
        int rcSem = RTSemRWReleaseWrite((pUVM)->dbgf.s.hAsDbLock); \
        AssertRC(rcSem); \
    } while (0)

/** Locks the address space database for reading. */
#define DBGF_AS_DB_LOCK_READ(pUVM) \
    do { \
        int rcSem = RTSemRWRequestRead((pUVM)->dbgf.s.hAsDbLock, RT_INDEFINITE_WAIT); \
        AssertRC(rcSem); \
    } while (0)

/** Unlocks the address space database after reading. */
#define DBGF_AS_DB_UNLOCK_READ(pUVM) \
    do { \
        int rcSem = RTSemRWReleaseRead((pUVM)->dbgf.s.hAsDbLock); \
        AssertRC(rcSem); \
    } while (0)



/**
 * Initializes the address space parts of DBGF.
 *
 * @returns VBox status code.
 * @param   pUVM        The user mode VM handle.
 */
int dbgfR3AsInit(PUVM pUVM)
{
    Assert(pUVM->pVM);

    /*
     * Create the semaphore.
     */
    int rc = RTSemRWCreate(&pUVM->dbgf.s.hAsDbLock);
    AssertRCReturn(rc, rc);

    /*
     * Create the debugging config instance and set it up, defaulting to
     * deferred loading in order to keep things fast.
     */
    rc = RTDbgCfgCreate(&pUVM->dbgf.s.hDbgCfg, "VBOXDBG_", true /*fNativePaths*/);
    AssertRCReturn(rc, rc);
    rc = RTDbgCfgChangeUInt(pUVM->dbgf.s.hDbgCfg, RTDBGCFGPROP_FLAGS, RTDBGCFGOP_PREPEND,
                            RTDBGCFG_FLAGS_DEFERRED);
    AssertRCReturn(rc, rc);

    static struct
    {
        RTDBGCFGPROP    enmProp;
        const char     *pszEnvName;
        const char     *pszCfgName;
    } const s_aProps[] =
    {
        { RTDBGCFGPROP_FLAGS,               "VBOXDBG_FLAGS",            "Flags"             },
        { RTDBGCFGPROP_PATH,                "VBOXDBG_PATH",             "Path"              },
        { RTDBGCFGPROP_SUFFIXES,            "VBOXDBG_SUFFIXES",         "Suffixes"          },
        { RTDBGCFGPROP_SRC_PATH,            "VBOXDBG_SRC_PATH",         "SrcPath"           },
    };
    PCFGMNODE pCfgDbgf = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "/DBGF");
    for (unsigned i = 0; i < RT_ELEMENTS(s_aProps); i++)
    {
        char szEnvValue[8192];
        rc = RTEnvGetEx(RTENV_DEFAULT, s_aProps[i].pszEnvName, szEnvValue, sizeof(szEnvValue), NULL);
        if (RT_SUCCESS(rc))
        {
            rc = RTDbgCfgChangeString(pUVM->dbgf.s.hDbgCfg, s_aProps[i].enmProp, RTDBGCFGOP_PREPEND, szEnvValue);
            if (RT_FAILURE(rc))
                return VMR3SetError(pUVM, rc, RT_SRC_POS,
                                    "DBGF Config Error: %s=%s -> %Rrc", s_aProps[i].pszEnvName, szEnvValue, rc);
        }
        else if (rc != VERR_ENV_VAR_NOT_FOUND)
            return VMR3SetError(pUVM, rc, RT_SRC_POS,
                                "DBGF Config Error: Error querying env.var. %s: %Rrc", s_aProps[i].pszEnvName, rc);

        char *pszCfgValue;
        rc = CFGMR3QueryStringAllocDef(pCfgDbgf, s_aProps[i].pszCfgName, &pszCfgValue, NULL);
        if (RT_FAILURE(rc))
            return VMR3SetError(pUVM, rc, RT_SRC_POS,
                                "DBGF Config Error: Querying /DBGF/%s -> %Rrc", s_aProps[i].pszCfgName, rc);
        if (pszCfgValue)
        {
            rc = RTDbgCfgChangeString(pUVM->dbgf.s.hDbgCfg, s_aProps[i].enmProp, RTDBGCFGOP_PREPEND, pszCfgValue);
            if (RT_FAILURE(rc))
                return VMR3SetError(pUVM, rc, RT_SRC_POS,
                                    "DBGF Config Error: /DBGF/%s=%s -> %Rrc", s_aProps[i].pszCfgName, pszCfgValue, rc);
            MMR3HeapFree(pszCfgValue);
        }
    }

    /*
     * Prepend the NoArch and VBoxDbgSyms directories to the path.
     */
    char szPath[RTPATH_MAX];
    rc = RTPathAppPrivateNoArch(szPath, sizeof(szPath));
    AssertRCReturn(rc, rc);
#ifdef RT_OS_DARWIN
    rc = RTPathAppend(szPath, sizeof(szPath), "../Resources/VBoxDbgSyms/");
#else
    rc = RTDbgCfgChangeString(pUVM->dbgf.s.hDbgCfg, RTDBGCFGPROP_PATH, RTDBGCFGOP_PREPEND, szPath);
    AssertRCReturn(rc, rc);

    rc = RTPathAppend(szPath, sizeof(szPath), "VBoxDbgSyms/");
#endif
    AssertRCReturn(rc, rc);
    rc = RTDbgCfgChangeString(pUVM->dbgf.s.hDbgCfg, RTDBGCFGPROP_PATH, RTDBGCFGOP_PREPEND, szPath);
    AssertRCReturn(rc, rc);

    /*
     * Create the standard address spaces.
     */
    RTDBGAS hDbgAs;
    rc = RTDbgAsCreate(&hDbgAs, 0, RTGCPTR_MAX, "Global");
    AssertRCReturn(rc, rc);
    rc = DBGFR3AsAdd(pUVM, hDbgAs, NIL_RTPROCESS);
    AssertRCReturn(rc, rc);
    pUVM->dbgf.s.ahAsAliases[DBGF_AS_ALIAS_2_INDEX(DBGF_AS_GLOBAL)] = hDbgAs;

    RTDbgAsRetain(hDbgAs);
    pUVM->dbgf.s.ahAsAliases[DBGF_AS_ALIAS_2_INDEX(DBGF_AS_KERNEL)] = hDbgAs;

    rc = RTDbgAsCreate(&hDbgAs, 0, RTGCPHYS_MAX, "Physical");
    AssertRCReturn(rc, rc);
    rc = DBGFR3AsAdd(pUVM, hDbgAs, NIL_RTPROCESS);
    AssertRCReturn(rc, rc);
    pUVM->dbgf.s.ahAsAliases[DBGF_AS_ALIAS_2_INDEX(DBGF_AS_PHYS)] = hDbgAs;

    rc = RTDbgAsCreate(&hDbgAs, 0, RTRCPTR_MAX, "HyperRawMode");
    AssertRCReturn(rc, rc);
    rc = DBGFR3AsAdd(pUVM, hDbgAs, NIL_RTPROCESS);
    AssertRCReturn(rc, rc);
    pUVM->dbgf.s.ahAsAliases[DBGF_AS_ALIAS_2_INDEX(DBGF_AS_RC)] = hDbgAs;
    RTDbgAsRetain(hDbgAs);
    pUVM->dbgf.s.ahAsAliases[DBGF_AS_ALIAS_2_INDEX(DBGF_AS_RC_AND_GC_GLOBAL)] = hDbgAs;

    rc = RTDbgAsCreate(&hDbgAs, 0, RTR0PTR_MAX, "HyperRing0");
    AssertRCReturn(rc, rc);
    rc = DBGFR3AsAdd(pUVM, hDbgAs, NIL_RTPROCESS);
    AssertRCReturn(rc, rc);
    pUVM->dbgf.s.ahAsAliases[DBGF_AS_ALIAS_2_INDEX(DBGF_AS_R0)] = hDbgAs;

    return VINF_SUCCESS;
}


/**
 * Callback used by dbgfR3AsTerm / RTAvlPVDestroy to release an address space.
 *
 * @returns 0.
 * @param   pNode           The address space database node.
 * @param   pvIgnore        NULL.
 */
static DECLCALLBACK(int) dbgfR3AsTermDestroyNode(PAVLPVNODECORE pNode, void *pvIgnore)
{
    PDBGFASDBNODE pDbNode = (PDBGFASDBNODE)pNode;
    RTDbgAsRelease((RTDBGAS)pDbNode->HandleCore.Key);
    pDbNode->HandleCore.Key = NIL_RTDBGAS;
    /* Don't bother freeing it here as MM will free it soon and MM is much at
       it when doing it wholesale instead of piecemeal. */
    NOREF(pvIgnore);
    return 0;
}


/**
 * Terminates the address space parts of DBGF.
 *
 * @param   pUVM        The user mode VM handle.
 */
void dbgfR3AsTerm(PUVM pUVM)
{
    /*
     * Create the semaphore.
     */
    int rc = RTSemRWDestroy(pUVM->dbgf.s.hAsDbLock);
    AssertRC(rc);
    pUVM->dbgf.s.hAsDbLock = NIL_RTSEMRW;

    /*
     * Release all the address spaces.
     */
    RTAvlPVDestroy(&pUVM->dbgf.s.AsHandleTree, dbgfR3AsTermDestroyNode, NULL);
    for (size_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.ahAsAliases); i++)
    {
        RTDbgAsRelease(pUVM->dbgf.s.ahAsAliases[i]);
        pUVM->dbgf.s.ahAsAliases[i] = NIL_RTDBGAS;
    }

    /*
     * Release the reference to the debugging config.
     */
    rc = RTDbgCfgRelease(pUVM->dbgf.s.hDbgCfg);
    AssertRC(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 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);
    }
}


/**
 * Gets the IPRT debugging configuration handle (no refs retained).
 *
 * @returns Config handle or NIL_RTDBGCFG.
 * @param   pUVM        The user mode VM handle.
 */
VMMR3DECL(RTDBGCFG)     DBGFR3AsGetConfig(PUVM pUVM)
{
    UVM_ASSERT_VALID_EXT_RETURN(pUVM, NIL_RTDBGCFG);
    return pUVM->dbgf.s.hDbgCfg;
}


/**
 * Adds the address space to the database.
 *
 * @returns VBox status code.
 * @param   pUVM        The user mode VM handle.
 * @param   hDbgAs      The address space handle. The reference of the caller
 *                      will NOT be consumed.
 * @param   ProcId      The process id or NIL_RTPROCESS.
 */
VMMR3DECL(int) DBGFR3AsAdd(PUVM pUVM, RTDBGAS hDbgAs, RTPROCESS ProcId)
{
    /*
     * Input validation.
     */
    UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
    const char *pszName = RTDbgAsName(hDbgAs);
    if (!pszName)
        return VERR_INVALID_HANDLE;
    uint32_t cRefs = RTDbgAsRetain(hDbgAs);
    if (cRefs == UINT32_MAX)
        return VERR_INVALID_HANDLE;

    /*
     * Allocate a tracking node.
     */
    int rc = VERR_NO_MEMORY;
    PDBGFASDBNODE pDbNode = (PDBGFASDBNODE)MMR3HeapAllocU(pUVM, MM_TAG_DBGF_AS, sizeof(*pDbNode));
    if (pDbNode)
    {
        pDbNode->HandleCore.Key     = hDbgAs;
        pDbNode->PidCore.Key        = ProcId;
        pDbNode->NameCore.pszString = pszName;
        pDbNode->NameCore.cchString = strlen(pszName);
        DBGF_AS_DB_LOCK_WRITE(pUVM);
        if (RTStrSpaceInsert(&pUVM->dbgf.s.AsNameSpace, &pDbNode->NameCore))
        {
            if (RTAvlPVInsert(&pUVM->dbgf.s.AsHandleTree, &pDbNode->HandleCore))
            {
                DBGF_AS_DB_UNLOCK_WRITE(pUVM);
                return VINF_SUCCESS;
            }

            /* bail out */
            RTStrSpaceRemove(&pUVM->dbgf.s.AsNameSpace, pszName);
        }
        DBGF_AS_DB_UNLOCK_WRITE(pUVM);
        MMR3HeapFree(pDbNode);
    }
    RTDbgAsRelease(hDbgAs);
    return rc;
}


/**
 * Delete an address space from the database.
 *
 * The address space must not be engaged as any of the standard aliases.
 *
 * @returns VBox status code.
 * @retval  VERR_SHARING_VIOLATION if in use as an alias.
 * @retval  VERR_NOT_FOUND if not found in the address space database.
 *
 * @param   pUVM        The user mode VM handle.
 * @param   hDbgAs      The address space handle. Aliases are not allowed.
 */
VMMR3DECL(int) DBGFR3AsDelete(PUVM pUVM, RTDBGAS hDbgAs)
{
    /*
     * Input validation. Retain the address space so it can be released outside
     * the lock as well as validated.
     */
    UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
    if (hDbgAs == NIL_RTDBGAS)
        return VINF_SUCCESS;
    uint32_t cRefs = RTDbgAsRetain(hDbgAs);
    if (cRefs == UINT32_MAX)
        return VERR_INVALID_HANDLE;
    RTDbgAsRelease(hDbgAs);

    DBGF_AS_DB_LOCK_WRITE(pUVM);

    /*
     * You cannot delete any of the aliases.
     */
    for (size_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.ahAsAliases); i++)
        if (pUVM->dbgf.s.ahAsAliases[i] == hDbgAs)
        {
            DBGF_AS_DB_UNLOCK_WRITE(pUVM);
            return VERR_SHARING_VIOLATION;
        }

    /*
     * Ok, try remove it from the database.
     */
    PDBGFASDBNODE pDbNode = (PDBGFASDBNODE)RTAvlPVRemove(&pUVM->dbgf.s.AsHandleTree, hDbgAs);
    if (!pDbNode)
    {
        DBGF_AS_DB_UNLOCK_WRITE(pUVM);
        return VERR_NOT_FOUND;
    }
    RTStrSpaceRemove(&pUVM->dbgf.s.AsNameSpace, pDbNode->NameCore.pszString);
    if (pDbNode->PidCore.Key != NIL_RTPROCESS)
        RTAvlU32Remove(&pUVM->dbgf.s.AsPidTree, pDbNode->PidCore.Key);

    DBGF_AS_DB_UNLOCK_WRITE(pUVM);

    /*
     * Free the resources.
     */
    RTDbgAsRelease(hDbgAs);
    MMR3HeapFree(pDbNode);

    return VINF_SUCCESS;
}


/**
 * 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 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;
    }
    else
        RTDbgAsRelease(hRealAliasFor);
    DBGF_AS_DB_UNLOCK_WRITE(pUVM);

    return rc;
}


/**
 * @callback_method_impl{FNPDMR3ENUM}
 */
static DECLCALLBACK(int) dbgfR3AsLazyPopulateR0Callback(PVM pVM, const char *pszFilename, const char *pszName,
                                                        RTUINTPTR ImageBase, size_t cbImage, PDMLDRCTX enmCtx, void *pvArg)
{
    NOREF(pVM); NOREF(cbImage);

    /* Only ring-0 modules. */
    if (enmCtx == PDMLDRCTX_RING_0)
    {
        RTDBGMOD hDbgMod;
        int rc = RTDbgModCreateFromImage(&hDbgMod, pszFilename, pszName, RTLDRARCH_HOST, pVM->pUVM->dbgf.s.hDbgCfg);
        if (RT_SUCCESS(rc))
        {
            rc = RTDbgAsModuleLink((RTDBGAS)pvArg, hDbgMod, ImageBase, 0 /*fFlags*/);
            if (RT_FAILURE(rc))
                LogRel(("DBGF: Failed to link module \"%s\" into DBGF_AS_R0 at %RTptr: %Rrc\n",
                        pszName, ImageBase, rc));
        }
        else
            LogRel(("DBGF: RTDbgModCreateFromImage failed with rc=%Rrc for module \"%s\" (%s)\n",
                    rc, pszName, pszFilename));
    }
    return VINF_SUCCESS;
}


/**
 * @callback_method_impl{FNPDMR3ENUM}
 */
static DECLCALLBACK(int) dbgfR3AsLazyPopulateRCCallback(PVM pVM, const char *pszFilename, const char *pszName,
                                                        RTUINTPTR ImageBase, size_t cbImage, PDMLDRCTX enmCtx, void *pvArg)
{
    NOREF(pVM); NOREF(cbImage);

    /* Only raw-mode modules. */
    if (enmCtx == PDMLDRCTX_RAW_MODE)
    {
        RTDBGMOD hDbgMod;
        int rc = RTDbgModCreateFromImage(&hDbgMod, pszFilename, pszName, RTLDRARCH_X86_32, pVM->pUVM->dbgf.s.hDbgCfg);
        if (RT_SUCCESS(rc))
        {
            rc = RTDbgAsModuleLink((RTDBGAS)pvArg, hDbgMod, ImageBase, 0 /*fFlags*/);
            if (RT_FAILURE(rc))
                LogRel(("DBGF: Failed to link module \"%s\" into DBGF_AS_RC at %RTptr: %Rrc\n",
                        pszName, ImageBase, rc));
        }
        else
            LogRel(("DBGF: RTDbgModCreateFromImage failed with rc=%Rrc for module \"%s\" (%s)\n",
                    rc, pszName, pszFilename));
    }
    return VINF_SUCCESS;
}


/**
 * Lazily populates the specified address space.
 *
 * @param   pUVM        The user mode VM handle.
 * @param   hAlias      The alias.
 */
static void dbgfR3AsLazyPopulate(PUVM pUVM, RTDBGAS hAlias)
{
    DBGF_AS_DB_LOCK_WRITE(pUVM);
    uintptr_t iAlias = DBGF_AS_ALIAS_2_INDEX(hAlias);
    if (!pUVM->dbgf.s.afAsAliasPopuplated[iAlias])
    {
        RTDBGAS hDbgAs = pUVM->dbgf.s.ahAsAliases[iAlias];
        if (hAlias == DBGF_AS_R0 && pUVM->pVM)
            PDMR3LdrEnumModules(pUVM->pVM, dbgfR3AsLazyPopulateR0Callback, hDbgAs);
        else if (hAlias == DBGF_AS_RC && pUVM->pVM && VM_IS_RAW_MODE_ENABLED(pUVM->pVM))
        {
            LogRel(("DBGF: Lazy init of RC address space\n"));
            PDMR3LdrEnumModules(pUVM->pVM, dbgfR3AsLazyPopulateRCCallback, hDbgAs);
        }
        else if (hAlias == DBGF_AS_PHYS && pUVM->pVM)
        {
            /** @todo Lazy load pc and vga bios symbols or the EFI stuff. */
        }

        pUVM->dbgf.s.afAsAliasPopuplated[iAlias] = true;
    }
    DBGF_AS_DB_UNLOCK_WRITE(pUVM);
}


/**
 * Resolves the address space handle into a real handle if it's an alias.
 *
 * @returns Real address space handle. NIL_RTDBGAS if invalid handle.
 *
 * @param   pUVM        The user mode VM handle.
 * @param   hAlias      The possibly address space alias.
 *
 * @remarks Doesn't take any locks.
 */
VMMR3DECL(RTDBGAS) DBGFR3AsResolve(PUVM pUVM, RTDBGAS hAlias)
{
    UVM_ASSERT_VALID_EXT_RETURN(pUVM, NULL);
    AssertCompileNS(NIL_RTDBGAS == (RTDBGAS)0);

    uintptr_t   iAlias = DBGF_AS_ALIAS_2_INDEX(hAlias);
    if (iAlias < DBGF_AS_COUNT)
        ASMAtomicReadHandle(&pUVM->dbgf.s.ahAsAliases[iAlias], &hAlias);
    return hAlias;
}


/**
 * Resolves the address space handle into a real handle if it's an alias,
 * and retains whatever it is.
 *
 * @returns Real address space handle. NIL_RTDBGAS if invalid handle.
 *
 * @param   pUVM        The user mode VM handle.
 * @param   hAlias      The possibly address space alias.
 */
VMMR3DECL(RTDBGAS) DBGFR3AsResolveAndRetain(PUVM pUVM, RTDBGAS hAlias)
{
    UVM_ASSERT_VALID_EXT_RETURN(pUVM, NULL);
    AssertCompileNS(NIL_RTDBGAS == (RTDBGAS)0);

    uint32_t    cRefs;
    uintptr_t   iAlias = DBGF_AS_ALIAS_2_INDEX(hAlias);
    if (iAlias < DBGF_AS_COUNT)
    {
        if (DBGF_AS_IS_FIXED_ALIAS(hAlias))
        {
            /* Perform lazy address space population. */
            if (!pUVM->dbgf.s.afAsAliasPopuplated[iAlias])
                dbgfR3AsLazyPopulate(pUVM, hAlias);

            /* Won't ever change, no need to grab the lock. */
            hAlias = pUVM->dbgf.s.ahAsAliases[iAlias];
            cRefs = RTDbgAsRetain(hAlias);
        }
        else
        {
            /* May change, grab the lock so we can read it safely. */
            DBGF_AS_DB_LOCK_READ(pUVM);
            hAlias = pUVM->dbgf.s.ahAsAliases[iAlias];
            cRefs = RTDbgAsRetain(hAlias);
            DBGF_AS_DB_UNLOCK_READ(pUVM);
        }
    }
    else
        /* Not an alias, just retain it. */
        cRefs = RTDbgAsRetain(hAlias);

    return cRefs != UINT32_MAX ? hAlias : NIL_RTDBGAS;
}


/**
 * Query an address space by name.
 *
 * @returns Retained address space handle if found, NIL_RTDBGAS if not.
 *
 * @param   pUVM        The user mode VM handle.
 * @param   pszName     The name.
 */
VMMR3DECL(RTDBGAS) DBGFR3AsQueryByName(PUVM pUVM, const char *pszName)
{
    /*
     * Validate the input.
     */
    UVM_ASSERT_VALID_EXT_RETURN(pUVM, NIL_RTDBGAS);
    AssertPtrReturn(pszName, NIL_RTDBGAS);
    AssertReturn(*pszName, NIL_RTDBGAS);

    /*
     * Look it up in the string space and retain the result.
     */
    RTDBGAS hDbgAs = NIL_RTDBGAS;
    DBGF_AS_DB_LOCK_READ(pUVM);

    PRTSTRSPACECORE pNode = RTStrSpaceGet(&pUVM->dbgf.s.AsNameSpace, pszName);
    if (pNode)
    {
        PDBGFASDBNODE pDbNode = RT_FROM_MEMBER(pNode, DBGFASDBNODE, NameCore);
        hDbgAs = (RTDBGAS)pDbNode->HandleCore.Key;
        uint32_t cRefs = RTDbgAsRetain(hDbgAs);
        if (RT_UNLIKELY(cRefs == UINT32_MAX))
            hDbgAs = NIL_RTDBGAS;
    }

    DBGF_AS_DB_UNLOCK_READ(pUVM);
    return hDbgAs;
}


/**
 * Query an address space by process ID.
 *
 * @returns Retained address space handle if found, NIL_RTDBGAS if not.
 *
 * @param   pUVM        The user mode VM handle.
 * @param   ProcId      The process ID.
 */
VMMR3DECL(RTDBGAS) DBGFR3AsQueryByPid(PUVM pUVM, RTPROCESS ProcId)
{
    /*
     * Validate the input.
     */
    UVM_ASSERT_VALID_EXT_RETURN(pUVM, NIL_RTDBGAS);
    AssertReturn(ProcId != NIL_RTPROCESS, NIL_RTDBGAS);

    /*
     * Look it up in the PID tree and retain the result.
     */
    RTDBGAS hDbgAs = NIL_RTDBGAS;
    DBGF_AS_DB_LOCK_READ(pUVM);

    PAVLU32NODECORE pNode = RTAvlU32Get(&pUVM->dbgf.s.AsPidTree, ProcId);
    if (pNode)
    {
        PDBGFASDBNODE pDbNode = RT_FROM_MEMBER(pNode, DBGFASDBNODE, PidCore);
        hDbgAs = (RTDBGAS)pDbNode->HandleCore.Key;
        uint32_t cRefs = RTDbgAsRetain(hDbgAs);
        if (RT_UNLIKELY(cRefs == UINT32_MAX))
            hDbgAs = NIL_RTDBGAS;
    }
    DBGF_AS_DB_UNLOCK_READ(pUVM);

    return hDbgAs;
}

#if 0 /* unused */

/**
 * Searches for the file in the path.
 *
 * The file is first tested without any path modification, then we walk the path
 * looking in each directory.
 *
 * @returns VBox status code.
 * @param   pszFilename     The file to search for.
 * @param   pszPath         The search path.
 * @param   pfnOpen         The open callback function.
 * @param   pvUser          User argument for the callback.
 */
static int dbgfR3AsSearchPath(const char *pszFilename, const char *pszPath, PFNDBGFR3ASSEARCHOPEN pfnOpen, void *pvUser)
{
    char szFound[RTPATH_MAX];

    /* Check the filename length. */
    size_t const    cchFilename = strlen(pszFilename);
    if (cchFilename >= sizeof(szFound))
        return VERR_FILENAME_TOO_LONG;
    const char     *pszName = RTPathFilename(pszFilename);
    if (!pszName)
        return VERR_IS_A_DIRECTORY;
    size_t const    cchName = strlen(pszName);

    /*
     * Try default location first.
     */
    memcpy(szFound, pszFilename, cchFilename + 1);
    int rc = pfnOpen(szFound, pvUser);
    if (RT_SUCCESS(rc))
        return rc;

    /*
     * Walk the search path.
     */
    const char *psz = pszPath;
    while (*psz)
    {
        /* Skip leading blanks - no directories with leading spaces, thank you. */
        while (RT_C_IS_BLANK(*psz))
            psz++;

        /* Find the end of this element. */
        const char *pszNext;
        const char *pszEnd = strchr(psz, ';');
        if (!pszEnd)
            pszEnd = pszNext = strchr(psz, '\0');
        else
            pszNext = pszEnd + 1;
        if (pszEnd != psz)
        {
            size_t const cch = pszEnd - psz;
            if (cch + 1 + cchName < sizeof(szFound))
            {
                /** @todo RTPathCompose, RTPathComposeN(). This code isn't right
                 * for 'E:' on DOS systems. It may also create unwanted double slashes. */
                memcpy(szFound, psz, cch);
                szFound[cch] = '/';
                memcpy(szFound + cch + 1, pszName, cchName + 1);
                int rc2 = pfnOpen(szFound, pvUser);
                if (RT_SUCCESS(rc2))
                    return rc2;
                if (    rc2 != rc
                    &&  (   rc == VERR_FILE_NOT_FOUND
                         || rc == VERR_OPEN_FAILED))
                    rc = rc2;
            }
        }

        /* advance */
        psz = pszNext;
    }

    /*
     * Walk the path once again, this time do a depth search.
     */
    /** @todo do a depth search using the specified path. */

    /* failed */
    return rc;
}


/**
 * Same as dbgfR3AsSearchEnv, except that the path is taken from the environment.
 *
 * If the environment variable doesn't exist, the current directory is searched
 * instead.
 *
 * @returns VBox status code.
 * @param   pszFilename     The filename.
 * @param   pszEnvVar       The environment variable name.
 * @param   pfnOpen         The open callback function.
 * @param   pvUser          User argument for the callback.
 */
static int dbgfR3AsSearchEnvPath(const char *pszFilename, const char *pszEnvVar, PFNDBGFR3ASSEARCHOPEN pfnOpen, void *pvUser)
{
    int     rc;
    char   *pszPath = RTEnvDupEx(RTENV_DEFAULT, pszEnvVar);
    if (pszPath)
    {
        rc = dbgfR3AsSearchPath(pszFilename, pszPath, pfnOpen, pvUser);
        RTStrFree(pszPath);
    }
    else
        rc = dbgfR3AsSearchPath(pszFilename, ".", pfnOpen, pvUser);
    return rc;
}


/**
 * Same as dbgfR3AsSearchEnv, except that the path is taken from the DBGF config
 * (CFGM).
 *
 * Nothing is done if the CFGM variable isn't set.
 *
 * @returns VBox status code.
 * @param   pUVM            The user mode VM handle.
 * @param   pszFilename     The filename.
 * @param   pszCfgValue     The name of the config variable (under /DBGF/).
 * @param   pfnOpen         The open callback function.
 * @param   pvUser          User argument for the callback.
 */
static int dbgfR3AsSearchCfgPath(PUVM pUVM, const char *pszFilename, const char *pszCfgValue,
                                 PFNDBGFR3ASSEARCHOPEN pfnOpen, void *pvUser)
{
    char *pszPath;
    int rc = CFGMR3QueryStringAllocDef(CFGMR3GetChild(CFGMR3GetRootU(pUVM), "/DBGF"), pszCfgValue, &pszPath, NULL);
    if (RT_FAILURE(rc))
        return rc;
    if (!pszPath)
        return VERR_FILE_NOT_FOUND;
    rc = dbgfR3AsSearchPath(pszFilename, pszPath, pfnOpen, pvUser);
    MMR3HeapFree(pszPath);
    return rc;
}

#endif /* unused */


/**
 * 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          For DBGFR3AsLinkModule, see RTDBGASLINK_FLAGS_*.
 */
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 & ~RTDBGASLINK_FLAGS_VALID_MASK), 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, fFlags & RTDBGASLINK_FLAGS_VALID_MASK);
        if (RT_FAILURE(rc))
            RTDbgModRelease(hDbgMod);
    }

    RTDbgAsRelease(hRealAS);
    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;

    RTDBGMOD hDbgMod;
    int rc = RTDbgModCreateFromMap(&hDbgMod, pszFilename, pszModName, uSubtrahend, 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;
}


/**
 * Wrapper around RTDbgAsModuleLink, RTDbgAsModuleLinkSeg and DBGFR3AsResolve.
 *
 * @returns VBox status code.
 * @param   pUVM            The user mode VM handle.
 * @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(PUVM pUVM, RTDBGAS hDbgAs, RTDBGMOD hMod, PCDBGFADDRESS pModAddress,
                                  RTDBGSEGIDX iModSeg, uint32_t fFlags)
{
    /*
     * Input validation.
     */
    UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
    AssertReturn(DBGFR3AddrIsValid(pUVM, pModAddress), VERR_INVALID_PARAMETER);
    RTDBGAS hRealAS = DBGFR3AsResolveAndRetain(pUVM, 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;
}


/**
 * Adds the module name to the symbol name.
 *
 * @param   pSymbol         The symbol info (in/out).
 * @param   hMod            The module handle.
 */
static void dbgfR3AsSymbolJoinNames(PRTDBGSYMBOL pSymbol, RTDBGMOD hMod)
{
    /* Figure the lengths, adjust them if the result is too long. */
    const char *pszModName = RTDbgModName(hMod);
    size_t      cchModName = strlen(pszModName);
    size_t      cchSymbol  = strlen(pSymbol->szName);
    if (cchModName + 1 + cchSymbol >= sizeof(pSymbol->szName))
    {
        if (cchModName >= sizeof(pSymbol->szName) / 4)
            cchModName = sizeof(pSymbol->szName) / 4;
        if (cchModName + 1 + cchSymbol >= sizeof(pSymbol->szName))
            cchSymbol = sizeof(pSymbol->szName) - cchModName - 2;
        Assert(cchModName + 1 + cchSymbol < sizeof(pSymbol->szName));
    }

    /* Do the moving and copying. */
    memmove(&pSymbol->szName[cchModName + 1], &pSymbol->szName[0], cchSymbol + 1);
    memcpy(&pSymbol->szName[0], pszModName, cchModName);
    pSymbol->szName[cchModName] = '!';
}


/**
 * 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);
    }

    RTDbgAsRelease(hRealAS);
    return rc;
}


/**
 * Convenience function that combines RTDbgSymbolDup and DBGFR3AsSymbolByAddr.
 *
 * @returns Pointer to the symbol on success. This must be free using
 *          RTDbgSymbolFree(). NULL is returned if not found or any error
 *          occurs.
 *
 * @param   pUVM            The user mode VM handle.
 * @param   hDbgAs          See DBGFR3AsSymbolByAddr.
 * @param   pAddress        See DBGFR3AsSymbolByAddr.
 * @param   fFlags          See DBGFR3AsSymbolByAddr.
 * @param   poffDisp        See DBGFR3AsSymbolByAddr.
 * @param   phMod           See DBGFR3AsSymbolByAddr.
 */
VMMR3DECL(PRTDBGSYMBOL) DBGFR3AsSymbolByAddrA(PUVM pUVM, RTDBGAS hDbgAs, PCDBGFADDRESS pAddress, uint32_t fFlags,
                                              PRTGCINTPTR poffDisp, PRTDBGMOD phMod)
{
    RTDBGSYMBOL SymInfo;
    int rc = DBGFR3AsSymbolByAddr(pUVM, hDbgAs, pAddress, fFlags, poffDisp, &SymInfo, phMod);
    if (RT_SUCCESS(rc))
        return RTDbgSymbolDup(&SymInfo);
    return NULL;
}


/**
 * 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);
    }

    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.
     */
    int rc = RTDbgAsLineByAddr(hRealAS, pAddress->FlatPtr, poffDisp, pLine, phMod);

    RTDbgAsRelease(hRealAS);
    return rc;
}


VMMR3DECL(PRTDBGLINE)   DBGFR3AsLineByAddrA(PUVM pUVM, RTDBGAS hDbgAs, PCDBGFADDRESS pAddress,
                                            PRTGCINTPTR poffDisp, PRTDBGMOD phMod)
{
    RTDBGLINE Line;
    int rc = DBGFR3AsLineByAddr(pUVM, hDbgAs, pAddress, poffDisp, &Line, phMod);
    if (RT_SUCCESS(rc))
        return RTDbgLineDup(&Line);
    return NULL;
}

