RTLdrFlt.cpp revision 682342827b0e80c493c820603508e79e76c42658
/* $Id$ */
/** @file
* IPRT - Utility for translating addresses into symbols+offset.
*/
/*
* Copyright (C) 2006-2012 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 <iprt/mem.h>
#include <iprt/assert.h>
#include <iprt/ctype.h>
#include <iprt/dbg.h>
#include <iprt/err.h>
#include <iprt/getopt.h>
#include <iprt/initterm.h>
#include <iprt/message.h>
#include <iprt/path.h>
#include <iprt/stream.h>
#include <iprt/string.h>
/**
* Tries to parse out an address at the head of the string.
*
* @returns true if found address, false if not.
* @param psz Where to start parsing.
* @param pcchAddress Where to store the address length.
* @param pu64Address Where to store the address value.
*/
static bool TryParseAddress(const char *psz, size_t *pcchAddress, uint64_t *pu64Address)
{
const char *pszStart = psz;
/*
* Hex prefix?
*/
if (psz[0] == '0' && (psz[1] == 'x' || psz[1] == 'X'))
psz += 2;
/*
* How many hex digits? We want at least 4 and at most 16.
*/
size_t off = 0;
while (RT_C_IS_XDIGIT(psz[off]))
off++;
if (off < 4 || off > 16)
return false;
/*
* Check for separator (xxxxxxxx'yyyyyyyy).
*/
bool fHave64bitSep = off <= 8
&& psz[off] == '\''
&& RT_C_IS_XDIGIT(psz[off + 1])
&& RT_C_IS_XDIGIT(psz[off + 2])
&& RT_C_IS_XDIGIT(psz[off + 3])
&& RT_C_IS_XDIGIT(psz[off + 4])
&& RT_C_IS_XDIGIT(psz[off + 5])
&& RT_C_IS_XDIGIT(psz[off + 6])
&& RT_C_IS_XDIGIT(psz[off + 7])
&& RT_C_IS_XDIGIT(psz[off + 8])
&& !RT_C_IS_XDIGIT(psz[off + 9]);
if (fHave64bitSep)
{
uint32_t u32High;
int rc = RTStrToUInt32Ex(psz, NULL, 16, &u32High);
if (rc != VWRN_TRAILING_CHARS)
return false;
uint32_t u32Low;
rc = RTStrToUInt32Ex(&psz[off + 1], NULL, 16, &u32Low);
if ( rc != VINF_SUCCESS
&& rc != VWRN_TRAILING_SPACES
&& rc != VWRN_TRAILING_CHARS)
return false;
*pu64Address = RT_MAKE_U64(u32Low, u32High);
off += 1 + 8;
}
else
{
int rc = RTStrToUInt64Ex(psz, NULL, 16, pu64Address);
if ( rc != VINF_SUCCESS
&& rc != VWRN_TRAILING_SPACES
&& rc != VWRN_TRAILING_CHARS)
return false;
}
*pcchAddress = psz + off - pszStart;
return true;
}
int main(int argc, char **argv)
{
int rc = RTR3InitExe(argc, &argv, 0);
if (RT_FAILURE(rc))
return RTMsgInitFailure(rc);
/*
* Create an empty address space that we can load modules and stuff into
* as we parse the parameters.
*/
RTDBGAS hDbgAs;
rc = RTDbgAsCreate(&hDbgAs, 0, RTUINTPTR_MAX, "");
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDBgAsCreate -> %Rrc", rc);
/*
* Create a debugging configuration instance to work with so that we can
* make use of (i.e. test) path searching and such.
*/
RTDBGCFG hDbgCfg;
rc = RTDbgCfgCreate(&hDbgCfg, "IPRT", true /*fNativePaths*/);
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgCfgCreate -> %Rrc", rc);
/*
* Parse arguments.
*/
static const RTGETOPTDEF s_aOptions[] =
{
{ "--input", 'i', RTGETOPT_REQ_STRING },
{ "--local-file", 'l', RTGETOPT_REQ_NOTHING },
{ "--cache-file", 'c', RTGETOPT_REQ_NOTHING },
{ "--pe-image", 'p', RTGETOPT_REQ_NOTHING },
{ "--verbose", 'v', RTGETOPT_REQ_NOTHING },
};
PRTSTREAM pInput = g_pStdIn;
PRTSTREAM pOutput = g_pStdOut;
unsigned cVerbosityLevel = 0;
enum {
kOpenMethod_FromImage,
kOpenMethod_FromPeImage
} enmOpenMethod = kOpenMethod_FromImage;
bool fCacheFile = false;
RTGETOPTUNION ValueUnion;
RTGETOPTSTATE GetState;
RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
while ((rc = RTGetOpt(&GetState, &ValueUnion)))
{
switch (rc)
{
case 'i':
rc = RTStrmOpen(ValueUnion.psz, "r", &pInput);
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open '%s' for reading: %Rrc", ValueUnion.psz, rc);
break;
case 'c':
fCacheFile = true;
break;
case 'l':
fCacheFile = false;
break;
case 'p':
enmOpenMethod = kOpenMethod_FromPeImage;
break;
case 'v':
cVerbosityLevel++;
break;
case 'h':
RTPrintf("Usage: %s [options] <module> <address> [<module> <address> [..]]\n"
"\n"
"Options:\n"
" -i,--input=file\n"
" Specify a input file instead of standard input.\n"
" --pe-image\n"
" Use RTDbgModCreateFromPeImage to open the file."
" -v, --verbose\n"
" Display the address space before doing the filtering.\n"
" -h, -?, --help\n"
" Display this help text and exit successfully.\n"
" -V, --version\n"
" Display the revision and exit successfully.\n"
, RTPathFilename(argv[0]));
return RTEXITCODE_SUCCESS;
case 'V':
RTPrintf("$Revision$\n");
return RTEXITCODE_SUCCESS;
case VINF_GETOPT_NOT_OPTION:
{
/* <module> <address> */
const char *pszModule = ValueUnion.psz;
rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX);
if (RT_FAILURE(rc))
return RTGetOptPrintError(rc, &ValueUnion);
uint64_t u64Address = ValueUnion.u64;
uint32_t cbImage = 0;
uint32_t uTimestamp = 0;
if (fCacheFile)
{
rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX);
if (RT_FAILURE(rc))
return RTGetOptPrintError(rc, &ValueUnion);
cbImage = ValueUnion.u32;
rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX);
if (RT_FAILURE(rc))
return RTGetOptPrintError(rc, &ValueUnion);
uTimestamp = ValueUnion.u32;
}
RTDBGMOD hMod;
if (enmOpenMethod == kOpenMethod_FromImage)
rc = RTDbgModCreateFromImage(&hMod, pszModule, NULL, RTLDRARCH_WHATEVER, hDbgCfg);
else
rc = RTDbgModCreateFromPeImage(&hMod, pszModule, NULL, NIL_RTLDRMOD, cbImage, uTimestamp, hDbgCfg);
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgModCreateFromImage(,%s,,) -> %Rrc", pszModule, rc);
rc = RTDbgAsModuleLink(hDbgAs, hMod, u64Address, 0 /* fFlags */);
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgAsModuleLink(,%s,%llx,) -> %Rrc", pszModule, u64Address, rc);
break;
}
default:
return RTGetOptPrintError(rc, &ValueUnion);
}
}
/*
* Display the address space.
*/
if (cVerbosityLevel)
{
RTPrintf("*** Address Space Dump ***\n");
uint32_t cModules = RTDbgAsModuleCount(hDbgAs);
for (uint32_t iModule = 0; iModule < cModules; iModule++)
{
RTDBGMOD hDbgMod = RTDbgAsModuleByIndex(hDbgAs, iModule);
RTPrintf("Module #%u: %s\n", iModule, RTDbgModName(hDbgMod));
RTDBGASMAPINFO aMappings[128];
uint32_t cMappings = RT_ELEMENTS(aMappings);
rc = RTDbgAsModuleQueryMapByIndex(hDbgAs, iModule, &aMappings[0], &cMappings, 0 /*fFlags*/);
if (RT_SUCCESS(rc))
{
for (uint32_t iMapping = 0; iMapping < cMappings; iMapping++)
{
if (aMappings[iMapping].iSeg == NIL_RTDBGSEGIDX)
RTPrintf(" mapping #%u: %RTptr-%RTptr\n",
iMapping,
aMappings[iMapping].Address,
aMappings[iMapping].Address + RTDbgModImageSize(hDbgMod) - 1);
else
{
RTDBGSEGMENT SegInfo;
rc = RTDbgModSegmentByIndex(hDbgMod, aMappings[iMapping].iSeg, &SegInfo);
if (RT_SUCCESS(rc))
RTPrintf(" mapping #%u: %RTptr-%RTptr (segment #%u - '%s')",
iMapping,
aMappings[iMapping].Address,
aMappings[iMapping].Address + SegInfo.cb,
SegInfo.iSeg, SegInfo.szName);
else
RTPrintf(" mapping #%u: %RTptr-???????? (segment #%u)", iMapping, aMappings[iMapping].Address);
}
if (cVerbosityLevel > 1)
{
uint32_t cSymbols = RTDbgModSymbolCount(hDbgMod);
RTPrintf(" %u symbols\n", cSymbols);
for (uint32_t iSymbol = 0; iSymbol < cSymbols; iSymbol++)
{
RTDBGSYMBOL SymInfo;
rc = RTDbgModSymbolByOrdinal(hDbgMod, iSymbol, &SymInfo);
if (RT_SUCCESS(rc))
RTPrintf(" #%04u at %08x:%RTptr %05llx %s\n",
SymInfo.iOrdinal, SymInfo.iSeg, SymInfo.offSeg,
(uint64_t)SymInfo.cb, SymInfo.szName);
}
}
}
}
else
RTMsgError("RTDbgAsModuleQueryMapByIndex failed: %Rrc", rc);
RTDbgModRelease(hDbgMod);
}
RTPrintf("*** End of Address Space Dump ***\n");
}
/*
* Read text from standard input and see if there is anything we can translate.
*/
for (;;)
{
/* Get a line. */
char szLine[_64K];
rc = RTStrmGetLine(pInput, szLine, sizeof(szLine));
if (rc == VERR_EOF)
break;
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTStrmGetLine() -> %Rrc\n", rc);
/*
* Search the line for potential addresses and replace them with
* symbols+offset.
*/
const char *pszStart = szLine;
const char *psz = szLine;
char ch;
while ((ch = *psz) != '\0')
{
size_t cchAddress;
uint64_t u64Address;
if ( ( ch == '0'
&& (psz[1] == 'x' || psz[1] == 'X')
&& TryParseAddress(psz, &cchAddress, &u64Address))
|| ( RT_C_IS_XDIGIT(ch)
&& TryParseAddress(psz, &cchAddress, &u64Address))
)
{
/* Print. */
psz += cchAddress;
if (pszStart != psz)
RTStrmWrite(pOutput, pszStart, psz - pszStart);
pszStart = psz;
/* Try get the module. */
RTUINTPTR uAddr;
RTDBGSEGIDX iSeg;
RTDBGMOD hDbgMod;
rc = RTDbgAsModuleByAddr(hDbgAs, u64Address, &hDbgMod, &uAddr, &iSeg);
if (RT_SUCCESS(rc))
{
if (iSeg != UINT32_MAX)
RTStrmPrintf(pOutput, "=[%s:%u", RTDbgModName(hDbgMod), iSeg);
else
RTStrmPrintf(pOutput, "=[%s", RTDbgModName(hDbgMod), iSeg);
/*
* Do we have symbols?
*/
RTDBGSYMBOL Symbol;
RTINTPTR offSym;
rc = RTDbgAsSymbolByAddr(hDbgAs, u64Address, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL, &offSym, &Symbol, NULL);
if (RT_SUCCESS(rc))
{
if (!offSym)
RTStrmPrintf(pOutput, "!%s", Symbol.szName);
else if (offSym > 0)
RTStrmPrintf(pOutput, "!%s+%#llx", Symbol.szName, offSym);
else
RTStrmPrintf(pOutput, "!%s-%#llx", Symbol.szName, -offSym);
}
else
RTStrmPrintf(pOutput, "+%#llx", u64Address - uAddr);
/*
* Do we have line numbers?
*/
RTDBGLINE Line;
RTINTPTR offLine;
rc = RTDbgAsLineByAddr(hDbgAs, u64Address, &offLine, &Line, NULL);
if (RT_SUCCESS(rc))
RTStrmPrintf(pOutput, " %Rbn(%u)", Line.szFilename, Line.uLineNo);
RTStrmPrintf(pOutput, "]");
RTDbgModRelease(hDbgMod);
}
}
else
psz++;
}
if (pszStart != psz)
RTStrmWrite(pOutput, pszStart, psz - pszStart);
RTStrmPutCh(pOutput, '\n');
}
return RTEXITCODE_SUCCESS;
}