text: /* $Id: DBGPlugInFreeBsd.cpp $ */
  /** @file
   * DBGPlugInFreeBsd - Debugger and Guest OS Digger Plugin For FreeBSD.
   */
  
  /*
   * Copyright (C) 2016-2017 Oracle Corporation
   *
   * This file is part of VirtualBox Open Source Edition (OSE), as
   * available from http://www.virtualbox.org. This file is free software;
   * you can redistribute it and/or modify it under the terms of the GNU
   * General Public License (GPL) as published by the Free Software
   * Foundation, in version 2 as it comes in the "COPYING" file of the
   * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
   * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
   */
  
  
  /*********************************************************************************************************************************
  *   Header Files                                                                                                                 *
  *********************************************************************************************************************************/
  #define LOG_GROUP LOG_GROUP_DBGF /// @todo add new log group.
  #include "DBGPlugIns.h"
  #include "DBGPlugInCommonELF.h"
  #include <VBox/vmm/dbgf.h>
  #include <iprt/asm.h>
  #include <iprt/ctype.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                                                                                                      *
  *********************************************************************************************************************************/
  
  /**
   * 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, 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   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, 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);
      int rc = DBGFR3MemRead(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 = DBGFR3MemRead(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)
                      {
                          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 = DBGFR3AsResolveAndRetain(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   pszName         The image name.
   */
  static void dbgDiggerFreeBsdProcessKernelImage(PDBGDIGGERFBSD pThis, PUVM pUVM, 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] - contiains 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
       *     [.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.
       * 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.
       */
  
      /* Calculate the start of the .hash section. */
      DBGFADDRESS AddrHashStart = pThis->AddrKernelInterp;
      DBGFR3AddrAdd(&AddrHashStart, sizeof(g_abNeedleInterp));
      AddrHashStart.FlatPtr = RT_ALIGN_GCPT(AddrHashStart.FlatPtr, pThis->f64Bit ? 8 : 4, RTGCUINTPTR);
      uint32_t au32Counters[2];
      int rc = DBGFR3MemRead(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;
  
              DBGFR3AddrAdd(&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 = DBGFR3MemRead(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)
                          {
                              DBGFR3AddrAdd(&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, 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. */
                  DBGFR3AddrAdd(&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));
      }
  }
  
  
  /**
   * @interface_method_impl{DBGFOSIDMESG,pfnQueryKernelLog}
   */
  static DECLCALLBACK(int) dbgDiggerFreeBsdIDmsg_QueryKernelLog(PDBGFOSIDMESG pThis, PUVM pUVM, uint32_t fFlags, uint32_t cMessages,
                                                                char *pszBuf, size_t cbBuf, size_t *pcbActual)
  {
      RT_NOREF1(fFlags);
      PDBGDIGGERFBSD pData = RT_FROM_MEMBER(pThis, DBGDIGGERFBSD, IDmesg);
  
      if (cMessages < 1)
          return VERR_INVALID_PARAMETER;
  
      /* Resolve the message buffer address from the msgbufp symbol. */
      RTDBGSYMBOL SymInfo;
      int rc = DBGFR3AsSymbolByName(pUVM, DBGF_AS_KERNEL, "kernel!msgbufp", &SymInfo, NULL);
      if (RT_SUCCESS(rc))
      {
          DBGFADDRESS AddrMsgBuf;
  
          /* Read the message buffer pointer. */
          RTGCPTR     GCPtrMsgBufP = 0;
          rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(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 = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(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 = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(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::pfnQueryInterface
   */
  static DECLCALLBACK(void *) dbgDiggerFreeBsdQueryInterface(PUVM pUVM, void *pvData, DBGFOSINTERFACE enmIf)
  {
      RT_NOREF1(pUVM);
      PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
      switch (enmIf)
      {
          case DBGFOSINTERFACE_DMESG:
              return &pThis->IDmesg;
  
          default:
              return NULL;
      }
  }
  
  
  /**
   * @copydoc DBGFOSREG::pfnQueryVersion
   */
  static DECLCALLBACK(int)  dbgDiggerFreeBsdQueryVersion(PUVM pUVM, void *pvData, char *pszVersion, size_t cchVersion)
  {
      PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
      Assert(pThis->fValid); RT_NOREF(pThis);
  
      RTDBGSYMBOL SymInfo;
      int rc = DBGFR3AsSymbolByName(pUVM, DBGF_AS_KERNEL, "kernel!version", &SymInfo, NULL);
      if (RT_SUCCESS(rc))
      {
          DBGFADDRESS AddrVersion;
          DBGFR3AddrFromFlat(pUVM, &AddrVersion, SymInfo.Value);
  
          rc = DBGFR3MemReadString(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, void *pvData)
  {
      RT_NOREF1(pUVM);
      PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
      Assert(pThis->fValid);
  
      RT_NOREF1(pUVM);
  
      pThis->fValid = false;
  }
  
  
  /**
   * @copydoc DBGFOSREG::pfnRefresh
   */
  static DECLCALLBACK(int)  dbgDiggerFreeBsdRefresh(PUVM pUVM, void *pvData)
  {
      PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
      NOREF(pThis);
      Assert(pThis->fValid);
  
      dbgDiggerFreeBsdTerm(pUVM, pvData);
      return dbgDiggerFreeBsdInit(pUVM, pvData);
  }
  
  
  /**
   * @copydoc DBGFOSREG::pfnInit
   */
  static DECLCALLBACK(int)  dbgDiggerFreeBsdInit(PUVM pUVM, void *pvData)
  {
      PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
      Assert(!pThis->fValid);
  
      RT_NOREF1(pUVM);
  
      dbgDiggerFreeBsdProcessKernelImage(pThis, pUVM, "kernel");
      pThis->fValid = true;
      return VINF_SUCCESS;
  }
  
  
  /**
   * @copydoc DBGFOSREG::pfnProbe
   */
  static DECLCALLBACK(bool)  dbgDiggerFreeBsdProbe(PUVM pUVM, 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;
          DBGFR3AddrFromFlat(pUVM, &KernelAddr, g_au64FreeBsdKernelAddresses[i]);
          DBGFADDRESS HitAddr;
          uint32_t    cbLeft  = FBSD_MAX_KERNEL_SIZE;
  
          while (cbLeft > X86_PAGE_4K_SIZE)
          {
              int rc = DBGFR3MemScan(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 = DBGFR3MemScan(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 = DBGFR3MemRead(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;
                          pThis->AddrKernelText.FlatPtr = 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;
              DBGFR3AddrAdd(&KernelAddr, cbDistance);
          }
      }
      return false;
  }
  
  
  /**
   * @copydoc DBGFOSREG::pfnDestruct
   */
  static DECLCALLBACK(void)  dbgDiggerFreeBsdDestruct(PUVM pUVM, void *pvData)
  {
      RT_NOREF2(pUVM, pvData);
  }
  
  
  /**
   * @copydoc DBGFOSREG::pfnConstruct
   */
  static DECLCALLBACK(int)  dbgDiggerFreeBsdConstruct(PUVM pUVM, void *pvData)
  {
      RT_NOREF1(pUVM);
      PDBGDIGGERFBSD pThis = (PDBGDIGGERFBSD)pvData;
  
      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,
      /* .u32EndMagic = */        DBGFOSREG_MAGIC
  };
  
  