/* $Id: dbgmod.cpp $ */
/** @file
 * IPRT - Debug Module Interpreter.
 */

/*
 * Copyright (C) 2009-2019 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.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
 * VirtualBox OSE distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 */


/*********************************************************************************************************************************
*   Header Files                                                                                                                 *
*********************************************************************************************************************************/
#define LOG_GROUP RTLOGGROUP_DBG
#include <iprt/dbg.h>
#include "internal/iprt.h"

#include <iprt/alloca.h>
#include <iprt/asm.h>
#include <iprt/assert.h>
#include <iprt/avl.h>
#include <iprt/err.h>
#include <iprt/initterm.h>
#include <iprt/log.h>
#include <iprt/mem.h>
#include <iprt/once.h>
#include <iprt/param.h>
#include <iprt/path.h>
#include <iprt/semaphore.h>
#include <iprt/strcache.h>
#include <iprt/string.h>
#include <iprt/uuid.h>
#include "internal/dbgmod.h"
#include "internal/magics.h"


/*********************************************************************************************************************************
*   Structures and Typedefs                                                                                                      *
*********************************************************************************************************************************/
/** Debug info interpreter registration record. */
typedef struct RTDBGMODREGDBG
{
    /** Pointer to the next record. */
    struct RTDBGMODREGDBG  *pNext;
    /** Pointer to the virtual function table for the interpreter.  */
    PCRTDBGMODVTDBG         pVt;
    /** Usage counter.  */
    uint32_t volatile       cUsers;
} RTDBGMODREGDBG;
typedef RTDBGMODREGDBG *PRTDBGMODREGDBG;

/** Image interpreter registration record. */
typedef struct RTDBGMODREGIMG
{
    /** Pointer to the next record. */
    struct RTDBGMODREGIMG  *pNext;
    /** Pointer to the virtual function table for the interpreter.  */
    PCRTDBGMODVTIMG         pVt;
    /** Usage counter.  */
    uint32_t volatile       cUsers;
} RTDBGMODREGIMG;
typedef RTDBGMODREGIMG *PRTDBGMODREGIMG;


/*********************************************************************************************************************************
*   Defined Constants And Macros                                                                                                 *
*********************************************************************************************************************************/
/** Validates a debug module handle and returns rc if not valid. */
#define RTDBGMOD_VALID_RETURN_RC(pDbgMod, rc) \
    do { \
        AssertPtrReturn((pDbgMod), (rc)); \
        AssertReturn((pDbgMod)->u32Magic == RTDBGMOD_MAGIC, (rc)); \
        AssertReturn((pDbgMod)->cRefs > 0, (rc)); \
    } while (0)

/** Locks the debug module. */
#define RTDBGMOD_LOCK(pDbgMod) \
    do { \
        int rcLock = RTCritSectEnter(&(pDbgMod)->CritSect); \
        AssertRC(rcLock); \
    } while (0)

/** Unlocks the debug module. */
#define RTDBGMOD_UNLOCK(pDbgMod) \
    do { \
        int rcLock = RTCritSectLeave(&(pDbgMod)->CritSect); \
        AssertRC(rcLock); \
    } while (0)


/*********************************************************************************************************************************
*   Global Variables                                                                                                             *
*********************************************************************************************************************************/
/** Init once object for lazy registration of the built-in image and debug
 * info interpreters. */
static RTONCE           g_rtDbgModOnce = RTONCE_INITIALIZER;
/** Read/Write semaphore protecting the list of registered interpreters.  */
static RTSEMRW          g_hDbgModRWSem = NIL_RTSEMRW;
/** List of registered image interpreters.  */
static PRTDBGMODREGIMG  g_pImgHead;
/** List of registered debug infor interpreters.  */
static PRTDBGMODREGDBG  g_pDbgHead;
/** String cache for the debug info interpreters.
 * RTSTRCACHE is thread safe. */
DECLHIDDEN(RTSTRCACHE)  g_hDbgModStrCache = NIL_RTSTRCACHE;





/**
 * Cleanup debug info interpreter globals.
 *
 * @param   enmReason           The cause of the termination.
 * @param   iStatus             The meaning of this depends on enmReason.
 * @param   pvUser              User argument, unused.
 */
static DECLCALLBACK(void) rtDbgModTermCallback(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
{
    NOREF(iStatus); NOREF(pvUser);
    if (enmReason == RTTERMREASON_UNLOAD)
    {
        RTSemRWDestroy(g_hDbgModRWSem);
        g_hDbgModRWSem = NIL_RTSEMRW;

        RTStrCacheDestroy(g_hDbgModStrCache);
        g_hDbgModStrCache = NIL_RTSTRCACHE;

        PRTDBGMODREGDBG pDbg = g_pDbgHead;
        g_pDbgHead = NULL;
        while (pDbg)
        {
            PRTDBGMODREGDBG pNext = pDbg->pNext;
            AssertMsg(pDbg->cUsers == 0, ("%#x %s\n", pDbg->cUsers, pDbg->pVt->pszName));
            RTMemFree(pDbg);
            pDbg = pNext;
        }

        PRTDBGMODREGIMG pImg = g_pImgHead;
        g_pImgHead = NULL;
        while (pImg)
        {
            PRTDBGMODREGIMG pNext = pImg->pNext;
            AssertMsg(pImg->cUsers == 0, ("%#x %s\n", pImg->cUsers, pImg->pVt->pszName));
            RTMemFree(pImg);
            pImg = pNext;
        }
    }
}


/**
 * Internal worker for register a debug interpreter.
 *
 * Called while owning the write lock or when locking isn't required.
 *
 * @returns IPRT status code.
 * @retval  VERR_NO_MEMORY
 * @retval  VERR_ALREADY_EXISTS
 *
 * @param   pVt                 The virtual function table of the debug
 *                              module interpreter.
 */
static int rtDbgModDebugInterpreterRegister(PCRTDBGMODVTDBG pVt)
{
    /*
     * Search or duplicate registration.
     */
    PRTDBGMODREGDBG pPrev = NULL;
    for (PRTDBGMODREGDBG pCur = g_pDbgHead; pCur; pCur = pCur->pNext)
    {
        if (pCur->pVt == pVt)
            return VERR_ALREADY_EXISTS;
        if (!strcmp(pCur->pVt->pszName, pVt->pszName))
            return VERR_ALREADY_EXISTS;
        pPrev = pCur;
    }

    /*
     * Create a new record and add it to the end of the list.
     */
    PRTDBGMODREGDBG pReg = (PRTDBGMODREGDBG)RTMemAlloc(sizeof(*pReg));
    if (!pReg)
        return VERR_NO_MEMORY;
    pReg->pVt    = pVt;
    pReg->cUsers = 0;
    pReg->pNext  = NULL;
    if (pPrev)
        pPrev->pNext = pReg;
    else
        g_pDbgHead   = pReg;
    return VINF_SUCCESS;
}


/**
 * Internal worker for register a image interpreter.
 *
 * Called while owning the write lock or when locking isn't required.
 *
 * @returns IPRT status code.
 * @retval  VERR_NO_MEMORY
 * @retval  VERR_ALREADY_EXISTS
 *
 * @param   pVt                 The virtual function table of the image
 *                              interpreter.
 */
static int rtDbgModImageInterpreterRegister(PCRTDBGMODVTIMG pVt)
{
    /*
     * Search or duplicate registration.
     */
    PRTDBGMODREGIMG pPrev = NULL;
    for (PRTDBGMODREGIMG pCur = g_pImgHead; pCur; pCur = pCur->pNext)
    {
        if (pCur->pVt == pVt)
            return VERR_ALREADY_EXISTS;
        if (!strcmp(pCur->pVt->pszName, pVt->pszName))
            return VERR_ALREADY_EXISTS;
        pPrev = pCur;
    }

    /*
     * Create a new record and add it to the end of the list.
     */
    PRTDBGMODREGIMG pReg = (PRTDBGMODREGIMG)RTMemAlloc(sizeof(*pReg));
    if (!pReg)
        return VERR_NO_MEMORY;
    pReg->pVt    = pVt;
    pReg->cUsers = 0;
    pReg->pNext  = NULL;
    if (pPrev)
        pPrev->pNext = pReg;
    else
        g_pImgHead   = pReg;
    return VINF_SUCCESS;
}


/**
 * Do-once callback that initializes the read/write semaphore and registers
 * the built-in interpreters.
 *
 * @returns IPRT status code.
 * @param   pvUser      NULL.
 */
static DECLCALLBACK(int) rtDbgModInitOnce(void *pvUser)
{
    NOREF(pvUser);

    /*
     * Create the semaphore and string cache.
     */
    int rc = RTSemRWCreate(&g_hDbgModRWSem);
    AssertRCReturn(rc, rc);

    rc = RTStrCacheCreate(&g_hDbgModStrCache, "RTDBGMOD");
    if (RT_SUCCESS(rc))
    {
        /*
         * Register the interpreters.
         */
        rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgNm);
        if (RT_SUCCESS(rc))
            rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgMapSym);
        if (RT_SUCCESS(rc))
            rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgDwarf);
        if (RT_SUCCESS(rc))
            rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgCodeView);
#ifdef RT_OS_WINDOWS
        if (RT_SUCCESS(rc))
            rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgDbgHelp);
#endif
        if (RT_SUCCESS(rc))
            rc = rtDbgModImageInterpreterRegister(&g_rtDbgModVtImgLdr);
        if (RT_SUCCESS(rc))
        {
            /*
             * Finally, register the IPRT cleanup callback.
             */
            rc = RTTermRegisterCallback(rtDbgModTermCallback, NULL);
            if (RT_SUCCESS(rc))
                return VINF_SUCCESS;

            /* bail out: use the termination callback. */
        }
    }
    else
        g_hDbgModStrCache = NIL_RTSTRCACHE;
    rtDbgModTermCallback(RTTERMREASON_UNLOAD, 0, NULL);
    return rc;
}


/**
 * Performs lazy init of our global variables.
 * @returns IPRT status code.
 */
DECLINLINE(int) rtDbgModLazyInit(void)
{
    return RTOnce(&g_rtDbgModOnce, rtDbgModInitOnce, NULL);
}


RTDECL(int) RTDbgModCreate(PRTDBGMOD phDbgMod, const char *pszName, RTUINTPTR cbSeg, uint32_t fFlags)
{
    /*
     * Input validation and lazy initialization.
     */
    AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER);
    *phDbgMod = NIL_RTDBGMOD;
    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
    AssertReturn(*pszName, VERR_INVALID_PARAMETER);
    AssertReturn(fFlags == 0 || fFlags == RTDBGMOD_F_NOT_DEFERRED, VERR_INVALID_PARAMETER);

    int rc = rtDbgModLazyInit();
    if (RT_FAILURE(rc))
        return rc;

    /*
     * Allocate a new module instance.
     */
    PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod));
    if (!pDbgMod)
        return VERR_NO_MEMORY;
    pDbgMod->u32Magic = RTDBGMOD_MAGIC;
    pDbgMod->cRefs = 1;
    rc = RTCritSectInit(&pDbgMod->CritSect);
    if (RT_SUCCESS(rc))
    {
        pDbgMod->pszImgFileSpecified = RTStrCacheEnter(g_hDbgModStrCache, pszName);
        pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, RTPathFilenameEx(pszName, RTPATH_STR_F_STYLE_DOS));
        if (pDbgMod->pszName)
        {
            rc = rtDbgModContainerCreate(pDbgMod, cbSeg);
            if (RT_SUCCESS(rc))
            {
                *phDbgMod = pDbgMod;
                return rc;
            }
            RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
            RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
        }
        RTCritSectDelete(&pDbgMod->CritSect);
    }

    RTMemFree(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModCreate);


RTDECL(int) RTDbgModCreateFromMap(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName,
                                  RTUINTPTR uSubtrahend, RTDBGCFG hDbgCfg)
{
    RT_NOREF_PV(hDbgCfg);

    /*
     * Input validation and lazy initialization.
     */
    AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER);
    *phDbgMod = NIL_RTDBGMOD;
    AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
    AssertReturn(*pszFilename, VERR_INVALID_PARAMETER);
    AssertPtrNullReturn(pszName, VERR_INVALID_POINTER);
    AssertReturn(uSubtrahend == 0, VERR_NOT_IMPLEMENTED); /** @todo implement uSubtrahend. */

    int rc = rtDbgModLazyInit();
    if (RT_FAILURE(rc))
        return rc;

    if (!pszName)
        pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS);

    /*
     * Allocate a new module instance.
     */
    PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod));
    if (!pDbgMod)
        return VERR_NO_MEMORY;
    pDbgMod->u32Magic = RTDBGMOD_MAGIC;
    pDbgMod->cRefs = 1;
    rc = RTCritSectInit(&pDbgMod->CritSect);
    if (RT_SUCCESS(rc))
    {
        pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName);
        if (pDbgMod->pszName)
        {
            pDbgMod->pszDbgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
            if (pDbgMod->pszDbgFile)
            {
                /*
                 * Try the map file readers.
                 */
                rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
                if (RT_SUCCESS(rc))
                {
                    rc = VERR_DBG_NO_MATCHING_INTERPRETER;
                    for (PRTDBGMODREGDBG pCur = g_pDbgHead; pCur; pCur = pCur->pNext)
                    {
                        if (pCur->pVt->fSupports & RT_DBGTYPE_MAP)
                        {
                            pDbgMod->pDbgVt = pCur->pVt;
                            pDbgMod->pvDbgPriv = NULL;
                            rc = pCur->pVt->pfnTryOpen(pDbgMod, RTLDRARCH_WHATEVER);
                            if (RT_SUCCESS(rc))
                            {
                                ASMAtomicIncU32(&pCur->cUsers);
                                RTSemRWReleaseRead(g_hDbgModRWSem);

                                *phDbgMod = pDbgMod;
                                return rc;
                            }
                        }
                    }

                    /* bail out */
                    RTSemRWReleaseRead(g_hDbgModRWSem);
                }
                RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
            }
            else
                rc = VERR_NO_STR_MEMORY;
            RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile);
        }
        else
            rc = VERR_NO_STR_MEMORY;
        RTCritSectDelete(&pDbgMod->CritSect);
    }

    RTMemFree(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModCreateFromMap);



/*
 *
 *  E x e c u t a b l e   I m a g e   F i l e s
 *  E x e c u t a b l e   I m a g e   F i l e s
 *  E x e c u t a b l e   I m a g e   F i l e s
 *
 */


/**
 * Opens debug information for an image.
 *
 * @returns IPRT status code
 * @param   pDbgMod             The debug module structure.
 *
 * @note    This will generally not look for debug info stored in external
 *          files.  rtDbgModFromPeImageExtDbgInfoCallback can help with that.
 */
static int rtDbgModOpenDebugInfoInsideImage(PRTDBGMODINT pDbgMod)
{
    AssertReturn(!pDbgMod->pDbgVt, VERR_DBG_MOD_IPE);
    AssertReturn(pDbgMod->pImgVt, VERR_DBG_MOD_IPE);

    int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
    if (RT_SUCCESS(rc))
    {
        for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext)
        {
            pDbgMod->pDbgVt    = pDbg->pVt;
            pDbgMod->pvDbgPriv = NULL;
            rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod));
            if (RT_SUCCESS(rc))
            {
                /*
                 * That's it!
                 */
                ASMAtomicIncU32(&pDbg->cUsers);
                RTSemRWReleaseRead(g_hDbgModRWSem);
                return VINF_SUCCESS;
            }

            pDbgMod->pDbgVt = NULL;
            Assert(pDbgMod->pvDbgPriv == NULL);
        }
        RTSemRWReleaseRead(g_hDbgModRWSem);
    }

    return VERR_DBG_NO_MATCHING_INTERPRETER;
}


/** @callback_method_impl{FNRTDBGCFGOPEN} */
static DECLCALLBACK(int) rtDbgModExtDbgInfoOpenCallback(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2)
{
    PRTDBGMODINT        pDbgMod   = (PRTDBGMODINT)pvUser1;
    PCRTLDRDBGINFO      pDbgInfo  = (PCRTLDRDBGINFO)pvUser2;
    RT_NOREF_PV(pDbgInfo); /** @todo consider a more direct search for a interpreter. */
    RT_NOREF_PV(hDbgCfg);

    Assert(!pDbgMod->pDbgVt);
    Assert(!pDbgMod->pvDbgPriv);
    Assert(!pDbgMod->pszDbgFile);
    Assert(pDbgMod->pImgVt);

    /*
     * Set the debug file name and try possible interpreters.
     */
    pDbgMod->pszDbgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);

    int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
    if (RT_SUCCESS(rc))
    {
        for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext)
        {
            pDbgMod->pDbgVt    = pDbg->pVt;
            pDbgMod->pvDbgPriv = NULL;
            rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod));
            if (RT_SUCCESS(rc))
            {
                /*
                 * Got it!
                 */
                ASMAtomicIncU32(&pDbg->cUsers);
                RTSemRWReleaseRead(g_hDbgModRWSem);
                return VINF_CALLBACK_RETURN;
            }

            pDbgMod->pDbgVt = NULL;
            Assert(pDbgMod->pvDbgPriv == NULL);
        }
        RTSemRWReleaseRead(g_hDbgModRWSem);
    }

    /* No joy. */
    RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile);
    pDbgMod->pszDbgFile = NULL;
    return rc;
}


/**
 * Argument package used by rtDbgModOpenDebugInfoExternalToImage.
 */
typedef struct RTDBGMODOPENDIETI
{
    PRTDBGMODINT    pDbgMod;
    RTDBGCFG        hDbgCfg;
} RTDBGMODOPENDIETI;


/** @callback_method_impl{FNRTLDRENUMDBG} */
static DECLCALLBACK(int)
rtDbgModOpenDebugInfoExternalToImageCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser)
{
    RTDBGMODOPENDIETI *pArgs = (RTDBGMODOPENDIETI *)pvUser;
    RT_NOREF_PV(hLdrMod);

    Assert(pDbgInfo->enmType > RTLDRDBGINFOTYPE_INVALID && pDbgInfo->enmType < RTLDRDBGINFOTYPE_END);
    const char *pszExtFile = pDbgInfo->pszExtFile;
    if (!pszExtFile)
    {
        /*
         * If a external debug type comes without a file name, calculate a
         * likely debug filename for it. (Hack for NT4 drivers.)
         */
        const char *pszExt = NULL;
        if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW_DBG)
            pszExt = ".dbg";
        else if (   pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW_PDB20
                 || pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW_PDB70)
            pszExt = ".pdb";
        if (pszExt && pArgs->pDbgMod->pszName)
        {
            size_t cchName = strlen(pArgs->pDbgMod->pszName);
            char *psz = (char *)alloca(cchName + strlen(pszExt) + 1);
            if (psz)
            {
                memcpy(psz, pArgs->pDbgMod->pszName, cchName + 1);
                RTPathStripSuffix(psz);
                pszExtFile = strcat(psz, pszExt);
            }
        }

        if (!pszExtFile)
        {
            Log2(("rtDbgModOpenDebugInfoExternalToImageCallback: enmType=%d\n", pDbgInfo->enmType));
            return VINF_SUCCESS;
        }
    }

    /*
     * Switch on type and call the appropriate search function.
     */
    int rc;
    switch (pDbgInfo->enmType)
    {
        case RTLDRDBGINFOTYPE_CODEVIEW_PDB70:
            rc = RTDbgCfgOpenPdb70(pArgs->hDbgCfg, pszExtFile,
                                   &pDbgInfo->u.Pdb70.Uuid,
                                   pDbgInfo->u.Pdb70.uAge,
                                   rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo);
            break;

        case RTLDRDBGINFOTYPE_CODEVIEW_PDB20:
            rc = RTDbgCfgOpenPdb20(pArgs->hDbgCfg, pszExtFile,
                                   pDbgInfo->u.Pdb20.cbImage,
                                   pDbgInfo->u.Pdb20.uTimestamp,
                                   pDbgInfo->u.Pdb20.uAge,
                                   rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo);
            break;

        case RTLDRDBGINFOTYPE_CODEVIEW_DBG:
            rc = RTDbgCfgOpenDbg(pArgs->hDbgCfg, pszExtFile,
                                 pDbgInfo->u.Dbg.cbImage,
                                 pDbgInfo->u.Dbg.uTimestamp,
                                 rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo);
            break;

        case RTLDRDBGINFOTYPE_DWARF_DWO:
            rc = RTDbgCfgOpenDwo(pArgs->hDbgCfg, pszExtFile,
                                 pDbgInfo->u.Dwo.uCrc32,
                                 rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo);
            break;

        default:
            Log(("rtDbgModOpenDebugInfoExternalToImageCallback: Don't know how to handle enmType=%d and pszFileExt=%s\n",
                 pDbgInfo->enmType, pszExtFile));
            return VERR_DBG_TODO;
    }
    if (RT_SUCCESS(rc))
    {
        LogFlow(("RTDbgMod: Successfully opened external debug info '%s' for '%s'\n",
                 pArgs->pDbgMod->pszDbgFile, pArgs->pDbgMod->pszImgFile));
        return VINF_CALLBACK_RETURN;
    }
    Log(("rtDbgModOpenDebugInfoExternalToImageCallback: '%s' (enmType=%d) for '%s'  -> %Rrc\n",
         pszExtFile, pDbgInfo->enmType, pArgs->pDbgMod->pszImgFile, rc));
    return rc;
}


/**
 * Opens debug info listed in the image that is stored in a separate file.
 *
 * @returns IPRT status code
 * @param   pDbgMod             The debug module.
 * @param   hDbgCfg             The debug config.  Can be NIL.
 */
static int rtDbgModOpenDebugInfoExternalToImage(PRTDBGMODINT pDbgMod, RTDBGCFG hDbgCfg)
{
    Assert(!pDbgMod->pDbgVt);

    RTDBGMODOPENDIETI Args;
    Args.pDbgMod = pDbgMod;
    Args.hDbgCfg = hDbgCfg;
    int rc = pDbgMod->pImgVt->pfnEnumDbgInfo(pDbgMod, rtDbgModOpenDebugInfoExternalToImageCallback, &Args);
    if (RT_SUCCESS(rc) && pDbgMod->pDbgVt)
        return VINF_SUCCESS;

    LogFlow(("rtDbgModOpenDebugInfoExternalToImage: rc=%Rrc\n", rc));
    return VERR_NOT_FOUND;
}


/** @callback_method_impl{FNRTDBGCFGOPEN} */
static DECLCALLBACK(int) rtDbgModExtDbgInfoOpenCallback2(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2)
{
    PRTDBGMODINT        pDbgMod   = (PRTDBGMODINT)pvUser1;
    RT_NOREF_PV(pvUser2); /** @todo image matching string or smth. */
    RT_NOREF_PV(hDbgCfg);

    Assert(!pDbgMod->pDbgVt);
    Assert(!pDbgMod->pvDbgPriv);
    Assert(!pDbgMod->pszDbgFile);
    Assert(pDbgMod->pImgVt);

    /*
     * Set the debug file name and try possible interpreters.
     */
    pDbgMod->pszDbgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);

    int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
    if (RT_SUCCESS(rc))
    {
        for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext)
        {
            pDbgMod->pDbgVt    = pDbg->pVt;
            pDbgMod->pvDbgPriv = NULL;
            rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod));
            if (RT_SUCCESS(rc))
            {
                /*
                 * Got it!
                 */
                ASMAtomicIncU32(&pDbg->cUsers);
                RTSemRWReleaseRead(g_hDbgModRWSem);
                return VINF_CALLBACK_RETURN;
            }
            pDbgMod->pDbgVt    = NULL;
            Assert(pDbgMod->pvDbgPriv == NULL);
        }
    }

    /* No joy. */
    RTSemRWReleaseRead(g_hDbgModRWSem);
    RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile);
    pDbgMod->pszDbgFile = NULL;
    return rc;
}


/**
 * Opens external debug info that is not listed in the image.
 *
 * @returns IPRT status code
 * @param   pDbgMod             The debug module.
 * @param   hDbgCfg             The debug config.  Can be NIL.
 */
static int rtDbgModOpenDebugInfoExternalToImage2(PRTDBGMODINT pDbgMod, RTDBGCFG hDbgCfg)
{
    int rc;
    Assert(!pDbgMod->pDbgVt);
    Assert(pDbgMod->pImgVt);

    /*
     * Figure out what to search for based on the image format.
     */
    const char *pszzExts = NULL;
    RTLDRFMT    enmFmt = pDbgMod->pImgVt->pfnGetFormat(pDbgMod);
    switch (enmFmt)
    {
        case RTLDRFMT_MACHO:
        {
            RTUUID  Uuid;
            PRTUUID pUuid = &Uuid;
            rc = pDbgMod->pImgVt->pfnQueryProp(pDbgMod, RTLDRPROP_UUID, &Uuid, sizeof(Uuid), NULL);
            if (RT_FAILURE(rc))
                pUuid = NULL;

            rc = RTDbgCfgOpenDsymBundle(hDbgCfg, pDbgMod->pszImgFile, pUuid,
                                        rtDbgModExtDbgInfoOpenCallback2, pDbgMod, NULL /*pvUser2*/);
            if (RT_SUCCESS(rc))
                return VINF_SUCCESS;
            break;
        }

#if 0 /* Will be links in the image if these apply. .map readers for PE or ELF we don't have. */
        case RTLDRFMT_ELF:
            pszzExts = ".debug\0.dwo\0";
            break;
        case RTLDRFMT_PE:
            pszzExts = ".map\0";
            break;
#endif
#if 0 /* Haven't implemented .sym or .map file readers for OS/2 yet. */
        case RTLDRFMT_LX:
            pszzExts = ".sym\0.map\0";
            break;
#endif
        default:
            rc = VERR_NOT_IMPLEMENTED;
            break;
    }

    NOREF(pszzExts);
#if 0 /* Later */
    if (pszzExts)
    {

    }
#endif

    LogFlow(("rtDbgModOpenDebugInfoExternalToImage2: rc=%Rrc\n", rc));
    return VERR_NOT_FOUND;
}


RTDECL(int) RTDbgModCreateFromImage(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName,
                                    RTLDRARCH enmArch, RTDBGCFG hDbgCfg)
{
    /*
     * Input validation and lazy initialization.
     */
    AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER);
    *phDbgMod = NIL_RTDBGMOD;
    AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
    AssertReturn(*pszFilename, VERR_INVALID_PARAMETER);
    AssertPtrNullReturn(pszName, VERR_INVALID_POINTER);
    AssertReturn(enmArch > RTLDRARCH_INVALID && enmArch < RTLDRARCH_END, VERR_INVALID_PARAMETER);

    int rc = rtDbgModLazyInit();
    if (RT_FAILURE(rc))
        return rc;

    if (!pszName)
        pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS);

    /*
     * Allocate a new module instance.
     */
    PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod));
    if (!pDbgMod)
        return VERR_NO_MEMORY;
    pDbgMod->u32Magic = RTDBGMOD_MAGIC;
    pDbgMod->cRefs = 1;
    rc = RTCritSectInit(&pDbgMod->CritSect);
    if (RT_SUCCESS(rc))
    {
        pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName);
        if (pDbgMod->pszName)
        {
            pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
            if (pDbgMod->pszImgFile)
            {
                RTStrCacheRetain(pDbgMod->pszImgFile);
                pDbgMod->pszImgFileSpecified = pDbgMod->pszImgFile;

                /*
                 * Find an image reader which groks the file.
                 */
                rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
                if (RT_SUCCESS(rc))
                {
                    rc = VERR_DBG_NO_MATCHING_INTERPRETER;
                    PRTDBGMODREGIMG pImg;
                    for (pImg = g_pImgHead; pImg; pImg = pImg->pNext)
                    {
                        pDbgMod->pImgVt    = pImg->pVt;
                        pDbgMod->pvImgPriv = NULL;
                        /** @todo need to specify some arch stuff here. */
                        rc = pImg->pVt->pfnTryOpen(pDbgMod, enmArch);
                        if (RT_SUCCESS(rc))
                        {
                            /*
                             * Image detected, but found no debug info we were
                             * able to understand.
                             */
                            /** @todo some generic way of matching image and debug info, flexible signature
                             *        of some kind. Apple uses UUIDs, microsoft uses a UUID+age or a
                             *        size+timestamp, and GNU a CRC32 (last time I checked). */
                            rc = rtDbgModOpenDebugInfoExternalToImage(pDbgMod, hDbgCfg);
                            if (RT_FAILURE(rc))
                                rc = rtDbgModOpenDebugInfoInsideImage(pDbgMod);
                            if (RT_FAILURE(rc))
                                rc = rtDbgModOpenDebugInfoExternalToImage2(pDbgMod, hDbgCfg);
                            if (RT_FAILURE(rc))
                                rc = rtDbgModCreateForExports(pDbgMod);
                            if (RT_SUCCESS(rc))
                            {
                                /*
                                 * We're done!
                                 */
                                ASMAtomicIncU32(&pImg->cUsers);
                                RTSemRWReleaseRead(g_hDbgModRWSem);

                                *phDbgMod = pDbgMod;
                                return VINF_SUCCESS;
                            }

                            /* Failed, close up the shop. */
                            pDbgMod->pImgVt->pfnClose(pDbgMod);
                            pDbgMod->pImgVt = NULL;
                            pDbgMod->pvImgPriv = NULL;
                            break;
                        }
                    }

                    /*
                     * Could it be a file containing raw debug info?
                     */
                    if (!pImg)
                    {
                        pDbgMod->pImgVt     = NULL;
                        pDbgMod->pvImgPriv  = NULL;
                        pDbgMod->pszDbgFile = pDbgMod->pszImgFile;
                        pDbgMod->pszImgFile = NULL;

                        for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext)
                        {
                            pDbgMod->pDbgVt = pDbg->pVt;
                            pDbgMod->pvDbgPriv = NULL;
                            rc = pDbg->pVt->pfnTryOpen(pDbgMod, enmArch);
                            if (RT_SUCCESS(rc))
                            {
                                /*
                                 * That's it!
                                 */
                                ASMAtomicIncU32(&pDbg->cUsers);
                                RTSemRWReleaseRead(g_hDbgModRWSem);

                                *phDbgMod = pDbgMod;
                                return rc;
                            }
                        }

                        pDbgMod->pszImgFile = pDbgMod->pszDbgFile;
                        pDbgMod->pszDbgFile = NULL;
                    }

                    /* bail out */
                    RTSemRWReleaseRead(g_hDbgModRWSem);
                }
                RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified);
                RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
            }
            else
                rc = VERR_NO_STR_MEMORY;
            RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
        }
        else
            rc = VERR_NO_STR_MEMORY;
        RTCritSectDelete(&pDbgMod->CritSect);
    }

    RTMemFree(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModCreateFromImage);





/*
 *
 *  P E   I M A G E
 *  P E   I M A G E
 *  P E   I M A G E
 *
 */



/** @callback_method_impl{FNRTDBGCFGOPEN} */
static DECLCALLBACK(int) rtDbgModFromPeImageOpenCallback(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2)
{
    PRTDBGMODINT        pDbgMod   = (PRTDBGMODINT)pvUser1;
    PRTDBGMODDEFERRED   pDeferred = (PRTDBGMODDEFERRED)pvUser2;
    LogFlow(("rtDbgModFromPeImageOpenCallback: %s\n", pszFilename));
    RT_NOREF_PV(hDbgCfg);

    Assert(pDbgMod->pImgVt == NULL);
    Assert(pDbgMod->pvImgPriv == NULL);
    Assert(pDbgMod->pDbgVt == NULL);
    Assert(pDbgMod->pvDbgPriv == NULL);

    /*
     * Replace the image file name while probing it.
     */
    const char *pszNewImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
    if (!pszNewImgFile)
        return VERR_NO_STR_MEMORY;
    const char *pszOldImgFile = pDbgMod->pszImgFile;
    pDbgMod->pszImgFile = pszNewImgFile;

    /*
     * Find an image reader which groks the file.
     */
    int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
    if (RT_SUCCESS(rc))
    {
        rc = VERR_DBG_NO_MATCHING_INTERPRETER;
        PRTDBGMODREGIMG pImg;
        for (pImg = g_pImgHead; pImg; pImg = pImg->pNext)
        {
            pDbgMod->pImgVt    = pImg->pVt;
            pDbgMod->pvImgPriv = NULL;
            rc = pImg->pVt->pfnTryOpen(pDbgMod, RTLDRARCH_WHATEVER);
            if (RT_SUCCESS(rc))
                break;
            pDbgMod->pImgVt    = NULL;
            Assert(pDbgMod->pvImgPriv == NULL);
        }
        RTSemRWReleaseRead(g_hDbgModRWSem);
        if (RT_SUCCESS(rc))
        {
            /*
             * Check the deferred info.
             */
            RTUINTPTR cbImage = pDbgMod->pImgVt->pfnImageSize(pDbgMod);
            if (   pDeferred->cbImage == 0
                || pDeferred->cbImage == cbImage)
            {
                uint32_t uTimestamp = pDeferred->u.PeImage.uTimestamp; /** @todo add method for getting the timestamp. */
                if (   pDeferred->u.PeImage.uTimestamp == 0
                    || pDeferred->u.PeImage.uTimestamp == uTimestamp)
                {
                    Log(("RTDbgMod: Found matching PE image '%s'\n", pszFilename));

                    /*
                     * We found the executable image we need, now go find any
                     * debug info associated with it.  For PE images, this is
                     * generally found in an external file, so we do a sweep
                     * for that first.
                     *
                     * Then try open debug inside the module, and finally
                     * falling back on exports.
                     */
                    rc = rtDbgModOpenDebugInfoExternalToImage(pDbgMod, pDeferred->hDbgCfg);
                    if (RT_FAILURE(rc))
                        rc = rtDbgModOpenDebugInfoInsideImage(pDbgMod);
                    if (RT_FAILURE(rc))
                        rc = rtDbgModCreateForExports(pDbgMod);
                    if (RT_SUCCESS(rc))
                    {
                        RTStrCacheRelease(g_hDbgModStrCache, pszOldImgFile);
                        return VINF_CALLBACK_RETURN;
                    }

                    /* Something bad happened, just give up. */
                    Log(("rtDbgModFromPeImageOpenCallback: rtDbgModCreateForExports failed: %Rrc\n", rc));
                }
                else
                {
                    LogFlow(("rtDbgModFromPeImageOpenCallback: uTimestamp mismatch (found %#x, expected %#x) - %s\n",
                             uTimestamp, pDeferred->u.PeImage.uTimestamp, pszFilename));
                    rc = VERR_DBG_FILE_MISMATCH;
                }
            }
            else
            {
                LogFlow(("rtDbgModFromPeImageOpenCallback: cbImage mismatch (found %#x, expected %#x) - %s\n",
                         cbImage, pDeferred->cbImage, pszFilename));
                rc = VERR_DBG_FILE_MISMATCH;
            }

            pDbgMod->pImgVt->pfnClose(pDbgMod);
            pDbgMod->pImgVt    = NULL;
            pDbgMod->pvImgPriv = NULL;
        }
        else
            LogFlow(("rtDbgModFromPeImageOpenCallback: Failed %Rrc - %s\n", rc, pszFilename));
    }

    /* Restore image name. */
    pDbgMod->pszImgFile = pszOldImgFile;
    RTStrCacheRelease(g_hDbgModStrCache, pszNewImgFile);
    return rc;
}


/** @callback_method_impl{FNRTDBGMODDEFERRED}  */
static DECLCALLBACK(int) rtDbgModFromPeImageDeferredCallback(PRTDBGMODINT pDbgMod, PRTDBGMODDEFERRED pDeferred)
{
    int rc;

    Assert(pDbgMod->pszImgFile);
    if (!pDbgMod->pImgVt)
        rc = RTDbgCfgOpenPeImage(pDeferred->hDbgCfg, pDbgMod->pszImgFile,
                                 pDeferred->cbImage, pDeferred->u.PeImage.uTimestamp,
                                 rtDbgModFromPeImageOpenCallback, pDbgMod, pDeferred);
    else
    {
        rc = rtDbgModOpenDebugInfoExternalToImage(pDbgMod, pDeferred->hDbgCfg);
        if (RT_FAILURE(rc))
            rc = rtDbgModOpenDebugInfoInsideImage(pDbgMod);
        if (RT_FAILURE(rc))
            rc = rtDbgModCreateForExports(pDbgMod);
    }
    return rc;
}


RTDECL(int) RTDbgModCreateFromPeImage(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName,
                                      PRTLDRMOD phLdrMod, uint32_t cbImage, uint32_t uTimestamp, RTDBGCFG hDbgCfg)
{
    /*
     * Input validation and lazy initialization.
     */
    AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER);
    *phDbgMod = NIL_RTDBGMOD;
    AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
    AssertReturn(*pszFilename, VERR_INVALID_PARAMETER);
    if (!pszName)
        pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS);
    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
    AssertPtrNullReturn(phLdrMod, VERR_INVALID_POINTER);
    RTLDRMOD hLdrMod = phLdrMod ? *phLdrMod : NIL_RTLDRMOD;
    AssertReturn(hLdrMod == NIL_RTLDRMOD || RTLdrSize(hLdrMod) != ~(size_t)0, VERR_INVALID_HANDLE);

    int rc = rtDbgModLazyInit();
    if (RT_FAILURE(rc))
        return rc;

    uint64_t fDbgCfg = 0;
    if (hDbgCfg)
    {
        rc = RTDbgCfgQueryUInt(hDbgCfg, RTDBGCFGPROP_FLAGS, &fDbgCfg);
        AssertRCReturn(rc, rc);
    }

    /*
     * Allocate a new module instance.
     */
    PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod));
    if (!pDbgMod)
        return VERR_NO_MEMORY;
    pDbgMod->u32Magic = RTDBGMOD_MAGIC;
    pDbgMod->cRefs = 1;
    rc = RTCritSectInit(&pDbgMod->CritSect);
    if (RT_SUCCESS(rc))
    {
        pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName);
        if (pDbgMod->pszName)
        {
            pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
            if (pDbgMod->pszImgFile)
            {
                RTStrCacheRetain(pDbgMod->pszImgFile);
                pDbgMod->pszImgFileSpecified = pDbgMod->pszImgFile;

                /*
                 * If we have a loader module, we must instantiate the loader
                 * side of things regardless of the deferred setting.
                 */
                if (hLdrMod != NIL_RTLDRMOD)
                {
                    if (!cbImage)
                        cbImage = (uint32_t)RTLdrSize(hLdrMod);
                    pDbgMod->pImgVt = &g_rtDbgModVtImgLdr;

                    rc = rtDbgModLdrOpenFromHandle(pDbgMod, hLdrMod);
                }
                if (RT_SUCCESS(rc))
                {
                    /* We now own the loader handle, so clear the caller variable. */
                    if (phLdrMod)
                        *phLdrMod = NIL_RTLDRMOD;

                    /*
                     * Do it now or procrastinate?
                     */
                    if (!(fDbgCfg & RTDBGCFG_FLAGS_DEFERRED) || !cbImage)
                    {
                        RTDBGMODDEFERRED Deferred;
                        Deferred.cbImage = cbImage;
                        Deferred.hDbgCfg = hDbgCfg;
                        Deferred.u.PeImage.uTimestamp = uTimestamp;
                        rc = rtDbgModFromPeImageDeferredCallback(pDbgMod, &Deferred);
                    }
                    else
                    {
                        PRTDBGMODDEFERRED pDeferred;
                        rc = rtDbgModDeferredCreate(pDbgMod, rtDbgModFromPeImageDeferredCallback, cbImage, hDbgCfg, 0,
                                                    &pDeferred);
                        if (RT_SUCCESS(rc))
                            pDeferred->u.PeImage.uTimestamp = uTimestamp;
                    }
                    if (RT_SUCCESS(rc))
                    {
                        *phDbgMod = pDbgMod;
                        return VINF_SUCCESS;
                    }

                    /* Failed, bail out. */
                    if (hLdrMod != NIL_RTLDRMOD)
                    {
                        Assert(pDbgMod->pImgVt);
                        pDbgMod->pImgVt->pfnClose(pDbgMod);
                    }
                }
                RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
            }
            else
                rc = VERR_NO_STR_MEMORY;
            RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified);
            RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
        }
        else
            rc = VERR_NO_STR_MEMORY;
        RTCritSectDelete(&pDbgMod->CritSect);
    }

    RTMemFree(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModCreateFromPeImage);




/*
 *
 *  M a c h - O   I M A G E
 *  M a c h - O   I M A G E
 *  M a c h - O   I M A G E
 *
 */


/**
 * Argument package used when opening Mach-O images and .dSYMs files.
 */
typedef struct RTDBGMODMACHOARGS
{
    /** For use more internal use in file locator callbacks. */
    RTLDRARCH           enmArch;
    /** For use more internal use in file locator callbacks. */
    PCRTUUID            pUuid;
    /** For use more internal use in file locator callbacks. */
    bool                fOpenImage;
} RTDBGMODMACHOARGS;
/** Pointer to a const segment package. */
typedef RTDBGMODMACHOARGS const *PCRTDBGMODMACHOARGS;



/** @callback_method_impl{FNRTDBGCFGOPEN} */
static DECLCALLBACK(int)
rtDbgModFromMachOImageOpenDsymMachOCallback(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2)
{
    PRTDBGMODINT        pDbgMod = (PRTDBGMODINT)pvUser1;
    PCRTDBGMODMACHOARGS pArgs   = (PCRTDBGMODMACHOARGS)pvUser2;
    RT_NOREF_PV(hDbgCfg);

    Assert(!pDbgMod->pDbgVt);
    Assert(!pDbgMod->pvDbgPriv);
    Assert(!pDbgMod->pszDbgFile);
    Assert(!pDbgMod->pImgVt);
    Assert(!pDbgMod->pvDbgPriv);
    Assert(pDbgMod->pszImgFile);
    Assert(pDbgMod->pszImgFileSpecified);

    const char *pszImgFileOrg = pDbgMod->pszImgFile;
    pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
    if (!pDbgMod->pszImgFile)
        return VERR_NO_STR_MEMORY;
    RTStrCacheRetain(pDbgMod->pszImgFile);
    pDbgMod->pszDbgFile = pDbgMod->pszImgFile;

    /*
     * Try image interpreters as the dwarf file inside the dSYM bundle is a
     * Mach-O file with dwarf debug sections insides it and no code or data.
     */
    int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
    if (RT_SUCCESS(rc))
    {
        rc = VERR_DBG_NO_MATCHING_INTERPRETER;
        PRTDBGMODREGIMG pImg;
        for (pImg = g_pImgHead; pImg; pImg = pImg->pNext)
        {
            pDbgMod->pImgVt    = pImg->pVt;
            pDbgMod->pvImgPriv = NULL;
            rc = pImg->pVt->pfnTryOpen(pDbgMod, pArgs->enmArch);
            if (RT_SUCCESS(rc))
                break;
            pDbgMod->pImgVt    = NULL;
            Assert(pDbgMod->pvImgPriv == NULL);
        }

        if (RT_SUCCESS(rc))
        {
            /*
             * Check the UUID if one was given.
             */
            if (pArgs->pUuid)
            {
                RTUUID UuidOpened;
                rc = pDbgMod->pImgVt->pfnQueryProp(pDbgMod, RTLDRPROP_UUID, &UuidOpened, sizeof(UuidOpened), NULL);
                if (RT_SUCCESS(rc))
                {
                    if (RTUuidCompare(&UuidOpened, pArgs->pUuid) != 0)
                        rc = VERR_DBG_FILE_MISMATCH;
                }
                else if (rc == VERR_NOT_FOUND || rc == VERR_NOT_IMPLEMENTED)
                    rc = VERR_DBG_FILE_MISMATCH;
            }
            if (RT_SUCCESS(rc))
            {
                /*
                 * Pass it to the DWARF reader(s).  Careful to restrict this or
                 * the dbghelp wrapper may end up being overly helpful.
                 */
                for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext)
                {
                    if (pDbg->pVt->fSupports & (RT_DBGTYPE_DWARF | RT_DBGTYPE_STABS | RT_DBGTYPE_WATCOM))

                    {
                        pDbgMod->pDbgVt    = pDbg->pVt;
                        pDbgMod->pvDbgPriv = NULL;
                        rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod));
                        if (RT_SUCCESS(rc))
                        {
                            /*
                             * Got it!
                             */
                            ASMAtomicIncU32(&pDbg->cUsers);
                            RTSemRWReleaseRead(g_hDbgModRWSem);
                            RTStrCacheRelease(g_hDbgModStrCache, pszImgFileOrg);
                            return VINF_CALLBACK_RETURN;
                        }
                        pDbgMod->pDbgVt    = NULL;
                        Assert(pDbgMod->pvDbgPriv == NULL);
                    }
                }

                /*
                 * Likely fallback for when opening image.
                 */
                if (pArgs->fOpenImage)
                {
                    rc = rtDbgModCreateForExports(pDbgMod);
                    if (RT_SUCCESS(rc))
                    {
                        /*
                         * Done.
                         */
                        RTSemRWReleaseRead(g_hDbgModRWSem);
                        RTStrCacheRelease(g_hDbgModStrCache, pszImgFileOrg);
                        return VINF_CALLBACK_RETURN;
                    }
                }
            }

            pDbgMod->pImgVt->pfnClose(pDbgMod);
            pDbgMod->pImgVt    = NULL;
            pDbgMod->pvImgPriv = NULL;
        }
    }

    /* No joy. */
    RTSemRWReleaseRead(g_hDbgModRWSem);
    RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
    pDbgMod->pszImgFile = pszImgFileOrg;
    RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile);
    pDbgMod->pszDbgFile = NULL;
    return rc;
}


static int rtDbgModFromMachOImageWorker(PRTDBGMODINT pDbgMod, RTLDRARCH enmArch, uint32_t cbImage,
                                        uint32_t cSegs, PCRTDBGSEGMENT paSegs, PCRTUUID pUuid, RTDBGCFG hDbgCfg)
{
    RT_NOREF_PV(cbImage); RT_NOREF_PV(cSegs); RT_NOREF_PV(paSegs);

    RTDBGMODMACHOARGS Args;
    Args.enmArch    = enmArch;
    Args.pUuid      = pUuid && RTUuidIsNull(pUuid) ? pUuid : NULL;
    Args.fOpenImage = false;

    /*
     * Search for the .dSYM bundle first, since that's generally all we need.
     */
    int rc = RTDbgCfgOpenDsymBundle(hDbgCfg, pDbgMod->pszImgFile, pUuid,
                                    rtDbgModFromMachOImageOpenDsymMachOCallback, pDbgMod, &Args);
    if (RT_FAILURE(rc))
    {
        /*
         * If we cannot get at the .dSYM, try the executable image.
         */
        Args.fOpenImage = true;
        rc = RTDbgCfgOpenMachOImage(hDbgCfg, pDbgMod->pszImgFile, pUuid,
                                    rtDbgModFromMachOImageOpenDsymMachOCallback, pDbgMod, &Args);
    }
    return rc;
}


/** @callback_method_impl{FNRTDBGMODDEFERRED}  */
static DECLCALLBACK(int) rtDbgModFromMachOImageDeferredCallback(PRTDBGMODINT pDbgMod, PRTDBGMODDEFERRED pDeferred)
{
    return rtDbgModFromMachOImageWorker(pDbgMod, pDeferred->u.MachO.enmArch, pDeferred->cbImage,
                                        pDeferred->u.MachO.cSegs, pDeferred->u.MachO.aSegs,
                                        &pDeferred->u.MachO.Uuid, pDeferred->hDbgCfg);
}


RTDECL(int) RTDbgModCreateFromMachOImage(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName,
                                         RTLDRARCH enmArch, uint32_t cbImage, uint32_t cSegs, PCRTDBGSEGMENT paSegs,
                                         PCRTUUID pUuid, RTDBGCFG hDbgCfg, uint32_t fFlags)
{
    /*
     * Input validation and lazy initialization.
     */
    AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER);
    *phDbgMod = NIL_RTDBGMOD;
    AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
    AssertReturn(*pszFilename, VERR_INVALID_PARAMETER);
    if (!pszName)
        pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_HOST);
    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
    if (cSegs)
    {
        AssertReturn(cSegs < 1024, VERR_INVALID_PARAMETER);
        AssertPtrReturn(paSegs, VERR_INVALID_POINTER);
        AssertReturn(!cbImage, VERR_INVALID_PARAMETER);
    }
    AssertReturn(cbImage || cSegs, VERR_INVALID_PARAMETER);
    AssertPtrNullReturn(pUuid, VERR_INVALID_POINTER);
    AssertReturn(!(fFlags & ~(RTDBGMOD_F_NOT_DEFERRED)), VERR_INVALID_PARAMETER);

    int rc = rtDbgModLazyInit();
    if (RT_FAILURE(rc))
        return rc;

    uint64_t fDbgCfg = 0;
    if (hDbgCfg)
    {
        rc = RTDbgCfgQueryUInt(hDbgCfg, RTDBGCFGPROP_FLAGS, &fDbgCfg);
        AssertRCReturn(rc, rc);
    }

    /*
     * Allocate a new module instance.
     */
    PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod));
    if (!pDbgMod)
        return VERR_NO_MEMORY;
    pDbgMod->u32Magic = RTDBGMOD_MAGIC;
    pDbgMod->cRefs = 1;
    rc = RTCritSectInit(&pDbgMod->CritSect);
    if (RT_SUCCESS(rc))
    {
        pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName);
        if (pDbgMod->pszName)
        {
            pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
            if (pDbgMod->pszImgFile)
            {
                RTStrCacheRetain(pDbgMod->pszImgFile);
                pDbgMod->pszImgFileSpecified = pDbgMod->pszImgFile;

                /*
                 * Load it immediately?
                 */
                if (   !(fDbgCfg & RTDBGCFG_FLAGS_DEFERRED)
                    || cSegs /* for the time being. */
                    || (!cbImage && !cSegs)
                    || (fFlags & RTDBGMOD_F_NOT_DEFERRED) )
                    rc = rtDbgModFromMachOImageWorker(pDbgMod, enmArch, cbImage, cSegs, paSegs, pUuid, hDbgCfg);
                else
                {
                    /*
                     * Procrastinate.  Need image size atm.
                     */
                    PRTDBGMODDEFERRED pDeferred;
                    rc = rtDbgModDeferredCreate(pDbgMod, rtDbgModFromMachOImageDeferredCallback, cbImage, hDbgCfg,
                                                RT_UOFFSETOF_DYN(RTDBGMODDEFERRED, u.MachO.aSegs[cSegs]),
                                                &pDeferred);
                    if (RT_SUCCESS(rc))
                    {
                        pDeferred->u.MachO.Uuid    = *pUuid;
                        pDeferred->u.MachO.enmArch = enmArch;
                        pDeferred->u.MachO.cSegs   = cSegs;
                        if (cSegs)
                            memcpy(&pDeferred->u.MachO.aSegs, paSegs, cSegs * sizeof(paSegs[0]));
                    }
                }
                if (RT_SUCCESS(rc))
                {
                    *phDbgMod = pDbgMod;
                    return VINF_SUCCESS;
                }

                /* Failed, bail out. */
                RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
            }
            else
                rc = VERR_NO_STR_MEMORY;
            RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified);
            RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
        }
        else
            rc = VERR_NO_STR_MEMORY;
        RTCritSectDelete(&pDbgMod->CritSect);
    }

    RTMemFree(pDbgMod);
    return rc;
}



RT_EXPORT_SYMBOL(RTDbgModCreateFromMachOImage);



/**
 * Destroys an module after the reference count has reached zero.
 *
 * @param   pDbgMod     The module instance.
 */
static void rtDbgModDestroy(PRTDBGMODINT pDbgMod)
{
    /*
     * Close the debug info interpreter first, then the image interpret.
     */
    RTCritSectEnter(&pDbgMod->CritSect); /* paranoia  */

    if (pDbgMod->pDbgVt)
    {
        pDbgMod->pDbgVt->pfnClose(pDbgMod);
        pDbgMod->pDbgVt = NULL;
        pDbgMod->pvDbgPriv = NULL;
    }

    if (pDbgMod->pImgVt)
    {
        pDbgMod->pImgVt->pfnClose(pDbgMod);
        pDbgMod->pImgVt = NULL;
        pDbgMod->pvImgPriv = NULL;
    }

    /*
     * Free the resources.
     */
    ASMAtomicWriteU32(&pDbgMod->u32Magic, ~RTDBGMOD_MAGIC);
    RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
    RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
    RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified);
    RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile);
    RTCritSectLeave(&pDbgMod->CritSect); /* paranoia  */
    RTCritSectDelete(&pDbgMod->CritSect);
    RTMemFree(pDbgMod);
}


RTDECL(uint32_t) RTDbgModRetain(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX);
    return ASMAtomicIncU32(&pDbgMod->cRefs);
}
RT_EXPORT_SYMBOL(RTDbgModRetain);


RTDECL(uint32_t) RTDbgModRelease(RTDBGMOD hDbgMod)
{
    if (hDbgMod == NIL_RTDBGMOD)
        return 0;
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX);

    uint32_t cRefs = ASMAtomicDecU32(&pDbgMod->cRefs);
    if (!cRefs)
        rtDbgModDestroy(pDbgMod);
    return cRefs;
}
RT_EXPORT_SYMBOL(RTDbgModRelease);


RTDECL(const char *) RTDbgModName(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL);
    return pDbgMod->pszName;
}
RT_EXPORT_SYMBOL(RTDbgModName);


RTDECL(const char *) RTDbgModDebugFile(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL);
    if (pDbgMod->fDeferred || pDbgMod->fExports)
        return NULL;
    return pDbgMod->pszDbgFile;
}
RT_EXPORT_SYMBOL(RTDbgModDebugFile);


RTDECL(const char *) RTDbgModImageFile(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL);
    return pDbgMod->pszImgFileSpecified;
}
RT_EXPORT_SYMBOL(RTDbgModImageFile);


RTDECL(const char *) RTDbgModImageFileUsed(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL);
    return pDbgMod->pszImgFile == pDbgMod->pszImgFileSpecified ? NULL : pDbgMod->pszImgFile;
}
RT_EXPORT_SYMBOL(RTDbgModImageFileUsed);


RTDECL(bool) RTDbgModIsDeferred(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, false);
    return pDbgMod->fDeferred;
}


RTDECL(bool) RTDbgModIsExports(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, false);
    return pDbgMod->fExports;
}


RTDECL(int) RTDbgModRemoveAll(RTDBGMOD hDbgMod, bool fLeaveSegments)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);

    RTDBGMOD_LOCK(pDbgMod);

    /* Only possible on container modules. */
    int rc = VINF_SUCCESS;
    if (pDbgMod->pDbgVt != &g_rtDbgModVtDbgContainer)
    {
        if (fLeaveSegments)
        {
            rc = rtDbgModContainer_LineRemoveAll(pDbgMod);
            if (RT_SUCCESS(rc))
                rc = rtDbgModContainer_SymbolRemoveAll(pDbgMod);
        }
        else
            rc = rtDbgModContainer_RemoveAll(pDbgMod);
    }
    else
        rc = VERR_ACCESS_DENIED;

    RTDBGMOD_UNLOCK(pDbgMod);
    return rc;
}


RTDECL(RTDBGSEGIDX) RTDbgModRvaToSegOff(RTDBGMOD hDbgMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, NIL_RTDBGSEGIDX);
    RTDBGMOD_LOCK(pDbgMod);

    RTDBGSEGIDX iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, uRva, poffSeg);

    RTDBGMOD_UNLOCK(pDbgMod);
    return iSeg;
}
RT_EXPORT_SYMBOL(RTDbgModRvaToSegOff);


RTDECL(uint64_t) RTDbgModGetTag(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, 0);
    return pDbgMod->uTag;
}
RT_EXPORT_SYMBOL(RTDbgModGetTag);


RTDECL(int) RTDbgModSetTag(RTDBGMOD hDbgMod, uint64_t uTag)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    RTDBGMOD_LOCK(pDbgMod);

    pDbgMod->uTag = uTag;

    RTDBGMOD_UNLOCK(pDbgMod);
    return VINF_SUCCESS;
}
RT_EXPORT_SYMBOL(RTDbgModSetTag);


RTDECL(RTUINTPTR) RTDbgModImageSize(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, RTUINTPTR_MAX);
    RTDBGMOD_LOCK(pDbgMod);

    RTUINTPTR cbImage = pDbgMod->pDbgVt->pfnImageSize(pDbgMod);

    RTDBGMOD_UNLOCK(pDbgMod);
    return cbImage;
}
RT_EXPORT_SYMBOL(RTDbgModImageSize);


RTDECL(RTLDRFMT) RTDbgModImageGetFormat(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, RTLDRFMT_INVALID);
    RTDBGMOD_LOCK(pDbgMod);

    RTLDRFMT enmFmt;
    if (   pDbgMod->pImgVt
        && pDbgMod->pImgVt->pfnGetFormat)
        enmFmt = pDbgMod->pImgVt->pfnGetFormat(pDbgMod);
    else
        enmFmt = RTLDRFMT_INVALID;

    RTDBGMOD_UNLOCK(pDbgMod);
    return enmFmt;
}
RT_EXPORT_SYMBOL(RTDbgModImageGetFormat);


RTDECL(RTLDRARCH) RTDbgModImageGetArch(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, RTLDRARCH_INVALID);
    RTDBGMOD_LOCK(pDbgMod);

    RTLDRARCH enmArch;
    if (   pDbgMod->pImgVt
        && pDbgMod->pImgVt->pfnGetArch)
        enmArch = pDbgMod->pImgVt->pfnGetArch(pDbgMod);
    else
        enmArch = RTLDRARCH_WHATEVER;

    RTDBGMOD_UNLOCK(pDbgMod);
    return enmArch;
}
RT_EXPORT_SYMBOL(RTDbgModImageGetArch);


RTDECL(int) RTDbgModImageQueryProp(RTDBGMOD hDbgMod, RTLDRPROP enmProp, void *pvBuf, size_t cbBuf, size_t *pcbRet)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    AssertPtrNullReturn(pcbRet, VERR_INVALID_POINTER);
    RTDBGMOD_LOCK(pDbgMod);

    int rc;
    if (   pDbgMod->pImgVt
        && pDbgMod->pImgVt->pfnQueryProp)
        rc = pDbgMod->pImgVt->pfnQueryProp(pDbgMod, enmProp, pvBuf, cbBuf, pcbRet);
    else
        rc = VERR_NOT_FOUND;

    RTDBGMOD_UNLOCK(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModImageQueryProp);


RTDECL(int) RTDbgModSegmentAdd(RTDBGMOD hDbgMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName,
                               uint32_t fFlags, PRTDBGSEGIDX piSeg)
{
    /*
     * Validate input.
     */
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    AssertMsgReturn(uRva + cb >= uRva, ("uRva=%RTptr cb=%RTptr\n", uRva, cb), VERR_DBG_ADDRESS_WRAP);
    Assert(*pszName);
    size_t cchName = strlen(pszName);
    AssertReturn(cchName > 0, VERR_DBG_SEGMENT_NAME_OUT_OF_RANGE);
    AssertReturn(cchName < RTDBG_SEGMENT_NAME_LENGTH, VERR_DBG_SEGMENT_NAME_OUT_OF_RANGE);
    AssertMsgReturn(!fFlags, ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
    AssertPtrNull(piSeg);
    AssertMsgReturn(!piSeg || *piSeg == NIL_RTDBGSEGIDX || *piSeg <= RTDBGSEGIDX_LAST, ("%#x\n", *piSeg), VERR_DBG_SPECIAL_SEGMENT);

    /*
     * Do the deed.
     */
    RTDBGMOD_LOCK(pDbgMod);
    int rc = pDbgMod->pDbgVt->pfnSegmentAdd(pDbgMod, uRva, cb, pszName, cchName, fFlags, piSeg);
    RTDBGMOD_UNLOCK(pDbgMod);

    return rc;

}
RT_EXPORT_SYMBOL(RTDbgModSegmentAdd);


RTDECL(RTDBGSEGIDX) RTDbgModSegmentCount(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, NIL_RTDBGSEGIDX);
    RTDBGMOD_LOCK(pDbgMod);

    RTDBGSEGIDX cSegs = pDbgMod->pDbgVt->pfnSegmentCount(pDbgMod);

    RTDBGMOD_UNLOCK(pDbgMod);
    return cSegs;
}
RT_EXPORT_SYMBOL(RTDbgModSegmentCount);


RTDECL(int) RTDbgModSegmentByIndex(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
{
    AssertMsgReturn(iSeg <= RTDBGSEGIDX_LAST, ("%#x\n", iSeg), VERR_DBG_SPECIAL_SEGMENT);
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    RTDBGMOD_LOCK(pDbgMod);

    int rc = pDbgMod->pDbgVt->pfnSegmentByIndex(pDbgMod, iSeg, pSegInfo);

    RTDBGMOD_UNLOCK(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModSegmentByIndex);


RTDECL(RTUINTPTR) RTDbgModSegmentSize(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg)
{
    if (iSeg == RTDBGSEGIDX_RVA)
        return RTDbgModImageSize(hDbgMod);
    RTDBGSEGMENT SegInfo;
    int rc = RTDbgModSegmentByIndex(hDbgMod, iSeg, &SegInfo);
    return RT_SUCCESS(rc) ? SegInfo.cb : RTUINTPTR_MAX;
}
RT_EXPORT_SYMBOL(RTDbgModSegmentSize);


RTDECL(RTUINTPTR) RTDbgModSegmentRva(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg)
{
    RTDBGSEGMENT SegInfo;
    int rc = RTDbgModSegmentByIndex(hDbgMod, iSeg, &SegInfo);
    return RT_SUCCESS(rc) ? SegInfo.uRva : RTUINTPTR_MAX;
}
RT_EXPORT_SYMBOL(RTDbgModSegmentRva);


RTDECL(int) RTDbgModSymbolAdd(RTDBGMOD hDbgMod, const char *pszSymbol, RTDBGSEGIDX iSeg, RTUINTPTR off,
                              RTUINTPTR cb, uint32_t fFlags, uint32_t *piOrdinal)
{
    /*
     * Validate input.
     */
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    AssertPtrReturn(pszSymbol, VERR_INVALID_POINTER);
    size_t cchSymbol = strlen(pszSymbol);
    AssertReturn(cchSymbol, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE);
    AssertReturn(cchSymbol < RTDBG_SYMBOL_NAME_LENGTH, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE);
    AssertMsgReturn(   iSeg <= RTDBGSEGIDX_LAST
                    || (    iSeg >= RTDBGSEGIDX_SPECIAL_FIRST
                        &&  iSeg <= RTDBGSEGIDX_SPECIAL_LAST),
                    ("%#x\n", iSeg),
                    VERR_DBG_INVALID_SEGMENT_INDEX);
    AssertMsgReturn(off + cb >= off, ("off=%RTptr cb=%RTptr\n", off, cb), VERR_DBG_ADDRESS_WRAP);
    AssertReturn(!(fFlags & ~RTDBGSYMBOLADD_F_VALID_MASK), VERR_INVALID_FLAGS);

    RTDBGMOD_LOCK(pDbgMod);

    /*
     * Convert RVAs.
     */
    if (iSeg == RTDBGSEGIDX_RVA)
    {
        iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off);
        if (iSeg == NIL_RTDBGSEGIDX)
        {
            RTDBGMOD_UNLOCK(pDbgMod);
            return VERR_DBG_INVALID_RVA;
        }
    }

    /*
     * Get down to business.
     */
    int rc = pDbgMod->pDbgVt->pfnSymbolAdd(pDbgMod, pszSymbol, cchSymbol, iSeg, off, cb, fFlags, piOrdinal);

    RTDBGMOD_UNLOCK(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModSymbolAdd);


RTDECL(uint32_t) RTDbgModSymbolCount(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX);
    RTDBGMOD_LOCK(pDbgMod);

    uint32_t cSymbols = pDbgMod->pDbgVt->pfnSymbolCount(pDbgMod);

    RTDBGMOD_UNLOCK(pDbgMod);
    return cSymbols;
}
RT_EXPORT_SYMBOL(RTDbgModSymbolCount);


RTDECL(int) RTDbgModSymbolByOrdinal(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    RTDBGMOD_LOCK(pDbgMod);

    int rc = pDbgMod->pDbgVt->pfnSymbolByOrdinal(pDbgMod, iOrdinal, pSymInfo);

    RTDBGMOD_UNLOCK(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModSymbolByOrdinal);


RTDECL(int) RTDbgModSymbolByOrdinalA(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGSYMBOL *ppSymInfo)
{
    AssertPtr(ppSymInfo);
    *ppSymInfo = NULL;

    PRTDBGSYMBOL pSymInfo = RTDbgSymbolAlloc();
    if (!pSymInfo)
        return VERR_NO_MEMORY;

    int rc = RTDbgModSymbolByOrdinal(hDbgMod, iOrdinal, pSymInfo);

    if (RT_SUCCESS(rc))
        *ppSymInfo = pSymInfo;
    else
        RTDbgSymbolFree(pSymInfo);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModSymbolByOrdinalA);


/**
 * Return a segment number/name as symbol if we couldn't find any
 * valid symbols within the segment.
 */
DECL_NO_INLINE(static, int)
rtDbgModSymbolByAddrTrySegments(PRTDBGMODINT pDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
                                PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
{
    Assert(iSeg <= RTDBGSEGIDX_LAST);
    RTDBGSEGMENT SegInfo;
    int rc = pDbgMod->pDbgVt->pfnSegmentByIndex(pDbgMod, iSeg, &SegInfo);
    if (RT_SUCCESS(rc))
    {
        pSymInfo->Value  = 0;
        pSymInfo->cb     = SegInfo.cb;
        pSymInfo->offSeg = 0;
        pSymInfo->iSeg   = iSeg;
        pSymInfo->fFlags = 0;
        if (SegInfo.szName[0])
            RTStrPrintf(pSymInfo->szName, sizeof(pSymInfo->szName), "start_seg%u_%s", SegInfo.iSeg, SegInfo.szName);
        else
            RTStrPrintf(pSymInfo->szName, sizeof(pSymInfo->szName), "start_seg%u", SegInfo.iSeg);
        if (poffDisp)
            *poffDisp = off;
        return VINF_SUCCESS;
    }
    return VERR_SYMBOL_NOT_FOUND;
}


RTDECL(int) RTDbgModSymbolByAddr(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
                                 PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
{
    /*
     * Validate input.
     */
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    AssertPtrNull(poffDisp);
    AssertPtr(pSymInfo);
    AssertReturn(!(fFlags & ~RTDBGSYMADDR_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);

    RTDBGMOD_LOCK(pDbgMod);

    /*
     * Convert RVAs.
     */
    if (iSeg == RTDBGSEGIDX_RVA)
    {
        iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off);
        if (iSeg == NIL_RTDBGSEGIDX)
        {
            RTDBGMOD_UNLOCK(pDbgMod);
            return VERR_DBG_INVALID_RVA;
        }
    }

    /*
     * Get down to business.
     */
    int rc = pDbgMod->pDbgVt->pfnSymbolByAddr(pDbgMod, iSeg, off, fFlags, poffDisp, pSymInfo);

    /* If we failed to locate a symbol, try use the specified segment as a reference. */
    if (   rc == VERR_SYMBOL_NOT_FOUND
        && iSeg <= RTDBGSEGIDX_LAST
        && !(fFlags & RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL))
        rc = rtDbgModSymbolByAddrTrySegments(pDbgMod, iSeg, off, poffDisp, pSymInfo);

    RTDBGMOD_UNLOCK(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModSymbolByAddr);


RTDECL(int) RTDbgModSymbolByAddrA(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
                                  PRTINTPTR poffDisp, PRTDBGSYMBOL *ppSymInfo)
{
    AssertPtr(ppSymInfo);
    *ppSymInfo = NULL;

    PRTDBGSYMBOL pSymInfo = RTDbgSymbolAlloc();
    if (!pSymInfo)
        return VERR_NO_MEMORY;

    int rc = RTDbgModSymbolByAddr(hDbgMod, iSeg, off, fFlags, poffDisp, pSymInfo);

    if (RT_SUCCESS(rc))
        *ppSymInfo = pSymInfo;
    else
        RTDbgSymbolFree(pSymInfo);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModSymbolByAddrA);


RTDECL(int) RTDbgModSymbolByName(RTDBGMOD hDbgMod, const char *pszSymbol, PRTDBGSYMBOL pSymInfo)
{
    /*
     * Validate input.
     */
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    AssertPtr(pszSymbol);
    size_t cchSymbol = strlen(pszSymbol);
    AssertReturn(cchSymbol, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE);
    AssertReturn(cchSymbol < RTDBG_SYMBOL_NAME_LENGTH, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE);
    AssertPtr(pSymInfo);

    /*
     * Make the query.
     */
    RTDBGMOD_LOCK(pDbgMod);
    int rc = pDbgMod->pDbgVt->pfnSymbolByName(pDbgMod, pszSymbol, cchSymbol, pSymInfo);
    RTDBGMOD_UNLOCK(pDbgMod);

    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModSymbolByName);


RTDECL(int) RTDbgModSymbolByNameA(RTDBGMOD hDbgMod, const char *pszSymbol, PRTDBGSYMBOL *ppSymInfo)
{
    AssertPtr(ppSymInfo);
    *ppSymInfo = NULL;

    PRTDBGSYMBOL pSymInfo = RTDbgSymbolAlloc();
    if (!pSymInfo)
        return VERR_NO_MEMORY;

    int rc = RTDbgModSymbolByName(hDbgMod, pszSymbol, pSymInfo);

    if (RT_SUCCESS(rc))
        *ppSymInfo = pSymInfo;
    else
        RTDbgSymbolFree(pSymInfo);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModSymbolByNameA);


RTDECL(int) RTDbgModLineAdd(RTDBGMOD hDbgMod, const char *pszFile, uint32_t uLineNo,
                            RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t *piOrdinal)
{
    /*
     * Validate input.
     */
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    AssertPtr(pszFile);
    size_t cchFile = strlen(pszFile);
    AssertReturn(cchFile, VERR_DBG_FILE_NAME_OUT_OF_RANGE);
    AssertReturn(cchFile < RTDBG_FILE_NAME_LENGTH, VERR_DBG_FILE_NAME_OUT_OF_RANGE);
    AssertMsgReturn(   iSeg <= RTDBGSEGIDX_LAST
                    || iSeg == RTDBGSEGIDX_RVA,
                    ("%#x\n", iSeg),
                    VERR_DBG_INVALID_SEGMENT_INDEX);
    AssertReturn(uLineNo > 0 && uLineNo < UINT32_MAX, VERR_INVALID_PARAMETER);

    RTDBGMOD_LOCK(pDbgMod);

    /*
     * Convert RVAs.
     */
    if (iSeg == RTDBGSEGIDX_RVA)
    {
        iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off);
        if (iSeg == NIL_RTDBGSEGIDX)
        {
            RTDBGMOD_UNLOCK(pDbgMod);
            return VERR_DBG_INVALID_RVA;
        }
    }

    /*
     * Get down to business.
     */
    int rc = pDbgMod->pDbgVt->pfnLineAdd(pDbgMod, pszFile, cchFile, uLineNo, iSeg, off, piOrdinal);

    RTDBGMOD_UNLOCK(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModLineAdd);


RTDECL(uint32_t) RTDbgModLineCount(RTDBGMOD hDbgMod)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX);
    RTDBGMOD_LOCK(pDbgMod);

    uint32_t cLineNumbers = pDbgMod->pDbgVt->pfnLineCount(pDbgMod);

    RTDBGMOD_UNLOCK(pDbgMod);
    return cLineNumbers;
}
RT_EXPORT_SYMBOL(RTDbgModLineCount);


RTDECL(int) RTDbgModLineByOrdinal(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
{
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    RTDBGMOD_LOCK(pDbgMod);

    int rc = pDbgMod->pDbgVt->pfnLineByOrdinal(pDbgMod, iOrdinal, pLineInfo);

    RTDBGMOD_UNLOCK(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModLineByOrdinal);


RTDECL(int) RTDbgModLineByOrdinalA(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGLINE *ppLineInfo)
{
    AssertPtr(ppLineInfo);
    *ppLineInfo = NULL;

    PRTDBGLINE pLineInfo = RTDbgLineAlloc();
    if (!pLineInfo)
        return VERR_NO_MEMORY;

    int rc = RTDbgModLineByOrdinal(hDbgMod, iOrdinal, pLineInfo);

    if (RT_SUCCESS(rc))
        *ppLineInfo = pLineInfo;
    else
        RTDbgLineFree(pLineInfo);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModLineByOrdinalA);


RTDECL(int) RTDbgModLineByAddr(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
{
    /*
     * Validate input.
     */
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    AssertPtrNull(poffDisp);
    AssertPtr(pLineInfo);

    RTDBGMOD_LOCK(pDbgMod);

    /*
     * Convert RVAs.
     */
    if (iSeg == RTDBGSEGIDX_RVA)
    {
        iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off);
        if (iSeg == NIL_RTDBGSEGIDX)
        {
            RTDBGMOD_UNLOCK(pDbgMod);
            return VERR_DBG_INVALID_RVA;
        }
    }

    int rc = pDbgMod->pDbgVt->pfnLineByAddr(pDbgMod, iSeg, off, poffDisp, pLineInfo);

    RTDBGMOD_UNLOCK(pDbgMod);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModLineByAddr);


RTDECL(int) RTDbgModLineByAddrA(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTINTPTR poffDisp, PRTDBGLINE *ppLineInfo)
{
    AssertPtr(ppLineInfo);
    *ppLineInfo = NULL;

    PRTDBGLINE pLineInfo = RTDbgLineAlloc();
    if (!pLineInfo)
        return VERR_NO_MEMORY;

    int rc = RTDbgModLineByAddr(hDbgMod, iSeg, off, poffDisp, pLineInfo);

    if (RT_SUCCESS(rc))
        *ppLineInfo = pLineInfo;
    else
        RTDbgLineFree(pLineInfo);
    return rc;
}
RT_EXPORT_SYMBOL(RTDbgModLineByAddrA);


RTDECL(int) RTDbgModUnwindFrame(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
{
    /*
     * Validate input.
     */
    PRTDBGMODINT pDbgMod = hDbgMod;
    RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
    AssertPtr(pState);
    AssertReturn(pState->u32Magic == RTDBGUNWINDSTATE_MAGIC, VERR_INVALID_MAGIC);

    RTDBGMOD_LOCK(pDbgMod);

    /*
     * Convert RVAs.
     */
    if (iSeg == RTDBGSEGIDX_RVA)
    {
        iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off);
        if (iSeg == NIL_RTDBGSEGIDX)
        {
            RTDBGMOD_UNLOCK(pDbgMod);
            return VERR_DBG_INVALID_RVA;
        }
    }

    /*
     * Try the debug module first, then the image.
     */
    int rc = VERR_DBG_NO_UNWIND_INFO;
    if (pDbgMod->pDbgVt->pfnUnwindFrame)
        rc = pDbgMod->pDbgVt->pfnUnwindFrame(pDbgMod, iSeg, off, pState);
    if (   (   rc == VERR_DBG_NO_UNWIND_INFO
            || rc == VERR_DBG_UNWIND_INFO_NOT_FOUND)
        && pDbgMod->pImgVt
        && pDbgMod->pImgVt->pfnUnwindFrame)
    {
        if (rc == VERR_DBG_NO_UNWIND_INFO)
            rc = pDbgMod->pImgVt->pfnUnwindFrame(pDbgMod, iSeg, off, pState);
        else
        {
            rc = pDbgMod->pImgVt->pfnUnwindFrame(pDbgMod, iSeg, off, pState);
            if (rc == VERR_DBG_NO_UNWIND_INFO)
                rc = VERR_DBG_UNWIND_INFO_NOT_FOUND;
        }
    }

    RTDBGMOD_UNLOCK(pDbgMod);
    return rc;

}
RT_EXPORT_SYMBOL(RTDbgModUnwindFrame);