VirtualBox

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

Last change on this file since 61558 was 61558, checked in by vboxsync, 9 years ago

Debugger/Digger/Linux: Implement getting the kernel log for older kernels where the log buffer is a simple ASCII buffer

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 63.5 KB
Line 
1/* $Id: DBGPlugInLinux.cpp 61558 2016-06-08 08:13:51Z vboxsync $ */
2/** @file
3 * DBGPlugInLinux - Debugger and Guest OS Digger Plugin For Linux.
4 */
5
6/*
7 * Copyright (C) 2008-2015 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/string.h>
28#include <iprt/mem.h>
29#include <iprt/stream.h>
30#include <iprt/ctype.h>
31
32
33/*********************************************************************************************************************************
34* Structures and Typedefs *
35*********************************************************************************************************************************/
36
37/** @name InternalLinux structures
38 * @{ */
39
40
41/** @} */
42
43
44/**
45 * Linux guest OS digger instance data.
46 */
47typedef struct DBGDIGGERLINUX
48{
49 /** Whether the information is valid or not.
50 * (For fending off illegal interface method calls.) */
51 bool fValid;
52 /** Set if 64-bit, clear if 32-bit. */
53 bool f64Bit;
54
55 /** The address of the linux banner.
56 * This is set during probing. */
57 DBGFADDRESS AddrLinuxBanner;
58 /** Kernel base address.
59 * This is set during probing, refined during kallsyms parsing. */
60 DBGFADDRESS AddrKernelBase;
61 /** The kernel size. */
62 uint32_t cbKernel;
63
64 /** The number of kernel symbols (kallsyms_num_syms).
65 * This is set during init. */
66 uint32_t cKernelSymbols;
67 /** The size of the kernel name table (sizeof(kallsyms_names)). */
68 uint32_t cbKernelNames;
69 /** Number of entries in the kernel_markers table. */
70 uint32_t cKernelNameMarkers;
71 /** The size of the kernel symbol token table. */
72 uint32_t cbKernelTokenTable;
73 /** The address of the encoded kernel symbol names (kallsyms_names). */
74 DBGFADDRESS AddrKernelNames;
75 /** The address of the kernel symbol addresses (kallsyms_addresses). */
76 DBGFADDRESS AddrKernelAddresses;
77 /** The address of the kernel symbol name markers (kallsyms_markers). */
78 DBGFADDRESS AddrKernelNameMarkers;
79 /** The address of the kernel symbol token table (kallsyms_token_table). */
80 DBGFADDRESS AddrKernelTokenTable;
81 /** The address of the kernel symbol token index table (kallsyms_token_index). */
82 DBGFADDRESS AddrKernelTokenIndex;
83
84 /** The kernel message log interface. */
85 DBGFOSIDMESG IDmesg;
86} DBGDIGGERLINUX;
87/** Pointer to the linux guest OS digger instance data. */
88typedef DBGDIGGERLINUX *PDBGDIGGERLINUX;
89
90
91/**
92 * The current printk_log structure.
93 */
94typedef struct LNXPRINTKHDR
95{
96 /** Monotonic timestamp. */
97 uint64_t nsTimestamp;
98 /** The total size of this message record. */
99 uint16_t cbTotal;
100 /** The size of the text part (immediately follows the header). */
101 uint16_t cbText;
102 /** The size of the optional dictionary part (follows the text). */
103 uint16_t cbDict;
104 /** The syslog facility number. */
105 uint8_t bFacility;
106 /** First 5 bits are internal flags, next 3 bits are log level. */
107 uint8_t fFlagsAndLevel;
108} LNXPRINTKHDR;
109AssertCompileSize(LNXPRINTKHDR, 2*sizeof(uint64_t));
110/** Pointer to linux printk_log header. */
111typedef LNXPRINTKHDR *PLNXPRINTKHDR;
112/** Pointer to linux const printk_log header. */
113typedef LNXPRINTKHDR const *PCLNXPRINTKHDR;
114
115
116/*********************************************************************************************************************************
117* Defined Constants And Macros *
118*********************************************************************************************************************************/
119/** Validates a 32-bit linux kernel address */
120#define LNX32_VALID_ADDRESS(Addr) ((Addr) > UINT32_C(0x80000000) && (Addr) < UINT32_C(0xfffff000))
121/** Validates a 64-bit linux kernel address */
122#define LNX64_VALID_ADDRESS(Addr) ((Addr) > UINT64_C(0xffff800000000000) && (Addr) < UINT64_C(0xfffffffffffff000))
123
124/** The max kernel size. */
125#define LNX_MAX_KERNEL_SIZE UINT32_C(0x0f000000)
126
127/** The maximum size we expect for kallsyms_names. */
128#define LNX_MAX_KALLSYMS_NAMES_SIZE UINT32_C(0x200000)
129/** The maximum size we expect for kallsyms_token_table. */
130#define LNX_MAX_KALLSYMS_TOKEN_TABLE_SIZE UINT32_C(0x10000)
131/** The minimum number of symbols we expect in kallsyms_num_syms. */
132#define LNX_MIN_KALLSYMS_SYMBOLS UINT32_C(2048)
133/** The maximum number of symbols we expect in kallsyms_num_syms. */
134#define LNX_MAX_KALLSYMS_SYMBOLS UINT32_C(1048576)
135/** The min length an encoded symbol in kallsyms_names is expected to have. */
136#define LNX_MIN_KALLSYMS_ENC_LENGTH UINT8_C(1)
137/** The max length an encoded symbol in kallsyms_names is expected to have.
138 * @todo check real life here. */
139#define LNX_MAX_KALLSYMS_ENC_LENGTH UINT8_C(28)
140/** The approximate maximum length of a string token. */
141#define LNX_MAX_KALLSYMS_TOKEN_LEN UINT16_C(32)
142
143/** Module tag for linux ('linuxmod' on little endian ASCII systems). */
144#define DIG_LNX_MOD_TAG UINT64_C(0x545f5d78758e898c)
145
146
147/*********************************************************************************************************************************
148* Internal Functions *
149*********************************************************************************************************************************/
150static DECLCALLBACK(int) dbgDiggerLinuxInit(PUVM pUVM, void *pvData);
151
152
153/*********************************************************************************************************************************
154* Global Variables *
155*********************************************************************************************************************************/
156/** Table of common linux kernel addresses. */
157static uint64_t g_au64LnxKernelAddresses[] =
158{
159 UINT64_C(0xc0100000),
160 UINT64_C(0x90100000),
161 UINT64_C(0xffffffff80200000)
162};
163
164static const uint8_t g_abLinuxVersion[] = "Linux version ";
165
166/**
167 * Disassembles a simple getter returning the value for it.
168 *
169 * @returns VBox status code.
170 * @param pThis The Linux digger data.
171 * @param pUVM The VM handle.
172 * @param hMod The module to use.
173 * @param pszSymbol The symbol of the getter.
174 * @param pvVal Where to store the value on success.
175 * @param cbVal Size of the value in bytes.
176 */
177static int dbgDiggerLinuxDisassembleSimpleGetter(PDBGDIGGERLINUX pThis, PUVM pUVM, RTDBGMOD hMod,
178 const char *pszSymbol, void *pvVal, uint32_t cbVal)
179{
180 int rc = VINF_SUCCESS;
181
182 RTDBGSYMBOL SymInfo;
183 rc = RTDbgModSymbolByName(hMod, pszSymbol, &SymInfo);
184 if (RT_SUCCESS(rc))
185 {
186 /*
187 * Do the diassembling. Disassemble until a ret instruction is encountered
188 * or a limit is reached (don't want to disassemble for too long as the getter
189 * should be short).
190 * push and pop instructions are skipped as well as any mov instructions not
191 * touching the rax or eax register (depending on the size of the value).
192 */
193 unsigned cInstrDisassembled = 0;
194 uint32_t offInstr = 0;
195 bool fRet = false;
196 DISSTATE DisState;
197 RT_ZERO(DisState);
198
199 do
200 {
201 DBGFADDRESS Addr;
202 RTGCPTR GCPtrCur = (RTGCPTR)SymInfo.Value + pThis->AddrKernelBase.FlatPtr + offInstr;
203 DBGFR3AddrFromFlat(pUVM, &Addr, GCPtrCur);
204
205 /* Prefetch the instruction. */
206 uint8_t abInstr[32];
207 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, &abInstr[0], sizeof(abInstr));
208 if (RT_SUCCESS(rc))
209 {
210 uint32_t cbInstr = 0;
211
212 rc = DISInstr(&abInstr[0], pThis->f64Bit ? DISCPUMODE_64BIT : DISCPUMODE_32BIT, &DisState, &cbInstr);
213 if (RT_SUCCESS(rc))
214 {
215 switch (DisState.pCurInstr->uOpcode)
216 {
217 case OP_PUSH:
218 case OP_POP:
219 case OP_NOP:
220 case OP_LEA:
221 break;
222 case OP_RETN:
223 /* Getter returned, abort disassembling. */
224 fRet = true;
225 break;
226 case OP_MOV:
227 /*
228 * Check that the destination is either rax or eax depending on the
229 * value size.
230 *
231 * Param1 is the destination and Param2 the source.
232 */
233 if ( ( ( (DisState.Param1.fUse & (DISUSE_BASE | DISUSE_REG_GEN32))
234 && cbVal == sizeof(uint32_t))
235 || ( (DisState.Param1.fUse & (DISUSE_BASE | DISUSE_REG_GEN64))
236 && cbVal == sizeof(uint64_t)))
237 && DisState.Param1.Base.idxGenReg == DISGREG_RAX)
238 {
239 /* Parse the source. */
240 if (DisState.Param2.fUse & (DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE64))
241 memcpy(pvVal, &DisState.Param2.uValue, cbVal);
242 else if (DisState.Param2.fUse & (DISUSE_RIPDISPLACEMENT32|DISUSE_DISPLACEMENT32|DISUSE_DISPLACEMENT64))
243 {
244 RTGCPTR GCPtrVal = 0;
245
246 if (DisState.Param2.fUse & DISUSE_RIPDISPLACEMENT32)
247 GCPtrVal = GCPtrCur + DisState.Param2.uDisp.i32 + cbInstr;
248 else if (DisState.Param2.fUse & DISUSE_DISPLACEMENT32)
249 GCPtrVal = (RTGCPTR)DisState.Param2.uDisp.u32;
250 else if (DisState.Param2.fUse & DISUSE_DISPLACEMENT64)
251 GCPtrVal = (RTGCPTR)DisState.Param2.uDisp.u64;
252 else
253 AssertMsgFailedBreakStmt(("Invalid displacement\n"), rc = VERR_INVALID_STATE);
254
255 DBGFADDRESS AddrVal;
256 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/,
257 DBGFR3AddrFromFlat(pUVM, &AddrVal, GCPtrVal),
258 pvVal, cbVal);
259 }
260 }
261 break;
262 default:
263 /* All other instructions will cause an error for now (playing safe here). */
264 rc = VERR_INVALID_PARAMETER;
265 break;
266 }
267 cInstrDisassembled++;
268 offInstr += cbInstr;
269 }
270 }
271 } while ( RT_SUCCESS(rc)
272 && cInstrDisassembled < 20
273 && !fRet);
274 }
275
276 return rc;
277}
278
279/**
280 * Try to get at the log buffer starting address and size by disassembling some exposed helpers.
281 *
282 * @returns VBox status code.
283 * @param pThis The Linux digger data.
284 * @param pUVM The VM handle.
285 * @param hMod The module to use.
286 * @param pGCPtrLogBuf Where to store the log buffer pointer on success.
287 * @param pcbLogBuf Where to store the size of the log buffer on success.
288 */
289static int dbgDiggerLinuxQueryLogBufferPtrs(PDBGDIGGERLINUX pThis, PUVM pUVM, RTDBGMOD hMod,
290 RTGCPTR *pGCPtrLogBuf, uint32_t *pcbLogBuf)
291{
292 int rc = VINF_SUCCESS;
293
294 struct { void *pvVar; size_t cbHost, cbGuest; const char *pszSymbol; } aSymbols[] =
295 {
296 { pGCPtrLogBuf, sizeof(RTGCPTR), pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t), "log_buf_addr_get" },
297 { pcbLogBuf, sizeof(uint32_t), sizeof(uint32_t), "log_buf_len_get" }
298 };
299 for (uint32_t i = 0; i < RT_ELEMENTS(aSymbols) && RT_SUCCESS(rc); i++)
300 {
301 RT_BZERO(aSymbols[i].pvVar, aSymbols[i].cbHost);
302 Assert(aSymbols[i].cbHost >= aSymbols[i].cbGuest);
303 rc = dbgDiggerLinuxDisassembleSimpleGetter(pThis, pUVM, hMod, aSymbols[i].pszSymbol,
304 aSymbols[i].pvVar, aSymbols[i].cbGuest);
305 }
306
307 return rc;
308}
309
310/**
311 * Returns whether the log buffer is a simple ascii buffer or a record based implementation
312 * based on the kernel version found.
313 *
314 * @returns Flag whether the log buffer is the simple ascii buffer.
315 * @param pThis The Linux digger data.
316 * @param pUVM The user mode VM handle.
317 */
318static bool dbgDiggerLinuxLogBufferIsAsciiBuffer(PDBGDIGGERLINUX pThis, PUVM pUVM)
319{
320 char szTmp[128];
321 char const *pszVer = &szTmp[sizeof(g_abLinuxVersion) - 1];
322
323 RT_ZERO(szTmp);
324 int rc = DBGFR3MemReadString(pUVM, 0, &pThis->AddrLinuxBanner, szTmp, sizeof(szTmp) - 1);
325 if ( RT_SUCCESS(rc)
326 && RTStrVersionCompare(pszVer, "3.4") == -1)
327 return true;
328
329 return false;
330}
331
332/**
333 * Worker to get at the kernel log for pre 3.4 kernels where the log buffer was just a char buffer.
334 *
335 * @returns VBox status code.
336 * @param pThis The Linux digger data.
337 * @param pUVM The VM user mdoe handle.
338 * @param hMod The debug module handle.
339 * @param fFlags Flags reserved for future use, MBZ.
340 * @param cMessages The number of messages to retrieve, counting from the
341 * end of the log (i.e. like tail), use UINT32_MAX for all.
342 * @param pszBuf The output buffer.
343 * @param cbBuf The buffer size.
344 * @param pcbActual Where to store the number of bytes actually returned,
345 * including zero terminator. On VERR_BUFFER_OVERFLOW this
346 * holds the necessary buffer size. Optional.
347 */
348static int dbgDiggerLinuxLogBufferQueryAscii(PDBGDIGGERLINUX pThis, PUVM pUVM, RTDBGMOD hMod,
349 uint32_t fFlags, uint32_t cMessages,
350 char *pszBuf, size_t cbBuf, size_t *pcbActual)
351{
352 int rc = VINF_SUCCESS;
353 RTGCPTR GCPtrLogBuf;
354 uint32_t cbLogBuf;
355
356 struct { void *pvVar; size_t cbHost, cbGuest; const char *pszSymbol; } aSymbols[] =
357 {
358 { &GCPtrLogBuf, sizeof(GCPtrLogBuf), pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t), "log_buf" },
359 { &cbLogBuf, sizeof(cbLogBuf), sizeof(cbLogBuf), "log_buf_len" },
360 };
361 for (uint32_t i = 0; i < RT_ELEMENTS(aSymbols); i++)
362 {
363 RTDBGSYMBOL SymInfo;
364 rc = RTDbgModSymbolByName(hMod, aSymbols[i].pszSymbol, &SymInfo);
365 if (RT_SUCCESS(rc))
366 {
367 RT_BZERO(aSymbols[i].pvVar, aSymbols[i].cbHost);
368 Assert(aSymbols[i].cbHost >= aSymbols[i].cbGuest);
369 DBGFADDRESS Addr;
370 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/,
371 DBGFR3AddrFromFlat(pUVM, &Addr, (RTGCPTR)SymInfo.Value + pThis->AddrKernelBase.FlatPtr),
372 aSymbols[i].pvVar, aSymbols[i].cbGuest);
373 if (RT_SUCCESS(rc))
374 continue;
375 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Reading '%s' at %RGv: %Rrc\n", aSymbols[i].pszSymbol, Addr.FlatPtr, rc));
376 }
377 else
378 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error looking up '%s': %Rrc\n", aSymbols[i].pszSymbol, rc));
379 rc = VERR_NOT_FOUND;
380 break;
381 }
382
383 if (RT_FAILURE(rc))
384 return rc;
385
386 /*
387 * Check if the values make sense.
388 */
389 if (pThis->f64Bit ? !LNX64_VALID_ADDRESS(GCPtrLogBuf) : !LNX32_VALID_ADDRESS(GCPtrLogBuf))
390 {
391 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf' value %RGv is not valid.\n", GCPtrLogBuf));
392 return VERR_NOT_FOUND;
393 }
394 if ( cbLogBuf < 4096
395 || !RT_IS_POWER_OF_TWO(cbLogBuf)
396 || cbLogBuf > 16*_1M)
397 {
398 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf_len' value %#x is not valid.\n", cbLogBuf));
399 return VERR_NOT_FOUND;
400 }
401
402 /*
403 * Read the whole log buffer.
404 */
405 uint8_t *pbLogBuf = (uint8_t *)RTMemAlloc(cbLogBuf);
406 if (!pbLogBuf)
407 {
408 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Failed to allocate %#x bytes for log buffer\n", cbLogBuf));
409 return VERR_NO_MEMORY;
410 }
411 DBGFADDRESS Addr;
412 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, GCPtrLogBuf), pbLogBuf, cbLogBuf);
413 if (RT_FAILURE(rc))
414 {
415 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error reading %#x bytes of log buffer at %RGv: %Rrc\n",
416 cbLogBuf, Addr.FlatPtr, rc));
417 RTMemFree(pbLogBuf);
418 return VERR_NOT_FOUND;
419 }
420
421 /** @todo: Try to parse where the single messages start to make use of cMessages. */
422 memcpy(&pszBuf[0], pbLogBuf, RT_MIN(cbBuf, cbLogBuf));
423
424 /* Done with the buffer. */
425 RTMemFree(pbLogBuf);
426
427 /* Set return size value. */
428 if (pcbActual)
429 *pcbActual = RT_MIN(cbBuf, cbLogBuf);
430
431 return cbBuf <= cbLogBuf ? VINF_SUCCESS : VERR_BUFFER_OVERFLOW;
432}
433
434/**
435 * Worker to get at the kernel log for post 3.4 kernels where the log buffer contains records.
436 *
437 * @returns VBox status code.
438 * @param pThis The Linux digger data.
439 * @param pUVM The VM user mdoe handle.
440 * @param hMod The debug module handle.
441 * @param fFlags Flags reserved for future use, MBZ.
442 * @param cMessages The number of messages to retrieve, counting from the
443 * end of the log (i.e. like tail), use UINT32_MAX for all.
444 * @param pszBuf The output buffer.
445 * @param cbBuf The buffer size.
446 * @param pcbActual Where to store the number of bytes actually returned,
447 * including zero terminator. On VERR_BUFFER_OVERFLOW this
448 * holds the necessary buffer size. Optional.
449 */
450static int dbgDiggerLinuxLogBufferQueryRecords(PDBGDIGGERLINUX pThis, PUVM pUVM, RTDBGMOD hMod,
451 uint32_t fFlags, uint32_t cMessages,
452 char *pszBuf, size_t cbBuf, size_t *pcbActual)
453{
454 int rc = VINF_SUCCESS;
455 RTGCPTR GCPtrLogBuf;
456 uint32_t cbLogBuf;
457 uint32_t idxFirst;
458 uint32_t idxNext;
459
460 struct { void *pvVar; size_t cbHost, cbGuest; const char *pszSymbol; } aSymbols[] =
461 {
462 { &GCPtrLogBuf, sizeof(GCPtrLogBuf), pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t), "log_buf" },
463 { &cbLogBuf, sizeof(cbLogBuf), sizeof(cbLogBuf), "log_buf_len" },
464 { &idxFirst, sizeof(idxFirst), sizeof(idxFirst), "log_first_idx" },
465 { &idxNext, sizeof(idxNext), sizeof(idxNext), "log_next_idx" },
466 };
467 for (uint32_t i = 0; i < RT_ELEMENTS(aSymbols); i++)
468 {
469 RTDBGSYMBOL SymInfo;
470 rc = RTDbgModSymbolByName(hMod, aSymbols[i].pszSymbol, &SymInfo);
471 if (RT_SUCCESS(rc))
472 {
473 RT_BZERO(aSymbols[i].pvVar, aSymbols[i].cbHost);
474 Assert(aSymbols[i].cbHost >= aSymbols[i].cbGuest);
475 DBGFADDRESS Addr;
476 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/,
477 DBGFR3AddrFromFlat(pUVM, &Addr, (RTGCPTR)SymInfo.Value + pThis->AddrKernelBase.FlatPtr),
478 aSymbols[i].pvVar, aSymbols[i].cbGuest);
479 if (RT_SUCCESS(rc))
480 continue;
481 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Reading '%s' at %RGv: %Rrc\n", aSymbols[i].pszSymbol, Addr.FlatPtr, rc));
482 }
483 else
484 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error looking up '%s': %Rrc\n", aSymbols[i].pszSymbol, rc));
485 rc = VERR_NOT_FOUND;
486 break;
487 }
488
489 /*
490 * Some kernels don't expose the variables in kallsyms so we have to try disassemble
491 * some public helpers to get at the addresses.
492 *
493 * @todo: Maybe cache those values so we don't have to do the heavy work every time?
494 */
495 if (rc == VERR_NOT_FOUND)
496 {
497 idxFirst = 0;
498 idxNext = 0;
499 rc = dbgDiggerLinuxQueryLogBufferPtrs(pThis, pUVM, hMod, &GCPtrLogBuf, &cbLogBuf);
500 if (RT_FAILURE(rc))
501 return rc;
502 }
503
504 /*
505 * Check if the values make sense.
506 */
507 if (pThis->f64Bit ? !LNX64_VALID_ADDRESS(GCPtrLogBuf) : !LNX32_VALID_ADDRESS(GCPtrLogBuf))
508 {
509 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf' value %RGv is not valid.\n", GCPtrLogBuf));
510 return VERR_NOT_FOUND;
511 }
512 if ( cbLogBuf < 4096
513 || !RT_IS_POWER_OF_TWO(cbLogBuf)
514 || cbLogBuf > 16*_1M)
515 {
516 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf_len' value %#x is not valid.\n", cbLogBuf));
517 return VERR_NOT_FOUND;
518 }
519 uint32_t const cbLogAlign = 4;
520 if ( idxFirst > cbLogBuf - sizeof(LNXPRINTKHDR)
521 || (idxFirst & (cbLogAlign - 1)) != 0)
522 {
523 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_first_idx' value %#x is not valid.\n", idxFirst));
524 return VERR_NOT_FOUND;
525 }
526 if ( idxNext > cbLogBuf - sizeof(LNXPRINTKHDR)
527 || (idxNext & (cbLogAlign - 1)) != 0)
528 {
529 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_next_idx' value %#x is not valid.\n", idxNext));
530 return VERR_NOT_FOUND;
531 }
532
533 /*
534 * Read the whole log buffer.
535 */
536 uint8_t *pbLogBuf = (uint8_t *)RTMemAlloc(cbLogBuf);
537 if (!pbLogBuf)
538 {
539 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Failed to allocate %#x bytes for log buffer\n", cbLogBuf));
540 return VERR_NO_MEMORY;
541 }
542 DBGFADDRESS Addr;
543 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, GCPtrLogBuf), pbLogBuf, cbLogBuf);
544 if (RT_FAILURE(rc))
545 {
546 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error reading %#x bytes of log buffer at %RGv: %Rrc\n",
547 cbLogBuf, Addr.FlatPtr, rc));
548 RTMemFree(pbLogBuf);
549 return VERR_NOT_FOUND;
550 }
551
552 /*
553 * Count the messages in the buffer while doing some basic validation.
554 */
555 uint32_t const cbUsed = idxFirst == idxNext ? cbLogBuf /* could be empty... */
556 : idxFirst < idxNext ? idxNext - idxFirst : cbLogBuf - idxFirst + idxNext;
557 uint32_t cbLeft = cbUsed;
558 uint32_t offCur = idxFirst;
559 uint32_t cLogMsgs = 0;
560
561 while (cbLeft > 0)
562 {
563 PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
564 if (!pHdr->cbTotal)
565 {
566 /* Wrap around packet, most likely... */
567 if (cbLogBuf - offCur >= cbLeft)
568 break;
569 offCur = 0;
570 pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
571 }
572 if (RT_UNLIKELY( pHdr->cbTotal > cbLogBuf - sizeof(*pHdr) - offCur
573 || pHdr->cbTotal > cbLeft
574 || (pHdr->cbTotal & (cbLogAlign - 1)) != 0
575 || pHdr->cbTotal < (uint32_t)pHdr->cbText + (uint32_t)pHdr->cbDict + sizeof(*pHdr) ))
576 {
577 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Invalid printk_log record at %#x: cbTotal=%#x cbText=%#x cbDict=%#x cbLogBuf=%#x cbLeft=%#x\n",
578 offCur, pHdr->cbTotal, pHdr->cbText, pHdr->cbDict, cbLogBuf, cbLeft));
579 rc = VERR_INVALID_STATE;
580 break;
581 }
582
583 if (pHdr->cbText > 0)
584 cLogMsgs++;
585
586 /* next */
587 offCur += pHdr->cbTotal;
588 cbLeft -= pHdr->cbTotal;
589 }
590 if (RT_FAILURE(rc))
591 {
592 RTMemFree(pbLogBuf);
593 return rc;
594 }
595
596 /*
597 * Copy the messages into the output buffer.
598 */
599 offCur = idxFirst;
600 cbLeft = cbUsed;
601
602 /* Skip messages that the caller doesn't want. */
603 if (cMessages < cLogMsgs)
604 {
605 uint32_t cToSkip = cLogMsgs - cMessages;
606 while (cToSkip > 0)
607 {
608 PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
609 if (!pHdr->cbTotal)
610 {
611 offCur = 0;
612 pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
613 }
614 if (pHdr->cbText > 0)
615 cToSkip--;
616
617 /* next */
618 offCur += pHdr->cbTotal;
619 cbLeft -= pHdr->cbTotal;
620 }
621 }
622
623 /* Now copy the messages. */
624 size_t offDst = 0;
625 while (cbLeft > 0)
626 {
627 PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
628 if (!pHdr->cbTotal)
629 {
630 if (cbLogBuf - offCur >= cbLeft)
631 break;
632 offCur = 0;
633 pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
634 }
635
636 if (pHdr->cbText > 0)
637 {
638 char *pchText = (char *)(pHdr + 1);
639 size_t cchText = RTStrNLen(pchText, pHdr->cbText);
640 if (offDst + cchText < cbBuf)
641 {
642 memcpy(&pszBuf[offDst], pHdr + 1, cchText);
643 pszBuf[offDst + cchText] = '\n';
644 }
645 else if (offDst < cbBuf)
646 memcpy(&pszBuf[offDst], pHdr + 1, cbBuf - offDst);
647 offDst += cchText + 1;
648 }
649
650 /* next */
651 offCur += pHdr->cbTotal;
652 cbLeft -= pHdr->cbTotal;
653 }
654
655 /* Done with the buffer. */
656 RTMemFree(pbLogBuf);
657
658 /* Make sure we've reserved a char for the terminator. */
659 if (!offDst)
660 offDst = 1;
661
662 /* Set return size value. */
663 if (pcbActual)
664 *pcbActual = offDst;
665
666 if (offDst <= cbBuf)
667 return VINF_SUCCESS;
668 else
669 return VERR_BUFFER_OVERFLOW;
670}
671
672/**
673 * @interface_method_impl{DBGFOSIDMESG,pfnQueryKernelLog}
674 */
675static DECLCALLBACK(int) dbgDiggerLinuxIDmsg_QueryKernelLog(PDBGFOSIDMESG pThis, PUVM pUVM, uint32_t fFlags, uint32_t cMessages,
676 char *pszBuf, size_t cbBuf, size_t *pcbActual)
677{
678 PDBGDIGGERLINUX pData = RT_FROM_MEMBER(pThis, DBGDIGGERLINUX, IDmesg);
679
680 if (cMessages < 1)
681 return VERR_INVALID_PARAMETER;
682
683 /*
684 * Resolve the symbols we need and read their values.
685 */
686 RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
687 RTDBGMOD hMod;
688 int rc = RTDbgAsModuleByName(hAs, "vmlinux", 0, &hMod);
689 if (RT_FAILURE(rc))
690 return VERR_NOT_FOUND;
691 RTDbgAsRelease(hAs);
692
693 size_t cbActual;
694 /*
695 * Check whether the kernel log buffer is a simple char buffer or the newer
696 * record based implementation.
697 * The record based implementation was presumably introduced with kernel 3.4,
698 * see: http://thread.gmane.org/gmane.linux.kernel/1284184
699 */
700 if (dbgDiggerLinuxLogBufferIsAsciiBuffer(pData, pUVM))
701 rc = dbgDiggerLinuxLogBufferQueryAscii(pData, pUVM, hMod, fFlags, cMessages, pszBuf, cbBuf, &cbActual);
702 else
703 rc = dbgDiggerLinuxLogBufferQueryRecords(pData, pUVM, hMod, fFlags, cMessages, pszBuf, cbBuf, &cbActual);
704
705 /* Release the module in any case. */
706 RTDbgModRelease(hMod);
707
708 if (RT_FAILURE(rc) && rc != VERR_BUFFER_OVERFLOW)
709 return rc;
710
711 if (pcbActual)
712 *pcbActual = cbActual;
713
714 /*
715 * All VBox strings are UTF-8 and bad things may in theory happen if we
716 * pass bad UTF-8 to code which assumes it's all valid. So, we enforce
717 * UTF-8 upon the guest kernel messages here even if they (probably) have
718 * no defined code set in reality.
719 */
720 if ( RT_SUCCESS(rc)
721 && cbActual <= cbBuf)
722 {
723 pszBuf[cbActual - 1] = '\0';
724 RTStrPurgeEncoding(pszBuf);
725 return VINF_SUCCESS;
726 }
727
728 if (cbBuf)
729 {
730 pszBuf[cbBuf - 1] = '\0';
731 RTStrPurgeEncoding(pszBuf);
732 }
733 return VERR_BUFFER_OVERFLOW;
734}
735
736
737/**
738 * @copydoc DBGFOSREG::pfnQueryInterface
739 */
740static DECLCALLBACK(void *) dbgDiggerLinuxQueryInterface(PUVM pUVM, void *pvData, DBGFOSINTERFACE enmIf)
741{
742 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
743 switch (enmIf)
744 {
745 case DBGFOSINTERFACE_DMESG:
746 return &pThis->IDmesg;
747
748 default:
749 return NULL;
750 }
751}
752
753
754/**
755 * @copydoc DBGFOSREG::pfnQueryVersion
756 */
757static DECLCALLBACK(int) dbgDiggerLinuxQueryVersion(PUVM pUVM, void *pvData, char *pszVersion, size_t cchVersion)
758{
759 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
760 Assert(pThis->fValid);
761
762 /*
763 * It's all in the linux banner.
764 */
765 int rc = DBGFR3MemReadString(pUVM, 0, &pThis->AddrLinuxBanner, pszVersion, cchVersion);
766 if (RT_SUCCESS(rc))
767 {
768 char *pszEnd = RTStrEnd(pszVersion, cchVersion);
769 AssertReturn(pszEnd, VERR_BUFFER_OVERFLOW);
770 while ( pszEnd > pszVersion
771 && RT_C_IS_SPACE(pszEnd[-1]))
772 pszEnd--;
773 *pszEnd = '\0';
774 }
775 else
776 RTStrPrintf(pszVersion, cchVersion, "DBGFR3MemRead -> %Rrc", rc);
777
778 return rc;
779}
780
781
782/**
783 * @copydoc DBGFOSREG::pfnTerm
784 */
785static DECLCALLBACK(void) dbgDiggerLinuxTerm(PUVM pUVM, void *pvData)
786{
787 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
788 Assert(pThis->fValid);
789
790 pThis->fValid = false;
791}
792
793
794/**
795 * @copydoc DBGFOSREG::pfnRefresh
796 */
797static DECLCALLBACK(int) dbgDiggerLinuxRefresh(PUVM pUVM, void *pvData)
798{
799 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
800 NOREF(pThis);
801 Assert(pThis->fValid);
802
803 /*
804 * For now we'll flush and reload everything.
805 */
806 dbgDiggerLinuxTerm(pUVM, pvData);
807 return dbgDiggerLinuxInit(pUVM, pvData);
808}
809
810
811/**
812 * Worker for dbgDiggerLinuxFindStartOfNamesAndSymbolCount that update the
813 * digger data.
814 *
815 * @returns VINF_SUCCESS.
816 * @param pThis The Linux digger data to update.
817 * @param pAddrKernelNames The kallsyms_names address.
818 * @param cKernelSymbols The number of kernel symbol.
819 * @param cbAddress The guest address size.
820 */
821static int dbgDiggerLinuxFoundStartOfNames(PDBGDIGGERLINUX pThis, PCDBGFADDRESS pAddrKernelNames,
822 uint32_t cKernelSymbols, uint32_t cbAddress)
823{
824 pThis->cKernelSymbols = cKernelSymbols;
825 pThis->AddrKernelNames = *pAddrKernelNames;
826 pThis->AddrKernelAddresses = *pAddrKernelNames;
827 DBGFR3AddrSub(&pThis->AddrKernelAddresses, (cKernelSymbols + 1) * cbAddress);
828
829 Log(("dbgDiggerLinuxFoundStartOfNames: AddrKernelAddresses=%RGv\n"
830 "dbgDiggerLinuxFoundStartOfNames: cKernelSymbols=%#x (at %RGv)\n"
831 "dbgDiggerLinuxFoundStartOfNames: AddrKernelName=%RGv\n",
832 pThis->AddrKernelAddresses.FlatPtr,
833 pThis->cKernelSymbols, pThis->AddrKernelNames.FlatPtr - cbAddress,
834 pThis->AddrKernelNames.FlatPtr));
835 return VINF_SUCCESS;
836}
837
838
839/**
840 * Tries to find the address of the kallsyms_names, kallsyms_num_syms and
841 * kallsyms_addresses symbols.
842 *
843 * The kallsyms_num_syms is read and stored in pThis->cKernelSymbols, while the
844 * addresses of the other two are stored as pThis->AddrKernelNames and
845 * pThis->AddrKernelAddresses.
846 *
847 * @returns VBox status code, success indicating that all three variables have
848 * been found and taken down.
849 * @param pUVM The user mode VM handle.
850 * @param pThis The Linux digger data.
851 * @param pHitAddr An address we think is inside kallsyms_names.
852 */
853static int dbgDiggerLinuxFindStartOfNamesAndSymbolCount(PUVM pUVM, PDBGDIGGERLINUX pThis, PCDBGFADDRESS pHitAddr)
854{
855 /*
856 * Search backwards in chunks.
857 */
858 union
859 {
860 uint8_t ab[0x1000];
861 uint32_t au32[0x1000 / sizeof(uint32_t)];
862 uint64_t au64[0x1000 / sizeof(uint64_t)];
863 } uBuf;
864 uint32_t cbLeft = LNX_MAX_KALLSYMS_NAMES_SIZE;
865 uint32_t cbBuf = pHitAddr->FlatPtr & (sizeof(uBuf) - 1);
866 DBGFADDRESS CurAddr = *pHitAddr;
867 DBGFR3AddrSub(&CurAddr, cbBuf);
868 cbBuf += sizeof(uint64_t) - 1; /* In case our kobj hit is in the first 4/8 bytes. */
869 for (;;)
870 {
871 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
872 if (RT_FAILURE(rc))
873 return rc;
874
875 /*
876 * We assume that the three symbols are aligned on guest pointer boundrary.
877 *
878 * The boundrary between the two tables should be noticable as the number
879 * is unlikely to be more than 16 millions, there will be at least one zero
880 * byte where it is, 64-bit will have 5 zero bytes. Zero bytes aren't all
881 * that common in the kallsyms_names table.
882 *
883 * Also the kallsyms_names table starts with a length byte, which means
884 * we're likely to see a byte in the range 1..31.
885 *
886 * The kallsyms_addresses are mostly sorted (except for the start where the
887 * absolute symbols are), so we'll spot a bunch of kernel addresses
888 * immediately preceeding the kallsyms_num_syms field.
889 *
890 * Lazy bird: If kallsyms_num_syms is on a buffer boundrary, we skip
891 * the check for kernel addresses preceeding it.
892 */
893 if (pThis->f64Bit)
894 {
895 uint32_t i = cbBuf / sizeof(uint64_t);
896 while (i-- > 0)
897 if ( uBuf.au64[i] <= LNX_MAX_KALLSYMS_SYMBOLS
898 && uBuf.au64[i] >= LNX_MIN_KALLSYMS_SYMBOLS)
899 {
900 uint8_t *pb = (uint8_t *)&uBuf.au64[i + 1];
901 if ( pb[0] <= LNX_MAX_KALLSYMS_ENC_LENGTH
902 && pb[0] >= LNX_MIN_KALLSYMS_ENC_LENGTH)
903 {
904 if ( (i <= 0 || LNX64_VALID_ADDRESS(uBuf.au64[i - 1]))
905 && (i <= 1 || LNX64_VALID_ADDRESS(uBuf.au64[i - 2]))
906 && (i <= 2 || LNX64_VALID_ADDRESS(uBuf.au64[i - 3])))
907 return dbgDiggerLinuxFoundStartOfNames(pThis,
908 DBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint64_t)),
909 (uint32_t)uBuf.au64[i], sizeof(uint64_t));
910 }
911 }
912 }
913 else
914 {
915 uint32_t i = cbBuf / sizeof(uint32_t);
916 while (i-- > 0)
917 if ( uBuf.au32[i] <= LNX_MAX_KALLSYMS_SYMBOLS
918 && uBuf.au32[i] >= LNX_MIN_KALLSYMS_SYMBOLS)
919 {
920 uint8_t *pb = (uint8_t *)&uBuf.au32[i + 1];
921 if ( pb[0] <= LNX_MAX_KALLSYMS_ENC_LENGTH
922 && pb[0] >= LNX_MIN_KALLSYMS_ENC_LENGTH)
923 {
924 if ( (i <= 0 || LNX32_VALID_ADDRESS(uBuf.au32[i - 1]))
925 && (i <= 1 || LNX32_VALID_ADDRESS(uBuf.au32[i - 2]))
926 && (i <= 2 || LNX32_VALID_ADDRESS(uBuf.au32[i - 3])))
927 return dbgDiggerLinuxFoundStartOfNames(pThis,
928 DBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint32_t)),
929 uBuf.au32[i], sizeof(uint32_t));
930 }
931 }
932 }
933
934 /*
935 * Advance
936 */
937 if (RT_UNLIKELY(cbLeft <= sizeof(uBuf)))
938 {
939 Log(("dbgDiggerLinuxFindStartOfNamesAndSymbolCount: failed (pHitAddr=%RGv)\n", pHitAddr->FlatPtr));
940 return VERR_NOT_FOUND;
941 }
942 cbLeft -= sizeof(uBuf);
943 DBGFR3AddrSub(&CurAddr, sizeof(uBuf));
944 cbBuf = sizeof(uBuf);
945 }
946}
947
948
949/**
950 * Worker for dbgDiggerLinuxFindEndNames that records the findings.
951 *
952 * @returns VINF_SUCCESS
953 * @param pThis The linux digger data to update.
954 * @param pAddrMarkers The address of the marker (kallsyms_markers).
955 * @param cbMarkerEntry The size of a marker entry (32-bit or 64-bit).
956 */
957static int dbgDiggerLinuxFoundMarkers(PDBGDIGGERLINUX pThis, PCDBGFADDRESS pAddrMarkers, uint32_t cbMarkerEntry)
958{
959 pThis->cbKernelNames = pAddrMarkers->FlatPtr - pThis->AddrKernelNames.FlatPtr;
960 pThis->AddrKernelNameMarkers = *pAddrMarkers;
961 pThis->cKernelNameMarkers = RT_ALIGN_32(pThis->cKernelSymbols, 256) / 256;
962 pThis->AddrKernelTokenTable = *pAddrMarkers;
963 DBGFR3AddrAdd(&pThis->AddrKernelTokenTable, pThis->cKernelNameMarkers * cbMarkerEntry);
964
965 Log(("dbgDiggerLinuxFoundMarkers: AddrKernelNames=%RGv cbKernelNames=%#x\n"
966 "dbgDiggerLinuxFoundMarkers: AddrKernelNameMarkers=%RGv cKernelNameMarkers=%#x\n"
967 "dbgDiggerLinuxFoundMarkers: AddrKernelTokenTable=%RGv\n",
968 pThis->AddrKernelNames.FlatPtr, pThis->cbKernelNames,
969 pThis->AddrKernelNameMarkers.FlatPtr, pThis->cKernelNameMarkers,
970 pThis->AddrKernelTokenTable.FlatPtr));
971 return VINF_SUCCESS;
972}
973
974
975/**
976 * Tries to find the end of kallsyms_names and thereby the start of
977 * kallsyms_markers and kallsyms_token_table.
978 *
979 * The kallsyms_names size is stored in pThis->cbKernelNames, the addresses of
980 * the two other symbols in pThis->AddrKernelNameMarkers and
981 * pThis->AddrKernelTokenTable. The number of marker entries is stored in
982 * pThis->cKernelNameMarkers.
983 *
984 * @returns VBox status code, success indicating that all three variables have
985 * been found and taken down.
986 * @param pUVM The user mode VM handle.
987 * @param pThis The Linux digger data.
988 * @param pHitAddr An address we think is inside kallsyms_names.
989 */
990static int dbgDiggerLinuxFindEndOfNamesAndMore(PUVM pUVM, PDBGDIGGERLINUX pThis, PCDBGFADDRESS pHitAddr)
991{
992 /*
993 * Search forward in chunks.
994 */
995 union
996 {
997 uint8_t ab[0x1000];
998 uint32_t au32[0x1000 / sizeof(uint32_t)];
999 uint64_t au64[0x1000 / sizeof(uint64_t)];
1000 } uBuf;
1001 bool fPendingZeroHit = false;
1002 uint32_t cbLeft = LNX_MAX_KALLSYMS_NAMES_SIZE + sizeof(uBuf);
1003 uint32_t offBuf = pHitAddr->FlatPtr & (sizeof(uBuf) - 1);
1004 DBGFADDRESS CurAddr = *pHitAddr;
1005 DBGFR3AddrSub(&CurAddr, offBuf);
1006 for (;;)
1007 {
1008 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
1009 if (RT_FAILURE(rc))
1010 return rc;
1011
1012 /*
1013 * The kallsyms_names table is followed by kallsyms_markers we assume,
1014 * using sizeof(unsigned long) alignment like the preceeding symbols.
1015 *
1016 * The kallsyms_markers table has entried sizeof(unsigned long) and
1017 * contains offsets into kallsyms_names. The kallsyms_markers used to
1018 * index kallsyms_names and reduce seek time when looking up the name
1019 * of an address/symbol. Each entry in kallsyms_markers covers 256
1020 * symbol names.
1021 *
1022 * Because of this, the first entry is always zero and all the entries
1023 * are ascending. It also follows that the size of the table can be
1024 * calculated from kallsyms_num_syms.
1025 *
1026 * Note! We could also have walked kallsyms_names by skipping
1027 * kallsyms_num_syms names, but this is faster and we will
1028 * validate the encoded names later.
1029 */
1030 if (pThis->f64Bit)
1031 {
1032 if ( RT_UNLIKELY(fPendingZeroHit)
1033 && uBuf.au64[0] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1034 && uBuf.au64[0] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1035 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrSub(&CurAddr, sizeof(uint64_t)), sizeof(uint64_t));
1036
1037 uint32_t const cEntries = sizeof(uBuf) / sizeof(uint64_t);
1038 for (uint32_t i = offBuf / sizeof(uint64_t); i < cEntries; i++)
1039 if (uBuf.au64[i] == 0)
1040 {
1041 if (RT_UNLIKELY(i + 1 >= cEntries))
1042 {
1043 fPendingZeroHit = true;
1044 break;
1045 }
1046 if ( uBuf.au64[i + 1] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1047 && uBuf.au64[i + 1] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1048 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrAdd(&CurAddr, i * sizeof(uint64_t)), sizeof(uint64_t));
1049 }
1050 }
1051 else
1052 {
1053 if ( RT_UNLIKELY(fPendingZeroHit)
1054 && uBuf.au32[0] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1055 && uBuf.au32[0] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1056 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrSub(&CurAddr, sizeof(uint32_t)), sizeof(uint32_t));
1057
1058 uint32_t const cEntries = sizeof(uBuf) / sizeof(uint32_t);
1059 for (uint32_t i = offBuf / sizeof(uint32_t); i < cEntries; i++)
1060 if (uBuf.au32[i] == 0)
1061 {
1062 if (RT_UNLIKELY(i + 1 >= cEntries))
1063 {
1064 fPendingZeroHit = true;
1065 break;
1066 }
1067 if ( uBuf.au32[i + 1] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1068 && uBuf.au32[i + 1] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1069 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrAdd(&CurAddr, i * sizeof(uint32_t)), sizeof(uint32_t));
1070 }
1071 }
1072
1073 /*
1074 * Advance
1075 */
1076 if (RT_UNLIKELY(cbLeft <= sizeof(uBuf)))
1077 {
1078 Log(("dbgDiggerLinuxFindEndOfNamesAndMore: failed (pHitAddr=%RGv)\n", pHitAddr->FlatPtr));
1079 return VERR_NOT_FOUND;
1080 }
1081 cbLeft -= sizeof(uBuf);
1082 DBGFR3AddrAdd(&CurAddr, sizeof(uBuf));
1083 offBuf = 0;
1084 }
1085}
1086
1087
1088/**
1089 * Locates the kallsyms_token_index table.
1090 *
1091 * Storing the address in pThis->AddrKernelTokenIndex and the size of the token
1092 * table in pThis->cbKernelTokenTable.
1093 *
1094 * @returns VBox status code.
1095 * @param pUVM The user mode VM handle.
1096 * @param pThis The Linux digger data.
1097 */
1098static int dbgDiggerLinuxFindTokenIndex(PUVM pUVM, PDBGDIGGERLINUX pThis)
1099{
1100 /*
1101 * The kallsyms_token_table is very much like a string table. Due to the
1102 * nature of the compression algorithm it is reasonably short (one example
1103 * here is 853 bytes), so we'll not be reading it in chunks but in full.
1104 * To be on the safe side, we read 8KB, ASSUMING we won't run into unmapped
1105 * memory or any other nasty stuff...
1106 */
1107 union
1108 {
1109 uint8_t ab[0x2000];
1110 uint16_t au16[0x2000 / sizeof(uint16_t)];
1111 } uBuf;
1112 DBGFADDRESS CurAddr = pThis->AddrKernelTokenTable;
1113 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
1114 if (RT_FAILURE(rc))
1115 return rc;
1116
1117 /*
1118 * We've got two choices here, either walk the string table or look for
1119 * the next structure, kallsyms_token_index.
1120 *
1121 * The token index is a table of 256 uint16_t entries (index by bytes
1122 * from kallsyms_names) that gives offsets in kallsyms_token_table. It
1123 * starts with a zero entry and the following entries are sorted in
1124 * ascending order. The range of the entries are reasonably small since
1125 * kallsyms_token_table is small.
1126 *
1127 * The alignment seems to be sizeof(unsigned long), just like
1128 * kallsyms_token_table.
1129 *
1130 * So, we start by looking for a zero 16-bit entry.
1131 */
1132 uint32_t cIncr = (pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t)) / sizeof(uint16_t);
1133
1134 for (uint32_t i = 0; i < sizeof(uBuf) / sizeof(uint16_t) - 16; i += cIncr)
1135 if ( uBuf.au16[i] == 0
1136 && uBuf.au16[i + 1] > 0
1137 && uBuf.au16[i + 1] <= LNX_MAX_KALLSYMS_TOKEN_LEN
1138 && (uint16_t)(uBuf.au16[i + 2] - uBuf.au16[i + 1] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1139 && (uint16_t)(uBuf.au16[i + 3] - uBuf.au16[i + 2] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1140 && (uint16_t)(uBuf.au16[i + 4] - uBuf.au16[i + 3] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1141 && (uint16_t)(uBuf.au16[i + 5] - uBuf.au16[i + 4] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1142 && (uint16_t)(uBuf.au16[i + 6] - uBuf.au16[i + 5] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1143 )
1144 {
1145 pThis->AddrKernelTokenIndex = CurAddr;
1146 DBGFR3AddrAdd(&pThis->AddrKernelTokenIndex, i * sizeof(uint16_t));
1147 pThis->cbKernelTokenTable = i * sizeof(uint16_t);
1148 return VINF_SUCCESS;
1149 }
1150
1151 Log(("dbgDiggerLinuxFindTokenIndex: Failed (%RGv..%RGv)\n", CurAddr.FlatPtr, CurAddr.FlatPtr + (RTGCUINTPTR)sizeof(uBuf)));
1152 return VERR_NOT_FOUND;
1153}
1154
1155
1156/**
1157 * Loads the kernel symbols from the kallsyms tables.
1158 *
1159 * @returns VBox status code.
1160 * @param pUVM The user mode VM handle.
1161 * @param pThis The Linux digger data.
1162 */
1163static int dbgDiggerLinuxLoadKernelSymbols(PUVM pUVM, PDBGDIGGERLINUX pThis)
1164{
1165 /*
1166 * Allocate memory for temporary table copies, reading the tables as we go.
1167 */
1168 uint32_t const cbGuestAddr = pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t);
1169 void *pvAddresses = RTMemAllocZ(pThis->cKernelSymbols * cbGuestAddr);
1170 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelAddresses, pvAddresses, pThis->cKernelSymbols * cbGuestAddr);
1171 if (RT_SUCCESS(rc))
1172 {
1173 uint8_t *pbNames = (uint8_t *)RTMemAllocZ(pThis->cbKernelNames);
1174 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelNames, pbNames, pThis->cbKernelNames);
1175 if (RT_SUCCESS(rc))
1176 {
1177 char *pszzTokens = (char *)RTMemAllocZ(pThis->cbKernelTokenTable);
1178 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelTokenTable, pszzTokens, pThis->cbKernelTokenTable);
1179 if (RT_SUCCESS(rc))
1180 {
1181 uint16_t *paoffTokens = (uint16_t *)RTMemAllocZ(256 * sizeof(uint16_t));
1182 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelTokenIndex, paoffTokens, 256 * sizeof(uint16_t));
1183 if (RT_SUCCESS(rc))
1184 {
1185 /*
1186 * Figure out the kernel start and end.
1187 */
1188 RTGCUINTPTR uKernelStart = pThis->AddrKernelAddresses.FlatPtr;
1189 RTGCUINTPTR uKernelEnd = pThis->AddrKernelTokenIndex.FlatPtr + 256 * sizeof(uint16_t);
1190 uint32_t i;
1191 if (cbGuestAddr == sizeof(uint64_t))
1192 {
1193 uint64_t *pauAddrs = (uint64_t *)pvAddresses;
1194 for (i = 0; i < pThis->cKernelSymbols; i++)
1195 if ( pauAddrs[i] < uKernelStart
1196 && LNX64_VALID_ADDRESS(pauAddrs[i])
1197 && uKernelStart - pauAddrs[i] < LNX_MAX_KERNEL_SIZE)
1198 uKernelStart = pauAddrs[i];
1199
1200 for (i = pThis->cKernelSymbols - 1; i > 0; i--)
1201 if ( pauAddrs[i] > uKernelEnd
1202 && LNX64_VALID_ADDRESS(pauAddrs[i])
1203 && pauAddrs[i] - uKernelEnd < LNX_MAX_KERNEL_SIZE)
1204 uKernelEnd = pauAddrs[i];
1205 }
1206 else
1207 {
1208 uint32_t *pauAddrs = (uint32_t *)pvAddresses;
1209 for (i = 0; i < pThis->cKernelSymbols; i++)
1210 if ( pauAddrs[i] < uKernelStart
1211 && LNX32_VALID_ADDRESS(pauAddrs[i])
1212 && uKernelStart - pauAddrs[i] < LNX_MAX_KERNEL_SIZE)
1213 uKernelStart = pauAddrs[i];
1214
1215 for (i = pThis->cKernelSymbols - 1; i > 0; i--)
1216 if ( pauAddrs[i] > uKernelEnd
1217 && LNX32_VALID_ADDRESS(pauAddrs[i])
1218 && pauAddrs[i] - uKernelEnd < LNX_MAX_KERNEL_SIZE)
1219 uKernelEnd = pauAddrs[i];
1220 }
1221
1222 RTGCUINTPTR cbKernel = uKernelEnd - uKernelStart;
1223 pThis->cbKernel = (uint32_t)cbKernel;
1224 DBGFR3AddrFromFlat(pUVM, &pThis->AddrKernelBase, uKernelStart);
1225 Log(("dbgDiggerLinuxLoadKernelSymbols: uKernelStart=%RGv cbKernel=%#x\n", uKernelStart, cbKernel));
1226
1227 /*
1228 * Create a module for the kernel.
1229 */
1230 RTDBGMOD hMod;
1231 rc = RTDbgModCreate(&hMod, "vmlinux", cbKernel, 0 /*fFlags*/);
1232 if (RT_SUCCESS(rc))
1233 {
1234 rc = RTDbgModSetTag(hMod, DIG_LNX_MOD_TAG); AssertRC(rc);
1235 rc = VINF_SUCCESS;
1236
1237 /*
1238 * Enumerate the symbols.
1239 */
1240 uint8_t const *pbCurAddr = (uint8_t const *)pvAddresses;
1241 uint32_t offName = 0;
1242 uint32_t cLeft = pThis->cKernelSymbols;
1243 while (cLeft-- > 0 && RT_SUCCESS(rc))
1244 {
1245 /* Decode the symbol name first. */
1246 if (RT_LIKELY(offName < pThis->cbKernelNames))
1247 {
1248 uint8_t cbName = pbNames[offName++];
1249 if (RT_LIKELY(offName + cbName <= pThis->cbKernelNames))
1250 {
1251 char szSymbol[4096];
1252 uint32_t offSymbol = 0;
1253 while (cbName-- > 0)
1254 {
1255 uint8_t bEnc = pbNames[offName++];
1256 uint16_t offToken = paoffTokens[bEnc];
1257 if (RT_LIKELY(offToken < pThis->cbKernelTokenTable))
1258 {
1259 const char *pszToken = &pszzTokens[offToken];
1260 char ch;
1261 while ((ch = *pszToken++) != '\0')
1262 if (offSymbol < sizeof(szSymbol) - 1)
1263 szSymbol[offSymbol++] = ch;
1264 }
1265 else
1266 {
1267 rc = VERR_INVALID_UTF8_ENCODING;
1268 break;
1269 }
1270 }
1271 szSymbol[offSymbol < sizeof(szSymbol) ? offSymbol : sizeof(szSymbol) - 1] = '\0';
1272
1273 /* The address. */
1274 RTGCUINTPTR uSymAddr = cbGuestAddr == sizeof(uint64_t)
1275 ? *(uint64_t *)pbCurAddr : *(uint32_t *)pbCurAddr;
1276 pbCurAddr += cbGuestAddr;
1277
1278 /* Add it without the type char. */
1279 if (uSymAddr - uKernelStart <= cbKernel)
1280 {
1281 rc = RTDbgModSymbolAdd(hMod, &szSymbol[1], RTDBGSEGIDX_RVA, uSymAddr - uKernelStart,
1282 0 /*cb*/, 0 /*fFlags*/, NULL);
1283 if (RT_FAILURE(rc))
1284 {
1285 if ( rc == VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE
1286 || rc == VERR_DBG_INVALID_RVA
1287 || rc == VERR_DBG_ADDRESS_CONFLICT
1288 || rc == VERR_DBG_DUPLICATE_SYMBOL)
1289 {
1290 Log2(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc (ignored)\n", szSymbol, rc));
1291 rc = VINF_SUCCESS;
1292 }
1293 else
1294 Log(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc\n", szSymbol, rc));
1295 }
1296 }
1297 }
1298 else
1299 {
1300 rc = VERR_END_OF_STRING;
1301 Log(("dbgDiggerLinuxLoadKernelSymbols: offName=%#x cLeft=%#x cbName=%#x cbKernelNames=%#x\n",
1302 offName, cLeft, cbName, pThis->cbKernelNames));
1303 }
1304 }
1305 else
1306 {
1307 rc = VERR_END_OF_STRING;
1308 Log(("dbgDiggerLinuxLoadKernelSymbols: offName=%#x cLeft=%#x cbKernelNames=%#x\n",
1309 offName, cLeft, pThis->cbKernelNames));
1310 }
1311 }
1312
1313 /*
1314 * Link the module into the address space.
1315 */
1316 if (RT_SUCCESS(rc))
1317 {
1318 RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
1319 if (hAs != NIL_RTDBGAS)
1320 rc = RTDbgAsModuleLink(hAs, hMod, uKernelStart, RTDBGASLINK_FLAGS_REPLACE);
1321 else
1322 rc = VERR_INTERNAL_ERROR;
1323 RTDbgAsRelease(hAs);
1324 }
1325 else
1326 Log(("dbgDiggerLinuxLoadKernelSymbols: Failed: %Rrc\n", rc));
1327 RTDbgModRelease(hMod);
1328 }
1329 else
1330 Log(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModCreate failed: %Rrc\n", rc));
1331 }
1332 else
1333 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading token index at %RGv failed: %Rrc\n",
1334 pThis->AddrKernelTokenIndex.FlatPtr, rc));
1335 RTMemFree(paoffTokens);
1336 }
1337 else
1338 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading token table at %RGv failed: %Rrc\n",
1339 pThis->AddrKernelTokenTable.FlatPtr, rc));
1340 RTMemFree(pszzTokens);
1341 }
1342 else
1343 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading encoded names at %RGv failed: %Rrc\n",
1344 pThis->AddrKernelNames.FlatPtr, rc));
1345 RTMemFree(pbNames);
1346 }
1347 else
1348 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading symbol addresses at %RGv failed: %Rrc\n",
1349 pThis->AddrKernelAddresses.FlatPtr, rc));
1350 RTMemFree(pvAddresses);
1351 return rc;
1352}
1353
1354
1355/**
1356 * Checks if there is a likely kallsyms_names fragment at pHitAddr.
1357 *
1358 * @returns true if it's a likely fragment, false if not.
1359 * @param pUVM The user mode VM handle.
1360 * @param pHitAddr The address where paNeedle was found.
1361 * @param pabNeedle The fragment we've been searching for.
1362 * @param cbNeedle The length of the fragment.
1363 */
1364static bool dbgDiggerLinuxIsLikelyNameFragment(PUVM pUVM, PCDBGFADDRESS pHitAddr, uint8_t const *pabNeedle, uint8_t cbNeedle)
1365{
1366 /*
1367 * Examples of lead and tail bytes of our choosen needle in a randomly
1368 * picked kernel:
1369 * k o b j
1370 * 22 6b 6f 62 6a aa
1371 * fc 6b 6f 62 6a aa
1372 * 82 6b 6f 62 6a 5f - ascii trail byte (_).
1373 * ee 6b 6f 62 6a aa
1374 * fc 6b 6f 62 6a 5f - ascii trail byte (_).
1375 * 0a 74 6b 6f 62 6a 5f ea - ascii lead (t) and trail (_) bytes.
1376 * 0b 54 6b 6f 62 6a aa - ascii lead byte (T).
1377 * ... omitting 29 samples similar to the last two ...
1378 * d8 6b 6f 62 6a aa
1379 * d8 6b 6f 62 6a aa
1380 * d8 6b 6f 62 6a aa
1381 * d8 6b 6f 62 6a aa
1382 * f9 5f 6b 6f 62 6a 5f 94 - ascii lead and trail bytes (_)
1383 * f9 5f 6b 6f 62 6a 0c - ascii lead byte (_).
1384 * fd 6b 6f 62 6a 0f
1385 * ... enough.
1386 */
1387 uint8_t abBuf[32];
1388 DBGFADDRESS ReadAddr = *pHitAddr;
1389 DBGFR3AddrSub(&ReadAddr, 2);
1390 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &ReadAddr, abBuf, 2 + cbNeedle + 2);
1391 if (RT_SUCCESS(rc))
1392 {
1393 if (memcmp(&abBuf[2], pabNeedle, cbNeedle) == 0) /* paranoia */
1394 {
1395 uint8_t const bLead = abBuf[1] == '_' || abBuf[1] == 'T' || abBuf[1] == 't' ? abBuf[0] : abBuf[1];
1396 uint8_t const offTail = 2 + cbNeedle;
1397 uint8_t const bTail = abBuf[offTail] == '_' ? abBuf[offTail] : abBuf[offTail + 1];
1398 if ( bLead >= 1 && (bLead < 0x20 || bLead >= 0x80)
1399 && bTail >= 1 && (bTail < 0x20 || bTail >= 0x80))
1400 return true;
1401 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: bLead=%#x bTail=%#x (offTail=%#x)\n",
1402 pHitAddr->FlatPtr, bLead, bTail, offTail));
1403 }
1404 else
1405 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: Needle changed!\n", pHitAddr->FlatPtr));
1406 }
1407 else
1408 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: %Rrc\n", pHitAddr->FlatPtr, rc));
1409
1410 return false;
1411}
1412
1413
1414/**
1415 * @copydoc DBGFOSREG::pfnInit
1416 */
1417static DECLCALLBACK(int) dbgDiggerLinuxInit(PUVM pUVM, void *pvData)
1418{
1419 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1420 Assert(!pThis->fValid);
1421
1422 /*
1423 * Assume 64-bit kernels all live way beyond 32-bit address space.
1424 */
1425 pThis->f64Bit = pThis->AddrLinuxBanner.FlatPtr > UINT32_MAX;
1426
1427 /*
1428 * Go looking for the kallsyms table. If it's there, it will be somewhere
1429 * after the linux_banner symbol, so use it for starting the search.
1430 */
1431 DBGFADDRESS CurAddr = pThis->AddrLinuxBanner;
1432 uint32_t cbLeft = LNX_MAX_KERNEL_SIZE;
1433 while (cbLeft > 4096)
1434 {
1435 static const uint8_t s_abNeedle[] = "kobj";
1436 DBGFADDRESS HitAddr;
1437 int rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &CurAddr, cbLeft, 1 /*uAlign*/,
1438 s_abNeedle, sizeof(s_abNeedle) - 1, &HitAddr);
1439 if (RT_FAILURE(rc))
1440 break;
1441 if (dbgDiggerLinuxIsLikelyNameFragment(pUVM, &HitAddr, s_abNeedle, sizeof(s_abNeedle) - 1))
1442 {
1443 /* There will be another hit near by. */
1444 DBGFR3AddrAdd(&HitAddr, 1);
1445 rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, LNX_MAX_KALLSYMS_NAMES_SIZE, 1 /*uAlign*/,
1446 s_abNeedle, sizeof(s_abNeedle) - 1, &HitAddr);
1447 if ( RT_SUCCESS(rc)
1448 && dbgDiggerLinuxIsLikelyNameFragment(pUVM, &HitAddr, s_abNeedle, sizeof(s_abNeedle) - 1))
1449 {
1450 /*
1451 * We've got a very likely candidate for a location inside kallsyms_names.
1452 * Try find the start of it, that is to say, try find kallsyms_num_syms.
1453 * kallsyms_num_syms is aligned on sizeof(unsigned long) boundrary
1454 */
1455 rc = dbgDiggerLinuxFindStartOfNamesAndSymbolCount(pUVM, pThis, &HitAddr);
1456 if (RT_SUCCESS(rc))
1457 rc = dbgDiggerLinuxFindEndOfNamesAndMore(pUVM, pThis, &HitAddr);
1458 if (RT_SUCCESS(rc))
1459 rc = dbgDiggerLinuxFindTokenIndex(pUVM, pThis);
1460 if (RT_SUCCESS(rc))
1461 rc = dbgDiggerLinuxLoadKernelSymbols(pUVM, pThis);
1462 if (RT_SUCCESS(rc))
1463 break;
1464 }
1465 }
1466
1467 /*
1468 * Advance.
1469 */
1470 RTGCUINTPTR cbDistance = HitAddr.FlatPtr - CurAddr.FlatPtr + sizeof(s_abNeedle) - 1;
1471 if (RT_UNLIKELY(cbDistance >= cbLeft))
1472 {
1473 Log(("dbgDiggerLinuxInit: Failed to find kallsyms\n"));
1474 break;
1475 }
1476 cbLeft -= cbDistance;
1477 DBGFR3AddrAdd(&CurAddr, cbDistance);
1478
1479 }
1480
1481 pThis->fValid = true;
1482 return VINF_SUCCESS;
1483}
1484
1485
1486/**
1487 * @copydoc DBGFOSREG::pfnProbe
1488 */
1489static DECLCALLBACK(bool) dbgDiggerLinuxProbe(PUVM pUVM, void *pvData)
1490{
1491 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1492
1493 /*
1494 * Look for "Linux version " at the start of the rodata segment.
1495 * Hope that this comes before any message buffer or other similar string.
1496 */
1497 for (unsigned i = 0; i < RT_ELEMENTS(g_au64LnxKernelAddresses); i++)
1498 {
1499 DBGFADDRESS KernelAddr;
1500 DBGFR3AddrFromFlat(pUVM, &KernelAddr, g_au64LnxKernelAddresses[i]);
1501 DBGFADDRESS HitAddr;
1502 int rc = DBGFR3MemScan(pUVM, 0, &KernelAddr, LNX_MAX_KERNEL_SIZE, 1,
1503 g_abLinuxVersion, sizeof(g_abLinuxVersion) - 1, &HitAddr);
1504 if (RT_SUCCESS(rc))
1505 {
1506 char szTmp[128];
1507 char const *pszX = &szTmp[sizeof(g_abLinuxVersion) - 1];
1508 rc = DBGFR3MemReadString(pUVM, 0, &HitAddr, szTmp, sizeof(szTmp));
1509 if ( RT_SUCCESS(rc)
1510 && ( ( pszX[0] == '2' /* 2.x.y with x in {0..6} */
1511 && pszX[1] == '.'
1512 && pszX[2] >= '0'
1513 && pszX[2] <= '6')
1514 || ( pszX[0] >= '3' /* 3.x, 4.x, ... 9.x */
1515 && pszX[0] <= '9'
1516 && pszX[1] == '.'
1517 && pszX[2] >= '0'
1518 && pszX[2] <= '9')
1519 )
1520 )
1521 {
1522 pThis->AddrKernelBase = KernelAddr;
1523 pThis->AddrLinuxBanner = HitAddr;
1524 return true;
1525 }
1526 }
1527 }
1528 return false;
1529}
1530
1531
1532/**
1533 * @copydoc DBGFOSREG::pfnDestruct
1534 */
1535static DECLCALLBACK(void) dbgDiggerLinuxDestruct(PUVM pUVM, void *pvData)
1536{
1537
1538}
1539
1540
1541/**
1542 * @copydoc DBGFOSREG::pfnConstruct
1543 */
1544static DECLCALLBACK(int) dbgDiggerLinuxConstruct(PUVM pUVM, void *pvData)
1545{
1546 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1547 pThis->IDmesg.u32Magic = DBGFOSIDMESG_MAGIC;
1548 pThis->IDmesg.pfnQueryKernelLog = dbgDiggerLinuxIDmsg_QueryKernelLog;
1549 pThis->IDmesg.u32EndMagic = DBGFOSIDMESG_MAGIC;
1550
1551 return VINF_SUCCESS;
1552}
1553
1554
1555const DBGFOSREG g_DBGDiggerLinux =
1556{
1557 /* .u32Magic = */ DBGFOSREG_MAGIC,
1558 /* .fFlags = */ 0,
1559 /* .cbData = */ sizeof(DBGDIGGERLINUX),
1560 /* .szName = */ "Linux",
1561 /* .pfnConstruct = */ dbgDiggerLinuxConstruct,
1562 /* .pfnDestruct = */ dbgDiggerLinuxDestruct,
1563 /* .pfnProbe = */ dbgDiggerLinuxProbe,
1564 /* .pfnInit = */ dbgDiggerLinuxInit,
1565 /* .pfnRefresh = */ dbgDiggerLinuxRefresh,
1566 /* .pfnTerm = */ dbgDiggerLinuxTerm,
1567 /* .pfnQueryVersion = */ dbgDiggerLinuxQueryVersion,
1568 /* .pfnQueryInterface = */ dbgDiggerLinuxQueryInterface,
1569 /* .u32EndMagic = */ DBGFOSREG_MAGIC
1570};
1571
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