VirtualBox

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

Last change on this file since 64359 was 64359, checked in by vboxsync, 8 years ago

Dbugger/Digger/Linux: Add support for relative offsets to the kernel symbol loading existing since Linux 4.6 and being used in recent distributions

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette