Beispiel #1
0
/**
 * Worker for dbgDiggerLinuxFindEndNames that records the findings.
 *
 * @returns VINF_SUCCESS
 * @param   pThis           The linux digger data to update.
 * @param   pAddrMarkers    The address of the marker (kallsyms_markers).
 * @param   cbMarkerEntry   The size of a marker entry (32-bit or 64-bit).
 */
static int dbgDiggerLinuxFoundMarkers(PDBGDIGGERLINUX pThis, PCDBGFADDRESS pAddrMarkers, uint32_t cbMarkerEntry)
{
    pThis->cbKernelNames         = pAddrMarkers->FlatPtr - pThis->AddrKernelNames.FlatPtr - 1;
    pThis->AddrKernelNameMarkers = *pAddrMarkers;
    pThis->cKernelNameMarkers    = RT_ALIGN_32(pThis->cKernelSymbols, 256) / 256;
    pThis->AddrKernelTokenTable  = *pAddrMarkers;
    DBGFR3AddrAdd(&pThis->AddrKernelTokenTable, pThis->cKernelNameMarkers * cbMarkerEntry);

    Log(("dbgDiggerLinuxFoundMarkers: AddrKernelNames=%RGv cbKernelNames=%#x\n"
         "dbgDiggerLinuxFoundMarkers: AddrKernelNameMarkers=%RGv cKernelNameMarkers=%#x\n"
         "dbgDiggerLinuxFoundMarkers: AddrKernelTokenTable=%RGv\n",
         pThis->AddrKernelNames.FlatPtr, pThis->cbKernelNames,
         pThis->AddrKernelNameMarkers.FlatPtr, pThis->cKernelNameMarkers,
         pThis->AddrKernelTokenTable.FlatPtr));
    return VINF_SUCCESS;
}
Beispiel #2
0
/**
 * Read stack memory.
 */
DECLINLINE(int) dbgfR3Read(PUVM pUVM, VMCPUID idCpu, void *pvBuf, PCDBGFADDRESS pSrcAddr, size_t cb, size_t *pcbRead)
{
    int rc = DBGFR3MemRead(pUVM, idCpu, pSrcAddr, pvBuf, cb);
    if (RT_FAILURE(rc))
    {
        /* fallback: byte by byte and zero the ones we fail to read. */
        size_t cbRead;
        for (cbRead = 0; cbRead < cb; cbRead++)
        {
            DBGFADDRESS Addr = *pSrcAddr;
            rc = DBGFR3MemRead(pUVM, idCpu, DBGFR3AddrAdd(&Addr, cbRead), (uint8_t *)pvBuf + cbRead, 1);
            if (RT_FAILURE(rc))
                break;
        }
        if (cbRead)
            rc = VINF_SUCCESS;
        memset((char *)pvBuf + cbRead, 0, cb - cbRead);
        *pcbRead = cbRead;
    }
    else
        *pcbRead = cb;
    return rc;
}
Beispiel #3
0
/**
 * Tries to find the end of kallsyms_names and thereby the start of
 * kallsyms_markers and kallsyms_token_table.
 *
 * The kallsyms_names size is stored in pThis->cbKernelNames, the addresses of
 * the two other symbols in pThis->AddrKernelNameMarkers and
 * pThis->AddrKernelTokenTable.  The number of marker entries is stored in
 * pThis->cKernelNameMarkers.
 *
 * @returns VBox status code, success indicating that all three variables have
 *          been found and taken down.
 * @param   pUVM                The user mode VM handle.
 * @param   pThis               The Linux digger data.
 * @param   pHitAddr            An address we think is inside kallsyms_names.
 */
static int dbgDiggerLinuxFindEndOfNamesAndMore(PUVM pUVM, PDBGDIGGERLINUX pThis, PCDBGFADDRESS pHitAddr)
{
    /*
     * Search forward in chunks.
     */
    union
    {
        uint8_t  ab[0x1000];
        uint32_t au32[0x1000 / sizeof(uint32_t)];
        uint64_t au64[0x1000 / sizeof(uint64_t)];
    } uBuf;
    bool            fPendingZeroHit = false;
    uint32_t        cbLeft  = LNX_MAX_KALLSYMS_NAMES_SIZE + sizeof(uBuf);
    uint32_t        offBuf  = pHitAddr->FlatPtr & (sizeof(uBuf) - 1);
    DBGFADDRESS     CurAddr = *pHitAddr;
    DBGFR3AddrSub(&CurAddr, offBuf);
    for (;;)
    {
        int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
        if (RT_FAILURE(rc))
            return rc;

        /*
         * The kallsyms_names table is followed by kallsyms_markers we assume,
         * using sizeof(unsigned long) alignment like the preceeding symbols.
         *
         * The kallsyms_markers table has entried sizeof(unsigned long) and
         * contains offsets into kallsyms_names.  The kallsyms_markers used to
         * index kallsyms_names and reduce seek time when looking up the name
         * of an address/symbol.  Each entry in kallsyms_markers covers 256
         * symbol names.
         *
         * Because of this, the first entry is always zero and all the entries
         * are ascending.  It also follows that the size of the table can be
         * calculated from kallsyms_num_syms.
         *
         * Note! We could also have walked kallsyms_names by skipping
         *       kallsyms_num_syms names, but this is faster and we will
         *       validate the encoded names later.
         */
        if (pThis->f64Bit)
        {
            if (   RT_UNLIKELY(fPendingZeroHit)
                && uBuf.au64[0] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
                && uBuf.au64[0] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
                return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrSub(&CurAddr, sizeof(uint64_t)), sizeof(uint64_t));

            uint32_t const cEntries = sizeof(uBuf) / sizeof(uint64_t);
            for (uint32_t i = offBuf / sizeof(uint64_t); i < cEntries; i++)
                if (uBuf.au64[i] == 0)
                {
                    if (RT_UNLIKELY(i + 1 >= cEntries))
                    {
                        fPendingZeroHit = true;
                        break;
                    }
                    if (   uBuf.au64[i + 1] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
                        && uBuf.au64[i + 1] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
                        return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrAdd(&CurAddr, i * sizeof(uint64_t)), sizeof(uint64_t));
                }
        }
        else
        {
            if (   RT_UNLIKELY(fPendingZeroHit)
                && uBuf.au32[0] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
                && uBuf.au32[0] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
                return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrSub(&CurAddr, sizeof(uint32_t)), sizeof(uint32_t));

            uint32_t const cEntries = sizeof(uBuf) / sizeof(uint32_t);
            for (uint32_t i = offBuf / sizeof(uint32_t); i < cEntries; i++)
                if (uBuf.au32[i] == 0)
                {
                    if (RT_UNLIKELY(i + 1 >= cEntries))
                    {
                        fPendingZeroHit = true;
                        break;
                    }
                    if (   uBuf.au32[i + 1] >= (LNX_MIN_KALLSYMS_ENC_LENGTH + 1) * 256
                        && uBuf.au32[i + 1] <= (LNX_MAX_KALLSYMS_ENC_LENGTH + 1) * 256)
                        return dbgDiggerLinuxFoundMarkers(pThis, DBGFR3AddrAdd(&CurAddr, i * sizeof(uint32_t)), sizeof(uint32_t));
                }
        }

        /*
         * Advance
         */
        if (RT_UNLIKELY(cbLeft <= sizeof(uBuf)))
        {
            Log(("dbgDiggerLinuxFindEndOfNamesAndMore: failed (pHitAddr=%RGv)\n", pHitAddr->FlatPtr));
            return VERR_NOT_FOUND;
        }
        cbLeft -= sizeof(uBuf);
        DBGFR3AddrAdd(&CurAddr, sizeof(uBuf));
        offBuf = 0;
    }
}
Beispiel #4
0
/**
 * Tries to find the address of the kallsyms_names, kallsyms_num_syms and
 * kallsyms_addresses symbols.
 *
 * The kallsyms_num_syms is read and stored in pThis->cKernelSymbols, while the
 * addresses of the other two are stored as pThis->AddrKernelNames and
 * pThis->AddrKernelAddresses.
 *
 * @returns VBox status code, success indicating that all three variables have
 *          been found and taken down.
 * @param   pUVM                The user mode VM handle.
 * @param   pThis               The Linux digger data.
 * @param   pHitAddr            An address we think is inside kallsyms_names.
 */
static int dbgDiggerLinuxFindStartOfNamesAndSymbolCount(PUVM pUVM, PDBGDIGGERLINUX pThis, PCDBGFADDRESS pHitAddr)
{
    /*
     * Search backwards in chunks.
     */
    union
    {
        uint8_t  ab[0x1000];
        uint32_t au32[0x1000 / sizeof(uint32_t)];
        uint64_t au64[0x1000 / sizeof(uint64_t)];
    } uBuf;
    uint32_t        cbLeft  = LNX_MAX_KALLSYMS_NAMES_SIZE;
    uint32_t        cbBuf   = pHitAddr->FlatPtr & (sizeof(uBuf) - 1);
    DBGFADDRESS     CurAddr = *pHitAddr;
    DBGFR3AddrSub(&CurAddr, cbBuf);
    cbBuf += sizeof(uint64_t) - 1;      /* In case our kobj hit is in the first 4/8 bytes. */
    for (;;)
    {
        int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &CurAddr, &uBuf, sizeof(uBuf));
        if (RT_FAILURE(rc))
            return rc;

        /*
         * We assume that the three symbols are aligned on guest pointer boundrary.
         *
         * The boundrary between the two tables should be noticable as the number
         * is unlikely to be more than 16 millions, there will be at least one zero
         * byte where it is, 64-bit will have 5 zero bytes.  Zero bytes aren't all
         * that common in the kallsyms_names table.
         *
         * Also the kallsyms_names table starts with a length byte, which means
         * we're likely to see a byte in the range 1..31.
         *
         * The kallsyms_addresses are mostly sorted (except for the start where the
         * absolute symbols are), so we'll spot a bunch of kernel addresses
         * immediately preceeding the kallsyms_num_syms field.
         *
         * Lazy bird: If kallsyms_num_syms is on a buffer boundrary, we skip
         *            the check for kernel addresses preceeding it.
         */
        if (pThis->f64Bit)
        {
            uint32_t i = cbBuf / sizeof(uint64_t);
            while (i-- > 0)
                if (   uBuf.au64[i] <= LNX_MAX_KALLSYMS_SYMBOLS
                    && uBuf.au64[i] >= LNX_MIN_KALLSYMS_SYMBOLS)
                {
                    uint8_t *pb = (uint8_t *)&uBuf.au64[i + 1];
                    if (   pb[0] <= LNX_MAX_KALLSYMS_ENC_LENGTH
                        && pb[0] >= LNX_MIN_KALLSYMS_ENC_LENGTH)
                    {
                        if (   (i <= 0 || LNX64_VALID_ADDRESS(uBuf.au64[i - 1]))
                            && (i <= 1 || LNX64_VALID_ADDRESS(uBuf.au64[i - 2]))
                            && (i <= 2 || LNX64_VALID_ADDRESS(uBuf.au64[i - 3])))
                            return dbgDiggerLinuxFoundStartOfNames(pThis,
                                                                   DBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint64_t)),
                                                                   (uint32_t)uBuf.au64[i], sizeof(uint64_t));
                    }
                }
        }
        else
        {
            uint32_t i = cbBuf / sizeof(uint32_t);
            while (i-- > 0)
                if (   uBuf.au32[i] <= LNX_MAX_KALLSYMS_SYMBOLS
                    && uBuf.au32[i] >= LNX_MIN_KALLSYMS_SYMBOLS)
                {
                    uint8_t *pb = (uint8_t *)&uBuf.au32[i + 1];
                    if (   pb[0] <= LNX_MAX_KALLSYMS_ENC_LENGTH
                        && pb[0] >= LNX_MIN_KALLSYMS_ENC_LENGTH)
                    {
                        if (   (i <= 0 || LNX32_VALID_ADDRESS(uBuf.au32[i - 1]))
                            && (i <= 1 || LNX32_VALID_ADDRESS(uBuf.au32[i - 2]))
                            && (i <= 2 || LNX32_VALID_ADDRESS(uBuf.au32[i - 3])))
                            return dbgDiggerLinuxFoundStartOfNames(pThis,
                                                                   DBGFR3AddrAdd(&CurAddr, (i + 1) * sizeof(uint32_t)),
                                                                   uBuf.au32[i], sizeof(uint32_t));
                    }
                }
        }

        /*
         * Advance
         */
        if (RT_UNLIKELY(cbLeft <= sizeof(uBuf)))
        {
            Log(("dbgDiggerLinuxFindStartOfNamesAndSymbolCount: failed (pHitAddr=%RGv)\n", pHitAddr->FlatPtr));
            return VERR_NOT_FOUND;
        }
        cbLeft -= sizeof(uBuf);
        DBGFR3AddrSub(&CurAddr, sizeof(uBuf));
        cbBuf = sizeof(uBuf);
    }
}
Beispiel #5
0
/**
 * @copydoc DBGFOSREG::pfnInit
 */
static DECLCALLBACK(int)  dbgDiggerLinuxInit(PUVM pUVM, void *pvData)
{
    PDBGDIGGERLINUX pThis = (PDBGDIGGERLINUX)pvData;
    Assert(!pThis->fValid);

    /*
     * Assume 64-bit kernels all live way beyond 32-bit address space.
     */
    pThis->f64Bit = pThis->AddrLinuxBanner.FlatPtr > UINT32_MAX;

    /*
     * Go looking for the kallsyms table.  If it's there, it will be somewhere
     * after the linux_banner symbol, so use it for starting the search.
     */
    DBGFADDRESS CurAddr = pThis->AddrLinuxBanner;
    uint32_t    cbLeft  = LNX_MAX_KERNEL_SIZE;
    while (cbLeft > 4096)
    {
        static const uint8_t s_abNeedle[] = "kobj";
        DBGFADDRESS          HitAddr;
        int rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &CurAddr, cbLeft, 1 /*uAlign*/,
                               s_abNeedle, sizeof(s_abNeedle) - 1, &HitAddr);
        if (RT_FAILURE(rc))
            break;
        if (dbgDiggerLinuxIsLikelyNameFragment(pUVM, &HitAddr, s_abNeedle, sizeof(s_abNeedle) - 1))
        {
            /* There will be another hit near by. */
            DBGFR3AddrAdd(&HitAddr, 1);
            rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, LNX_MAX_KALLSYMS_NAMES_SIZE, 1 /*uAlign*/,
                               s_abNeedle, sizeof(s_abNeedle) - 1, &HitAddr);
            if (   RT_SUCCESS(rc)
                && dbgDiggerLinuxIsLikelyNameFragment(pUVM, &HitAddr, s_abNeedle, sizeof(s_abNeedle) - 1))
            {
                /*
                 * We've got a very likely candidate for a location inside kallsyms_names.
                 * Try find the start of it, that is to say, try find kallsyms_num_syms.
                 * kallsyms_num_syms is aligned on sizeof(unsigned long) boundrary
                 */
                rc = dbgDiggerLinuxFindStartOfNamesAndSymbolCount(pUVM, pThis, &HitAddr);
                if (RT_SUCCESS(rc))
                    rc = dbgDiggerLinuxFindEndOfNamesAndMore(pUVM, pThis, &HitAddr);
                if (RT_SUCCESS(rc))
                    rc = dbgDiggerLinuxFindTokenIndex(pUVM, pThis);
                if (RT_SUCCESS(rc))
                    rc = dbgDiggerLinuxLoadKernelSymbols(pUVM, pThis);
                if (RT_SUCCESS(rc))
                    break;
            }
        }

        /*
         * Advance.
         */
        RTGCUINTPTR cbDistance = HitAddr.FlatPtr - CurAddr.FlatPtr + sizeof(s_abNeedle) - 1;
        if (RT_UNLIKELY(cbDistance >= cbLeft))
        {
            Log(("dbgDiggerLinuxInit: Failed to find kallsyms\n"));
            break;
        }
        cbLeft -= cbDistance;
        DBGFR3AddrAdd(&CurAddr, cbDistance);

    }

    pThis->fValid = true;
    return VINF_SUCCESS;
}