VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/dbg/dbgmodghidra.cpp@ 92799

Last change on this file since 92799 was 89927, checked in by vboxsync, 3 years ago

Runtime/dbg/dbgmodghidra.cpp: Parse function tags if existing and add source file and line number information.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 KB
Line 
1/* $Id: dbgmodghidra.cpp 89927 2021-06-28 10:24:55Z vboxsync $ */
2/** @file
3 * IPRT - Debug Info Reader for Ghidra XML files created with createPdbXmlFiles.bat/pdb.exe.
4 */
5
6/*
7 * Copyright (C) 2021 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/dbg.h>
32#include "internal/iprt.h"
33
34#include <iprt/err.h>
35#include <iprt/ctype.h>
36#include <iprt/mem.h>
37#include <iprt/sort.h>
38#include <iprt/stream.h>
39#include <iprt/string.h>
40#include <iprt/cpp/xml.h>
41#include "internal/dbgmod.h"
42
43
44/*********************************************************************************************************************************
45* Structures and Typedefs *
46*********************************************************************************************************************************/
47/**
48 * Temporary segment data.
49 */
50typedef struct RTDBGMODGHIDRASEG
51{
52 const char *pszNumber;
53 RTUINTPTR uRva;
54} RTDBGMODGHIDRASEG;
55typedef RTDBGMODGHIDRASEG *PRTDBGMODGHIDRASEG;
56typedef const RTDBGMODGHIDRASEG *PCRTDBGMODGHIDRASEG;
57
58
59/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
60static DECLCALLBACK(int) rtDbgModGhidra_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
61{
62 RT_NOREF(pMod, iSeg, off, pState);
63 return VERR_DBG_NO_UNWIND_INFO;
64}
65
66
67/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
68static DECLCALLBACK(int) rtDbgModGhidra_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
69 PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
70{
71 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
72 return RTDbgModLineByAddr(hCnt, iSeg, off, poffDisp, pLineInfo);
73}
74
75
76/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
77static DECLCALLBACK(int) rtDbgModGhidra_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
78{
79 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
80 return RTDbgModLineByOrdinal(hCnt, iOrdinal, pLineInfo);
81}
82
83
84/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
85static DECLCALLBACK(uint32_t) rtDbgModGhidra_LineCount(PRTDBGMODINT pMod)
86{
87 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
88 return RTDbgModLineCount(hCnt);
89}
90
91
92/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
93static DECLCALLBACK(int) rtDbgModGhidra_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
94 uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
95{
96 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
97 Assert(!pszFile[cchFile]); NOREF(cchFile);
98 return RTDbgModLineAdd(hCnt, pszFile, uLineNo, iSeg, off, piOrdinal);
99}
100
101
102/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
103static DECLCALLBACK(int) rtDbgModGhidra_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
104 PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
105{
106 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
107 return RTDbgModSymbolByAddr(hCnt, iSeg, off, fFlags, poffDisp, pSymInfo);
108}
109
110
111/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
112static DECLCALLBACK(int) rtDbgModGhidra_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
113 PRTDBGSYMBOL pSymInfo)
114{
115 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
116 Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
117 return RTDbgModSymbolByName(hCnt, pszSymbol, pSymInfo);
118}
119
120
121/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
122static DECLCALLBACK(int) rtDbgModGhidra_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
123{
124 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
125 return RTDbgModSymbolByOrdinal(hCnt, iOrdinal, pSymInfo);
126}
127
128
129/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
130static DECLCALLBACK(uint32_t) rtDbgModGhidra_SymbolCount(PRTDBGMODINT pMod)
131{
132 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
133 return RTDbgModSymbolCount(hCnt);
134}
135
136
137/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
138static DECLCALLBACK(int) rtDbgModGhidra_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
139 RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
140 uint32_t *piOrdinal)
141{
142 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
143 Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
144 return RTDbgModSymbolAdd(hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal);
145}
146
147
148/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
149static DECLCALLBACK(int) rtDbgModGhidra_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
150{
151 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
152 return RTDbgModSegmentByIndex(hCnt, iSeg, pSegInfo);
153}
154
155
156/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
157static DECLCALLBACK(RTDBGSEGIDX) rtDbgModGhidra_SegmentCount(PRTDBGMODINT pMod)
158{
159 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
160 return RTDbgModSegmentCount(hCnt);
161}
162
163
164/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
165static DECLCALLBACK(int) rtDbgModGhidra_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName,
166 size_t cchName, uint32_t fFlags, PRTDBGSEGIDX piSeg)
167{
168 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
169 Assert(!pszName[cchName]); NOREF(cchName);
170 return RTDbgModSegmentAdd(hCnt, uRva, cb, pszName, fFlags, piSeg);
171}
172
173
174/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
175static DECLCALLBACK(RTUINTPTR) rtDbgModGhidra_ImageSize(PRTDBGMODINT pMod)
176{
177 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
178 return RTDbgModImageSize(hCnt);
179}
180
181
182/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
183static DECLCALLBACK(RTDBGSEGIDX) rtDbgModGhidra_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
184{
185 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
186 return RTDbgModRvaToSegOff(hCnt, uRva, poffSeg);
187}
188
189
190/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
191static DECLCALLBACK(int) rtDbgModGhidra_Close(PRTDBGMODINT pMod)
192{
193 RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
194 RTDbgModRelease(hCnt);
195 pMod->pvDbgPriv = NULL;
196 return VINF_SUCCESS;
197}
198
199
200/**
201 * Returns the table with the given name from the given table list.
202 *
203 * @returns Pointer to the XML element node containing the given table or NULL if not found.
204 * @param pelmTables Pointer to the node containing the tables.
205 * @param pszName The table name to look for.
206 */
207static const xml::ElementNode *rtDbgModGhidraGetTableByName(const xml::ElementNode *pelmTables, const char *pszName)
208{
209 xml::NodesLoop nl1(*pelmTables, "table");
210 const xml::ElementNode *pelmTbl;
211 while ((pelmTbl = nl1.forAllNodes()))
212 {
213 const char *pszTblName = NULL;
214
215 if ( pelmTbl->getAttributeValue("name", &pszTblName)
216 && !strcmp(pszTblName, pszName))
217 return pelmTbl;
218 }
219
220 return NULL;
221}
222
223
224/**
225 * Adds the symbols from the given \"Symbols\" table.
226 *
227 * @returns IPRT status code.
228 * @param hCnt Debug module container handle.
229 * @param elmTbl Reference to the XML node containing the symbols.
230 */
231static int rtDbgModGhidraXmlParseSymbols(RTDBGMOD hCnt, const xml::ElementNode &elmTbl)
232{
233 xml::NodesLoop nlSym(elmTbl, "symbol");
234 const xml::ElementNode *pelmSym;
235 while ((pelmSym = nlSym.forAllNodes()))
236 {
237 /* Only parse Function and PublicSymbol tags. */
238 const char *pszTag = NULL;
239 if ( pelmSym->getAttributeValue("tag", &pszTag)
240 && ( !strcmp(pszTag, "PublicSymbol")
241 || !strcmp(pszTag, "Function")))
242 {
243 const char *pszSymName = NULL;
244 if ( !pelmSym->getAttributeValue("undecorated", &pszSymName)
245 || *pszSymName == '\0')
246 pelmSym->getAttributeValue("name", &pszSymName);
247
248 if ( pszSymName
249 && strlen(pszSymName) < RTDBG_SYMBOL_NAME_LENGTH)
250 {
251 uint64_t u64Addr = 0;
252 uint64_t u64Length = 0;
253 if ( pelmSym->getAttributeValue("address", &u64Addr)
254 && pelmSym->getAttributeValue("length", &u64Length))
255 {
256 int rc = RTDbgModSymbolAdd(hCnt, pszSymName, RTDBGSEGIDX_RVA, u64Addr, u64Length, 0 /*fFlags*/, NULL);
257 if ( RT_FAILURE(rc)
258 && rc != VERR_DBG_DUPLICATE_SYMBOL
259 && rc != VERR_DBG_ADDRESS_CONFLICT
260 && rc != VERR_DBG_INVALID_RVA) /* (don't be too strict) */
261 return rc;
262 }
263 }
264 }
265 }
266
267 return VINF_SUCCESS;
268}
269
270
271/**
272 * Adds the symbols from the given \"functions\" table.
273 *
274 * @returns IPRT status code.
275 * @param hCnt Debug module container handle.
276 * @param elmTbl Reference to the XML node containing the symbols.
277 */
278static int rtDbgModGhidraXmlParseFunctions(RTDBGMOD hCnt, const xml::ElementNode &elmTbl)
279{
280 xml::NodesLoop nlFun(elmTbl, "function");
281 const xml::ElementNode *pelmFun;
282 while ((pelmFun = nlFun.forAllNodes()))
283 {
284 xml::NodesLoop nlLn(*pelmFun, "line_number");
285 const xml::ElementNode *pelmLn;
286 while ((pelmLn = nlLn.forAllNodes()))
287 {
288 const char *pszFile = NULL;
289 uint32_t uLineNo = 0;
290 uint64_t off = 0;
291 if ( pelmLn->getAttributeValue("source_file", &pszFile)
292 && pelmLn->getAttributeValue("start", &uLineNo)
293 && pelmLn->getAttributeValue("addr", &off))
294 {
295 int rc = RTDbgModLineAdd(hCnt, pszFile, uLineNo, RTDBGSEGIDX_RVA, off, NULL /*piOrdinal*/);
296 if ( RT_FAILURE(rc)
297 && rc != VERR_DBG_DUPLICATE_SYMBOL
298 && rc != VERR_DBG_ADDRESS_CONFLICT
299 && rc != VERR_DBG_INVALID_RVA) /* (don't be too strict) */
300 return rc;
301 }
302 }
303 }
304
305 return VINF_SUCCESS;
306}
307
308
309/**
310 * @copydoc FNRTSORTCMP
311 */
312static DECLCALLBACK(int) rtDbgModGhidraSegmentsSortCmp(void const *pvElement1, void const *pvElement2, void *pvUser)
313{
314 RT_NOREF(pvUser);
315 PCRTDBGMODGHIDRASEG pSeg1 = (PCRTDBGMODGHIDRASEG)pvElement1;
316 PCRTDBGMODGHIDRASEG pSeg2 = (PCRTDBGMODGHIDRASEG)pvElement2;
317
318 if (pSeg1->uRva > pSeg2->uRva)
319 return 1;
320 if (pSeg1->uRva < pSeg2->uRva)
321 return -1;
322
323 return 0;
324}
325
326
327/**
328 * Adds the segments in the given \"SegmentMap\" table.
329 *
330 * @returns IPRT status code.
331 * @param hCnt Debug module container handle.
332 * @param elmTblSeg Reference to the XML node containing the segments.
333 */
334static int rtDbgModGhidraSegmentsAdd(RTDBGMOD hCnt, const xml::ElementNode &elmTblSeg)
335{
336 RTDBGMODGHIDRASEG aSegments[32];
337 uint32_t idxSeg = 0;
338
339 xml::NodesLoop nl1(elmTblSeg, "segment");
340 const xml::ElementNode *pelmSeg;
341 while ( (pelmSeg = nl1.forAllNodes())
342 && idxSeg < RT_ELEMENTS(aSegments))
343 {
344 const char *pszNumber = NULL;
345 RTUINTPTR uRva = 0;
346
347 if ( pelmSeg->getAttributeValue("number", &pszNumber)
348 && pelmSeg->getAttributeValue("address", &uRva))
349 {
350 aSegments[idxSeg].pszNumber = pszNumber;
351 aSegments[idxSeg].uRva = uRva;
352 idxSeg++;
353 }
354 }
355
356 /* Sort the segments by RVA so it is possible to deduce segment sizes. */
357 RTSortShell(&aSegments[0], idxSeg, sizeof(aSegments[0]), rtDbgModGhidraSegmentsSortCmp, NULL);
358
359 for (uint32_t i = 0; i < idxSeg - 1; i++)
360 {
361 int rc = RTDbgModSegmentAdd(hCnt, aSegments[i].uRva, aSegments[i + 1].uRva - aSegments[i].uRva,
362 aSegments[i].pszNumber, 0 /*fFlags*/, NULL);
363 if (RT_FAILURE(rc))
364 return rc;
365 }
366
367 /* Last segment for which we assume a size of 0 right now. */
368 int rc = RTDbgModSegmentAdd(hCnt, aSegments[idxSeg - 1].uRva, 0,
369 aSegments[idxSeg - 1].pszNumber, 0 /*fFlags*/, NULL);
370 if (RT_FAILURE(rc))
371 return rc;
372
373 return rc;
374}
375
376
377/**
378 * Load the symbols from an XML document.
379 *
380 * @returns IPRT status code.
381 * @param hCnt Debug module container handle.
382 * @param a_pDoc Pointer to the XML document.
383 */
384static int rtDbgModGhidraXmlParse(RTDBGMOD hCnt, xml::Document *a_pDoc)
385{
386 /*
387 * Get the root element and check whether it looks like a valid Ghidra XML.
388 */
389 const xml::ElementNode *pelmRoot = a_pDoc->getRootElement();
390 if ( !pelmRoot
391 || strcmp(pelmRoot->getName(), "pdb") != 0)
392 return VERR_DBG_NO_MATCHING_INTERPRETER;
393
394 const xml::ElementNode *pelmTables = pelmRoot->findChildElement("tables");
395 if (!pelmTables)
396 return VERR_DBG_NO_MATCHING_INTERPRETER;
397
398 const xml::ElementNode *pelmTbl = rtDbgModGhidraGetTableByName(pelmTables, "SegmentMap");
399 if (pelmTbl)
400 {
401 int rc = rtDbgModGhidraSegmentsAdd(hCnt, *pelmTbl);
402 if (RT_SUCCESS(rc))
403 {
404 pelmTbl = rtDbgModGhidraGetTableByName(pelmTables, "Symbols");
405 if (pelmTbl)
406 {
407 rc = rtDbgModGhidraXmlParseSymbols(hCnt, *pelmTbl);
408 if (RT_SUCCESS(rc))
409 {
410 pelmTbl = pelmRoot->findChildElement("functions"); /* Might not be there. */
411 if (pelmTbl)
412 rc = rtDbgModGhidraXmlParseFunctions(hCnt, *pelmTbl);
413 return rc;
414 }
415 }
416 }
417 }
418
419 return VERR_DBG_NO_MATCHING_INTERPRETER;
420}
421
422
423/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
424static DECLCALLBACK(int) rtDbgModGhidra_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch)
425{
426 RT_NOREF(enmArch);
427
428 /*
429 * Fend off images.
430 */
431 if (!pMod->pszDbgFile)
432 return VERR_DBG_NO_MATCHING_INTERPRETER;
433 pMod->pvDbgPriv = NULL;
434
435 /*
436 * Try open the file and create an instance.
437 */
438 xml::Document Doc;
439 {
440 xml::XmlFileParser Parser;
441 try
442 {
443 Parser.read(pMod->pszDbgFile, Doc);
444 }
445 catch (xml::XmlError &rErr)
446 {
447 RT_NOREF(rErr);
448 return VERR_DBG_NO_MATCHING_INTERPRETER;
449 }
450 catch (xml::EIPRTFailure &rErr)
451 {
452 return rErr.rc();
453 }
454 }
455
456 RTDBGMOD hCnt;
457 int rc = RTDbgModCreate(&hCnt, pMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/);
458 if (RT_SUCCESS(rc))
459 {
460 /*
461 * Hand the xml doc over to the common code.
462 */
463 try
464 {
465 rc = rtDbgModGhidraXmlParse(hCnt, &Doc);
466 if (RT_SUCCESS(rc))
467 {
468 pMod->pvDbgPriv = hCnt;
469 return VINF_SUCCESS;
470 }
471 }
472 catch (RTCError &rXcpt) // includes all XML exceptions
473 {
474 RT_NOREF(rXcpt);
475 rc = VERR_DBG_NO_MATCHING_INTERPRETER;
476 }
477 RTDbgModRelease(hCnt);
478 }
479
480 return rc;
481}
482
483
484
485/** Virtual function table for the Ghidra XML file reader. */
486DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgGhidra =
487{
488 /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
489 /*.fSupports = */ RT_DBGTYPE_OTHER | RT_DBGTYPE_MAP,
490 /*.pszName = */ "ghidra",
491 /*.pfnTryOpen = */ rtDbgModGhidra_TryOpen,
492 /*.pfnClose = */ rtDbgModGhidra_Close,
493
494 /*.pfnRvaToSegOff = */ rtDbgModGhidra_RvaToSegOff,
495 /*.pfnImageSize = */ rtDbgModGhidra_ImageSize,
496
497 /*.pfnSegmentAdd = */ rtDbgModGhidra_SegmentAdd,
498 /*.pfnSegmentCount = */ rtDbgModGhidra_SegmentCount,
499 /*.pfnSegmentByIndex = */ rtDbgModGhidra_SegmentByIndex,
500
501 /*.pfnSymbolAdd = */ rtDbgModGhidra_SymbolAdd,
502 /*.pfnSymbolCount = */ rtDbgModGhidra_SymbolCount,
503 /*.pfnSymbolByOrdinal = */ rtDbgModGhidra_SymbolByOrdinal,
504 /*.pfnSymbolByName = */ rtDbgModGhidra_SymbolByName,
505 /*.pfnSymbolByAddr = */ rtDbgModGhidra_SymbolByAddr,
506
507 /*.pfnLineAdd = */ rtDbgModGhidra_LineAdd,
508 /*.pfnLineCount = */ rtDbgModGhidra_LineCount,
509 /*.pfnLineByOrdinal = */ rtDbgModGhidra_LineByOrdinal,
510 /*.pfnLineByAddr = */ rtDbgModGhidra_LineByAddr,
511
512 /*.pfnUnwindFrame = */ rtDbgModGhidra_UnwindFrame,
513
514 /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
515};
516
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