VirtualBox

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

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

IPRT,DBGF,Diggers: Moved DBGFRETURNTYPE and the unwind state structure to IPRT (dbg.h) in prep for debug module interface and more. Added stack unwind assist callback for the OS diggers so they can identify special stack frames and supply more info via the sure-register-value array and frame flags. Identify and decode NT/AMD64 trap frames.

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