VirtualBox

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

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

Debugger/Digger/Linux: Another alternate needle required for OpenSuse 10.2 x86

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.6 KB
Line 
1/* $Id: DBGPlugInLinux.cpp 61607 2016-06-09 09:51:33Z 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 size_t cchLength = RTStrNLen((const char *)pbLogBuf, cbLogBuf);
423 memcpy(&pszBuf[0], pbLogBuf, RT_MIN(cbBuf, cchLength));
424
425 /* Done with the buffer. */
426 RTMemFree(pbLogBuf);
427
428 /* Set return size value. */
429 if (pcbActual)
430 *pcbActual = RT_MIN(cbBuf, cchLength);
431
432 return cbBuf <= cchLength ? VERR_BUFFER_OVERFLOW : VINF_SUCCESS;
433}
434
435/**
436 * Worker to get at the kernel log for post 3.4 kernels where the log buffer contains records.
437 *
438 * @returns VBox status code.
439 * @param pThis The Linux digger data.
440 * @param pUVM The VM user mdoe handle.
441 * @param hMod The debug module handle.
442 * @param fFlags Flags reserved for future use, MBZ.
443 * @param cMessages The number of messages to retrieve, counting from the
444 * end of the log (i.e. like tail), use UINT32_MAX for all.
445 * @param pszBuf The output buffer.
446 * @param cbBuf The buffer size.
447 * @param pcbActual Where to store the number of bytes actually returned,
448 * including zero terminator. On VERR_BUFFER_OVERFLOW this
449 * holds the necessary buffer size. Optional.
450 */
451static int dbgDiggerLinuxLogBufferQueryRecords(PDBGDIGGERLINUX pThis, PUVM pUVM, RTDBGMOD hMod,
452 uint32_t fFlags, uint32_t cMessages,
453 char *pszBuf, size_t cbBuf, size_t *pcbActual)
454{
455 int rc = VINF_SUCCESS;
456 RTGCPTR GCPtrLogBuf;
457 uint32_t cbLogBuf;
458 uint32_t idxFirst;
459 uint32_t idxNext;
460
461 struct { void *pvVar; size_t cbHost, cbGuest; const char *pszSymbol; } aSymbols[] =
462 {
463 { &GCPtrLogBuf, sizeof(GCPtrLogBuf), pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t), "log_buf" },
464 { &cbLogBuf, sizeof(cbLogBuf), sizeof(cbLogBuf), "log_buf_len" },
465 { &idxFirst, sizeof(idxFirst), sizeof(idxFirst), "log_first_idx" },
466 { &idxNext, sizeof(idxNext), sizeof(idxNext), "log_next_idx" },
467 };
468 for (uint32_t i = 0; i < RT_ELEMENTS(aSymbols); i++)
469 {
470 RTDBGSYMBOL SymInfo;
471 rc = RTDbgModSymbolByName(hMod, aSymbols[i].pszSymbol, &SymInfo);
472 if (RT_SUCCESS(rc))
473 {
474 RT_BZERO(aSymbols[i].pvVar, aSymbols[i].cbHost);
475 Assert(aSymbols[i].cbHost >= aSymbols[i].cbGuest);
476 DBGFADDRESS Addr;
477 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/,
478 DBGFR3AddrFromFlat(pUVM, &Addr, (RTGCPTR)SymInfo.Value + pThis->AddrKernelBase.FlatPtr),
479 aSymbols[i].pvVar, aSymbols[i].cbGuest);
480 if (RT_SUCCESS(rc))
481 continue;
482 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Reading '%s' at %RGv: %Rrc\n", aSymbols[i].pszSymbol, Addr.FlatPtr, rc));
483 }
484 else
485 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error looking up '%s': %Rrc\n", aSymbols[i].pszSymbol, rc));
486 rc = VERR_NOT_FOUND;
487 break;
488 }
489
490 /*
491 * Some kernels don't expose the variables in kallsyms so we have to try disassemble
492 * some public helpers to get at the addresses.
493 *
494 * @todo: Maybe cache those values so we don't have to do the heavy work every time?
495 */
496 if (rc == VERR_NOT_FOUND)
497 {
498 idxFirst = 0;
499 idxNext = 0;
500 rc = dbgDiggerLinuxQueryLogBufferPtrs(pThis, pUVM, hMod, &GCPtrLogBuf, &cbLogBuf);
501 if (RT_FAILURE(rc))
502 return rc;
503 }
504
505 /*
506 * Check if the values make sense.
507 */
508 if (pThis->f64Bit ? !LNX64_VALID_ADDRESS(GCPtrLogBuf) : !LNX32_VALID_ADDRESS(GCPtrLogBuf))
509 {
510 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf' value %RGv is not valid.\n", GCPtrLogBuf));
511 return VERR_NOT_FOUND;
512 }
513 if ( cbLogBuf < 4096
514 || !RT_IS_POWER_OF_TWO(cbLogBuf)
515 || cbLogBuf > 16*_1M)
516 {
517 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf_len' value %#x is not valid.\n", cbLogBuf));
518 return VERR_NOT_FOUND;
519 }
520 uint32_t const cbLogAlign = 4;
521 if ( idxFirst > cbLogBuf - sizeof(LNXPRINTKHDR)
522 || (idxFirst & (cbLogAlign - 1)) != 0)
523 {
524 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_first_idx' value %#x is not valid.\n", idxFirst));
525 return VERR_NOT_FOUND;
526 }
527 if ( idxNext > cbLogBuf - sizeof(LNXPRINTKHDR)
528 || (idxNext & (cbLogAlign - 1)) != 0)
529 {
530 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_next_idx' value %#x is not valid.\n", idxNext));
531 return VERR_NOT_FOUND;
532 }
533
534 /*
535 * Read the whole log buffer.
536 */
537 uint8_t *pbLogBuf = (uint8_t *)RTMemAlloc(cbLogBuf);
538 if (!pbLogBuf)
539 {
540 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Failed to allocate %#x bytes for log buffer\n", cbLogBuf));
541 return VERR_NO_MEMORY;
542 }
543 DBGFADDRESS Addr;
544 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, GCPtrLogBuf), pbLogBuf, cbLogBuf);
545 if (RT_FAILURE(rc))
546 {
547 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error reading %#x bytes of log buffer at %RGv: %Rrc\n",
548 cbLogBuf, Addr.FlatPtr, rc));
549 RTMemFree(pbLogBuf);
550 return VERR_NOT_FOUND;
551 }
552
553 /*
554 * Count the messages in the buffer while doing some basic validation.
555 */
556 uint32_t const cbUsed = idxFirst == idxNext ? cbLogBuf /* could be empty... */
557 : idxFirst < idxNext ? idxNext - idxFirst : cbLogBuf - idxFirst + idxNext;
558 uint32_t cbLeft = cbUsed;
559 uint32_t offCur = idxFirst;
560 uint32_t cLogMsgs = 0;
561
562 while (cbLeft > 0)
563 {
564 PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
565 if (!pHdr->cbTotal)
566 {
567 /* Wrap around packet, most likely... */
568 if (cbLogBuf - offCur >= cbLeft)
569 break;
570 offCur = 0;
571 pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
572 }
573 if (RT_UNLIKELY( pHdr->cbTotal > cbLogBuf - sizeof(*pHdr) - offCur
574 || pHdr->cbTotal > cbLeft
575 || (pHdr->cbTotal & (cbLogAlign - 1)) != 0
576 || pHdr->cbTotal < (uint32_t)pHdr->cbText + (uint32_t)pHdr->cbDict + sizeof(*pHdr) ))
577 {
578 Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Invalid printk_log record at %#x: cbTotal=%#x cbText=%#x cbDict=%#x cbLogBuf=%#x cbLeft=%#x\n",
579 offCur, pHdr->cbTotal, pHdr->cbText, pHdr->cbDict, cbLogBuf, cbLeft));
580 rc = VERR_INVALID_STATE;
581 break;
582 }
583
584 if (pHdr->cbText > 0)
585 cLogMsgs++;
586
587 /* next */
588 offCur += pHdr->cbTotal;
589 cbLeft -= pHdr->cbTotal;
590 }
591 if (RT_FAILURE(rc))
592 {
593 RTMemFree(pbLogBuf);
594 return rc;
595 }
596
597 /*
598 * Copy the messages into the output buffer.
599 */
600 offCur = idxFirst;
601 cbLeft = cbUsed;
602
603 /* Skip messages that the caller doesn't want. */
604 if (cMessages < cLogMsgs)
605 {
606 uint32_t cToSkip = cLogMsgs - cMessages;
607 while (cToSkip > 0)
608 {
609 PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
610 if (!pHdr->cbTotal)
611 {
612 offCur = 0;
613 pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
614 }
615 if (pHdr->cbText > 0)
616 cToSkip--;
617
618 /* next */
619 offCur += pHdr->cbTotal;
620 cbLeft -= pHdr->cbTotal;
621 }
622 }
623
624 /* Now copy the messages. */
625 size_t offDst = 0;
626 while (cbLeft > 0)
627 {
628 PCLNXPRINTKHDR pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
629 if (!pHdr->cbTotal)
630 {
631 if (cbLogBuf - offCur >= cbLeft)
632 break;
633 offCur = 0;
634 pHdr = (PCLNXPRINTKHDR)&pbLogBuf[offCur];
635 }
636
637 if (pHdr->cbText > 0)
638 {
639 char *pchText = (char *)(pHdr + 1);
640 size_t cchText = RTStrNLen(pchText, pHdr->cbText);
641 if (offDst + cchText < cbBuf)
642 {
643 memcpy(&pszBuf[offDst], pHdr + 1, cchText);
644 pszBuf[offDst + cchText] = '\n';
645 }
646 else if (offDst < cbBuf)
647 memcpy(&pszBuf[offDst], pHdr + 1, cbBuf - offDst);
648 offDst += cchText + 1;
649 }
650
651 /* next */
652 offCur += pHdr->cbTotal;
653 cbLeft -= pHdr->cbTotal;
654 }
655
656 /* Done with the buffer. */
657 RTMemFree(pbLogBuf);
658
659 /* Make sure we've reserved a char for the terminator. */
660 if (!offDst)
661 offDst = 1;
662
663 /* Set return size value. */
664 if (pcbActual)
665 *pcbActual = offDst;
666
667 if (offDst <= cbBuf)
668 return VINF_SUCCESS;
669 else
670 return VERR_BUFFER_OVERFLOW;
671}
672
673/**
674 * @interface_method_impl{DBGFOSIDMESG,pfnQueryKernelLog}
675 */
676static DECLCALLBACK(int) dbgDiggerLinuxIDmsg_QueryKernelLog(PDBGFOSIDMESG pThis, PUVM pUVM, uint32_t fFlags, uint32_t cMessages,
677 char *pszBuf, size_t cbBuf, size_t *pcbActual)
678{
679 PDBGDIGGERLINUX pData = RT_FROM_MEMBER(pThis, DBGDIGGERLINUX, IDmesg);
680
681 if (cMessages < 1)
682 return VERR_INVALID_PARAMETER;
683
684 /*
685 * Resolve the symbols we need and read their values.
686 */
687 RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
688 RTDBGMOD hMod;
689 int rc = RTDbgAsModuleByName(hAs, "vmlinux", 0, &hMod);
690 if (RT_FAILURE(rc))
691 return VERR_NOT_FOUND;
692 RTDbgAsRelease(hAs);
693
694 size_t cbActual;
695 /*
696 * Check whether the kernel log buffer is a simple char buffer or the newer
697 * record based implementation.
698 * The record based implementation was presumably introduced with kernel 3.4,
699 * see: http://thread.gmane.org/gmane.linux.kernel/1284184
700 */
701 if (dbgDiggerLinuxLogBufferIsAsciiBuffer(pData, pUVM))
702 rc = dbgDiggerLinuxLogBufferQueryAscii(pData, pUVM, hMod, fFlags, cMessages, pszBuf, cbBuf, &cbActual);
703 else
704 rc = dbgDiggerLinuxLogBufferQueryRecords(pData, pUVM, hMod, fFlags, cMessages, pszBuf, cbBuf, &cbActual);
705
706 /* Release the module in any case. */
707 RTDbgModRelease(hMod);
708
709 if (RT_FAILURE(rc) && rc != VERR_BUFFER_OVERFLOW)
710 return rc;
711
712 if (pcbActual)
713 *pcbActual = cbActual;
714
715 /*
716 * All VBox strings are UTF-8 and bad things may in theory happen if we
717 * pass bad UTF-8 to code which assumes it's all valid. So, we enforce
718 * UTF-8 upon the guest kernel messages here even if they (probably) have
719 * no defined code set in reality.
720 */
721 if ( RT_SUCCESS(rc)
722 && cbActual <= cbBuf)
723 {
724 pszBuf[cbActual - 1] = '\0';
725 RTStrPurgeEncoding(pszBuf);
726 return VINF_SUCCESS;
727 }
728
729 if (cbBuf)
730 {
731 pszBuf[cbBuf - 1] = '\0';
732 RTStrPurgeEncoding(pszBuf);
733 }
734 return VERR_BUFFER_OVERFLOW;
735}
736
737
738/**
739 * @copydoc DBGFOSREG::pfnQueryInterface
740 */
741static DECLCALLBACK(void *) dbgDiggerLinuxQueryInterface(PUVM pUVM, void *pvData, DBGFOSINTERFACE enmIf)
742{
743 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
744 switch (enmIf)
745 {
746 case DBGFOSINTERFACE_DMESG:
747 return &pThis->IDmesg;
748
749 default:
750 return NULL;
751 }
752}
753
754
755/**
756 * @copydoc DBGFOSREG::pfnQueryVersion
757 */
758static DECLCALLBACK(int) dbgDiggerLinuxQueryVersion(PUVM pUVM, void *pvData, char *pszVersion, size_t cchVersion)
759{
760 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
761 Assert(pThis->fValid);
762
763 /*
764 * It's all in the linux banner.
765 */
766 int rc = DBGFR3MemReadString(pUVM, 0, &pThis->AddrLinuxBanner, pszVersion, cchVersion);
767 if (RT_SUCCESS(rc))
768 {
769 char *pszEnd = RTStrEnd(pszVersion, cchVersion);
770 AssertReturn(pszEnd, VERR_BUFFER_OVERFLOW);
771 while ( pszEnd > pszVersion
772 && RT_C_IS_SPACE(pszEnd[-1]))
773 pszEnd--;
774 *pszEnd = '\0';
775 }
776 else
777 RTStrPrintf(pszVersion, cchVersion, "DBGFR3MemRead -> %Rrc", rc);
778
779 return rc;
780}
781
782
783/**
784 * @copydoc DBGFOSREG::pfnTerm
785 */
786static DECLCALLBACK(void) dbgDiggerLinuxTerm(PUVM pUVM, void *pvData)
787{
788 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
789 Assert(pThis->fValid);
790
791 pThis->fValid = false;
792}
793
794
795/**
796 * @copydoc DBGFOSREG::pfnRefresh
797 */
798static DECLCALLBACK(int) dbgDiggerLinuxRefresh(PUVM pUVM, void *pvData)
799{
800 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
801 NOREF(pThis);
802 Assert(pThis->fValid);
803
804 /*
805 * For now we'll flush and reload everything.
806 */
807 dbgDiggerLinuxTerm(pUVM, pvData);
808 return dbgDiggerLinuxInit(pUVM, pvData);
809}
810
811
812/**
813 * Worker for dbgDiggerLinuxFindStartOfNamesAndSymbolCount that update the
814 * digger data.
815 *
816 * @returns VINF_SUCCESS.
817 * @param pThis The Linux digger data to update.
818 * @param pAddrKernelNames The kallsyms_names address.
819 * @param cKernelSymbols The number of kernel symbol.
820 * @param cbAddress The guest address size.
821 */
822static int dbgDiggerLinuxFoundStartOfNames(PDBGDIGGERLINUX pThis, PCDBGFADDRESS pAddrKernelNames,
823 uint32_t cKernelSymbols, uint32_t cbAddress)
824{
825 pThis->cKernelSymbols = cKernelSymbols;
826 pThis->AddrKernelNames = *pAddrKernelNames;
827 pThis->AddrKernelAddresses = *pAddrKernelNames;
828 DBGFR3AddrSub(&pThis->AddrKernelAddresses, (cKernelSymbols + 1) * cbAddress);
829
830 Log(("dbgDiggerLinuxFoundStartOfNames: AddrKernelAddresses=%RGv\n"
831 "dbgDiggerLinuxFoundStartOfNames: cKernelSymbols=%#x (at %RGv)\n"
832 "dbgDiggerLinuxFoundStartOfNames: AddrKernelName=%RGv\n",
833 pThis->AddrKernelAddresses.FlatPtr,
834 pThis->cKernelSymbols, pThis->AddrKernelNames.FlatPtr - cbAddress,
835 pThis->AddrKernelNames.FlatPtr));
836 return VINF_SUCCESS;
837}
838
839
840/**
841 * Tries to find the address of the kallsyms_names, kallsyms_num_syms and
842 * kallsyms_addresses symbols.
843 *
844 * The kallsyms_num_syms is read and stored in pThis->cKernelSymbols, while the
845 * addresses of the other two are stored as pThis->AddrKernelNames and
846 * pThis->AddrKernelAddresses.
847 *
848 * @returns VBox status code, success indicating that all three variables have
849 * been found and taken down.
850 * @param pUVM The user mode VM handle.
851 * @param pThis The Linux digger data.
852 * @param pHitAddr An address we think is inside kallsyms_names.
853 */
854static int dbgDiggerLinuxFindStartOfNamesAndSymbolCount(PUVM pUVM, PDBGDIGGERLINUX pThis, PCDBGFADDRESS pHitAddr)
855{
856 /*
857 * Search backwards in chunks.
858 */
859 union
860 {
861 uint8_t ab[0x1000];
862 uint32_t au32[0x1000 / sizeof(uint32_t)];
863 uint64_t au64[0x1000 / sizeof(uint64_t)];
864 } uBuf;
865 uint32_t cbLeft = LNX_MAX_KALLSYMS_NAMES_SIZE;
866 uint32_t cbBuf = pHitAddr->FlatPtr & (sizeof(uBuf) - 1);
867 DBGFADDRESS CurAddr = *pHitAddr;
868 DBGFR3AddrSub(&CurAddr, cbBuf);
869 cbBuf += sizeof(uint64_t) - 1; /* In case our kobj hit is in the first 4/8 bytes. */
870 for (;;)
871 {
872 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
873 if (RT_FAILURE(rc))
874 return rc;
875
876 /*
877 * We assume that the three symbols are aligned on guest pointer boundrary.
878 *
879 * The boundrary between the two tables should be noticable as the number
880 * is unlikely to be more than 16 millions, there will be at least one zero
881 * byte where it is, 64-bit will have 5 zero bytes. Zero bytes aren't all
882 * that common in the kallsyms_names table.
883 *
884 * Also the kallsyms_names table starts with a length byte, which means
885 * we're likely to see a byte in the range 1..31.
886 *
887 * The kallsyms_addresses are mostly sorted (except for the start where the
888 * absolute symbols are), so we'll spot a bunch of kernel addresses
889 * immediately preceeding the kallsyms_num_syms field.
890 *
891 * Lazy bird: If kallsyms_num_syms is on a buffer boundrary, we skip
892 * the check for kernel addresses preceeding it.
893 */
894 if (pThis->f64Bit)
895 {
896 uint32_t i = cbBuf / sizeof(uint64_t);
897 while (i-- > 0)
898 if ( uBuf.au64[i] <= LNX_MAX_KALLSYMS_SYMBOLS
899 && uBuf.au64[i] >= LNX_MIN_KALLSYMS_SYMBOLS)
900 {
901 uint8_t *pb = (uint8_t *)&uBuf.au64[i + 1];
902 if ( pb[0] <= LNX_MAX_KALLSYMS_ENC_LENGTH
903 && pb[0] >= LNX_MIN_KALLSYMS_ENC_LENGTH)
904 {
905 if ( (i <= 0 || LNX64_VALID_ADDRESS(uBuf.au64[i - 1]))
906 && (i <= 1 || LNX64_VALID_ADDRESS(uBuf.au64[i - 2]))
907 && (i <= 2 || LNX64_VALID_ADDRESS(uBuf.au64[i - 3])))
908 return dbgDiggerLinuxFoundStartOfNames(pThis,
909 DBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint64_t)),
910 (uint32_t)uBuf.au64[i], sizeof(uint64_t));
911 }
912 }
913 }
914 else
915 {
916 uint32_t i = cbBuf / sizeof(uint32_t);
917 while (i-- > 0)
918 if ( uBuf.au32[i] <= LNX_MAX_KALLSYMS_SYMBOLS
919 && uBuf.au32[i] >= LNX_MIN_KALLSYMS_SYMBOLS)
920 {
921 uint8_t *pb = (uint8_t *)&uBuf.au32[i + 1];
922 if ( pb[0] <= LNX_MAX_KALLSYMS_ENC_LENGTH
923 && pb[0] >= LNX_MIN_KALLSYMS_ENC_LENGTH)
924 {
925 if ( (i <= 0 || LNX32_VALID_ADDRESS(uBuf.au32[i - 1]))
926 && (i <= 1 || LNX32_VALID_ADDRESS(uBuf.au32[i - 2]))
927 && (i <= 2 || LNX32_VALID_ADDRESS(uBuf.au32[i - 3])))
928 return dbgDiggerLinuxFoundStartOfNames(pThis,
929 DBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint32_t)),
930 uBuf.au32[i], sizeof(uint32_t));
931 }
932 }
933 }
934
935 /*
936 * Advance
937 */
938 if (RT_UNLIKELY(cbLeft <= sizeof(uBuf)))
939 {
940 Log(("dbgDiggerLinuxFindStartOfNamesAndSymbolCount: failed (pHitAddr=%RGv)\n", pHitAddr->FlatPtr));
941 return VERR_NOT_FOUND;
942 }
943 cbLeft -= sizeof(uBuf);
944 DBGFR3AddrSub(&CurAddr, sizeof(uBuf));
945 cbBuf = sizeof(uBuf);
946 }
947}
948
949
950/**
951 * Worker for dbgDiggerLinuxFindEndNames that records the findings.
952 *
953 * @returns VINF_SUCCESS
954 * @param pThis The linux digger data to update.
955 * @param pAddrMarkers The address of the marker (kallsyms_markers).
956 * @param cbMarkerEntry The size of a marker entry (32-bit or 64-bit).
957 */
958static int dbgDiggerLinuxFoundMarkers(PDBGDIGGERLINUX pThis, PCDBGFADDRESS pAddrMarkers, uint32_t cbMarkerEntry)
959{
960 pThis->cbKernelNames = pAddrMarkers->FlatPtr - pThis->AddrKernelNames.FlatPtr;
961 pThis->AddrKernelNameMarkers = *pAddrMarkers;
962 pThis->cKernelNameMarkers = RT_ALIGN_32(pThis->cKernelSymbols, 256) / 256;
963 pThis->AddrKernelTokenTable = *pAddrMarkers;
964 DBGFR3AddrAdd(&pThis->AddrKernelTokenTable, pThis->cKernelNameMarkers * cbMarkerEntry);
965
966 Log(("dbgDiggerLinuxFoundMarkers: AddrKernelNames=%RGv cbKernelNames=%#x\n"
967 "dbgDiggerLinuxFoundMarkers: AddrKernelNameMarkers=%RGv cKernelNameMarkers=%#x\n"
968 "dbgDiggerLinuxFoundMarkers: AddrKernelTokenTable=%RGv\n",
969 pThis->AddrKernelNames.FlatPtr, pThis->cbKernelNames,
970 pThis->AddrKernelNameMarkers.FlatPtr, pThis->cKernelNameMarkers,
971 pThis->AddrKernelTokenTable.FlatPtr));
972 return VINF_SUCCESS;
973}
974
975
976/**
977 * Tries to find the end of kallsyms_names and thereby the start of
978 * kallsyms_markers and kallsyms_token_table.
979 *
980 * The kallsyms_names size is stored in pThis->cbKernelNames, the addresses of
981 * the two other symbols in pThis->AddrKernelNameMarkers and
982 * pThis->AddrKernelTokenTable. The number of marker entries is stored in
983 * pThis->cKernelNameMarkers.
984 *
985 * @returns VBox status code, success indicating that all three variables have
986 * been found and taken down.
987 * @param pUVM The user mode VM handle.
988 * @param pThis The Linux digger data.
989 * @param pHitAddr An address we think is inside kallsyms_names.
990 */
991static int dbgDiggerLinuxFindEndOfNamesAndMore(PUVM pUVM, PDBGDIGGERLINUX pThis, PCDBGFADDRESS pHitAddr)
992{
993 /*
994 * Search forward in chunks.
995 */
996 union
997 {
998 uint8_t ab[0x1000];
999 uint32_t au32[0x1000 / sizeof(uint32_t)];
1000 uint64_t au64[0x1000 / sizeof(uint64_t)];
1001 } uBuf;
1002 bool fPendingZeroHit = false;
1003 uint32_t cbLeft = LNX_MAX_KALLSYMS_NAMES_SIZE + sizeof(uBuf);
1004 uint32_t offBuf = pHitAddr->FlatPtr & (sizeof(uBuf) - 1);
1005 DBGFADDRESS CurAddr = *pHitAddr;
1006 DBGFR3AddrSub(&CurAddr, offBuf);
1007 for (;;)
1008 {
1009 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
1010 if (RT_FAILURE(rc))
1011 return rc;
1012
1013 /*
1014 * The kallsyms_names table is followed by kallsyms_markers we assume,
1015 * using sizeof(unsigned long) alignment like the preceeding symbols.
1016 *
1017 * The kallsyms_markers table has entried sizeof(unsigned long) and
1018 * contains offsets into kallsyms_names. The kallsyms_markers used to
1019 * index kallsyms_names and reduce seek time when looking up the name
1020 * of an address/symbol. Each entry in kallsyms_markers covers 256
1021 * symbol names.
1022 *
1023 * Because of this, the first entry is always zero and all the entries
1024 * are ascending. It also follows that the size of the table can be
1025 * calculated from kallsyms_num_syms.
1026 *
1027 * Note! We could also have walked kallsyms_names by skipping
1028 * kallsyms_num_syms names, but this is faster and we will
1029 * validate the encoded names later.
1030 */
1031 if (pThis->f64Bit)
1032 {
1033 if ( RT_UNLIKELY(fPendingZeroHit)
1034 && uBuf.au64[0] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1035 && uBuf.au64[0] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1036 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrSub(&CurAddr, sizeof(uint64_t)), sizeof(uint64_t));
1037
1038 uint32_t const cEntries = sizeof(uBuf) / sizeof(uint64_t);
1039 for (uint32_t i = offBuf / sizeof(uint64_t); i < cEntries; i++)
1040 if (uBuf.au64[i] == 0)
1041 {
1042 if (RT_UNLIKELY(i + 1 >= cEntries))
1043 {
1044 fPendingZeroHit = true;
1045 break;
1046 }
1047 if ( uBuf.au64[i + 1] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1048 && uBuf.au64[i + 1] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1049 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrAdd(&CurAddr, i * sizeof(uint64_t)), sizeof(uint64_t));
1050 }
1051 }
1052 else
1053 {
1054 if ( RT_UNLIKELY(fPendingZeroHit)
1055 && uBuf.au32[0] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1056 && uBuf.au32[0] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1057 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrSub(&CurAddr, sizeof(uint32_t)), sizeof(uint32_t));
1058
1059 uint32_t const cEntries = sizeof(uBuf) / sizeof(uint32_t);
1060 for (uint32_t i = offBuf / sizeof(uint32_t); i < cEntries; i++)
1061 if (uBuf.au32[i] == 0)
1062 {
1063 if (RT_UNLIKELY(i + 1 >= cEntries))
1064 {
1065 fPendingZeroHit = true;
1066 break;
1067 }
1068 if ( uBuf.au32[i + 1] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
1069 && uBuf.au32[i + 1] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
1070 return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrAdd(&CurAddr, i * sizeof(uint32_t)), sizeof(uint32_t));
1071 }
1072 }
1073
1074 /*
1075 * Advance
1076 */
1077 if (RT_UNLIKELY(cbLeft <= sizeof(uBuf)))
1078 {
1079 Log(("dbgDiggerLinuxFindEndOfNamesAndMore: failed (pHitAddr=%RGv)\n", pHitAddr->FlatPtr));
1080 return VERR_NOT_FOUND;
1081 }
1082 cbLeft -= sizeof(uBuf);
1083 DBGFR3AddrAdd(&CurAddr, sizeof(uBuf));
1084 offBuf = 0;
1085 }
1086}
1087
1088
1089/**
1090 * Locates the kallsyms_token_index table.
1091 *
1092 * Storing the address in pThis->AddrKernelTokenIndex and the size of the token
1093 * table in pThis->cbKernelTokenTable.
1094 *
1095 * @returns VBox status code.
1096 * @param pUVM The user mode VM handle.
1097 * @param pThis The Linux digger data.
1098 */
1099static int dbgDiggerLinuxFindTokenIndex(PUVM pUVM, PDBGDIGGERLINUX pThis)
1100{
1101 /*
1102 * The kallsyms_token_table is very much like a string table. Due to the
1103 * nature of the compression algorithm it is reasonably short (one example
1104 * here is 853 bytes), so we'll not be reading it in chunks but in full.
1105 * To be on the safe side, we read 8KB, ASSUMING we won't run into unmapped
1106 * memory or any other nasty stuff...
1107 */
1108 union
1109 {
1110 uint8_t ab[0x2000];
1111 uint16_t au16[0x2000 / sizeof(uint16_t)];
1112 } uBuf;
1113 DBGFADDRESS CurAddr = pThis->AddrKernelTokenTable;
1114 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
1115 if (RT_FAILURE(rc))
1116 return rc;
1117
1118 /*
1119 * We've got two choices here, either walk the string table or look for
1120 * the next structure, kallsyms_token_index.
1121 *
1122 * The token index is a table of 256 uint16_t entries (index by bytes
1123 * from kallsyms_names) that gives offsets in kallsyms_token_table. It
1124 * starts with a zero entry and the following entries are sorted in
1125 * ascending order. The range of the entries are reasonably small since
1126 * kallsyms_token_table is small.
1127 *
1128 * The alignment seems to be sizeof(unsigned long), just like
1129 * kallsyms_token_table.
1130 *
1131 * So, we start by looking for a zero 16-bit entry.
1132 */
1133 uint32_t cIncr = (pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t)) / sizeof(uint16_t);
1134
1135 for (uint32_t i = 0; i < sizeof(uBuf) / sizeof(uint16_t) - 16; i += cIncr)
1136 if ( uBuf.au16[i] == 0
1137 && uBuf.au16[i + 1] > 0
1138 && uBuf.au16[i + 1] <= LNX_MAX_KALLSYMS_TOKEN_LEN
1139 && (uint16_t)(uBuf.au16[i + 2] - uBuf.au16[i + 1] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1140 && (uint16_t)(uBuf.au16[i + 3] - uBuf.au16[i + 2] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1141 && (uint16_t)(uBuf.au16[i + 4] - uBuf.au16[i + 3] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1142 && (uint16_t)(uBuf.au16[i + 5] - uBuf.au16[i + 4] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1143 && (uint16_t)(uBuf.au16[i + 6] - uBuf.au16[i + 5] - 1U) <= (uint16_t)LNX_MAX_KALLSYMS_TOKEN_LEN
1144 )
1145 {
1146 pThis->AddrKernelTokenIndex = CurAddr;
1147 DBGFR3AddrAdd(&pThis->AddrKernelTokenIndex, i * sizeof(uint16_t));
1148 pThis->cbKernelTokenTable = i * sizeof(uint16_t);
1149 return VINF_SUCCESS;
1150 }
1151
1152 Log(("dbgDiggerLinuxFindTokenIndex: Failed (%RGv..%RGv)\n", CurAddr.FlatPtr, CurAddr.FlatPtr + (RTGCUINTPTR)sizeof(uBuf)));
1153 return VERR_NOT_FOUND;
1154}
1155
1156
1157/**
1158 * Loads the kernel symbols from the kallsyms tables.
1159 *
1160 * @returns VBox status code.
1161 * @param pUVM The user mode VM handle.
1162 * @param pThis The Linux digger data.
1163 */
1164static int dbgDiggerLinuxLoadKernelSymbols(PUVM pUVM, PDBGDIGGERLINUX pThis)
1165{
1166 /*
1167 * Allocate memory for temporary table copies, reading the tables as we go.
1168 */
1169 uint32_t const cbGuestAddr = pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t);
1170 void *pvAddresses = RTMemAllocZ(pThis->cKernelSymbols * cbGuestAddr);
1171 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelAddresses, pvAddresses, pThis->cKernelSymbols * cbGuestAddr);
1172 if (RT_SUCCESS(rc))
1173 {
1174 uint8_t *pbNames = (uint8_t *)RTMemAllocZ(pThis->cbKernelNames);
1175 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelNames, pbNames, pThis->cbKernelNames);
1176 if (RT_SUCCESS(rc))
1177 {
1178 char *pszzTokens = (char *)RTMemAllocZ(pThis->cbKernelTokenTable);
1179 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelTokenTable, pszzTokens, pThis->cbKernelTokenTable);
1180 if (RT_SUCCESS(rc))
1181 {
1182 uint16_t *paoffTokens = (uint16_t *)RTMemAllocZ(256 * sizeof(uint16_t));
1183 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->AddrKernelTokenIndex, paoffTokens, 256 * sizeof(uint16_t));
1184 if (RT_SUCCESS(rc))
1185 {
1186 /*
1187 * Figure out the kernel start and end.
1188 */
1189 RTGCUINTPTR uKernelStart = pThis->AddrKernelAddresses.FlatPtr;
1190 RTGCUINTPTR uKernelEnd = pThis->AddrKernelTokenIndex.FlatPtr + 256 * sizeof(uint16_t);
1191 uint32_t i;
1192 if (cbGuestAddr == sizeof(uint64_t))
1193 {
1194 uint64_t *pauAddrs = (uint64_t *)pvAddresses;
1195 for (i = 0; i < pThis->cKernelSymbols; i++)
1196 if ( pauAddrs[i] < uKernelStart
1197 && LNX64_VALID_ADDRESS(pauAddrs[i])
1198 && uKernelStart - pauAddrs[i] < LNX_MAX_KERNEL_SIZE)
1199 uKernelStart = pauAddrs[i];
1200
1201 for (i = pThis->cKernelSymbols - 1; i > 0; i--)
1202 if ( pauAddrs[i] > uKernelEnd
1203 && LNX64_VALID_ADDRESS(pauAddrs[i])
1204 && pauAddrs[i] - uKernelEnd < LNX_MAX_KERNEL_SIZE)
1205 uKernelEnd = pauAddrs[i];
1206 }
1207 else
1208 {
1209 uint32_t *pauAddrs = (uint32_t *)pvAddresses;
1210 for (i = 0; i < pThis->cKernelSymbols; i++)
1211 if ( pauAddrs[i] < uKernelStart
1212 && LNX32_VALID_ADDRESS(pauAddrs[i])
1213 && uKernelStart - pauAddrs[i] < LNX_MAX_KERNEL_SIZE)
1214 uKernelStart = pauAddrs[i];
1215
1216 for (i = pThis->cKernelSymbols - 1; i > 0; i--)
1217 if ( pauAddrs[i] > uKernelEnd
1218 && LNX32_VALID_ADDRESS(pauAddrs[i])
1219 && pauAddrs[i] - uKernelEnd < LNX_MAX_KERNEL_SIZE)
1220 uKernelEnd = pauAddrs[i];
1221 }
1222
1223 RTGCUINTPTR cbKernel = uKernelEnd - uKernelStart;
1224 pThis->cbKernel = (uint32_t)cbKernel;
1225 DBGFR3AddrFromFlat(pUVM, &pThis->AddrKernelBase, uKernelStart);
1226 Log(("dbgDiggerLinuxLoadKernelSymbols: uKernelStart=%RGv cbKernel=%#x\n", uKernelStart, cbKernel));
1227
1228 /*
1229 * Create a module for the kernel.
1230 */
1231 RTDBGMOD hMod;
1232 rc = RTDbgModCreate(&hMod, "vmlinux", cbKernel, 0 /*fFlags*/);
1233 if (RT_SUCCESS(rc))
1234 {
1235 rc = RTDbgModSetTag(hMod, DIG_LNX_MOD_TAG); AssertRC(rc);
1236 rc = VINF_SUCCESS;
1237
1238 /*
1239 * Enumerate the symbols.
1240 */
1241 uint8_t const *pbCurAddr = (uint8_t const *)pvAddresses;
1242 uint32_t offName = 0;
1243 uint32_t cLeft = pThis->cKernelSymbols;
1244 while (cLeft-- > 0 && RT_SUCCESS(rc))
1245 {
1246 /* Decode the symbol name first. */
1247 if (RT_LIKELY(offName < pThis->cbKernelNames))
1248 {
1249 uint8_t cbName = pbNames[offName++];
1250 if (RT_LIKELY(offName + cbName <= pThis->cbKernelNames))
1251 {
1252 char szSymbol[4096];
1253 uint32_t offSymbol = 0;
1254 while (cbName-- > 0)
1255 {
1256 uint8_t bEnc = pbNames[offName++];
1257 uint16_t offToken = paoffTokens[bEnc];
1258 if (RT_LIKELY(offToken < pThis->cbKernelTokenTable))
1259 {
1260 const char *pszToken = &pszzTokens[offToken];
1261 char ch;
1262 while ((ch = *pszToken++) != '\0')
1263 if (offSymbol < sizeof(szSymbol) - 1)
1264 szSymbol[offSymbol++] = ch;
1265 }
1266 else
1267 {
1268 rc = VERR_INVALID_UTF8_ENCODING;
1269 break;
1270 }
1271 }
1272 szSymbol[offSymbol < sizeof(szSymbol) ? offSymbol : sizeof(szSymbol) - 1] = '\0';
1273
1274 /* The address. */
1275 RTGCUINTPTR uSymAddr = cbGuestAddr == sizeof(uint64_t)
1276 ? *(uint64_t *)pbCurAddr : *(uint32_t *)pbCurAddr;
1277 pbCurAddr += cbGuestAddr;
1278
1279 /* Add it without the type char. */
1280 if (uSymAddr - uKernelStart <= cbKernel)
1281 {
1282 rc = RTDbgModSymbolAdd(hMod, &szSymbol[1], RTDBGSEGIDX_RVA, uSymAddr - uKernelStart,
1283 0 /*cb*/, 0 /*fFlags*/, NULL);
1284 if (RT_FAILURE(rc))
1285 {
1286 if ( rc == VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE
1287 || rc == VERR_DBG_INVALID_RVA
1288 || rc == VERR_DBG_ADDRESS_CONFLICT
1289 || rc == VERR_DBG_DUPLICATE_SYMBOL)
1290 {
1291 Log2(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc (ignored)\n", szSymbol, rc));
1292 rc = VINF_SUCCESS;
1293 }
1294 else
1295 Log(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModSymbolAdd(,%s,) failed %Rrc\n", szSymbol, rc));
1296 }
1297 }
1298 }
1299 else
1300 {
1301 rc = VERR_END_OF_STRING;
1302 Log(("dbgDiggerLinuxLoadKernelSymbols: offName=%#x cLeft=%#x cbName=%#x cbKernelNames=%#x\n",
1303 offName, cLeft, cbName, pThis->cbKernelNames));
1304 }
1305 }
1306 else
1307 {
1308 rc = VERR_END_OF_STRING;
1309 Log(("dbgDiggerLinuxLoadKernelSymbols: offName=%#x cLeft=%#x cbKernelNames=%#x\n",
1310 offName, cLeft, pThis->cbKernelNames));
1311 }
1312 }
1313
1314 /*
1315 * Link the module into the address space.
1316 */
1317 if (RT_SUCCESS(rc))
1318 {
1319 RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
1320 if (hAs != NIL_RTDBGAS)
1321 rc = RTDbgAsModuleLink(hAs, hMod, uKernelStart, RTDBGASLINK_FLAGS_REPLACE);
1322 else
1323 rc = VERR_INTERNAL_ERROR;
1324 RTDbgAsRelease(hAs);
1325 }
1326 else
1327 Log(("dbgDiggerLinuxLoadKernelSymbols: Failed: %Rrc\n", rc));
1328 RTDbgModRelease(hMod);
1329 }
1330 else
1331 Log(("dbgDiggerLinuxLoadKernelSymbols: RTDbgModCreate failed: %Rrc\n", rc));
1332 }
1333 else
1334 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading token index at %RGv failed: %Rrc\n",
1335 pThis->AddrKernelTokenIndex.FlatPtr, rc));
1336 RTMemFree(paoffTokens);
1337 }
1338 else
1339 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading token table at %RGv failed: %Rrc\n",
1340 pThis->AddrKernelTokenTable.FlatPtr, rc));
1341 RTMemFree(pszzTokens);
1342 }
1343 else
1344 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading encoded names at %RGv failed: %Rrc\n",
1345 pThis->AddrKernelNames.FlatPtr, rc));
1346 RTMemFree(pbNames);
1347 }
1348 else
1349 Log(("dbgDiggerLinuxLoadKernelSymbols: Reading symbol addresses at %RGv failed: %Rrc\n",
1350 pThis->AddrKernelAddresses.FlatPtr, rc));
1351 RTMemFree(pvAddresses);
1352 return rc;
1353}
1354
1355
1356/**
1357 * Checks if there is a likely kallsyms_names fragment at pHitAddr.
1358 *
1359 * @returns true if it's a likely fragment, false if not.
1360 * @param pUVM The user mode VM handle.
1361 * @param pHitAddr The address where paNeedle was found.
1362 * @param pabNeedle The fragment we've been searching for.
1363 * @param cbNeedle The length of the fragment.
1364 */
1365static bool dbgDiggerLinuxIsLikelyNameFragment(PUVM pUVM, PCDBGFADDRESS pHitAddr, uint8_t const *pabNeedle, uint8_t cbNeedle)
1366{
1367 /*
1368 * Examples of lead and tail bytes of our choosen needle in a randomly
1369 * picked kernel:
1370 * k o b j
1371 * 22 6b 6f 62 6a aa
1372 * fc 6b 6f 62 6a aa
1373 * 82 6b 6f 62 6a 5f - ascii trail byte (_).
1374 * ee 6b 6f 62 6a aa
1375 * fc 6b 6f 62 6a 5f - ascii trail byte (_).
1376 * 0a 74 6b 6f 62 6a 5f ea - ascii lead (t) and trail (_) bytes.
1377 * 0b 54 6b 6f 62 6a aa - ascii lead byte (T).
1378 * ... omitting 29 samples similar to the last two ...
1379 * d8 6b 6f 62 6a aa
1380 * d8 6b 6f 62 6a aa
1381 * d8 6b 6f 62 6a aa
1382 * d8 6b 6f 62 6a aa
1383 * f9 5f 6b 6f 62 6a 5f 94 - ascii lead and trail bytes (_)
1384 * f9 5f 6b 6f 62 6a 0c - ascii lead byte (_).
1385 * fd 6b 6f 62 6a 0f
1386 * ... enough.
1387 */
1388 uint8_t abBuf[32];
1389 DBGFADDRESS ReadAddr = *pHitAddr;
1390 DBGFR3AddrSub(&ReadAddr, 2);
1391 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &ReadAddr, abBuf, 2 + cbNeedle + 2);
1392 if (RT_SUCCESS(rc))
1393 {
1394 if (memcmp(&abBuf[2], pabNeedle, cbNeedle) == 0) /* paranoia */
1395 {
1396 uint8_t const bLead = abBuf[1] == '_' || abBuf[1] == 'T' || abBuf[1] == 't' ? abBuf[0] : abBuf[1];
1397 uint8_t const offTail = 2 + cbNeedle;
1398 uint8_t const bTail = abBuf[offTail] == '_' ? abBuf[offTail] : abBuf[offTail + 1];
1399 if ( bLead >= 1 && (bLead < 0x20 || bLead >= 0x80)
1400 && bTail >= 1 && (bTail < 0x20 || bTail >= 0x80))
1401 return true;
1402 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: bLead=%#x bTail=%#x (offTail=%#x)\n",
1403 pHitAddr->FlatPtr, bLead, bTail, offTail));
1404 }
1405 else
1406 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: Needle changed!\n", pHitAddr->FlatPtr));
1407 }
1408 else
1409 Log(("dbgDiggerLinuxIsLikelyNameFragment: failed at %RGv: %Rrc\n", pHitAddr->FlatPtr, rc));
1410
1411 return false;
1412}
1413
1414/**
1415 * Tries to find and load the kernel symbol table with the given needle.
1416 *
1417 * @returns VBox status code.
1418 * @param pThis The Linux digger data.
1419 * @param pUVM The user mode VM handle.
1420 * @param pabNeedle The needle to use for searching.
1421 * @param cbNeedle Size of the needle in bytes.
1422 */
1423static int dbgDiggerLinuxFindSymbolTableFromNeedle(PDBGDIGGERLINUX pThis, PUVM pUVM, uint8_t const *pabNeedle, size_t cbNeedle)
1424{
1425 int rc = VINF_SUCCESS;
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 DBGFADDRESS HitAddr;
1436 rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &CurAddr, cbLeft, 1 /*uAlign*/,
1437 pabNeedle, cbNeedle, &HitAddr);
1438 if (RT_FAILURE(rc))
1439 break;
1440 if (dbgDiggerLinuxIsLikelyNameFragment(pUVM, &HitAddr, pabNeedle, cbNeedle))
1441 {
1442 /* There will be another hit near by. */
1443 DBGFR3AddrAdd(&HitAddr, 1);
1444 rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, LNX_MAX_KALLSYMS_NAMES_SIZE, 1 /*uAlign*/,
1445 pabNeedle, cbNeedle, &HitAddr);
1446 if ( RT_SUCCESS(rc)
1447 && dbgDiggerLinuxIsLikelyNameFragment(pUVM, &HitAddr, pabNeedle, cbNeedle))
1448 {
1449 /*
1450 * We've got a very likely candidate for a location inside kallsyms_names.
1451 * Try find the start of it, that is to say, try find kallsyms_num_syms.
1452 * kallsyms_num_syms is aligned on sizeof(unsigned long) boundrary
1453 */
1454 rc = dbgDiggerLinuxFindStartOfNamesAndSymbolCount(pUVM, pThis, &HitAddr);
1455 if (RT_SUCCESS(rc))
1456 rc = dbgDiggerLinuxFindEndOfNamesAndMore(pUVM, pThis, &HitAddr);
1457 if (RT_SUCCESS(rc))
1458 rc = dbgDiggerLinuxFindTokenIndex(pUVM, pThis);
1459 if (RT_SUCCESS(rc))
1460 rc = dbgDiggerLinuxLoadKernelSymbols(pUVM, pThis);
1461 if (RT_SUCCESS(rc))
1462 break;
1463 }
1464 }
1465
1466 /*
1467 * Advance.
1468 */
1469 RTGCUINTPTR cbDistance = HitAddr.FlatPtr - CurAddr.FlatPtr + cbNeedle;
1470 if (RT_UNLIKELY(cbDistance >= cbLeft))
1471 {
1472 Log(("dbgDiggerLinuxInit: Failed to find kallsyms\n"));
1473 break;
1474 }
1475 cbLeft -= cbDistance;
1476 DBGFR3AddrAdd(&CurAddr, cbDistance);
1477
1478 }
1479
1480 return rc;
1481}
1482/**
1483 * @copydoc DBGFOSREG::pfnInit
1484 */
1485static DECLCALLBACK(int) dbgDiggerLinuxInit(PUVM pUVM, void *pvData)
1486{
1487 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1488 Assert(!pThis->fValid);
1489
1490 /*
1491 * Assume 64-bit kernels all live way beyond 32-bit address space.
1492 */
1493 pThis->f64Bit = pThis->AddrLinuxBanner.FlatPtr > UINT32_MAX;
1494
1495 static const uint8_t s_abNeedle[] = "kobj";
1496 int rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, s_abNeedle, sizeof(s_abNeedle) - 1);
1497 if (RT_FAILURE(rc))
1498 {
1499 /* Try alternate needle (seen on older x86 Linux kernels). */
1500 static const uint8_t s_abNeedleAlt[] = "kobjec";
1501 rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, s_abNeedleAlt, sizeof(s_abNeedleAlt) - 1);
1502 if (RT_FAILURE(rc))
1503 {
1504 static const uint8_t s_abNeedleOSuseX86[] = "nmi"; /* OpenSuSe 10.2 x86 */
1505 rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, s_abNeedleOSuseX86, sizeof(s_abNeedleOSuseX86) - 1);
1506 }
1507 }
1508
1509 pThis->fValid = true;
1510 return VINF_SUCCESS;
1511}
1512
1513
1514/**
1515 * @copydoc DBGFOSREG::pfnProbe
1516 */
1517static DECLCALLBACK(bool) dbgDiggerLinuxProbe(PUVM pUVM, void *pvData)
1518{
1519 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1520
1521 /*
1522 * Look for "Linux version " at the start of the rodata segment.
1523 * Hope that this comes before any message buffer or other similar string.
1524 */
1525 for (unsigned i = 0; i < RT_ELEMENTS(g_au64LnxKernelAddresses); i++)
1526 {
1527 DBGFADDRESS KernelAddr;
1528 DBGFR3AddrFromFlat(pUVM, &KernelAddr, g_au64LnxKernelAddresses[i]);
1529 DBGFADDRESS HitAddr;
1530 int rc = DBGFR3MemScan(pUVM, 0, &KernelAddr, LNX_MAX_KERNEL_SIZE, 1,
1531 g_abLinuxVersion, sizeof(g_abLinuxVersion) - 1, &HitAddr);
1532 if (RT_SUCCESS(rc))
1533 {
1534 char szTmp[128];
1535 char const *pszX = &szTmp[sizeof(g_abLinuxVersion) - 1];
1536 rc = DBGFR3MemReadString(pUVM, 0, &HitAddr, szTmp, sizeof(szTmp));
1537 if ( RT_SUCCESS(rc)
1538 && ( ( pszX[0] == '2' /* 2.x.y with x in {0..6} */
1539 && pszX[1] == '.'
1540 && pszX[2] >= '0'
1541 && pszX[2] <= '6')
1542 || ( pszX[0] >= '3' /* 3.x, 4.x, ... 9.x */
1543 && pszX[0] <= '9'
1544 && pszX[1] == '.'
1545 && pszX[2] >= '0'
1546 && pszX[2] <= '9')
1547 )
1548 )
1549 {
1550 pThis->AddrKernelBase = KernelAddr;
1551 pThis->AddrLinuxBanner = HitAddr;
1552 return true;
1553 }
1554 }
1555 }
1556 return false;
1557}
1558
1559
1560/**
1561 * @copydoc DBGFOSREG::pfnDestruct
1562 */
1563static DECLCALLBACK(void) dbgDiggerLinuxDestruct(PUVM pUVM, void *pvData)
1564{
1565
1566}
1567
1568
1569/**
1570 * @copydoc DBGFOSREG::pfnConstruct
1571 */
1572static DECLCALLBACK(int) dbgDiggerLinuxConstruct(PUVM pUVM, void *pvData)
1573{
1574 PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
1575 pThis->IDmesg.u32Magic = DBGFOSIDMESG_MAGIC;
1576 pThis->IDmesg.pfnQueryKernelLog = dbgDiggerLinuxIDmsg_QueryKernelLog;
1577 pThis->IDmesg.u32EndMagic = DBGFOSIDMESG_MAGIC;
1578
1579 return VINF_SUCCESS;
1580}
1581
1582
1583const DBGFOSREG g_DBGDiggerLinux =
1584{
1585 /* .u32Magic = */ DBGFOSREG_MAGIC,
1586 /* .fFlags = */ 0,
1587 /* .cbData = */ sizeof(DBGDIGGERLINUX),
1588 /* .szName = */ "Linux",
1589 /* .pfnConstruct = */ dbgDiggerLinuxConstruct,
1590 /* .pfnDestruct = */ dbgDiggerLinuxDestruct,
1591 /* .pfnProbe = */ dbgDiggerLinuxProbe,
1592 /* .pfnInit = */ dbgDiggerLinuxInit,
1593 /* .pfnRefresh = */ dbgDiggerLinuxRefresh,
1594 /* .pfnTerm = */ dbgDiggerLinuxTerm,
1595 /* .pfnQueryVersion = */ dbgDiggerLinuxQueryVersion,
1596 /* .pfnQueryInterface = */ dbgDiggerLinuxQueryInterface,
1597 /* .u32EndMagic = */ DBGFOSREG_MAGIC
1598};
1599
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