Skip to content

Commit 36c34ed

Browse files
Replace Win32 resource reading logic with cross platform implementation (dotnet#23363)
* FindResource direct implementation in PEDecoder * Fixup bugs identified in resource reading
1 parent 32df0e7 commit 36c34ed

File tree

2 files changed

+134
-17
lines changed

2 files changed

+134
-17
lines changed

src/inc/pedecoder.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,10 +258,12 @@ class PEDecoder
258258
PTR_VOID GetTlsRange(COUNT_T *pSize = NULL) const;
259259
UINT32 GetTlsIndex() const;
260260

261-
#ifndef FEATURE_PAL
262261
// Win32 resources
263262
void *GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSize = NULL) const;
264-
#endif // FEATURE_PAL
263+
private:
264+
DWORD ReadResourceDictionary(DWORD rvaOfResourceSection, DWORD rva, LPCWSTR name, BOOL *pIsDictionary) const;
265+
DWORD ReadResourceDataEntry(DWORD rva, COUNT_T *pSize) const;
266+
public:
265267

266268
// COR header fields
267269

src/utilcode/pedecoder.cpp

Lines changed: 130 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,7 +1790,96 @@ void PEDecoder::LayoutILOnly(void *base, BOOL allowFullPE) const
17901790

17911791
#endif // #ifndef DACCESS_COMPILE
17921792

1793-
#ifndef FEATURE_PAL
1793+
DWORD PEDecoder::ReadResourceDictionary(DWORD rvaOfResourceSection, DWORD rva, LPCWSTR name, BOOL *pIsDictionary) const
1794+
{
1795+
*pIsDictionary = FALSE;
1796+
1797+
if (!CheckRva(rva, sizeof(IMAGE_RESOURCE_DIRECTORY)))
1798+
{
1799+
return 0;
1800+
}
1801+
1802+
IMAGE_RESOURCE_DIRECTORY *pResourceDirectory = (IMAGE_RESOURCE_DIRECTORY *)GetRvaData(rva);
1803+
1804+
if (pResourceDirectory->MajorVersion != 4)
1805+
return 0;
1806+
1807+
if (pResourceDirectory->MinorVersion != 0)
1808+
return 0;
1809+
1810+
// Check to see if entire resource dictionary is accessible
1811+
if (!CheckRva(rva + sizeof(IMAGE_RESOURCE_DIRECTORY),
1812+
(sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * pResourceDirectory->NumberOfNamedEntries) +
1813+
(sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * pResourceDirectory->NumberOfIdEntries)))
1814+
{
1815+
return 0;
1816+
}
1817+
1818+
IMAGE_RESOURCE_DIRECTORY_ENTRY* pDirectoryEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)GetRvaData(rva + sizeof(IMAGE_RESOURCE_DIRECTORY));
1819+
1820+
// A fast implementation of resource lookup uses a binary search, but our needs are simple, and a linear search
1821+
// is easier to prove correct, so do that instead.
1822+
DWORD iEntryCount = (DWORD)pResourceDirectory->NumberOfNamedEntries + (DWORD)pResourceDirectory->NumberOfIdEntries;
1823+
1824+
for (DWORD iEntry = 0; iEntry < iEntryCount; iEntry++)
1825+
{
1826+
BOOL foundEntry = FALSE;
1827+
1828+
if (((UINT_PTR)name) <= 0xFFFF)
1829+
{
1830+
// name is id
1831+
if (pDirectoryEntries[iEntry].Name == (DWORD)name)
1832+
foundEntry = TRUE;
1833+
}
1834+
else
1835+
{
1836+
// name is string
1837+
DWORD entryName = pDirectoryEntries[iEntry].Name;
1838+
if (!(entryName & IMAGE_RESOURCE_NAME_IS_STRING))
1839+
continue;
1840+
1841+
DWORD entryNameRva = (entryName & ~IMAGE_RESOURCE_NAME_IS_STRING) + rvaOfResourceSection;
1842+
1843+
if (!CheckRva(entryNameRva, sizeof(WORD)))
1844+
return 0;
1845+
1846+
size_t entryNameLen = *(WORD*)GetRvaData(entryNameRva);
1847+
if (wcslen(name) != entryNameLen)
1848+
continue;
1849+
1850+
if (!CheckRva(entryNameRva, (COUNT_T)(sizeof(WORD) * (1 + entryNameLen))))
1851+
return 0;
1852+
1853+
if (memcmp((WCHAR*)GetRvaData(entryNameRva + sizeof(WORD)), name, entryNameLen * sizeof(WCHAR)) == 0)
1854+
foundEntry = TRUE;
1855+
}
1856+
1857+
if (!foundEntry)
1858+
continue;
1859+
1860+
*pIsDictionary = !!(pDirectoryEntries[iEntry].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY);
1861+
DWORD offsetToData = pDirectoryEntries[iEntry].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY;
1862+
DWORD dataRva = rvaOfResourceSection + offsetToData;
1863+
return dataRva;
1864+
}
1865+
1866+
return 0;
1867+
}
1868+
1869+
DWORD PEDecoder::ReadResourceDataEntry(DWORD rva, COUNT_T *pSize) const
1870+
{
1871+
*pSize = 0;
1872+
1873+
if (!CheckRva(rva, sizeof(IMAGE_RESOURCE_DATA_ENTRY)))
1874+
{
1875+
return 0;
1876+
}
1877+
1878+
IMAGE_RESOURCE_DATA_ENTRY *pDataEntry = (IMAGE_RESOURCE_DATA_ENTRY *)GetRvaData(rva);
1879+
*pSize = pDataEntry->Size;
1880+
return pDataEntry->OffsetToData;
1881+
}
1882+
17941883
void * PEDecoder::GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSize /*=NULL*/) const
17951884
{
17961885
CONTRACTL {
@@ -1800,31 +1889,57 @@ void * PEDecoder::GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSiz
18001889
GC_NOTRIGGER;
18011890
} CONTRACTL_END;
18021891

1803-
if (pSize != NULL)
1804-
*pSize = 0;
1892+
COUNT_T sizeUnused = 0; // Use this variable if pSize is null
1893+
if (pSize == NULL)
1894+
pSize = &sizeUnused;
1895+
1896+
*pSize = 0;
1897+
1898+
if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE))
1899+
return NULL;
1900+
1901+
COUNT_T resourceDataSize = 0;
1902+
IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE);
18051903

1806-
HMODULE hModule = (HMODULE) dac_cast<TADDR>(GetBase());
1904+
if (pDir->VirtualAddress == 0)
1905+
return NULL;
18071906

1808-
// Use the Win32 functions to decode the resources
1907+
BOOL isDictionary = FALSE;
1908+
DWORD nameTableRva = ReadResourceDictionary(pDir->VirtualAddress, pDir->VirtualAddress, lpType, &isDictionary);
18091909

1810-
HRSRC hResource = WszFindResourceEx(hModule, lpType, lpName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
1811-
if (!hResource)
1910+
if (!isDictionary)
1911+
return NULL;
1912+
1913+
if (nameTableRva == 0)
18121914
return NULL;
18131915

1814-
HGLOBAL hLoadedResource = ::LoadResource(hModule, hResource);
1815-
if (!hLoadedResource)
1916+
DWORD languageTableRva = ReadResourceDictionary(pDir->VirtualAddress, nameTableRva, lpName, &isDictionary);
1917+
if (!isDictionary)
18161918
return NULL;
18171919

1818-
PVOID pResource = ::LockResource(hLoadedResource);
1819-
if (!pResource)
1920+
if (languageTableRva == 0)
18201921
return NULL;
18211922

1822-
if (pSize != NULL)
1823-
*pSize = ::SizeofResource(hModule, hResource);
1923+
// This api is designed to find resources with LANGID = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
1924+
// This translates to LANGID 0 as the initial lookup point, which is sufficient for the needs of this api at this time
1925+
// (FindResource in the Windows api implements a large number of fallback paths which this api does not implement)
1926+
1927+
DWORD resourceDataEntryRva = ReadResourceDictionary(pDir->VirtualAddress, languageTableRva, 0, &isDictionary);
1928+
if (isDictionary) // This must not be a resource dictionary itself
1929+
return NULL;
1930+
1931+
if (resourceDataEntryRva == 0)
1932+
return NULL;
1933+
1934+
DWORD resourceDataRva = ReadResourceDataEntry(resourceDataEntryRva, pSize);
1935+
if (!CheckRva(resourceDataRva, *pSize))
1936+
{
1937+
*pSize = 0;
1938+
return NULL;
1939+
}
18241940

1825-
return pResource;
1941+
return (void*)GetRvaData(resourceDataRva);
18261942
}
1827-
#endif // FEATURE_PAL
18281943

18291944
BOOL PEDecoder::HasNativeHeader() const
18301945
{

0 commit comments

Comments
 (0)