summaryrefslogtreecommitdiffstats
path: root/src/VBox/Debugger/DBGPlugInFreeBsd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Debugger/DBGPlugInFreeBsd.cpp')
-rw-r--r--src/VBox/Debugger/DBGPlugInFreeBsd.cpp961
1 files changed, 961 insertions, 0 deletions
diff --git a/src/VBox/Debugger/DBGPlugInFreeBsd.cpp b/src/VBox/Debugger/DBGPlugInFreeBsd.cpp
new file mode 100644
index 00000000..6d8a04ee
--- /dev/null
+++ b/src/VBox/Debugger/DBGPlugInFreeBsd.cpp
@@ -0,0 +1,961 @@
+/* $Id: DBGPlugInFreeBsd.cpp $ */
+/** @file
+ * DBGPlugInFreeBsd - Debugger and Guest OS Digger Plugin For FreeBSD.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DBGF /// @todo add new log group.
+#include "DBGPlugIns.h"
+#include "DBGPlugInCommonELF.h"
+#include <VBox/vmm/vmmr3vtable.h>
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** FreeBSD on little endian ASCII systems. */
+#define DIG_FBSD_MOD_TAG UINT64_C(0x0044534265657246)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * FreeBSD .dynstr and .dynsym location probing state.
+ */
+typedef enum FBSDPROBESTATE
+{
+ /** Invalid state. */
+ FBSDPROBESTATE_INVALID = 0,
+ /** Searching for the end of the .dynstr section (terminator). */
+ FBSDPROBESTATE_DYNSTR_END,
+ /** Last symbol was a symbol terminator character. */
+ FBSDPROBESTATE_DYNSTR_SYM_TERMINATOR,
+ /** Last symbol was a symbol character. */
+ FBSDPROBESTATE_DYNSTR_SYM_CHAR
+} FBSDPROBESTATE;
+
+/**
+ * ELF headers union.
+ */
+typedef union ELFEHDRS
+{
+ /** 32bit version of the ELF header. */
+ Elf32_Ehdr Hdr32;
+ /** 64bit version of the ELF header. */
+ Elf64_Ehdr Hdr64;
+} ELFEHDRS;
+/** Pointer to a ELF header union. */
+typedef ELFEHDRS *PELFEHDRS;
+/** Pointer to const ELF header union. */
+typedef ELFEHDRS const *PCELFEHDRS;
+
+/**
+ * ELF symbol entry union.
+ */
+typedef union ELFSYMS
+{
+ /** 32bit version of the ELF section header. */
+ Elf32_Sym Hdr32;
+ /** 64bit version of the ELF section header. */
+ Elf64_Sym Hdr64;
+} ELFSYMS;
+/** Pointer to a ELF symbol entry union. */
+typedef ELFSYMS *PELFSYMS;
+/** Pointer to const ELF symbol entry union. */
+typedef ELFSYMS const *PCELFSYMS;
+
+/**
+ * Message buffer structure.
+ */
+typedef union FBSDMSGBUF
+{
+ /** 32bit version. */
+ struct
+ {
+ /** Message buffer pointer. */
+ uint32_t msg_ptr;
+ /** Magic value to identify the structure. */
+ uint32_t msg_magic;
+ /** Size of the buffer area. */
+ uint32_t msg_size;
+ /** Write sequence number. */
+ uint32_t msg_wseq;
+ /** Read sequence number. */
+ uint32_t msg_rseq;
+ /** @todo More fields which are not required atm. */
+ } Hdr32;
+ /** 64bit version. */
+ struct
+ {
+ /** Message buffer pointer. */
+ uint64_t msg_ptr;
+ /** Magic value to identify the structure. */
+ uint32_t msg_magic;
+ /** Size of the buffer area. */
+ uint32_t msg_size;
+ /** Write sequence number. */
+ uint32_t msg_wseq;
+ /** Read sequence number. */
+ uint32_t msg_rseq;
+ /** @todo More fields which are not required atm. */
+ } Hdr64;
+} FBSDMSGBUF;
+/** Pointer to a message buffer structure. */
+typedef FBSDMSGBUF *PFBSDMSGBUF;
+/** Pointer to a const message buffer structure. */
+typedef FBSDMSGBUF const *PCFBSDMSGBUF;
+
+/** Magic value to identify the message buffer structure. */
+#define FBSD_MSGBUF_MAGIC UINT32_C(0x063062)
+
+/**
+ * FreeBSD guest OS digger instance data.
+ */
+typedef struct DBGDIGGERFBSD
+{
+ /** Whether the information is valid or not.
+ * (For fending off illegal interface method calls.) */
+ bool fValid;
+ /** 64-bit/32-bit indicator. */
+ bool f64Bit;
+
+ /** Address of the start of the kernel ELF image,
+ * set during probing. */
+ DBGFADDRESS AddrKernelElfStart;
+ /** Address of the interpreter content aka "/red/herring". */
+ DBGFADDRESS AddrKernelInterp;
+ /** Address of the start of the text section. */
+ DBGFADDRESS AddrKernelText;
+
+ /** The kernel message log interface. */
+ DBGFOSIDMESG IDmesg;
+
+} DBGDIGGERFBSD;
+/** Pointer to the FreeBSD guest OS digger instance data. */
+typedef DBGDIGGERFBSD *PDBGDIGGERFBSD;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Min kernel address (32bit). */
+#define FBSD32_MIN_KRNL_ADDR UINT32_C(0x80000000)
+/** Max kernel address (32bit). */
+#define FBSD32_MAX_KRNL_ADDR UINT32_C(0xfffff000)
+
+/** Min kernel address (64bit). */
+#define FBSD64_MIN_KRNL_ADDR UINT64_C(0xFFFFF80000000000)
+/** Max kernel address (64bit). */
+#define FBSD64_MAX_KRNL_ADDR UINT64_C(0xFFFFFFFFFFF00000)
+
+
+/** Validates a 32-bit FreeBSD kernel address */
+#define FBSD32_VALID_ADDRESS(Addr) ( (Addr) > FBSD32_MIN_KRNL_ADDR \
+ && (Addr) < FBSD32_MAX_KRNL_ADDR)
+/** Validates a 64-bit FreeBSD kernel address */
+#define FBSD64_VALID_ADDRESS(Addr) ( (Addr) > FBSD64_MIN_KRNL_ADDR \
+ && (Addr) < FBSD64_MAX_KRNL_ADDR)
+
+/** Validates a FreeBSD kernel address. */
+#define FBSD_VALID_ADDRESS(a_pThis, a_Addr) ((a_pThis)->f64Bit ? FBSD64_VALID_ADDRESS(a_Addr) : FBSD32_VALID_ADDRESS(a_Addr))
+
+/** Maximum offset from the start of the ELF image we look for the /red/herring .interp section content. */
+#define FBSD_MAX_INTERP_OFFSET _16K
+/** The max kernel size. */
+#define FBSD_MAX_KERNEL_SIZE UINT32_C(0x0f000000)
+
+/** Versioned and bitness wrapper. */
+#define FBSD_UNION(a_pThis, a_pUnion, a_Member) ((a_pThis)->f64Bit ? (a_pUnion)->Hdr64. a_Member : (a_pUnion)->Hdr32. a_Member )
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) dbgDiggerFreeBsdInit(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Table of common FreeBSD kernel addresses. */
+static uint64_t g_au64FreeBsdKernelAddresses[] =
+{
+ UINT64_C(0xc0100000),
+ UINT64_C(0xffffffff80100000)
+};
+/** Magic string which resides in the .interp section of the image. */
+static const uint8_t g_abNeedleInterp[] = "/red/herring";
+
+
+/**
+ * Load the symbols from the .dynsym and .dynstr sections given
+ * by their address in guest memory.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance data.
+ * @param pUVM The user mode VM handle.
+ * @param pVMM The VMM function table.
+ * @param pszName The image name.
+ * @param uKernelStart The kernel start address.
+ * @param cbKernel Size of the kernel image.
+ * @param pAddrDynsym Start address of the .dynsym section.
+ * @param cSymbols Number of symbols in the .dynsym section.
+ * @param pAddrDynstr Start address of the .dynstr section containing the symbol names.
+ * @param cbDynstr Size of the .dynstr section.
+ */
+static int dbgDiggerFreeBsdLoadSymbols(PDBGDIGGERFBSD pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, const char *pszName,
+ RTGCUINTPTR uKernelStart, size_t cbKernel, PDBGFADDRESS pAddrDynsym, uint32_t cSymbols,
+ PDBGFADDRESS pAddrDynstr, size_t cbDynstr)
+{
+ LogFlowFunc(("pThis=%#p pszName=%s uKernelStart=%RGv cbKernel=%zu pAddrDynsym=%#p{%RGv} cSymbols=%u pAddrDynstr=%#p{%RGv} cbDynstr=%zu\n",
+ pThis, pszName, uKernelStart, cbKernel, pAddrDynsym, pAddrDynsym->FlatPtr, cSymbols, pAddrDynstr, pAddrDynstr->FlatPtr, cbDynstr));
+
+ char *pbDynstr = (char *)RTMemAllocZ(cbDynstr + 1); /* Extra terminator. */
+ int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pAddrDynstr, pbDynstr, cbDynstr);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cbDynsymEnt = pThis->f64Bit ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
+ uint8_t *pbDynsym = (uint8_t *)RTMemAllocZ(cSymbols * cbDynsymEnt);
+ rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pAddrDynsym, pbDynsym, cSymbols * cbDynsymEnt);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a module for the kernel.
+ */
+ RTDBGMOD hMod;
+ rc = RTDbgModCreate(&hMod, pszName, cbKernel, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDbgModSetTag(hMod, DIG_FBSD_MOD_TAG); AssertRC(rc);
+ rc = VINF_SUCCESS;
+
+ /*
+ * Enumerate the symbols.
+ */
+ uint32_t cLeft = cSymbols;
+ while (cLeft-- > 0 && RT_SUCCESS(rc))
+ {
+ PCELFSYMS pSym = (PCELFSYMS)&pbDynsym[cLeft * cbDynsymEnt];
+ uint32_t idxSymStr = FBSD_UNION(pThis, pSym, st_name);
+ uint8_t uType = FBSD_UNION(pThis, pSym, st_info);
+ RTGCUINTPTR AddrVal = FBSD_UNION(pThis, pSym, st_value);
+ size_t cbSymVal = FBSD_UNION(pThis, pSym, st_size);
+
+ /* Add it without the type char. */
+ RT_NOREF(uType);
+ if ( AddrVal <= uKernelStart + cbKernel
+ && idxSymStr < cbDynstr)
+ {
+ rc = RTDbgModSymbolAdd(hMod, &pbDynstr[idxSymStr], RTDBGSEGIDX_RVA, AddrVal - uKernelStart,
+ cbSymVal, 0 /*fFlags*/, NULL);
+ if (RT_FAILURE(rc))
+ {
+ if ( rc == VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE
+ || rc == VERR_DBG_INVALID_RVA
+ || rc == VERR_DBG_ADDRESS_CONFLICT
+ || rc == VERR_DBG_DUPLICATE_SYMBOL)
+ {
+ Log2(("dbgDiggerFreeBsdLoadSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc (ignored)\n",
+ &pbDynstr[idxSymStr], rc));
+ rc = VINF_SUCCESS;
+ }
+ else
+ Log(("dbgDiggerFreeBsdLoadSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc\n",
+ &pbDynstr[idxSymStr], rc));
+ }
+ }
+ }
+
+ /*
+ * Link the module into the address space.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ RTDBGAS hAs = pVMM->pfnDBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
+ if (hAs != NIL_RTDBGAS)
+ rc = RTDbgAsModuleLink(hAs, hMod, uKernelStart, RTDBGASLINK_FLAGS_REPLACE);
+ else
+ rc = VERR_INTERNAL_ERROR;
+ RTDbgAsRelease(hAs);
+ }
+ else
+ Log(("dbgDiggerFreeBsdLoadSymbols: Failed: %Rrc\n", rc));
+ RTDbgModRelease(hMod);
+ }
+ else
+ Log(("dbgDiggerFreeBsdLoadSymbols: RTDbgModCreate failed: %Rrc\n", rc));
+ }
+ else
+ Log(("dbgDiggerFreeBsdLoadSymbols: Reading symbol table at %RGv failed: %Rrc\n",
+ pAddrDynsym->FlatPtr, rc));
+ RTMemFree(pbDynsym);
+ }
+ else
+ Log(("dbgDiggerFreeBsdLoadSymbols: Reading symbol string table at %RGv failed: %Rrc\n",
+ pAddrDynstr->FlatPtr, rc));
+ RTMemFree(pbDynstr);
+
+ LogFlowFunc(("returns %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Process the kernel image.
+ *
+ * @param pThis The instance data.
+ * @param pUVM The user mode VM handle.
+ * @param pVMM The VMM function table.
+ * @param pszName The image name.
+ */
+static void dbgDiggerFreeBsdProcessKernelImage(PDBGDIGGERFBSD pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, const char *pszName)
+{
+ /*
+ * FreeBSD has parts of the kernel ELF image in guest memory, starting with the
+ * ELF header and the content of the sections which are indicated to be loaded
+ * into memory (text, rodata, etc.) of course. Whats missing are the section headers
+ * which is understandable but unfortunate because it would make our life easier.
+ *
+ * All checked FreeBSD kernels so far have the following layout in the kernel:
+ * [.interp] - contains the /red/herring string we used for probing earlier
+ * [.hash] - contains the hashes of the symbol names, 8 byte alignment on 64bit, 4 byte on 32bit
+ * [.gnu.hash] - GNU hash section. (introduced somewhere between 10.0 and 12.0 @todo Find out when exactly)
+ * [.dynsym] - contains the ELF symbol descriptors, 8 byte alignment, 4 byte on 32bit
+ * [.dynstr] - contains the symbol names as a string table, 1 byte alignmnt
+ * [.text] - contains the executable code, 16 byte alignment.
+ *
+ * To find the start of the .dynsym and .dynstr sections we scan backwards from the start of the .text section
+ * and check for all characters allowed for symbol names and count the amount of symbols found. When the start of the
+ * .dynstr section is reached the number of entries in .dynsym is known and we can deduce the start address.
+ *
+ * This applied to the old code before the FreeBSD kernel introduced the .gnu.hash section
+ * (keeping it here for informational pruposes):
+ * The sections are always adjacent (sans alignment) so we just parse the .hash section right after
+ * .interp, ELF states that it can contain 32bit or 64bit words but all observed kernels
+ * always use 32bit words. It contains two counters at the beginning which we can use to
+ * deduct the .hash section size and the beginning of .dynsym.
+ * .dynsym contains an array of symbol descriptors which have a fixed size depending on the
+ * guest bitness.
+ * Finding the end of .dynsym is not easily doable as there is no counter available (it lives
+ * in the section headers) at this point so we just have to check whether the record is valid
+ * and if not check if it contains an ASCII string which marks the start of the .dynstr section.
+ */
+
+#if 0
+ DBGFADDRESS AddrInterpEnd = pThis->AddrKernelInterp;
+ DBGFR3AddrAdd(&AddrInterpEnd, sizeof(g_abNeedleInterp));
+
+ DBGFADDRESS AddrCur = pThis->AddrKernelText;
+ int rc = VINF_SUCCESS;
+ uint32_t cSymbols = 0;
+ size_t cbKernel = 512 * _1M;
+ RTGCUINTPTR uKernelStart = pThis->AddrKernelElfStart.FlatPtr;
+ FBSDPROBESTATE enmState = FBSDPROBESTATE_DYNSTR_END; /* Start searching for the end of the .dynstr section. */
+
+ while (AddrCur.FlatPtr > AddrInterpEnd.FlatPtr)
+ {
+ char achBuf[_16K];
+ size_t cbToRead = RT_MIN(sizeof(achBuf), AddrCur.FlatPtr - AddrInterpEnd.FlatPtr);
+
+ rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrSub(&AddrCur, cbToRead), &achBuf[0], cbToRead);
+ if (RT_FAILURE(rc))
+ break;
+
+ for (unsigned i = cbToRead; i > 0; i--)
+ {
+ char ch = achBuf[i - 1];
+
+ switch (enmState)
+ {
+ case FBSDPROBESTATE_DYNSTR_END:
+ {
+ if (ch != '\0')
+ enmState = FBSDPROBESTATE_DYNSTR_SYM_CHAR;
+ break;
+ }
+ case FBSDPROBESTATE_DYNSTR_SYM_TERMINATOR:
+ {
+ if ( RT_C_IS_ALNUM(ch)
+ || ch == '_'
+ || ch == '.')
+ enmState = FBSDPROBESTATE_DYNSTR_SYM_CHAR;
+ else
+ {
+ /* Two consecutive terminator symbols mean end of .dynstr section. */
+ pVMM->pfnDBGFR3AddrAdd(&AddrCur, i);
+ DBGFADDRESS AddrDynstrStart = AddrCur;
+ DBGFADDRESS AddrDynsymStart = AddrCur;
+ pVMM->pfnDBGFR3AddrSub(&AddrDynsymStart, cSymbols * (pThis->f64Bit ? sizeof(Elf64_Sym) : sizeof(Elf64_Sym)));
+ LogFlowFunc(("Found all required section start addresses (.dynsym=%RGv cSymbols=%u, .dynstr=%RGv cb=%u)\n",
+ AddrDynsymStart.FlatPtr, cSymbols, AddrDynstrStart.FlatPtr,
+ pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr));
+ dbgDiggerFreeBsdLoadSymbols(pThis, pUVM, pVMM, pszName, uKernelStart, cbKernel,
+ &AddrDynsymStart, cSymbols, &AddrDynstrStart,
+ pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr);
+ return;
+ }
+ break;
+ }
+ case FBSDPROBESTATE_DYNSTR_SYM_CHAR:
+ {
+ if ( !RT_C_IS_ALNUM(ch)
+ && ch != '_'
+ && ch != '.')
+ {
+ /* Non symbol character. */
+ if (ch == '\0')
+ {
+ enmState = FBSDPROBESTATE_DYNSTR_SYM_TERMINATOR;
+ cSymbols++;
+ }
+ else
+ {
+ /* Indicates the end of the .dynstr section. */
+ pVMM->pfnDBGFR3AddrAdd(&AddrCur, i);
+ DBGFADDRESS AddrDynstrStart = AddrCur;
+ DBGFADDRESS AddrDynsymStart = AddrCur;
+ pVMM->pfnDBGFR3AddrSub(&AddrDynsymStart, cSymbols * (pThis->f64Bit ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym)));
+ LogFlowFunc(("Found all required section start addresses (.dynsym=%RGv cSymbols=%u, .dynstr=%RGv cb=%u)\n",
+ AddrDynsymStart.FlatPtr, cSymbols, AddrDynstrStart.FlatPtr,
+ pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr));
+ dbgDiggerFreeBsdLoadSymbols(pThis, pUVM, pVMM, pszName, uKernelStart, cbKernel,
+ &AddrDynsymStart, cSymbols, &AddrDynstrStart,
+ pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr);
+ return;
+ }
+ }
+ break;
+ }
+ default:
+ AssertFailedBreak();
+ }
+ }
+ }
+
+ LogFlow(("Failed to find valid .dynsym and .dynstr sections (%Rrc), can't load kernel symbols\n", rc));
+#else
+ /* Calculate the start of the .hash section. */
+ DBGFADDRESS AddrHashStart = pThis->AddrKernelInterp;
+ pVMM->pfnDBGFR3AddrAdd(&AddrHashStart, sizeof(g_abNeedleInterp));
+ AddrHashStart.FlatPtr = RT_ALIGN_GCPT(AddrHashStart.FlatPtr, pThis->f64Bit ? 8 : 4, RTGCUINTPTR);
+ uint32_t au32Counters[2];
+ int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrHashStart, &au32Counters[0], sizeof(au32Counters));
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbHash = (au32Counters[0] + au32Counters[1] + 2) * sizeof(uint32_t);
+ if (AddrHashStart.FlatPtr + cbHash < pThis->AddrKernelText.FlatPtr) /* Should be much smaller */
+ {
+ DBGFADDRESS AddrDynsymStart = AddrHashStart;
+ uint32_t cSymbols = 0;
+ size_t cbKernel = 0;
+ RTGCUINTPTR uKernelStart = pThis->AddrKernelElfStart.FlatPtr;
+
+ pVMM->pfnDBGFR3AddrAdd(&AddrDynsymStart, cbHash);
+ AddrDynsymStart.FlatPtr = RT_ALIGN_GCPT(AddrDynsymStart.FlatPtr, pThis->f64Bit ? 8 : 4, RTGCUINTPTR);
+
+ DBGFADDRESS AddrDynstrStart = AddrDynsymStart;
+ while (AddrDynstrStart.FlatPtr < pThis->AddrKernelText.FlatPtr)
+ {
+ size_t cbDynSymEnt = pThis->f64Bit ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
+ uint8_t abBuf[_16K];
+ size_t cbToRead = RT_MIN(sizeof(abBuf), pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr);
+
+ rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrDynstrStart, &abBuf[0], cbToRead);
+ if (RT_FAILURE(rc))
+ break;
+
+ for (unsigned i = 0; i < cbToRead / cbDynSymEnt; i++)
+ {
+ PCELFSYMS pSym = (PCELFSYMS)&abBuf[i * cbDynSymEnt];
+ uint32_t idxSymStr = FBSD_UNION(pThis, pSym, st_name);
+ uint8_t uType = FBSD_UNION(pThis, pSym, st_info);
+ RTGCUINTPTR AddrVal = FBSD_UNION(pThis, pSym, st_value);
+ size_t cbSymVal = FBSD_UNION(pThis, pSym, st_size);
+
+ /*
+ * If the entry doesn't look valid check whether it contains an ASCII string,
+ * we then found the start of the .dynstr section.
+ */
+ RT_NOREF(uType);
+ if ( ELF32_ST_TYPE(uType) != STT_NOTYPE
+ && ( !FBSD_VALID_ADDRESS(pThis, AddrVal)
+ || cbSymVal > FBSD_MAX_KERNEL_SIZE
+ || idxSymStr > pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr))
+ {
+ LogFlowFunc(("Invalid symbol table entry found at %RGv\n",
+ AddrDynstrStart.FlatPtr + i * cbDynSymEnt));
+
+ uint8_t *pbBuf = &abBuf[i * cbDynSymEnt];
+ size_t cbLeft = cbToRead - i * cbDynSymEnt;
+ /*
+ * Check to the end of the buffer whether it contains only a certain set of
+ * ASCII characters and 0 terminators.
+ */
+ while ( cbLeft > 0
+ && ( RT_C_IS_ALNUM(*pbBuf)
+ || *pbBuf == '_'
+ || *pbBuf == '\0'
+ || *pbBuf == '.'))
+ {
+ cbLeft--;
+ pbBuf++;
+ }
+
+ if (!cbLeft)
+ {
+ pVMM->pfnDBGFR3AddrAdd(&AddrDynstrStart, i * cbDynSymEnt);
+ LogFlowFunc(("Found all required section start addresses (.dynsym=%RGv cSymbols=%u, .dynstr=%RGv cb=%u)\n",
+ AddrDynsymStart.FlatPtr, cSymbols, AddrDynstrStart.FlatPtr,
+ pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr));
+ dbgDiggerFreeBsdLoadSymbols(pThis, pUVM, pVMM, pszName, uKernelStart, cbKernel,
+ &AddrDynsymStart, cSymbols, &AddrDynstrStart,
+ pThis->AddrKernelText.FlatPtr - AddrDynstrStart.FlatPtr);
+ return;
+ }
+ else
+ LogFlowFunc(("Found invalid ASCII character in .dynstr section candidate: %#x\n", *pbBuf));
+ }
+ else
+ {
+ cSymbols++;
+ if ( ELF32_ST_TYPE(uType) != STT_NOTYPE
+ && FBSD_VALID_ADDRESS(pThis, AddrVal))
+ {
+ uKernelStart = RT_MIN(uKernelStart, AddrVal);
+ cbKernel = RT_MAX(cbKernel, AddrVal + cbSymVal - uKernelStart);
+ }
+ }
+ }
+
+ /* Don't account incomplete entries. */
+ pVMM->pfnDBGFR3AddrAdd(&AddrDynstrStart, (cbToRead / cbDynSymEnt) * cbDynSymEnt);
+ }
+ }
+ else
+ LogFlowFunc((".hash section overlaps with .text section: %zu (expected much less than %u)\n", cbHash,
+ pThis->AddrKernelText.FlatPtr - AddrHashStart.FlatPtr));
+ }
+#endif
+}
+
+
+/**
+ * @interface_method_impl{DBGFOSIDMESG,pfnQueryKernelLog}
+ */
+static DECLCALLBACK(int) dbgDiggerFreeBsdIDmsg_QueryKernelLog(PDBGFOSIDMESG pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, uint32_t fFlags,
+ uint32_t cMessages, char *pszBuf, size_t cbBuf, size_t *pcbActual)
+{
+ PDBGDIGGERFBSD pData = RT_FROM_MEMBER(pThis, DBGDIGGERFBSD, IDmesg);
+ RT_NOREF(fFlags);
+
+ if (cMessages < 1)
+ return VERR_INVALID_PARAMETER;
+
+ /* Resolve the message buffer address from the msgbufp symbol. */
+ RTDBGSYMBOL SymInfo;
+ int rc = pVMM->pfnDBGFR3AsSymbolByName(pUVM, DBGF_AS_KERNEL, "kernel!msgbufp", &SymInfo, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ DBGFADDRESS AddrMsgBuf;
+
+ /* Read the message buffer pointer. */
+ RTGCPTR GCPtrMsgBufP = 0;
+ rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrMsgBuf, SymInfo.Value),
+ &GCPtrMsgBufP, pData->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t));
+ if (RT_FAILURE(rc))
+ {
+ Log(("dbgDiggerFreeBsdIDmsg_QueryKernelLog: failed to read msgbufp at %RGv: %Rrc\n", AddrMsgBuf.FlatPtr, rc));
+ return VERR_NOT_FOUND;
+ }
+ if (!FBSD_VALID_ADDRESS(pData, GCPtrMsgBufP))
+ {
+ Log(("dbgDiggerFreeBsdIDmsg_QueryKernelLog: Invalid address for msgbufp: %RGv\n", GCPtrMsgBufP));
+ return VERR_NOT_FOUND;
+ }
+
+ /* Read the structure. */
+ FBSDMSGBUF MsgBuf;
+ rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrMsgBuf, GCPtrMsgBufP),
+ &MsgBuf, sizeof(MsgBuf));
+ if (RT_SUCCESS(rc))
+ {
+ RTGCUINTPTR AddrBuf = FBSD_UNION(pData, &MsgBuf, msg_ptr);
+ uint32_t cbMsgBuf = FBSD_UNION(pData, &MsgBuf, msg_size);
+ uint32_t uMsgBufSeqR = FBSD_UNION(pData, &MsgBuf, msg_rseq);
+ uint32_t uMsgBufSeqW = FBSD_UNION(pData, &MsgBuf, msg_wseq);
+
+ /*
+ * Validate the structure.
+ */
+ if ( FBSD_UNION(pData, &MsgBuf, msg_magic) != FBSD_MSGBUF_MAGIC
+ || cbMsgBuf < UINT32_C(4096)
+ || cbMsgBuf > 16*_1M
+ || FBSD_UNION(pData, &MsgBuf, msg_rseq) > cbMsgBuf
+ || FBSD_UNION(pData, &MsgBuf, msg_wseq) > cbMsgBuf
+ || !FBSD_VALID_ADDRESS(pData, AddrBuf) )
+ {
+ Log(("dbgDiggerFreeBsdIDmsg_QueryKernelLog: Invalid MsgBuf data: msg_magic=%#x msg_size=%#x msg_rseq=%#x msg_wseq=%#x msg_ptr=%RGv\n",
+ FBSD_UNION(pData, &MsgBuf, msg_magic), cbMsgBuf, uMsgBufSeqR, uMsgBufSeqW, AddrBuf));
+ return VERR_INVALID_STATE;
+ }
+
+ /*
+ * Read the buffer.
+ */
+ char *pchMsgBuf = (char *)RTMemAlloc(cbMsgBuf);
+ if (!pchMsgBuf)
+ {
+ Log(("dbgDiggerFreeBsdIDmsg_QueryKernelLog: Failed to allocate %#x bytes of memory for the log buffer\n",
+ cbMsgBuf));
+ return VERR_INVALID_STATE;
+ }
+ rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrMsgBuf, AddrBuf),
+ pchMsgBuf, cbMsgBuf);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Copy it out raw.
+ */
+ uint32_t offDst = 0;
+ if (uMsgBufSeqR < uMsgBufSeqW)
+ {
+ /* Single chunk between the read and write offsets. */
+ uint32_t cbToCopy = uMsgBufSeqW - uMsgBufSeqR;
+ if (cbToCopy < cbBuf)
+ {
+ memcpy(pszBuf, &pchMsgBuf[uMsgBufSeqR], cbToCopy);
+ pszBuf[cbToCopy] = '\0';
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ if (cbBuf)
+ {
+ memcpy(pszBuf, &pchMsgBuf[uMsgBufSeqR], cbBuf - 1);
+ pszBuf[cbBuf - 1] = '\0';
+ }
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ offDst = cbToCopy + 1;
+ }
+ else
+ {
+ /* Two chunks, read offset to end, start to write offset. */
+ uint32_t cbFirst = cbMsgBuf - uMsgBufSeqR;
+ uint32_t cbSecond = uMsgBufSeqW;
+ if (cbFirst + cbSecond < cbBuf)
+ {
+ memcpy(pszBuf, &pchMsgBuf[uMsgBufSeqR], cbFirst);
+ memcpy(&pszBuf[cbFirst], pchMsgBuf, cbSecond);
+ offDst = cbFirst + cbSecond;
+ pszBuf[offDst++] = '\0';
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ offDst = cbFirst + cbSecond + 1;
+ if (cbFirst < cbBuf)
+ {
+ memcpy(pszBuf, &pchMsgBuf[uMsgBufSeqR], cbFirst);
+ memcpy(&pszBuf[cbFirst], pchMsgBuf, cbBuf - cbFirst);
+ pszBuf[cbBuf - 1] = '\0';
+ }
+ else if (cbBuf)
+ {
+ memcpy(pszBuf, &pchMsgBuf[uMsgBufSeqR], cbBuf - 1);
+ pszBuf[cbBuf - 1] = '\0';
+ }
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+
+ if (pcbActual)
+ *pcbActual = offDst;
+ }
+ else
+ Log(("dbgDiggerFreeBsdIDmsg_QueryKernelLog: Error reading %#x bytes at %RGv: %Rrc\n", cbBuf, AddrBuf, rc));
+ RTMemFree(pchMsgBuf);
+ }
+ else
+ LogFlowFunc(("Failed to read message buffer header: %Rrc\n", rc));
+ }
+
+ return rc;
+}
+
+
+/**
+ * @copydoc DBGFOSREG::pfnStackUnwindAssist
+ */
+static DECLCALLBACK(int) dbgDiggerFreeBsdStackUnwindAssist(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData, VMCPUID idCpu,
+ PDBGFSTACKFRAME pFrame, PRTDBGUNWINDSTATE pState,
+ PCCPUMCTX pInitialCtx, RTDBGAS hAs, uint64_t *puScratch)
+{
+ RT_NOREF(pUVM, pVMM, pvData, idCpu, pFrame, pState, pInitialCtx, hAs, puScratch);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @copydoc DBGFOSREG::pfnQueryInterface
+ */
+static DECLCALLBACK(void *) dbgDiggerFreeBsdQueryInterface(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData, DBGFOSINTERFACE enmIf)
+{
+ PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
+ RT_NOREF(pUVM, pVMM);
+
+ switch (enmIf)
+ {
+ case DBGFOSINTERFACE_DMESG:
+ return &pThis->IDmesg;
+
+ default:
+ return NULL;
+ }
+}
+
+
+/**
+ * @copydoc DBGFOSREG::pfnQueryVersion
+ */
+static DECLCALLBACK(int) dbgDiggerFreeBsdQueryVersion(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData,
+ char *pszVersion, size_t cchVersion)
+{
+ PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
+ Assert(pThis->fValid); RT_NOREF(pThis);
+
+ RTDBGSYMBOL SymInfo;
+ int rc = pVMM->pfnDBGFR3AsSymbolByName(pUVM, DBGF_AS_KERNEL, "kernel!version", &SymInfo, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ DBGFADDRESS AddrVersion;
+ pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrVersion, SymInfo.Value);
+
+ rc = pVMM->pfnDBGFR3MemReadString(pUVM, 0, &AddrVersion, pszVersion, cchVersion);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszEnd = RTStrEnd(pszVersion, cchVersion);
+ AssertReturn(pszEnd, VERR_BUFFER_OVERFLOW);
+ while ( pszEnd > pszVersion
+ && RT_C_IS_SPACE(pszEnd[-1]))
+ pszEnd--;
+ *pszEnd = '\0';
+ }
+ else
+ RTStrPrintf(pszVersion, cchVersion, "DBGFR3MemReadString -> %Rrc", rc);
+ }
+
+ return rc;
+}
+
+
+
+/**
+ * @copydoc DBGFOSREG::pfnTerm
+ */
+static DECLCALLBACK(void) dbgDiggerFreeBsdTerm(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
+{
+ PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
+ Assert(pThis->fValid);
+ RT_NOREF(pUVM, pVMM);
+
+ pThis->fValid = false;
+}
+
+
+/**
+ * @copydoc DBGFOSREG::pfnRefresh
+ */
+static DECLCALLBACK(int) dbgDiggerFreeBsdRefresh(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
+{
+ PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
+ NOREF(pThis);
+ Assert(pThis->fValid);
+
+ dbgDiggerFreeBsdTerm(pUVM, pVMM, pvData);
+ return dbgDiggerFreeBsdInit(pUVM, pVMM, pvData);
+}
+
+
+/**
+ * @copydoc DBGFOSREG::pfnInit
+ */
+static DECLCALLBACK(int) dbgDiggerFreeBsdInit(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
+{
+ PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
+ Assert(!pThis->fValid);
+
+ RT_NOREF1(pUVM);
+
+ dbgDiggerFreeBsdProcessKernelImage(pThis, pUVM, pVMM, "kernel");
+ pThis->fValid = true;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @copydoc DBGFOSREG::pfnProbe
+ */
+static DECLCALLBACK(bool) dbgDiggerFreeBsdProbe(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
+{
+ PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
+
+ /*
+ * Look for the magic ELF header near the known start addresses.
+ * If one is found look for the magic "/red/herring" string which is in the
+ * "interp" section not far away and then validate the start of the ELF header
+ * to be sure.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_au64FreeBsdKernelAddresses); i++)
+ {
+ static const uint8_t s_abNeedle[] = ELFMAG;
+ DBGFADDRESS KernelAddr;
+ pVMM->pfnDBGFR3AddrFromFlat(pUVM, &KernelAddr, g_au64FreeBsdKernelAddresses[i]);
+ DBGFADDRESS HitAddr;
+ uint32_t cbLeft = FBSD_MAX_KERNEL_SIZE;
+
+ while (cbLeft > X86_PAGE_4K_SIZE)
+ {
+ int rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &KernelAddr, cbLeft, 1,
+ s_abNeedle, sizeof(s_abNeedle) - 1, &HitAddr);
+ if (RT_FAILURE(rc))
+ break;
+
+ /*
+ * Look for the magic "/red/herring" near the header and verify the basic
+ * ELF header.
+ */
+ DBGFADDRESS HitAddrInterp;
+ rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, FBSD_MAX_INTERP_OFFSET, 1,
+ g_abNeedleInterp, sizeof(g_abNeedleInterp), &HitAddrInterp);
+ if (RT_SUCCESS(rc))
+ {
+ union
+ {
+ uint8_t ab[2 * X86_PAGE_4K_SIZE];
+ Elf32_Ehdr Hdr32;
+ Elf64_Ehdr Hdr64;
+ } ElfHdr;
+ AssertCompileMembersSameSizeAndOffset(Elf64_Ehdr, e_ident, Elf32_Ehdr, e_ident);
+ AssertCompileMembersSameSizeAndOffset(Elf64_Ehdr, e_type, Elf32_Ehdr, e_type);
+ AssertCompileMembersSameSizeAndOffset(Elf64_Ehdr, e_machine, Elf32_Ehdr, e_machine);
+ AssertCompileMembersSameSizeAndOffset(Elf64_Ehdr, e_version, Elf32_Ehdr, e_version);
+
+ rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &HitAddr, &ElfHdr.ab[0], X86_PAGE_4K_SIZE);
+ if (RT_SUCCESS(rc))
+ {
+ /* We verified the magic above already by scanning for it. */
+ if ( ( ElfHdr.Hdr32.e_ident[EI_CLASS] == ELFCLASS32
+ || ElfHdr.Hdr32.e_ident[EI_CLASS] == ELFCLASS64)
+ && ElfHdr.Hdr32.e_ident[EI_DATA] == ELFDATA2LSB
+ && ElfHdr.Hdr32.e_ident[EI_VERSION] == EV_CURRENT
+ && ElfHdr.Hdr32.e_ident[EI_OSABI] == ELFOSABI_FREEBSD
+ && ElfHdr.Hdr32.e_type == ET_EXEC
+ && ( ElfHdr.Hdr32.e_machine == EM_386
+ || ElfHdr.Hdr32.e_machine == EM_X86_64)
+ && ElfHdr.Hdr32.e_version == EV_CURRENT)
+ {
+ pThis->f64Bit = ElfHdr.Hdr32.e_ident[EI_CLASS] == ELFCLASS64;
+ pThis->AddrKernelElfStart = HitAddr;
+ pThis->AddrKernelInterp = HitAddrInterp;
+ pVMM->pfnDBGFR3AddrFromFlat(pUVM, &pThis->AddrKernelText, FBSD_UNION(pThis, &ElfHdr, e_entry));
+ LogFunc(("Found %s FreeBSD kernel at %RGv (.interp section at %RGv, .text section at %RGv)\n",
+ pThis->f64Bit ? "amd64" : "i386", pThis->AddrKernelElfStart.FlatPtr,
+ pThis->AddrKernelInterp.FlatPtr, pThis->AddrKernelText.FlatPtr));
+ return true;
+ }
+ }
+ }
+
+ /*
+ * Advance.
+ */
+ RTGCUINTPTR cbDistance = HitAddr.FlatPtr - KernelAddr.FlatPtr + sizeof(s_abNeedle) - 1;
+ if (RT_UNLIKELY(cbDistance >= cbLeft))
+ break;
+
+ cbLeft -= cbDistance;
+ pVMM->pfnDBGFR3AddrAdd(&KernelAddr, cbDistance);
+ }
+ }
+ return false;
+}
+
+
+/**
+ * @copydoc DBGFOSREG::pfnDestruct
+ */
+static DECLCALLBACK(void) dbgDiggerFreeBsdDestruct(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
+{
+ RT_NOREF(pUVM, pVMM, pvData);
+}
+
+
+/**
+ * @copydoc DBGFOSREG::pfnConstruct
+ */
+static DECLCALLBACK(int) dbgDiggerFreeBsdConstruct(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
+{
+ PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
+ RT_NOREF(pUVM, pVMM);
+
+ pThis->fValid = false;
+ pThis->f64Bit = false;
+ pThis->IDmesg.u32Magic = DBGFOSIDMESG_MAGIC;
+ pThis->IDmesg.pfnQueryKernelLog = dbgDiggerFreeBsdIDmsg_QueryKernelLog;
+ pThis->IDmesg.u32EndMagic = DBGFOSIDMESG_MAGIC;
+
+ return VINF_SUCCESS;
+}
+
+
+const DBGFOSREG g_DBGDiggerFreeBsd =
+{
+ /* .u32Magic = */ DBGFOSREG_MAGIC,
+ /* .fFlags = */ 0,
+ /* .cbData = */ sizeof(DBGDIGGERFBSD),
+ /* .szName = */ "FreeBSD",
+ /* .pfnConstruct = */ dbgDiggerFreeBsdConstruct,
+ /* .pfnDestruct = */ dbgDiggerFreeBsdDestruct,
+ /* .pfnProbe = */ dbgDiggerFreeBsdProbe,
+ /* .pfnInit = */ dbgDiggerFreeBsdInit,
+ /* .pfnRefresh = */ dbgDiggerFreeBsdRefresh,
+ /* .pfnTerm = */ dbgDiggerFreeBsdTerm,
+ /* .pfnQueryVersion = */ dbgDiggerFreeBsdQueryVersion,
+ /* .pfnQueryInterface = */ dbgDiggerFreeBsdQueryInterface,
+ /* .pfnStackUnwindAssist = */ dbgDiggerFreeBsdStackUnwindAssist,
+ /* .u32EndMagic = */ DBGFOSREG_MAGIC
+};
+