VirtualBox

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

Last change on this file since 76389 was 76389, checked in by vboxsync, 6 years ago

VBox/sup.h: Don't include VBox/err.h for no good reason. bugref:9344

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