VirtualBox

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

Last change on this file since 92706 was 90181, checked in by vboxsync, 3 years ago

Debugger/DBGPlugInLinux: Fix getting at symbols for Linux kernels 4.20.0+, commit 80ffbaa5b1bd98e80e3239a3b8cfda2da433009a made kallsyms_markers and kallsyms_num_syms 32bit, even on 64bit systems. bugref:1098

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