diff options
author | David Wrighton <davidwr@microsoft.com> | 2019-05-01 15:29:34 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-01 15:29:34 -0700 |
commit | 4d820df4437139275b7c05330dd98631db708802 (patch) | |
tree | 1fd8c1b6c43880f5dace1ef33952139ea010779f /src/utilcode | |
parent | 75fd45860789ad54223e223ed04a24b2f2d2a8d5 (diff) | |
download | coreclr-4d820df4437139275b7c05330dd98631db708802.tar.gz coreclr-4d820df4437139275b7c05330dd98631db708802.tar.bz2 coreclr-4d820df4437139275b7c05330dd98631db708802.zip |
Copy all win32 resources (#24308)
- Add crossgen test to verify file version is preserved
- Add support for general win32 resource copying to ReadyToRun
- Copy all resources
Diffstat (limited to 'src/utilcode')
-rw-r--r-- | src/utilcode/pedecoder.cpp | 326 |
1 files changed, 298 insertions, 28 deletions
diff --git a/src/utilcode/pedecoder.cpp b/src/utilcode/pedecoder.cpp index 24cde95441..411b462a94 100644 --- a/src/utilcode/pedecoder.cpp +++ b/src/utilcode/pedecoder.cpp @@ -1790,26 +1790,73 @@ void PEDecoder::LayoutILOnly(void *base, BOOL allowFullPE) const #endif // #ifndef DACCESS_COMPILE -DWORD PEDecoder::ReadResourceDictionary(DWORD rvaOfResourceSection, DWORD rva, LPCWSTR name, BOOL *pIsDictionary) const +bool ReadResourceDirectoryHeader(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, DWORD rva, IMAGE_RESOURCE_DIRECTORY_ENTRY** ppDirectoryEntries, IMAGE_RESOURCE_DIRECTORY **ppResourceDirectory) { - *pIsDictionary = FALSE; + if (!pDecoder->CheckRva(rva, sizeof(IMAGE_RESOURCE_DIRECTORY))) + { + return false; + } - if (!CheckRva(rva, sizeof(IMAGE_RESOURCE_DIRECTORY))) + *ppResourceDirectory = (IMAGE_RESOURCE_DIRECTORY *)pDecoder->GetRvaData(rva); + + // Check to see if entire resource directory is accessible + if (!pDecoder->CheckRva(rva + sizeof(IMAGE_RESOURCE_DIRECTORY), + (sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * (*ppResourceDirectory)->NumberOfNamedEntries) + + (sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * (*ppResourceDirectory)->NumberOfIdEntries))) { - return 0; + return false; } - IMAGE_RESOURCE_DIRECTORY *pResourceDirectory = (IMAGE_RESOURCE_DIRECTORY *)GetRvaData(rva); + *ppDirectoryEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)pDecoder->GetRvaData(rva + sizeof(IMAGE_RESOURCE_DIRECTORY)); + return true; +} + +bool ReadNameFromResourceDirectoryEntry(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, IMAGE_RESOURCE_DIRECTORY_ENTRY* pDirectoryEntries, DWORD iEntry, DWORD *pNameUInt, WCHAR **pNameStr) +{ + *pNameStr = NULL; + *pNameUInt = 0; - // Check to see if entire resource dictionary is accessible - if (!CheckRva(rva + sizeof(IMAGE_RESOURCE_DIRECTORY), - (sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * pResourceDirectory->NumberOfNamedEntries) + - (sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * pResourceDirectory->NumberOfIdEntries))) + if (!IS_INTRESOURCE(pDirectoryEntries[iEntry].Name)) { - return 0; + DWORD entryName = pDirectoryEntries[iEntry].Name; + if (!(entryName & IMAGE_RESOURCE_NAME_IS_STRING)) + return false; + DWORD entryNameRva = (entryName & ~IMAGE_RESOURCE_NAME_IS_STRING) + rvaOfResourceSection; + + if (!pDecoder->CheckRva(entryNameRva, sizeof(WORD))) + return false; + + size_t entryNameLen = *(WORD*)pDecoder->GetRvaData(entryNameRva); + if (!pDecoder->CheckRva(entryNameRva, (COUNT_T)(sizeof(WORD) * (1 + entryNameLen)))) + return false; + *pNameStr = new(nothrow) WCHAR[entryNameLen + 1]; + if ((*pNameStr) == NULL) + return false; + memcpy((*pNameStr), (WCHAR*)pDecoder->GetRvaData(entryNameRva + sizeof(WORD)), entryNameLen * sizeof(WCHAR)); + (*pNameStr)[entryNameLen] = 0; + } + else + { + DWORD name = pDirectoryEntries[iEntry].Name; + if (!IS_INTRESOURCE(name)) + return false; + + *pNameUInt = name; } - IMAGE_RESOURCE_DIRECTORY_ENTRY* pDirectoryEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)GetRvaData(rva + sizeof(IMAGE_RESOURCE_DIRECTORY)); + return true; +} + +DWORD ReadResourceDirectory(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, DWORD rva, LPCWSTR name, BOOL *pisDirectory) +{ + *pisDirectory = FALSE; + + IMAGE_RESOURCE_DIRECTORY* pResourceDirectory; + IMAGE_RESOURCE_DIRECTORY_ENTRY* pDirectoryEntries; + if (!ReadResourceDirectoryHeader(pDecoder, rvaOfResourceSection, rva, &pDirectoryEntries, &pResourceDirectory)) + { + return 0; + } // A fast implementation of resource lookup uses a binary search, but our needs are simple, and a linear search // is easier to prove correct, so do that instead. @@ -1819,7 +1866,7 @@ DWORD PEDecoder::ReadResourceDictionary(DWORD rvaOfResourceSection, DWORD rva, L { BOOL foundEntry = FALSE; - if (((UINT_PTR)name) <= 0xFFFF) + if (IS_INTRESOURCE(name)) { // name is id if (pDirectoryEntries[iEntry].Name == (DWORD)(SIZE_T)name) @@ -1834,24 +1881,24 @@ DWORD PEDecoder::ReadResourceDictionary(DWORD rvaOfResourceSection, DWORD rva, L DWORD entryNameRva = (entryName & ~IMAGE_RESOURCE_NAME_IS_STRING) + rvaOfResourceSection; - if (!CheckRva(entryNameRva, sizeof(WORD))) + if (!pDecoder->CheckRva(entryNameRva, sizeof(WORD))) return 0; - size_t entryNameLen = *(WORD*)GetRvaData(entryNameRva); + size_t entryNameLen = *(WORD*)pDecoder->GetRvaData(entryNameRva); if (wcslen(name) != entryNameLen) continue; - if (!CheckRva(entryNameRva, (COUNT_T)(sizeof(WORD) * (1 + entryNameLen)))) + if (!pDecoder->CheckRva(entryNameRva, (COUNT_T)(sizeof(WORD) * (1 + entryNameLen)))) return 0; - if (memcmp((WCHAR*)GetRvaData(entryNameRva + sizeof(WORD)), name, entryNameLen * sizeof(WCHAR)) == 0) + if (memcmp((WCHAR*)pDecoder->GetRvaData(entryNameRva + sizeof(WORD)), name, entryNameLen * sizeof(WCHAR)) == 0) foundEntry = TRUE; } if (!foundEntry) continue; - *pIsDictionary = !!(pDirectoryEntries[iEntry].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY); + *pisDirectory = !!(pDirectoryEntries[iEntry].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY); DWORD offsetToData = pDirectoryEntries[iEntry].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY; DWORD dataRva = rvaOfResourceSection + offsetToData; return dataRva; @@ -1860,16 +1907,16 @@ DWORD PEDecoder::ReadResourceDictionary(DWORD rvaOfResourceSection, DWORD rva, L return 0; } -DWORD PEDecoder::ReadResourceDataEntry(DWORD rva, COUNT_T *pSize) const +DWORD ReadResourceDataEntry(const PEDecoder *pDecoder, DWORD rva, COUNT_T *pSize) { *pSize = 0; - if (!CheckRva(rva, sizeof(IMAGE_RESOURCE_DATA_ENTRY))) + if (!pDecoder->CheckRva(rva, sizeof(IMAGE_RESOURCE_DATA_ENTRY))) { return 0; } - IMAGE_RESOURCE_DATA_ENTRY *pDataEntry = (IMAGE_RESOURCE_DATA_ENTRY *)GetRvaData(rva); + IMAGE_RESOURCE_DATA_ENTRY *pDataEntry = (IMAGE_RESOURCE_DATA_ENTRY *)pDecoder->GetRvaData(rva); *pSize = pDataEntry->Size; return pDataEntry->OffsetToData; } @@ -1898,17 +1945,17 @@ void * PEDecoder::GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSiz if (pDir->VirtualAddress == 0) return NULL; - BOOL isDictionary = FALSE; - DWORD nameTableRva = ReadResourceDictionary(pDir->VirtualAddress, pDir->VirtualAddress, lpType, &isDictionary); + BOOL isDirectory = FALSE; + DWORD nameTableRva = ReadResourceDirectory(this, pDir->VirtualAddress, pDir->VirtualAddress, lpType, &isDirectory); - if (!isDictionary) + if (!isDirectory) return NULL; if (nameTableRva == 0) return NULL; - DWORD languageTableRva = ReadResourceDictionary(pDir->VirtualAddress, nameTableRva, lpName, &isDictionary); - if (!isDictionary) + DWORD languageTableRva = ReadResourceDirectory(this, pDir->VirtualAddress, nameTableRva, lpName, &isDirectory); + if (!isDirectory) return NULL; if (languageTableRva == 0) @@ -1918,14 +1965,14 @@ void * PEDecoder::GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSiz // This translates to LANGID 0 as the initial lookup point, which is sufficient for the needs of this api at this time // (FindResource in the Windows api implements a large number of fallback paths which this api does not implement) - DWORD resourceDataEntryRva = ReadResourceDictionary(pDir->VirtualAddress, languageTableRva, 0, &isDictionary); - if (isDictionary) // This must not be a resource dictionary itself + DWORD resourceDataEntryRva = ReadResourceDirectory(this, pDir->VirtualAddress, languageTableRva, 0, &isDirectory); + if (isDirectory) // This must not be a resource directory itself return NULL; if (resourceDataEntryRva == 0) return NULL; - DWORD resourceDataRva = ReadResourceDataEntry(resourceDataEntryRva, pSize); + DWORD resourceDataRva = ReadResourceDataEntry(this, resourceDataEntryRva, pSize); if (!CheckRva(resourceDataRva, *pSize)) { *pSize = 0; @@ -1935,6 +1982,229 @@ void * PEDecoder::GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSiz return (void*)GetRvaData(resourceDataRva); } +typedef bool (*PEDecoder_EnumerateResourceTableFunction)(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context); + +struct ResourceEnumerateNamesState +{ + PEDecoder_ResourceNamesCallbackFunction namesCallback; + PEDecoder_ResourceCallbackFunction langIDcallback; + void *context; + LPCWSTR nameType; + LPCWSTR nameName; + PEDecoder_EnumerateResourceTableFunction callbackPerName; + PEDecoder_EnumerateResourceTableFunction callbackPerLangID; +}; + +struct ResourceEnumerateTypesState +{ + PEDecoder_ResourceTypesCallbackFunction callback; + void *context; + LPCWSTR nameType; +}; + +bool EnumerateWin32ResourceTable(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, DWORD rvaOfResourceTable, PEDecoder_EnumerateResourceTableFunction resourceTableEnumerator, void *context) +{ + IMAGE_RESOURCE_DIRECTORY* pResourceDirectory; + IMAGE_RESOURCE_DIRECTORY_ENTRY* pDirectoryEntries; + if (!ReadResourceDirectoryHeader(pDecoder, rvaOfResourceSection, rvaOfResourceTable, &pDirectoryEntries, &pResourceDirectory)) + { + return false; + } + + DWORD iEntryCount = (DWORD)pResourceDirectory->NumberOfNamedEntries + (DWORD)pResourceDirectory->NumberOfIdEntries; + + for (DWORD iEntry = 0; iEntry < iEntryCount; iEntry++) + { + DWORD nameUInt; + NewArrayHolder<WCHAR> nameString; + if (!ReadNameFromResourceDirectoryEntry(pDecoder, rvaOfResourceSection, pDirectoryEntries, iEntry, &nameUInt, &nameString)) + return false; + + LPCWSTR name = MAKEINTRESOURCEW(nameUInt); + if (nameString != NULL) + name = &nameString[0]; + + bool isDirectory = !!(pDirectoryEntries[iEntry].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY); + DWORD offsetToData = pDirectoryEntries[iEntry].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY; + DWORD dataRva = rvaOfResourceSection + offsetToData; + + if (!resourceTableEnumerator(pDecoder, rvaOfResourceSection, isDirectory, name, dataRva, context)) + return false; + } + + return true; +} + +bool DoesResourceNameMatch(LPCWSTR nameA, LPCWSTR nameB) +{ + bool foundEntry = false; + + if (IS_INTRESOURCE(nameA)) + { + // name is id + if (nameA == nameB) + foundEntry = true; + } + else + { + // name is a string. + + // Check for name enumerated is an id. If so, it doesn't match, skip to next. + if (IS_INTRESOURCE(nameB)) + return false; + else + foundEntry = !wcscmp(nameB, nameA); + } + + return foundEntry; +} + +bool EnumerateLangIDs(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context) +{ + ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context; + if (isDirectory) + return false; + + // Only LangIDs are permitted here + if (!IS_INTRESOURCE(name)) + return false; + + if (dataRVA == 0) + return false; + + COUNT_T cbData; + DWORD resourceDataRva = ReadResourceDataEntry(pDecoder, dataRVA, &cbData); + if (!pDecoder->CheckRva(resourceDataRva, cbData)) + { + return false; + } + + BYTE *pData = (BYTE*)pDecoder->GetRvaData(resourceDataRva); + + return state->langIDcallback(state->nameName, state->nameType, (DWORD)name, pData, cbData, state->context); +} + + +bool EnumerateNames(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context) +{ + ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context; + if (!isDirectory) + return false; + + state->nameName = name; + return state->namesCallback(state->nameName, state->nameType, state->context); +} + +bool EnumerateNamesForLangID(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context) +{ + ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context; + if (!isDirectory) + return false; + + bool foundEntry = DoesResourceNameMatch(state->nameName, name); + + if (foundEntry) + return EnumerateWin32ResourceTable(pDecoder, rvaOfResourceSection, dataRVA, state->callbackPerLangID, context); + else + return true; // Keep scanning +} + + +bool EnumerateTypes(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context) +{ + ResourceEnumerateTypesState *state = (ResourceEnumerateTypesState*)context; + if (!isDirectory) + return false; + + state->nameType = name; + return state->callback(name, state->context); +} + +bool EnumerateTypesForNames(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context) +{ + ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context; + if (!isDirectory) + return false; + + bool foundEntry = DoesResourceNameMatch(state->nameType, name); + + if (foundEntry) + return EnumerateWin32ResourceTable(pDecoder, rvaOfResourceSection, dataRVA, state->callbackPerName, context); + else + return true; // Keep scanning +} + + +bool PEDecoder::EnumerateWin32ResourceTypes(PEDecoder_ResourceTypesCallbackFunction callback, void* context) const +{ + if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE)) + return true; + + COUNT_T resourceDataSize = 0; + IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE); + + if (pDir->VirtualAddress == 0) + return true; + + DWORD rvaOfResourceSection = pDir->VirtualAddress; + + ResourceEnumerateTypesState state; + state.context = context; + state.callback = callback; + + return EnumerateWin32ResourceTable(this, rvaOfResourceSection, rvaOfResourceSection, EnumerateTypes, &state); +} + +bool PEDecoder::EnumerateWin32ResourceNames(LPCWSTR lpType, PEDecoder_ResourceNamesCallbackFunction callback, void* context) const +{ + if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE)) + return true; + + COUNT_T resourceDataSize = 0; + IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE); + + if (pDir->VirtualAddress == 0) + return true; + + DWORD rvaOfResourceSection = pDir->VirtualAddress; + + ResourceEnumerateNamesState state; + state.context = context; + state.namesCallback = callback; + state.langIDcallback = NULL; + state.nameType = lpType; + state.nameName = NULL; + state.callbackPerName = EnumerateNames; + state.callbackPerLangID = NULL; + + return EnumerateWin32ResourceTable(this, rvaOfResourceSection, rvaOfResourceSection, EnumerateTypesForNames, &state); +} + +bool PEDecoder::EnumerateWin32Resources(LPCWSTR lpName, LPCWSTR lpType, PEDecoder_ResourceCallbackFunction callback, void* context) const +{ + if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE)) + return true; + + COUNT_T resourceDataSize = 0; + IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE); + + if (pDir->VirtualAddress == 0) + return true; + + DWORD rvaOfResourceSection = pDir->VirtualAddress; + + ResourceEnumerateNamesState state; + state.context = context; + state.namesCallback = NULL; + state.langIDcallback = callback; + state.nameType = lpType; + state.nameName = lpName; + state.callbackPerName = EnumerateNamesForLangID; + state.callbackPerLangID = EnumerateLangIDs; + + return EnumerateWin32ResourceTable(this, rvaOfResourceSection, rvaOfResourceSection, EnumerateTypesForNames, &state); +} + BOOL PEDecoder::HasNativeHeader() const { CONTRACT(BOOL) |