VirtualBox

Changeset 62319 in vbox for trunk


Ignore:
Timestamp:
Jul 19, 2016 11:40:50 AM (9 years ago)
Author:
vboxsync
Message:

Debugger/Digger/Linux: Implement extraction and parsin of the compressed kernel config found in most kernels. The config is guarded by IKCFG_ST and IKCFG_ED for the start and end of the config. The config database will be used later to determine the method to use to find the kernel symbol table.

File:
1 edited

Legend:

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

    r61622 r62319  
    2525#include <VBox/vmm/dbgf.h>
    2626#include <VBox/dis.h>
     27#include <iprt/file.h>
    2728#include <iprt/string.h>
    2829#include <iprt/mem.h>
    2930#include <iprt/stream.h>
    3031#include <iprt/ctype.h>
     32#include <iprt/vfs.h>
     33#include <iprt/zip.h>
    3134
    3235
     
    4144/** @} */
    4245
     46
     47/**
     48 * Config item type.
     49 */
     50typedef enum DBGDIGGERLINUXCFGITEMTYPE
     51{
     52    /** Invalid type. */
     53    DBGDIGGERLINUXCFGITEMTYPE_INVALID = 0,
     54    /** String. */
     55    DBGDIGGERLINUXCFGITEMTYPE_STRING,
     56    /** Number. */
     57    DBGDIGGERLINUXCFGITEMTYPE_NUMBER,
     58    /** Flag whether this feature is included in the
     59     * kernel or as a module. */
     60    DBGDIGGERLINUXCFGITEMTYPE_FLAG
     61} DBGDIGGERLINUXCFGITEMTYPE;
     62
     63/**
     64 * Item in the config database.
     65 */
     66typedef struct DBGDIGGERLINUXCFGITEM
     67{
     68    /** String space core. */
     69    RTSTRSPACECORE            Core;
     70    /** Config item type. */
     71    DBGDIGGERLINUXCFGITEMTYPE enmType;
     72    /** Data based on the type. */
     73    union
     74    {
     75        /** Number. */
     76        int64_t               i64Num;
     77        /** Flag. */
     78        bool                  fModule;
     79        /** String - variable in size. */
     80        char                  aszString[1];
     81    } u;
     82} DBGDIGGERLINUXCFGITEM;
     83/** Pointer to a config database item. */
     84typedef DBGDIGGERLINUXCFGITEM *PDBGDIGGERLINUXCFGITEM;
     85/** Pointer to a const config database item. */
     86typedef const DBGDIGGERLINUXCFGITEM *PCDBGDIGGERLINUXCFGITEM;
    4387
    4488/**
     
    84128    /** The kernel message log interface. */
    85129    DBGFOSIDMESG    IDmesg;
     130
     131    /** The config database root. */
     132    RTSTRSPACE      hCfgDb;
    86133} DBGDIGGERLINUX;
    87134/** Pointer to the linux guest OS digger instance data. */
     
    140187/** The approximate maximum length of a string token. */
    141188#define LNX_MAX_KALLSYMS_TOKEN_LEN          UINT16_C(32)
     189/** Maximum compressed config size expected. */
     190#define LNX_MAX_COMPRESSED_CFG_SIZE         _1M
    142191
    143192/** Module tag for linux ('linuxmod' on little endian ASCII systems). */
     
    9701019
    9711020/**
     1021 * Worker destroying the config database.
     1022 */
     1023static DECLCALLBACK(int) dbgDiggerLinuxCfgDbDestroyWorker(PRTSTRSPACECORE pStr, void *pvUser)
     1024{
     1025    PDBGDIGGERLINUXCFGITEM pCfgItem = (PDBGDIGGERLINUXCFGITEM)pStr;
     1026    RTStrFree((char *)pCfgItem->Core.pszString);
     1027    RTMemFree(pCfgItem);
     1028    NOREF(pvUser);
     1029    return 0;
     1030}
     1031
     1032
     1033/**
     1034 * Destroy the config database.
     1035 *
     1036 * @returns nothing.
     1037 * @param   pThis               The Linux digger data.
     1038 */
     1039static void dbgDiggerLinuxCfgDbDestroy(PDBGDIGGERLINUX pThis)
     1040{
     1041    RTStrSpaceDestroy(&pThis->hCfgDb, dbgDiggerLinuxCfgDbDestroyWorker, NULL);
     1042}
     1043
     1044
     1045/**
    9721046 * @copydoc DBGFOSREG::pfnQueryInterface
    9731047 */
     
    10221096    Assert(pThis->fValid);
    10231097
     1098    dbgDiggerLinuxCfgDbDestroy(pThis);
    10241099    pThis->fValid = false;
    10251100}
     
    17131788    return rc;
    17141789}
     1790
     1791/**
     1792 * Skips whitespace and comments in the given config returning the pointer
     1793 * to the first non whitespace character.
     1794 *
     1795 * @returns Pointer to the first non whitespace character or NULL if the end
     1796 *          of the string was reached.
     1797 * @param   pszCfg              The config string.
     1798 */
     1799static const char *dbgDiggerLinuxCfgSkipWhitespace(const char *pszCfg)
     1800{
     1801    do
     1802    {
     1803        while (   *pszCfg != '\0'
     1804               && (   RT_C_IS_SPACE(*pszCfg)
     1805                   || *pszCfg == '\n'))
     1806            pszCfg++;
     1807
     1808        /* Do we have a comment? Skip it. */
     1809        if (*pszCfg == '#')
     1810        {
     1811            while (   *pszCfg != '\n'
     1812                   && *pszCfg != '\0')
     1813                pszCfg++;
     1814        }
     1815    } while (   *pszCfg != '\0'
     1816             && (   RT_C_IS_SPACE(*pszCfg)
     1817                 || *pszCfg == '\n'
     1818                 || *pszCfg == '#'));
     1819
     1820    return pszCfg;
     1821}
     1822
     1823/**
     1824 * Parses an identifier at the given position.
     1825 *
     1826 * @returns VBox status code.
     1827 * @param   pszCfg              The config data.
     1828 * @param   ppszCfgNext         Where to store the pointer to the data following the identifier.
     1829 * @param   ppszIde             Where to store the pointer to the identifier on success.
     1830 *                              Free with RTStrFree().         
     1831 */
     1832static int dbgDiggerLinuxCfgParseIde(const char *pszCfg, const char **ppszCfgNext, char **ppszIde)
     1833{
     1834    int rc = VINF_SUCCESS;
     1835    size_t cchIde = 0;
     1836
     1837    while (   *pszCfg != '\0'
     1838           && (   RT_C_IS_ALNUM(*pszCfg)
     1839               || *pszCfg == '_'))
     1840    {
     1841        cchIde++;
     1842        pszCfg++;
     1843    }
     1844
     1845    if (cchIde)
     1846    {
     1847        *ppszIde = RTStrDupN(pszCfg - cchIde, cchIde);
     1848        if (!*ppszIde)
     1849            rc = VERR_NO_STR_MEMORY;
     1850    }
     1851
     1852    *ppszCfgNext = pszCfg;
     1853    return rc;
     1854}
     1855
     1856/**
     1857 * Parses a value for a config item.
     1858 *
     1859 * @returns VBox status code.
     1860 * @param   pszCfg              The config data.
     1861 * @param   ppszCfgNext         Where to store the pointer to the data following the identifier.
     1862 * @param   pcbCfg              Where the initial size of the string is stored.
     1863 *                              Contains the remaining string length on return.
     1864 * @param   ppCfgItem           Where to store the created config item on success.
     1865 */
     1866static int dbgDiggerLinuxCfgParseVal(const char *pszCfg, const char **ppszCfgNext,
     1867                                     PDBGDIGGERLINUXCFGITEM *ppCfgItem)
     1868{
     1869    int rc = VINF_SUCCESS;
     1870    PDBGDIGGERLINUXCFGITEM pCfgItem = NULL;
     1871
     1872    if (RT_C_IS_DIGIT(*pszCfg) || *pszCfg == '-')
     1873    {
     1874        /* Parse the number. */
     1875        int64_t i64Num;
     1876        rc = RTStrToInt64Ex(pszCfg, (char **)ppszCfgNext, 0, &i64Num);
     1877        if (   RT_SUCCESS(rc)
     1878            || rc == VWRN_TRAILING_CHARS
     1879            || rc == VWRN_TRAILING_SPACES)
     1880        {
     1881            pCfgItem = (PDBGDIGGERLINUXCFGITEM)RTMemAllocZ(sizeof(DBGDIGGERLINUXCFGITEM));
     1882            if (pCfgItem)
     1883            {
     1884                pCfgItem->enmType = DBGDIGGERLINUXCFGITEMTYPE_NUMBER;
     1885                pCfgItem->u.i64Num = i64Num;
     1886            }
     1887            else
     1888                rc = VERR_NO_MEMORY;
     1889        }
     1890    }
     1891    else if (*pszCfg == '\"')
     1892    {
     1893        /* Parse a string. */
     1894        const char *pszCfgCur = pszCfg + 1;
     1895        while (   *pszCfgCur != '\0'
     1896               && *pszCfgCur != '\"')
     1897            pszCfgCur++;
     1898
     1899        if (*pszCfgCur == '\"')
     1900        {
     1901            pCfgItem = (PDBGDIGGERLINUXCFGITEM)RTMemAllocZ(RT_OFFSETOF(DBGDIGGERLINUXCFGITEM, u.aszString[pszCfgCur - pszCfg + 1]));
     1902            if (pCfgItem)
     1903            {
     1904                pCfgItem->enmType = DBGDIGGERLINUXCFGITEMTYPE_STRING;
     1905                RTStrCopyEx(&pCfgItem->u.aszString[0], pszCfgCur - pszCfg + 1, pszCfg, pszCfgCur - pszCfg);
     1906                *ppszCfgNext = pszCfgCur + 1;
     1907            }
     1908            else
     1909                rc = VERR_NO_MEMORY;
     1910        }
     1911        else
     1912            rc = VERR_INVALID_STATE;
     1913    }
     1914    else if (   *pszCfg == 'y'
     1915             || *pszCfg == 'm')
     1916    {
     1917        /* Included or module. */
     1918        pCfgItem = (PDBGDIGGERLINUXCFGITEM)RTMemAllocZ(sizeof(DBGDIGGERLINUXCFGITEM));
     1919        if (pCfgItem)
     1920        {
     1921            pCfgItem->enmType = DBGDIGGERLINUXCFGITEMTYPE_FLAG;
     1922            pCfgItem->u.fModule = *pszCfg == 'm';
     1923        }
     1924        else
     1925            rc = VERR_NO_MEMORY;
     1926        pszCfg++;
     1927        *ppszCfgNext = pszCfg;
     1928    }
     1929    else
     1930        rc = VERR_INVALID_STATE;
     1931
     1932    if (RT_SUCCESS(rc))
     1933        *ppCfgItem = pCfgItem;
     1934    else if (pCfgItem)
     1935        RTMemFree(pCfgItem);
     1936
     1937    return rc;
     1938}
     1939
     1940/**
     1941 * Parses the given kernel config and creates the config database.
     1942 *
     1943 * @returns VBox status code
     1944 * @param   pThis               The Linux digger data.
     1945 * @param   pbCfg               The config string.
     1946 */
     1947static int dbgDiggerLinuxCfgParse(PDBGDIGGERLINUX pThis, const char *pszCfg)
     1948{
     1949    int rc = VINF_SUCCESS;
     1950
     1951    /*
     1952     * The config is a text file with the following elements:
     1953     *     # starts a comment which goes till the end of the line
     1954     *     <Ide>=<val> where <Ide> is an identifier consisting of
     1955     *                 alphanumerical characters (including _)
     1956     *     <val> denotes the value for the identifier and can have the following
     1957     *           formats:
     1958     *               (-)[0-9]* for numbers
     1959     *               "..."     for a string value
     1960     *               m         when a feature is enabled as a module
     1961     *               y         when a feature is enabled
     1962     * Newlines are used as a separator between values and mark the end
     1963     * of a comment
     1964     */
     1965    const char *pszCfgCur = pszCfg;
     1966    while (   RT_SUCCESS(rc)
     1967           && *pszCfgCur != '\0')
     1968    {
     1969        /* Start skipping the whitespace. */
     1970        pszCfgCur = dbgDiggerLinuxCfgSkipWhitespace(pszCfgCur);
     1971        if (   pszCfgCur
     1972            && *pszCfgCur != '\0')
     1973        {
     1974            char *pszIde = NULL;
     1975            /* Must be an identifier, parse it. */
     1976            rc = dbgDiggerLinuxCfgParseIde(pszCfgCur, &pszCfgCur, &pszIde);
     1977            if (RT_SUCCESS(rc))
     1978            {
     1979                /*
     1980                 * Skip whitespace again (shouldn't be required because = follows immediately
     1981                 * in the observed configs).
     1982                 */
     1983                pszCfgCur = dbgDiggerLinuxCfgSkipWhitespace(pszCfgCur);
     1984                if (   pszCfgCur
     1985                    && *pszCfgCur == '=')
     1986                {
     1987                    pszCfgCur++;
     1988                    pszCfgCur = dbgDiggerLinuxCfgSkipWhitespace(pszCfgCur);
     1989                    if (   pszCfgCur
     1990                        && *pszCfgCur != '\0')
     1991                    {
     1992                        /* Get the value. */
     1993                        PDBGDIGGERLINUXCFGITEM pCfgItem = NULL;
     1994                        rc = dbgDiggerLinuxCfgParseVal(pszCfgCur, &pszCfgCur, &pCfgItem);
     1995                        if (RT_SUCCESS(rc))
     1996                        {
     1997                            pCfgItem->Core.pszString = pszIde;
     1998                            bool fRc = RTStrSpaceInsert(&pThis->hCfgDb, &pCfgItem->Core);
     1999                            if (!fRc)
     2000                            {
     2001                                RTStrFree(pszIde);
     2002                                RTMemFree(pCfgItem);
     2003                                rc = VERR_INVALID_STATE;
     2004                            }
     2005                        }
     2006                    }
     2007                    else
     2008                        rc = VERR_EOF;
     2009                }
     2010                else
     2011                    rc = VERR_INVALID_STATE;
     2012            }
     2013
     2014            if (RT_FAILURE(rc))
     2015                RTStrFree(pszIde);
     2016        }
     2017        else
     2018            break; /* Reached the end of the config. */
     2019    }
     2020
     2021    if (RT_FAILURE(rc))
     2022        dbgDiggerLinuxCfgDbDestroy(pThis);
     2023
     2024    return rc;
     2025}
     2026
     2027/**
     2028 * Decompresses the given config and validates the UTF-8 encoding.
     2029 *
     2030 * @returns VBox status code.
     2031 * @param   pbCfgComp           The compressed config.
     2032 * @param   cbCfgComp           Size of the compressed config.
     2033 * @param   ppszCfg             Where to store the pointer to the decompressed config
     2034 *                              on success.
     2035 */
     2036static int dbgDiggerLinuxCfgDecompress(const uint8_t *pbCfgComp, size_t cbCfgComp, char **ppszCfg)
     2037{
     2038    int rc = VINF_SUCCESS;
     2039    RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
     2040
     2041    rc = RTVfsIoStrmFromBuffer(RTFILE_O_READ, pbCfgComp, cbCfgComp, &hVfsIos);
     2042    if (RT_SUCCESS(rc))
     2043    {
     2044        RTVFSIOSTREAM hVfsIosDecomp = NIL_RTVFSIOSTREAM;
     2045        rc = RTZipGzipDecompressIoStream(hVfsIos, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR, &hVfsIosDecomp);
     2046        if (RT_SUCCESS(rc))
     2047        {
     2048            char *pszCfg = NULL;
     2049            size_t cchCfg = 0;
     2050            size_t cbRead = 0;
     2051
     2052            do
     2053            {
     2054                uint8_t abBuf[_64K];
     2055                rc = RTVfsIoStrmRead(hVfsIosDecomp, abBuf, sizeof(abBuf), true /*fBlocking*/, &cbRead);
     2056                if (rc == VINF_EOF && cbRead == 0)
     2057                    rc = VINF_SUCCESS;
     2058                if (   RT_SUCCESS(rc)
     2059                    && cbRead > 0)
     2060                {
     2061                    /* Append data. */
     2062                    char *pszCfgNew = pszCfg;
     2063                    rc = RTStrRealloc(&pszCfgNew, cchCfg + cbRead + 1);
     2064                    if (RT_SUCCESS(rc))
     2065                    {
     2066                        pszCfg = pszCfgNew;
     2067                        memcpy(pszCfg + cchCfg, &abBuf[0], cbRead);
     2068                        cchCfg += cbRead;
     2069                        pszCfg[cchCfg] = '\0'; /* Enforce string termination. */
     2070                    }
     2071                }
     2072            } while (RT_SUCCESS(rc) && cbRead > 0);
     2073
     2074            if (RT_SUCCESS(rc))
     2075                *ppszCfg = pszCfg;
     2076            else if (RT_FAILURE(rc) && pszCfg)
     2077                RTStrFree(pszCfg);
     2078
     2079            RTVfsIoStrmRelease(hVfsIosDecomp);
     2080        }
     2081        RTVfsIoStrmRelease(hVfsIos);
     2082    }
     2083
     2084    return rc;
     2085}
     2086
     2087/**
     2088 * Reads and decodes the compressed kernel config.
     2089 *
     2090 * @returns VBox status code.
     2091 * @param   pThis               The Linux digger data.
     2092 * @param   pUVM                The user mode VM handle.
     2093 * @param   pAddrStart          The start address of the compressed config.
     2094 * @param   cbCfgComp           The size of the compressed config.
     2095 */
     2096static int dbgDiggerLinuxCfgDecode(PDBGDIGGERLINUX pThis, PUVM pUVM,
     2097                                   PCDBGFADDRESS pAddrStart, size_t cbCfgComp)
     2098{
     2099    int rc = VINF_SUCCESS;
     2100    uint8_t *pbCfgComp = (uint8_t *)RTMemTmpAlloc(cbCfgComp);
     2101    if (!pbCfgComp)
     2102        return VERR_NO_MEMORY;
     2103
     2104    rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, pAddrStart, pbCfgComp, cbCfgComp);
     2105    if (RT_SUCCESS(rc))
     2106    {
     2107        char *pszCfg = NULL;
     2108        rc = dbgDiggerLinuxCfgDecompress(pbCfgComp, cbCfgComp, &pszCfg);
     2109        if (RT_SUCCESS(rc))
     2110        {
     2111            if (RTStrIsValidEncoding(pszCfg))
     2112                rc = dbgDiggerLinuxCfgParse(pThis, pszCfg);
     2113            else
     2114                rc = VERR_INVALID_UTF8_ENCODING;
     2115            RTStrFree(pszCfg);
     2116        }
     2117    }
     2118
     2119    RTMemFree(pbCfgComp);
     2120    return rc;
     2121}
     2122
     2123/**
     2124 * Tries to find the compressed kernel config in the kernel address space
     2125 * and sets up the config database.
     2126 *
     2127 * @returns VBox status code.
     2128 * @param   pThis               The Linux digger data.
     2129 * @param   pUVM                The user mode VM handle.
     2130 */
     2131static int dbgDiggerLinuxCfgFind(PDBGDIGGERLINUX pThis, PUVM pUVM)
     2132{
     2133    int rc = VINF_SUCCESS;
     2134
     2135    /*
     2136     * Go looking for the IKCFG_ST string which indicates the start
     2137     * of the compressed config file.
     2138     */
     2139    static const uint8_t s_abCfgNeedleStart[] = "IKCFG_ST";
     2140    static const uint8_t s_abCfgNeedleEnd[] = "IKCFG_ED";
     2141    DBGFADDRESS CurAddr = pThis->AddrLinuxBanner;
     2142    uint32_t    cbLeft  = LNX_MAX_KERNEL_SIZE;
     2143    while (cbLeft > 4096)
     2144    {
     2145        DBGFADDRESS HitAddrStart;
     2146        rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &CurAddr, cbLeft, 1 /*uAlign*/,
     2147                           s_abCfgNeedleStart, sizeof(s_abCfgNeedleStart) - 1, &HitAddrStart);
     2148        if (RT_FAILURE(rc))
     2149            break;
     2150
     2151        /* Check for the end marker which shouldn't be that far away. */
     2152        DBGFR3AddrAdd(&HitAddrStart, sizeof(s_abCfgNeedleStart) - 1);
     2153        DBGFADDRESS HitAddrEnd;
     2154        rc = DBGFR3MemScan(pUVM, 0 /* idCpu */, &HitAddrStart, LNX_MAX_COMPRESSED_CFG_SIZE,
     2155                           1 /* uAlign */, s_abCfgNeedleEnd, sizeof(s_abCfgNeedleEnd) - 1, &HitAddrEnd);
     2156        if (RT_SUCCESS(rc))
     2157        {
     2158            /* Allocate a buffer to hold the compressed data between the markers and fetch it. */
     2159            RTGCUINTPTR cbCfg = HitAddrEnd.FlatPtr - HitAddrStart.FlatPtr;
     2160            Assert(cbCfg == (size_t)cbCfg);
     2161            rc = dbgDiggerLinuxCfgDecode(pThis, pUVM, &HitAddrStart, cbCfg);
     2162            if (RT_SUCCESS(rc))
     2163                break;
     2164        }
     2165
     2166        /*
     2167         * Advance.
     2168         */
     2169        RTGCUINTPTR cbDistance = HitAddrStart.FlatPtr - CurAddr.FlatPtr + sizeof(s_abCfgNeedleStart) - 1;
     2170        if (RT_UNLIKELY(cbDistance >= cbLeft))
     2171        {
     2172            LogFunc(("Failed to find compressed kernel config\n"));
     2173            break;
     2174        }
     2175        cbLeft -= cbDistance;
     2176        DBGFR3AddrAdd(&CurAddr, cbDistance);
     2177
     2178    }
     2179
     2180    return rc;
     2181}
     2182
    17152183/**
    17162184 * @copydoc DBGFOSREG::pfnInit
     
    17262194    pThis->f64Bit = pThis->AddrLinuxBanner.FlatPtr > UINT32_MAX;
    17272195
     2196    pThis->hCfgDb = NULL;
     2197
     2198    /*
     2199     * Try to find the compressed kernel config and parse it before we try
     2200     * to get the symbol table, the config database is required to select
     2201     * the method to use.
     2202     */
     2203    int rc = dbgDiggerLinuxCfgFind(pThis, pUVM);
     2204    if (RT_FAILURE(rc))
     2205        LogFlowFunc(("Failed to find kernel config (%Rrc), no config database available\n", rc));
     2206
    17282207    static const uint8_t s_abNeedle[] = "kobj";
    1729     int rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, s_abNeedle, sizeof(s_abNeedle) - 1);
     2208    rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, s_abNeedle, sizeof(s_abNeedle) - 1);
    17302209    if (RT_FAILURE(rc))
    17312210    {
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