VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTLdrFlt.cpp@ 94130

Last change on this file since 94130 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.3 KB
Line 
1/* $Id: RTLdrFlt.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * IPRT - Utility for translating addresses into symbols+offset.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/mem.h>
32#include <iprt/assert.h>
33#include <iprt/ctype.h>
34#include <iprt/dbg.h>
35#include <iprt/err.h>
36#include <iprt/getopt.h>
37#include <iprt/initterm.h>
38#include <iprt/message.h>
39#include <iprt/path.h>
40#include <iprt/stream.h>
41#include <iprt/string.h>
42
43
44
45/** Worker for ProduceKAllSyms. */
46static void PrintSymbolForKAllSyms(const char *pszModule, PCRTDBGSYMBOL pSymInfo, PCRTDBGSEGMENT pSegInfo,
47 RTUINTPTR uBaseAddr, bool fOneSeg)
48{
49 RTUINTPTR uAddr;
50 char chType = 't';
51 if (pSymInfo->iSeg < RTDBGSEGIDX_SPECIAL_FIRST)
52 {
53 uAddr = uBaseAddr + pSymInfo->offSeg;
54 if (!fOneSeg)
55 uAddr += pSegInfo->uRva;
56 if (pSegInfo->szName[0])
57 {
58 if (strstr(pSegInfo->szName, "rodata") != NULL)
59 chType = 'r';
60 else if (strstr(pSegInfo->szName, "bss") != NULL)
61 chType = 'b';
62 else if (strstr(pSegInfo->szName, "data") != NULL)
63 chType = 'd';
64 }
65 }
66 else if (pSymInfo->iSeg == RTDBGSEGIDX_ABS)
67 {
68 chType = 'a';
69 uAddr = pSymInfo->offSeg;
70 }
71 else if (pSymInfo->iSeg == RTDBGSEGIDX_RVA)
72 {
73 Assert(!fOneSeg);
74 uAddr = uBaseAddr + pSymInfo->offSeg;
75 }
76 else
77 {
78 RTMsgError("Unsupported special segment %#x for %s in %s!", pSymInfo->iSeg, pSymInfo->szName, pszModule);
79 return;
80 }
81
82 RTPrintf("%RTptr %c %s\t[%s]\n", uAddr, chType, pSymInfo->szName, pszModule);
83}
84
85
86/**
87 * Produces a /proc/kallsyms compatible symbol listing of @a hDbgAs on standard
88 * output.
89 *
90 * @returns Exit code.
91 * @param hDbgAs The address space to dump.
92 */
93static RTEXITCODE ProduceKAllSyms(RTDBGAS hDbgAs)
94{
95 /*
96 * Iterate modules.
97 */
98 uint32_t cModules = RTDbgAsModuleCount(hDbgAs);
99 for (uint32_t iModule = 0; iModule < cModules; iModule++)
100 {
101 RTDBGMOD const hDbgMod = RTDbgAsModuleByIndex(hDbgAs, iModule);
102 const char * const pszModule = RTDbgModName(hDbgMod);
103
104 /*
105 * Iterate mappings of the module.
106 */
107 RTDBGASMAPINFO aMappings[128];
108 uint32_t cMappings = RT_ELEMENTS(aMappings);
109 int rc = RTDbgAsModuleQueryMapByIndex(hDbgAs, iModule, &aMappings[0], &cMappings, 0 /*fFlags*/);
110 if (RT_SUCCESS(rc))
111 {
112 for (uint32_t iMapping = 0; iMapping < cMappings; iMapping++)
113 {
114 RTDBGSEGMENT SegInfo = {0};
115 if (aMappings[iMapping].iSeg == NIL_RTDBGSEGIDX)
116 {
117 /*
118 * Flat mapping of the entire module.
119 */
120 SegInfo.iSeg = NIL_RTDBGSEGIDX;
121 uint32_t cSymbols = RTDbgModSymbolCount(hDbgMod);
122 for (uint32_t iSymbol = 0; iSymbol < cSymbols; iSymbol++)
123 {
124 RTDBGSYMBOL SymInfo;
125 rc = RTDbgModSymbolByOrdinal(hDbgMod, iSymbol, &SymInfo);
126 if (RT_SUCCESS(rc))
127 {
128 if ( SymInfo.iSeg != SegInfo.iSeg
129 && SymInfo.iSeg < RTDBGSEGIDX_SPECIAL_FIRST)
130 {
131 rc = RTDbgModSegmentByIndex(hDbgMod, SymInfo.iSeg, &SegInfo);
132 if (RT_FAILURE(rc))
133 {
134 RTMsgError("RTDbgModSegmentByIndex(%s, %u) failed: %Rrc", pszModule, SymInfo.iSeg, rc);
135 continue;
136 }
137 }
138 PrintSymbolForKAllSyms(pszModule, &SymInfo, &SegInfo, aMappings[iMapping].Address, false);
139 }
140 else
141 RTMsgError("RTDbgModSymbolByOrdinal(%s, %u) failed: %Rrc", pszModule, iSymbol, rc);
142 }
143 }
144 else
145 {
146 /*
147 * Just one segment.
148 */
149 rc = RTDbgModSegmentByIndex(hDbgMod, aMappings[iMapping].iSeg, &SegInfo);
150 if (RT_SUCCESS(rc))
151 {
152 /** @todo */
153 }
154 else
155 RTMsgError("RTDbgModSegmentByIndex(%s, %u) failed: %Rrc", pszModule, aMappings[iMapping].iSeg, rc);
156 }
157 }
158 }
159 else
160 RTMsgError("RTDbgAsModuleQueryMapByIndex failed: %Rrc", rc);
161 RTDbgModRelease(hDbgMod);
162 }
163
164 return RTEXITCODE_SUCCESS;
165}
166
167
168/**
169 * Dumps the address space.
170 */
171static void DumpAddressSpace(RTDBGAS hDbgAs, unsigned cVerbosityLevel)
172{
173 RTPrintf("*** Address Space Dump ***\n");
174 uint32_t cModules = RTDbgAsModuleCount(hDbgAs);
175 for (uint32_t iModule = 0; iModule < cModules; iModule++)
176 {
177 RTDBGMOD hDbgMod = RTDbgAsModuleByIndex(hDbgAs, iModule);
178 RTPrintf("Module #%u: %s\n", iModule, RTDbgModName(hDbgMod));
179
180 RTDBGASMAPINFO aMappings[128];
181 uint32_t cMappings = RT_ELEMENTS(aMappings);
182 int rc = RTDbgAsModuleQueryMapByIndex(hDbgAs, iModule, &aMappings[0], &cMappings, 0 /*fFlags*/);
183 if (RT_SUCCESS(rc))
184 {
185 for (uint32_t iMapping = 0; iMapping < cMappings; iMapping++)
186 {
187 if (aMappings[iMapping].iSeg == NIL_RTDBGSEGIDX)
188 {
189 RTPrintf(" mapping #%u: %RTptr-%RTptr\n",
190 iMapping,
191 aMappings[iMapping].Address,
192 aMappings[iMapping].Address + RTDbgModImageSize(hDbgMod) - 1);
193 if (cVerbosityLevel > 2)
194 {
195 uint32_t cSegments = RTDbgModSegmentCount(hDbgMod);
196 for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
197 {
198 RTDBGSEGMENT SegInfo;
199 rc = RTDbgModSegmentByIndex(hDbgMod, iSeg, &SegInfo);
200 if (RT_SUCCESS(rc))
201 RTPrintf(" seg #%u: %RTptr LB %RTptr '%s'\n",
202 iSeg, SegInfo.uRva, SegInfo.cb, SegInfo.szName);
203 else
204 RTPrintf(" seg #%u: %Rrc\n", iSeg, rc);
205 }
206 }
207 }
208 else
209 {
210 RTDBGSEGMENT SegInfo;
211 rc = RTDbgModSegmentByIndex(hDbgMod, aMappings[iMapping].iSeg, &SegInfo);
212 if (RT_SUCCESS(rc))
213 RTPrintf(" mapping #%u: %RTptr-%RTptr (segment #%u - '%s')\n",
214 iMapping,
215 aMappings[iMapping].Address,
216 aMappings[iMapping].Address + SegInfo.cb,
217 SegInfo.iSeg, SegInfo.szName);
218 else
219 RTPrintf(" mapping #%u: %RTptr-???????? (segment #%u) rc=%Rrc\n",
220 iMapping, aMappings[iMapping].Address, aMappings[iMapping].iSeg, rc);
221 }
222
223 if (cVerbosityLevel > 1)
224 {
225 uint32_t cSymbols = RTDbgModSymbolCount(hDbgMod);
226 RTPrintf(" %u symbols\n", cSymbols);
227 for (uint32_t iSymbol = 0; iSymbol < cSymbols; iSymbol++)
228 {
229 RTDBGSYMBOL SymInfo;
230 rc = RTDbgModSymbolByOrdinal(hDbgMod, iSymbol, &SymInfo);
231 if (RT_SUCCESS(rc))
232 RTPrintf(" #%04u at %08x:%RTptr (%RTptr) %05llx %s\n",
233 SymInfo.iOrdinal, SymInfo.iSeg, SymInfo.offSeg, SymInfo.Value,
234 (uint64_t)SymInfo.cb, SymInfo.szName);
235 }
236 }
237 }
238 }
239 else
240 RTMsgError("RTDbgAsModuleQueryMapByIndex failed: %Rrc", rc);
241 RTDbgModRelease(hDbgMod);
242 }
243 RTPrintf("*** End of Address Space Dump ***\n");
244}
245
246
247/**
248 * Tries to parse out an address at the head of the string.
249 *
250 * @returns true if found address, false if not.
251 * @param psz Where to start parsing.
252 * @param pcchAddress Where to store the address length.
253 * @param pu64Address Where to store the address value.
254 */
255static bool TryParseAddress(const char *psz, size_t *pcchAddress, uint64_t *pu64Address)
256{
257 const char *pszStart = psz;
258
259 /*
260 * Hex prefix?
261 */
262 if (psz[0] == '0' && (psz[1] == 'x' || psz[1] == 'X'))
263 psz += 2;
264
265 /*
266 * How many hex digits? We want at least 4 and at most 16.
267 */
268 size_t off = 0;
269 while (RT_C_IS_XDIGIT(psz[off]))
270 off++;
271 if (off < 4 || off > 16)
272 return false;
273
274 /*
275 * Check for separator (xxxxxxxx'yyyyyyyy).
276 */
277 bool fHave64bitSep = off <= 8
278 && psz[off] == '\''
279 && RT_C_IS_XDIGIT(psz[off + 1])
280 && RT_C_IS_XDIGIT(psz[off + 2])
281 && RT_C_IS_XDIGIT(psz[off + 3])
282 && RT_C_IS_XDIGIT(psz[off + 4])
283 && RT_C_IS_XDIGIT(psz[off + 5])
284 && RT_C_IS_XDIGIT(psz[off + 6])
285 && RT_C_IS_XDIGIT(psz[off + 7])
286 && RT_C_IS_XDIGIT(psz[off + 8])
287 && !RT_C_IS_XDIGIT(psz[off + 9]);
288 if (fHave64bitSep)
289 {
290 uint32_t u32High;
291 int rc = RTStrToUInt32Ex(psz, NULL, 16, &u32High);
292 if (rc != VWRN_TRAILING_CHARS)
293 return false;
294
295 uint32_t u32Low;
296 rc = RTStrToUInt32Ex(&psz[off + 1], NULL, 16, &u32Low);
297 if ( rc != VINF_SUCCESS
298 && rc != VWRN_TRAILING_SPACES
299 && rc != VWRN_TRAILING_CHARS)
300 return false;
301
302 *pu64Address = RT_MAKE_U64(u32Low, u32High);
303 off += 1 + 8;
304 }
305 else
306 {
307 int rc = RTStrToUInt64Ex(psz, NULL, 16, pu64Address);
308 if ( rc != VINF_SUCCESS
309 && rc != VWRN_TRAILING_SPACES
310 && rc != VWRN_TRAILING_CHARS)
311 return false;
312 }
313
314 *pcchAddress = psz + off - pszStart;
315 return true;
316}
317
318
319int main(int argc, char **argv)
320{
321 int rc = RTR3InitExe(argc, &argv, 0);
322 if (RT_FAILURE(rc))
323 return RTMsgInitFailure(rc);
324
325 /*
326 * Create an empty address space that we can load modules and stuff into
327 * as we parse the parameters.
328 */
329 RTDBGAS hDbgAs;
330 rc = RTDbgAsCreate(&hDbgAs, 0, RTUINTPTR_MAX, "");
331 if (RT_FAILURE(rc))
332 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDBgAsCreate -> %Rrc", rc);
333
334 /*
335 * Create a debugging configuration instance to work with so that we can
336 * make use of (i.e. test) path searching and such.
337 */
338 RTDBGCFG hDbgCfg;
339 rc = RTDbgCfgCreate(&hDbgCfg, "IPRT", true /*fNativePaths*/);
340 if (RT_FAILURE(rc))
341 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgCfgCreate -> %Rrc", rc);
342
343 /*
344 * Parse arguments.
345 */
346 static const RTGETOPTDEF s_aOptions[] =
347 {
348 { "--input", 'i', RTGETOPT_REQ_STRING },
349 { "--local-file", 'l', RTGETOPT_REQ_NOTHING },
350 { "--cache-file", 'c', RTGETOPT_REQ_NOTHING },
351 { "--pe-image", 'p', RTGETOPT_REQ_NOTHING },
352 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
353 { "--x86", '8', RTGETOPT_REQ_NOTHING },
354 { "--amd64", '6', RTGETOPT_REQ_NOTHING },
355 { "--whatever", '*', RTGETOPT_REQ_NOTHING },
356 { "--kallsyms", 'k', RTGETOPT_REQ_NOTHING },
357 };
358
359 PRTSTREAM pInput = g_pStdIn;
360 PRTSTREAM pOutput = g_pStdOut;
361 unsigned cVerbosityLevel = 0;
362 enum {
363 kOpenMethod_FromImage,
364 kOpenMethod_FromPeImage
365 } enmOpenMethod = kOpenMethod_FromImage;
366 bool fCacheFile = false;
367 RTLDRARCH enmArch = RTLDRARCH_WHATEVER;
368 bool fKAllSyms = false;
369
370 RTGETOPTUNION ValueUnion;
371 RTGETOPTSTATE GetState;
372 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
373 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
374 {
375 switch (rc)
376 {
377 case 'i':
378 rc = RTStrmOpen(ValueUnion.psz, "r", &pInput);
379 if (RT_FAILURE(rc))
380 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open '%s' for reading: %Rrc", ValueUnion.psz, rc);
381 break;
382
383 case 'c':
384 fCacheFile = true;
385 break;
386
387 case 'k':
388 fKAllSyms = true;
389 break;
390
391 case 'l':
392 fCacheFile = false;
393 break;
394
395 case 'p':
396 enmOpenMethod = kOpenMethod_FromPeImage;
397 break;
398
399 case 'v':
400 cVerbosityLevel++;
401 break;
402
403 case '8':
404 enmArch = RTLDRARCH_X86_32;
405 break;
406
407 case '6':
408 enmArch = RTLDRARCH_AMD64;
409 break;
410
411 case '*':
412 enmArch = RTLDRARCH_WHATEVER;
413 break;
414
415 case 'h':
416 RTPrintf("Usage: %s [options] <module> <address> [<module> <address> [..]]\n"
417 "\n"
418 "Options:\n"
419 " -i,--input=file\n"
420 " Specify a input file instead of standard input.\n"
421 " --pe-image\n"
422 " Use RTDbgModCreateFromPeImage to open the file."
423 " -v, --verbose\n"
424 " Display the address space before doing the filtering.\n"
425 " --amd64,--x86,--whatever\n"
426 " Selects the desired architecture.\n"
427 " -k,--kallsyms\n"
428 " Produce a /proc/kallsyms compatible symbol listing and quit.\n"
429 " -h, -?, --help\n"
430 " Display this help text and exit successfully.\n"
431 " -V, --version\n"
432 " Display the revision and exit successfully.\n"
433 , RTPathFilename(argv[0]));
434 return RTEXITCODE_SUCCESS;
435
436 case 'V':
437 RTPrintf("$Revision: 93115 $\n");
438 return RTEXITCODE_SUCCESS;
439
440 case VINF_GETOPT_NOT_OPTION:
441 {
442 /* <module> <address> */
443 const char *pszModule = ValueUnion.psz;
444
445 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX);
446 if (RT_FAILURE(rc))
447 return RTGetOptPrintError(rc, &ValueUnion);
448 uint64_t u64Address = ValueUnion.u64;
449
450 uint32_t cbImage = 0;
451 uint32_t uTimestamp = 0;
452 if (fCacheFile)
453 {
454 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX);
455 if (RT_FAILURE(rc))
456 return RTGetOptPrintError(rc, &ValueUnion);
457 cbImage = ValueUnion.u32;
458
459 rc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX);
460 if (RT_FAILURE(rc))
461 return RTGetOptPrintError(rc, &ValueUnion);
462 uTimestamp = ValueUnion.u32;
463 }
464
465 RTDBGMOD hMod;
466 if (enmOpenMethod == kOpenMethod_FromImage)
467 rc = RTDbgModCreateFromImage(&hMod, pszModule, NULL, enmArch, hDbgCfg);
468 else
469 rc = RTDbgModCreateFromPeImage(&hMod, pszModule, NULL, NULL, cbImage, uTimestamp, hDbgCfg);
470 if (RT_FAILURE(rc))
471 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgModCreateFromImage(,%s,,) -> %Rrc", pszModule, rc);
472
473 rc = RTDbgAsModuleLink(hDbgAs, hMod, u64Address, 0 /* fFlags */);
474 if (RT_FAILURE(rc))
475 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgAsModuleLink(,%s,%llx,) -> %Rrc", pszModule, u64Address, rc);
476 break;
477 }
478
479 default:
480 return RTGetOptPrintError(rc, &ValueUnion);
481 }
482 }
483
484 /*
485 * Display the address space.
486 */
487 if (cVerbosityLevel)
488 DumpAddressSpace(hDbgAs, cVerbosityLevel);
489
490 /*
491 * Produce the /proc/kallsyms output.
492 */
493 if (fKAllSyms)
494 return ProduceKAllSyms(hDbgAs);
495
496 /*
497 * Read text from standard input and see if there is anything we can translate.
498 */
499 for (;;)
500 {
501 /* Get a line. */
502 char szLine[_64K];
503 rc = RTStrmGetLine(pInput, szLine, sizeof(szLine));
504 if (rc == VERR_EOF)
505 break;
506 if (RT_FAILURE(rc))
507 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTStrmGetLine() -> %Rrc\n", rc);
508
509 /*
510 * Search the line for potential addresses and replace them with
511 * symbols+offset.
512 */
513 const char *pszStart = szLine;
514 const char *psz = szLine;
515 char ch;
516 while ((ch = *psz) != '\0')
517 {
518 size_t cchAddress;
519 uint64_t u64Address;
520
521 if ( ( ch == '0'
522 && (psz[1] == 'x' || psz[1] == 'X')
523 && TryParseAddress(psz, &cchAddress, &u64Address))
524 || ( RT_C_IS_XDIGIT(ch)
525 && TryParseAddress(psz, &cchAddress, &u64Address))
526 )
527 {
528 /* Print. */
529 psz += cchAddress;
530 if (pszStart != psz)
531 RTStrmWrite(pOutput, pszStart, psz - pszStart);
532 pszStart = psz;
533
534 /* Try get the module. */
535 RTUINTPTR uAddr;
536 RTDBGSEGIDX iSeg;
537 RTDBGMOD hDbgMod;
538 rc = RTDbgAsModuleByAddr(hDbgAs, u64Address, &hDbgMod, &uAddr, &iSeg);
539 if (RT_SUCCESS(rc))
540 {
541 if (iSeg != UINT32_MAX)
542 RTStrmPrintf(pOutput, "=[%s:%u", RTDbgModName(hDbgMod), iSeg);
543 else
544 RTStrmPrintf(pOutput, "=[%s", RTDbgModName(hDbgMod));
545
546 /*
547 * Do we have symbols?
548 */
549 RTDBGSYMBOL Symbol;
550 RTINTPTR offSym;
551 rc = RTDbgAsSymbolByAddr(hDbgAs, u64Address, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL, &offSym, &Symbol, NULL);
552 if (RT_SUCCESS(rc))
553 {
554 if (!offSym)
555 RTStrmPrintf(pOutput, "!%s", Symbol.szName);
556 else if (offSym > 0)
557 RTStrmPrintf(pOutput, "!%s+%#llx", Symbol.szName, offSym);
558 else
559 RTStrmPrintf(pOutput, "!%s-%#llx", Symbol.szName, -offSym);
560 }
561 else
562 RTStrmPrintf(pOutput, "+%#llx", u64Address - uAddr);
563
564 /*
565 * Do we have line numbers?
566 */
567 RTDBGLINE Line;
568 RTINTPTR offLine;
569 rc = RTDbgAsLineByAddr(hDbgAs, u64Address, &offLine, &Line, NULL);
570 if (RT_SUCCESS(rc))
571 RTStrmPrintf(pOutput, " %Rbn(%u)", Line.szFilename, Line.uLineNo);
572
573 RTStrmPrintf(pOutput, "]");
574 RTDbgModRelease(hDbgMod);
575 }
576 }
577 else
578 psz++;
579 }
580
581 if (pszStart != psz)
582 RTStrmWrite(pOutput, pszStart, psz - pszStart);
583 RTStrmPutCh(pOutput, '\n');
584 }
585
586 return RTEXITCODE_SUCCESS;
587}
588
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette