VirtualBox

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

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

*: scm cleanup run.

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