VirtualBox

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

Last change on this file since 69820 was 69820, checked in by vboxsync, 7 years ago

Debugger/Linux: Cope with KASLR being used when probing for a Linux guest

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 105.4 KB
Line 
1/* $Id: DBGPlugInLinux.cpp 69820 2017-11-24 11:43:38Z 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::pfnQueryInterface
1089 */
1090static DECLCALLBACK(void *) dbgDiggerLinuxQueryInterface(PUVM pUVM, void *pvData, DBGFOSINTERFACE enmIf)
1091{
1092 RT_NOREF1(pUVM);
1093 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1094 switch (enmIf)
1095 {
1096 case DBGFOSINTERFACE_DMESG:
1097 return &pThis->IDmesg;
1098
1099 default:
1100 return NULL;
1101 }
1102}
1103
1104
1105/**
1106 * @copydoc DBGFOSREG::pfnQueryVersion
1107 */
1108static DECLCALLBACK(int) dbgDiggerLinuxQueryVersion(PUVM pUVM, void *pvData, char *pszVersion, size_t cchVersion)
1109{
1110 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1111 Assert(pThis->fValid);
1112
1113 /*
1114 * It's all in the linux banner.
1115 */
1116 int rc = DBGFR3MemReadString(pUVM, 0, &pThis->AddrLinuxBanner, pszVersion, cchVersion);
1117 if (RT_SUCCESS(rc))
1118 {
1119 char *pszEnd = RTStrEnd(pszVersion, cchVersion);
1120 AssertReturn(pszEnd, VERR_BUFFER_OVERFLOW);
1121 while ( pszEnd > pszVersion
1122 && RT_C_IS_SPACE(pszEnd[-1]))
1123 pszEnd--;
1124 *pszEnd = '\0';
1125 }
1126 else
1127 RTStrPrintf(pszVersion, cchVersion, "DBGFR3MemRead -> %Rrc", rc);
1128
1129 return rc;
1130}
1131
1132
1133/**
1134 * @copydoc DBGFOSREG::pfnTerm
1135 */
1136static DECLCALLBACK(void) dbgDiggerLinuxTerm(PUVM pUVM, void *pvData)
1137{
1138 RT_NOREF1(pUVM);
1139 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1140 Assert(pThis->fValid);
1141
1142 dbgDiggerLinuxCfgDbDestroy(pThis);
1143 pThis->fValid = false;
1144}
1145
1146
1147/**
1148 * @copydoc DBGFOSREG::pfnRefresh
1149 */
1150static DECLCALLBACK(int) dbgDiggerLinuxRefresh(PUVM pUVM, void *pvData)
1151{
1152 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1153 NOREF(pThis);
1154 Assert(pThis->fValid);
1155
1156 /*
1157 * For now we'll flush and reload everything.
1158 */
1159 dbgDiggerLinuxTerm(pUVM, pvData);
1160 return dbgDiggerLinuxInit(pUVM, pvData);
1161}
1162
1163
1164/**
1165 * Worker for dbgDiggerLinuxFindStartOfNamesAndSymbolCount that update the
1166 * digger data.
1167 *
1168 * @returns VINF_SUCCESS.
1169 * @param pThis The Linux digger data to update.
1170 * @param pAddrKernelNames The kallsyms_names address.
1171 * @param cKernelSymbols The number of kernel symbol.
1172 * @param cbAddress The guest address size.
1173 */
1174static int dbgDiggerLinuxFoundStartOfNames(PDBGDIGGERLINUX pThis, PCDBGFADDRESS pAddrKernelNames,
1175 uint32_t cKernelSymbols, uint32_t cbAddress)
1176{
1177 pThis->cKernelSymbols = cKernelSymbols;
1178 pThis->AddrKernelNames = *pAddrKernelNames;
1179 pThis->AddrKernelAddresses = *pAddrKernelNames;
1180 uint32_t cbSymbolsSkip = (pThis->fRelKrnlAddr ? 2 : 1) * cbAddress; /* Relative addressing introduces kallsyms_relative_base. */
1181 uint32_t cbOffsets = pThis->fRelKrnlAddr ? sizeof(int32_t) : cbAddress; /* Offsets are always 32bits wide for relative addressing. */
1182 uint32_t cbAlign = 0;
1183
1184 /*
1185 * If the number of symbols is odd there is padding to align the following guest pointer
1186 * sized data properly on 64bit systems with relative addressing.
1187 */
1188 if ( pThis->fRelKrnlAddr
1189 && pThis->f64Bit
1190 && (pThis->cKernelSymbols & 1))
1191 cbAlign = sizeof(int32_t);
1192 DBGFR3AddrSub(&pThis->AddrKernelAddresses, cKernelSymbols * cbOffsets + cbSymbolsSkip + cbAlign);
1193
1194 Log(("dbgDiggerLinuxFoundStartOfNames: AddrKernelAddresses=%RGv\n"
1195 "dbgDiggerLinuxFoundStartOfNames: cKernelSymbols=%#x (at %RGv)\n"
1196 "dbgDiggerLinuxFoundStartOfNames: AddrKernelName=%RGv\n",
1197 pThis->AddrKernelAddresses.FlatPtr,
1198 pThis->cKernelSymbols, pThis->AddrKernelNames.FlatPtr - cbAddress,
1199 pThis->AddrKernelNames.FlatPtr));
1200 return VINF_SUCCESS;
1201}
1202
1203
1204/**
1205 * Tries to find the address of the kallsyms_names, kallsyms_num_syms and
1206 * kallsyms_addresses symbols.
1207 *
1208 * The kallsyms_num_syms is read and stored in pThis->cKernelSymbols, while the
1209 * addresses of the other two are stored as pThis->AddrKernelNames and
1210 * pThis->AddrKernelAddresses.
1211 *
1212 * @returns VBox status code, success indicating that all three variables have
1213 * been found and taken down.
1214 * @param pUVM The user mode VM handle.
1215 * @param pThis The Linux digger data.
1216 * @param pHitAddr An address we think is inside kallsyms_names.
1217 */
1218static int dbgDiggerLinuxFindStartOfNamesAndSymbolCount(PUVM pUVM, PDBGDIGGERLINUX pThis, PCDBGFADDRESS pHitAddr)
1219{
1220 /*
1221 * Search backwards in chunks.
1222 */
1223 union
1224 {
1225 uint8_t ab[0x1000];
1226 uint32_t au32[0x1000 / sizeof(uint32_t)];
1227 uint64_t au64[0x1000 / sizeof(uint64_t)];
1228 } uBuf;
1229 uint32_t cbLeft = LNX_MAX_KALLSYMS_NAMES_SIZE;
1230 uint32_t cbBuf = pHitAddr->FlatPtr & (sizeof(uBuf) - 1);
1231 DBGFADDRESS CurAddr = *pHitAddr;
1232 DBGFR3AddrSub(&CurAddr, cbBuf);
1233 cbBuf += sizeof(uint64_t) - 1; /* In case our kobj hit is in the first 4/8 bytes. */
1234 for (;;)
1235 {
1236 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
1237 if (RT_FAILURE(rc))
1238 return rc;
1239
1240 /*
1241 * Since Linux 4.6 there are two different methods to store the kallsyms addresses
1242 * in the image.
1243 *
1244 * The first and longer existing method is to store the absolute addresses in an
1245 * array starting at kallsyms_addresses followed by a field which stores the number
1246 * of kernel symbols called kallsyms_num_syms.
1247 * The newer method is to use offsets stored in kallsyms_offsets and have a base pointer
1248 * to relate the offsets to called kallsyms_relative_base. One entry in kallsyms_offsets is
1249 * always 32bit wide regardless of the guest pointer size (this halves the table on 64bit
1250 * systems) but means more work for us for the 64bit case.
1251 *
1252 * When absolute addresses are used the following assumptions hold:
1253 *
1254 * We assume that the three symbols are aligned on guest pointer boundary.
1255 *
1256 * The boundary between the two tables should be noticable as the number
1257 * is unlikely to be more than 16 millions, there will be at least one zero
1258 * byte where it is, 64-bit will have 5 zero bytes. Zero bytes aren't all
1259 * that common in the kallsyms_names table.
1260 *
1261 * Also the kallsyms_names table starts with a length byte, which means
1262 * we're likely to see a byte in the range 1..31.
1263 *
1264 * The kallsyms_addresses are mostly sorted (except for the start where the
1265 * absolute symbols are), so we'll spot a bunch of kernel addresses
1266 * immediately preceeding the kallsyms_num_syms field.
1267 *
1268 * Lazy bird: If kallsyms_num_syms is on a buffer boundrary, we skip
1269 * the check for kernel addresses preceeding it.
1270 *
1271 * For relative offsets most of the assumptions from above are true too
1272 * except that we have to distinguish between the relative base address and the offsets.
1273 * Every observed kernel has a valid kernel address fo the relative base and kallsyms_relative_base
1274 * always comes before kallsyms_num_syms and is aligned on a guest pointer boundary.
1275 * Offsets are stored before kallsyms_relative_base and don't contain valid kernel addresses.
1276 *
1277 * To distinguish between absolute and relative offsetting we check the data before a candidate
1278 * for kallsyms_num_syms. If all entries before the kallsyms_num_syms candidate are valid kernel
1279 * addresses absolute addresses are assumed. If this is not the case but the first entry before
1280 * kallsyms_num_syms is a valid kernel address we check whether the data before and the possible
1281 * relative base form a valid kernel address and assume relative offsets.
1282 */
1283 if (pThis->f64Bit)
1284 {
1285 uint32_t i = cbBuf / sizeof(uint64_t);
1286 while (i-- > 0)
1287 if ( uBuf.au64[i] <= LNX_MAX_KALLSYMS_SYMBOLS
1288 && uBuf.au64[i] >= LNX_MIN_KALLSYMS_SYMBOLS)
1289 {
1290 uint8_t *pb = (uint8_t *)&uBuf.au64[i + 1];
1291 if ( pb[0] <= LNX_MAX_KALLSYMS_ENC_LENGTH
1292 && pb[0] >= LNX_MIN_KALLSYMS_ENC_LENGTH)
1293 {
1294 /*
1295 * Check whether we have a valid kernel address and try to distinguish
1296 * whether the kernel uses relative offsetting or absolute addresses.
1297 */
1298 if ( (i >= 1 && LNX64_VALID_ADDRESS(uBuf.au64[i - 1]))
1299 && (i >= 2 && !LNX64_VALID_ADDRESS(uBuf.au64[i - 2]))
1300 && (i >= 3 && !LNX64_VALID_ADDRESS(uBuf.au64[i - 3])))
1301 {
1302 RTGCUINTPTR uKrnlRelBase = uBuf.au64[i - 1];
1303 DBGFADDRESS RelAddr = CurAddr;
1304 int32_t aiRelOff[3];
1305 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrAdd(&RelAddr, (i - 1) * sizeof(uint64_t) - sizeof(aiRelOff)),
1306 &aiRelOff[0], sizeof(aiRelOff));
1307 if ( RT_SUCCESS(rc)
1308 && LNX64_VALID_ADDRESS(uKrnlRelBase + aiRelOff[0])
1309 && LNX64_VALID_ADDRESS(uKrnlRelBase + aiRelOff[1])
1310 && LNX64_VALID_ADDRESS(uKrnlRelBase + aiRelOff[2]))
1311 {
1312 Log(("dbgDiggerLinuxFindStartOfNamesAndSymbolCount: relative base %RGv (at %RGv)\n",
1313 uKrnlRelBase, CurAddr.FlatPtr + (i - 1) * sizeof(uint64_t)));
1314 pThis->fRelKrnlAddr = true;
1315 pThis->uKernelRelativeBase = uKrnlRelBase;
1316 return dbgDiggerLinuxFoundStartOfNames(pThis,
1317 DBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint64_t)),
1318 (uint32_t)uBuf.au64[i], sizeof(uint64_t));
1319 }
1320 }
1321
1322 if ( (i <= 0 || LNX64_VALID_ADDRESS(uBuf.au64[i - 1]))
1323 && (i <= 1 || LNX64_VALID_ADDRESS(uBuf.au64[i - 2]))
1324 && (i <= 2 || LNX64_VALID_ADDRESS(uBuf.au64[i - 3])))
1325 return dbgDiggerLinuxFoundStartOfNames(pThis,
1326 DBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint64_t)),
1327 (uint32_t)uBuf.au64[i], sizeof(uint64_t));
1328 }
1329 }
1330 }
1331 else
1332 {
1333 uint32_t i = cbBuf / sizeof(uint32_t);
1334 while (i-- > 0)
1335 if ( uBuf.au32[i] <= LNX_MAX_KALLSYMS_SYMBOLS
1336 && uBuf.au32[i] >= LNX_MIN_KALLSYMS_SYMBOLS)
1337 {
1338 uint8_t *pb = (uint8_t *)&uBuf.au32[i + 1];
1339 if ( pb[0] <= LNX_MAX_KALLSYMS_ENC_LENGTH
1340 && pb[0] >= LNX_MIN_KALLSYMS_ENC_LENGTH)
1341 {
1342 /* Check for relative base addressing. */
1343 if (i >= 1 && LNX32_VALID_ADDRESS(uBuf.au32[i - 1]))
1344 {
1345 RTGCUINTPTR uKrnlRelBase = uBuf.au32[i - 1];
1346 if ( (i <= 1 || LNX32_VALID_ADDRESS(uKrnlRelBase + uBuf.au32[i - 2]))
1347 && (i <= 2 || LNX32_VALID_ADDRESS(uKrnlRelBase + uBuf.au32[i - 3])))
1348 {
1349 Log(("dbgDiggerLinuxFindStartOfNamesAndSymbolCount: relative base %RGv (at %RGv)\n",
1350 uKrnlRelBase, CurAddr.FlatPtr + (i - 1) * sizeof(uint32_t)));
1351 pThis->fRelKrnlAddr = true;
1352 pThis->uKernelRelativeBase = uKrnlRelBase;
1353 return dbgDiggerLinuxFoundStartOfNames(pThis,
1354 DBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint32_t)),
1355 uBuf.au32[i], sizeof(uint32_t));
1356 }
1357 }
1358
1359 if ( (i <= 0 || LNX32_VALID_ADDRESS(uBuf.au32[i - 1]))
1360 && (i <= 1 || LNX32_VALID_ADDRESS(uBuf.au32[i - 2]))
1361 && (i <= 2 || LNX32_VALID_ADDRESS(uBuf.au32[i - 3])))
1362 return dbgDiggerLinuxFoundStartOfNames(pThis,
1363 DBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint32_t)),
1364 uBuf.au32[i], sizeof(uint32_t));
1365 }
1366 }
1367 }
1368
1369 /*
1370 * Advance
1371 */
1372 if (RT_UNLIKELY(cbLeft <= sizeof(uBuf)))
1373 {
1374 Log(("dbgDiggerLinuxFindStartOfNamesAndSymbolCount: failed (pHitAddr=%RGv)\n", pHitAddr->FlatPtr));
1375 return VERR_NOT_FOUND;
1376 }
1377 cbLeft -= sizeof(uBuf);
1378 DBGFR3AddrSub(&CurAddr, sizeof(uBuf));
1379 cbBuf = sizeof(uBuf);
1380 }
1381}
1382
1383
1384/**
1385 * Worker for dbgDiggerLinuxFindEndNames that records the findings.
1386 *
1387 * @returns VINF_SUCCESS
1388 * @param pThis The linux digger data to update.
1389 * @param pAddrMarkers The address of the marker (kallsyms_markers).
1390 * @param cbMarkerEntry The size of a marker entry (32-bit or 64-bit).
1391 */
1392static int dbgDiggerLinuxFoundMarkers(PDBGDIGGERLINUX pThis, PCDBGFADDRESS pAddrMarkers, uint32_t cbMarkerEntry)
1393{
1394 pThis->cbKernelNames = pAddrMarkers->FlatPtr - pThis->AddrKernelNames.FlatPtr;
1395 pThis->AddrKernelNameMarkers = *pAddrMarkers;
1396 pThis->cKernelNameMarkers = RT_ALIGN_32(pThis->cKernelSymbols, 256) / 256;
1397 pThis->AddrKernelTokenTable = *pAddrMarkers;
1398 DBGFR3AddrAdd(&pThis->AddrKernelTokenTable, pThis->cKernelNameMarkers * cbMarkerEntry);
1399
1400 Log(("dbgDiggerLinuxFoundMarkers: AddrKernelNames=%RGv cbKernelNames=%#x\n"
1401 "dbgDiggerLinuxFoundMarkers: AddrKernelNameMarkers=%RGv cKernelNameMarkers=%#x\n"
1402 "dbgDiggerLinuxFoundMarkers: AddrKernelTokenTable=%RGv\n",
1403 pThis->AddrKernelNames.FlatPtr, pThis->cbKernelNames,
1404 pThis->AddrKernelNameMarkers.FlatPtr, pThis->cKernelNameMarkers,
1405 pThis->AddrKernelTokenTable.FlatPtr));
1406 return VINF_SUCCESS;
1407}
1408
1409
1410/**
1411 * Tries to find the end of kallsyms_names and thereby the start of
1412 * kallsyms_markers and kallsyms_token_table.
1413 *
1414 * The kallsyms_names size is stored in pThis->cbKernelNames, the addresses of
1415 * the two other symbols in pThis->AddrKernelNameMarkers and
1416 * pThis->AddrKernelTokenTable. The number of marker entries is stored in
1417 * pThis->cKernelNameMarkers.
1418 *
1419 * @returns VBox status code, success indicating that all three variables have
1420 * been found and taken down.
1421 * @param pUVM The user mode VM handle.
1422 * @param pThis The Linux digger data.
1423 * @param pHitAddr An address we think is inside kallsyms_names.
1424 */
1425static int dbgDiggerLinuxFindEndOfNamesAndMore(PUVM pUVM, PDBGDIGGERLINUX pThis, PCDBGFADDRESS pHitAddr)
1426{
1427 /*
1428 * Search forward in chunks.
1429 */
1430 union
1431 {
1432 uint8_t ab[0x1000];
1433 uint32_t au32[0x1000 / sizeof(uint32_t)];
1434 uint64_t au64[0x1000 / sizeof(uint64_t)];
1435 } uBuf;
1436 bool fPendingZeroHit = false;
1437 uint32_t cbLeft = LNX_MAX_KALLSYMS_NAMES_SIZE + sizeof(uBuf);
1438 uint32_t offBuf = pHitAddr->FlatPtr & (sizeof(uBuf) - 1);
1439 DBGFADDRESS CurAddr = *pHitAddr;
1440 DBGFR3AddrSub(&CurAddr, offBuf);
1441 for (;;)
1442 {
1443 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
1444 if (RT_FAILURE(rc))
1445 return rc;
1446
1447 /*
1448 * The kallsyms_names table is followed by kallsyms_markers we assume,
1449 * using sizeof(unsigned long) alignment like the preceeding symbols.
1450 *
1451 * The kallsyms_markers table has entried sizeof(unsigned long) and
1452 * contains offsets into kallsyms_names. The kallsyms_markers used to
1453 * index kallsyms_names and reduce seek time when looking up the name
1454 * of an address/symbol. Each entry in kallsyms_markers covers 256
1455 * symbol names.
1456 *
1457 * Because of this, the first entry is always zero and all the entries
1458 * are ascending. It also follows that the size of the table can be
1459 * calculated from kallsyms_num_syms.
1460 *
1461 * Note! We could also have walked kallsyms_names by skipping
1462 * kallsyms_num_syms names, but this is faster and we will
1463 * validate the encoded names later.
1464 */
1465 if (pThis->f64Bit)
1466 {
1467 if ( RT_UNLIKELY(fPendingZeroHit)
1468 && uBuf.au64[0] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1469 && uBuf.au64[0] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1470 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrSub(&CurAddr, sizeof(uint64_t)), sizeof(uint64_t));
1471
1472 uint32_t const cEntries = sizeof(uBuf) / sizeof(uint64_t);
1473 for (uint32_t i = offBuf / sizeof(uint64_t); i < cEntries; i++)
1474 if (uBuf.au64[i] == 0)
1475 {
1476 if (RT_UNLIKELY(i + 1 >= cEntries))
1477 {
1478 fPendingZeroHit = true;
1479 break;
1480 }
1481 if ( uBuf.au64[i + 1] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1482 && uBuf.au64[i + 1] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1483 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrAdd(&CurAddr, i * sizeof(uint64_t)), sizeof(uint64_t));
1484 }
1485 }
1486 else
1487 {
1488 if ( RT_UNLIKELY(fPendingZeroHit)
1489 && uBuf.au32[0] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1490 && uBuf.au32[0] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1491 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrSub(&CurAddr, sizeof(uint32_t)), sizeof(uint32_t));
1492
1493 uint32_t const cEntries = sizeof(uBuf) / sizeof(uint32_t);
1494 for (uint32_t i = offBuf / sizeof(uint32_t); i < cEntries; i++)
1495 if (uBuf.au32[i] == 0)
1496 {
1497 if (RT_UNLIKELY(i + 1 >= cEntries))
1498 {
1499 fPendingZeroHit = true;
1500 break;
1501 }
1502 if ( uBuf.au32[i + 1] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1503 && uBuf.au32[i + 1] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1504 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrAdd(&CurAddr, i * sizeof(uint32_t)), sizeof(uint32_t));
1505 }
1506 }
1507
1508 /*
1509 * Advance
1510 */
1511 if (RT_UNLIKELY(cbLeft <= sizeof(uBuf)))
1512 {
1513 Log(("dbgDiggerLinuxFindEndOfNamesAndMore: failed (pHitAddr=%RGv)\n", pHitAddr->FlatPtr));
1514 return VERR_NOT_FOUND;
1515 }
1516 cbLeft -= sizeof(uBuf);
1517 DBGFR3AddrAdd(&CurAddr, sizeof(uBuf));
1518 offBuf = 0;
1519 }
1520}
1521
1522
1523/**
1524 * Locates the kallsyms_token_index table.
1525 *
1526 * Storing the address in pThis->AddrKernelTokenIndex and the size of the token
1527 * table in pThis->cbKernelTokenTable.
1528 *
1529 * @returns VBox status code.
1530 * @param pUVM The user mode VM handle.
1531 * @param pThis The Linux digger data.
1532 */
1533static int dbgDiggerLinuxFindTokenIndex(PUVM pUVM, PDBGDIGGERLINUX pThis)
1534{
1535 /*
1536 * The kallsyms_token_table is very much like a string table. Due to the
1537 * nature of the compression algorithm it is reasonably short (one example
1538 * here is 853 bytes), so we'll not be reading it in chunks but in full.
1539 * To be on the safe side, we read 8KB, ASSUMING we won't run into unmapped
1540 * memory or any other nasty stuff...
1541 */
1542 union
1543 {
1544 uint8_t ab[0x2000];
1545 uint16_t au16[0x2000 / sizeof(uint16_t)];
1546 } uBuf;
1547 DBGFADDRESS CurAddr = pThis->AddrKernelTokenTable;
1548 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
1549 if (RT_FAILURE(rc))
1550 return rc;
1551
1552 /*
1553 * We've got two choices here, either walk the string table or look for
1554 * the next structure, kallsyms_token_index.
1555 *
1556 * The token index is a table of 256 uint16_t entries (index by bytes
1557 * from kallsyms_names) that gives offsets in kallsyms_token_table. It
1558 * starts with a zero entry and the following entries are sorted in
1559 * ascending order. The range of the entries are reasonably small since
1560 * kallsyms_token_table is small.
1561 *
1562 * The alignment seems to be sizeof(unsigned long), just like
1563 * kallsyms_token_table.
1564 *
1565 * So, we start by looking for a zero 16-bit entry.
1566 */
1567 uint32_t cIncr = (pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t)) / sizeof(uint16_t);
1568
1569 for (uint32_t i = 0; i < sizeof(uBuf) / sizeof(uint16_t) - 16; i += cIncr)
1570 if ( uBuf.au16[i] == 0
1571 && uBuf.au16[i + 1] > 0
1572 && uBuf.au16[i + 1] <= LNX_MAX_KALLSYMS_TOKEN_LEN
1573 && (uint16_t)(uBuf.au16[i + 2] - uBuf.au16[i + 1] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1574 && (uint16_t)(uBuf.au16[i + 3] - uBuf.au16[i + 2] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1575 && (uint16_t)(uBuf.au16[i + 4] - uBuf.au16[i + 3] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1576 && (uint16_t)(uBuf.au16[i + 5] - uBuf.au16[i + 4] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1577 && (uint16_t)(uBuf.au16[i + 6] - uBuf.au16[i + 5] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1578 )
1579 {
1580 pThis->AddrKernelTokenIndex = CurAddr;
1581 DBGFR3AddrAdd(&pThis->AddrKernelTokenIndex, i * sizeof(uint16_t));
1582 pThis->cbKernelTokenTable = i * sizeof(uint16_t);
1583 return VINF_SUCCESS;
1584 }
1585
1586 Log(("dbgDiggerLinuxFindTokenIndex: Failed (%RGv..%RGv)\n", CurAddr.FlatPtr, CurAddr.FlatPtr + (RTGCUINTPTR)sizeof(uBuf)));
1587 return VERR_NOT_FOUND;
1588}
1589
1590
1591/**
1592 * Loads the kernel symbols from the given kallsyms offset table decoding the symbol names
1593 * (worker common for dbgDiggerLinuxLoadKernelSymbolsAbsolute() and dbgDiggerLinuxLoadKernelSymbolsRelative()).
1594 *
1595 * @returns VBox status code.
1596 * @param pUVM The user mode VM handle.
1597 * @param pThis The Linux digger data.
1598 * @param uKernelStart Flat kernel start address.
1599 * @param cbKernel Size of the kernel in bytes.
1600 * @param pauSymOff Pointer to the array of symbol offsets in the kallsyms table
1601 * relative to the start of the kernel.
1602 */
1603static int dbgDiggerLinuxLoadKernelSymbolsWorker(PUVM pUVM, PDBGDIGGERLINUX pThis, RTGCUINTPTR uKernelStart,
1604 RTGCUINTPTR cbKernel, RTGCUINTPTR *pauSymOff)
1605{
1606 uint8_t *pbNames = (uint8_t *)RTMemAllocZ(pThis->cbKernelNames);
1607 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelNames, pbNames, pThis->cbKernelNames);
1608 if (RT_SUCCESS(rc))
1609 {
1610 char *pszzTokens = (char *)RTMemAllocZ(pThis->cbKernelTokenTable);
1611 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelTokenTable, pszzTokens, pThis->cbKernelTokenTable);
1612 if (RT_SUCCESS(rc))
1613 {
1614 uint16_t *paoffTokens = (uint16_t *)RTMemAllocZ(256 * sizeof(uint16_t));
1615 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelTokenIndex, paoffTokens, 256 * sizeof(uint16_t));
1616 if (RT_SUCCESS(rc))
1617 {
1618 /*
1619 * Create a module for the kernel.
1620 */
1621 RTDBGMOD hMod;
1622 rc = RTDbgModCreate(&hMod, "vmlinux", cbKernel, 0 /*fFlags*/);
1623 if (RT_SUCCESS(rc))
1624 {
1625 rc = RTDbgModSetTag(hMod, DIG_LNX_MOD_TAG); AssertRC(rc);
1626 rc = VINF_SUCCESS;
1627
1628 /*
1629 * Enumerate the symbols.
1630 */
1631 uint32_t offName = 0;
1632 uint32_t cLeft = pThis->cKernelSymbols;
1633 while (cLeft-- > 0 && RT_SUCCESS(rc))
1634 {
1635 /* Decode the symbol name first. */
1636 if (RT_LIKELY(offName < pThis->cbKernelNames))
1637 {
1638 uint8_t cbName = pbNames[offName++];
1639 if (RT_LIKELY(offName + cbName <= pThis->cbKernelNames))
1640 {
1641 char szSymbol[4096];
1642 uint32_t offSymbol = 0;
1643 while (cbName-- > 0)
1644 {
1645 uint8_t bEnc = pbNames[offName++];
1646 uint16_t offToken = paoffTokens[bEnc];
1647 if (RT_LIKELY(offToken < pThis->cbKernelTokenTable))
1648 {
1649 const char *pszToken = &pszzTokens[offToken];
1650 char ch;
1651 while ((ch = *pszToken++) != '\0')
1652 if (offSymbol < sizeof(szSymbol) - 1)
1653 szSymbol[offSymbol++] = ch;
1654 }
1655 else
1656 {
1657 rc = VERR_INVALID_UTF8_ENCODING;
1658 break;
1659 }
1660 }
1661 szSymbol[offSymbol < sizeof(szSymbol) ? offSymbol : sizeof(szSymbol) - 1] = '\0';
1662
1663 /* The offset. */
1664 RTGCUINTPTR uSymOff = *pauSymOff;
1665 pauSymOff++;
1666
1667 /* Add it without the type char. */
1668 if (uSymOff <= cbKernel)
1669 {
1670 rc = RTDbgModSymbolAdd(hMod, &szSymbol[1], RTDBGSEGIDX_RVA, uSymOff,
1671 0 /*cb*/, 0 /*fFlags*/, NULL);
1672 if (RT_FAILURE(rc))
1673 {
1674 if ( rc == VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE
1675 || rc == VERR_DBG_INVALID_RVA
1676 || rc == VERR_DBG_ADDRESS_CONFLICT
1677 || rc == VERR_DBG_DUPLICATE_SYMBOL)
1678 {
1679 Log2(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc (ignored)\n", szSymbol, rc));
1680 rc = VINF_SUCCESS;
1681 }
1682 else
1683 Log(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc\n", szSymbol, rc));
1684 }
1685 }
1686 }
1687 else
1688 {
1689 rc = VERR_END_OF_STRING;
1690 Log(("dbgDiggerLinuxLoadKernelSymbols: offName=%#x cLeft=%#x cbName=%#x cbKernelNames=%#x\n",
1691 offName, cLeft, cbName, pThis->cbKernelNames));
1692 }
1693 }
1694 else
1695 {
1696 rc = VERR_END_OF_STRING;
1697 Log(("dbgDiggerLinuxLoadKernelSymbols: offName=%#x cLeft=%#x cbKernelNames=%#x\n",
1698 offName, cLeft, pThis->cbKernelNames));
1699 }
1700 }
1701
1702 /*
1703 * Link the module into the address space.
1704 */
1705 if (RT_SUCCESS(rc))
1706 {
1707 RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
1708 if (hAs != NIL_RTDBGAS)
1709 rc = RTDbgAsModuleLink(hAs, hMod, uKernelStart, RTDBGASLINK_FLAGS_REPLACE);
1710 else
1711 rc = VERR_INTERNAL_ERROR;
1712 RTDbgAsRelease(hAs);
1713 }
1714 else
1715 Log(("dbgDiggerLinuxLoadKernelSymbols: Failed: %Rrc\n", rc));
1716 RTDbgModRelease(hMod);
1717 }
1718 else
1719 Log(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModCreate failed: %Rrc\n", rc));
1720 }
1721 else
1722 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading token index at %RGv failed: %Rrc\n",
1723 pThis->AddrKernelTokenIndex.FlatPtr, rc));
1724 RTMemFree(paoffTokens);
1725 }
1726 else
1727 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading token table at %RGv failed: %Rrc\n",
1728 pThis->AddrKernelTokenTable.FlatPtr, rc));
1729 RTMemFree(pszzTokens);
1730 }
1731 else
1732 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading encoded names at %RGv failed: %Rrc\n",
1733 pThis->AddrKernelNames.FlatPtr, rc));
1734 RTMemFree(pbNames);
1735
1736 return rc;
1737}
1738
1739/**
1740 * Loads the kernel symbols from the kallsyms table if it contains absolute addresses
1741 *
1742 * @returns VBox status code.
1743 * @param pUVM The user mode VM handle.
1744 * @param pThis The Linux digger data.
1745 */
1746static int dbgDiggerLinuxLoadKernelSymbolsAbsolute(PUVM pUVM, PDBGDIGGERLINUX pThis)
1747{
1748 /*
1749 * Allocate memory for temporary table copies, reading the tables as we go.
1750 */
1751 uint32_t const cbGuestAddr = pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t);
1752 void *pvAddresses = RTMemAllocZ(pThis->cKernelSymbols * cbGuestAddr);
1753 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelAddresses, pvAddresses, pThis->cKernelSymbols * cbGuestAddr);
1754 if (RT_SUCCESS(rc))
1755 {
1756 /*
1757 * Figure out the kernel start and end and convert the absolute addresses to relative offsets.
1758 */
1759 RTGCUINTPTR uKernelStart = pThis->AddrKernelAddresses.FlatPtr;
1760 RTGCUINTPTR uKernelEnd = pThis->AddrKernelTokenIndex.FlatPtr + 256 * sizeof(uint16_t);
1761 RTGCUINTPTR *pauSymOff = (RTGCUINTPTR *)RTMemTmpAllocZ(pThis->cKernelSymbols * sizeof(RTGCUINTPTR));
1762 uint32_t i;
1763 if (cbGuestAddr == sizeof(uint64_t))
1764 {
1765 uint64_t *pauAddrs = (uint64_t *)pvAddresses;
1766 for (i = 0; i < pThis->cKernelSymbols; i++)
1767 if ( pauAddrs[i] < uKernelStart
1768 && LNX64_VALID_ADDRESS(pauAddrs[i])
1769 && uKernelStart - pauAddrs[i] < LNX_MAX_KERNEL_SIZE)
1770 uKernelStart = pauAddrs[i];
1771
1772 for (i = pThis->cKernelSymbols - 1; i > 0; i--)
1773 if ( pauAddrs[i] > uKernelEnd
1774 && LNX64_VALID_ADDRESS(pauAddrs[i])
1775 && pauAddrs[i] - uKernelEnd < LNX_MAX_KERNEL_SIZE)
1776 uKernelEnd = pauAddrs[i];
1777
1778 for (i = 0; i < pThis->cKernelSymbols; i++)
1779 pauSymOff[i] = pauAddrs[i] - uKernelStart;
1780 }
1781 else
1782 {
1783 uint32_t *pauAddrs = (uint32_t *)pvAddresses;
1784 for (i = 0; i < pThis->cKernelSymbols; i++)
1785 if ( pauAddrs[i] < uKernelStart
1786 && LNX32_VALID_ADDRESS(pauAddrs[i])
1787 && uKernelStart - pauAddrs[i] < LNX_MAX_KERNEL_SIZE)
1788 uKernelStart = pauAddrs[i];
1789
1790 for (i = pThis->cKernelSymbols - 1; i > 0; i--)
1791 if ( pauAddrs[i] > uKernelEnd
1792 && LNX32_VALID_ADDRESS(pauAddrs[i])
1793 && pauAddrs[i] - uKernelEnd < LNX_MAX_KERNEL_SIZE)
1794 uKernelEnd = pauAddrs[i];
1795
1796 for (i = 0; i < pThis->cKernelSymbols; i++)
1797 pauSymOff[i] = pauAddrs[i] - uKernelStart;
1798 }
1799
1800 RTGCUINTPTR cbKernel = uKernelEnd - uKernelStart;
1801 pThis->cbKernel = (uint32_t)cbKernel;
1802 DBGFR3AddrFromFlat(pUVM, &pThis->AddrKernelBase, uKernelStart);
1803 Log(("dbgDiggerLinuxLoadKernelSymbolsAbsolute: uKernelStart=%RGv cbKernel=%#x\n", uKernelStart, cbKernel));
1804
1805 rc = dbgDiggerLinuxLoadKernelSymbolsWorker(pUVM, pThis, uKernelStart, cbKernel, pauSymOff);
1806 if (RT_FAILURE(rc))
1807 Log(("dbgDiggerLinuxLoadKernelSymbolsAbsolute: Loading symbols from given offset table failed: %Rrc\n", rc));
1808 RTMemTmpFree(pauSymOff);
1809 }
1810 else
1811 Log(("dbgDiggerLinuxLoadKernelSymbolsAbsolute: Reading symbol addresses at %RGv failed: %Rrc\n",
1812 pThis->AddrKernelAddresses.FlatPtr, rc));
1813 RTMemFree(pvAddresses);
1814
1815 return rc;
1816}
1817
1818
1819/**
1820 * Loads the kernel symbols from the kallsyms table if it contains absolute addresses
1821 *
1822 * @returns VBox status code.
1823 * @param pUVM The user mode VM handle.
1824 * @param pThis The Linux digger data.
1825 */
1826static int dbgDiggerLinuxLoadKernelSymbolsRelative(PUVM pUVM, PDBGDIGGERLINUX pThis)
1827{
1828 /*
1829 * Allocate memory for temporary table copies, reading the tables as we go.
1830 */
1831 int32_t *pai32Offsets = (int32_t *)RTMemAllocZ(pThis->cKernelSymbols * sizeof(int32_t));
1832 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelAddresses, pai32Offsets, pThis->cKernelSymbols * sizeof(int32_t));
1833 if (RT_SUCCESS(rc))
1834 {
1835 /*
1836 * Figure out the kernel start and end and convert the absolute addresses to relative offsets.
1837 */
1838 RTGCUINTPTR uKernelStart = pThis->AddrKernelAddresses.FlatPtr;
1839 RTGCUINTPTR uKernelEnd = pThis->AddrKernelTokenIndex.FlatPtr + 256 * sizeof(uint16_t);
1840 RTGCUINTPTR *pauSymOff = (RTGCUINTPTR *)RTMemTmpAllocZ(pThis->cKernelSymbols * sizeof(RTGCUINTPTR));
1841 uint32_t i;
1842
1843 for (i = 0; i < pThis->cKernelSymbols; i++)
1844 {
1845 RTGCUINTPTR uSymAddr = dbgDiggerLinuxConvOffsetToAddr(pThis, pai32Offsets[i]);
1846
1847 if ( uSymAddr < uKernelStart
1848 && (pThis->f64Bit ? LNX64_VALID_ADDRESS(uSymAddr) : LNX32_VALID_ADDRESS(uSymAddr))
1849 && uKernelStart - uSymAddr < LNX_MAX_KERNEL_SIZE)
1850 uKernelStart = uSymAddr;
1851 }
1852
1853 for (i = pThis->cKernelSymbols - 1; i > 0; i--)
1854 {
1855 RTGCUINTPTR uSymAddr = dbgDiggerLinuxConvOffsetToAddr(pThis, pai32Offsets[i]);
1856
1857 if ( uSymAddr > uKernelEnd
1858 && (pThis->f64Bit ? LNX64_VALID_ADDRESS(uSymAddr) : LNX32_VALID_ADDRESS(uSymAddr))
1859 && uSymAddr - uKernelEnd < LNX_MAX_KERNEL_SIZE)
1860 uKernelEnd = uSymAddr;
1861
1862 /* Store the offset from the derived kernel start address. */
1863 pauSymOff[i] = uSymAddr - uKernelStart;
1864 }
1865
1866 RTGCUINTPTR cbKernel = uKernelEnd - uKernelStart;
1867 pThis->cbKernel = (uint32_t)cbKernel;
1868 DBGFR3AddrFromFlat(pUVM, &pThis->AddrKernelBase, uKernelStart);
1869 Log(("dbgDiggerLinuxLoadKernelSymbolsRelative: uKernelStart=%RGv cbKernel=%#x\n", uKernelStart, cbKernel));
1870
1871 rc = dbgDiggerLinuxLoadKernelSymbolsWorker(pUVM, pThis, uKernelStart, cbKernel, pauSymOff);
1872 if (RT_FAILURE(rc))
1873 Log(("dbgDiggerLinuxLoadKernelSymbolsRelative: Loading symbols from given offset table failed: %Rrc\n", rc));
1874 RTMemTmpFree(pauSymOff);
1875 }
1876 else
1877 Log(("dbgDiggerLinuxLoadKernelSymbolsRelative: Reading symbol addresses at %RGv failed: %Rrc\n",
1878 pThis->AddrKernelAddresses.FlatPtr, rc));
1879 RTMemFree(pai32Offsets);
1880
1881 return rc;
1882}
1883
1884
1885/**
1886 * Loads the kernel symbols.
1887 *
1888 * @returns VBox status code.
1889 * @param pUVM The user mode VM handle.
1890 * @param pThis The Linux digger data.
1891 */
1892static int dbgDiggerLinuxLoadKernelSymbols(PUVM pUVM, PDBGDIGGERLINUX pThis)
1893{
1894 if (pThis->fRelKrnlAddr)
1895 return dbgDiggerLinuxLoadKernelSymbolsRelative(pUVM, pThis);
1896 else
1897 return dbgDiggerLinuxLoadKernelSymbolsAbsolute(pUVM, pThis);
1898}
1899
1900/**
1901 * Checks if there is a likely kallsyms_names fragment at pHitAddr.
1902 *
1903 * @returns true if it's a likely fragment, false if not.
1904 * @param pUVM The user mode VM handle.
1905 * @param pHitAddr The address where paNeedle was found.
1906 * @param pabNeedle The fragment we've been searching for.
1907 * @param cbNeedle The length of the fragment.
1908 */
1909static bool dbgDiggerLinuxIsLikelyNameFragment(PUVM pUVM, PCDBGFADDRESS pHitAddr, uint8_t const *pabNeedle, uint8_t cbNeedle)
1910{
1911 /*
1912 * Examples of lead and tail bytes of our choosen needle in a randomly
1913 * picked kernel:
1914 * k o b j
1915 * 22 6b 6f 62 6a aa
1916 * fc 6b 6f 62 6a aa
1917 * 82 6b 6f 62 6a 5f - ascii trail byte (_).
1918 * ee 6b 6f 62 6a aa
1919 * fc 6b 6f 62 6a 5f - ascii trail byte (_).
1920 * 0a 74 6b 6f 62 6a 5f ea - ascii lead (t) and trail (_) bytes.
1921 * 0b 54 6b 6f 62 6a aa - ascii lead byte (T).
1922 * ... omitting 29 samples similar to the last two ...
1923 * d8 6b 6f 62 6a aa
1924 * d8 6b 6f 62 6a aa
1925 * d8 6b 6f 62 6a aa
1926 * d8 6b 6f 62 6a aa
1927 * f9 5f 6b 6f 62 6a 5f 94 - ascii lead and trail bytes (_)
1928 * f9 5f 6b 6f 62 6a 0c - ascii lead byte (_).
1929 * fd 6b 6f 62 6a 0f
1930 * ... enough.
1931 */
1932 uint8_t abBuf[32];
1933 DBGFADDRESS ReadAddr = *pHitAddr;
1934 DBGFR3AddrSub(&ReadAddr, 2);
1935 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &ReadAddr, abBuf, 2 + cbNeedle + 2);
1936 if (RT_SUCCESS(rc))
1937 {
1938 if (memcmp(&abBuf[2], pabNeedle, cbNeedle) == 0) /* paranoia */
1939 {
1940 uint8_t const bLead = abBuf[1] == '_' || abBuf[1] == 'T' || abBuf[1] == 't' ? abBuf[0] : abBuf[1];
1941 uint8_t const offTail = 2 + cbNeedle;
1942 uint8_t const bTail = abBuf[offTail] == '_' ? abBuf[offTail] : abBuf[offTail + 1];
1943 if ( bLead >= 1 && (bLead < 0x20 || bLead >= 0x80)
1944 && bTail >= 1 && (bTail < 0x20 || bTail >= 0x80))
1945 return true;
1946 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: bLead=%#x bTail=%#x (offTail=%#x)\n",
1947 pHitAddr->FlatPtr, bLead, bTail, offTail));
1948 }
1949 else
1950 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: Needle changed!\n", pHitAddr->FlatPtr));
1951 }
1952 else
1953 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: %Rrc\n", pHitAddr->FlatPtr, rc));
1954
1955 return false;
1956}
1957
1958/**
1959 * Tries to find and load the kernel symbol table with the given needle.
1960 *
1961 * @returns VBox status code.
1962 * @param pThis The Linux digger data.
1963 * @param pUVM The user mode VM handle.
1964 * @param pabNeedle The needle to use for searching.
1965 * @param cbNeedle Size of the needle in bytes.
1966 */
1967static int dbgDiggerLinuxFindSymbolTableFromNeedle(PDBGDIGGERLINUX pThis, PUVM pUVM, uint8_t const *pabNeedle, uint8_t cbNeedle)
1968{
1969 int rc = VINF_SUCCESS;
1970
1971 /*
1972 * Go looking for the kallsyms table. If it's there, it will be somewhere
1973 * after the linux_banner symbol, so use it for starting the search.
1974 */
1975 DBGFADDRESS CurAddr = pThis->AddrLinuxBanner;
1976 uint32_t cbLeft = LNX_MAX_KERNEL_SIZE;
1977 while (cbLeft > 4096)
1978 {
1979 DBGFADDRESS HitAddr;
1980 rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &CurAddr, cbLeft, 1 /*uAlign*/,
1981 pabNeedle, cbNeedle, &HitAddr);
1982 if (RT_FAILURE(rc))
1983 break;
1984 if (dbgDiggerLinuxIsLikelyNameFragment(pUVM, &HitAddr, pabNeedle, cbNeedle))
1985 {
1986 /* There will be another hit near by. */
1987 DBGFR3AddrAdd(&HitAddr, 1);
1988 rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, LNX_MAX_KALLSYMS_NAMES_SIZE, 1 /*uAlign*/,
1989 pabNeedle, cbNeedle, &HitAddr);
1990 if ( RT_SUCCESS(rc)
1991 && dbgDiggerLinuxIsLikelyNameFragment(pUVM, &HitAddr, pabNeedle, cbNeedle))
1992 {
1993 /*
1994 * We've got a very likely candidate for a location inside kallsyms_names.
1995 * Try find the start of it, that is to say, try find kallsyms_num_syms.
1996 * kallsyms_num_syms is aligned on sizeof(unsigned long) boundrary
1997 */
1998 rc = dbgDiggerLinuxFindStartOfNamesAndSymbolCount(pUVM, pThis, &HitAddr);
1999 if (RT_SUCCESS(rc))
2000 rc = dbgDiggerLinuxFindEndOfNamesAndMore(pUVM, pThis, &HitAddr);
2001 if (RT_SUCCESS(rc))
2002 rc = dbgDiggerLinuxFindTokenIndex(pUVM, pThis);
2003 if (RT_SUCCESS(rc))
2004 rc = dbgDiggerLinuxLoadKernelSymbols(pUVM, pThis);
2005 if (RT_SUCCESS(rc))
2006 break;
2007 }
2008 }
2009
2010 /*
2011 * Advance.
2012 */
2013 RTGCUINTPTR cbDistance = HitAddr.FlatPtr - CurAddr.FlatPtr + cbNeedle;
2014 if (RT_UNLIKELY(cbDistance >= cbLeft))
2015 {
2016 Log(("dbgDiggerLinuxInit: Failed to find kallsyms\n"));
2017 break;
2018 }
2019 cbLeft -= cbDistance;
2020 DBGFR3AddrAdd(&CurAddr, cbDistance);
2021
2022 }
2023
2024 return rc;
2025}
2026
2027/**
2028 * Skips whitespace and comments in the given config returning the pointer
2029 * to the first non whitespace character.
2030 *
2031 * @returns Pointer to the first non whitespace character or NULL if the end
2032 * of the string was reached.
2033 * @param pszCfg The config string.
2034 */
2035static const char *dbgDiggerLinuxCfgSkipWhitespace(const char *pszCfg)
2036{
2037 do
2038 {
2039 while ( *pszCfg != '\0'
2040 && ( RT_C_IS_SPACE(*pszCfg)
2041 || *pszCfg == '\n'))
2042 pszCfg++;
2043
2044 /* Do we have a comment? Skip it. */
2045 if (*pszCfg == '#')
2046 {
2047 while ( *pszCfg != '\n'
2048 && *pszCfg != '\0')
2049 pszCfg++;
2050 }
2051 } while ( *pszCfg != '\0'
2052 && ( RT_C_IS_SPACE(*pszCfg)
2053 || *pszCfg == '\n'
2054 || *pszCfg == '#'));
2055
2056 return pszCfg;
2057}
2058
2059/**
2060 * Parses an identifier at the given position.
2061 *
2062 * @returns VBox status code.
2063 * @param pszCfg The config data.
2064 * @param ppszCfgNext Where to store the pointer to the data following the identifier.
2065 * @param ppszIde Where to store the pointer to the identifier on success.
2066 * Free with RTStrFree().
2067 */
2068static int dbgDiggerLinuxCfgParseIde(const char *pszCfg, const char **ppszCfgNext, char **ppszIde)
2069{
2070 int rc = VINF_SUCCESS;
2071 size_t cchIde = 0;
2072
2073 while ( *pszCfg != '\0'
2074 && ( RT_C_IS_ALNUM(*pszCfg)
2075 || *pszCfg == '_'))
2076 {
2077 cchIde++;
2078 pszCfg++;
2079 }
2080
2081 if (cchIde)
2082 {
2083 *ppszIde = RTStrDupN(pszCfg - cchIde, cchIde);
2084 if (!*ppszIde)
2085 rc = VERR_NO_STR_MEMORY;
2086 }
2087
2088 *ppszCfgNext = pszCfg;
2089 return rc;
2090}
2091
2092/**
2093 * Parses a value for a config item.
2094 *
2095 * @returns VBox status code.
2096 * @param pszCfg The config data.
2097 * @param ppszCfgNext Where to store the pointer to the data following the identifier.
2098 * @param ppCfgItem Where to store the created config item on success.
2099 */
2100static int dbgDiggerLinuxCfgParseVal(const char *pszCfg, const char **ppszCfgNext,
2101 PDBGDIGGERLINUXCFGITEM *ppCfgItem)
2102{
2103 int rc = VINF_SUCCESS;
2104 PDBGDIGGERLINUXCFGITEM pCfgItem = NULL;
2105
2106 if (RT_C_IS_DIGIT(*pszCfg) || *pszCfg == '-')
2107 {
2108 /* Parse the number. */
2109 int64_t i64Num;
2110 rc = RTStrToInt64Ex(pszCfg, (char **)ppszCfgNext, 0, &i64Num);
2111 if ( RT_SUCCESS(rc)
2112 || rc == VWRN_TRAILING_CHARS
2113 || rc == VWRN_TRAILING_SPACES)
2114 {
2115 pCfgItem = (PDBGDIGGERLINUXCFGITEM)RTMemAllocZ(sizeof(DBGDIGGERLINUXCFGITEM));
2116 if (pCfgItem)
2117 {
2118 pCfgItem->enmType = DBGDIGGERLINUXCFGITEMTYPE_NUMBER;
2119 pCfgItem->u.i64Num = i64Num;
2120 }
2121 else
2122 rc = VERR_NO_MEMORY;
2123 }
2124 }
2125 else if (*pszCfg == '\"')
2126 {
2127 /* Parse a string. */
2128 const char *pszCfgCur = pszCfg + 1;
2129 while ( *pszCfgCur != '\0'
2130 && *pszCfgCur != '\"')
2131 pszCfgCur++;
2132
2133 if (*pszCfgCur == '\"')
2134 {
2135 pCfgItem = (PDBGDIGGERLINUXCFGITEM)RTMemAllocZ(RT_OFFSETOF(DBGDIGGERLINUXCFGITEM, u.aszString[pszCfgCur - pszCfg + 1]));
2136 if (pCfgItem)
2137 {
2138 pCfgItem->enmType = DBGDIGGERLINUXCFGITEMTYPE_STRING;
2139 RTStrCopyEx(&pCfgItem->u.aszString[0], pszCfgCur - pszCfg + 1, pszCfg, pszCfgCur - pszCfg);
2140 *ppszCfgNext = pszCfgCur + 1;
2141 }
2142 else
2143 rc = VERR_NO_MEMORY;
2144 }
2145 else
2146 rc = VERR_INVALID_STATE;
2147 }
2148 else if ( *pszCfg == 'y'
2149 || *pszCfg == 'm')
2150 {
2151 /* Included or module. */
2152 pCfgItem = (PDBGDIGGERLINUXCFGITEM)RTMemAllocZ(sizeof(DBGDIGGERLINUXCFGITEM));
2153 if (pCfgItem)
2154 {
2155 pCfgItem->enmType = DBGDIGGERLINUXCFGITEMTYPE_FLAG;
2156 pCfgItem->u.fModule = *pszCfg == 'm';
2157 }
2158 else
2159 rc = VERR_NO_MEMORY;
2160 pszCfg++;
2161 *ppszCfgNext = pszCfg;
2162 }
2163 else
2164 rc = VERR_INVALID_STATE;
2165
2166 if (RT_SUCCESS(rc))
2167 *ppCfgItem = pCfgItem;
2168 else if (pCfgItem)
2169 RTMemFree(pCfgItem);
2170
2171 return rc;
2172}
2173
2174/**
2175 * Parses the given kernel config and creates the config database.
2176 *
2177 * @returns VBox status code
2178 * @param pThis The Linux digger data.
2179 * @param pszCfg The config string.
2180 */
2181static int dbgDiggerLinuxCfgParse(PDBGDIGGERLINUX pThis, const char *pszCfg)
2182{
2183 int rc = VINF_SUCCESS;
2184
2185 /*
2186 * The config is a text file with the following elements:
2187 * # starts a comment which goes till the end of the line
2188 * <Ide>=<val> where <Ide> is an identifier consisting of
2189 * alphanumerical characters (including _)
2190 * <val> denotes the value for the identifier and can have the following
2191 * formats:
2192 * (-)[0-9]* for numbers
2193 * "..." for a string value
2194 * m when a feature is enabled as a module
2195 * y when a feature is enabled
2196 * Newlines are used as a separator between values and mark the end
2197 * of a comment
2198 */
2199 const char *pszCfgCur = pszCfg;
2200 while ( RT_SUCCESS(rc)
2201 && *pszCfgCur != '\0')
2202 {
2203 /* Start skipping the whitespace. */
2204 pszCfgCur = dbgDiggerLinuxCfgSkipWhitespace(pszCfgCur);
2205 if ( pszCfgCur
2206 && *pszCfgCur != '\0')
2207 {
2208 char *pszIde = NULL;
2209 /* Must be an identifier, parse it. */
2210 rc = dbgDiggerLinuxCfgParseIde(pszCfgCur, &pszCfgCur, &pszIde);
2211 if (RT_SUCCESS(rc))
2212 {
2213 /*
2214 * Skip whitespace again (shouldn't be required because = follows immediately
2215 * in the observed configs).
2216 */
2217 pszCfgCur = dbgDiggerLinuxCfgSkipWhitespace(pszCfgCur);
2218 if ( pszCfgCur
2219 && *pszCfgCur == '=')
2220 {
2221 pszCfgCur++;
2222 pszCfgCur = dbgDiggerLinuxCfgSkipWhitespace(pszCfgCur);
2223 if ( pszCfgCur
2224 && *pszCfgCur != '\0')
2225 {
2226 /* Get the value. */
2227 PDBGDIGGERLINUXCFGITEM pCfgItem = NULL;
2228 rc = dbgDiggerLinuxCfgParseVal(pszCfgCur, &pszCfgCur, &pCfgItem);
2229 if (RT_SUCCESS(rc))
2230 {
2231 pCfgItem->Core.pszString = pszIde;
2232 bool fRc = RTStrSpaceInsert(&pThis->hCfgDb, &pCfgItem->Core);
2233 if (!fRc)
2234 {
2235 RTStrFree(pszIde);
2236 RTMemFree(pCfgItem);
2237 rc = VERR_INVALID_STATE;
2238 }
2239 }
2240 }
2241 else
2242 rc = VERR_EOF;
2243 }
2244 else
2245 rc = VERR_INVALID_STATE;
2246 }
2247
2248 if (RT_FAILURE(rc))
2249 RTStrFree(pszIde);
2250 }
2251 else
2252 break; /* Reached the end of the config. */
2253 }
2254
2255 if (RT_FAILURE(rc))
2256 dbgDiggerLinuxCfgDbDestroy(pThis);
2257
2258 return rc;
2259}
2260
2261/**
2262 * Decompresses the given config and validates the UTF-8 encoding.
2263 *
2264 * @returns VBox status code.
2265 * @param pbCfgComp The compressed config.
2266 * @param cbCfgComp Size of the compressed config.
2267 * @param ppszCfg Where to store the pointer to the decompressed config
2268 * on success.
2269 */
2270static int dbgDiggerLinuxCfgDecompress(const uint8_t *pbCfgComp, size_t cbCfgComp, char **ppszCfg)
2271{
2272 int rc = VINF_SUCCESS;
2273 RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
2274
2275 rc = RTVfsIoStrmFromBuffer(RTFILE_O_READ, pbCfgComp, cbCfgComp, &hVfsIos);
2276 if (RT_SUCCESS(rc))
2277 {
2278 RTVFSIOSTREAM hVfsIosDecomp = NIL_RTVFSIOSTREAM;
2279 rc = RTZipGzipDecompressIoStream(hVfsIos, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR, &hVfsIosDecomp);
2280 if (RT_SUCCESS(rc))
2281 {
2282 char *pszCfg = NULL;
2283 size_t cchCfg = 0;
2284 size_t cbRead = 0;
2285
2286 do
2287 {
2288 uint8_t abBuf[_64K];
2289 rc = RTVfsIoStrmRead(hVfsIosDecomp, abBuf, sizeof(abBuf), true /*fBlocking*/, &cbRead);
2290 if (rc == VINF_EOF && cbRead == 0)
2291 rc = VINF_SUCCESS;
2292 if ( RT_SUCCESS(rc)
2293 && cbRead > 0)
2294 {
2295 /* Append data. */
2296 char *pszCfgNew = pszCfg;
2297 rc = RTStrRealloc(&pszCfgNew, cchCfg + cbRead + 1);
2298 if (RT_SUCCESS(rc))
2299 {
2300 pszCfg = pszCfgNew;
2301 memcpy(pszCfg + cchCfg, &abBuf[0], cbRead);
2302 cchCfg += cbRead;
2303 pszCfg[cchCfg] = '\0'; /* Enforce string termination. */
2304 }
2305 }
2306 } while (RT_SUCCESS(rc) && cbRead > 0);
2307
2308 if (RT_SUCCESS(rc))
2309 *ppszCfg = pszCfg;
2310 else if (RT_FAILURE(rc) && pszCfg)
2311 RTStrFree(pszCfg);
2312
2313 RTVfsIoStrmRelease(hVfsIosDecomp);
2314 }
2315 RTVfsIoStrmRelease(hVfsIos);
2316 }
2317
2318 return rc;
2319}
2320
2321/**
2322 * Reads and decodes the compressed kernel config.
2323 *
2324 * @returns VBox status code.
2325 * @param pThis The Linux digger data.
2326 * @param pUVM The user mode VM handle.
2327 * @param pAddrStart The start address of the compressed config.
2328 * @param cbCfgComp The size of the compressed config.
2329 */
2330static int dbgDiggerLinuxCfgDecode(PDBGDIGGERLINUX pThis, PUVM pUVM,
2331 PCDBGFADDRESS pAddrStart, size_t cbCfgComp)
2332{
2333 int rc = VINF_SUCCESS;
2334 uint8_t *pbCfgComp = (uint8_t *)RTMemTmpAlloc(cbCfgComp);
2335 if (!pbCfgComp)
2336 return VERR_NO_MEMORY;
2337
2338 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, pAddrStart, pbCfgComp, cbCfgComp);
2339 if (RT_SUCCESS(rc))
2340 {
2341 char *pszCfg = NULL;
2342 rc = dbgDiggerLinuxCfgDecompress(pbCfgComp, cbCfgComp, &pszCfg);
2343 if (RT_SUCCESS(rc))
2344 {
2345 if (RTStrIsValidEncoding(pszCfg))
2346 rc = dbgDiggerLinuxCfgParse(pThis, pszCfg);
2347 else
2348 rc = VERR_INVALID_UTF8_ENCODING;
2349 RTStrFree(pszCfg);
2350 }
2351 }
2352
2353 RTMemFree(pbCfgComp);
2354 return rc;
2355}
2356
2357/**
2358 * Tries to find the compressed kernel config in the kernel address space
2359 * and sets up the config database.
2360 *
2361 * @returns VBox status code.
2362 * @param pThis The Linux digger data.
2363 * @param pUVM The user mode VM handle.
2364 */
2365static int dbgDiggerLinuxCfgFind(PDBGDIGGERLINUX pThis, PUVM pUVM)
2366{
2367 int rc = VINF_SUCCESS;
2368
2369 /*
2370 * Go looking for the IKCFG_ST string which indicates the start
2371 * of the compressed config file.
2372 */
2373 static const uint8_t s_abCfgNeedleStart[] = "IKCFG_ST";
2374 static const uint8_t s_abCfgNeedleEnd[] = "IKCFG_ED";
2375 DBGFADDRESS CurAddr = pThis->AddrLinuxBanner;
2376 uint32_t cbLeft = LNX_MAX_KERNEL_SIZE;
2377 while (cbLeft > 4096)
2378 {
2379 DBGFADDRESS HitAddrStart;
2380 rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &CurAddr, cbLeft, 1 /*uAlign*/,
2381 s_abCfgNeedleStart, sizeof(s_abCfgNeedleStart) - 1, &HitAddrStart);
2382 if (RT_FAILURE(rc))
2383 break;
2384
2385 /* Check for the end marker which shouldn't be that far away. */
2386 DBGFR3AddrAdd(&HitAddrStart, sizeof(s_abCfgNeedleStart) - 1);
2387 DBGFADDRESS HitAddrEnd;
2388 rc = DBGFR3MemScan(pUVM, 0 /* idCpu */, &HitAddrStart, LNX_MAX_COMPRESSED_CFG_SIZE,
2389 1 /* uAlign */, s_abCfgNeedleEnd, sizeof(s_abCfgNeedleEnd) - 1, &HitAddrEnd);
2390 if (RT_SUCCESS(rc))
2391 {
2392 /* Allocate a buffer to hold the compressed data between the markers and fetch it. */
2393 RTGCUINTPTR cbCfg = HitAddrEnd.FlatPtr - HitAddrStart.FlatPtr;
2394 Assert(cbCfg == (size_t)cbCfg);
2395 rc = dbgDiggerLinuxCfgDecode(pThis, pUVM, &HitAddrStart, cbCfg);
2396 if (RT_SUCCESS(rc))
2397 break;
2398 }
2399
2400 /*
2401 * Advance.
2402 */
2403 RTGCUINTPTR cbDistance = HitAddrStart.FlatPtr - CurAddr.FlatPtr + sizeof(s_abCfgNeedleStart) - 1;
2404 if (RT_UNLIKELY(cbDistance >= cbLeft))
2405 {
2406 LogFunc(("Failed to find compressed kernel config\n"));
2407 break;
2408 }
2409 cbLeft -= cbDistance;
2410 DBGFR3AddrAdd(&CurAddr, cbDistance);
2411
2412 }
2413
2414 return rc;
2415}
2416
2417/**
2418 * Probes for a Linux kernel starting at the given address.
2419 *
2420 * @returns Flag whether something which looks like a valid Linux kernel was found.
2421 * @param pThis The Linux digger data.
2422 * @param pUVM The user mode VM handle.
2423 * @param uAddrStart The address to start scanning at.
2424 * @param cbScan How much to scan.
2425 */
2426static bool dbgDiggerLinuxProbeWithAddr(PDBGDIGGERLINUX pThis, PUVM pUVM, RTGCUINTPTR uAddrStart, size_t cbScan)
2427{
2428 /*
2429 * Look for "Linux version " at the start of the rodata segment.
2430 * Hope that this comes before any message buffer or other similar string.
2431 */
2432 DBGFADDRESS KernelAddr;
2433 DBGFR3AddrFromFlat(pUVM, &KernelAddr, uAddrStart);
2434 DBGFADDRESS HitAddr;
2435 int rc = DBGFR3MemScan(pUVM, 0, &KernelAddr, cbScan, 1,
2436 g_abLinuxVersion, sizeof(g_abLinuxVersion) - 1, &HitAddr);
2437 if (RT_SUCCESS(rc))
2438 {
2439 char szTmp[128];
2440 char const *pszX = &szTmp[sizeof(g_abLinuxVersion) - 1];
2441 rc = DBGFR3MemReadString(pUVM, 0, &HitAddr, szTmp, sizeof(szTmp));
2442 if ( RT_SUCCESS(rc)
2443 && ( ( pszX[0] == '2' /* 2.x.y with x in {0..6} */
2444 && pszX[1] == '.'
2445 && pszX[2] >= '0'
2446 && pszX[2] <= '6')
2447 || ( pszX[0] >= '3' /* 3.x, 4.x, ... 9.x */
2448 && pszX[0] <= '9'
2449 && pszX[1] == '.'
2450 && pszX[2] >= '0'
2451 && pszX[2] <= '9')
2452 )
2453 )
2454 {
2455 pThis->AddrKernelBase = KernelAddr;
2456 pThis->AddrLinuxBanner = HitAddr;
2457 return true;
2458 }
2459 }
2460
2461 return false;
2462}
2463
2464/**
2465 * Probes for a Linux kernel which has KASLR enabled.
2466 *
2467 * @returns Flag whether a possible candidate location was found.
2468 * @param pThis The Linux digger data.
2469 * @param pUVM The user mode VM handle.
2470 * @param uAddrKernelStart The first address the kernel is expected at.
2471 */
2472static bool dbgDiggerLinuxProbeKaslr(PDBGDIGGERLINUX pThis, PUVM pUVM, RTGCUINTPTR uAddrKernelStart)
2473{
2474 /**
2475 * With KASLR the kernel is loaded at a different address at each boot making detection
2476 * more difficult for us.
2477 *
2478 * The randomization is done in arch/x86/boot/compressed/kaslr.c:choose_random_location() (as of Nov 2017).
2479 * At the end of the method a random offset is chosen using find_random_virt_addr() which is added to the
2480 * kernel map start in the caller (the start of the kernel depends on the bit size, see LNX32_KERNEL_ADDRESS_START
2481 * and LNX64_KERNEL_ADDRESS_START for 32bit and 64bit kernels respectively).
2482 * The lowest offset possible is LOAD_PHYSICAL_ADDR which is defined in arch/x86/include/asm/boot.h
2483 * using CONFIG_PHYSICAL_START aligned to CONFIG_PHYSICAL_ALIGN.
2484 * The default CONFIG_PHYSICAL_START and CONFIG_PHYSICAL_ALIGN are both 0x1000000 no matter whether a 32bit
2485 * or a 64bit kernel is used. So the lowest offset to the kernel start address is 0x1000000.
2486 * The find_random_virt_addr() the number of possible slots where the kernel can be placed based on the image size
2487 * is calculated using the following formula:
2488 * cSlots = ((KERNEL_IMAGE_SIZE - 0x1000000 (minimum) - image_size) / 0x1000000 (CONFIG_PHYSICAL_ALIGN)) + 1
2489 *
2490 * KERNEL_IMAGE_SIZE is 1GB for 64bit kernels and 512MB for 32bit kernels, so the maximum number of slots (resulting
2491 * in the largest possible offset) can be achieved when image_size (which contains the real size of the kernel image
2492 * which is unknown for us) goes to 0 and a 1GB KERNEL_IMAGE_SIZE is assumed. With that the biggest cSlots which can be
2493 * achieved is 64. The chosen random offset is taken from a random long integer using kaslr_get_random_long() modulo the
2494 * number of slots which selects a slot between 0 and 63. The final offset is calculated using:
2495 * offAddr = random_addr * 0x1000000 (CONFIG_PHYSICAL_ALIGN) + 0x1000000 (minimum)
2496 *
2497 * So the highest offset the kernel can start is 0x40000000 which is 1GB (plus the maximum kernel size we defined).
2498 */
2499 if (dbgDiggerLinuxProbeWithAddr(pThis, pUVM, uAddrKernelStart, _1G + LNX_MAX_KERNEL_SIZE))
2500 return true;
2501
2502 return false;
2503}
2504
2505/**
2506 * @copydoc DBGFOSREG::pfnInit
2507 */
2508static DECLCALLBACK(int) dbgDiggerLinuxInit(PUVM pUVM, void *pvData)
2509{
2510 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
2511 Assert(!pThis->fValid);
2512
2513 /*
2514 * Assume 64-bit kernels all live way beyond 32-bit address space.
2515 */
2516 pThis->f64Bit = pThis->AddrLinuxBanner.FlatPtr > UINT32_MAX;
2517 pThis->fRelKrnlAddr = false;
2518
2519 pThis->hCfgDb = NULL;
2520
2521 /*
2522 * Try to find the compressed kernel config and parse it before we try
2523 * to get the symbol table, the config database is required to select
2524 * the method to use.
2525 */
2526 int rc = dbgDiggerLinuxCfgFind(pThis, pUVM);
2527 if (RT_FAILURE(rc))
2528 LogFlowFunc(("Failed to find kernel config (%Rrc), no config database available\n", rc));
2529
2530 static const uint8_t s_abNeedle[] = "kobj";
2531 rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, s_abNeedle, sizeof(s_abNeedle) - 1);
2532 if (RT_FAILURE(rc))
2533 {
2534 /* Try alternate needle (seen on older x86 Linux kernels). */
2535 static const uint8_t s_abNeedleAlt[] = "kobjec";
2536 rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, s_abNeedleAlt, sizeof(s_abNeedleAlt) - 1);
2537 if (RT_FAILURE(rc))
2538 {
2539 static const uint8_t s_abNeedleOSuseX86[] = "nmi"; /* OpenSuSe 10.2 x86 */
2540 rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, s_abNeedleOSuseX86, sizeof(s_abNeedleOSuseX86) - 1);
2541 }
2542 }
2543
2544 pThis->fValid = true;
2545 return VINF_SUCCESS;
2546}
2547
2548
2549/**
2550 * @copydoc DBGFOSREG::pfnProbe
2551 */
2552static DECLCALLBACK(bool) dbgDiggerLinuxProbe(PUVM pUVM, void *pvData)
2553{
2554 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
2555
2556 for (unsigned i = 0; i < RT_ELEMENTS(g_au64LnxKernelAddresses); i++)
2557 {
2558 if (dbgDiggerLinuxProbeWithAddr(pThis, pUVM, g_au64LnxKernelAddresses[i], LNX_MAX_KERNEL_SIZE))
2559 return true;
2560 }
2561
2562 /* Maybe the kernel uses KASLR. */
2563 if (dbgDiggerLinuxProbeKaslr(pThis, pUVM, LNX32_KERNEL_ADDRESS_START))
2564 return true;
2565
2566 if (dbgDiggerLinuxProbeKaslr(pThis, pUVM, LNX64_KERNEL_ADDRESS_START))
2567 return true;
2568
2569 return false;
2570}
2571
2572
2573/**
2574 * @copydoc DBGFOSREG::pfnDestruct
2575 */
2576static DECLCALLBACK(void) dbgDiggerLinuxDestruct(PUVM pUVM, void *pvData)
2577{
2578 RT_NOREF2(pUVM, pvData);
2579}
2580
2581
2582/**
2583 * @copydoc DBGFOSREG::pfnConstruct
2584 */
2585static DECLCALLBACK(int) dbgDiggerLinuxConstruct(PUVM pUVM, void *pvData)
2586{
2587 RT_NOREF1(pUVM);
2588 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
2589 pThis->IDmesg.u32Magic = DBGFOSIDMESG_MAGIC;
2590 pThis->IDmesg.pfnQueryKernelLog = dbgDiggerLinuxIDmsg_QueryKernelLog;
2591 pThis->IDmesg.u32EndMagic = DBGFOSIDMESG_MAGIC;
2592
2593 return VINF_SUCCESS;
2594}
2595
2596
2597const DBGFOSREG g_DBGDiggerLinux =
2598{
2599 /* .u32Magic = */ DBGFOSREG_MAGIC,
2600 /* .fFlags = */ 0,
2601 /* .cbData = */ sizeof(DBGDIGGERLINUX),
2602 /* .szName = */ "Linux",
2603 /* .pfnConstruct = */ dbgDiggerLinuxConstruct,
2604 /* .pfnDestruct = */ dbgDiggerLinuxDestruct,
2605 /* .pfnProbe = */ dbgDiggerLinuxProbe,
2606 /* .pfnInit = */ dbgDiggerLinuxInit,
2607 /* .pfnRefresh = */ dbgDiggerLinuxRefresh,
2608 /* .pfnTerm = */ dbgDiggerLinuxTerm,
2609 /* .pfnQueryVersion = */ dbgDiggerLinuxQueryVersion,
2610 /* .pfnQueryInterface = */ dbgDiggerLinuxQueryInterface,
2611 /* .u32EndMagic = */ DBGFOSREG_MAGIC
2612};
2613
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