/* $Id: dbgmoddeferred.cpp 46109 2013-05-15 19:54:06Z vboxsync $ */ /** @file * IPRT - Debug Module Deferred Loading Stub. */ /* * Copyright (C) 2013 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 * *******************************************************************************/ #include #include "internal/iprt.h" #include #include #include #include #include #include #include #include "internal/dbgmod.h" #include "internal/magics.h" /** * Releases the instance data. * * @param pThis The instance data. */ static void rtDbgModDeferredReleaseInstanceData(PRTDBGMODDEFERRED pThis) { AssertPtr(pThis); uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); Assert(cRefs < 8); if (!cRefs) { RTDbgCfgRelease(pThis->hDbgCfg); pThis->hDbgCfg = NIL_RTDBGCFG; RTMemFree(pThis); } } /** * Does the deferred loading of the real data (image and/or debug info). * * @returns VINF_SUCCESS or VERR_DBG_DEFERRED_LOAD_FAILED. * @param pMod The generic module instance data. * @param fForcedRetry Whether it's a forced retry by one of the * pfnTryOpen methods. */ static int rtDbgModDeferredDoIt(PRTDBGMODINT pMod, bool fForcedRetry) { RTCritSectEnter(&pMod->CritSect); int rc; if (!pMod->fDeferredFailed || fForcedRetry) { bool const fDbgVt = pMod->pDbgVt == &g_rtDbgModVtDbgDeferred; bool const fImgVt = pMod->pImgVt == &g_rtDbgModVtImgDeferred; AssertReturnStmt(fDbgVt || fImgVt, RTCritSectLeave(&pMod->CritSect), VERR_INTERNAL_ERROR_5); PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)(fDbgVt ? pMod->pvDbgPriv : pMod->pvImgPriv); /* Reset the method tables and private data pointes so the deferred loading procedure can figure out what to do and won't get confused. */ if (fDbgVt) { pMod->pvDbgPriv = NULL; pMod->pDbgVt = NULL; } if (fImgVt) { pMod->pvImgPriv = NULL; pMod->pImgVt = NULL; } /* Do the deferred loading. */ rc = pThis->pfnDeferred(pMod, pThis); if (RT_SUCCESS(rc)) { Assert(!fDbgVt || pMod->pDbgVt != NULL); Assert(!fImgVt || pMod->pImgVt != NULL); pMod->fDeferred = false; pMod->fDeferredFailed = false; rtDbgModDeferredReleaseInstanceData(pThis); if (fImgVt && fDbgVt) rtDbgModDeferredReleaseInstanceData(pThis); } else { /* Failed, bail out and restore the deferred setup. */ pMod->fDeferredFailed = true; if (fDbgVt) { Assert(!pMod->pDbgVt); pMod->pDbgVt = &g_rtDbgModVtDbgDeferred; pMod->pvDbgPriv = pThis; } if (fImgVt) { Assert(!pMod->pImgVt); pMod->pImgVt = &g_rtDbgModVtImgDeferred; pMod->pvImgPriv = pThis; } } } else rc = VERR_DBG_DEFERRED_LOAD_FAILED; RTCritSectLeave(&pMod->CritSect); return rc; } /* * * D e b u g I n f o M e t h o d s * D e b u g I n f o M e t h o d s * D e b u g I n f o M e t h o d s * */ /** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */ static DECLCALLBACK(int) rtDbgModDeferredDbg_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pDbgVt->pfnLineByAddr(pMod, iSeg, off, poffDisp, pLineInfo); return rc; } /** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */ static DECLCALLBACK(int) rtDbgModDeferredDbg_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pDbgVt->pfnLineByOrdinal(pMod, iOrdinal, pLineInfo); return rc; } /** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */ static DECLCALLBACK(uint32_t) rtDbgModDeferredDbg_LineCount(PRTDBGMODINT pMod) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) return pMod->pDbgVt->pfnLineCount(pMod); return 0; } /** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */ static DECLCALLBACK(int) rtDbgModDeferredDbg_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo, uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pDbgVt->pfnLineAdd(pMod, pszFile, cchFile, uLineNo, iSeg, off, piOrdinal); return rc; } /** * Fill in symbol info for the fake start symbol. * * @returns VINF_SUCCESS * @param pThis The deferred load data. * @param pSymInfo The output structure. */ static int rtDbgModDeferredDbgSymInfo_Start(PRTDBGMODDEFERRED pThis, PRTDBGSYMBOL pSymInfo) { pSymInfo->Value = 0; pSymInfo->cb = pThis->cbImage; pSymInfo->offSeg = 0; pSymInfo->iSeg = 0; pSymInfo->fFlags = 0; pSymInfo->iOrdinal = 0; memcpy(pSymInfo->szName, RT_STR_TUPLE("DeferredStart")); return VINF_SUCCESS; } /** * Fill in symbol info for the fake last symbol. * * @returns VINF_SUCCESS * @param pThis The deferred load data. * @param pSymInfo The output structure. */ static int rtDbgModDeferredDbgSymInfo_Last(PRTDBGMODDEFERRED pThis, PRTDBGSYMBOL pSymInfo) { pSymInfo->Value = pThis->cbImage - 1; pSymInfo->cb = 0; pSymInfo->offSeg = pThis->cbImage - 1; pSymInfo->iSeg = 0; pSymInfo->fFlags = 0; pSymInfo->iOrdinal = 1; memcpy(pSymInfo->szName, RT_STR_TUPLE("DeferredLast")); return VINF_SUCCESS; } /** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */ static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pDbgVt->pfnSymbolByAddr(pMod, iSeg, off, fFlags, poffDisp, pSymInfo); else { PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv; if (off == 0) rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo); else if (off >= pThis->cbImage - 1 || (fFlags & RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL)) rc = rtDbgModDeferredDbgSymInfo_Last(pThis, pSymInfo); else rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo); if (poffDisp) *poffDisp = off - pSymInfo->offSeg; } return rc; } /** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */ static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, PRTDBGSYMBOL pSymInfo) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pDbgVt->pfnSymbolByName(pMod, pszSymbol, cchSymbol, pSymInfo); else { PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv; if ( cchSymbol == sizeof("DeferredStart") - 1 && !memcmp(pszSymbol, RT_STR_TUPLE("DeferredStart"))) rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo); else if ( cchSymbol == sizeof("DeferredLast") - 1 && !memcmp(pszSymbol, RT_STR_TUPLE("DeferredLast"))) rc = rtDbgModDeferredDbgSymInfo_Last(pThis, pSymInfo); else rc = VERR_SYMBOL_NOT_FOUND; } return rc; } /** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */ static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pDbgVt->pfnSymbolByOrdinal(pMod, iOrdinal, pSymInfo); else { PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv; if (iOrdinal == 0) rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo); else if (iOrdinal == 1) rc = rtDbgModDeferredDbgSymInfo_Last(pThis, pSymInfo); else rc = VERR_SYMBOL_NOT_FOUND; } return rc; } /** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */ static DECLCALLBACK(uint32_t) rtDbgModDeferredDbg_SymbolCount(PRTDBGMODINT pMod) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) return pMod->pDbgVt->pfnSymbolCount(pMod); return 2; } /** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */ static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, uint32_t *piOrdinal) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pDbgVt->pfnSymbolAdd(pMod, pszSymbol, cchSymbol, iSeg, off, cb, fFlags, piOrdinal); return rc; } /** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */ static DECLCALLBACK(int) rtDbgModDeferredDbg_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pDbgVt->pfnSegmentByIndex(pMod, iSeg, pSegInfo); else if (iSeg == 0) { PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv; pSegInfo->Address = 0; pSegInfo->uRva = 0; pSegInfo->cb = pThis->cbImage; pSegInfo->fFlags = 0; pSegInfo->iSeg = 0; memcpy(pSegInfo->szName, RT_STR_TUPLE("LATER")); } else rc = VERR_DBG_INVALID_SEGMENT_INDEX; return rc; } /** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */ static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDeferredDbg_SegmentCount(PRTDBGMODINT pMod) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) return pMod->pDbgVt->pfnSegmentCount(pMod); return 1; } /** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */ static DECLCALLBACK(int) rtDbgModDeferredDbg_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName, uint32_t fFlags, PRTDBGSEGIDX piSeg) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pDbgVt->pfnSegmentAdd(pMod, uRva, cb, pszName, cchName, fFlags, piSeg); return rc; } /** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */ static DECLCALLBACK(RTUINTPTR) rtDbgModDeferredDbg_ImageSize(PRTDBGMODINT pMod) { PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv; return pThis->cbImage; } /** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */ static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDeferredDbg_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) return pMod->pDbgVt->pfnRvaToSegOff(pMod, uRva, poffSeg); return 0; } /** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */ static DECLCALLBACK(int) rtDbgModDeferredDbg_Close(PRTDBGMODINT pMod) { rtDbgModDeferredReleaseInstanceData((PRTDBGMODDEFERRED)pMod->pvImgPriv); return VINF_SUCCESS; } /** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */ static DECLCALLBACK(int) rtDbgModDeferredDbg_TryOpen(PRTDBGMODINT pMod) { return rtDbgModDeferredDoIt(pMod, true /*fForceRetry*/); } /** Virtual function table for the deferred debug info reader. */ DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgDeferred = { /*.u32Magic = */ RTDBGMODVTDBG_MAGIC, /*.fSupports = */ RT_DBGTYPE_MAP, /*.pszName = */ "deferred", /*.pfnTryOpen = */ rtDbgModDeferredDbg_TryOpen, /*.pfnClose = */ rtDbgModDeferredDbg_Close, /*.pfnRvaToSegOff = */ rtDbgModDeferredDbg_RvaToSegOff, /*.pfnImageSize = */ rtDbgModDeferredDbg_ImageSize, /*.pfnSegmentAdd = */ rtDbgModDeferredDbg_SegmentAdd, /*.pfnSegmentCount = */ rtDbgModDeferredDbg_SegmentCount, /*.pfnSegmentByIndex = */ rtDbgModDeferredDbg_SegmentByIndex, /*.pfnSymbolAdd = */ rtDbgModDeferredDbg_SymbolAdd, /*.pfnSymbolCount = */ rtDbgModDeferredDbg_SymbolCount, /*.pfnSymbolByOrdinal = */ rtDbgModDeferredDbg_SymbolByOrdinal, /*.pfnSymbolByName = */ rtDbgModDeferredDbg_SymbolByName, /*.pfnSymbolByAddr = */ rtDbgModDeferredDbg_SymbolByAddr, /*.pfnLineAdd = */ rtDbgModDeferredDbg_LineAdd, /*.pfnLineCount = */ rtDbgModDeferredDbg_LineCount, /*.pfnLineByOrdinal = */ rtDbgModDeferredDbg_LineByOrdinal, /*.pfnLineByAddr = */ rtDbgModDeferredDbg_LineByAddr, /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC }; /* * * I m a g e M e t h o d s * I m a g e M e t h o d s * I m a g e M e t h o d s * */ /** @interface_method_impl{RTDBGMODVTIMG,pfnUnmapPart} */ static DECLCALLBACK(int) rtDbgModDeferredImg_UnmapPart(PRTDBGMODINT pMod, size_t cb, void const **ppvMap) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pImgVt->pfnUnmapPart(pMod, cb, ppvMap); return rc; } /** @interface_method_impl{RTDBGMODVTIMG,pfnMapPart} */ static DECLCALLBACK(int) rtDbgModDeferredImg_MapPart(PRTDBGMODINT pMod, RTFOFF off, size_t cb, void const **ppvMap) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pImgVt->pfnMapPart(pMod, off, cb, ppvMap); return rc; } /** @interface_method_impl{RTDBGMODVTIMG,pfnImageSize} */ static DECLCALLBACK(RTUINTPTR) rtDbgModDeferredImg_ImageSize(PRTDBGMODINT pMod) { PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvImgPriv; return pThis->cbImage; } /** @interface_method_impl{RTDBGMODVTIMG,pfnLinkAddressToSegOffset} */ static DECLCALLBACK(int) rtDbgModDeferredImg_LinkAddressToSegOffset(PRTDBGMODINT pMod, RTLDRADDR LinkAddress, PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pImgVt->pfnLinkAddressToSegOffset(pMod, LinkAddress, piSeg, poffSeg); return rc; } /** @interface_method_impl{RTDBGMODVTIMG,pfnEnumSegments} */ static DECLCALLBACK(int) rtDbgModDeferredImg_EnumSegments(PRTDBGMODINT pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pImgVt->pfnEnumSegments(pMod, pfnCallback, pvUser); return rc; } /** @interface_method_impl{RTDBGMODVTIMG,pfnEnumDbgInfo} */ static DECLCALLBACK(int) rtDbgModDeferredImg_EnumDbgInfo(PRTDBGMODINT pMod, PFNRTLDRENUMDBG pfnCallback, void *pvUser) { int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); if (RT_SUCCESS(rc)) rc = pMod->pImgVt->pfnEnumDbgInfo(pMod, pfnCallback, pvUser); return rc; } /** @interface_method_impl{RTDBGMODVTIMG,pfnClose} */ static DECLCALLBACK(int) rtDbgModDeferredImg_Close(PRTDBGMODINT pMod) { rtDbgModDeferredReleaseInstanceData((PRTDBGMODDEFERRED)pMod->pvImgPriv); return VINF_SUCCESS; } /** @interface_method_impl{RTDBGMODVTIMG,pfnTryOpen} */ static DECLCALLBACK(int) rtDbgModDeferredImg_TryOpen(PRTDBGMODINT pMod) { return rtDbgModDeferredDoIt(pMod, true /*fForceRetry*/); } /** Virtual function table for the RTLdr based image reader. */ DECL_HIDDEN_CONST(RTDBGMODVTIMG) const g_rtDbgModVtImgDeferred = { /*.u32Magic = */ RTDBGMODVTIMG_MAGIC, /*.fReserved = */ 0, /*.pszName = */ "deferred", /*.pfnTryOpen = */ rtDbgModDeferredImg_TryOpen, /*.pfnClose = */ rtDbgModDeferredImg_Close, /*.pfnEnumDbgInfo = */ rtDbgModDeferredImg_EnumDbgInfo, /*.pfnEnumSegments = */ rtDbgModDeferredImg_EnumSegments, /*.pfnGetLoadedSize = */ rtDbgModDeferredImg_ImageSize, /*.pfnLinkAddressToSegOffset = */ rtDbgModDeferredImg_LinkAddressToSegOffset, /*.pfnMapPart = */ rtDbgModDeferredImg_MapPart, /*.pfnUnmapPart = */ rtDbgModDeferredImg_UnmapPart, /*.u32EndMagic = */ RTDBGMODVTIMG_MAGIC }; /** * Creates a deferred loading stub for both image and debug info. * * @returns IPRT status code. * @param pDbgMod The debug module. * @param pfnDeferred The callback that will try load the image and * debug info. * @param cbImage The size of the image. * @param hDbgCfg The debug config handle. Can be NIL. A * reference will be retained. * @param ppDeferred Where to return the instance data. Can be NULL. */ DECLHIDDEN(int) rtDbgModDeferredCreate(PRTDBGMODINT pDbgMod, PFNRTDBGMODDEFERRED pfnDeferred, RTUINTPTR cbImage, RTDBGCFG hDbgCfg, PRTDBGMODDEFERRED *ppDeferred) { AssertReturn(!pDbgMod->pDbgVt, VERR_DBG_MOD_IPE); PRTDBGMODDEFERRED pDeferred = (PRTDBGMODDEFERRED)RTMemAllocZ(sizeof(*pDeferred)); if (!pDeferred) return VERR_NO_MEMORY; pDeferred->cbImage = cbImage; pDeferred->cRefs = 1 + (pDbgMod->pImgVt == NULL); if (hDbgCfg != NIL_RTDBGCFG) RTDbgCfgRetain(hDbgCfg); pDeferred->hDbgCfg = hDbgCfg; pDeferred->pfnDeferred = pfnDeferred; pDbgMod->pDbgVt = &g_rtDbgModVtDbgDeferred; pDbgMod->pvDbgPriv = pDeferred; if (!pDbgMod->pImgVt) { pDbgMod->pImgVt = &g_rtDbgModVtImgDeferred; pDbgMod->pvImgPriv = pDeferred; } pDbgMod->fDeferred = true; pDbgMod->fDeferredFailed = false; if (ppDeferred) *ppDeferred = pDeferred; return VINF_SUCCESS; }