Changeset 61622 in vbox
- Timestamp:
- Jun 9, 2016 1:56:01 PM (9 years ago)
- svn:sync-xref-src-repo-rev:
- 107967
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Debugger/DBGPlugInLinux.cpp
r61607 r61622 278 278 279 279 /** 280 * Try to get at the log buffer starting address and size by disassembling emit_log_char. 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 */ 289 static int dbgDiggerLinuxQueryAsciiLogBufferPtrs(PDBGDIGGERLINUX pThis, PUVM pUVM, RTDBGMOD hMod, 290 RTGCPTR *pGCPtrLogBuf, uint32_t *pcbLogBuf) 291 { 292 int rc = VINF_SUCCESS; 293 294 /** 295 * We disassemble emit_log_char to get at the log buffer address and size. 296 * This is used in case the symbols are not exported in kallsyms. 297 * 298 * This is what it typically looks like: 299 * vmlinux!emit_log_char: 300 * %00000000c01204a1 56 push esi 301 * %00000000c01204a2 8b 35 d0 1c 34 c0 mov esi, dword [0c0341cd0h] 302 * %00000000c01204a8 53 push ebx 303 * %00000000c01204a9 8b 1d 74 3b 3e c0 mov ebx, dword [0c03e3b74h] 304 * %00000000c01204af 8b 0d d8 1c 34 c0 mov ecx, dword [0c0341cd8h] 305 * %00000000c01204b5 8d 56 ff lea edx, [esi-001h] 306 * %00000000c01204b8 21 da and edx, ebx 307 * %00000000c01204ba 88 04 11 mov byte [ecx+edx], al 308 * %00000000c01204bd 8d 53 01 lea edx, [ebx+001h] 309 * %00000000c01204c0 89 d0 mov eax, edx 310 * [...] 311 */ 312 RTDBGSYMBOL SymInfo; 313 rc = RTDbgModSymbolByName(hMod, "emit_log_char", &SymInfo); 314 if (RT_SUCCESS(rc)) 315 { 316 /* 317 * Do the diassembling. Disassemble until a ret instruction is encountered 318 * or a limit is reached (don't want to disassemble for too long as the getter 319 * should be short). Certain instructions found are ignored (push, nop, etc.). 320 */ 321 unsigned cInstrDisassembled = 0; 322 uint32_t offInstr = 0; 323 bool fRet = false; 324 DISSTATE DisState; 325 unsigned idxAddressesUsed = 0; 326 struct { size_t cb; RTGCPTR GCPtrOrigSrc; } aAddresses[5]; 327 RT_ZERO(DisState); 328 RT_ZERO(aAddresses); 329 330 do 331 { 332 DBGFADDRESS Addr; 333 RTGCPTR GCPtrCur = (RTGCPTR)SymInfo.Value + pThis->AddrKernelBase.FlatPtr + offInstr; 334 DBGFR3AddrFromFlat(pUVM, &Addr, GCPtrCur); 335 336 /* Prefetch the instruction. */ 337 uint8_t abInstr[32]; 338 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, &abInstr[0], sizeof(abInstr)); 339 if (RT_SUCCESS(rc)) 340 { 341 uint32_t cbInstr = 0; 342 343 rc = DISInstr(&abInstr[0], pThis->f64Bit ? DISCPUMODE_64BIT : DISCPUMODE_32BIT, &DisState, &cbInstr); 344 if (RT_SUCCESS(rc)) 345 { 346 switch (DisState.pCurInstr->uOpcode) 347 { 348 case OP_PUSH: 349 case OP_POP: 350 case OP_NOP: 351 case OP_LEA: 352 case OP_AND: 353 case OP_CBW: 354 break; 355 case OP_RETN: 356 /* emit_log_char returned, abort disassembling. */ 357 rc = VERR_NOT_FOUND; 358 fRet = true; 359 break; 360 case OP_MOV: 361 case OP_MOVSXD: 362 /* 363 * If a mov is encountered writing to memory with al (or dil for amd64) being the source the 364 * character is stored and we can infer the base address and size of the log buffer from 365 * the source addresses. 366 */ 367 if ( (DisState.Param2.fUse & DISUSE_REG_GEN8) 368 && ( (DisState.Param2.Base.idxGenReg == DISGREG_AL && !pThis->f64Bit) 369 || (DisState.Param2.Base.idxGenReg == DISGREG_DIL && pThis->f64Bit)) 370 && DISUSE_IS_EFFECTIVE_ADDR(DisState.Param1.fUse)) 371 { 372 RTGCPTR GCPtrLogBuf = 0; 373 size_t cbLogBuf = 0; 374 375 /* 376 * We can stop disassembling now and inspect all registers, look for a valid kernel address first. 377 * Only one of the accessed registers should hold a valid kernel address. 378 * For the log size look for the biggest non kernel address. 379 */ 380 for (unsigned i = 0; i < idxAddressesUsed; i++) 381 { 382 DBGFADDRESS AddrVal; 383 union { uint8_t abVal[8]; uint32_t u32Val; uint64_t u64Val; } Val; 384 385 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, 386 DBGFR3AddrFromFlat(pUVM, &AddrVal, aAddresses[i].GCPtrOrigSrc), 387 &Val.abVal[0], aAddresses[i].cb); 388 if (RT_SUCCESS(rc)) 389 { 390 if (pThis->f64Bit && aAddresses[i].cb == sizeof(uint64_t)) 391 { 392 if (LNX64_VALID_ADDRESS(Val.u64Val)) 393 { 394 if (GCPtrLogBuf == 0) 395 GCPtrLogBuf = Val.u64Val; 396 else 397 { 398 rc = VERR_NOT_FOUND; 399 break; 400 } 401 } 402 } 403 else 404 { 405 AssertMsgBreakStmt(aAddresses[i].cb == sizeof(uint32_t), 406 ("Invalid value size\n"), rc = VERR_INVALID_STATE); 407 408 /* Might be a kernel address or a size indicator. */ 409 if (!pThis->f64Bit && LNX32_VALID_ADDRESS(Val.u32Val)) 410 { 411 if (GCPtrLogBuf == 0) 412 GCPtrLogBuf = Val.u32Val; 413 else 414 { 415 rc = VERR_NOT_FOUND; 416 break; 417 } 418 } 419 else 420 { 421 /* 422 * The highest value will be the log buffer because the other 423 * accessed variables are indexes into the buffer and hence 424 * always smaller than the size. 425 */ 426 if (cbLogBuf < Val.u32Val) 427 cbLogBuf = Val.u32Val; 428 } 429 } 430 } 431 } 432 433 if ( RT_SUCCESS(rc) 434 && GCPtrLogBuf != 0 435 && cbLogBuf != 0) 436 { 437 *pGCPtrLogBuf = GCPtrLogBuf; 438 *pcbLogBuf = cbLogBuf; 439 } 440 else if (RT_SUCCESS(rc)) 441 rc = VERR_NOT_FOUND; 442 443 fRet = true; 444 break; 445 } 446 else 447 { 448 /* 449 * In case of a memory to register move store the destination register index and the 450 * source address in the relation table for later processing. 451 */ 452 if ( (DisState.Param1.fUse & (DISUSE_BASE | DISUSE_REG_GEN32 | DISUSE_REG_GEN64)) 453 && (DisState.Param2.cb == sizeof(uint32_t) || DisState.Param2.cb == sizeof(uint64_t)) 454 && (DisState.Param2.fUse & (DISUSE_RIPDISPLACEMENT32|DISUSE_DISPLACEMENT32|DISUSE_DISPLACEMENT64))) 455 { 456 RTGCPTR GCPtrVal = 0; 457 458 if (DisState.Param2.fUse & DISUSE_RIPDISPLACEMENT32) 459 GCPtrVal = GCPtrCur + DisState.Param2.uDisp.i32 + cbInstr; 460 else if (DisState.Param2.fUse & DISUSE_DISPLACEMENT32) 461 GCPtrVal = (RTGCPTR)DisState.Param2.uDisp.u32; 462 else if (DisState.Param2.fUse & DISUSE_DISPLACEMENT64) 463 GCPtrVal = (RTGCPTR)DisState.Param2.uDisp.u64; 464 else 465 AssertMsgFailedBreakStmt(("Invalid displacement\n"), rc = VERR_INVALID_STATE); 466 467 if (idxAddressesUsed < RT_ELEMENTS(aAddresses)) 468 { 469 /* movsxd reads always 32bits. */ 470 if (DisState.pCurInstr->uOpcode == OP_MOVSXD) 471 aAddresses[idxAddressesUsed].cb = sizeof(uint32_t); 472 else 473 aAddresses[idxAddressesUsed].cb = DisState.Param2.cb; 474 aAddresses[idxAddressesUsed].GCPtrOrigSrc = GCPtrVal; 475 idxAddressesUsed++; 476 } 477 else 478 { 479 rc = VERR_INVALID_PARAMETER; 480 break; 481 } 482 } 483 } 484 break; 485 default: 486 /* All other instructions will cause an error for now (playing safe here). */ 487 rc = VERR_INVALID_PARAMETER; 488 break; 489 } 490 cInstrDisassembled++; 491 offInstr += cbInstr; 492 } 493 } 494 } while ( RT_SUCCESS(rc) 495 && cInstrDisassembled < 20 496 && !fRet); 497 } 498 499 return rc; 500 } 501 502 /** 280 503 * Try to get at the log buffer starting address and size by disassembling some exposed helpers. 281 504 * … … 381 604 } 382 605 383 if (RT_FAILURE(rc)) 384 return rc; 606 /* 607 * Some kernels don't expose the variables in kallsyms so we have to try disassemble 608 * some public helpers to get at the addresses. 609 * 610 * @todo: Maybe cache those values so we don't have to do the heavy work every time? 611 */ 612 if (rc == VERR_NOT_FOUND) 613 { 614 rc = dbgDiggerLinuxQueryAsciiLogBufferPtrs(pThis, pUVM, hMod, &GCPtrLogBuf, &cbLogBuf); 615 if (RT_FAILURE(rc)) 616 return rc; 617 } 385 618 386 619 /*
Note:
See TracChangeset
for help on using the changeset viewer.