summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Wrighton <davidwr@microsoft.com>2019-05-01 15:29:34 -0700
committerGitHub <noreply@github.com>2019-05-01 15:29:34 -0700
commit4d820df4437139275b7c05330dd98631db708802 (patch)
tree1fd8c1b6c43880f5dace1ef33952139ea010779f /src
parent75fd45860789ad54223e223ed04a24b2f2d2a8d5 (diff)
downloadcoreclr-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')
-rw-r--r--src/inc/pedecoder.h10
-rw-r--r--src/pal/inc/rt/palrt.h3
-rw-r--r--src/utilcode/pedecoder.cpp326
-rw-r--r--src/zap/zapheaders.cpp261
-rw-r--r--src/zap/zapheaders.h114
-rw-r--r--src/zap/zapimage.cpp2
-rw-r--r--src/zap/zapimage.h2
-rw-r--r--src/zap/zapnodetype.h2
8 files changed, 639 insertions, 81 deletions
diff --git a/src/inc/pedecoder.h b/src/inc/pedecoder.h
index 37051d96fd..5dff335427 100644
--- a/src/inc/pedecoder.h
+++ b/src/inc/pedecoder.h
@@ -106,6 +106,10 @@ inline CHECK CheckOverflow(RVA value1, COUNT_T value2)
typedef DPTR(class PEDecoder) PTR_PEDecoder;
+typedef bool (*PEDecoder_ResourceTypesCallbackFunction)(LPCWSTR lpType, void* context);
+typedef bool (*PEDecoder_ResourceNamesCallbackFunction)(LPCWSTR lpName, LPCWSTR lpType, void* context);
+typedef bool (*PEDecoder_ResourceCallbackFunction)(LPCWSTR lpName, LPCWSTR lpType, DWORD langid, BYTE* data, COUNT_T cbData, void* context);
+
class PEDecoder
{
public:
@@ -253,9 +257,9 @@ class PEDecoder
// Win32 resources
void *GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSize = NULL) const;
- private:
- DWORD ReadResourceDictionary(DWORD rvaOfResourceSection, DWORD rva, LPCWSTR name, BOOL *pIsDictionary) const;
- DWORD ReadResourceDataEntry(DWORD rva, COUNT_T *pSize) const;
+ bool EnumerateWin32ResourceTypes(PEDecoder_ResourceTypesCallbackFunction callback, void* context) const;
+ bool EnumerateWin32ResourceNames(LPCWSTR lpType, PEDecoder_ResourceNamesCallbackFunction callback, void* context) const;
+ bool EnumerateWin32Resources(LPCWSTR lpName, LPCWSTR lpType, PEDecoder_ResourceCallbackFunction callback, void* context) const;
public:
// COR header fields
diff --git a/src/pal/inc/rt/palrt.h b/src/pal/inc/rt/palrt.h
index 89f28e7c74..6502cf5891 100644
--- a/src/pal/inc/rt/palrt.h
+++ b/src/pal/inc/rt/palrt.h
@@ -1133,8 +1133,9 @@ typedef JIT_DEBUG_INFO JIT_DEBUG_INFO32, *LPJIT_DEBUG_INFO32;
typedef JIT_DEBUG_INFO JIT_DEBUG_INFO64, *LPJIT_DEBUG_INFO64;
/******************* resources ***************************************/
-
+#define IS_INTRESOURCE(_r) ((((ULONG_PTR)(_r)) >> 16) == 0)
#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
+#define MAKEINTRESOURCE(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
#define RT_RCDATA MAKEINTRESOURCE(10)
#define RT_VERSION MAKEINTRESOURCE(16)
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)
diff --git a/src/zap/zapheaders.cpp b/src/zap/zapheaders.cpp
index c931d3bfcb..cdf511b126 100644
--- a/src/zap/zapheaders.cpp
+++ b/src/zap/zapheaders.cpp
@@ -19,6 +19,9 @@
#include "zapmetadata.h"
#include "zapimport.h"
+#include <clr_std/vector>
+#include <clr_std/algorithm>
+
//
// IMAGE_COR20_HEADER
//
@@ -190,54 +193,252 @@ void ZapImage::SaveCodeManagerEntry()
// Needed for RT_VERSION.
#define MAKEINTRESOURCE(v) MAKEINTRESOURCEW(v)
-void ZapVersionResource::Save(ZapWriter * pZapWriter)
+void ZapWin32ResourceDirectory::Save(ZapWriter * pZapWriter)
{
- // Resources are binary-sorted tree structure. Since we are saving just one resource
- // the binary structure is degenerated link list. By convention, Windows uses three levels
- // for resources: Type, Name, Language.
+ //
+ // The IMAGE_RESOURCE_DIRECTORY resource data structure is followed by a number of IMAGE_RESOURCE_DIRECTORY_ENTRY entries, which can either
+ // point to other resource directories (RVAs to other ZapWin32ResourceDirectory nodes), or point to actual resource data (RVAs to a number
+ // of IMAGE_RESOURCE_DATA_ENTRY entries that immediately follow the IMAGE_RESOURCE_DIRECTORY_ENTRY entries).
+ //
- // See vctools\link\doc\pecoff.doc for detailed documentation of PE format.
+ //
+ // Sorting for resources is done in the following way accoring to the PE format specifications:
+ // 1) First, all the IMAGE_RESOURCE_DIRECTORY_ENTRY entries where the ID is a name string, sorted by names
+ // 2) Second, all the IMAGE_RESOURCE_DIRECTORY_ENTRY entries with non-string IDs, sorted by IDs.
+ //
+ struct ResourceSorter
+ {
+ bool operator() (DataOrSubDirectoryEntry& a, DataOrSubDirectoryEntry& b)
+ {
+ if (a.m_nameOrIdIsString && !b.m_nameOrIdIsString)
+ return true;
+ if (!a.m_nameOrIdIsString && b.m_nameOrIdIsString)
+ return false;
+ if (a.m_nameOrIdIsString)
+ return wcscmp(((ZapWin32ResourceString*)(a.m_pNameOrId))->GetString(), ((ZapWin32ResourceString*)(b.m_pNameOrId))->GetString()) < 0;
+ else
+ return a.m_pNameOrId < b.m_pNameOrId;
+ }
+ } resourceSorter;
+ std::sort(m_entries.begin(), m_entries.end(), resourceSorter);
- VersionResourceHeader header;
- ZeroMemory(&header, sizeof(header));
+ IMAGE_RESOURCE_DIRECTORY directory;
+ ZeroMemory(&directory, sizeof(IMAGE_RESOURCE_DIRECTORY));
+
+ for (auto& entry : m_entries)
+ {
+ if (entry.m_nameOrIdIsString)
+ directory.NumberOfNamedEntries++;
+ else
+ directory.NumberOfIdEntries++;
+ }
+ pZapWriter->Write(&directory, sizeof(IMAGE_RESOURCE_DIRECTORY));
- header.TypeDir.NumberOfIdEntries = 1;
- header.TypeEntry.Id = (USHORT)((ULONG_PTR)RT_VERSION);
- header.TypeEntry.OffsetToDirectory = offsetof(VersionResourceHeader, NameDir);
- header.TypeEntry.DataIsDirectory = 1;
+ // Offsets are based from the begining of the resources blob (see PE format documentation)
+ DWORD dataEntryRVA = this->GetRVA() - m_pWin32ResourceSection->GetRVA()
+ + sizeof(IMAGE_RESOURCE_DIRECTORY) +
+ (DWORD)m_entries.size() * sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
- header.NameDir.NumberOfIdEntries = 1;
- header.NameEntry.Id = 1;
- header.NameEntry.OffsetToDirectory = offsetof(VersionResourceHeader, LangDir);
- header.NameEntry.DataIsDirectory = 1;
+ for (auto& entry : m_entries)
+ {
+ IMAGE_RESOURCE_DIRECTORY_ENTRY dirEntry;
+ ZeroMemory(&dirEntry, sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY));
- header.LangDir.NumberOfIdEntries = 1;
- header.LangEntry.OffsetToDirectory = offsetof(VersionResourceHeader, DataEntry);
+ if (entry.m_nameOrIdIsString)
+ {
+ // Offsets are based from the begining of the resources blob (see PE format documentation)
+ dirEntry.NameOffset = ((ZapWin32ResourceString*)(entry.m_pNameOrId))->GetRVA() - m_pWin32ResourceSection->GetRVA();
+ dirEntry.NameIsString = true;
+ }
+ else
+ {
+ _ASSERT(IS_INTRESOURCE(entry.m_pNameOrId));
+ dirEntry.Id = (WORD)((ULONG_PTR)entry.m_pNameOrId & 0xffff);
+ }
- header.DataEntry.OffsetToData = m_pVersionData->GetRVA();
- header.DataEntry.Size = m_pVersionData->GetSize();
+ if (entry.m_dataIsSubDirectory)
+ {
+ // Offsets are based from the begining of the resources blob (see PE format documentation)
+ dirEntry.OffsetToDirectory = entry.m_pDataOrSubDirectory->GetRVA() - m_pWin32ResourceSection->GetRVA();
+ dirEntry.DataIsDirectory = true;
+ }
+ else
+ {
+ dirEntry.OffsetToData = dataEntryRVA;
+ dataEntryRVA += sizeof(IMAGE_RESOURCE_DATA_ENTRY);
+ }
- pZapWriter->Write(&header, sizeof(header));
+ pZapWriter->Write(&dirEntry, sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY));
+ }
+
+ for (auto& entry : m_entries)
+ {
+ if (entry.m_dataIsSubDirectory)
+ continue;
+
+ IMAGE_RESOURCE_DATA_ENTRY dataEntry;
+ ZeroMemory(&dataEntry, sizeof(IMAGE_RESOURCE_DATA_ENTRY));
+
+ dataEntry.OffsetToData = entry.m_pDataOrSubDirectory->GetRVA();
+ dataEntry.Size = entry.m_pDataOrSubDirectory->GetSize();
+
+ pZapWriter->Write(&dataEntry, sizeof(IMAGE_RESOURCE_DATA_ENTRY));
+ }
}
-void ZapImage::CopyWin32VersionResource()
+void ZapImage::CopyWin32Resources()
{
- // Copy the version resource over so it is easy to see in the dumps where the ngened module came from
- COUNT_T cbResourceData;
- PVOID pResourceData = m_ModuleDecoder.GetWin32Resource(MAKEINTRESOURCE(1), RT_VERSION, &cbResourceData);
+#ifdef FEATURE_PREJIT
+ if (!IsReadyToRunCompilation())
+ {
+ // When compiling a fragile NGEN image, in order to avoid the risk of regression, only copy the RT_VERSION resource over so it
+ // is easy to see in the dumps where the ngened module came from. For R2R, we copy all resources (new behavior).
+ COUNT_T cbResourceData;
+ PVOID pResourceData = m_ModuleDecoder.GetWin32Resource(MAKEINTRESOURCE(1), RT_VERSION, &cbResourceData);
+
+ if (!pResourceData || !cbResourceData)
+ return;
+
+ ZapBlob * pVersionData = new (GetHeap()) ZapBlobPtr(pResourceData, cbResourceData);
+
+ ZapWin32ResourceDirectory* pTypeDirectory = new (GetHeap()) ZapWin32ResourceDirectory(m_pWin32ResourceSection);
+ ZapWin32ResourceDirectory* pNameDirectory = new (GetHeap()) ZapWin32ResourceDirectory(m_pWin32ResourceSection);
+ ZapWin32ResourceDirectory* pLanguageDirectory = new (GetHeap()) ZapWin32ResourceDirectory(m_pWin32ResourceSection);
+
+ pTypeDirectory->AddEntry(RT_VERSION, false, pNameDirectory, true);
+ pNameDirectory->AddEntry(MAKEINTRESOURCE(1), false, pLanguageDirectory, true);
+ pLanguageDirectory->AddEntry(MAKEINTRESOURCE(0), false, pVersionData, false);
+
+ pTypeDirectory->PlaceNodeAndDependencies(m_pWin32ResourceSection);
+
+ m_pWin32ResourceSection->Place(pVersionData);
+
+ SetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE, m_pWin32ResourceSection);
- if (!pResourceData || !cbResourceData)
return;
+ }
+#endif
+
+ class ResourceEnumerationCallback
+ {
+ ZapImage* m_pZapImage;
+ PEDecoder* m_pModuleDecoder;
+
+ std::vector<ZapNode*> m_dataEntries;
+ std::vector<ZapBlob*> m_stringEntries;
+
+ ZapWin32ResourceDirectory* m_pRootDirectory;
+ ZapWin32ResourceDirectory* m_pCurrentTypesDirectory;
+ ZapWin32ResourceDirectory* m_pCurrentNamesDirectory;
+
+ bool AddResource(LPCWSTR lpszResourceName, LPCWSTR lpszResourceType, DWORD langID, BYTE* pResourceData, COUNT_T cbResourceData)
+ {
+ ZapBlob* pDataBlob = new (m_pZapImage->GetHeap()) ZapBlobPtr(pResourceData, cbResourceData);
+ m_dataEntries.push_back(pDataBlob);
+
+ m_pCurrentNamesDirectory->AddEntry((PVOID)(ULONG_PTR)langID, false, pDataBlob, false);
+
+ return true;
+ }
+
+ ZapWin32ResourceDirectory* CreateResourceSubDirectory(ZapWin32ResourceDirectory* pRootDir, LPCWSTR pNameOrId)
+ {
+ bool nameIsString = !IS_INTRESOURCE(pNameOrId);
+
+ PVOID pIdOrNameZapNode = (PVOID)pNameOrId;
+ if (nameIsString)
+ {
+ pIdOrNameZapNode = new (m_pZapImage->GetHeap()) ZapWin32ResourceString(pNameOrId);
+ m_stringEntries.push_back((ZapBlob*)pIdOrNameZapNode);
+ }
+
+ ZapWin32ResourceDirectory* pResult = new (m_pZapImage->GetHeap()) ZapWin32ResourceDirectory(m_pZapImage->m_pWin32ResourceSection);
+ pRootDir->AddEntry(pIdOrNameZapNode, nameIsString, pResult, true);
+
+ return pResult;
+ }
+
+ public:
+ ResourceEnumerationCallback(PEDecoder* pModuleDecoder, ZapImage* pZapImage)
+ : m_pZapImage(pZapImage), m_pModuleDecoder(pModuleDecoder)
+ {
+ m_pRootDirectory = new (pZapImage->GetHeap()) ZapWin32ResourceDirectory(pZapImage->m_pWin32ResourceSection);
+ m_pCurrentTypesDirectory = m_pCurrentNamesDirectory = NULL;
+ }
- ZapBlob * pVersionData = new (GetHeap()) ZapBlobPtr(pResourceData, cbResourceData);
+ static bool EnumResourcesCallback(LPCWSTR lpszResourceName, LPCWSTR lpszResourceType, DWORD langID, BYTE* data, COUNT_T cbData, void *context)
+ {
+ ResourceEnumerationCallback* pCallback = (ResourceEnumerationCallback*)context;
+ // Third level in the enumeration: resources by langid for each name/type.
- ZapVersionResource * pVersionResource = new (GetHeap()) ZapVersionResource(pVersionData);
+ // Note that this callback is not equivalent to the Windows enumeration apis as this api provides the resource data
+ // itself, and the resources are guaranteed to be present directly in the associated binary. This does not exactly
+ // match the Windows api, but it is exactly what we want when copying all resource data.
- m_pWin32ResourceSection->Place(pVersionResource);
- m_pWin32ResourceSection->Place(pVersionData);
+ return pCallback->AddResource(lpszResourceName, lpszResourceType, langID, data, cbData);
+ }
+
+ static bool EnumResourceNamesCallback(LPCWSTR lpszResourceName, LPCWSTR lpszResourceType, void *context)
+ {
+ // Second level in the enumeration: resources by names for each resource type
+
+ ResourceEnumerationCallback* pCallback = (ResourceEnumerationCallback*)context;
+ pCallback->m_pCurrentNamesDirectory = pCallback->CreateResourceSubDirectory(pCallback->m_pCurrentTypesDirectory, lpszResourceName);
+
+ return pCallback->m_pModuleDecoder->EnumerateWin32Resources(lpszResourceName, lpszResourceType, ResourceEnumerationCallback::EnumResourcesCallback, context);
+ }
- SetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE, m_pWin32ResourceSection);
+ static bool EnumResourceTypesCallback(LPCWSTR lpszType, void *context)
+ {
+ // First level in the enumeration: resources by types
+
+ // Skip IBC resources
+ if (!IS_INTRESOURCE(lpszType) && (wcscmp(lpszType, W("IBC")) == 0))
+ return true;
+
+ ResourceEnumerationCallback* pCallback = (ResourceEnumerationCallback*)context;
+ pCallback->m_pCurrentTypesDirectory = pCallback->CreateResourceSubDirectory(pCallback->m_pRootDirectory, lpszType);
+
+ return pCallback->m_pModuleDecoder->EnumerateWin32ResourceNames(lpszType, ResourceEnumerationCallback::EnumResourceNamesCallback, context);
+ }
+
+ void PlaceResourceNodes(ZapVirtualSection* pWin32ResourceSection)
+ {
+ m_pRootDirectory->PlaceNodeAndDependencies(pWin32ResourceSection);
+
+ //
+ // These strings are stored together after the last Resource Directory entry and before the first Resource Data entry. This
+ // minimizes the impact of these variable-length strings on the alignment of the fixed-size directory entries
+ //
+ for (auto& entry : m_stringEntries)
+ pWin32ResourceSection->Place(entry);
+
+ for (auto& entry : m_dataEntries)
+ pWin32ResourceSection->Place(entry);
+ }
+ };
+
+ ResourceEnumerationCallback callbacks(&m_ModuleDecoder, this);
+
+ HMODULE hModule = (HMODULE)dac_cast<TADDR>(m_ModuleDecoder.GetBase());
+
+ //
+ // Resources are binary-sorted tree structure. By convention, Windows uses three levels
+ // for resources: Type, Name, Language. To reduces the overall complexity, we'll copy and store resources in the
+ // "neutral" language only.
+ //
+
+ if (!m_ModuleDecoder.EnumerateWin32ResourceTypes(ResourceEnumerationCallback::EnumResourceTypesCallback, &callbacks))
+ {
+ ThrowHR(E_FAIL);
+ }
+ else
+ {
+ callbacks.PlaceResourceNodes(m_pWin32ResourceSection);
+
+ SetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE, m_pWin32ResourceSection);
+ }
}
#undef MAKEINTRESOURCE
diff --git a/src/zap/zapheaders.h b/src/zap/zapheaders.h
index f966ced1b1..415ea4ef4a 100644
--- a/src/zap/zapheaders.h
+++ b/src/zap/zapheaders.h
@@ -13,6 +13,8 @@
#ifndef __ZAPHEADERS_H__
#define __ZAPHEADERS_H__
+#include <clr_std/vector>
+
//
// IMAGE_COR20_HEADER
//
@@ -184,33 +186,99 @@ public:
};
//
-// Version Resource
+// Win32 Resources
//
-class ZapVersionResource : public ZapNode
+class ZapWin32ResourceString : public ZapNode
{
- ZapNode * m_pVersionData;
+ //
+ // This ZapNode maps the IMAGE_RESOURCE_DIR_STRING_U resource data structure for storing strings.
+ //
+
+ LPWSTR m_pString;
public:
- ZapVersionResource(ZapNode * pVersionData)
- : m_pVersionData(pVersionData)
+ ZapWin32ResourceString(LPCWSTR pString)
+ {
+ size_t strLen = wcslen(pString);
+ _ASSERT(pString != NULL && strLen < 0xffff);
+
+ m_pString = new WCHAR[strLen + 1];
+ wcscpy(m_pString, pString);
+ m_pString[strLen] = L'\0';
+ }
+
+ LPCWSTR GetString() { return m_pString; }
+
+ virtual DWORD GetSize()
+ {
+ return sizeof(WORD) + sizeof(WCHAR) * (DWORD)wcslen(m_pString);
+ }
+
+ virtual UINT GetAlignment()
+ {
+ return sizeof(WORD);
+ }
+
+ virtual ZapNodeType GetType()
{
+ return ZapNodeType_Blob;
}
- struct VersionResourceHeader {
- IMAGE_RESOURCE_DIRECTORY TypeDir;
- IMAGE_RESOURCE_DIRECTORY_ENTRY TypeEntry;
- IMAGE_RESOURCE_DIRECTORY NameDir;
- IMAGE_RESOURCE_DIRECTORY_ENTRY NameEntry;
- IMAGE_RESOURCE_DIRECTORY LangDir;
- IMAGE_RESOURCE_DIRECTORY_ENTRY LangEntry;
- IMAGE_RESOURCE_DATA_ENTRY DataEntry;
- CHAR Data[0];
+ virtual void Save(ZapWriter * pZapWriter)
+ {
+ WORD size = (WORD)wcslen(m_pString);
+ pZapWriter->Write(&size, sizeof(WORD));
+ pZapWriter->Write((PVOID)m_pString, sizeof(WCHAR) * size);
+ }
+};
+
+class ZapWin32ResourceDirectory : public ZapNode
+{
+ //
+ // This ZapNode maps the IMAGE_RESOURCE_DIRECTORY resource data structure for storing a resource directory. Each directory
+ // is then followed by a number of IMAGE_RESOURCE_DIRECTORY_ENTRY entries, which can either point to other resource directories (RVAs
+ // to other ZapWin32ResourceDirectory nodes), or point to actual resource data (RVAs to a number of IMAGE_RESOURCE_DATA_ENTRY entries
+ // that immediately follow the IMAGE_RESOURCE_DIRECTORY_ENTRY entries).
+ //
+ // Refer to the PE resources format for more information (https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-rsrc-section)
+ //
+
+ struct DataOrSubDirectoryEntry
+ {
+ PVOID m_pNameOrId;
+ bool m_nameOrIdIsString;
+ ZapNode* m_pDataOrSubDirectory;
+ bool m_dataIsSubDirectory;
};
+ std::vector<DataOrSubDirectoryEntry> m_entries;
+ ZapVirtualSection* m_pWin32ResourceSection;
+
+public:
+ ZapWin32ResourceDirectory(ZapVirtualSection* pWin32ResourceSection)
+ : m_pWin32ResourceSection(pWin32ResourceSection)
+ { }
+
+ void AddEntry(PVOID pNameOrId, bool nameOrIdIsString, ZapNode* pDataOrSubDirectory, bool dataIsSubDirectory)
+ {
+ DataOrSubDirectoryEntry entry;
+ entry.m_pDataOrSubDirectory = pDataOrSubDirectory;
+ entry.m_dataIsSubDirectory = dataIsSubDirectory;
+ entry.m_pNameOrId = pNameOrId;
+ entry.m_nameOrIdIsString = nameOrIdIsString;
+
+ m_entries.push_back(entry);
+ }
virtual DWORD GetSize()
{
- return sizeof(VersionResourceHeader);
+ DWORD size = sizeof(IMAGE_RESOURCE_DIRECTORY) + (DWORD)m_entries.size() * sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
+ for (auto& entry : m_entries)
+ {
+ if (!entry.m_dataIsSubDirectory)
+ size += sizeof(IMAGE_RESOURCE_DATA_ENTRY);
+ }
+ return size;
}
virtual UINT GetAlignment()
@@ -220,7 +288,21 @@ public:
virtual ZapNodeType GetType()
{
- return ZapNodeType_VersionResource;
+ return ZapNodeType_Win32Resources;
+ }
+
+ void PlaceNodeAndDependencies(ZapVirtualSection* pWin32ResourceSection)
+ {
+ pWin32ResourceSection->Place(this);
+
+ for (auto& entry : m_entries)
+ {
+ if (entry.m_dataIsSubDirectory)
+ {
+ ZapWin32ResourceDirectory* pSubDirNode = (ZapWin32ResourceDirectory*)entry.m_pDataOrSubDirectory;
+ pSubDirNode->PlaceNodeAndDependencies(pWin32ResourceSection);
+ }
+ }
}
virtual void Save(ZapWriter * pZapWriter);
diff --git a/src/zap/zapimage.cpp b/src/zap/zapimage.cpp
index aa661f53eb..50df05ff6f 100644
--- a/src/zap/zapimage.cpp
+++ b/src/zap/zapimage.cpp
@@ -1496,7 +1496,7 @@ void ZapImage::OutputTables()
}
CopyDebugDirEntry();
- CopyWin32VersionResource();
+ CopyWin32Resources();
if (m_pILMetaData != NULL)
{
diff --git a/src/zap/zapimage.h b/src/zap/zapimage.h
index 832f2b198e..d5317a31e3 100644
--- a/src/zap/zapimage.h
+++ b/src/zap/zapimage.h
@@ -599,7 +599,7 @@ private:
void OutputManifestMetadataForReadyToRun();
void CopyDebugDirEntry();
- void CopyWin32VersionResource();
+ void CopyWin32Resources();
void OutputManifestMetadata();
void OutputTables();
diff --git a/src/zap/zapnodetype.h b/src/zap/zapnodetype.h
index ae7d58a937..82bad5b692 100644
--- a/src/zap/zapnodetype.h
+++ b/src/zap/zapnodetype.h
@@ -35,7 +35,7 @@ enum ZapNodeType {
ZapNodeType_CodeManagerEntry,
ZapNodeType_MetaData,
ZapNodeType_DebugDirectory,
- ZapNodeType_VersionResource,
+ ZapNodeType_Win32Resources,
// PlaceHolders