VirtualBox

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

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