VirtualBox

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

Last change on this file since 106877 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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