VirtualBox

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

Last change on this file since 82781 was 80109, checked in by vboxsync, 5 years ago

Debugger/DBGPlugInLinux: Fix broken symbol digging caused by r132005 (Fix index into the buffer)

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

© 2023 Oracle
ContactPrivacy policyTerms of Use