VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGPlugInLinux.cpp@ 93718

Last change on this file since 93718 was 93470, checked in by vboxsync, 3 years ago

DbgPlugInDiggers,VMM,Main: Refactored the diggers and related interfaces to work via the VMM function table. Removed non-working tstVBoxDbg (needs proper COM now). bugref:10072

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 123.5 KB
Line 
1/* $Id: DBGPlugInLinux.cpp 93470 2022-01-27 23:51:28Z vboxsync $ */
2/** @file
3 * DBGPlugInLinux - Debugger and Guest OS Digger Plugin For Linux.
4 */
5
6/*
7 * Copyright (C) 2008-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGF /// @todo add new log group.
23#include "DBGPlugIns.h"
24#include "DBGPlugInCommonELF.h"
25#include <VBox/vmm/vmmr3vtable.h>
26#include <VBox/dis.h>
27#include <iprt/ctype.h>
28#include <iprt/file.h>
29#include <iprt/err.h>
30#include <iprt/mem.h>
31#include <iprt/stream.h>
32#include <iprt/string.h>
33#include <iprt/vfs.h>
34#include <iprt/zip.h>
35
36
37/*********************************************************************************************************************************
38* Structures and Typedefs *
39*********************************************************************************************************************************/
40
41/** @name InternalLinux structures
42 * @{ */
43
44
45/** @} */
46
47
48/**
49 * Config item type.
50 */
51typedef enum DBGDIGGERLINUXCFGITEMTYPE
52{
53 /** Invalid type. */
54 DBGDIGGERLINUXCFGITEMTYPE_INVALID = 0,
55 /** String. */
56 DBGDIGGERLINUXCFGITEMTYPE_STRING,
57 /** Number. */
58 DBGDIGGERLINUXCFGITEMTYPE_NUMBER,
59 /** Flag whether this feature is included in the
60 * kernel or as a module. */
61 DBGDIGGERLINUXCFGITEMTYPE_FLAG
62} DBGDIGGERLINUXCFGITEMTYPE;
63
64/**
65 * Item in the config database.
66 */
67typedef struct DBGDIGGERLINUXCFGITEM
68{
69 /** String space core. */
70 RTSTRSPACECORE Core;
71 /** Config item type. */
72 DBGDIGGERLINUXCFGITEMTYPE enmType;
73 /** Data based on the type. */
74 union
75 {
76 /** Number. */
77 int64_t i64Num;
78 /** Flag. */
79 bool fModule;
80 /** String - variable in size. */
81 char aszString[1];
82 } u;
83} DBGDIGGERLINUXCFGITEM;
84/** Pointer to a config database item. */
85typedef DBGDIGGERLINUXCFGITEM *PDBGDIGGERLINUXCFGITEM;
86/** Pointer to a const config database item. */
87typedef const DBGDIGGERLINUXCFGITEM *PCDBGDIGGERLINUXCFGITEM;
88
89/**
90 * Linux guest OS digger instance data.
91 */
92typedef struct DBGDIGGERLINUX
93{
94 /** Whether the information is valid or not.
95 * (For fending off illegal interface method calls.) */
96 bool fValid;
97 /** Set if 64-bit, clear if 32-bit. */
98 bool f64Bit;
99 /** Set if the kallsyms table uses relative addressing, clear
100 * if absolute addresses are used. */
101 bool fRelKrnlAddr;
102 /** The relative base when kernel symbols use offsets rather than
103 * absolute addresses. */
104 RTGCUINTPTR uKernelRelativeBase;
105 /** The guest kernel version used for version comparisons. */
106 uint32_t uKrnlVer;
107 /** The guest kernel major version. */
108 uint32_t uKrnlVerMaj;
109 /** The guest kernel minor version. */
110 uint32_t uKrnlVerMin;
111 /** The guest kernel build version. */
112 uint32_t uKrnlVerBld;
113
114 /** The address of the linux banner.
115 * This is set during probing. */
116 DBGFADDRESS AddrLinuxBanner;
117 /** Kernel base address.
118 * This is set during probing, refined during kallsyms parsing. */
119 DBGFADDRESS AddrKernelBase;
120 /** The kernel size. */
121 uint32_t cbKernel;
122
123 /** The number of kernel symbols (kallsyms_num_syms).
124 * This is set during init. */
125 uint32_t cKernelSymbols;
126 /** The size of the kernel name table (sizeof(kallsyms_names)). */
127 uint32_t cbKernelNames;
128 /** Number of entries in the kernel_markers table. */
129 uint32_t cKernelNameMarkers;
130 /** The size of the kernel symbol token table. */
131 uint32_t cbKernelTokenTable;
132 /** The address of the encoded kernel symbol names (kallsyms_names). */
133 DBGFADDRESS AddrKernelNames;
134 /** The address of the kernel symbol addresses (kallsyms_addresses). */
135 DBGFADDRESS AddrKernelAddresses;
136 /** The address of the kernel symbol name markers (kallsyms_markers). */
137 DBGFADDRESS AddrKernelNameMarkers;
138 /** The address of the kernel symbol token table (kallsyms_token_table). */
139 DBGFADDRESS AddrKernelTokenTable;
140 /** The address of the kernel symbol token index table (kallsyms_token_index). */
141 DBGFADDRESS AddrKernelTokenIndex;
142
143 /** The kernel message log interface. */
144 DBGFOSIDMESG IDmesg;
145
146 /** The config database root. */
147 RTSTRSPACE hCfgDb;
148} DBGDIGGERLINUX;
149/** Pointer to the linux guest OS digger instance data. */
150typedef DBGDIGGERLINUX *PDBGDIGGERLINUX;
151
152
153/**
154 * The current printk_log structure.
155 */
156typedef struct LNXPRINTKHDR
157{
158 /** Monotonic timestamp. */
159 uint64_t nsTimestamp;
160 /** The total size of this message record. */
161 uint16_t cbTotal;
162 /** The size of the text part (immediately follows the header). */
163 uint16_t cbText;
164 /** The size of the optional dictionary part (follows the text). */
165 uint16_t cbDict;
166 /** The syslog facility number. */
167 uint8_t bFacility;
168 /** First 5 bits are internal flags, next 3 bits are log level. */
169 uint8_t fFlagsAndLevel;
170} LNXPRINTKHDR;
171AssertCompileSize(LNXPRINTKHDR, 2*sizeof(uint64_t));
172/** Pointer to linux printk_log header. */
173typedef LNXPRINTKHDR *PLNXPRINTKHDR;
174/** Pointer to linux const printk_log header. */
175typedef LNXPRINTKHDR const *PCLNXPRINTKHDR;
176
177
178/*********************************************************************************************************************************
179* Defined Constants And Macros *
180*********************************************************************************************************************************/
181/** First kernel map address for 32bit Linux hosts (__START_KERNEL_map). */
182#define LNX32_KERNEL_ADDRESS_START UINT32_C(0xc0000000)
183/** First kernel map address for 64bit Linux hosts (__START_KERNEL_map). */
184#define LNX64_KERNEL_ADDRESS_START UINT64_C(0xffffffff80000000)
185/** Validates a 32-bit linux kernel address */
186#define LNX32_VALID_ADDRESS(Addr) ((Addr) > UINT32_C(0x80000000) && (Addr) < UINT32_C(0xfffff000))
187/** Validates a 64-bit linux kernel address */
188#define LNX64_VALID_ADDRESS(Addr) ((Addr) > UINT64_C(0xffff800000000000) && (Addr) < UINT64_C(0xfffffffffffff000))
189
190/** The max kernel size. */
191#define LNX_MAX_KERNEL_SIZE UINT32_C(0x0f000000)
192/** Maximum kernel log buffer size. */
193#define LNX_MAX_KERNEL_LOG_SIZE (16 * _1M)
194
195/** The maximum size we expect for kallsyms_names. */
196#define LNX_MAX_KALLSYMS_NAMES_SIZE UINT32_C(0x200000)
197/** The maximum size we expect for kallsyms_token_table. */
198#define LNX_MAX_KALLSYMS_TOKEN_TABLE_SIZE UINT32_C(0x10000)
199/** The minimum number of symbols we expect in kallsyms_num_syms. */
200#define LNX_MIN_KALLSYMS_SYMBOLS UINT32_C(2048)
201/** The maximum number of symbols we expect in kallsyms_num_syms. */
202#define LNX_MAX_KALLSYMS_SYMBOLS UINT32_C(1048576)
203/** The min length an encoded symbol in kallsyms_names is expected to have. */
204#define LNX_MIN_KALLSYMS_ENC_LENGTH UINT8_C(1)
205/** The max length an encoded symbol in kallsyms_names is expected to have.
206 * @todo check real life here. */
207#define LNX_MAX_KALLSYMS_ENC_LENGTH UINT8_C(28)
208/** The approximate maximum length of a string token. */
209#define LNX_MAX_KALLSYMS_TOKEN_LEN UINT16_C(32)
210/** Maximum compressed config size expected. */
211#define LNX_MAX_COMPRESSED_CFG_SIZE _1M
212
213/** Module tag for linux ('linuxmod' on little endian ASCII systems). */
214#define DIG_LNX_MOD_TAG UINT64_C(0x545f5d78758e898c)
215/** Macro for building a Linux kernel version which can be used for comparisons. */
216#define LNX_MK_VER(major, minor, build) (((major) << 22) | ((minor) << 12) | (build))
217
218
219/*********************************************************************************************************************************
220* Internal Functions *
221*********************************************************************************************************************************/
222static DECLCALLBACK(int) dbgDiggerLinuxInit(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData);
223
224
225/*********************************************************************************************************************************
226* Global Variables *
227*********************************************************************************************************************************/
228/** Table of common linux kernel addresses. */
229static uint64_t g_au64LnxKernelAddresses[] =
230{
231 UINT64_C(0xc0100000),
232 UINT64_C(0x90100000),
233 UINT64_C(0xffffffff80200000)
234};
235
236static const uint8_t g_abLinuxVersion[] = "Linux version ";
237/** The needle for searching for the kernel log area (the value is observed in pretty much all 32bit and 64bit x86 kernels).
238 * This needle should appear only once in the memory due to the address being filled in by a format string. */
239static const uint8_t g_abKrnlLogNeedle[] = "BIOS-e820: [mem 0x0000000000000000";
240
241
242/**
243 * Tries to resolve the kernel log buffer start and end by searching for needle.
244 *
245 * @returns VBox status code.
246 * @param pThis The Linux digger data.
247 * @param pUVM The VM handle.
248 * @param pVMM The VMM function table.
249 * @param pGCPtrLogBuf Where to store the start of the kernel log buffer on success.
250 * @param pcbLogBuf Where to store the size of the kernel log buffer on success.
251 */
252static int dbgDiggerLinuxKrnlLogBufFindByNeedle(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
253 RTGCPTR *pGCPtrLogBuf, uint32_t *pcbLogBuf)
254{
255 int rc = VINF_SUCCESS;
256
257 /* Try to find the needle, it should be very early in the kernel log buffer. */
258 DBGFADDRESS AddrScan;
259 DBGFADDRESS AddrHit;
260 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrScan, pThis->f64Bit ? LNX64_KERNEL_ADDRESS_START : LNX32_KERNEL_ADDRESS_START);
261
262 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &AddrScan, ~(RTGCUINTPTR)0, 1 /*uAlign*/,
263 g_abKrnlLogNeedle, sizeof(g_abKrnlLogNeedle) - 1, &AddrHit);
264 if (RT_SUCCESS(rc))
265 {
266 uint32_t cbLogBuf = 0;
267 uint64_t tsLastNs = 0;
268 DBGFADDRESS AddrCur;
269
270 pVMM->pfnDBGFR3AddrSub(&AddrHit, sizeof(LNXPRINTKHDR));
271 AddrCur = AddrHit;
272
273 /* Try to find the end of the kernel log buffer. */
274 for (;;)
275 {
276 if (cbLogBuf >= LNX_MAX_KERNEL_LOG_SIZE)
277 break;
278
279 LNXPRINTKHDR Hdr;
280 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrCur, &Hdr, sizeof(Hdr));
281 if (RT_SUCCESS(rc))
282 {
283 uint32_t const cbLogAlign = 4;
284
285 /*
286 * If the header does not look valid anymore we stop.
287 * Timestamps are monotonically increasing.
288 */
289 if ( !Hdr.cbTotal /* Zero entry size means there is no record anymore, doesn't make sense to look futher. */
290 || Hdr.cbText + Hdr.cbDict + sizeof(Hdr) > Hdr.cbTotal
291 || (Hdr.cbTotal & (cbLogAlign - 1)) != 0
292 || tsLastNs > Hdr.nsTimestamp)
293 break;
294
295 /** @todo Maybe read text part and verify it is all ASCII. */
296
297 cbLogBuf += Hdr.cbTotal;
298 pVMM->pfnDBGFR3AddrAdd(&AddrCur, Hdr.cbTotal);
299 }
300
301 if (RT_FAILURE(rc))
302 break;
303 }
304
305 /** @todo Go back to find the start address of the kernel log (or we loose potential kernel log messages). */
306
307 if ( RT_SUCCESS(rc)
308 && cbLogBuf)
309 {
310 /* Align log buffer size to a power of two. */
311 uint32_t idxBitLast = ASMBitLastSetU32(cbLogBuf);
312 idxBitLast--; /* There is at least one bit set, see check above. */
313
314 if (cbLogBuf & (RT_BIT_32(idxBitLast) - 1))
315 idxBitLast++;
316
317 *pGCPtrLogBuf = AddrHit.FlatPtr;
318 *pcbLogBuf = RT_MIN(RT_BIT_32(idxBitLast), LNX_MAX_KERNEL_LOG_SIZE);
319 }
320 else if (RT_SUCCESS(rc))
321 rc = VERR_NOT_FOUND;
322 }
323
324 return rc;
325}
326
327
328/**
329 * Converts a given offset into an absolute address if relative kernel offsets are used for
330 * kallsyms.
331 *
332 * @returns The absolute kernel address.
333 * @param pThis The Linux digger data.
334 * @param uOffset The offset to convert.
335 */
336DECLINLINE(RTGCUINTPTR) dbgDiggerLinuxConvOffsetToAddr(PDBGDIGGERLINUX pThis, int32_t uOffset)
337{
338 RTGCUINTPTR uAddr;
339
340 /*
341 * How the absolute address is calculated from the offset depends on the
342 * CONFIG_KALLSYMS_ABSOLUTE_PERCPU config which is only set for 64bit
343 * SMP kernels (we assume that all 64bit kernels always have SMP enabled too).
344 */
345 if (pThis->f64Bit)
346 {
347 if (uOffset >= 0)
348 uAddr = uOffset;
349 else
350 uAddr = pThis->uKernelRelativeBase - 1 - uOffset;
351 }
352 else
353 uAddr = pThis->uKernelRelativeBase + (uint32_t)uOffset;
354
355 return uAddr;
356}
357
358/**
359 * Disassembles a simple getter returning the value for it.
360 *
361 * @returns VBox status code.
362 * @param pThis The Linux digger data.
363 * @param pUVM The VM handle.
364 * @param pVMM The VMM function table.
365 * @param hMod The module to use.
366 * @param pszSymbol The symbol of the getter.
367 * @param pvVal Where to store the value on success.
368 * @param cbVal Size of the value in bytes.
369 */
370static int dbgDiggerLinuxDisassembleSimpleGetter(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, RTDBGMOD hMod,
371 const char *pszSymbol, void *pvVal, uint32_t cbVal)
372{
373 int rc = VINF_SUCCESS;
374
375 RTDBGSYMBOL SymInfo;
376 rc = RTDbgModSymbolByName(hMod, pszSymbol, &SymInfo);
377 if (RT_SUCCESS(rc))
378 {
379 /*
380 * Do the diassembling. Disassemble until a ret instruction is encountered
381 * or a limit is reached (don't want to disassemble for too long as the getter
382 * should be short).
383 * push and pop instructions are skipped as well as any mov instructions not
384 * touching the rax or eax register (depending on the size of the value).
385 */
386 unsigned cInstrDisassembled = 0;
387 uint32_t offInstr = 0;
388 bool fRet = false;
389 DISSTATE DisState;
390 RT_ZERO(DisState);
391
392 do
393 {
394 DBGFADDRESS Addr;
395 RTGCPTR GCPtrCur = (RTGCPTR)SymInfo.Value + pThis->AddrKernelBase.FlatPtr + offInstr;
396 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, GCPtrCur);
397
398 /* Prefetch the instruction. */
399 uint8_t abInstr[32];
400 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, &abInstr[0], sizeof(abInstr));
401 if (RT_SUCCESS(rc))
402 {
403 uint32_t cbInstr = 0;
404
405 rc = DISInstr(&abInstr[0], pThis->f64Bit ? DISCPUMODE_64BIT : DISCPUMODE_32BIT, &DisState, &cbInstr);
406 if (RT_SUCCESS(rc))
407 {
408 switch (DisState.pCurInstr->uOpcode)
409 {
410 case OP_PUSH:
411 case OP_POP:
412 case OP_NOP:
413 case OP_LEA:
414 break;
415 case OP_RETN:
416 /* Getter returned, abort disassembling. */
417 fRet = true;
418 break;
419 case OP_MOV:
420 /*
421 * Check that the destination is either rax or eax depending on the
422 * value size.
423 *
424 * Param1 is the destination and Param2 the source.
425 */
426 if ( ( ( (DisState.Param1.fUse & (DISUSE_BASE | DISUSE_REG_GEN32))
427 && cbVal == sizeof(uint32_t))
428 || ( (DisState.Param1.fUse & (DISUSE_BASE | DISUSE_REG_GEN64))
429 && cbVal == sizeof(uint64_t)))
430 && DisState.Param1.Base.idxGenReg == DISGREG_RAX)
431 {
432 /* Parse the source. */
433 if (DisState.Param2.fUse & (DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE64))
434 memcpy(pvVal, &DisState.Param2.uValue, cbVal);
435 else if (DisState.Param2.fUse & (DISUSE_RIPDISPLACEMENT32|DISUSE_DISPLACEMENT32|DISUSE_DISPLACEMENT64))
436 {
437 RTGCPTR GCPtrVal = 0;
438
439 if (DisState.Param2.fUse & DISUSE_RIPDISPLACEMENT32)
440 GCPtrVal = GCPtrCur + DisState.Param2.uDisp.i32 + cbInstr;
441 else if (DisState.Param2.fUse & DISUSE_DISPLACEMENT32)
442 GCPtrVal = (RTGCPTR)DisState.Param2.uDisp.u32;
443 else if (DisState.Param2.fUse & DISUSE_DISPLACEMENT64)
444 GCPtrVal = (RTGCPTR)DisState.Param2.uDisp.u64;
445 else
446 AssertMsgFailedBreakStmt(("Invalid displacement\n"), rc = VERR_INVALID_STATE);
447
448 DBGFADDRESS AddrVal;
449 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
450 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrVal, GCPtrVal),
451 pvVal, cbVal);
452 }
453 }
454 break;
455 default:
456 /* All other instructions will cause an error for now (playing safe here). */
457 rc = VERR_INVALID_PARAMETER;
458 break;
459 }
460 cInstrDisassembled++;
461 offInstr += cbInstr;
462 }
463 }
464 } while ( RT_SUCCESS(rc)
465 && cInstrDisassembled < 20
466 && !fRet);
467 }
468
469 return rc;
470}
471
472/**
473 * Try to get at the log buffer starting address and size by disassembling emit_log_char.
474 *
475 * @returns VBox status code.
476 * @param pThis The Linux digger data.
477 * @param pUVM The VM handle.
478 * @param pVMM The VMM function table.
479 * @param hMod The module to use.
480 * @param pGCPtrLogBuf Where to store the log buffer pointer on success.
481 * @param pcbLogBuf Where to store the size of the log buffer on success.
482 */
483static int dbgDiggerLinuxQueryAsciiLogBufferPtrs(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, RTDBGMOD hMod,
484 RTGCPTR *pGCPtrLogBuf, uint32_t *pcbLogBuf)
485{
486 int rc = VINF_SUCCESS;
487
488 /**
489 * We disassemble emit_log_char to get at the log buffer address and size.
490 * This is used in case the symbols are not exported in kallsyms.
491 *
492 * This is what it typically looks like:
493 * vmlinux!emit_log_char:
494 * %00000000c01204a1 56 push esi
495 * %00000000c01204a2 8b 35 d0 1c 34 c0 mov esi, dword [0c0341cd0h]
496 * %00000000c01204a8 53 push ebx
497 * %00000000c01204a9 8b 1d 74 3b 3e c0 mov ebx, dword [0c03e3b74h]
498 * %00000000c01204af 8b 0d d8 1c 34 c0 mov ecx, dword [0c0341cd8h]
499 * %00000000c01204b5 8d 56 ff lea edx, [esi-001h]
500 * %00000000c01204b8 21 da and edx, ebx
501 * %00000000c01204ba 88 04 11 mov byte [ecx+edx], al
502 * %00000000c01204bd 8d 53 01 lea edx, [ebx+001h]
503 * %00000000c01204c0 89 d0 mov eax, edx
504 * [...]
505 */
506 RTDBGSYMBOL SymInfo;
507 rc = RTDbgModSymbolByName(hMod, "emit_log_char", &SymInfo);
508 if (RT_SUCCESS(rc))
509 {
510 /*
511 * Do the diassembling. Disassemble until a ret instruction is encountered
512 * or a limit is reached (don't want to disassemble for too long as the getter
513 * should be short). Certain instructions found are ignored (push, nop, etc.).
514 */
515 unsigned cInstrDisassembled = 0;
516 uint32_t offInstr = 0;
517 bool fRet = false;
518 DISSTATE DisState;
519 unsigned cAddressesUsed = 0;
520 struct { size_t cb; RTGCPTR GCPtrOrigSrc; } aAddresses[5];
521 RT_ZERO(DisState);
522 RT_ZERO(aAddresses);
523
524 do
525 {
526 DBGFADDRESS Addr;
527 RTGCPTR GCPtrCur = (RTGCPTR)SymInfo.Value + pThis->AddrKernelBase.FlatPtr + offInstr;
528 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, GCPtrCur);
529
530 /* Prefetch the instruction. */
531 uint8_t abInstr[32];
532 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, &abInstr[0], sizeof(abInstr));
533 if (RT_SUCCESS(rc))
534 {
535 uint32_t cbInstr = 0;
536
537 rc = DISInstr(&abInstr[0], pThis->f64Bit ? DISCPUMODE_64BIT : DISCPUMODE_32BIT, &DisState, &cbInstr);
538 if (RT_SUCCESS(rc))
539 {
540 switch (DisState.pCurInstr->uOpcode)
541 {
542 case OP_PUSH:
543 case OP_POP:
544 case OP_NOP:
545 case OP_LEA:
546 case OP_AND:
547 case OP_CBW:
548 case OP_DEC:
549 break;
550 case OP_RETN:
551 /* emit_log_char returned, abort disassembling. */
552 rc = VERR_NOT_FOUND;
553 fRet = true;
554 break;
555 case OP_MOV:
556 case OP_MOVSXD:
557 /*
558 * If a mov is encountered writing to memory with al (or dil for amd64) being the source the
559 * character is stored and we can infer the base address and size of the log buffer from
560 * the source addresses.
561 */
562 if ( (DisState.Param2.fUse & DISUSE_REG_GEN8)
563 && ( (DisState.Param2.Base.idxGenReg == DISGREG_AL && !pThis->f64Bit)
564 || (DisState.Param2.Base.idxGenReg == DISGREG_DIL && pThis->f64Bit))
565 && DISUSE_IS_EFFECTIVE_ADDR(DisState.Param1.fUse))
566 {
567 RTGCPTR GCPtrLogBuf = 0;
568 uint32_t cbLogBuf = 0;
569
570 /*
571 * We can stop disassembling now and inspect all registers, look for a valid kernel address first.
572 * Only one of the accessed registers should hold a valid kernel address.
573 * For the log size look for the biggest non kernel address.
574 */
575 for (unsigned i = 0; i < cAddressesUsed; i++)
576 {
577 DBGFADDRESS AddrVal;
578 union { uint8_t abVal[8]; uint32_t u32Val; uint64_t u64Val; } Val;
579
580 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
581 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrVal,
582 aAddresses[i].GCPtrOrigSrc),
583 &Val.abVal[0], aAddresses[i].cb);
584 if (RT_SUCCESS(rc))
585 {
586 if (pThis->f64Bit && aAddresses[i].cb == sizeof(uint64_t))
587 {
588 if (LNX64_VALID_ADDRESS(Val.u64Val))
589 {
590 if (GCPtrLogBuf == 0)
591 GCPtrLogBuf = Val.u64Val;
592 else
593 {
594 rc = VERR_NOT_FOUND;
595 break;
596 }
597 }
598 }
599 else
600 {
601 AssertMsgBreakStmt(aAddresses[i].cb == sizeof(uint32_t),
602 ("Invalid value size\n"), rc = VERR_INVALID_STATE);
603
604 /* Might be a kernel address or a size indicator. */
605 if (!pThis->f64Bit && LNX32_VALID_ADDRESS(Val.u32Val))
606 {
607 if (GCPtrLogBuf == 0)
608 GCPtrLogBuf = Val.u32Val;
609 else
610 {
611 rc = VERR_NOT_FOUND;
612 break;
613 }
614 }
615 else
616 {
617 /*
618 * The highest value will be the log buffer because the other
619 * accessed variables are indexes into the buffer and hence
620 * always smaller than the size.
621 */
622 if (cbLogBuf < Val.u32Val)
623 cbLogBuf = Val.u32Val;
624 }
625 }
626 }
627 }
628
629 if ( RT_SUCCESS(rc)
630 && GCPtrLogBuf != 0
631 && cbLogBuf != 0)
632 {
633 *pGCPtrLogBuf = GCPtrLogBuf;
634 *pcbLogBuf = cbLogBuf;
635 }
636 else if (RT_SUCCESS(rc))
637 rc = VERR_NOT_FOUND;
638
639 fRet = true;
640 break;
641 }
642 else
643 {
644 /*
645 * In case of a memory to register move store the destination register index and the
646 * source address in the relation table for later processing.
647 */
648 if ( (DisState.Param1.fUse & (DISUSE_BASE | DISUSE_REG_GEN32 | DISUSE_REG_GEN64))
649 && (DisState.Param2.cb == sizeof(uint32_t) || DisState.Param2.cb == sizeof(uint64_t))
650 && (DisState.Param2.fUse & (DISUSE_RIPDISPLACEMENT32|DISUSE_DISPLACEMENT32|DISUSE_DISPLACEMENT64)))
651 {
652 RTGCPTR GCPtrVal = 0;
653
654 if (DisState.Param2.fUse & DISUSE_RIPDISPLACEMENT32)
655 GCPtrVal = GCPtrCur + DisState.Param2.uDisp.i32 + cbInstr;
656 else if (DisState.Param2.fUse & DISUSE_DISPLACEMENT32)
657 GCPtrVal = (RTGCPTR)DisState.Param2.uDisp.u32;
658 else if (DisState.Param2.fUse & DISUSE_DISPLACEMENT64)
659 GCPtrVal = (RTGCPTR)DisState.Param2.uDisp.u64;
660 else
661 AssertMsgFailedBreakStmt(("Invalid displacement\n"), rc = VERR_INVALID_STATE);
662
663 if (cAddressesUsed < RT_ELEMENTS(aAddresses))
664 {
665 /* movsxd reads always 32bits. */
666 if (DisState.pCurInstr->uOpcode == OP_MOVSXD)
667 aAddresses[cAddressesUsed].cb = sizeof(uint32_t);
668 else
669 aAddresses[cAddressesUsed].cb = DisState.Param2.cb;
670 aAddresses[cAddressesUsed].GCPtrOrigSrc = GCPtrVal;
671 cAddressesUsed++;
672 }
673 else
674 {
675 rc = VERR_INVALID_PARAMETER;
676 break;
677 }
678 }
679 }
680 break;
681 default:
682 /* All other instructions will cause an error for now (playing safe here). */
683 rc = VERR_INVALID_PARAMETER;
684 break;
685 }
686 cInstrDisassembled++;
687 offInstr += cbInstr;
688 }
689 }
690 } while ( RT_SUCCESS(rc)
691 && cInstrDisassembled < 20
692 && !fRet);
693 }
694
695 return rc;
696}
697
698/**
699 * Try to get at the log buffer starting address and size by disassembling some exposed helpers.
700 *
701 * @returns VBox status code.
702 * @param pThis The Linux digger data.
703 * @param pUVM The VM handle.
704 * @param pVMM The VMM function table.
705 * @param hMod The module to use.
706 * @param pGCPtrLogBuf Where to store the log buffer pointer on success.
707 * @param pcbLogBuf Where to store the size of the log buffer on success.
708 */
709static int dbgDiggerLinuxQueryLogBufferPtrs(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, RTDBGMOD hMod,
710 RTGCPTR *pGCPtrLogBuf, uint32_t *pcbLogBuf)
711{
712 int rc = VINF_SUCCESS;
713
714 struct { void *pvVar; uint32_t cbHost, cbGuest; const char *pszSymbol; } aSymbols[] =
715 {
716 { pGCPtrLogBuf, (uint32_t)sizeof(RTGCPTR), (uint32_t)(pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t)), "log_buf_addr_get" },
717 { pcbLogBuf, (uint32_t)sizeof(uint32_t), (uint32_t)sizeof(uint32_t), "log_buf_len_get" }
718 };
719 for (uint32_t i = 0; i < RT_ELEMENTS(aSymbols) && RT_SUCCESS(rc); i++)
720 {
721 RT_BZERO(aSymbols[i].pvVar, aSymbols[i].cbHost);
722 Assert(aSymbols[i].cbHost >= aSymbols[i].cbGuest);
723 rc = dbgDiggerLinuxDisassembleSimpleGetter(pThis, pUVM, pVMM, hMod, aSymbols[i].pszSymbol,
724 aSymbols[i].pvVar, aSymbols[i].cbGuest);
725 }
726
727 return rc;
728}
729
730/**
731 * Returns whether the log buffer is a simple ascii buffer or a record based implementation
732 * based on the kernel version found.
733 *
734 * @returns Flag whether the log buffer is the simple ascii buffer.
735 * @param pThis The Linux digger data.
736 * @param pUVM The user mode VM handle.
737 * @param pVMM The VMM function table.
738 */
739static bool dbgDiggerLinuxLogBufferIsAsciiBuffer(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM)
740{
741 char szTmp[128];
742 char const *pszVer = &szTmp[sizeof(g_abLinuxVersion) - 1];
743
744 RT_ZERO(szTmp);
745 int rc = pVMM->pfnDBGFR3MemReadString(pUVM, 0, &pThis->AddrLinuxBanner, szTmp, sizeof(szTmp) - 1);
746 if ( RT_SUCCESS(rc)
747 && RTStrVersionCompare(pszVer, "3.4") == -1)
748 return true;
749
750 return false;
751}
752
753/**
754 * Worker to get at the kernel log for pre 3.4 kernels where the log buffer was just a char buffer.
755 *
756 * @returns VBox status code.
757 * @param pThis The Linux digger data.
758 * @param pUVM The VM user mdoe handle.
759 * @param pVMM The VMM function table.
760 * @param hMod The debug module handle.
761 * @param fFlags Flags reserved for future use, MBZ.
762 * @param cMessages The number of messages to retrieve, counting from the
763 * end of the log (i.e. like tail), use UINT32_MAX for all.
764 * @param pszBuf The output buffer.
765 * @param cbBuf The buffer size.
766 * @param pcbActual Where to store the number of bytes actually returned,
767 * including zero terminator. On VERR_BUFFER_OVERFLOW this
768 * holds the necessary buffer size. Optional.
769 */
770static int dbgDiggerLinuxLogBufferQueryAscii(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, RTDBGMOD hMod,
771 uint32_t fFlags, uint32_t cMessages,
772 char *pszBuf, size_t cbBuf, size_t *pcbActual)
773{
774 RT_NOREF2(fFlags, cMessages);
775 int rc = VINF_SUCCESS;
776 RTGCPTR GCPtrLogBuf;
777 uint32_t cbLogBuf;
778
779 struct { void *pvVar; size_t cbHost, cbGuest; const char *pszSymbol; } aSymbols[] =
780 {
781 { &GCPtrLogBuf, sizeof(GCPtrLogBuf), pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t), "log_buf" },
782 { &cbLogBuf, sizeof(cbLogBuf), sizeof(cbLogBuf), "log_buf_len" },
783 };
784 for (uint32_t i = 0; i < RT_ELEMENTS(aSymbols); i++)
785 {
786 RTDBGSYMBOL SymInfo;
787 rc = RTDbgModSymbolByName(hMod, aSymbols[i].pszSymbol, &SymInfo);
788 if (RT_SUCCESS(rc))
789 {
790 RT_BZERO(aSymbols[i].pvVar, aSymbols[i].cbHost);
791 Assert(aSymbols[i].cbHost >= aSymbols[i].cbGuest);
792 DBGFADDRESS Addr;
793 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
794 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr,
795 (RTGCPTR)SymInfo.Value + pThis->AddrKernelBase.FlatPtr),
796 aSymbols[i].pvVar, aSymbols[i].cbGuest);
797 if (RT_SUCCESS(rc))
798 continue;
799 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: Reading '%s' at %RGv: %Rrc\n", aSymbols[i].pszSymbol, Addr.FlatPtr, rc));
800 }
801 else
802 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error looking up '%s': %Rrc\n", aSymbols[i].pszSymbol, rc));
803 rc = VERR_NOT_FOUND;
804 break;
805 }
806
807 /*
808 * Some kernels don't expose the variables in kallsyms so we have to try disassemble
809 * some public helpers to get at the addresses.
810 *
811 * @todo: Maybe cache those values so we don't have to do the heavy work every time?
812 */
813 if (rc == VERR_NOT_FOUND)
814 {
815 rc = dbgDiggerLinuxQueryAsciiLogBufferPtrs(pThis, pUVM, pVMM, hMod, &GCPtrLogBuf, &cbLogBuf);
816 if (RT_FAILURE(rc))
817 return rc;
818 }
819
820 /*
821 * Check if the values make sense.
822 */
823 if (pThis->f64Bit ? !LNX64_VALID_ADDRESS(GCPtrLogBuf) : !LNX32_VALID_ADDRESS(GCPtrLogBuf))
824 {
825 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf' value %RGv is not valid.\n", GCPtrLogBuf));
826 return VERR_NOT_FOUND;
827 }
828 if ( cbLogBuf < 4096
829 || !RT_IS_POWER_OF_TWO(cbLogBuf)
830 || cbLogBuf > 16*_1M)
831 {
832 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf_len' value %#x is not valid.\n", cbLogBuf));
833 return VERR_NOT_FOUND;
834 }
835
836 /*
837 * Read the whole log buffer.
838 */
839 uint8_t *pbLogBuf = (uint8_t *)RTMemAlloc(cbLogBuf);
840 if (!pbLogBuf)
841 {
842 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: Failed to allocate %#x bytes for log buffer\n", cbLogBuf));
843 return VERR_NO_MEMORY;
844 }
845 DBGFADDRESS Addr;
846 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, GCPtrLogBuf), pbLogBuf, cbLogBuf);
847 if (RT_FAILURE(rc))
848 {
849 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error reading %#x bytes of log buffer at %RGv: %Rrc\n",
850 cbLogBuf, Addr.FlatPtr, rc));
851 RTMemFree(pbLogBuf);
852 return VERR_NOT_FOUND;
853 }
854
855 /** @todo Try to parse where the single messages start to make use of cMessages. */
856 size_t cchLength = RTStrNLen((const char *)pbLogBuf, cbLogBuf);
857 memcpy(&pszBuf[0], pbLogBuf, RT_MIN(cbBuf, cchLength));
858
859 /* Done with the buffer. */
860 RTMemFree(pbLogBuf);
861
862 /* Set return size value. */
863 if (pcbActual)
864 *pcbActual = RT_MIN(cbBuf, cchLength);
865
866 return cbBuf <= cchLength ? VERR_BUFFER_OVERFLOW : VINF_SUCCESS;
867}
868
869
870/**
871 * Worker to process a given record based kernel log.
872 *
873 * @returns VBox status code.
874 * @param pThis The Linux digger data.
875 * @param pUVM The VM user mode handle.
876 * @param pVMM The VMM function table.
877 * @param GCPtrLogBuf Flat guest address of the start of the log buffer.
878 * @param cbLogBuf Power of two aligned size of the log buffer.
879 * @param idxFirst Index in the log bfufer of the first message.
880 * @param idxNext Index where to write hte next message in the log buffer.
881 * @param fFlags Flags reserved for future use, MBZ.
882 * @param cMessages The number of messages to retrieve, counting from the
883 * end of the log (i.e. like tail), use UINT32_MAX for all.
884 * @param pszBuf The output buffer.
885 * @param cbBuf The buffer size.
886 * @param pcbActual Where to store the number of bytes actually returned,
887 * including zero terminator. On VERR_BUFFER_OVERFLOW this
888 * holds the necessary buffer size. Optional.
889 */
890static int dbgDiggerLinuxKrnLogBufferProcess(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, RTGCPTR GCPtrLogBuf,
891 uint32_t cbLogBuf, uint32_t idxFirst, uint32_t idxNext,
892 uint32_t fFlags, uint32_t cMessages, char *pszBuf, size_t cbBuf,
893 size_t *pcbActual)
894{
895 RT_NOREF(fFlags);
896
897 /*
898 * Check if the values make sense.
899 */
900 if (pThis->f64Bit ? !LNX64_VALID_ADDRESS(GCPtrLogBuf) : !LNX32_VALID_ADDRESS(GCPtrLogBuf))
901 {
902 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf' value %RGv is not valid.\n", GCPtrLogBuf));
903 return VERR_NOT_FOUND;
904 }
905 if ( cbLogBuf < _4K
906 || !RT_IS_POWER_OF_TWO(cbLogBuf)
907 || cbLogBuf > LNX_MAX_KERNEL_LOG_SIZE)
908 {
909 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf_len' value %#x is not valid.\n", cbLogBuf));
910 return VERR_NOT_FOUND;
911 }
912 uint32_t const cbLogAlign = 4;
913 if ( idxFirst > cbLogBuf - sizeof(LNXPRINTKHDR)
914 || (idxFirst & (cbLogAlign - 1)) != 0)
915 {
916 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_first_idx' value %#x is not valid.\n", idxFirst));
917 return VERR_NOT_FOUND;
918 }
919 if ( idxNext > cbLogBuf - sizeof(LNXPRINTKHDR)
920 || (idxNext & (cbLogAlign - 1)) != 0)
921 {
922 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_next_idx' value %#x is not valid.\n", idxNext));
923 return VERR_NOT_FOUND;
924 }
925
926 /*
927 * Read the whole log buffer.
928 */
929 uint8_t *pbLogBuf = (uint8_t *)RTMemAlloc(cbLogBuf);
930 if (!pbLogBuf)
931 {
932 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: Failed to allocate %#x bytes for log buffer\n", cbLogBuf));
933 return VERR_NO_MEMORY;
934 }
935 DBGFADDRESS Addr;
936 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, GCPtrLogBuf), pbLogBuf, cbLogBuf);
937 if (RT_FAILURE(rc))
938 {
939 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error reading %#x bytes of log buffer at %RGv: %Rrc\n",
940 cbLogBuf, Addr.FlatPtr, rc));
941 RTMemFree(pbLogBuf);
942 return VERR_NOT_FOUND;
943 }
944
945 /*
946 * Count the messages in the buffer while doing some basic validation.
947 */
948 uint32_t const cbUsed = idxFirst == idxNext ? cbLogBuf /* could be empty... */
949 : idxFirst < idxNext ? idxNext - idxFirst : cbLogBuf - idxFirst + idxNext;
950 uint32_t cbLeft = cbUsed;
951 uint32_t offCur = idxFirst;
952 uint32_t cLogMsgs = 0;
953
954 while (cbLeft > 0)
955 {
956 PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
957 if (!pHdr->cbTotal)
958 {
959 /* Wrap around packet, most likely... */
960 if (cbLogBuf - offCur >= cbLeft)
961 break;
962 offCur = 0;
963 pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
964 }
965 if (RT_UNLIKELY( pHdr->cbTotal > cbLogBuf - sizeof(*pHdr) - offCur
966 || pHdr->cbTotal > cbLeft
967 || (pHdr->cbTotal & (cbLogAlign - 1)) != 0
968 || pHdr->cbTotal < (uint32_t)pHdr->cbText + (uint32_t)pHdr->cbDict + sizeof(*pHdr) ))
969 {
970 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: Invalid printk_log record at %#x: cbTotal=%#x cbText=%#x cbDict=%#x cbLogBuf=%#x cbLeft=%#x\n",
971 offCur, pHdr->cbTotal, pHdr->cbText, pHdr->cbDict, cbLogBuf, cbLeft));
972 break;
973 }
974
975 if (pHdr->cbText > 0)
976 cLogMsgs++;
977
978 /* next */
979 offCur += pHdr->cbTotal;
980 cbLeft -= pHdr->cbTotal;
981 }
982 if (!cLogMsgs)
983 {
984 RTMemFree(pbLogBuf);
985 return VERR_NOT_FOUND;
986 }
987
988 /*
989 * Copy the messages into the output buffer.
990 */
991 offCur = idxFirst;
992 cbLeft = cbUsed - cbLeft;
993
994 /* Skip messages that the caller doesn't want. */
995 if (cMessages < cLogMsgs)
996 {
997 uint32_t cToSkip = cLogMsgs - cMessages;
998 cLogMsgs -= cToSkip;
999
1000 while (cToSkip > 0)
1001 {
1002 PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
1003 if (!pHdr->cbTotal)
1004 {
1005 offCur = 0;
1006 pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
1007 }
1008 if (pHdr->cbText > 0)
1009 cToSkip--;
1010
1011 /* next */
1012 offCur += pHdr->cbTotal;
1013 cbLeft -= pHdr->cbTotal;
1014 }
1015 }
1016
1017 /* Now copy the messages. */
1018 size_t offDst = 0;
1019 while (cbLeft > 0)
1020 {
1021 PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
1022 if ( !pHdr->cbTotal
1023 || !cLogMsgs)
1024 {
1025 if (cbLogBuf - offCur >= cbLeft)
1026 break;
1027 offCur = 0;
1028 pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
1029 }
1030
1031 if (pHdr->cbText > 0)
1032 {
1033 char *pchText = (char *)(pHdr + 1);
1034 size_t cchText = RTStrNLen(pchText, pHdr->cbText);
1035 if (offDst + cchText < cbBuf)
1036 {
1037 memcpy(&pszBuf[offDst], pHdr + 1, cchText);
1038 pszBuf[offDst + cchText] = '\n';
1039 }
1040 else if (offDst < cbBuf)
1041 memcpy(&pszBuf[offDst], pHdr + 1, cbBuf - offDst);
1042 offDst += cchText + 1;
1043 }
1044
1045 /* next */
1046 offCur += pHdr->cbTotal;
1047 cbLeft -= pHdr->cbTotal;
1048 }
1049
1050 /* Done with the buffer. */
1051 RTMemFree(pbLogBuf);
1052
1053 /* Make sure we've reserved a char for the terminator. */
1054 if (!offDst)
1055 offDst = 1;
1056
1057 /* Set return size value. */
1058 if (pcbActual)
1059 *pcbActual = offDst;
1060
1061 if (offDst <= cbBuf)
1062 return VINF_SUCCESS;
1063 return VERR_BUFFER_OVERFLOW;
1064}
1065
1066
1067/**
1068 * Worker to get at the kernel log for post 3.4 kernels where the log buffer contains records.
1069 *
1070 * @returns VBox status code.
1071 * @param pThis The Linux digger data.
1072 * @param pUVM The VM user mdoe handle.
1073 * @param pVMM The VMM function table.
1074 * @param hMod The debug module handle.
1075 * @param fFlags Flags reserved for future use, MBZ.
1076 * @param cMessages The number of messages to retrieve, counting from the
1077 * end of the log (i.e. like tail), use UINT32_MAX for all.
1078 * @param pszBuf The output buffer.
1079 * @param cbBuf The buffer size.
1080 * @param pcbActual Where to store the number of bytes actually returned,
1081 * including zero terminator. On VERR_BUFFER_OVERFLOW this
1082 * holds the necessary buffer size. Optional.
1083 */
1084static int dbgDiggerLinuxLogBufferQueryRecords(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, RTDBGMOD hMod,
1085 uint32_t fFlags, uint32_t cMessages,
1086 char *pszBuf, size_t cbBuf, size_t *pcbActual)
1087{
1088 int rc = VINF_SUCCESS;
1089 RTGCPTR GCPtrLogBuf;
1090 uint32_t cbLogBuf;
1091 uint32_t idxFirst;
1092 uint32_t idxNext;
1093
1094 struct { void *pvVar; size_t cbHost, cbGuest; const char *pszSymbol; } aSymbols[] =
1095 {
1096 { &GCPtrLogBuf, sizeof(GCPtrLogBuf), pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t), "log_buf" },
1097 { &cbLogBuf, sizeof(cbLogBuf), sizeof(cbLogBuf), "log_buf_len" },
1098 { &idxFirst, sizeof(idxFirst), sizeof(idxFirst), "log_first_idx" },
1099 { &idxNext, sizeof(idxNext), sizeof(idxNext), "log_next_idx" },
1100 };
1101 for (uint32_t i = 0; i < RT_ELEMENTS(aSymbols); i++)
1102 {
1103 RTDBGSYMBOL SymInfo;
1104 rc = RTDbgModSymbolByName(hMod, aSymbols[i].pszSymbol, &SymInfo);
1105 if (RT_SUCCESS(rc))
1106 {
1107 RT_BZERO(aSymbols[i].pvVar, aSymbols[i].cbHost);
1108 Assert(aSymbols[i].cbHost >= aSymbols[i].cbGuest);
1109 DBGFADDRESS Addr;
1110 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1111 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr,
1112 (RTGCPTR)SymInfo.Value + pThis->AddrKernelBase.FlatPtr),
1113 aSymbols[i].pvVar, aSymbols[i].cbGuest);
1114 if (RT_SUCCESS(rc))
1115 continue;
1116 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: Reading '%s' at %RGv: %Rrc\n", aSymbols[i].pszSymbol, Addr.FlatPtr, rc));
1117 }
1118 else
1119 LogRel(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error looking up '%s': %Rrc\n", aSymbols[i].pszSymbol, rc));
1120 rc = VERR_NOT_FOUND;
1121 break;
1122 }
1123
1124 /*
1125 * Some kernels don't expose the variables in kallsyms so we have to try disassemble
1126 * some public helpers to get at the addresses.
1127 *
1128 * @todo: Maybe cache those values so we don't have to do the heavy work every time?
1129 */
1130 if (rc == VERR_NOT_FOUND)
1131 {
1132 idxFirst = 0;
1133 idxNext = 0;
1134 rc = dbgDiggerLinuxQueryLogBufferPtrs(pThis, pUVM, pVMM, hMod, &GCPtrLogBuf, &cbLogBuf);
1135 if (RT_FAILURE(rc))
1136 {
1137 /*
1138 * Last resort, scan for a known value which should appear only once in the kernel log buffer
1139 * and try to deduce the boundaries from there.
1140 */
1141 return dbgDiggerLinuxKrnlLogBufFindByNeedle(pThis, pUVM, pVMM, &GCPtrLogBuf, &cbLogBuf);
1142 }
1143 }
1144
1145 return dbgDiggerLinuxKrnLogBufferProcess(pThis, pUVM, pVMM, GCPtrLogBuf, cbLogBuf, idxFirst, idxNext,
1146 fFlags, cMessages, pszBuf, cbBuf, pcbActual);
1147}
1148
1149/**
1150 * @interface_method_impl{DBGFOSIDMESG,pfnQueryKernelLog}
1151 */
1152static DECLCALLBACK(int) dbgDiggerLinuxIDmsg_QueryKernelLog(PDBGFOSIDMESG pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, uint32_t fFlags,
1153 uint32_t cMessages, char *pszBuf, size_t cbBuf, size_t *pcbActual)
1154{
1155 PDBGDIGGERLINUX pData = RT_FROM_MEMBER(pThis, DBGDIGGERLINUX, IDmesg);
1156
1157 if (cMessages < 1)
1158 return VERR_INVALID_PARAMETER;
1159
1160 /*
1161 * Resolve the symbols we need and read their values.
1162 */
1163 RTDBGAS hAs = pVMM->pfnDBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
1164 RTDBGMOD hMod;
1165 int rc = RTDbgAsModuleByName(hAs, "vmlinux", 0, &hMod);
1166 RTDbgAsRelease(hAs);
1167
1168 size_t cbActual = 0;
1169 if (RT_SUCCESS(rc))
1170 {
1171 /*
1172 * Check whether the kernel log buffer is a simple char buffer or the newer
1173 * record based implementation.
1174 * The record based implementation was presumably introduced with kernel 3.4,
1175 * see: http://thread.gmane.org/gmane.linux.kernel/1284184
1176 */
1177 if (dbgDiggerLinuxLogBufferIsAsciiBuffer(pData, pUVM, pVMM))
1178 rc = dbgDiggerLinuxLogBufferQueryAscii(pData, pUVM, pVMM, hMod, fFlags, cMessages, pszBuf, cbBuf, &cbActual);
1179 else
1180 rc = dbgDiggerLinuxLogBufferQueryRecords(pData, pUVM, pVMM, hMod, fFlags, cMessages, pszBuf, cbBuf, &cbActual);
1181
1182 /* Release the module in any case. */
1183 RTDbgModRelease(hMod);
1184 }
1185 else
1186 {
1187 /*
1188 * For the record based kernel versions we have a last resort heuristic which doesn't
1189 * require any symbols, try that here.
1190 */
1191 if (!dbgDiggerLinuxLogBufferIsAsciiBuffer(pData, pUVM, pVMM))
1192 {
1193 RTGCPTR GCPtrLogBuf = 0;
1194 uint32_t cbLogBuf = 0;
1195
1196 rc = dbgDiggerLinuxKrnlLogBufFindByNeedle(pData, pUVM, pVMM, &GCPtrLogBuf, &cbLogBuf);
1197 if (RT_SUCCESS(rc))
1198 rc = dbgDiggerLinuxKrnLogBufferProcess(pData, pUVM, pVMM, GCPtrLogBuf, cbLogBuf, 0 /*idxFirst*/, 0 /*idxNext*/,
1199 fFlags, cMessages, pszBuf, cbBuf, &cbActual);
1200 }
1201 else
1202 rc = VERR_NOT_FOUND;
1203 }
1204
1205 if (RT_FAILURE(rc) && rc != VERR_BUFFER_OVERFLOW)
1206 return rc;
1207
1208 if (pcbActual)
1209 *pcbActual = cbActual;
1210
1211 /*
1212 * All VBox strings are UTF-8 and bad things may in theory happen if we
1213 * pass bad UTF-8 to code which assumes it's all valid. So, we enforce
1214 * UTF-8 upon the guest kernel messages here even if they (probably) have
1215 * no defined code set in reality.
1216 */
1217 if ( RT_SUCCESS(rc)
1218 && cbActual <= cbBuf)
1219 {
1220 pszBuf[cbActual - 1] = '\0';
1221 RTStrPurgeEncoding(pszBuf);
1222 return VINF_SUCCESS;
1223 }
1224
1225 if (cbBuf)
1226 {
1227 pszBuf[cbBuf - 1] = '\0';
1228 RTStrPurgeEncoding(pszBuf);
1229 }
1230 return VERR_BUFFER_OVERFLOW;
1231}
1232
1233
1234/**
1235 * Worker destroying the config database.
1236 */
1237static DECLCALLBACK(int) dbgDiggerLinuxCfgDbDestroyWorker(PRTSTRSPACECORE pStr, void *pvUser)
1238{
1239 PDBGDIGGERLINUXCFGITEM pCfgItem = (PDBGDIGGERLINUXCFGITEM)pStr;
1240 RTStrFree((char *)pCfgItem->Core.pszString);
1241 RTMemFree(pCfgItem);
1242 NOREF(pvUser);
1243 return 0;
1244}
1245
1246
1247/**
1248 * Destroy the config database.
1249 *
1250 * @returns nothing.
1251 * @param pThis The Linux digger data.
1252 */
1253static void dbgDiggerLinuxCfgDbDestroy(PDBGDIGGERLINUX pThis)
1254{
1255 RTStrSpaceDestroy(&pThis->hCfgDb, dbgDiggerLinuxCfgDbDestroyWorker, NULL);
1256}
1257
1258
1259/**
1260 * @copydoc DBGFOSREG::pfnStackUnwindAssist
1261 */
1262static DECLCALLBACK(int) dbgDiggerLinuxStackUnwindAssist(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData, VMCPUID idCpu,
1263 PDBGFSTACKFRAME pFrame, PRTDBGUNWINDSTATE pState, PCCPUMCTX pInitialCtx,
1264 RTDBGAS hAs, uint64_t *puScratch)
1265{
1266 RT_NOREF(pUVM, pVMM, pvData, idCpu, pFrame, pState, pInitialCtx, hAs, puScratch);
1267 return VINF_SUCCESS;
1268}
1269
1270
1271/**
1272 * @copydoc DBGFOSREG::pfnQueryInterface
1273 */
1274static DECLCALLBACK(void *) dbgDiggerLinuxQueryInterface(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData, DBGFOSINTERFACE enmIf)
1275{
1276 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1277 RT_NOREF(pUVM, pVMM);
1278
1279 switch (enmIf)
1280 {
1281 case DBGFOSINTERFACE_DMESG:
1282 return &pThis->IDmesg;
1283
1284 default:
1285 return NULL;
1286 }
1287}
1288
1289
1290/**
1291 * @copydoc DBGFOSREG::pfnQueryVersion
1292 */
1293static DECLCALLBACK(int) dbgDiggerLinuxQueryVersion(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData,
1294 char *pszVersion, size_t cchVersion)
1295{
1296 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1297 Assert(pThis->fValid);
1298
1299 /*
1300 * It's all in the linux banner.
1301 */
1302 int rc = pVMM->pfnDBGFR3MemReadString(pUVM, 0, &pThis->AddrLinuxBanner, pszVersion, cchVersion);
1303 if (RT_SUCCESS(rc))
1304 {
1305 char *pszEnd = RTStrEnd(pszVersion, cchVersion);
1306 AssertReturn(pszEnd, VERR_BUFFER_OVERFLOW);
1307 while ( pszEnd > pszVersion
1308 && RT_C_IS_SPACE(pszEnd[-1]))
1309 pszEnd--;
1310 *pszEnd = '\0';
1311 }
1312 else
1313 RTStrPrintf(pszVersion, cchVersion, "DBGFR3MemRead -> %Rrc", rc);
1314
1315 return rc;
1316}
1317
1318
1319/**
1320 * @copydoc DBGFOSREG::pfnTerm
1321 */
1322static DECLCALLBACK(void) dbgDiggerLinuxTerm(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
1323{
1324 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1325 Assert(pThis->fValid);
1326
1327 /*
1328 * Destroy configuration database.
1329 */
1330 dbgDiggerLinuxCfgDbDestroy(pThis);
1331
1332 /*
1333 * Unlink and release our modules.
1334 */
1335 RTDBGAS hDbgAs = pVMM->pfnDBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
1336 if (hDbgAs != NIL_RTDBGAS)
1337 {
1338 uint32_t iMod = RTDbgAsModuleCount(hDbgAs);
1339 while (iMod-- > 0)
1340 {
1341 RTDBGMOD hMod = RTDbgAsModuleByIndex(hDbgAs, iMod);
1342 if (hMod != NIL_RTDBGMOD)
1343 {
1344 if (RTDbgModGetTag(hMod) == DIG_LNX_MOD_TAG)
1345 {
1346 int rc = RTDbgAsModuleUnlink(hDbgAs, hMod);
1347 AssertRC(rc);
1348 }
1349 RTDbgModRelease(hMod);
1350 }
1351 }
1352 RTDbgAsRelease(hDbgAs);
1353 }
1354
1355 pThis->fValid = false;
1356}
1357
1358
1359/**
1360 * @copydoc DBGFOSREG::pfnRefresh
1361 */
1362static DECLCALLBACK(int) dbgDiggerLinuxRefresh(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
1363{
1364 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1365 RT_NOREF(pThis);
1366 Assert(pThis->fValid);
1367
1368 /*
1369 * For now we'll flush and reload everything.
1370 */
1371 dbgDiggerLinuxTerm(pUVM, pVMM, pvData);
1372 return dbgDiggerLinuxInit(pUVM, pVMM, pvData);
1373}
1374
1375
1376/**
1377 * Worker for dbgDiggerLinuxFindStartOfNamesAndSymbolCount that update the
1378 * digger data.
1379 *
1380 * @returns VINF_SUCCESS.
1381 * @param pThis The Linux digger data to update.
1382 * @param pVMM The VMM function table.
1383 * @param pAddrKernelNames The kallsyms_names address.
1384 * @param cKernelSymbols The number of kernel symbol.
1385 * @param cbAddress The guest address size.
1386 */
1387static int dbgDiggerLinuxFoundStartOfNames(PDBGDIGGERLINUX pThis, PCVMMR3VTABLE pVMM, PCDBGFADDRESS pAddrKernelNames,
1388 uint32_t cKernelSymbols, uint32_t cbAddress)
1389{
1390 pThis->cKernelSymbols = cKernelSymbols;
1391 pThis->AddrKernelNames = *pAddrKernelNames;
1392 pThis->AddrKernelAddresses = *pAddrKernelNames;
1393 uint32_t cbSymbolsSkip = (pThis->fRelKrnlAddr ? 2 : 1) * cbAddress; /* Relative addressing introduces kallsyms_relative_base. */
1394 uint32_t cbOffsets = pThis->fRelKrnlAddr ? sizeof(int32_t) : cbAddress; /* Offsets are always 32bits wide for relative addressing. */
1395 uint32_t cbAlign = 0;
1396
1397 /*
1398 * If the number of symbols is odd there is padding to align the following guest pointer
1399 * sized data properly on 64bit systems with relative addressing.
1400 */
1401 if ( pThis->fRelKrnlAddr
1402 && pThis->f64Bit
1403 && (pThis->cKernelSymbols & 1))
1404 cbAlign = sizeof(int32_t);
1405 pVMM->pfnDBGFR3AddrSub(&pThis->AddrKernelAddresses, cKernelSymbols * cbOffsets + cbSymbolsSkip + cbAlign);
1406
1407 Log(("dbgDiggerLinuxFoundStartOfNames: AddrKernelAddresses=%RGv\n"
1408 "dbgDiggerLinuxFoundStartOfNames: cKernelSymbols=%#x (at %RGv)\n"
1409 "dbgDiggerLinuxFoundStartOfNames: AddrKernelName=%RGv\n",
1410 pThis->AddrKernelAddresses.FlatPtr,
1411 pThis->cKernelSymbols, pThis->AddrKernelNames.FlatPtr - cbAddress,
1412 pThis->AddrKernelNames.FlatPtr));
1413 return VINF_SUCCESS;
1414}
1415
1416
1417/**
1418 * Tries to find the address of the kallsyms_names, kallsyms_num_syms and
1419 * kallsyms_addresses symbols.
1420 *
1421 * The kallsyms_num_syms is read and stored in pThis->cKernelSymbols, while the
1422 * addresses of the other two are stored as pThis->AddrKernelNames and
1423 * pThis->AddrKernelAddresses.
1424 *
1425 * @returns VBox status code, success indicating that all three variables have
1426 * been found and taken down.
1427 * @param pUVM The user mode VM handle.
1428 * @param pVMM The VMM function table.
1429 * @param pThis The Linux digger data.
1430 * @param pHitAddr An address we think is inside kallsyms_names.
1431 */
1432static int dbgDiggerLinuxFindStartOfNamesAndSymbolCount(PUVM pUVM, PCVMMR3VTABLE pVMM, PDBGDIGGERLINUX pThis,
1433 PCDBGFADDRESS pHitAddr)
1434{
1435 /*
1436 * Search backwards in chunks.
1437 */
1438 union
1439 {
1440 uint8_t ab[0x1000];
1441 uint32_t au32[0x1000 / sizeof(uint32_t)];
1442 uint64_t au64[0x1000 / sizeof(uint64_t)];
1443 } uBuf;
1444 uint32_t cbLeft = LNX_MAX_KALLSYMS_NAMES_SIZE;
1445 uint32_t cbBuf = pHitAddr->FlatPtr & (sizeof(uBuf) - 1);
1446 DBGFADDRESS CurAddr = *pHitAddr;
1447 pVMM->pfnDBGFR3AddrSub(&CurAddr, cbBuf);
1448 cbBuf += sizeof(uint64_t) - 1; /* In case our kobj hit is in the first 4/8 bytes. */
1449 for (;;)
1450 {
1451 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
1452 if (RT_FAILURE(rc))
1453 return rc;
1454
1455 /*
1456 * Since Linux 4.6 there are two different methods to store the kallsyms addresses
1457 * in the image.
1458 *
1459 * The first and longer existing method is to store the absolute addresses in an
1460 * array starting at kallsyms_addresses followed by a field which stores the number
1461 * of kernel symbols called kallsyms_num_syms.
1462 * The newer method is to use offsets stored in kallsyms_offsets and have a base pointer
1463 * to relate the offsets to called kallsyms_relative_base. One entry in kallsyms_offsets is
1464 * always 32bit wide regardless of the guest pointer size (this halves the table on 64bit
1465 * systems) but means more work for us for the 64bit case.
1466 *
1467 * When absolute addresses are used the following assumptions hold:
1468 *
1469 * We assume that the three symbols are aligned on guest pointer boundary.
1470 *
1471 * The boundary between the two tables should be noticable as the number
1472 * is unlikely to be more than 16 millions, there will be at least one zero
1473 * byte where it is, 64-bit will have 5 zero bytes. Zero bytes aren't all
1474 * that common in the kallsyms_names table.
1475 *
1476 * Also the kallsyms_names table starts with a length byte, which means
1477 * we're likely to see a byte in the range 1..31.
1478 *
1479 * The kallsyms_addresses are mostly sorted (except for the start where the
1480 * absolute symbols are), so we'll spot a bunch of kernel addresses
1481 * immediately preceeding the kallsyms_num_syms field.
1482 *
1483 * Lazy bird: If kallsyms_num_syms is on a buffer boundrary, we skip
1484 * the check for kernel addresses preceeding it.
1485 *
1486 * For relative offsets most of the assumptions from above are true too
1487 * except that we have to distinguish between the relative base address and the offsets.
1488 * Every observed kernel has a valid kernel address fo the relative base and kallsyms_relative_base
1489 * always comes before kallsyms_num_syms and is aligned on a guest pointer boundary.
1490 * Offsets are stored before kallsyms_relative_base and don't contain valid kernel addresses.
1491 *
1492 * To distinguish between absolute and relative offsetting we check the data before a candidate
1493 * for kallsyms_num_syms. If all entries before the kallsyms_num_syms candidate are valid kernel
1494 * addresses absolute addresses are assumed. If this is not the case but the first entry before
1495 * kallsyms_num_syms is a valid kernel address we check whether the data before and the possible
1496 * relative base form a valid kernel address and assume relative offsets.
1497 *
1498 * Other notable changes between various Linux kernel versions:
1499 *
1500 * 4.20.0+: Commit 80ffbaa5b1bd98e80e3239a3b8cfda2da433009a made kallsyms_num_syms 32bit
1501 * even on 64bit systems but the alignment of the variables makes the code below work for now
1502 * (tested with a 5.4 and 5.12 kernel) do we keep it that way to avoid making the code even
1503 * messy.
1504 */
1505 if (pThis->f64Bit)
1506 {
1507 uint32_t i = cbBuf / sizeof(uint64_t) - 1;
1508 while (i-- > 0)
1509 if ( uBuf.au64[i] <= LNX_MAX_KALLSYMS_SYMBOLS
1510 && uBuf.au64[i] >= LNX_MIN_KALLSYMS_SYMBOLS)
1511 {
1512 uint8_t *pb = (uint8_t *)&uBuf.au64[i + 1];
1513 if ( pb[0] <= LNX_MAX_KALLSYMS_ENC_LENGTH
1514 && pb[0] >= LNX_MIN_KALLSYMS_ENC_LENGTH)
1515 {
1516 /*
1517 * Check whether we have a valid kernel address and try to distinguish
1518 * whether the kernel uses relative offsetting or absolute addresses.
1519 */
1520 if ( (i >= 1 && LNX64_VALID_ADDRESS(uBuf.au64[i - 1]))
1521 && (i >= 2 && !LNX64_VALID_ADDRESS(uBuf.au64[i - 2]))
1522 && (i >= 3 && !LNX64_VALID_ADDRESS(uBuf.au64[i - 3])))
1523 {
1524 RTGCUINTPTR uKrnlRelBase = uBuf.au64[i - 1];
1525 DBGFADDRESS RelAddr = CurAddr;
1526 int32_t aiRelOff[3];
1527 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1528 pVMM->pfnDBGFR3AddrAdd(&RelAddr,
1529 (i - 1) * sizeof(uint64_t) - sizeof(aiRelOff)),
1530 &aiRelOff[0], sizeof(aiRelOff));
1531 if ( RT_SUCCESS(rc)
1532 && LNX64_VALID_ADDRESS(uKrnlRelBase + aiRelOff[0])
1533 && LNX64_VALID_ADDRESS(uKrnlRelBase + aiRelOff[1])
1534 && LNX64_VALID_ADDRESS(uKrnlRelBase + aiRelOff[2]))
1535 {
1536 Log(("dbgDiggerLinuxFindStartOfNamesAndSymbolCount: relative base %RGv (at %RGv)\n",
1537 uKrnlRelBase, CurAddr.FlatPtr + (i - 1) * sizeof(uint64_t)));
1538 pThis->fRelKrnlAddr = true;
1539 pThis->uKernelRelativeBase = uKrnlRelBase;
1540 return dbgDiggerLinuxFoundStartOfNames(pThis, pVMM,
1541 pVMM->pfnDBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint64_t)),
1542 (uint32_t)uBuf.au64[i], sizeof(uint64_t));
1543 }
1544 }
1545
1546 if ( (i <= 0 || LNX64_VALID_ADDRESS(uBuf.au64[i - 1]))
1547 && (i <= 1 || LNX64_VALID_ADDRESS(uBuf.au64[i - 2]))
1548 && (i <= 2 || LNX64_VALID_ADDRESS(uBuf.au64[i - 3])))
1549 return dbgDiggerLinuxFoundStartOfNames(pThis, pVMM,
1550 pVMM->pfnDBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint64_t)),
1551 (uint32_t)uBuf.au64[i], sizeof(uint64_t));
1552 }
1553 }
1554 }
1555 else
1556 {
1557 uint32_t i = cbBuf / sizeof(uint32_t) - 1;
1558 while (i-- > 0)
1559 if ( uBuf.au32[i] <= LNX_MAX_KALLSYMS_SYMBOLS
1560 && uBuf.au32[i] >= LNX_MIN_KALLSYMS_SYMBOLS)
1561 {
1562 uint8_t *pb = (uint8_t *)&uBuf.au32[i + 1];
1563 if ( pb[0] <= LNX_MAX_KALLSYMS_ENC_LENGTH
1564 && pb[0] >= LNX_MIN_KALLSYMS_ENC_LENGTH)
1565 {
1566 /* Check for relative base addressing. */
1567 if (i >= 1 && LNX32_VALID_ADDRESS(uBuf.au32[i - 1]))
1568 {
1569 RTGCUINTPTR uKrnlRelBase = uBuf.au32[i - 1];
1570 if ( (i <= 1 || LNX32_VALID_ADDRESS(uKrnlRelBase + uBuf.au32[i - 2]))
1571 && (i <= 2 || LNX32_VALID_ADDRESS(uKrnlRelBase + uBuf.au32[i - 3])))
1572 {
1573 Log(("dbgDiggerLinuxFindStartOfNamesAndSymbolCount: relative base %RGv (at %RGv)\n",
1574 uKrnlRelBase, CurAddr.FlatPtr + (i - 1) * sizeof(uint32_t)));
1575 pThis->fRelKrnlAddr = true;
1576 pThis->uKernelRelativeBase = uKrnlRelBase;
1577 return dbgDiggerLinuxFoundStartOfNames(pThis, pVMM,
1578 pVMM->pfnDBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint32_t)),
1579 uBuf.au32[i], sizeof(uint32_t));
1580 }
1581 }
1582
1583 if ( (i <= 0 || LNX32_VALID_ADDRESS(uBuf.au32[i - 1]))
1584 && (i <= 1 || LNX32_VALID_ADDRESS(uBuf.au32[i - 2]))
1585 && (i <= 2 || LNX32_VALID_ADDRESS(uBuf.au32[i - 3])))
1586 return dbgDiggerLinuxFoundStartOfNames(pThis, pVMM,
1587 pVMM->pfnDBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint32_t)),
1588 uBuf.au32[i], sizeof(uint32_t));
1589 }
1590 }
1591 }
1592
1593 /*
1594 * Advance
1595 */
1596 if (RT_UNLIKELY(cbLeft <= sizeof(uBuf)))
1597 {
1598 Log(("dbgDiggerLinuxFindStartOfNamesAndSymbolCount: failed (pHitAddr=%RGv)\n", pHitAddr->FlatPtr));
1599 return VERR_NOT_FOUND;
1600 }
1601 cbLeft -= sizeof(uBuf);
1602 pVMM->pfnDBGFR3AddrSub(&CurAddr, sizeof(uBuf));
1603 cbBuf = sizeof(uBuf);
1604 }
1605}
1606
1607
1608/**
1609 * Worker for dbgDiggerLinuxFindEndNames that records the findings.
1610 *
1611 * @returns VINF_SUCCESS
1612 * @param pThis The linux digger data to update.
1613 * @param pVMM The VMM function table.
1614 * @param pAddrMarkers The address of the marker (kallsyms_markers).
1615 * @param cbMarkerEntry The size of a marker entry (32-bit or 64-bit).
1616 */
1617static int dbgDiggerLinuxFoundMarkers(PDBGDIGGERLINUX pThis, PCVMMR3VTABLE pVMM,
1618 PCDBGFADDRESS pAddrMarkers, uint32_t cbMarkerEntry)
1619{
1620 pThis->cbKernelNames = pAddrMarkers->FlatPtr - pThis->AddrKernelNames.FlatPtr;
1621 pThis->AddrKernelNameMarkers = *pAddrMarkers;
1622 pThis->cKernelNameMarkers = RT_ALIGN_32(pThis->cKernelSymbols, 256) / 256;
1623 pThis->AddrKernelTokenTable = *pAddrMarkers;
1624 pVMM->pfnDBGFR3AddrAdd(&pThis->AddrKernelTokenTable, pThis->cKernelNameMarkers * cbMarkerEntry);
1625
1626 Log(("dbgDiggerLinuxFoundMarkers: AddrKernelNames=%RGv cbKernelNames=%#x\n"
1627 "dbgDiggerLinuxFoundMarkers: AddrKernelNameMarkers=%RGv cKernelNameMarkers=%#x\n"
1628 "dbgDiggerLinuxFoundMarkers: AddrKernelTokenTable=%RGv\n",
1629 pThis->AddrKernelNames.FlatPtr, pThis->cbKernelNames,
1630 pThis->AddrKernelNameMarkers.FlatPtr, pThis->cKernelNameMarkers,
1631 pThis->AddrKernelTokenTable.FlatPtr));
1632 return VINF_SUCCESS;
1633}
1634
1635
1636/**
1637 * Tries to find the end of kallsyms_names and thereby the start of
1638 * kallsyms_markers and kallsyms_token_table.
1639 *
1640 * The kallsyms_names size is stored in pThis->cbKernelNames, the addresses of
1641 * the two other symbols in pThis->AddrKernelNameMarkers and
1642 * pThis->AddrKernelTokenTable. The number of marker entries is stored in
1643 * pThis->cKernelNameMarkers.
1644 *
1645 * @returns VBox status code, success indicating that all three variables have
1646 * been found and taken down.
1647 * @param pUVM The user mode VM handle.
1648 * @param pVMM The VMM function table.
1649 * @param pThis The Linux digger data.
1650 * @param pHitAddr An address we think is inside kallsyms_names.
1651 */
1652static int dbgDiggerLinuxFindEndOfNamesAndMore(PUVM pUVM, PCVMMR3VTABLE pVMM, PDBGDIGGERLINUX pThis, PCDBGFADDRESS pHitAddr)
1653{
1654 /*
1655 * Search forward in chunks.
1656 */
1657 union
1658 {
1659 uint8_t ab[0x1000];
1660 uint32_t au32[0x1000 / sizeof(uint32_t)];
1661 uint64_t au64[0x1000 / sizeof(uint64_t)];
1662 } uBuf;
1663 bool fPendingZeroHit = false;
1664 uint32_t cbLeft = LNX_MAX_KALLSYMS_NAMES_SIZE + sizeof(uBuf);
1665 uint32_t offBuf = pHitAddr->FlatPtr & (sizeof(uBuf) - 1);
1666 DBGFADDRESS CurAddr = *pHitAddr;
1667 pVMM->pfnDBGFR3AddrSub(&CurAddr, offBuf);
1668 for (;;)
1669 {
1670 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
1671 if (RT_FAILURE(rc))
1672 return rc;
1673
1674 /*
1675 * The kallsyms_names table is followed by kallsyms_markers we assume,
1676 * using sizeof(unsigned long) alignment like the preceeding symbols.
1677 *
1678 * The kallsyms_markers table has entried sizeof(unsigned long) and
1679 * contains offsets into kallsyms_names. The kallsyms_markers used to
1680 * index kallsyms_names and reduce seek time when looking up the name
1681 * of an address/symbol. Each entry in kallsyms_markers covers 256
1682 * symbol names.
1683 *
1684 * Because of this, the first entry is always zero and all the entries
1685 * are ascending. It also follows that the size of the table can be
1686 * calculated from kallsyms_num_syms.
1687 *
1688 * Note! We could also have walked kallsyms_names by skipping
1689 * kallsyms_num_syms names, but this is faster and we will
1690 * validate the encoded names later.
1691 *
1692 * git commit 80ffbaa5b1bd98e80e3239a3b8cfda2da433009a (which became 4.20+) makes kallsyms_markers
1693 * and kallsyms_num_syms uint32_t, even on 64bit systems. Take that into account.
1694 */
1695 if ( pThis->f64Bit
1696 && pThis->uKrnlVer < LNX_MK_VER(4, 20, 0))
1697 {
1698 if ( RT_UNLIKELY(fPendingZeroHit)
1699 && uBuf.au64[0] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1700 && uBuf.au64[0] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1701 return dbgDiggerLinuxFoundMarkers(pThis, pVMM,
1702 pVMM->pfnDBGFR3AddrSub(&CurAddr, sizeof(uint64_t)), sizeof(uint64_t));
1703
1704 uint32_t const cEntries = sizeof(uBuf) / sizeof(uint64_t);
1705 for (uint32_t i = offBuf / sizeof(uint64_t); i < cEntries; i++)
1706 if (uBuf.au64[i] == 0)
1707 {
1708 if (RT_UNLIKELY(i + 1 >= cEntries))
1709 {
1710 fPendingZeroHit = true;
1711 break;
1712 }
1713 if ( uBuf.au64[i + 1] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1714 && uBuf.au64[i + 1] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1715 return dbgDiggerLinuxFoundMarkers(pThis, pVMM,
1716 pVMM->pfnDBGFR3AddrAdd(&CurAddr, i * sizeof(uint64_t)), sizeof(uint64_t));
1717 }
1718 }
1719 else
1720 {
1721 if ( RT_UNLIKELY(fPendingZeroHit)
1722 && uBuf.au32[0] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1723 && uBuf.au32[0] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1724 return dbgDiggerLinuxFoundMarkers(pThis, pVMM,
1725 pVMM->pfnDBGFR3AddrSub(&CurAddr, sizeof(uint32_t)), sizeof(uint32_t));
1726
1727 uint32_t const cEntries = sizeof(uBuf) / sizeof(uint32_t);
1728 for (uint32_t i = offBuf / sizeof(uint32_t); i < cEntries; i++)
1729 if (uBuf.au32[i] == 0)
1730 {
1731 if (RT_UNLIKELY(i + 1 >= cEntries))
1732 {
1733 fPendingZeroHit = true;
1734 break;
1735 }
1736 if ( uBuf.au32[i + 1] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1737 && uBuf.au32[i + 1] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1738 return dbgDiggerLinuxFoundMarkers(pThis, pVMM,
1739 pVMM->pfnDBGFR3AddrAdd(&CurAddr, i * sizeof(uint32_t)), sizeof(uint32_t));
1740 }
1741 }
1742
1743 /*
1744 * Advance
1745 */
1746 if (RT_UNLIKELY(cbLeft <= sizeof(uBuf)))
1747 {
1748 Log(("dbgDiggerLinuxFindEndOfNamesAndMore: failed (pHitAddr=%RGv)\n", pHitAddr->FlatPtr));
1749 return VERR_NOT_FOUND;
1750 }
1751 cbLeft -= sizeof(uBuf);
1752 pVMM->pfnDBGFR3AddrAdd(&CurAddr, sizeof(uBuf));
1753 offBuf = 0;
1754 }
1755}
1756
1757
1758/**
1759 * Locates the kallsyms_token_index table.
1760 *
1761 * Storing the address in pThis->AddrKernelTokenIndex and the size of the token
1762 * table in pThis->cbKernelTokenTable.
1763 *
1764 * @returns VBox status code.
1765 * @param pUVM The user mode VM handle.
1766 * @param pVMM The VMM function table.
1767 * @param pThis The Linux digger data.
1768 */
1769static int dbgDiggerLinuxFindTokenIndex(PUVM pUVM, PCVMMR3VTABLE pVMM, PDBGDIGGERLINUX pThis)
1770{
1771 /*
1772 * The kallsyms_token_table is very much like a string table. Due to the
1773 * nature of the compression algorithm it is reasonably short (one example
1774 * here is 853 bytes), so we'll not be reading it in chunks but in full.
1775 * To be on the safe side, we read 8KB, ASSUMING we won't run into unmapped
1776 * memory or any other nasty stuff...
1777 */
1778 union
1779 {
1780 uint8_t ab[0x2000];
1781 uint16_t au16[0x2000 / sizeof(uint16_t)];
1782 } uBuf;
1783 DBGFADDRESS CurAddr = pThis->AddrKernelTokenTable;
1784 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
1785 if (RT_FAILURE(rc))
1786 return rc;
1787
1788 /*
1789 * We've got two choices here, either walk the string table or look for
1790 * the next structure, kallsyms_token_index.
1791 *
1792 * The token index is a table of 256 uint16_t entries (index by bytes
1793 * from kallsyms_names) that gives offsets in kallsyms_token_table. It
1794 * starts with a zero entry and the following entries are sorted in
1795 * ascending order. The range of the entries are reasonably small since
1796 * kallsyms_token_table is small.
1797 *
1798 * The alignment seems to be sizeof(unsigned long), just like
1799 * kallsyms_token_table.
1800 *
1801 * So, we start by looking for a zero 16-bit entry.
1802 */
1803 uint32_t cIncr = (pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t)) / sizeof(uint16_t);
1804
1805 for (uint32_t i = 0; i < sizeof(uBuf) / sizeof(uint16_t) - 16; i += cIncr)
1806 if ( uBuf.au16[i] == 0
1807 && uBuf.au16[i + 1] > 0
1808 && uBuf.au16[i + 1] <= LNX_MAX_KALLSYMS_TOKEN_LEN
1809 && (uint16_t)(uBuf.au16[i + 2] - uBuf.au16[i + 1] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1810 && (uint16_t)(uBuf.au16[i + 3] - uBuf.au16[i + 2] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1811 && (uint16_t)(uBuf.au16[i + 4] - uBuf.au16[i + 3] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1812 && (uint16_t)(uBuf.au16[i + 5] - uBuf.au16[i + 4] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1813 && (uint16_t)(uBuf.au16[i + 6] - uBuf.au16[i + 5] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1814 )
1815 {
1816 pThis->AddrKernelTokenIndex = CurAddr;
1817 pVMM->pfnDBGFR3AddrAdd(&pThis->AddrKernelTokenIndex, i * sizeof(uint16_t));
1818 pThis->cbKernelTokenTable = i * sizeof(uint16_t);
1819 return VINF_SUCCESS;
1820 }
1821
1822 Log(("dbgDiggerLinuxFindTokenIndex: Failed (%RGv..%RGv)\n", CurAddr.FlatPtr, CurAddr.FlatPtr + (RTGCUINTPTR)sizeof(uBuf)));
1823 return VERR_NOT_FOUND;
1824}
1825
1826
1827/**
1828 * Loads the kernel symbols from the given kallsyms offset table decoding the symbol names
1829 * (worker common for dbgDiggerLinuxLoadKernelSymbolsAbsolute() and dbgDiggerLinuxLoadKernelSymbolsRelative()).
1830 *
1831 * @returns VBox status code.
1832 * @param pUVM The user mode VM handle.
1833 * @param pVMM The VMM function table.
1834 * @param pThis The Linux digger data.
1835 * @param uKernelStart Flat kernel start address.
1836 * @param cbKernel Size of the kernel in bytes.
1837 * @param pauSymOff Pointer to the array of symbol offsets in the kallsyms table
1838 * relative to the start of the kernel.
1839 */
1840static int dbgDiggerLinuxLoadKernelSymbolsWorker(PUVM pUVM, PCVMMR3VTABLE pVMM, PDBGDIGGERLINUX pThis, RTGCUINTPTR uKernelStart,
1841 RTGCUINTPTR cbKernel, RTGCUINTPTR *pauSymOff)
1842{
1843 uint8_t *pbNames = (uint8_t *)RTMemAllocZ(pThis->cbKernelNames);
1844 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelNames, pbNames, pThis->cbKernelNames);
1845 if (RT_SUCCESS(rc))
1846 {
1847 char *pszzTokens = (char *)RTMemAllocZ(pThis->cbKernelTokenTable);
1848 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelTokenTable, pszzTokens, pThis->cbKernelTokenTable);
1849 if (RT_SUCCESS(rc))
1850 {
1851 uint16_t *paoffTokens = (uint16_t *)RTMemAllocZ(256 * sizeof(uint16_t));
1852 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelTokenIndex, paoffTokens, 256 * sizeof(uint16_t));
1853 if (RT_SUCCESS(rc))
1854 {
1855 /*
1856 * Create a module for the kernel.
1857 */
1858 RTDBGMOD hMod;
1859 rc = RTDbgModCreate(&hMod, "vmlinux", cbKernel, 0 /*fFlags*/);
1860 if (RT_SUCCESS(rc))
1861 {
1862 rc = RTDbgModSetTag(hMod, DIG_LNX_MOD_TAG); AssertRC(rc);
1863 rc = VINF_SUCCESS;
1864
1865 /*
1866 * Enumerate the symbols.
1867 */
1868 uint32_t offName = 0;
1869 uint32_t cLeft = pThis->cKernelSymbols;
1870 while (cLeft-- > 0 && RT_SUCCESS(rc))
1871 {
1872 /* Decode the symbol name first. */
1873 if (RT_LIKELY(offName < pThis->cbKernelNames))
1874 {
1875 uint8_t cbName = pbNames[offName++];
1876 if (RT_LIKELY(offName + cbName <= pThis->cbKernelNames))
1877 {
1878 char szSymbol[4096];
1879 uint32_t offSymbol = 0;
1880 while (cbName-- > 0)
1881 {
1882 uint8_t bEnc = pbNames[offName++];
1883 uint16_t offToken = paoffTokens[bEnc];
1884 if (RT_LIKELY(offToken < pThis->cbKernelTokenTable))
1885 {
1886 const char *pszToken = &pszzTokens[offToken];
1887 char ch;
1888 while ((ch = *pszToken++) != '\0')
1889 if (offSymbol < sizeof(szSymbol) - 1)
1890 szSymbol[offSymbol++] = ch;
1891 }
1892 else
1893 {
1894 rc = VERR_INVALID_UTF8_ENCODING;
1895 break;
1896 }
1897 }
1898 szSymbol[offSymbol < sizeof(szSymbol) ? offSymbol : sizeof(szSymbol) - 1] = '\0';
1899
1900 /* The offset. */
1901 RTGCUINTPTR uSymOff = *pauSymOff;
1902 pauSymOff++;
1903
1904 /* Add it without the type char. */
1905 if (uSymOff <= cbKernel)
1906 {
1907 rc = RTDbgModSymbolAdd(hMod, &szSymbol[1], RTDBGSEGIDX_RVA, uSymOff,
1908 0 /*cb*/, 0 /*fFlags*/, NULL);
1909 if (RT_FAILURE(rc))
1910 {
1911 if ( rc == VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE
1912 || rc == VERR_DBG_INVALID_RVA
1913 || rc == VERR_DBG_ADDRESS_CONFLICT
1914 || rc == VERR_DBG_DUPLICATE_SYMBOL)
1915 {
1916 Log2(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc (ignored)\n", szSymbol, rc));
1917 rc = VINF_SUCCESS;
1918 }
1919 else
1920 Log(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc\n", szSymbol, rc));
1921 }
1922 }
1923 }
1924 else
1925 {
1926 rc = VERR_END_OF_STRING;
1927 Log(("dbgDiggerLinuxLoadKernelSymbols: offName=%#x cLeft=%#x cbName=%#x cbKernelNames=%#x\n",
1928 offName, cLeft, cbName, pThis->cbKernelNames));
1929 }
1930 }
1931 else
1932 {
1933 rc = VERR_END_OF_STRING;
1934 Log(("dbgDiggerLinuxLoadKernelSymbols: offName=%#x cLeft=%#x cbKernelNames=%#x\n",
1935 offName, cLeft, pThis->cbKernelNames));
1936 }
1937 }
1938
1939 /*
1940 * Link the module into the address space.
1941 */
1942 if (RT_SUCCESS(rc))
1943 {
1944 RTDBGAS hAs = pVMM->pfnDBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
1945 if (hAs != NIL_RTDBGAS)
1946 rc = RTDbgAsModuleLink(hAs, hMod, uKernelStart, RTDBGASLINK_FLAGS_REPLACE);
1947 else
1948 rc = VERR_INTERNAL_ERROR;
1949 RTDbgAsRelease(hAs);
1950 }
1951 else
1952 Log(("dbgDiggerLinuxLoadKernelSymbols: Failed: %Rrc\n", rc));
1953 RTDbgModRelease(hMod);
1954 }
1955 else
1956 Log(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModCreate failed: %Rrc\n", rc));
1957 }
1958 else
1959 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading token index at %RGv failed: %Rrc\n",
1960 pThis->AddrKernelTokenIndex.FlatPtr, rc));
1961 RTMemFree(paoffTokens);
1962 }
1963 else
1964 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading token table at %RGv failed: %Rrc\n",
1965 pThis->AddrKernelTokenTable.FlatPtr, rc));
1966 RTMemFree(pszzTokens);
1967 }
1968 else
1969 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading encoded names at %RGv failed: %Rrc\n",
1970 pThis->AddrKernelNames.FlatPtr, rc));
1971 RTMemFree(pbNames);
1972
1973 return rc;
1974}
1975
1976/**
1977 * Loads the kernel symbols from the kallsyms table if it contains absolute addresses
1978 *
1979 * @returns VBox status code.
1980 * @param pUVM The user mode VM handle.
1981 * @param pVMM The VMM function table.
1982 * @param pThis The Linux digger data.
1983 */
1984static int dbgDiggerLinuxLoadKernelSymbolsAbsolute(PUVM pUVM, PCVMMR3VTABLE pVMM, PDBGDIGGERLINUX pThis)
1985{
1986 /*
1987 * Allocate memory for temporary table copies, reading the tables as we go.
1988 */
1989 uint32_t const cbGuestAddr = pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t);
1990 void *pvAddresses = RTMemAllocZ(pThis->cKernelSymbols * cbGuestAddr);
1991 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelAddresses,
1992 pvAddresses, pThis->cKernelSymbols * cbGuestAddr);
1993 if (RT_SUCCESS(rc))
1994 {
1995 /*
1996 * Figure out the kernel start and end and convert the absolute addresses to relative offsets.
1997 */
1998 RTGCUINTPTR uKernelStart = pThis->AddrKernelAddresses.FlatPtr;
1999 RTGCUINTPTR uKernelEnd = pThis->AddrKernelTokenIndex.FlatPtr + 256 * sizeof(uint16_t);
2000 RTGCUINTPTR *pauSymOff = (RTGCUINTPTR *)RTMemTmpAllocZ(pThis->cKernelSymbols * sizeof(RTGCUINTPTR));
2001 uint32_t i;
2002 if (cbGuestAddr == sizeof(uint64_t))
2003 {
2004 uint64_t *pauAddrs = (uint64_t *)pvAddresses;
2005 for (i = 0; i < pThis->cKernelSymbols; i++)
2006 if ( pauAddrs[i] < uKernelStart
2007 && LNX64_VALID_ADDRESS(pauAddrs[i])
2008 && uKernelStart - pauAddrs[i] < LNX_MAX_KERNEL_SIZE)
2009 uKernelStart = pauAddrs[i];
2010
2011 for (i = pThis->cKernelSymbols - 1; i > 0; i--)
2012 if ( pauAddrs[i] > uKernelEnd
2013 && LNX64_VALID_ADDRESS(pauAddrs[i])
2014 && pauAddrs[i] - uKernelEnd < LNX_MAX_KERNEL_SIZE)
2015 uKernelEnd = pauAddrs[i];
2016
2017 for (i = 0; i < pThis->cKernelSymbols; i++)
2018 pauSymOff[i] = pauAddrs[i] - uKernelStart;
2019 }
2020 else
2021 {
2022 uint32_t *pauAddrs = (uint32_t *)pvAddresses;
2023 for (i = 0; i < pThis->cKernelSymbols; i++)
2024 if ( pauAddrs[i] < uKernelStart
2025 && LNX32_VALID_ADDRESS(pauAddrs[i])
2026 && uKernelStart - pauAddrs[i] < LNX_MAX_KERNEL_SIZE)
2027 uKernelStart = pauAddrs[i];
2028
2029 for (i = pThis->cKernelSymbols - 1; i > 0; i--)
2030 if ( pauAddrs[i] > uKernelEnd
2031 && LNX32_VALID_ADDRESS(pauAddrs[i])
2032 && pauAddrs[i] - uKernelEnd < LNX_MAX_KERNEL_SIZE)
2033 uKernelEnd = pauAddrs[i];
2034
2035 for (i = 0; i < pThis->cKernelSymbols; i++)
2036 pauSymOff[i] = pauAddrs[i] - uKernelStart;
2037 }
2038
2039 RTGCUINTPTR cbKernel = uKernelEnd - uKernelStart;
2040 pThis->cbKernel = (uint32_t)cbKernel;
2041 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &pThis->AddrKernelBase, uKernelStart);
2042 Log(("dbgDiggerLinuxLoadKernelSymbolsAbsolute: uKernelStart=%RGv cbKernel=%#x\n", uKernelStart, cbKernel));
2043
2044 rc = dbgDiggerLinuxLoadKernelSymbolsWorker(pUVM, pVMM, pThis, uKernelStart, cbKernel, pauSymOff);
2045 if (RT_FAILURE(rc))
2046 Log(("dbgDiggerLinuxLoadKernelSymbolsAbsolute: Loading symbols from given offset table failed: %Rrc\n", rc));
2047 RTMemTmpFree(pauSymOff);
2048 }
2049 else
2050 Log(("dbgDiggerLinuxLoadKernelSymbolsAbsolute: Reading symbol addresses at %RGv failed: %Rrc\n",
2051 pThis->AddrKernelAddresses.FlatPtr, rc));
2052 RTMemFree(pvAddresses);
2053
2054 return rc;
2055}
2056
2057
2058/**
2059 * Loads the kernel symbols from the kallsyms table if it contains absolute addresses
2060 *
2061 * @returns VBox status code.
2062 * @param pUVM The user mode VM handle.
2063 * @param pVMM The VMM function table.
2064 * @param pThis The Linux digger data.
2065 */
2066static int dbgDiggerLinuxLoadKernelSymbolsRelative(PUVM pUVM, PCVMMR3VTABLE pVMM, PDBGDIGGERLINUX pThis)
2067{
2068 /*
2069 * Allocate memory for temporary table copies, reading the tables as we go.
2070 */
2071 int32_t *pai32Offsets = (int32_t *)RTMemAllocZ(pThis->cKernelSymbols * sizeof(int32_t));
2072 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelAddresses,
2073 pai32Offsets, pThis->cKernelSymbols * sizeof(int32_t));
2074 if (RT_SUCCESS(rc))
2075 {
2076 /*
2077 * Figure out the kernel start and end and convert the absolute addresses to relative offsets.
2078 */
2079 RTGCUINTPTR uKernelStart = pThis->AddrKernelAddresses.FlatPtr;
2080 RTGCUINTPTR uKernelEnd = pThis->AddrKernelTokenIndex.FlatPtr + 256 * sizeof(uint16_t);
2081 RTGCUINTPTR *pauSymOff = (RTGCUINTPTR *)RTMemTmpAllocZ(pThis->cKernelSymbols * sizeof(RTGCUINTPTR));
2082 uint32_t i;
2083
2084 for (i = 0; i < pThis->cKernelSymbols; i++)
2085 {
2086 RTGCUINTPTR uSymAddr = dbgDiggerLinuxConvOffsetToAddr(pThis, pai32Offsets[i]);
2087
2088 if ( uSymAddr < uKernelStart
2089 && (pThis->f64Bit ? LNX64_VALID_ADDRESS(uSymAddr) : LNX32_VALID_ADDRESS(uSymAddr))
2090 && uKernelStart - uSymAddr < LNX_MAX_KERNEL_SIZE)
2091 uKernelStart = uSymAddr;
2092 }
2093
2094 for (i = pThis->cKernelSymbols - 1; i > 0; i--)
2095 {
2096 RTGCUINTPTR uSymAddr = dbgDiggerLinuxConvOffsetToAddr(pThis, pai32Offsets[i]);
2097
2098 if ( uSymAddr > uKernelEnd
2099 && (pThis->f64Bit ? LNX64_VALID_ADDRESS(uSymAddr) : LNX32_VALID_ADDRESS(uSymAddr))
2100 && uSymAddr - uKernelEnd < LNX_MAX_KERNEL_SIZE)
2101 uKernelEnd = uSymAddr;
2102
2103 /* Store the offset from the derived kernel start address. */
2104 pauSymOff[i] = uSymAddr - uKernelStart;
2105 }
2106
2107 RTGCUINTPTR cbKernel = uKernelEnd - uKernelStart;
2108 pThis->cbKernel = (uint32_t)cbKernel;
2109 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &pThis->AddrKernelBase, uKernelStart);
2110 Log(("dbgDiggerLinuxLoadKernelSymbolsRelative: uKernelStart=%RGv cbKernel=%#x\n", uKernelStart, cbKernel));
2111
2112 rc = dbgDiggerLinuxLoadKernelSymbolsWorker(pUVM, pVMM, pThis, uKernelStart, cbKernel, pauSymOff);
2113 if (RT_FAILURE(rc))
2114 Log(("dbgDiggerLinuxLoadKernelSymbolsRelative: Loading symbols from given offset table failed: %Rrc\n", rc));
2115 RTMemTmpFree(pauSymOff);
2116 }
2117 else
2118 Log(("dbgDiggerLinuxLoadKernelSymbolsRelative: Reading symbol addresses at %RGv failed: %Rrc\n",
2119 pThis->AddrKernelAddresses.FlatPtr, rc));
2120 RTMemFree(pai32Offsets);
2121
2122 return rc;
2123}
2124
2125
2126/**
2127 * Loads the kernel symbols.
2128 *
2129 * @returns VBox status code.
2130 * @param pUVM The user mode VM handle.
2131 * @param pVMM The VMM function table.
2132 * @param pThis The Linux digger data.
2133 */
2134static int dbgDiggerLinuxLoadKernelSymbols(PUVM pUVM, PCVMMR3VTABLE pVMM, PDBGDIGGERLINUX pThis)
2135{
2136 /*
2137 * First the kernel itself.
2138 */
2139 if (pThis->fRelKrnlAddr)
2140 return dbgDiggerLinuxLoadKernelSymbolsRelative(pUVM, pVMM, pThis);
2141 return dbgDiggerLinuxLoadKernelSymbolsAbsolute(pUVM, pVMM, pThis);
2142}
2143
2144
2145/*
2146 * The module structure changed it was easier to produce different code for
2147 * each version of the structure. The C preprocessor rules!
2148 */
2149#define LNX_TEMPLATE_HEADER "DBGPlugInLinuxModuleCodeTmpl.cpp.h"
2150
2151#define LNX_BIT_SUFFIX _amd64
2152#define LNX_PTR_T uint64_t
2153#define LNX_64BIT 1
2154#include "DBGPlugInLinuxModuleVerTmpl.cpp.h"
2155
2156#define LNX_BIT_SUFFIX _x86
2157#define LNX_PTR_T uint32_t
2158#define LNX_64BIT 0
2159#include "DBGPlugInLinuxModuleVerTmpl.cpp.h"
2160
2161#undef LNX_TEMPLATE_HEADER
2162
2163static const struct
2164{
2165 uint32_t uVersion;
2166 bool f64Bit;
2167 uint64_t (*pfnProcessModule)(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, PDBGFADDRESS pAddrModule);
2168} g_aModVersions[] =
2169{
2170#define LNX_TEMPLATE_HEADER "DBGPlugInLinuxModuleTableEntryTmpl.cpp.h"
2171
2172#define LNX_BIT_SUFFIX _amd64
2173#define LNX_64BIT 1
2174#include "DBGPlugInLinuxModuleVerTmpl.cpp.h"
2175
2176#define LNX_BIT_SUFFIX _x86
2177#define LNX_64BIT 0
2178#include "DBGPlugInLinuxModuleVerTmpl.cpp.h"
2179
2180#undef LNX_TEMPLATE_HEADER
2181};
2182
2183
2184/**
2185 * Tries to find and process the module list.
2186 *
2187 * @returns VBox status code.
2188 * @param pThis The Linux digger data.
2189 * @param pUVM The user mode VM handle.
2190 * @param pVMM The VMM function table.
2191 */
2192static int dbgDiggerLinuxLoadModules(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM)
2193{
2194 /*
2195 * Locate the list head.
2196 */
2197 RTDBGAS hAs = pVMM->pfnDBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
2198 RTDBGSYMBOL SymInfo;
2199 int rc = RTDbgAsSymbolByName(hAs, "vmlinux!modules", &SymInfo, NULL);
2200 RTDbgAsRelease(hAs);
2201 if (RT_FAILURE(rc))
2202 return VERR_NOT_FOUND;
2203
2204 if (RT_FAILURE(rc))
2205 {
2206 LogRel(("dbgDiggerLinuxLoadModules: Failed to locate the module list (%Rrc).\n", rc));
2207 return VERR_NOT_FOUND;
2208 }
2209
2210 /*
2211 * Read the list anchor.
2212 */
2213 union
2214 {
2215 uint32_t volatile u32Pair[2];
2216 uint64_t u64Pair[2];
2217 } uListAnchor;
2218 DBGFADDRESS Addr;
2219 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, SymInfo.Value),
2220 &uListAnchor, pThis->f64Bit ? sizeof(uListAnchor.u64Pair) : sizeof(uListAnchor.u32Pair));
2221 if (RT_FAILURE(rc))
2222 {
2223 LogRel(("dbgDiggerLinuxLoadModules: Error reading list anchor at %RX64: %Rrc\n", SymInfo.Value, rc));
2224 return VERR_NOT_FOUND;
2225 }
2226 if (!pThis->f64Bit)
2227 {
2228 uListAnchor.u64Pair[1] = uListAnchor.u32Pair[1];
2229 ASMCompilerBarrier();
2230 uListAnchor.u64Pair[0] = uListAnchor.u32Pair[0];
2231 }
2232
2233 if (pThis->uKrnlVer == 0)
2234 {
2235 LogRel(("dbgDiggerLinuxLoadModules: No valid kernel version given: %#x\n", pThis->uKrnlVer));
2236 return VERR_NOT_FOUND;
2237 }
2238
2239 /*
2240 * Find the g_aModVersion entry that fits the best.
2241 * ASSUMES strict descending order by bitcount and version.
2242 */
2243 Assert(g_aModVersions[0].f64Bit == true);
2244 unsigned i = 0;
2245 if (!pThis->f64Bit)
2246 while (i < RT_ELEMENTS(g_aModVersions) && g_aModVersions[i].f64Bit)
2247 i++;
2248 while ( i < RT_ELEMENTS(g_aModVersions)
2249 && g_aModVersions[i].f64Bit == pThis->f64Bit
2250 && pThis->uKrnlVer < g_aModVersions[i].uVersion)
2251 i++;
2252 if (i >= RT_ELEMENTS(g_aModVersions))
2253 {
2254 LogRel(("dbgDiggerLinuxLoadModules: Failed to find anything matching version: %u.%u.%u\n",
2255 pThis->uKrnlVerMaj, pThis->uKrnlVerMin, pThis->uKrnlVerBld));
2256 return VERR_NOT_FOUND;
2257 }
2258
2259 /*
2260 * Walk the list.
2261 */
2262 uint64_t uModAddr = uListAnchor.u64Pair[0];
2263 for (size_t iModule = 0; iModule < 4096 && uModAddr != SymInfo.Value && uModAddr != 0; iModule++)
2264 uModAddr = g_aModVersions[i].pfnProcessModule(pThis, pUVM, pVMM, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, uModAddr));
2265
2266 return VINF_SUCCESS;
2267}
2268
2269
2270/**
2271 * Checks if there is a likely kallsyms_names fragment at pHitAddr.
2272 *
2273 * @returns true if it's a likely fragment, false if not.
2274 * @param pUVM The user mode VM handle.
2275 * @param pVMM The VMM function table.
2276 * @param pHitAddr The address where paNeedle was found.
2277 * @param pabNeedle The fragment we've been searching for.
2278 * @param cbNeedle The length of the fragment.
2279 */
2280static bool dbgDiggerLinuxIsLikelyNameFragment(PUVM pUVM, PCVMMR3VTABLE pVMM, PCDBGFADDRESS pHitAddr,
2281 uint8_t const *pabNeedle, uint8_t cbNeedle)
2282{
2283 /*
2284 * Examples of lead and tail bytes of our choosen needle in a randomly
2285 * picked kernel:
2286 * k o b j
2287 * 22 6b 6f 62 6a aa
2288 * fc 6b 6f 62 6a aa
2289 * 82 6b 6f 62 6a 5f - ascii trail byte (_).
2290 * ee 6b 6f 62 6a aa
2291 * fc 6b 6f 62 6a 5f - ascii trail byte (_).
2292 * 0a 74 6b 6f 62 6a 5f ea - ascii lead (t) and trail (_) bytes.
2293 * 0b 54 6b 6f 62 6a aa - ascii lead byte (T).
2294 * ... omitting 29 samples similar to the last two ...
2295 * d8 6b 6f 62 6a aa
2296 * d8 6b 6f 62 6a aa
2297 * d8 6b 6f 62 6a aa
2298 * d8 6b 6f 62 6a aa
2299 * f9 5f 6b 6f 62 6a 5f 94 - ascii lead and trail bytes (_)
2300 * f9 5f 6b 6f 62 6a 0c - ascii lead byte (_).
2301 * fd 6b 6f 62 6a 0f
2302 * ... enough.
2303 */
2304 uint8_t abBuf[32];
2305 DBGFADDRESS ReadAddr = *pHitAddr;
2306 pVMM->pfnDBGFR3AddrSub(&ReadAddr, 2);
2307 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &ReadAddr, abBuf, 2 + cbNeedle + 2);
2308 if (RT_SUCCESS(rc))
2309 {
2310 if (memcmp(&abBuf[2], pabNeedle, cbNeedle) == 0) /* paranoia */
2311 {
2312 uint8_t const bLead = abBuf[1] == '_' || abBuf[1] == 'T' || abBuf[1] == 't' ? abBuf[0] : abBuf[1];
2313 uint8_t const offTail = 2 + cbNeedle;
2314 uint8_t const bTail = abBuf[offTail] == '_' ? abBuf[offTail] : abBuf[offTail + 1];
2315 if ( bLead >= 1 && (bLead < 0x20 || bLead >= 0x80)
2316 && bTail >= 1 && (bTail < 0x20 || bTail >= 0x80))
2317 return true;
2318 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: bLead=%#x bTail=%#x (offTail=%#x)\n",
2319 pHitAddr->FlatPtr, bLead, bTail, offTail));
2320 }
2321 else
2322 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: Needle changed!\n", pHitAddr->FlatPtr));
2323 }
2324 else
2325 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: %Rrc\n", pHitAddr->FlatPtr, rc));
2326
2327 return false;
2328}
2329
2330/**
2331 * Tries to find and load the kernel symbol table with the given needle.
2332 *
2333 * @returns VBox status code.
2334 * @param pThis The Linux digger data.
2335 * @param pUVM The user mode VM handle.
2336 * @param pVMM The VMM function table.
2337 * @param pabNeedle The needle to use for searching.
2338 * @param cbNeedle Size of the needle in bytes.
2339 */
2340static int dbgDiggerLinuxFindSymbolTableFromNeedle(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
2341 uint8_t const *pabNeedle, uint8_t cbNeedle)
2342{
2343 /*
2344 * Go looking for the kallsyms table. If it's there, it will be somewhere
2345 * after the linux_banner symbol, so use it for starting the search.
2346 */
2347 int rc = VINF_SUCCESS;
2348 DBGFADDRESS CurAddr = pThis->AddrLinuxBanner;
2349 uint32_t cbLeft = LNX_MAX_KERNEL_SIZE;
2350 while (cbLeft > 4096)
2351 {
2352 DBGFADDRESS HitAddr;
2353 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &CurAddr, cbLeft, 1 /*uAlign*/,
2354 pabNeedle, cbNeedle, &HitAddr);
2355 if (RT_FAILURE(rc))
2356 break;
2357 if (dbgDiggerLinuxIsLikelyNameFragment(pUVM, pVMM, &HitAddr, pabNeedle, cbNeedle))
2358 {
2359 /* There will be another hit near by. */
2360 pVMM->pfnDBGFR3AddrAdd(&HitAddr, 1);
2361 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, LNX_MAX_KALLSYMS_NAMES_SIZE, 1 /*uAlign*/,
2362 pabNeedle, cbNeedle, &HitAddr);
2363 if ( RT_SUCCESS(rc)
2364 && dbgDiggerLinuxIsLikelyNameFragment(pUVM, pVMM, &HitAddr, pabNeedle, cbNeedle))
2365 {
2366 /*
2367 * We've got a very likely candidate for a location inside kallsyms_names.
2368 * Try find the start of it, that is to say, try find kallsyms_num_syms.
2369 * kallsyms_num_syms is aligned on sizeof(unsigned long) boundrary
2370 */
2371 rc = dbgDiggerLinuxFindStartOfNamesAndSymbolCount(pUVM, pVMM, pThis, &HitAddr);
2372 if (RT_SUCCESS(rc))
2373 rc = dbgDiggerLinuxFindEndOfNamesAndMore(pUVM, pVMM, pThis, &HitAddr);
2374 if (RT_SUCCESS(rc))
2375 rc = dbgDiggerLinuxFindTokenIndex(pUVM, pVMM, pThis);
2376 if (RT_SUCCESS(rc))
2377 rc = dbgDiggerLinuxLoadKernelSymbols(pUVM, pVMM, pThis);
2378 if (RT_SUCCESS(rc))
2379 {
2380 rc = dbgDiggerLinuxLoadModules(pThis, pUVM, pVMM);
2381 break;
2382 }
2383 }
2384 }
2385
2386 /*
2387 * Advance.
2388 */
2389 RTGCUINTPTR cbDistance = HitAddr.FlatPtr - CurAddr.FlatPtr + cbNeedle;
2390 if (RT_UNLIKELY(cbDistance >= cbLeft))
2391 {
2392 Log(("dbgDiggerLinuxInit: Failed to find kallsyms\n"));
2393 break;
2394 }
2395 cbLeft -= cbDistance;
2396 pVMM->pfnDBGFR3AddrAdd(&CurAddr, cbDistance);
2397 }
2398
2399 return rc;
2400}
2401
2402/**
2403 * Skips whitespace and comments in the given config returning the pointer
2404 * to the first non whitespace character.
2405 *
2406 * @returns Pointer to the first non whitespace character or NULL if the end
2407 * of the string was reached.
2408 * @param pszCfg The config string.
2409 */
2410static const char *dbgDiggerLinuxCfgSkipWhitespace(const char *pszCfg)
2411{
2412 do
2413 {
2414 while ( *pszCfg != '\0'
2415 && ( RT_C_IS_SPACE(*pszCfg)
2416 || *pszCfg == '\n'))
2417 pszCfg++;
2418
2419 /* Do we have a comment? Skip it. */
2420 if (*pszCfg == '#')
2421 {
2422 while ( *pszCfg != '\n'
2423 && *pszCfg != '\0')
2424 pszCfg++;
2425 }
2426 } while ( *pszCfg != '\0'
2427 && ( RT_C_IS_SPACE(*pszCfg)
2428 || *pszCfg == '\n'
2429 || *pszCfg == '#'));
2430
2431 return pszCfg;
2432}
2433
2434/**
2435 * Parses an identifier at the given position.
2436 *
2437 * @returns VBox status code.
2438 * @param pszCfg The config data.
2439 * @param ppszCfgNext Where to store the pointer to the data following the identifier.
2440 * @param ppszIde Where to store the pointer to the identifier on success.
2441 * Free with RTStrFree().
2442 */
2443static int dbgDiggerLinuxCfgParseIde(const char *pszCfg, const char **ppszCfgNext, char **ppszIde)
2444{
2445 int rc = VINF_SUCCESS;
2446 size_t cchIde = 0;
2447
2448 while ( *pszCfg != '\0'
2449 && ( RT_C_IS_ALNUM(*pszCfg)
2450 || *pszCfg == '_'))
2451 {
2452 cchIde++;
2453 pszCfg++;
2454 }
2455
2456 if (cchIde)
2457 {
2458 *ppszIde = RTStrDupN(pszCfg - cchIde, cchIde);
2459 if (!*ppszIde)
2460 rc = VERR_NO_STR_MEMORY;
2461 }
2462
2463 *ppszCfgNext = pszCfg;
2464 return rc;
2465}
2466
2467/**
2468 * Parses a value for a config item.
2469 *
2470 * @returns VBox status code.
2471 * @param pszCfg The config data.
2472 * @param ppszCfgNext Where to store the pointer to the data following the identifier.
2473 * @param ppCfgItem Where to store the created config item on success.
2474 */
2475static int dbgDiggerLinuxCfgParseVal(const char *pszCfg, const char **ppszCfgNext,
2476 PDBGDIGGERLINUXCFGITEM *ppCfgItem)
2477{
2478 int rc = VINF_SUCCESS;
2479 PDBGDIGGERLINUXCFGITEM pCfgItem = NULL;
2480
2481 if (RT_C_IS_DIGIT(*pszCfg) || *pszCfg == '-')
2482 {
2483 /* Parse the number. */
2484 int64_t i64Num;
2485 rc = RTStrToInt64Ex(pszCfg, (char **)ppszCfgNext, 0, &i64Num);
2486 if ( RT_SUCCESS(rc)
2487 || rc == VWRN_TRAILING_CHARS
2488 || rc == VWRN_TRAILING_SPACES)
2489 {
2490 pCfgItem = (PDBGDIGGERLINUXCFGITEM)RTMemAllocZ(sizeof(DBGDIGGERLINUXCFGITEM));
2491 if (pCfgItem)
2492 {
2493 pCfgItem->enmType = DBGDIGGERLINUXCFGITEMTYPE_NUMBER;
2494 pCfgItem->u.i64Num = i64Num;
2495 }
2496 else
2497 rc = VERR_NO_MEMORY;
2498 }
2499 }
2500 else if (*pszCfg == '\"')
2501 {
2502 /* Parse a string. */
2503 const char *pszCfgCur = pszCfg + 1;
2504 while ( *pszCfgCur != '\0'
2505 && *pszCfgCur != '\"')
2506 pszCfgCur++;
2507
2508 if (*pszCfgCur == '\"')
2509 {
2510 pCfgItem = (PDBGDIGGERLINUXCFGITEM)RTMemAllocZ(RT_UOFFSETOF_DYN(DBGDIGGERLINUXCFGITEM,
2511 u.aszString[pszCfgCur - pszCfg + 1]));
2512 if (pCfgItem)
2513 {
2514 pCfgItem->enmType = DBGDIGGERLINUXCFGITEMTYPE_STRING;
2515 RTStrCopyEx(&pCfgItem->u.aszString[0], pszCfgCur - pszCfg + 1, pszCfg, pszCfgCur - pszCfg);
2516 *ppszCfgNext = pszCfgCur + 1;
2517 }
2518 else
2519 rc = VERR_NO_MEMORY;
2520 }
2521 else
2522 rc = VERR_INVALID_STATE;
2523 }
2524 else if ( *pszCfg == 'y'
2525 || *pszCfg == 'm')
2526 {
2527 /* Included or module. */
2528 pCfgItem = (PDBGDIGGERLINUXCFGITEM)RTMemAllocZ(sizeof(DBGDIGGERLINUXCFGITEM));
2529 if (pCfgItem)
2530 {
2531 pCfgItem->enmType = DBGDIGGERLINUXCFGITEMTYPE_FLAG;
2532 pCfgItem->u.fModule = *pszCfg == 'm';
2533 }
2534 else
2535 rc = VERR_NO_MEMORY;
2536 pszCfg++;
2537 *ppszCfgNext = pszCfg;
2538 }
2539 else
2540 rc = VERR_INVALID_STATE;
2541
2542 if (RT_SUCCESS(rc))
2543 *ppCfgItem = pCfgItem;
2544 else if (pCfgItem)
2545 RTMemFree(pCfgItem);
2546
2547 return rc;
2548}
2549
2550/**
2551 * Parses the given kernel config and creates the config database.
2552 *
2553 * @returns VBox status code
2554 * @param pThis The Linux digger data.
2555 * @param pszCfg The config string.
2556 */
2557static int dbgDiggerLinuxCfgParse(PDBGDIGGERLINUX pThis, const char *pszCfg)
2558{
2559 int rc = VINF_SUCCESS;
2560
2561 /*
2562 * The config is a text file with the following elements:
2563 * # starts a comment which goes till the end of the line
2564 * <Ide>=<val> where <Ide> is an identifier consisting of
2565 * alphanumerical characters (including _)
2566 * <val> denotes the value for the identifier and can have the following
2567 * formats:
2568 * (-)[0-9]* for numbers
2569 * "..." for a string value
2570 * m when a feature is enabled as a module
2571 * y when a feature is enabled
2572 * Newlines are used as a separator between values and mark the end
2573 * of a comment
2574 */
2575 const char *pszCfgCur = pszCfg;
2576 while ( RT_SUCCESS(rc)
2577 && *pszCfgCur != '\0')
2578 {
2579 /* Start skipping the whitespace. */
2580 pszCfgCur = dbgDiggerLinuxCfgSkipWhitespace(pszCfgCur);
2581 if ( pszCfgCur
2582 && *pszCfgCur != '\0')
2583 {
2584 char *pszIde = NULL;
2585 /* Must be an identifier, parse it. */
2586 rc = dbgDiggerLinuxCfgParseIde(pszCfgCur, &pszCfgCur, &pszIde);
2587 if (RT_SUCCESS(rc))
2588 {
2589 /*
2590 * Skip whitespace again (shouldn't be required because = follows immediately
2591 * in the observed configs).
2592 */
2593 pszCfgCur = dbgDiggerLinuxCfgSkipWhitespace(pszCfgCur);
2594 if ( pszCfgCur
2595 && *pszCfgCur == '=')
2596 {
2597 pszCfgCur++;
2598 pszCfgCur = dbgDiggerLinuxCfgSkipWhitespace(pszCfgCur);
2599 if ( pszCfgCur
2600 && *pszCfgCur != '\0')
2601 {
2602 /* Get the value. */
2603 PDBGDIGGERLINUXCFGITEM pCfgItem = NULL;
2604 rc = dbgDiggerLinuxCfgParseVal(pszCfgCur, &pszCfgCur, &pCfgItem);
2605 if (RT_SUCCESS(rc))
2606 {
2607 pCfgItem->Core.pszString = pszIde;
2608 bool fRc = RTStrSpaceInsert(&pThis->hCfgDb, &pCfgItem->Core);
2609 if (!fRc)
2610 {
2611 RTStrFree(pszIde);
2612 RTMemFree(pCfgItem);
2613 rc = VERR_INVALID_STATE;
2614 }
2615 }
2616 }
2617 else
2618 rc = VERR_EOF;
2619 }
2620 else
2621 rc = VERR_INVALID_STATE;
2622 }
2623
2624 if (RT_FAILURE(rc))
2625 RTStrFree(pszIde);
2626 }
2627 else
2628 break; /* Reached the end of the config. */
2629 }
2630
2631 if (RT_FAILURE(rc))
2632 dbgDiggerLinuxCfgDbDestroy(pThis);
2633
2634 return rc;
2635}
2636
2637/**
2638 * Decompresses the given config and validates the UTF-8 encoding.
2639 *
2640 * @returns VBox status code.
2641 * @param pbCfgComp The compressed config.
2642 * @param cbCfgComp Size of the compressed config.
2643 * @param ppszCfg Where to store the pointer to the decompressed config
2644 * on success.
2645 */
2646static int dbgDiggerLinuxCfgDecompress(const uint8_t *pbCfgComp, size_t cbCfgComp, char **ppszCfg)
2647{
2648 int rc = VINF_SUCCESS;
2649 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
2650
2651 rc = RTVfsIoStrmFromBuffer(RTFILE_O_READ, pbCfgComp, cbCfgComp, &hVfsIos);
2652 if (RT_SUCCESS(rc))
2653 {
2654 RTVFSIOSTREAM hVfsIosDecomp = NIL_RTVFSIOSTREAM;
2655 rc = RTZipGzipDecompressIoStream(hVfsIos, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR, &hVfsIosDecomp);
2656 if (RT_SUCCESS(rc))
2657 {
2658 char *pszCfg = NULL;
2659 size_t cchCfg = 0;
2660 size_t cbRead = 0;
2661
2662 do
2663 {
2664 uint8_t abBuf[_64K];
2665 rc = RTVfsIoStrmRead(hVfsIosDecomp, abBuf, sizeof(abBuf), true /*fBlocking*/, &cbRead);
2666 if (rc == VINF_EOF && cbRead == 0)
2667 rc = VINF_SUCCESS;
2668 if ( RT_SUCCESS(rc)
2669 && cbRead > 0)
2670 {
2671 /* Append data. */
2672 char *pszCfgNew = pszCfg;
2673 rc = RTStrRealloc(&pszCfgNew, cchCfg + cbRead + 1);
2674 if (RT_SUCCESS(rc))
2675 {
2676 pszCfg = pszCfgNew;
2677 memcpy(pszCfg + cchCfg, &abBuf[0], cbRead);
2678 cchCfg += cbRead;
2679 pszCfg[cchCfg] = '\0'; /* Enforce string termination. */
2680 }
2681 }
2682 } while (RT_SUCCESS(rc) && cbRead > 0);
2683
2684 if (RT_SUCCESS(rc))
2685 *ppszCfg = pszCfg;
2686 else if (RT_FAILURE(rc) && pszCfg)
2687 RTStrFree(pszCfg);
2688
2689 RTVfsIoStrmRelease(hVfsIosDecomp);
2690 }
2691 RTVfsIoStrmRelease(hVfsIos);
2692 }
2693
2694 return rc;
2695}
2696
2697/**
2698 * Reads and decodes the compressed kernel config.
2699 *
2700 * @returns VBox status code.
2701 * @param pThis The Linux digger data.
2702 * @param pUVM The user mode VM handle.
2703 * @param pVMM The VMM function table.
2704 * @param pAddrStart The start address of the compressed config.
2705 * @param cbCfgComp The size of the compressed config.
2706 */
2707static int dbgDiggerLinuxCfgDecode(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
2708 PCDBGFADDRESS pAddrStart, size_t cbCfgComp)
2709{
2710 int rc = VINF_SUCCESS;
2711 uint8_t *pbCfgComp = (uint8_t *)RTMemTmpAlloc(cbCfgComp);
2712 if (!pbCfgComp)
2713 return VERR_NO_MEMORY;
2714
2715 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pAddrStart, pbCfgComp, cbCfgComp);
2716 if (RT_SUCCESS(rc))
2717 {
2718 char *pszCfg = NULL;
2719 rc = dbgDiggerLinuxCfgDecompress(pbCfgComp, cbCfgComp, &pszCfg);
2720 if (RT_SUCCESS(rc))
2721 {
2722 if (RTStrIsValidEncoding(pszCfg))
2723 rc = dbgDiggerLinuxCfgParse(pThis, pszCfg);
2724 else
2725 rc = VERR_INVALID_UTF8_ENCODING;
2726 RTStrFree(pszCfg);
2727 }
2728 }
2729
2730 RTMemFree(pbCfgComp);
2731 return rc;
2732}
2733
2734/**
2735 * Tries to find the compressed kernel config in the kernel address space
2736 * and sets up the config database.
2737 *
2738 * @returns VBox status code.
2739 * @param pThis The Linux digger data.
2740 * @param pUVM The user mode VM handle.
2741 * @param pVMM The VMM function table.
2742 */
2743static int dbgDiggerLinuxCfgFind(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM)
2744{
2745 /*
2746 * Go looking for the IKCFG_ST string which indicates the start
2747 * of the compressed config file.
2748 */
2749 static const uint8_t s_abCfgNeedleStart[] = "IKCFG_ST";
2750 static const uint8_t s_abCfgNeedleEnd[] = "IKCFG_ED";
2751 int rc = VINF_SUCCESS;
2752 DBGFADDRESS CurAddr = pThis->AddrLinuxBanner;
2753 uint32_t cbLeft = LNX_MAX_KERNEL_SIZE;
2754 while (cbLeft > 4096)
2755 {
2756 DBGFADDRESS HitAddrStart;
2757 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &CurAddr, cbLeft, 1 /*uAlign*/,
2758 s_abCfgNeedleStart, sizeof(s_abCfgNeedleStart) - 1, &HitAddrStart);
2759 if (RT_FAILURE(rc))
2760 break;
2761
2762 /* Check for the end marker which shouldn't be that far away. */
2763 pVMM->pfnDBGFR3AddrAdd(&HitAddrStart, sizeof(s_abCfgNeedleStart) - 1);
2764 DBGFADDRESS HitAddrEnd;
2765 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /* idCpu */, &HitAddrStart, LNX_MAX_COMPRESSED_CFG_SIZE,
2766 1 /* uAlign */, s_abCfgNeedleEnd, sizeof(s_abCfgNeedleEnd) - 1, &HitAddrEnd);
2767 if (RT_SUCCESS(rc))
2768 {
2769 /* Allocate a buffer to hold the compressed data between the markers and fetch it. */
2770 RTGCUINTPTR cbCfg = HitAddrEnd.FlatPtr - HitAddrStart.FlatPtr;
2771 Assert(cbCfg == (size_t)cbCfg);
2772 rc = dbgDiggerLinuxCfgDecode(pThis, pUVM, pVMM, &HitAddrStart, cbCfg);
2773 if (RT_SUCCESS(rc))
2774 break;
2775 }
2776
2777 /*
2778 * Advance.
2779 */
2780 RTGCUINTPTR cbDistance = HitAddrStart.FlatPtr - CurAddr.FlatPtr + sizeof(s_abCfgNeedleStart) - 1;
2781 if (RT_UNLIKELY(cbDistance >= cbLeft))
2782 {
2783 LogFunc(("Failed to find compressed kernel config\n"));
2784 break;
2785 }
2786 cbLeft -= cbDistance;
2787 pVMM->pfnDBGFR3AddrAdd(&CurAddr, cbDistance);
2788 }
2789
2790 return rc;
2791}
2792
2793/**
2794 * Probes for a Linux kernel starting at the given address.
2795 *
2796 * @returns Flag whether something which looks like a valid Linux kernel was found.
2797 * @param pThis The Linux digger data.
2798 * @param pUVM The user mode VM handle.
2799 * @param pVMM The VMM function table.
2800 * @param uAddrStart The address to start scanning at.
2801 * @param cbScan How much to scan.
2802 */
2803static bool dbgDiggerLinuxProbeWithAddr(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
2804 RTGCUINTPTR uAddrStart, size_t cbScan)
2805{
2806 /*
2807 * Look for "Linux version " at the start of the rodata segment.
2808 * Hope that this comes before any message buffer or other similar string.
2809 */
2810 DBGFADDRESS KernelAddr;
2811 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &KernelAddr, uAddrStart);
2812 DBGFADDRESS HitAddr;
2813 int rc = pVMM->pfnDBGFR3MemScan(pUVM, 0, &KernelAddr, cbScan, 1,
2814 g_abLinuxVersion, sizeof(g_abLinuxVersion) - 1, &HitAddr);
2815 if (RT_SUCCESS(rc))
2816 {
2817 char szTmp[128];
2818 char const *pszX = &szTmp[sizeof(g_abLinuxVersion) - 1];
2819 rc = pVMM->pfnDBGFR3MemReadString(pUVM, 0, &HitAddr, szTmp, sizeof(szTmp));
2820 if ( RT_SUCCESS(rc)
2821 && ( ( pszX[0] == '2' /* 2.x.y with x in {0..6} */
2822 && pszX[1] == '.'
2823 && pszX[2] >= '0'
2824 && pszX[2] <= '6')
2825 || ( pszX[0] >= '3' /* 3.x, 4.x, ... 9.x */
2826 && pszX[0] <= '9'
2827 && pszX[1] == '.'
2828 && pszX[2] >= '0'
2829 && pszX[2] <= '9')
2830 )
2831 )
2832 {
2833 pThis->AddrKernelBase = KernelAddr;
2834 pThis->AddrLinuxBanner = HitAddr;
2835 return true;
2836 }
2837 }
2838
2839 return false;
2840}
2841
2842/**
2843 * Probes for a Linux kernel which has KASLR enabled.
2844 *
2845 * @returns Flag whether a possible candidate location was found.
2846 * @param pThis The Linux digger data.
2847 * @param pUVM The user mode VM handle.
2848 * @param pVMM The VMM function table.
2849 */
2850static bool dbgDiggerLinuxProbeKaslr(PDBGDIGGERLINUX pThis, PUVM pUVM, PCVMMR3VTABLE pVMM)
2851{
2852 /**
2853 * With KASLR the kernel is loaded at a different address at each boot making detection
2854 * more difficult for us.
2855 *
2856 * The randomization is done in arch/x86/boot/compressed/kaslr.c:choose_random_location() (as of Nov 2017).
2857 * At the end of the method a random offset is chosen using find_random_virt_addr() which is added to the
2858 * kernel map start in the caller (the start of the kernel depends on the bit size, see LNX32_KERNEL_ADDRESS_START
2859 * and LNX64_KERNEL_ADDRESS_START for 32bit and 64bit kernels respectively).
2860 * The lowest offset possible is LOAD_PHYSICAL_ADDR which is defined in arch/x86/include/asm/boot.h
2861 * using CONFIG_PHYSICAL_START aligned to CONFIG_PHYSICAL_ALIGN.
2862 * The default CONFIG_PHYSICAL_START and CONFIG_PHYSICAL_ALIGN are both 0x1000000 no matter whether a 32bit
2863 * or a 64bit kernel is used. So the lowest offset to the kernel start address is 0x1000000.
2864 * The find_random_virt_addr() the number of possible slots where the kernel can be placed based on the image size
2865 * is calculated using the following formula:
2866 * cSlots = ((KERNEL_IMAGE_SIZE - 0x1000000 (minimum) - image_size) / 0x1000000 (CONFIG_PHYSICAL_ALIGN)) + 1
2867 *
2868 * KERNEL_IMAGE_SIZE is 1GB for 64bit kernels and 512MB for 32bit kernels, so the maximum number of slots (resulting
2869 * in the largest possible offset) can be achieved when image_size (which contains the real size of the kernel image
2870 * which is unknown for us) goes to 0 and a 1GB KERNEL_IMAGE_SIZE is assumed. With that the biggest cSlots which can be
2871 * achieved is 64. The chosen random offset is taken from a random long integer using kaslr_get_random_long() modulo the
2872 * number of slots which selects a slot between 0 and 63. The final offset is calculated using:
2873 * offAddr = random_addr * 0x1000000 (CONFIG_PHYSICAL_ALIGN) + 0x1000000 (minimum)
2874 *
2875 * So the highest offset the kernel can start is 0x40000000 which is 1GB (plus the maximum kernel size we defined).
2876 */
2877 if (dbgDiggerLinuxProbeWithAddr(pThis, pUVM, pVMM, LNX64_KERNEL_ADDRESS_START, _1G + LNX_MAX_KERNEL_SIZE))
2878 return true;
2879
2880 /*
2881 * 32bit variant, makes sure we don't exceed the 4GB address space or DBGFR3MemScan() returns VERR_DBGF_MEM_NOT_FOUND immediately
2882 * without searching the remainder of the address space.
2883 *
2884 * The default split is 3GB userspace and 1GB kernel, so we just search the entire upper 1GB kernel space.
2885 */
2886 if (dbgDiggerLinuxProbeWithAddr(pThis, pUVM, pVMM, LNX32_KERNEL_ADDRESS_START, _4G - LNX32_KERNEL_ADDRESS_START))
2887 return true;
2888
2889 return false;
2890}
2891
2892/**
2893 * @copydoc DBGFOSREG::pfnInit
2894 */
2895static DECLCALLBACK(int) dbgDiggerLinuxInit(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
2896{
2897 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
2898 Assert(!pThis->fValid);
2899
2900 char szVersion[256] = "Linux version 4.19.0";
2901 int rc = pVMM->pfnDBGFR3MemReadString(pUVM, 0, &pThis->AddrLinuxBanner, &szVersion[0], sizeof(szVersion));
2902 if (RT_SUCCESS(rc))
2903 {
2904 /*
2905 * Get a numerical version number.
2906 */
2907 const char *pszVersion = szVersion;
2908 while (*pszVersion && !RT_C_IS_DIGIT(*pszVersion))
2909 pszVersion++;
2910
2911 size_t offVersion = 0;
2912 uint32_t uMajor = 0;
2913 while (pszVersion[offVersion] && RT_C_IS_DIGIT(pszVersion[offVersion]))
2914 uMajor = uMajor * 10 + pszVersion[offVersion++] - '0';
2915
2916 if (pszVersion[offVersion] == '.')
2917 offVersion++;
2918
2919 uint32_t uMinor = 0;
2920 while (pszVersion[offVersion] && RT_C_IS_DIGIT(pszVersion[offVersion]))
2921 uMinor = uMinor * 10 + pszVersion[offVersion++] - '0';
2922
2923 if (pszVersion[offVersion] == '.')
2924 offVersion++;
2925
2926 uint32_t uBuild = 0;
2927 while (pszVersion[offVersion] && RT_C_IS_DIGIT(pszVersion[offVersion]))
2928 uBuild = uBuild * 10 + pszVersion[offVersion++] - '0';
2929
2930 pThis->uKrnlVer = LNX_MK_VER(uMajor, uMinor, uBuild);
2931 pThis->uKrnlVerMaj = uMajor;
2932 pThis->uKrnlVerMin = uMinor;
2933 pThis->uKrnlVerBld = uBuild;
2934 if (pThis->uKrnlVer == 0)
2935 LogRel(("dbgDiggerLinuxInit: Failed to parse version string: %s\n", pszVersion));
2936 }
2937
2938 /*
2939 * Assume 64-bit kernels all live way beyond 32-bit address space.
2940 */
2941 pThis->f64Bit = pThis->AddrLinuxBanner.FlatPtr > UINT32_MAX;
2942 pThis->fRelKrnlAddr = false;
2943
2944 pThis->hCfgDb = NULL;
2945
2946 /*
2947 * Try to find the compressed kernel config and parse it before we try
2948 * to get the symbol table, the config database is required to select
2949 * the method to use.
2950 */
2951 rc = dbgDiggerLinuxCfgFind(pThis, pUVM, pVMM);
2952 if (RT_FAILURE(rc))
2953 LogFlowFunc(("Failed to find kernel config (%Rrc), no config database available\n", rc));
2954
2955 static const uint8_t s_abNeedle[] = "kobj";
2956 rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, pVMM, s_abNeedle, sizeof(s_abNeedle) - 1);
2957 if (RT_FAILURE(rc))
2958 {
2959 /* Try alternate needle (seen on older x86 Linux kernels). */
2960 static const uint8_t s_abNeedleAlt[] = "kobjec";
2961 rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, pVMM, s_abNeedleAlt, sizeof(s_abNeedleAlt) - 1);
2962 if (RT_FAILURE(rc))
2963 {
2964 static const uint8_t s_abNeedleOSuseX86[] = "nmi"; /* OpenSuSe 10.2 x86 */
2965 rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, pVMM, s_abNeedleOSuseX86, sizeof(s_abNeedleOSuseX86) - 1);
2966 }
2967 }
2968
2969 pThis->fValid = true;
2970 return VINF_SUCCESS;
2971}
2972
2973
2974/**
2975 * @copydoc DBGFOSREG::pfnProbe
2976 */
2977static DECLCALLBACK(bool) dbgDiggerLinuxProbe(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
2978{
2979 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
2980
2981 for (unsigned i = 0; i < RT_ELEMENTS(g_au64LnxKernelAddresses); i++)
2982 {
2983 if (dbgDiggerLinuxProbeWithAddr(pThis, pUVM, pVMM, g_au64LnxKernelAddresses[i], LNX_MAX_KERNEL_SIZE))
2984 return true;
2985 }
2986
2987 /* Maybe the kernel uses KASLR. */
2988 if (dbgDiggerLinuxProbeKaslr(pThis, pUVM, pVMM))
2989 return true;
2990
2991 return false;
2992}
2993
2994
2995/**
2996 * @copydoc DBGFOSREG::pfnDestruct
2997 */
2998static DECLCALLBACK(void) dbgDiggerLinuxDestruct(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
2999{
3000 RT_NOREF(pUVM, pVMM, pvData);
3001}
3002
3003
3004/**
3005 * @copydoc DBGFOSREG::pfnConstruct
3006 */
3007static DECLCALLBACK(int) dbgDiggerLinuxConstruct(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
3008{
3009 RT_NOREF(pUVM, pVMM);
3010 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
3011 pThis->IDmesg.u32Magic = DBGFOSIDMESG_MAGIC;
3012 pThis->IDmesg.pfnQueryKernelLog = dbgDiggerLinuxIDmsg_QueryKernelLog;
3013 pThis->IDmesg.u32EndMagic = DBGFOSIDMESG_MAGIC;
3014
3015 return VINF_SUCCESS;
3016}
3017
3018
3019const DBGFOSREG g_DBGDiggerLinux =
3020{
3021 /* .u32Magic = */ DBGFOSREG_MAGIC,
3022 /* .fFlags = */ 0,
3023 /* .cbData = */ sizeof(DBGDIGGERLINUX),
3024 /* .szName = */ "Linux",
3025 /* .pfnConstruct = */ dbgDiggerLinuxConstruct,
3026 /* .pfnDestruct = */ dbgDiggerLinuxDestruct,
3027 /* .pfnProbe = */ dbgDiggerLinuxProbe,
3028 /* .pfnInit = */ dbgDiggerLinuxInit,
3029 /* .pfnRefresh = */ dbgDiggerLinuxRefresh,
3030 /* .pfnTerm = */ dbgDiggerLinuxTerm,
3031 /* .pfnQueryVersion = */ dbgDiggerLinuxQueryVersion,
3032 /* .pfnQueryInterface = */ dbgDiggerLinuxQueryInterface,
3033 /* .pfnStackUnwindAssist = */ dbgDiggerLinuxStackUnwindAssist,
3034 /* .u32EndMagic = */ DBGFOSREG_MAGIC
3035};
3036
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