dbgmoddwarf.cpp revision 5a5b5956f8b592c807c94785d58c25e717d430c4
/* $Id$ */
/** @file
* IPRT - Debug Info Reader For DWARF.
*/
/*
* Copyright (C) 2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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_DWARF
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** @name Standard DWARF Line Number Opcodes
* @{ */
#define DW_LNS_extended UINT8_C(0)
/** @} */
/** @name Extended DWARF Line Number Opcodes
* @{ */
/** @} */
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* DWARF sections.
*/
typedef enum krtDbgModDwarfSect
{
/** End of valid parts (exclusive). */
/**
* The instance data of the DWARF reader.
*/
typedef struct RTDBGMODDWARF
{
/** The debug container containing doing the real work. */
/** Pointer to back to the debug info module (no reference ofc). */
/** DWARF debug info sections. */
struct
{
/** The file offset of the part. */
/** The size of the part. */
/** The memory mapping of the part. */
void const *pv;
/** Set if present. */
bool fPresent;
/** Pointer to instance data of the DWARF reader. */
typedef RTDBGMODDWARF *PRTDBGMODDWARF;
/**
* Section reader instance.
*/
typedef struct RTDWARFSECTRDR
{
/** The current position. */
/** The number of bytes left to read. */
/** The number of bytes left to read in the current unit. */
/** The DWARF debug info reader instance. */
/** Set if this is 64-bit DWARF, clear if 32-bit. */
bool f64bitDwarf;
/** Set if the format endian is native, clear if endian needs to be
* inverted. */
bool fNativEndian;
/** The size of a native address. */
/** The cursor status code. This is VINF_SUCCESS until some error
* occurs. */
int rc;
/** Pointer to a DWARF section reader. */
typedef RTDWARFCURSOR *PRTDWARFCURSOR;
/**
* Loads a DWARF section from the image file.
*
* @returns IPRT status code.
* @param pThis The DWARF instance.
* @param enmSect The section to load.
*/
{
/*
* Don't load stuff twice.
*/
return VINF_SUCCESS;
/*
* Sections that are not present cannot be loaded, treat them like they
* are empty
*/
{
return VINF_SUCCESS;
}
return VINF_SUCCESS;
/*
* Sections must be readable with the current image interface.
*/
return VERR_OUT_OF_RANGE;
/*
* Do the job.
*/
return pThis->pMod->pImgVt->pfnMapPart(pThis->pMod, pThis->aSections[enmSect].offFile, pThis->aSections[enmSect].cb,
}
/**
* Unloads a DWARF section previously mapped by rtDbgModDwarfLoadSection.
*
* @returns IPRT status code.
* @param pThis The DWARF instance.
* @param enmSect The section to unload.
*/
{
return VINF_SUCCESS;
int rc = pThis->pMod->pImgVt->pfnUnmapPart(pThis->pMod, pThis->aSections[enmSect].cb, &pThis->aSections[enmSect].pv);
return rc;
}
/**
* Converts to UTF-8 or otherwise makes sure it's valid UTF-8.
*
* @returns IPRT status code.
* @param pThis The DWARF instance.
* @param ppsz Pointer to the string pointer. May be
* reallocated (RTStr*).
*/
{
return VINF_SUCCESS;
}
/**
* Convers a link address into a segment+offset or RVA.
*
* @returns IPRT status code.
* @param pThis The DWARF instance.
* @param LinkAddress The address to convert..
* @param piSeg The segment index.
* @param poffSeg Where to return the segment offset.
*/
{
/** @todo The image should be doing this conversion, not we. */
*piSeg = RTDBGSEGIDX_RVA;
*poffSeg = LinkAddress;
return VINF_SUCCESS;
}
{
{
return uErrValue;
}
return u8;
}
{
{
pCursor->cbUnitLeft = 0;
return uErrValue;
}
if (!pCursor->fNativEndian)
return u16;
}
{
{
pCursor->cbUnitLeft = 0;
return uErrValue;
}
if (!pCursor->fNativEndian)
return u32;
}
{
{
pCursor->cbUnitLeft = 0;
return uErrValue;
}
if (!pCursor->fNativEndian)
return u64;
}
/**
* Gets a unsigned LEB128 encoded number.
*
* @returns unsigned number.
* @param pCursor The cursor.
* @param uErrValue The value to return on error.
*/
{
{
return uErrValue;
}
/*
* Special case - single byte.
*/
if (!(b & 0x80))
{
return b;
}
/*
* Generic case.
*/
/* Decode. */
do
{
{
break;
}
off++;
} while (b & 0x80);
/* Update the cursor. */
/* Check the range. */
if (cBits > 64)
{
}
return u64Ret;
}
/**
* Gets a signed LEB128 encoded number.
*
* @returns signed number.
* @param pCursor The cursor.
* @param sErrValue The value to return on error.
*/
{
{
return sErrValue;
}
/*
* Special case - single byte.
*/
if (!(b & 0x80))
{
if (b & 0x40)
b |= 0x80;
return (int8_t)b;
}
/*
* Generic case.
*/
/* Decode it. */
do
{
{
break;
}
off++;
} while (b & 0x80);
/* Update cursor. */
/* Check the range. */
if (cBits > 64)
{
}
/* Sign extend the value. */
}
/**
* Gets a unsigned LEB128 encoded number, max 32-bit width.
*
* @returns unsigned number.
* @param pCursor The cursor.
* @param uErrValue The value to return on error.
*/
{
if (u64 > UINT32_MAX)
{
return uErrValue;
}
}
/**
* Gets a signed LEB128 encoded number, max 32-bit width.
*
* @returns unsigned number.
* @param pCursor The cursor.
* @param sErrValue The value to return on error.
*/
{
{
return sErrValue;
}
}
{
do
{
{
break;
}
}
/**
* Reads a zero terminated string, advancing the cursor beyond the terminator.
*
* @returns Pointer to the string.
* @param pCursor The cursor.
* @param pszErrValue What to return if the string isn't terminated
* before the end of the unit.
*/
{
for (;;)
{
if (!pCursor->cbUnitLeft)
{
return pszErrValue;
}
pCursor->cbUnitLeft--;
break;
}
return pszRet;
}
{
}
{
}
{
}
{
if (pCursor->f64bitDwarf)
}
/**
* Gets the unit length, updating the unit length member and DWARF bitness
* members of the cursor.
*
* @returns The unit length.
* @param pCursor The cursor.
*/
{
/*
* Read the initial length.
*/
pCursor->f64bitDwarf = false;
else
{
pCursor->f64bitDwarf = true;
}
/*
* Set the unit length, quitely fixing bad lengths.
*/
return cbUnit;
}
/**
* Calculates an absolute cursor position from one relative to the current
* cursor position.
*
* @returns The absolute cursor position.
* @param pCursor The cursor.
* @param offRelative The relative position. Must be a positive
* offset.
*/
{
{
return NULL;
}
}
/**
* Advances the cursor to the given position.
*
* @returns IPRT status code.
* @param pCursor The cursor.
* @param pbNewPos The new position - returned by
* rtDwarfCursor_CalcPos().
*/
{
{
AssertFailed();
}
}
{
return !pCursor->cbUnitLeft;
}
{
pCursor->cbUnitLeft = 0;
return VINF_SUCCESS;
}
{
}
/**
* Initialize a section reader.
*
* @returns IPRT status code.
* @param pCursor The section reader.
* @param pThis The dwarf module.
* @param enmSect .
*/
static int rtDwarfCursor_Init(PRTDWARFCURSOR pCursor, PRTDBGMODDWARF pThis, krtDbgModDwarfSect enmSect)
{
if (RT_FAILURE(rc))
return rc;
pCursor->f64bitDwarf = false;
/** @todo ask the image about the endian used as well as the address
* width. */
return VINF_SUCCESS;
}
/**
* Deletes a section reader initialized by rtDwarfCursor_Init.
*
* @param pCursor The section reader.
*/
{
/* ... and a drop of poison. */
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
static DECLCALLBACK(int) rtDbgModDwarf_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
static DECLCALLBACK(int) rtDbgModDwarf_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
static DECLCALLBACK(int) rtDbgModDwarf_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
static DECLCALLBACK(int) rtDbgModDwarf_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
static DECLCALLBACK(int) rtDbgModDwarf_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
static DECLCALLBACK(int) rtDbgModDwarf_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
static DECLCALLBACK(int) rtDbgModDwarf_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
static DECLCALLBACK(int) rtDbgModDwarf_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
static DECLCALLBACK(int) rtDbgModDwarf_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName,
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDwarf_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
{
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
{
pThis->pMod->pImgVt->pfnUnmapPart(pThis->pMod, pThis->aSections[iSect].cb, &pThis->aSections[iSect].pv);
return VINF_SUCCESS;
}
/**
* DWARF line number program state.
*/
typedef struct RTDWARFLINESTATE
{
/** Virtual Line Number Machine Registers. */
struct
{
bool fIsStatement;
bool fBasicBlock;
bool fEndSequence;
bool fPrologueEnd;
bool fEpilogueBegin;
} Regs;
/** @} */
/** Header. */
struct
{
uint8_t const *pacStdOperands;
} Hdr;
/** @name Include Path Table (0-based)
* @{ */
const char **papszIncPaths;
/** @} */
/** @name File Name Table (0-based, dummy zero entry)
* @{ */
char **papszFileNames;
/** @} */
/** The DWARF debug info reader instance. */
/** Pointer to a DWARF line number program state. */
typedef RTDWARFLINESTATE *PRTDWARFLINESTATE;
static int rtDwarfLine_DefineFileName(PRTDWARFLINESTATE pLnState, const char *pszFilename, uint64_t idxInc)
{
/*
* Resize the array if necessary.
*/
if ((iFileName % 2) == 0)
{
void *pv = RTMemRealloc(pLnState->papszFileNames, sizeof(pLnState->papszFileNames[0]) * (iFileName + 2));
if (!pv)
return VERR_NO_MEMORY;
}
/*
* Add the file name.
*/
if ( pszFilename[0] == '/'
|| pszFilename[0] == '\\'
else
return VERR_NO_STR_MEMORY;
/*
* Sanitize the name.
*/
return rc;
}
{
: "<bad file name index>";
int rc = rtDbgModDwarfLinkAddressToSegOffset(pLnState->pDwarfMod, pLnState->Regs.uAddress, &iSeg, &offSeg);
if (RT_SUCCESS(rc))
{
Log2(("rtDwarfLine_StdOp_Copy: %x:%08llx (%#llx) %s(%d)\n", iSeg, offSeg, pLnState->Regs.uAddress, pszFile, pLnState->Regs.uLine));
/* Ignore address conflicts for now. */
if (rc == VERR_DBG_ADDRESS_CONFLICT)
rc = VINF_SUCCESS;
}
return rc;
}
/**
* Reset the program to the start-of-sequence state.
*
* @param pLnState The line number program state.
*/
{
}
/**
* Runs the line number program.
*
* @returns IPRT status code.
* @param pLnState The line number program state.
* @param pCursor The cursor.
*/
{
int rc = VINF_SUCCESS;
while (!rtDwarfCursor_IsAtEndOfUnit(pCursor))
{
{
/*
* Special opcode.
*/
Log2(("DW Special Opcode %#04x: uLine + %d => %u; uAddress + %#llx => %#llx; idxOp + %#llx => %#llx\n",
}
else
{
switch (bOpCode)
{
/*
* Standard opcode.
*/
case DW_LNS_copy:
Log2(("DW_LNS_copy\n"));
break;
case DW_LNS_advance_pc:
{
Log2(("DW_LNS_advance_pc\n"));
break;
}
case DW_LNS_advance_line:
{
break;
}
case DW_LNS_set_file:
break;
case DW_LNS_set_column:
Log2(("DW_LNS_set_column\n"));
break;
case DW_LNS_negate_stmt:
Log2(("DW_LNS_negate_stmt\n"));
break;
case DW_LNS_set_basic_block:
Log2(("DW_LNS_set_basic_block\n"));
break;
case DW_LNS_const_add_pc:
Log2(("DW_LNS_const_add_pc\n"));
break;
case DW_LNS_fixed_advance_pc:
Log2(("DW_LNS_fixed_advance_pc\n"));
break;
case DW_LNS_set_prologue_end:
Log2(("DW_LNS_set_prologue_end\n"));
break;
Log2(("DW_LNS_set_epilogue_begin\n"));
break;
case DW_LNS_set_isa:
break;
default:
{
while (cOpsToSkip-- > 0)
break;
}
/*
* Extended opcode.
*/
case DW_LNS_extended:
{
/* The instruction has a length prefix. */
return VERR_DWARF_BAD_LNE;
/* Get the opcode and deal with it if we know it. */
switch (bOpCode)
{
case DW_LNE_end_sequence:
#if 0 /* No need for this */
#endif
Log2(("DW_LNE_end_sequence\n"));
break;
case DW_LNE_set_address:
switch (cbInstr - 1)
{
default:
break;
}
break;
case DW_LNE_define_file:
{
if (RT_SUCCESS(rc))
}
case DW_LNE_set_descriminator:
break;
default:
break;
}
/* Advance the cursor to the end of the instruction . */
break;
}
}
}
/*
* Check the status before looping.
*/
if (RT_FAILURE(rc))
return rc;
}
return rc;
}
/**
* Reads the include directories for a line number unit.
*
* @returns IPRT status code
* @param pLnState The line number program state.
* @param pCursor The cursor.
*/
{
if (RT_FAILURE(rc))
return rc;
for (;;)
{
if (!*psz)
break;
if (RT_FAILURE(rc))
return rc;
}
}
/**
* Reads the include directories for a line number unit.
*
* @returns IPRT status code
* @param pLnState The line number program state.
* @param pCursor The cursor.
*/
{
for (;;)
{
{
void *pv = RTMemRealloc(pLnState->papszIncPaths, sizeof(pLnState->papszIncPaths[0]) * (pLnState->cIncPaths + 2));
if (!pv)
return VERR_NO_MEMORY;
}
if (!*psz)
break;
}
}
{
/*
* Parse the header.
*/
return rtDwarfCursor_SkipUnit(pCursor);
else
Log2(("DWARF Line number header:\n"
" uVer %d\n"
" offFirstOpcode %#llx\n"
" cbMinInstr %u\n"
" cMaxOpsPerInstr %u\n"
" u8DefIsStmt %u\n"
" s8LineBase %d\n"
" u8LineRange %u\n"
" u8OpcodeBase %u\n",
LnState.Hdr.u8DefIsStmt, LnState.Hdr.s8LineBase, LnState.Hdr.u8LineRange, LnState.Hdr.u8OpcodeBase));
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
/*
* Run the program....
*/
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
/*
* Clean up.
*/
while (i-- > 0)
return rc;
}
/**
* Explodes the line number table.
*
* The line numbers are insered into the debug info container.
*
* @returns IPRT status code
* @param pThis The DWARF instance.
*/
{
return VINF_SUCCESS;
if (RT_FAILURE(rc))
return rc;
while ( !rtDwarfCursor_IsAtEnd(&Cursor)
&& RT_SUCCESS(rc))
return rc;
}
/**
* Extracts the symbols.
*
* The symbols are insered into the debug info container.
*
* @returns IPRT status code
* @param pThis The DWARF instance.
*/
{
if (RT_FAILURE(rc))
return rc;
/** @todo */
return rc;
}
/**
* Loads the abbreviations used to parse the info section.
*
* @returns IPRT status code
* @param pThis The DWARF instance.
*/
{
if (RT_FAILURE(rc))
return rc;
#if 0 /** @todo */
while (cbLeft > 0)
{
}
#endif
return rc;
}
/** @callback_method_impl{FNRTLDRENUMSEGS} */
static DECLCALLBACK(int) rtDbgModHlpAddSegmentCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
{
Log(("Segment %.*s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n",
#if 0
return pMod->pDbgVt->pfnSegmentAdd(pMod, pSeg->RVA, pSeg->cb, pSeg->pchName, pSeg->cchName, 0 /*fFlags*/, NULL);
#else
return pMod->pDbgVt->pfnSegmentAdd(pMod, pSeg->LinkAddress, pSeg->cb, pSeg->pchName, pSeg->cchName, 0 /*fFlags*/, NULL);
#endif
}
/**
* Calls pfnSegmentAdd for each segment in the executable image.
*
* @returns IPRT status code.
* @param pMod The debug module.
*/
{
}
/** @callback_method_impl{FNRTLDRENUMDBG} */
static DECLCALLBACK(int) rtDbgModDwarfEnumCallback(RTLDRMOD hLdrMod, uint32_t iDbgInfo, RTLDRDBGINFOTYPE enmType,
const char *pszExtFile, void *pvUser)
{
/*
* Skip stuff we can't handle.
*/
if ( enmType != RTLDRDBGINFOTYPE_DWARF
|| !pszPartNm
|| pszExtFile)
return VINF_SUCCESS;
/*
* Must have a part name starting with debug_ and possibly prefixed by dots
* or underscores.
*/
else
/*
* Figure out which part we're talking about.
*/
if (0) { /* dummy */ }
#define ELSE_IF_STRCMP_SET(a_Name) else if (!strcmp(pszPartNm, #a_Name)) enmSect = krtDbgModDwarfSect_ ## a_Name
else
{
return VINF_SUCCESS;
}
/*
* Record the section.
*/
AssertMsgReturn(!pThis->aSections[enmSect].fPresent, ("duplicate %s\n", pszPartNm), VINF_SUCCESS /*ignore*/);
return VINF_SUCCESS;
}
/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
{
/*
* DWARF is only supported when part of an image.
*/
return VERR_DBG_NO_MATCHING_INTERPRETER;
/*
* Enumerate the debug info in the module, looking for DWARF bits.
*/
if (!pThis)
return VERR_NO_MEMORY;
if (RT_SUCCESS(rc))
{
{
/*
* Extract / explode the data we want (symbols and line numbers)
* storing them in a container module.
*/
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
{
return VINF_SUCCESS;
}
/* bail out. */
}
}
else
}
return rc;
}
/** Virtual function table for the DWARF debug info reader. */
{
/*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
/*.fSupports = */ RT_DBGTYPE_DWARF,
/*.pszName = */ "dwarf",
/*.pfnTryOpen = */ rtDbgModDwarf_TryOpen,
/*.pfnClose = */ rtDbgModDwarf_Close,
/*.pfnRvaToSegOff = */ rtDbgModDwarf_RvaToSegOff,
/*.pfnImageSize = */ rtDbgModDwarf_ImageSize,
/*.pfnSegmentAdd = */ rtDbgModDwarf_SegmentAdd,
/*.pfnSegmentCount = */ rtDbgModDwarf_SegmentCount,
/*.pfnSegmentByIndex = */ rtDbgModDwarf_SegmentByIndex,
/*.pfnSymbolAdd = */ rtDbgModDwarf_SymbolAdd,
/*.pfnSymbolCount = */ rtDbgModDwarf_SymbolCount,
/*.pfnSymbolByOrdinal = */ rtDbgModDwarf_SymbolByOrdinal,
/*.pfnSymbolByName = */ rtDbgModDwarf_SymbolByName,
/*.pfnSymbolByAddr = */ rtDbgModDwarf_SymbolByAddr,
/*.pfnLineAdd = */ rtDbgModDwarf_LineAdd,
/*.pfnLineCount = */ rtDbgModDwarf_LineCount,
/*.pfnLineByOrdinal = */ rtDbgModDwarf_LineByOrdinal,
/*.pfnLineByAddr = */ rtDbgModDwarf_LineByAddr,
/*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
};