VirtualBox

Changeset 61622 in vbox


Ignore:
Timestamp:
Jun 9, 2016 1:56:01 PM (9 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
107967
Message:

Debugger/Digger/Linux: Another way to get at the log buffer for certain older kernels where log_buf and log_buf_len are not exposed. Disassemble emit_log_char and infer the log buffer address and sizes

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Debugger/DBGPlugInLinux.cpp

    r61607 r61622  
    278278
    279279/**
     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 */
     289static 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/**
    280503 * Try to get at the log buffer starting address and size by disassembling some exposed helpers.
    281504 *
     
    381604    }
    382605
    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    }
    385618
    386619    /*
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette