VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGPlugInFreeBsd.cpp@ 94521

Last change on this file since 94521 was 93485, checked in by vboxsync, 2 years ago

Debugger,Main: Doxygen fixes. bugref:10074

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.3 KB
Line 
1/* $Id: DBGPlugInFreeBsd.cpp 93485 2022-01-29 12:32:20Z vboxsync $ */
2/** @file
3 * DBGPlugInFreeBsd - Debugger and Guest OS Digger Plugin For FreeBSD.
4 */
5
6/*
7 * Copyright (C) 2016-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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGF /// @todo add new log group.
23#include "DBGPlugIns.h"
24#include "DBGPlugInCommonELF.h"
25#include <VBox/vmm/vmmr3vtable.h>
26#include <iprt/asm.h>
27#include <iprt/ctype.h>
28#include <iprt/err.h>
29#include <iprt/mem.h>
30#include <iprt/stream.h>
31#include <iprt/string.h>
32
33
34/*********************************************************************************************************************************
35* Defined Constants And Macros *
36*********************************************************************************************************************************/
37/** FreeBSD on little endian ASCII systems. */
38#define DIG_FBSD_MOD_TAG UINT64_C(0x0044534265657246)
39
40
41/*********************************************************************************************************************************
42* Structures and Typedefs *
43*********************************************************************************************************************************/
44
45/**
46 * FreeBSD .dynstr and .dynsym location probing state.
47 */
48typedef enum FBSDPROBESTATE
49{
50 /** Invalid state. */
51 FBSDPROBESTATE_INVALID = 0,
52 /** Searching for the end of the .dynstr section (terminator). */
53 FBSDPROBESTATE_DYNSTR_END,
54 /** Last symbol was a symbol terminator character. */
55 FBSDPROBESTATE_DYNSTR_SYM_TERMINATOR,
56 /** Last symbol was a symbol character. */
57 FBSDPROBESTATE_DYNSTR_SYM_CHAR
58} FBSDPROBESTATE;
59
60/**
61 * ELF headers union.
62 */
63typedef union ELFEHDRS
64{
65 /** 32bit version of the ELF header. */
66 Elf32_Ehdr Hdr32;
67 /** 64bit version of the ELF header. */
68 Elf64_Ehdr Hdr64;
69} ELFEHDRS;
70/** Pointer to a ELF header union. */
71typedef ELFEHDRS *PELFEHDRS;
72/** Pointer to const ELF header union. */
73typedef ELFEHDRS const *PCELFEHDRS;
74
75/**
76 * ELF symbol entry union.
77 */
78typedef union ELFSYMS
79{
80 /** 32bit version of the ELF section header. */
81 Elf32_Sym Hdr32;
82 /** 64bit version of the ELF section header. */
83 Elf64_Sym Hdr64;
84} ELFSYMS;
85/** Pointer to a ELF symbol entry union. */
86typedef ELFSYMS *PELFSYMS;
87/** Pointer to const ELF symbol entry union. */
88typedef ELFSYMS const *PCELFSYMS;
89
90/**
91 * Message buffer structure.
92 */
93typedef union FBSDMSGBUF
94{
95 /** 32bit version. */
96 struct
97 {
98 /** Message buffer pointer. */
99 uint32_t msg_ptr;
100 /** Magic value to identify the structure. */
101 uint32_t msg_magic;
102 /** Size of the buffer area. */
103 uint32_t msg_size;
104 /** Write sequence number. */
105 uint32_t msg_wseq;
106 /** Read sequence number. */
107 uint32_t msg_rseq;
108 /** @todo More fields which are not required atm. */
109 } Hdr32;
110 /** 64bit version. */
111 struct
112 {
113 /** Message buffer pointer. */
114 uint64_t msg_ptr;
115 /** Magic value to identify the structure. */
116 uint32_t msg_magic;
117 /** Size of the buffer area. */
118 uint32_t msg_size;
119 /** Write sequence number. */
120 uint32_t msg_wseq;
121 /** Read sequence number. */
122 uint32_t msg_rseq;
123 /** @todo More fields which are not required atm. */
124 } Hdr64;
125} FBSDMSGBUF;
126/** Pointer to a message buffer structure. */
127typedef FBSDMSGBUF *PFBSDMSGBUF;
128/** Pointer to a const message buffer structure. */
129typedef FBSDMSGBUF const *PCFBSDMSGBUF;
130
131/** Magic value to identify the message buffer structure. */
132#define FBSD_MSGBUF_MAGIC UINT32_C(0x063062)
133
134/**
135 * FreeBSD guest OS digger instance data.
136 */
137typedef struct DBGDIGGERFBSD
138{
139 /** Whether the information is valid or not.
140 * (For fending off illegal interface method calls.) */
141 bool fValid;
142 /** 64-bit/32-bit indicator. */
143 bool f64Bit;
144
145 /** Address of the start of the kernel ELF image,
146 * set during probing. */
147 DBGFADDRESS AddrKernelElfStart;
148 /** Address of the interpreter content aka "/red/herring". */
149 DBGFADDRESS AddrKernelInterp;
150 /** Address of the start of the text section. */
151 DBGFADDRESS AddrKernelText;
152
153 /** The kernel message log interface. */
154 DBGFOSIDMESG IDmesg;
155
156} DBGDIGGERFBSD;
157/** Pointer to the FreeBSD guest OS digger instance data. */
158typedef DBGDIGGERFBSD *PDBGDIGGERFBSD;
159
160
161/*********************************************************************************************************************************
162* Defined Constants And Macros *
163*********************************************************************************************************************************/
164/** Min kernel address (32bit). */
165#define FBSD32_MIN_KRNL_ADDR UINT32_C(0x80000000)
166/** Max kernel address (32bit). */
167#define FBSD32_MAX_KRNL_ADDR UINT32_C(0xfffff000)
168
169/** Min kernel address (64bit). */
170#define FBSD64_MIN_KRNL_ADDR UINT64_C(0xFFFFF80000000000)
171/** Max kernel address (64bit). */
172#define FBSD64_MAX_KRNL_ADDR UINT64_C(0xFFFFFFFFFFF00000)
173
174
175/** Validates a 32-bit FreeBSD kernel address */
176#define FBSD32_VALID_ADDRESS(Addr) ( (Addr) > FBSD32_MIN_KRNL_ADDR \
177 && (Addr) < FBSD32_MAX_KRNL_ADDR)
178/** Validates a 64-bit FreeBSD kernel address */
179#define FBSD64_VALID_ADDRESS(Addr) ( (Addr) > FBSD64_MIN_KRNL_ADDR \
180 && (Addr) < FBSD64_MAX_KRNL_ADDR)
181
182/** Validates a FreeBSD kernel address. */
183#define FBSD_VALID_ADDRESS(a_pThis, a_Addr) ((a_pThis)->f64Bit ? FBSD64_VALID_ADDRESS(a_Addr) : FBSD32_VALID_ADDRESS(a_Addr))
184
185/** Maximum offset from the start of the ELF image we look for the /red/herring .interp section content. */
186#define FBSD_MAX_INTERP_OFFSET _16K
187/** The max kernel size. */
188#define FBSD_MAX_KERNEL_SIZE UINT32_C(0x0f000000)
189
190/** Versioned and bitness wrapper. */
191#define FBSD_UNION(a_pThis, a_pUnion, a_Member) ((a_pThis)->f64Bit ? (a_pUnion)->Hdr64. a_Member : (a_pUnion)->Hdr32. a_Member )
192
193
194/*********************************************************************************************************************************
195* Internal Functions *
196*********************************************************************************************************************************/
197static DECLCALLBACK(int) dbgDiggerFreeBsdInit(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData);
198
199
200/*********************************************************************************************************************************
201* Global Variables *
202*********************************************************************************************************************************/
203/** Table of common FreeBSD kernel addresses. */
204static uint64_t g_au64FreeBsdKernelAddresses[] =
205{
206 UINT64_C(0xc0100000),
207 UINT64_C(0xffffffff80100000)
208};
209/** Magic string which resides in the .interp section of the image. */
210static const uint8_t g_abNeedleInterp[] = "/red/herring";
211
212
213/**
214 * Load the symbols from the .dynsym and .dynstr sections given
215 * by their address in guest memory.
216 *
217 * @returns VBox status code.
218 * @param pThis The instance data.
219 * @param pUVM The user mode VM handle.
220 * @param pVMM The VMM function table.
221 * @param pszName The image name.
222 * @param uKernelStart The kernel start address.
223 * @param cbKernel Size of the kernel image.
224 * @param pAddrDynsym Start address of the .dynsym section.
225 * @param cSymbols Number of symbols in the .dynsym section.
226 * @param pAddrDynstr Start address of the .dynstr section containing the symbol names.
227 * @param cbDynstr Size of the .dynstr section.
228 */
229static int dbgDiggerFreeBsdLoadSymbols(PDBGDIGGERFBSD pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, const char *pszName,
230 RTGCUINTPTR uKernelStart, size_t cbKernel, PDBGFADDRESS pAddrDynsym, uint32_t cSymbols,
231 PDBGFADDRESS pAddrDynstr, size_t cbDynstr)
232{
233 LogFlowFunc(("pThis=%#p pszName=%s uKernelStart=%RGv cbKernel=%zu pAddrDynsym=%#p{%RGv} cSymbols=%u pAddrDynstr=%#p{%RGv} cbDynstr=%zu\n",
234 pThis, pszName, uKernelStart, cbKernel, pAddrDynsym, pAddrDynsym->FlatPtr, cSymbols, pAddrDynstr, pAddrDynstr->FlatPtr, cbDynstr));
235
236 char *pbDynstr = (char *)RTMemAllocZ(cbDynstr + 1); /* Extra terminator. */
237 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pAddrDynstr, pbDynstr, cbDynstr);
238 if (RT_SUCCESS(rc))
239 {
240 uint32_t cbDynsymEnt = pThis->f64Bit ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
241 uint8_t *pbDynsym = (uint8_t *)RTMemAllocZ(cSymbols * cbDynsymEnt);
242 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pAddrDynsym, pbDynsym, cSymbols * cbDynsymEnt);
243 if (RT_SUCCESS(rc))
244 {
245 /*
246 * Create a module for the kernel.
247 */
248 RTDBGMOD hMod;
249 rc = RTDbgModCreate(&hMod, pszName, cbKernel, 0 /*fFlags*/);
250 if (RT_SUCCESS(rc))
251 {
252 rc = RTDbgModSetTag(hMod, DIG_FBSD_MOD_TAG); AssertRC(rc);
253 rc = VINF_SUCCESS;
254
255 /*
256 * Enumerate the symbols.
257 */
258 uint32_t cLeft = cSymbols;
259 while (cLeft-- > 0 && RT_SUCCESS(rc))
260 {
261 PCELFSYMS pSym = (PCELFSYMS)&pbDynsym[cLeft * cbDynsymEnt];
262 uint32_t idxSymStr = FBSD_UNION(pThis, pSym, st_name);
263 uint8_t uType = FBSD_UNION(pThis, pSym, st_info);
264 RTGCUINTPTR AddrVal = FBSD_UNION(pThis, pSym, st_value);
265 size_t cbSymVal = FBSD_UNION(pThis, pSym, st_size);
266
267 /* Add it without the type char. */
268 RT_NOREF(uType);
269 if ( AddrVal <= uKernelStart + cbKernel
270 && idxSymStr < cbDynstr)
271 {
272 rc = RTDbgModSymbolAdd(hMod, &pbDynstr[idxSymStr], RTDBGSEGIDX_RVA, AddrVal - uKernelStart,
273 cbSymVal, 0 /*fFlags*/, NULL);
274 if (RT_FAILURE(rc))
275 {
276 if ( rc == VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE
277 || rc == VERR_DBG_INVALID_RVA
278 || rc == VERR_DBG_ADDRESS_CONFLICT
279 || rc == VERR_DBG_DUPLICATE_SYMBOL)
280 {
281 Log2(("dbgDiggerFreeBsdLoadSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc (ignored)\n",
282 &pbDynstr[idxSymStr], rc));
283 rc = VINF_SUCCESS;
284 }
285 else
286 Log(("dbgDiggerFreeBsdLoadSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc\n",
287 &pbDynstr[idxSymStr], rc));
288 }
289 }
290 }
291
292 /*
293 * Link the module into the address space.
294 */
295 if (RT_SUCCESS(rc))
296 {
297 RTDBGAS hAs = pVMM->pfnDBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
298 if (hAs != NIL_RTDBGAS)
299 rc = RTDbgAsModuleLink(hAs, hMod, uKernelStart, RTDBGASLINK_FLAGS_REPLACE);
300 else
301 rc = VERR_INTERNAL_ERROR;
302 RTDbgAsRelease(hAs);
303 }
304 else
305 Log(("dbgDiggerFreeBsdLoadSymbols: Failed: %Rrc\n", rc));
306 RTDbgModRelease(hMod);
307 }
308 else
309 Log(("dbgDiggerFreeBsdLoadSymbols: RTDbgModCreate failed: %Rrc\n", rc));
310 }
311 else
312 Log(("dbgDiggerFreeBsdLoadSymbols: Reading symbol table at %RGv failed: %Rrc\n",
313 pAddrDynsym->FlatPtr, rc));
314 RTMemFree(pbDynsym);
315 }
316 else
317 Log(("dbgDiggerFreeBsdLoadSymbols: Reading symbol string table at %RGv failed: %Rrc\n",
318 pAddrDynstr->FlatPtr, rc));
319 RTMemFree(pbDynstr);
320
321 LogFlowFunc(("returns %Rrc\n", rc));
322 return rc;
323}
324
325/**
326 * Process the kernel image.
327 *
328 * @param pThis The instance data.
329 * @param pUVM The user mode VM handle.
330 * @param pVMM The VMM function table.
331 * @param pszName The image name.
332 */
333static void dbgDiggerFreeBsdProcessKernelImage(PDBGDIGGERFBSD pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, const char *pszName)
334{
335 /*
336 * FreeBSD has parts of the kernel ELF image in guest memory, starting with the
337 * ELF header and the content of the sections which are indicated to be loaded
338 * into memory (text, rodata, etc.) of course. Whats missing are the section headers
339 * which is understandable but unfortunate because it would make our life easier.
340 *
341 * All checked FreeBSD kernels so far have the following layout in the kernel:
342 * [.interp] - contains the /red/herring string we used for probing earlier
343 * [.hash] - contains the hashes of the symbol names, 8 byte alignment on 64bit, 4 byte on 32bit
344 * [.gnu.hash] - GNU hash section. (introduced somewhere between 10.0 and 12.0 @todo Find out when exactly)
345 * [.dynsym] - contains the ELF symbol descriptors, 8 byte alignment, 4 byte on 32bit
346 * [.dynstr] - contains the symbol names as a string table, 1 byte alignmnt
347 * [.text] - contains the executable code, 16 byte alignment.
348 *
349 * To find the start of the .dynsym and .dynstr sections we scan backwards from the start of the .text section
350 * and check for all characters allowed for symbol names and count the amount of symbols found. When the start of the
351 * .dynstr section is reached the number of entries in .dynsym is known and we can deduce the start address.
352 *
353 * This applied to the old code before the FreeBSD kernel introduced the .gnu.hash section
354 * (keeping it here for informational pruposes):
355 * The sections are always adjacent (sans alignment) so we just parse the .hash section right after
356 * .interp, ELF states that it can contain 32bit or 64bit words but all observed kernels
357 * always use 32bit words. It contains two counters at the beginning which we can use to
358 * deduct the .hash section size and the beginning of .dynsym.
359 * .dynsym contains an array of symbol descriptors which have a fixed size depending on the
360 * guest bitness.
361 * Finding the end of .dynsym is not easily doable as there is no counter available (it lives
362 * in the section headers) at this point so we just have to check whether the record is valid
363 * and if not check if it contains an ASCII string which marks the start of the .dynstr section.
364 */
365
366#if 0
367 DBGFADDRESS AddrInterpEnd = pThis->AddrKernelInterp;
368 DBGFR3AddrAdd(&AddrInterpEnd, sizeof(g_abNeedleInterp));
369
370 DBGFADDRESS AddrCur = pThis->AddrKernelText;
371 int rc = VINF_SUCCESS;
372 uint32_t cSymbols = 0;
373 size_t cbKernel = 512 * _1M;
374 RTGCUINTPTR uKernelStart = pThis->AddrKernelElfStart.FlatPtr;
375 FBSDPROBESTATE enmState = FBSDPROBESTATE_DYNSTR_END; /* Start searching for the end of the .dynstr section. */
376
377 while (AddrCur.FlatPtr > AddrInterpEnd.FlatPtr)
378 {
379 char achBuf[_16K];
380 size_t cbToRead = RT_MIN(sizeof(achBuf), AddrCur.FlatPtr - AddrInterpEnd.FlatPtr);
381
382 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrSub(&AddrCur, cbToRead), &achBuf[0], cbToRead);
383 if (RT_FAILURE(rc))
384 break;
385
386 for (unsigned i = cbToRead; i > 0; i--)
387 {
388 char ch = achBuf[i - 1];
389
390 switch (enmState)
391 {
392 case FBSDPROBESTATE_DYNSTR_END:
393 {
394 if (ch != '\0')
395 enmState = FBSDPROBESTATE_DYNSTR_SYM_CHAR;
396 break;
397 }
398 case FBSDPROBESTATE_DYNSTR_SYM_TERMINATOR:
399 {
400 if ( RT_C_IS_ALNUM(ch)
401 || ch == '_'
402 || ch == '.')
403 enmState = FBSDPROBESTATE_DYNSTR_SYM_CHAR;
404 else
405 {
406 /* Two consecutive terminator symbols mean end of .dynstr section. */
407 pVMM->pfnDBGFR3AddrAdd(&AddrCur, i);
408 DBGFADDRESS AddrDynstrStart = AddrCur;
409 DBGFADDRESS AddrDynsymStart = AddrCur;
410 pVMM->pfnDBGFR3AddrSub(&AddrDynsymStart, cSymbols * (pThis->f64Bit ? sizeof(Elf64_Sym) : sizeof(Elf64_Sym)));
411 LogFlowFunc(("Found all required section start addresses (.dynsym=%RGv cSymbols=%u, .dynstr=%RGv cb=%u)\n",
412 AddrDynsymStart.FlatPtr, cSymbols, AddrDynstrStart.FlatPtr,
413 pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr));
414 dbgDiggerFreeBsdLoadSymbols(pThis, pUVM, pVMM, pszName, uKernelStart, cbKernel,
415 &AddrDynsymStart, cSymbols, &AddrDynstrStart,
416 pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr);
417 return;
418 }
419 break;
420 }
421 case FBSDPROBESTATE_DYNSTR_SYM_CHAR:
422 {
423 if ( !RT_C_IS_ALNUM(ch)
424 && ch != '_'
425 && ch != '.')
426 {
427 /* Non symbol character. */
428 if (ch == '\0')
429 {
430 enmState = FBSDPROBESTATE_DYNSTR_SYM_TERMINATOR;
431 cSymbols++;
432 }
433 else
434 {
435 /* Indicates the end of the .dynstr section. */
436 pVMM->pfnDBGFR3AddrAdd(&AddrCur, i);
437 DBGFADDRESS AddrDynstrStart = AddrCur;
438 DBGFADDRESS AddrDynsymStart = AddrCur;
439 pVMM->pfnDBGFR3AddrSub(&AddrDynsymStart, cSymbols * (pThis->f64Bit ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym)));
440 LogFlowFunc(("Found all required section start addresses (.dynsym=%RGv cSymbols=%u, .dynstr=%RGv cb=%u)\n",
441 AddrDynsymStart.FlatPtr, cSymbols, AddrDynstrStart.FlatPtr,
442 pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr));
443 dbgDiggerFreeBsdLoadSymbols(pThis, pUVM, pVMM, pszName, uKernelStart, cbKernel,
444 &AddrDynsymStart, cSymbols, &AddrDynstrStart,
445 pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr);
446 return;
447 }
448 }
449 break;
450 }
451 default:
452 AssertFailedBreak();
453 }
454 }
455 }
456
457 LogFlow(("Failed to find valid .dynsym and .dynstr sections (%Rrc), can't load kernel symbols\n", rc));
458#else
459 /* Calculate the start of the .hash section. */
460 DBGFADDRESS AddrHashStart = pThis->AddrKernelInterp;
461 pVMM->pfnDBGFR3AddrAdd(&AddrHashStart, sizeof(g_abNeedleInterp));
462 AddrHashStart.FlatPtr = RT_ALIGN_GCPT(AddrHashStart.FlatPtr, pThis->f64Bit ? 8 : 4, RTGCUINTPTR);
463 uint32_t au32Counters[2];
464 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrHashStart, &au32Counters[0], sizeof(au32Counters));
465 if (RT_SUCCESS(rc))
466 {
467 size_t cbHash = (au32Counters[0] + au32Counters[1] + 2) * sizeof(uint32_t);
468 if (AddrHashStart.FlatPtr + cbHash < pThis->AddrKernelText.FlatPtr) /* Should be much smaller */
469 {
470 DBGFADDRESS AddrDynsymStart = AddrHashStart;
471 uint32_t cSymbols = 0;
472 size_t cbKernel = 0;
473 RTGCUINTPTR uKernelStart = pThis->AddrKernelElfStart.FlatPtr;
474
475 pVMM->pfnDBGFR3AddrAdd(&AddrDynsymStart, cbHash);
476 AddrDynsymStart.FlatPtr = RT_ALIGN_GCPT(AddrDynsymStart.FlatPtr, pThis->f64Bit ? 8 : 4, RTGCUINTPTR);
477
478 DBGFADDRESS AddrDynstrStart = AddrDynsymStart;
479 while (AddrDynstrStart.FlatPtr < pThis->AddrKernelText.FlatPtr)
480 {
481 size_t cbDynSymEnt = pThis->f64Bit ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
482 uint8_t abBuf[_16K];
483 size_t cbToRead = RT_MIN(sizeof(abBuf), pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr);
484
485 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrDynstrStart, &abBuf[0], cbToRead);
486 if (RT_FAILURE(rc))
487 break;
488
489 for (unsigned i = 0; i < cbToRead / cbDynSymEnt; i++)
490 {
491 PCELFSYMS pSym = (PCELFSYMS)&abBuf[i * cbDynSymEnt];
492 uint32_t idxSymStr = FBSD_UNION(pThis, pSym, st_name);
493 uint8_t uType = FBSD_UNION(pThis, pSym, st_info);
494 RTGCUINTPTR AddrVal = FBSD_UNION(pThis, pSym, st_value);
495 size_t cbSymVal = FBSD_UNION(pThis, pSym, st_size);
496
497 /*
498 * If the entry doesn't look valid check whether it contains an ASCII string,
499 * we then found the start of the .dynstr section.
500 */
501 RT_NOREF(uType);
502 if ( ELF32_ST_TYPE(uType) != STT_NOTYPE
503 && ( !FBSD_VALID_ADDRESS(pThis, AddrVal)
504 || cbSymVal > FBSD_MAX_KERNEL_SIZE
505 || idxSymStr > pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr))
506 {
507 LogFlowFunc(("Invalid symbol table entry found at %RGv\n",
508 AddrDynstrStart.FlatPtr + i * cbDynSymEnt));
509
510 uint8_t *pbBuf = &abBuf[i * cbDynSymEnt];
511 size_t cbLeft = cbToRead - i * cbDynSymEnt;
512 /*
513 * Check to the end of the buffer whether it contains only a certain set of
514 * ASCII characters and 0 terminators.
515 */
516 while ( cbLeft > 0
517 && ( RT_C_IS_ALNUM(*pbBuf)
518 || *pbBuf == '_'
519 || *pbBuf == '\0'
520 || *pbBuf == '.'))
521 {
522 cbLeft--;
523 pbBuf++;
524 }
525
526 if (!cbLeft)
527 {
528 pVMM->pfnDBGFR3AddrAdd(&AddrDynstrStart, i * cbDynSymEnt);
529 LogFlowFunc(("Found all required section start addresses (.dynsym=%RGv cSymbols=%u, .dynstr=%RGv cb=%u)\n",
530 AddrDynsymStart.FlatPtr, cSymbols, AddrDynstrStart.FlatPtr,
531 pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr));
532 dbgDiggerFreeBsdLoadSymbols(pThis, pUVM, pVMM, pszName, uKernelStart, cbKernel,
533 &AddrDynsymStart, cSymbols, &AddrDynstrStart,
534 pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr);
535 return;
536 }
537 else
538 LogFlowFunc(("Found invalid ASCII character in .dynstr section candidate: %#x\n", *pbBuf));
539 }
540 else
541 {
542 cSymbols++;
543 if ( ELF32_ST_TYPE(uType) != STT_NOTYPE
544 && FBSD_VALID_ADDRESS(pThis, AddrVal))
545 {
546 uKernelStart = RT_MIN(uKernelStart, AddrVal);
547 cbKernel = RT_MAX(cbKernel, AddrVal + cbSymVal - uKernelStart);
548 }
549 }
550 }
551
552 /* Don't account incomplete entries. */
553 pVMM->pfnDBGFR3AddrAdd(&AddrDynstrStart, (cbToRead / cbDynSymEnt) * cbDynSymEnt);
554 }
555 }
556 else
557 LogFlowFunc((".hash section overlaps with .text section: %zu (expected much less than %u)\n", cbHash,
558 pThis->AddrKernelText.FlatPtr - AddrHashStart.FlatPtr));
559 }
560#endif
561}
562
563
564/**
565 * @interface_method_impl{DBGFOSIDMESG,pfnQueryKernelLog}
566 */
567static DECLCALLBACK(int) dbgDiggerFreeBsdIDmsg_QueryKernelLog(PDBGFOSIDMESG pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, uint32_t fFlags,
568 uint32_t cMessages, char *pszBuf, size_t cbBuf, size_t *pcbActual)
569{
570 PDBGDIGGERFBSD pData = RT_FROM_MEMBER(pThis, DBGDIGGERFBSD, IDmesg);
571 RT_NOREF(fFlags);
572
573 if (cMessages < 1)
574 return VERR_INVALID_PARAMETER;
575
576 /* Resolve the message buffer address from the msgbufp symbol. */
577 RTDBGSYMBOL SymInfo;
578 int rc = pVMM->pfnDBGFR3AsSymbolByName(pUVM, DBGF_AS_KERNEL, "kernel!msgbufp", &SymInfo, NULL);
579 if (RT_SUCCESS(rc))
580 {
581 DBGFADDRESS AddrMsgBuf;
582
583 /* Read the message buffer pointer. */
584 RTGCPTR GCPtrMsgBufP = 0;
585 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrMsgBuf, SymInfo.Value),
586 &GCPtrMsgBufP, pData->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t));
587 if (RT_FAILURE(rc))
588 {
589 Log(("dbgDiggerFreeBsdIDmsg_QueryKernelLog: failed to read msgbufp at %RGv: %Rrc\n", AddrMsgBuf.FlatPtr, rc));
590 return VERR_NOT_FOUND;
591 }
592 if (!FBSD_VALID_ADDRESS(pData, GCPtrMsgBufP))
593 {
594 Log(("dbgDiggerFreeBsdIDmsg_QueryKernelLog: Invalid address for msgbufp: %RGv\n", GCPtrMsgBufP));
595 return VERR_NOT_FOUND;
596 }
597
598 /* Read the structure. */
599 FBSDMSGBUF MsgBuf;
600 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrMsgBuf, GCPtrMsgBufP),
601 &MsgBuf, sizeof(MsgBuf));
602 if (RT_SUCCESS(rc))
603 {
604 RTGCUINTPTR AddrBuf = FBSD_UNION(pData, &MsgBuf, msg_ptr);
605 uint32_t cbMsgBuf = FBSD_UNION(pData, &MsgBuf, msg_size);
606 uint32_t uMsgBufSeqR = FBSD_UNION(pData, &MsgBuf, msg_rseq);
607 uint32_t uMsgBufSeqW = FBSD_UNION(pData, &MsgBuf, msg_wseq);
608
609 /*
610 * Validate the structure.
611 */
612 if ( FBSD_UNION(pData, &MsgBuf, msg_magic) != FBSD_MSGBUF_MAGIC
613 || cbMsgBuf < UINT32_C(4096)
614 || cbMsgBuf > 16*_1M
615 || FBSD_UNION(pData, &MsgBuf, msg_rseq) > cbMsgBuf
616 || FBSD_UNION(pData, &MsgBuf, msg_wseq) > cbMsgBuf
617 || !FBSD_VALID_ADDRESS(pData, AddrBuf) )
618 {
619 Log(("dbgDiggerFreeBsdIDmsg_QueryKernelLog: Invalid MsgBuf data: msg_magic=%#x msg_size=%#x msg_rseq=%#x msg_wseq=%#x msg_ptr=%RGv\n",
620 FBSD_UNION(pData, &MsgBuf, msg_magic), cbMsgBuf, uMsgBufSeqR, uMsgBufSeqW, AddrBuf));
621 return VERR_INVALID_STATE;
622 }
623
624 /*
625 * Read the buffer.
626 */
627 char *pchMsgBuf = (char *)RTMemAlloc(cbMsgBuf);
628 if (!pchMsgBuf)
629 {
630 Log(("dbgDiggerFreeBsdIDmsg_QueryKernelLog: Failed to allocate %#x bytes of memory for the log buffer\n",
631 cbMsgBuf));
632 return VERR_INVALID_STATE;
633 }
634 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrMsgBuf, AddrBuf),
635 pchMsgBuf, cbMsgBuf);
636 if (RT_SUCCESS(rc))
637 {
638 /*
639 * Copy it out raw.
640 */
641 uint32_t offDst = 0;
642 if (uMsgBufSeqR < uMsgBufSeqW)
643 {
644 /* Single chunk between the read and write offsets. */
645 uint32_t cbToCopy = uMsgBufSeqW - uMsgBufSeqR;
646 if (cbToCopy < cbBuf)
647 {
648 memcpy(pszBuf, &pchMsgBuf[uMsgBufSeqR], cbToCopy);
649 pszBuf[cbToCopy] = '\0';
650 rc = VINF_SUCCESS;
651 }
652 else
653 {
654 if (cbBuf)
655 {
656 memcpy(pszBuf, &pchMsgBuf[uMsgBufSeqR], cbBuf - 1);
657 pszBuf[cbBuf - 1] = '\0';
658 }
659 rc = VERR_BUFFER_OVERFLOW;
660 }
661 offDst = cbToCopy + 1;
662 }
663 else
664 {
665 /* Two chunks, read offset to end, start to write offset. */
666 uint32_t cbFirst = cbMsgBuf - uMsgBufSeqR;
667 uint32_t cbSecond = uMsgBufSeqW;
668 if (cbFirst + cbSecond < cbBuf)
669 {
670 memcpy(pszBuf, &pchMsgBuf[uMsgBufSeqR], cbFirst);
671 memcpy(&pszBuf[cbFirst], pchMsgBuf, cbSecond);
672 offDst = cbFirst + cbSecond;
673 pszBuf[offDst++] = '\0';
674 rc = VINF_SUCCESS;
675 }
676 else
677 {
678 offDst = cbFirst + cbSecond + 1;
679 if (cbFirst < cbBuf)
680 {
681 memcpy(pszBuf, &pchMsgBuf[uMsgBufSeqR], cbFirst);
682 memcpy(&pszBuf[cbFirst], pchMsgBuf, cbBuf - cbFirst);
683 pszBuf[cbBuf - 1] = '\0';
684 }
685 else if (cbBuf)
686 {
687 memcpy(pszBuf, &pchMsgBuf[uMsgBufSeqR], cbBuf - 1);
688 pszBuf[cbBuf - 1] = '\0';
689 }
690 rc = VERR_BUFFER_OVERFLOW;
691 }
692 }
693
694 if (pcbActual)
695 *pcbActual = offDst;
696 }
697 else
698 Log(("dbgDiggerFreeBsdIDmsg_QueryKernelLog: Error reading %#x bytes at %RGv: %Rrc\n", cbBuf, AddrBuf, rc));
699 RTMemFree(pchMsgBuf);
700 }
701 else
702 LogFlowFunc(("Failed to read message buffer header: %Rrc\n", rc));
703 }
704
705 return rc;
706}
707
708
709/**
710 * @copydoc DBGFOSREG::pfnStackUnwindAssist
711 */
712static DECLCALLBACK(int) dbgDiggerFreeBsdStackUnwindAssist(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData, VMCPUID idCpu,
713 PDBGFSTACKFRAME pFrame, PRTDBGUNWINDSTATE pState,
714 PCCPUMCTX pInitialCtx, RTDBGAS hAs, uint64_t *puScratch)
715{
716 RT_NOREF(pUVM, pVMM, pvData, idCpu, pFrame, pState, pInitialCtx, hAs, puScratch);
717 return VINF_SUCCESS;
718}
719
720
721/**
722 * @copydoc DBGFOSREG::pfnQueryInterface
723 */
724static DECLCALLBACK(void *) dbgDiggerFreeBsdQueryInterface(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData, DBGFOSINTERFACE enmIf)
725{
726 PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
727 RT_NOREF(pUVM, pVMM);
728
729 switch (enmIf)
730 {
731 case DBGFOSINTERFACE_DMESG:
732 return &pThis->IDmesg;
733
734 default:
735 return NULL;
736 }
737}
738
739
740/**
741 * @copydoc DBGFOSREG::pfnQueryVersion
742 */
743static DECLCALLBACK(int) dbgDiggerFreeBsdQueryVersion(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData,
744 char *pszVersion, size_t cchVersion)
745{
746 PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
747 Assert(pThis->fValid); RT_NOREF(pThis);
748
749 RTDBGSYMBOL SymInfo;
750 int rc = pVMM->pfnDBGFR3AsSymbolByName(pUVM, DBGF_AS_KERNEL, "kernel!version", &SymInfo, NULL);
751 if (RT_SUCCESS(rc))
752 {
753 DBGFADDRESS AddrVersion;
754 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrVersion, SymInfo.Value);
755
756 rc = pVMM->pfnDBGFR3MemReadString(pUVM, 0, &AddrVersion, pszVersion, cchVersion);
757 if (RT_SUCCESS(rc))
758 {
759 char *pszEnd = RTStrEnd(pszVersion, cchVersion);
760 AssertReturn(pszEnd, VERR_BUFFER_OVERFLOW);
761 while ( pszEnd > pszVersion
762 && RT_C_IS_SPACE(pszEnd[-1]))
763 pszEnd--;
764 *pszEnd = '\0';
765 }
766 else
767 RTStrPrintf(pszVersion, cchVersion, "DBGFR3MemReadString -> %Rrc", rc);
768 }
769
770 return rc;
771}
772
773
774
775/**
776 * @copydoc DBGFOSREG::pfnTerm
777 */
778static DECLCALLBACK(void) dbgDiggerFreeBsdTerm(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
779{
780 PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
781 Assert(pThis->fValid);
782 RT_NOREF(pUVM, pVMM);
783
784 pThis->fValid = false;
785}
786
787
788/**
789 * @copydoc DBGFOSREG::pfnRefresh
790 */
791static DECLCALLBACK(int) dbgDiggerFreeBsdRefresh(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
792{
793 PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
794 NOREF(pThis);
795 Assert(pThis->fValid);
796
797 dbgDiggerFreeBsdTerm(pUVM, pVMM, pvData);
798 return dbgDiggerFreeBsdInit(pUVM, pVMM, pvData);
799}
800
801
802/**
803 * @copydoc DBGFOSREG::pfnInit
804 */
805static DECLCALLBACK(int) dbgDiggerFreeBsdInit(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
806{
807 PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
808 Assert(!pThis->fValid);
809
810 RT_NOREF1(pUVM);
811
812 dbgDiggerFreeBsdProcessKernelImage(pThis, pUVM, pVMM, "kernel");
813 pThis->fValid = true;
814 return VINF_SUCCESS;
815}
816
817
818/**
819 * @copydoc DBGFOSREG::pfnProbe
820 */
821static DECLCALLBACK(bool) dbgDiggerFreeBsdProbe(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
822{
823 PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
824
825 /*
826 * Look for the magic ELF header near the known start addresses.
827 * If one is found look for the magic "/red/herring" string which is in the
828 * "interp" section not far away and then validate the start of the ELF header
829 * to be sure.
830 */
831 for (unsigned i = 0; i < RT_ELEMENTS(g_au64FreeBsdKernelAddresses); i++)
832 {
833 static const uint8_t s_abNeedle[] = ELFMAG;
834 DBGFADDRESS KernelAddr;
835 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &KernelAddr, g_au64FreeBsdKernelAddresses[i]);
836 DBGFADDRESS HitAddr;
837 uint32_t cbLeft = FBSD_MAX_KERNEL_SIZE;
838
839 while (cbLeft > X86_PAGE_4K_SIZE)
840 {
841 int rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &KernelAddr, cbLeft, 1,
842 s_abNeedle, sizeof(s_abNeedle) - 1, &HitAddr);
843 if (RT_FAILURE(rc))
844 break;
845
846 /*
847 * Look for the magic "/red/herring" near the header and verify the basic
848 * ELF header.
849 */
850 DBGFADDRESS HitAddrInterp;
851 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, FBSD_MAX_INTERP_OFFSET, 1,
852 g_abNeedleInterp, sizeof(g_abNeedleInterp), &HitAddrInterp);
853 if (RT_SUCCESS(rc))
854 {
855 union
856 {
857 uint8_t ab[2 * X86_PAGE_4K_SIZE];
858 Elf32_Ehdr Hdr32;
859 Elf64_Ehdr Hdr64;
860 } ElfHdr;
861 AssertCompileMembersSameSizeAndOffset(Elf64_Ehdr, e_ident, Elf32_Ehdr, e_ident);
862 AssertCompileMembersSameSizeAndOffset(Elf64_Ehdr, e_type, Elf32_Ehdr, e_type);
863 AssertCompileMembersSameSizeAndOffset(Elf64_Ehdr, e_machine, Elf32_Ehdr, e_machine);
864 AssertCompileMembersSameSizeAndOffset(Elf64_Ehdr, e_version, Elf32_Ehdr, e_version);
865
866 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &HitAddr, &ElfHdr.ab[0], X86_PAGE_4K_SIZE);
867 if (RT_SUCCESS(rc))
868 {
869 /* We verified the magic above already by scanning for it. */
870 if ( ( ElfHdr.Hdr32.e_ident[EI_CLASS] == ELFCLASS32
871 || ElfHdr.Hdr32.e_ident[EI_CLASS] == ELFCLASS64)
872 && ElfHdr.Hdr32.e_ident[EI_DATA] == ELFDATA2LSB
873 && ElfHdr.Hdr32.e_ident[EI_VERSION] == EV_CURRENT
874 && ElfHdr.Hdr32.e_ident[EI_OSABI] == ELFOSABI_FREEBSD
875 && ElfHdr.Hdr32.e_type == ET_EXEC
876 && ( ElfHdr.Hdr32.e_machine == EM_386
877 || ElfHdr.Hdr32.e_machine == EM_X86_64)
878 && ElfHdr.Hdr32.e_version == EV_CURRENT)
879 {
880 pThis->f64Bit = ElfHdr.Hdr32.e_ident[EI_CLASS] == ELFCLASS64;
881 pThis->AddrKernelElfStart = HitAddr;
882 pThis->AddrKernelInterp = HitAddrInterp;
883 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &pThis->AddrKernelText, FBSD_UNION(pThis, &ElfHdr, e_entry));
884 LogFunc(("Found %s FreeBSD kernel at %RGv (.interp section at %RGv, .text section at %RGv)\n",
885 pThis->f64Bit ? "amd64" : "i386", pThis->AddrKernelElfStart.FlatPtr,
886 pThis->AddrKernelInterp.FlatPtr, pThis->AddrKernelText.FlatPtr));
887 return true;
888 }
889 }
890 }
891
892 /*
893 * Advance.
894 */
895 RTGCUINTPTR cbDistance = HitAddr.FlatPtr - KernelAddr.FlatPtr + sizeof(s_abNeedle) - 1;
896 if (RT_UNLIKELY(cbDistance >= cbLeft))
897 break;
898
899 cbLeft -= cbDistance;
900 pVMM->pfnDBGFR3AddrAdd(&KernelAddr, cbDistance);
901 }
902 }
903 return false;
904}
905
906
907/**
908 * @copydoc DBGFOSREG::pfnDestruct
909 */
910static DECLCALLBACK(void) dbgDiggerFreeBsdDestruct(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
911{
912 RT_NOREF(pUVM, pVMM, pvData);
913}
914
915
916/**
917 * @copydoc DBGFOSREG::pfnConstruct
918 */
919static DECLCALLBACK(int) dbgDiggerFreeBsdConstruct(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
920{
921 PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
922 RT_NOREF(pUVM, pVMM);
923
924 pThis->fValid = false;
925 pThis->f64Bit = false;
926 pThis->IDmesg.u32Magic = DBGFOSIDMESG_MAGIC;
927 pThis->IDmesg.pfnQueryKernelLog = dbgDiggerFreeBsdIDmsg_QueryKernelLog;
928 pThis->IDmesg.u32EndMagic = DBGFOSIDMESG_MAGIC;
929
930 return VINF_SUCCESS;
931}
932
933
934const DBGFOSREG g_DBGDiggerFreeBsd =
935{
936 /* .u32Magic = */ DBGFOSREG_MAGIC,
937 /* .fFlags = */ 0,
938 /* .cbData = */ sizeof(DBGDIGGERFBSD),
939 /* .szName = */ "FreeBSD",
940 /* .pfnConstruct = */ dbgDiggerFreeBsdConstruct,
941 /* .pfnDestruct = */ dbgDiggerFreeBsdDestruct,
942 /* .pfnProbe = */ dbgDiggerFreeBsdProbe,
943 /* .pfnInit = */ dbgDiggerFreeBsdInit,
944 /* .pfnRefresh = */ dbgDiggerFreeBsdRefresh,
945 /* .pfnTerm = */ dbgDiggerFreeBsdTerm,
946 /* .pfnQueryVersion = */ dbgDiggerFreeBsdQueryVersion,
947 /* .pfnQueryInterface = */ dbgDiggerFreeBsdQueryInterface,
948 /* .pfnStackUnwindAssist = */ dbgDiggerFreeBsdStackUnwindAssist,
949 /* .u32EndMagic = */ DBGFOSREG_MAGIC
950};
951
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use