VirtualBox

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

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

scm: cleaning up todos

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