VirtualBox

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

Last change on this file since 84980 was 84980, checked in by vboxsync, 4 years ago

Debugger/DBGPlugInLinux: New heuristic to find the kernel log buffer even if we couldn't get at the symbols, used as a last resort if everything fails.

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