Changeset 62319 in vbox for trunk/src/VBox/Debugger
- Timestamp:
- Jul 19, 2016 11:40:50 AM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Debugger/DBGPlugInLinux.cpp
r61622 r62319 25 25 #include <VBox/vmm/dbgf.h> 26 26 #include <VBox/dis.h> 27 #include <iprt/file.h> 27 28 #include <iprt/string.h> 28 29 #include <iprt/mem.h> 29 30 #include <iprt/stream.h> 30 31 #include <iprt/ctype.h> 32 #include <iprt/vfs.h> 33 #include <iprt/zip.h> 31 34 32 35 … … 41 44 /** @} */ 42 45 46 47 /** 48 * Config item type. 49 */ 50 typedef 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 */ 66 typedef 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. */ 84 typedef DBGDIGGERLINUXCFGITEM *PDBGDIGGERLINUXCFGITEM; 85 /** Pointer to a const config database item. */ 86 typedef const DBGDIGGERLINUXCFGITEM *PCDBGDIGGERLINUXCFGITEM; 43 87 44 88 /** … … 84 128 /** The kernel message log interface. */ 85 129 DBGFOSIDMESG IDmesg; 130 131 /** The config database root. */ 132 RTSTRSPACE hCfgDb; 86 133 } DBGDIGGERLINUX; 87 134 /** Pointer to the linux guest OS digger instance data. */ … … 140 187 /** The approximate maximum length of a string token. */ 141 188 #define LNX_MAX_KALLSYMS_TOKEN_LEN UINT16_C(32) 189 /** Maximum compressed config size expected. */ 190 #define LNX_MAX_COMPRESSED_CFG_SIZE _1M 142 191 143 192 /** Module tag for linux ('linuxmod' on little endian ASCII systems). */ … … 970 1019 971 1020 /** 1021 * Worker destroying the config database. 1022 */ 1023 static 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 */ 1039 static void dbgDiggerLinuxCfgDbDestroy(PDBGDIGGERLINUX pThis) 1040 { 1041 RTStrSpaceDestroy(&pThis->hCfgDb, dbgDiggerLinuxCfgDbDestroyWorker, NULL); 1042 } 1043 1044 1045 /** 972 1046 * @copydoc DBGFOSREG::pfnQueryInterface 973 1047 */ … … 1022 1096 Assert(pThis->fValid); 1023 1097 1098 dbgDiggerLinuxCfgDbDestroy(pThis); 1024 1099 pThis->fValid = false; 1025 1100 } … … 1713 1788 return rc; 1714 1789 } 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 */ 1799 static 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 */ 1832 static 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 */ 1866 static 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 */ 1947 static 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 */ 2036 static 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 */ 2096 static 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 */ 2131 static 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 1715 2183 /** 1716 2184 * @copydoc DBGFOSREG::pfnInit … … 1726 2194 pThis->f64Bit = pThis->AddrLinuxBanner.FlatPtr > UINT32_MAX; 1727 2195 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 1728 2207 static const uint8_t s_abNeedle[] = "kobj"; 1729 intrc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, s_abNeedle, sizeof(s_abNeedle) - 1);2208 rc = dbgDiggerLinuxFindSymbolTableFromNeedle(pThis, pUVM, s_abNeedle, sizeof(s_abNeedle) - 1); 1730 2209 if (RT_FAILURE(rc)) 1731 2210 {
Note:
See TracChangeset
for help on using the changeset viewer.