_data[offset + 3] = (byte)((emit >> 24) & 0xFF);
}
+ public void EmitUInt(Reservation reservation, uint emit)
+ {
+ int offset = ReturnReservationTicket(reservation);
+ _data[offset] = (byte)(emit & 0xFF);
+ _data[offset + 1] = (byte)((emit >> 8) & 0xFF);
+ _data[offset + 2] = (byte)((emit >> 16) & 0xFF);
+ _data[offset + 3] = (byte)((emit >> 24) & 0xFF);
+ }
+
public void EmitReloc(ISymbolNode symbol, RelocType relocType, int delta = 0)
{
#if DEBUG
{
_definedSymbols.Add(node);
}
+
+ public void PadAlignment(int align)
+ {
+ Debug.Assert((align == 2) || (align == 4) || (align == 8) || (align == 16));
+ int misalignment = _data.Count & (align - 1);
+ if (misalignment != 0)
+ {
+ EmitZeros(align - misalignment);
+ }
+ }
}
}
r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size);
+ if (_nodeFactory.Win32ResourcesNode != null)
+ {
+ Debug.Assert(_nodeFactory.Win32ResourcesNode.Size != 0);
+ r2rPeBuilder.SetWin32Resources(_nodeFactory.Win32ResourcesNode, _nodeFactory.Win32ResourcesNode.Size);
+ }
+
using (var peStream = File.Create(_objectFilePath))
{
r2rPeBuilder.Write(peStream);
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+
+using Internal.NativeFormat;
+using Internal.Text;
+using ILCompiler.Win32Resources;
+using Internal.TypeSystem;
+using Internal.TypeSystem.Ecma;
+
+namespace ILCompiler.DependencyAnalysis.ReadyToRun
+{
+ public class Win32ResourcesNode : ObjectNode, ISymbolDefinitionNode
+ {
+ private ResourceData _resourceData;
+ private int _size;
+
+ public Win32ResourcesNode(ResourceData resourceData)
+ {
+ _resourceData = resourceData;
+ }
+
+ public override ObjectNodeSection Section => ObjectNodeSection.TextSection;
+
+ public override bool IsShareable => false;
+
+ public override int ClassCode => 315358339;
+
+ public override bool StaticDependenciesAreComputed => true;
+
+ public int Offset => 0;
+
+ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
+ {
+ sb.Append("____Win32Resources");
+ }
+
+ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
+ {
+ ObjectDataBuilder builder = new ObjectDataBuilder();
+ builder.AddSymbol(this);
+ _resourceData.WriteResources(this, ref builder);
+ _size = builder.CountBytes;
+ return builder.ToObjectData();
+ }
+
+ protected override string GetName(NodeFactory context)
+ {
+ return "____Win32Resources";
+ }
+
+ public int Size => _size;
+ }
+}
using Internal.TypeSystem;
using Internal.Text;
using Internal.TypeSystem.Ecma;
+using ILCompiler.Win32Resources;
namespace ILCompiler.DependencyAnalysis
{
NameMangler nameMangler,
ModuleTokenResolver moduleTokenResolver,
SignatureContext signatureContext,
- CopiedCorHeaderNode corHeaderNode)
+ CopiedCorHeaderNode corHeaderNode,
+ ResourceData win32Resources)
: base(context,
compilationModuleGroup,
nameMangler,
Resolver = moduleTokenResolver;
InputModuleContext = signatureContext;
CopiedCorHeaderNode = corHeaderNode;
+ if (!win32Resources.IsEmpty)
+ Win32ResourcesNode = new Win32ResourcesNode(win32Resources);
}
public SignatureContext InputModuleContext;
public CopiedCorHeaderNode CopiedCorHeaderNode;
+ public Win32ResourcesNode Win32ResourcesNode;
+
public HeaderNode Header;
public RuntimeFunctionsTableNode RuntimeFunctionsTable;
graph.AddRoot(Header, "ReadyToRunHeader is always generated");
graph.AddRoot(CopiedCorHeaderNode, "MSIL COR header is always generated");
+ if (Win32ResourcesNode != null)
+ graph.AddRoot(Win32ResourcesNode, "Win32 Resources are placed if not empty");
+
MetadataManager.AttachToDependencyGraph(graph);
}
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysis.ReadyToRun;
using ILCompiler.DependencyAnalysisFramework;
-
+using ILCompiler.Win32Resources;
using Internal.IL;
using Internal.JitInterface;
using Internal.TypeSystem;
SignatureContext signatureContext = new SignatureContext(_inputModule, moduleTokenResolver);
CopiedCorHeaderNode corHeaderNode = new CopiedCorHeaderNode(_inputModule);
+ // Produce a ResourceData where the IBC PROFILE_DATA entry has been filtered out
+ ResourceData win32Resources = new ResourceData(_inputModule, (object type, object name, ushort language) =>
+ {
+ if (!(type is string) || !(name is string))
+ return true;
+ if (language != 0)
+ return true;
+
+ string typeString = (string)type;
+ string nameString = (string)name;
+
+ if ((typeString == "IBC") && (nameString == "PROFILE_DATA"))
+ return false;
+
+ return true;
+ });
+
ReadyToRunCodegenNodeFactory factory = new ReadyToRunCodegenNodeFactory(
_context,
_compilationGroup,
_nameMangler,
moduleTokenResolver,
signatureContext,
- corHeaderNode);
+ corHeaderNode,
+ win32Resources);
DependencyAnalyzerBase<NodeFactory> graph = CreateDependencyGraph(factory);
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\TransitionBlock.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\TypeFixupSignature.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\TypesTableNode.cs" />
+ <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Win32ResourcesNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRunSymbolNodeFactory.cs" />
<Compile Include="Compiler\DependencyAnalysis\TypeAndMethod.cs" />
<Compile Include="Compiler\IRootingServiceProvider.cs" />
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
-using System.Linq;
-using System.Reflection;
using System.Reflection.Metadata;
-using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using ILCompiler.DependencyAnalysis;
/// Name of the initialized data section.
/// </summary>
public const string SDataSectionName = ".sdata";
-
- /// <summary>
- /// Name of the resource section.
- /// </summary>
- public const string RsrcSectionName = ".rsrc";
/// <summary>
/// Name of the relocation section.
_customSections.Add(section.SectionName);
}
- foreach (SectionHeader sectionHeader in peReader.PEHeaders.SectionHeaders)
- {
- if (_sectionBuilder.FindSection(sectionHeader.Name) == null)
- {
- _sectionBuilder.AddSection(sectionHeader.Name, sectionHeader.SectionCharacteristics, peReader.PEHeaders.PEHeader.SectionAlignment);
- }
- }
-
if (_sectionBuilder.FindSection(R2RPEBuilder.RelocSectionName) == null)
{
// Always inject the relocation section to the end of section list
_sectionBuilder.SetCorHeader(symbol, headerSize);
}
+ public void SetWin32Resources(ISymbolNode symbol, int resourcesSize)
+ {
+ _sectionBuilder.SetWin32Resources(symbol, resourcesSize);
+ }
+
/// <summary>
/// Emit a single object data item into the output R2R PE file using the section builder.
/// </summary>
protected override PEDirectoriesBuilder GetDirectories()
{
PEDirectoriesBuilder builder = new PEDirectoriesBuilder();
- builder.CorHeaderTable = RelocateDirectoryEntry(_peReader.PEHeaders.PEHeader.CorHeaderTableDirectory);
- builder.ResourceTable = RelocateDirectoryEntry(_peReader.PEHeaders.PEHeader.ResourceTableDirectory);
_sectionBuilder.UpdateDirectories(builder);
_sectionRVAs[outputSectionIndex] = sectionStartRva;
}
- int inputSectionIndex = _peReader.PEHeaders.SectionHeaders.Count() - 1;
- while (inputSectionIndex >= 0 && _peReader.PEHeaders.SectionHeaders[inputSectionIndex].Name != name)
- {
- inputSectionIndex--;
- }
- if (inputSectionIndex >= 0)
- {
- SectionHeader sectionHeader = _peReader.PEHeaders.SectionHeaders[inputSectionIndex];
- int sectionOffset = (_peReader.IsLoadedImage ? sectionHeader.VirtualAddress : sectionHeader.PointerToRawData);
- int rvaDelta = location.RelativeVirtualAddress - sectionHeader.VirtualAddress;
-
- _sectionRvaDeltas.Add(new SectionRVADelta(
- startRVA: sectionHeader.VirtualAddress,
- endRVA: sectionHeader.VirtualAddress + Math.Max(sectionHeader.VirtualSize, sectionHeader.SizeOfRawData),
- deltaRVA: rvaDelta));
-
-
- int bytesToRead = Math.Min(sectionHeader.SizeOfRawData, sectionHeader.VirtualSize);
- BlobReader inputSectionReader = _peReader.GetEntireImage().GetReader(sectionOffset, bytesToRead);
-
- if (name == RsrcSectionName)
- {
- // There seems to be a bug in BlobBuilder - when we LinkSuffix to an empty blob builder,
- // the blob data goes out of sync and WriteContentTo outputs garbage.
- sectionDataBuilder = PEResourceHelper.Relocate(inputSectionReader, rvaDelta);
- }
-
- int alignedSize = sectionHeader.VirtualSize;
-
- // When custom section data is present, align the section size to 4K to prevent
- // pre-generated MSIL relocations from tampering with native relocations.
- if (_customSections.Contains(name))
- {
- alignedSize = (alignedSize + 0xFFF) & ~0xFFF;
- }
-
- if (sectionDataBuilder != null)
- {
- if (alignedSize > bytesToRead)
- {
- // If the number of bytes read from the source PE file is less than the virtual size,
- // zero pad to the end of virtual size before emitting extra section data
- sectionDataBuilder.WriteBytes(0, alignedSize - bytesToRead);
- }
-
- location = new SectionLocation(
- location.RelativeVirtualAddress + sectionDataBuilder.Count,
- location.PointerToRawData + sectionDataBuilder.Count);
- }
- }
-
BlobBuilder extraData = _sectionBuilder.SerializeSection(name, location);
if (extraData != null)
{
return sectionDataBuilder;
}
}
-
- /// <summary>
- /// When copying PE contents we may need to move the resource section, however its internal
- /// ResourceDataEntry records hold RVA's so they need to be relocated. Thankfully the resource
- /// data model is very simple so that we just traverse the structure using offset constants.
- /// </summary>
- unsafe sealed class PEResourceHelper
- {
- /// <summary>
- /// Field offsets in the resource directory table.
- /// </summary>
- private static class DirectoryTable
- {
- public const int Characteristics = 0x0;
- public const int TimeDateStamp = 0x04;
- public const int MajorVersion = 0x08;
- public const int MinorVersion = 0x0A;
- public const int NumberOfNameEntries = 0x0C;
- public const int NumberOfIDEntries = 0x0E;
- public const int Size = 0x10;
- }
-
- /// <summary>
- /// Field offsets in the resource directory entry.
- /// </summary>
- private static class DirectoryEntry
- {
- public const int NameOffsetOrID = 0x0;
- public const int DataOrSubdirectoryOffset = 0x4;
- public const int Size = 0x8;
- }
-
- /// <summary>
- /// When the 4-byte value at the offset DirectoryEntry.DataOrSubdirectoryOffset
- /// has 31-st bit set, it's a subdirectory table entry; when it's clear, it's a
- /// resource data entry.
- /// </summary>
- private const int EntryOffsetIsSubdirectory = unchecked((int)0x80000000u);
-
- /// <summary>
- /// Field offsets in the resource data entry.
- /// </summary>
- private static class DataEntry
- {
- public const int RVA = 0x0;
- public const int Size = 0x4;
- public const int Codepage = 0x8;
- public const int Reserved = 0xC;
- }
-
- /// <summary>
- /// Blob reader representing the input resource section.
- /// </summary>
- private BlobReader _reader;
-
- /// <summary>
- /// This BlobBuilder holds the relocated resource section after the ctor finishes.
- /// </summary>
- private BlobBuilder _builder;
-
- /// <summary>
- /// Relocation delta (the difference between input and output RVA of the resource section).
- /// </summary>
- private int _delta;
-
- /// <summary>
- /// Offsets within the resource section representing RVA's in the resource data entries
- /// that need relocating.
- /// </summary>
- private List<int> _offsetsOfRvasToRelocate;
-
- /// <summary>
- /// Public API receives the input resource section reader and the relocation delta
- /// and returns a blob builder representing the relocated resource section.
- /// </summary>
- /// <param name="reader">Blob reader representing the input resource section</param>
- /// <param name="delta">Relocation delta to apply (value to add to RVA's)</param>
- public static BlobBuilder Relocate(BlobReader reader, int delta)
- {
- return new PEResourceHelper(reader, delta)._builder;
- }
-
- /// <summary>
- /// Private constructor first traverses the internal graph of resource tables
- /// and collects offsets to RVA's that need relocation; after that we sort the list of
- /// offsets and do a linear copying pass patching the RVA cells with the updated values.
- /// </summary>
- /// <param name="reader">Blob reader representing the input resource section</param>
- /// <param name="delta">Relocation delta to apply (value to add to RVA's)</param>
- private PEResourceHelper(BlobReader reader, int delta)
- {
- _reader = reader;
- _builder = new BlobBuilder();
- _delta = delta;
-
- _offsetsOfRvasToRelocate = new List<int>();
-
- TraverseDirectoryTable(tableOffset: 0);
-
- _offsetsOfRvasToRelocate.Sort();
- int currentOffset = 0;
-
- _reader.Reset();
- foreach (int offsetOfRvaToRelocate in _offsetsOfRvasToRelocate)
- {
- int bytesToCopy = offsetOfRvaToRelocate - currentOffset;
- Debug.Assert(bytesToCopy >= 0);
- if (bytesToCopy > 0)
- {
- _builder.WriteBytes(_reader.CurrentPointer, bytesToCopy);
- _reader.Offset += bytesToCopy;
- currentOffset += bytesToCopy;
- }
- int rva = _reader.ReadInt32();
- _builder.WriteInt32(rva + delta);
- currentOffset += sizeof(int);
- }
- if (_reader.RemainingBytes > 0)
- {
- _builder.WriteBytes(_reader.CurrentPointer, _reader.RemainingBytes);
- }
- }
-
- /// <summary>
- /// Traverse a single directory table at a given offset within the resource section.
- /// Please note the method might end up calling itself recursively through the call graph
- /// TraverseDirectoryTable -> TraverseDirectoryEntry -> TraverseDirectoryTable.
- /// Maximum depth is equal to depth of the table graph - today resources use 3.
- /// </summary>
- /// <param name="tableOffset">Offset of the resource directory table within the resource section</param>
- private void TraverseDirectoryTable(int tableOffset)
- {
- _reader.Offset = tableOffset + DirectoryTable.NumberOfNameEntries;
- int numberOfNameEntries = _reader.ReadInt16();
- int numberOfIDEntries = _reader.ReadInt16();
- int totalEntries = numberOfNameEntries + numberOfIDEntries;
- for (int entryIndex = 0; entryIndex < totalEntries; entryIndex++)
- {
- TraverseDirectoryEntry(tableOffset + DirectoryTable.Size + entryIndex * DirectoryEntry.Size);
- }
- }
-
- /// <summary>
- /// Traverse a single directory entry (name- and ID-based directory entries are processed
- /// the same way as we're not really interested in the entry identifier, just in the
- /// data / table pointers.
- /// </summary>
- /// <param name="entryOffset">Offset of the resource directory entry within the resource section</param>
- private void TraverseDirectoryEntry(int entryOffset)
- {
- _reader.Offset = entryOffset + DirectoryEntry.DataOrSubdirectoryOffset;
- int dataOrSubdirectoryOffset = _reader.ReadInt32();
- if ((dataOrSubdirectoryOffset & EntryOffsetIsSubdirectory) != 0)
- {
- // subdirectory offset
- TraverseDirectoryTable(dataOrSubdirectoryOffset & ~EntryOffsetIsSubdirectory);
- }
- else
- {
- // data entry offset
- _offsetsOfRvasToRelocate.Add(dataOrSubdirectoryOffset + DataEntry.RVA);
- }
- }
- }
/// <summary>
/// Simple helper for copying the various global values in the PE header.
int _corHeaderSize;
/// <summary>
+ /// Symbol representing the start of the win32 resources
+ /// </summary>
+ ISymbolNode _win32ResourcesSymbol;
+
+ /// <summary>
+ /// Size of the win32 resources
+ /// </summary>
+ int _win32ResourcesSize;
+
+ /// <summary>
/// Padding 4-byte sequence to use in code section. Typically corresponds
/// to some interrupt to be thrown at "invalid" IP addresses.
/// </summary>
_corHeaderSize = headerSize;
}
+ public void SetWin32Resources(ISymbolNode symbol, int resourcesSize)
+ {
+ _win32ResourcesSymbol = symbol;
+ _win32ResourcesSize = resourcesSize;
+ }
+
private CoreRTNameMangler _nameMangler;
private NameMangler GetNameMangler()
directoriesBuilder.CorHeaderTable = new DirectoryEntry(section.RVAWhenPlaced + symbolTarget.Offset, _corHeaderSize);
}
+ if (_win32ResourcesSymbol != null)
+ {
+ SymbolTarget symbolTarget = _symbolMap[_win32ResourcesSymbol];
+ Section section = _sections[symbolTarget.SectionIndex];
+ Debug.Assert(section.RVAWhenPlaced != 0);
+ directoriesBuilder.ResourceTable = new DirectoryEntry(section.RVAWhenPlaced + symbolTarget.Offset, _win32ResourcesSize);
+ }
+
if (_exportDirectoryEntry.Size != 0)
{
directoriesBuilder.ExportTable = _exportDirectoryEntry;
using System;
using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
namespace ILCompiler.Win32Resources
{
public unsafe partial class ResourceData
{
- private void ReadResourceData()
+ private void ReadResourceData(BlobReader resourceReader, PEReader peFile, Func<object, object, ushort, bool> resourceFilter)
{
- BlobReader resourceReader = _resourceDataBlob;
DoResourceDirectoryRead(resourceReader, 0, ProcessOuterResource);
return;
IMAGE_RESOURCE_DATA_ENTRY resourceData = new IMAGE_RESOURCE_DATA_ENTRY(ref resourceReader);
// The actual resource data offset is relative to the start address of the file
- BlobReader resourceDataBlob = _peFile.GetSectionData(checked((int)resourceData.OffsetToData)).GetReader(0, checked((int)resourceData.Size));
+ BlobReader resourceDataBlob = peFile.GetSectionData(checked((int)resourceData.OffsetToData)).GetReader(0, checked((int)resourceData.Size));
byte[] data = resourceDataBlob.ReadBytes((int)resourceData.Size);
+ if (resourceFilter != null)
+ {
+ // If the filter returns false, don't add this resource to the model
+ if (!resourceFilter(typeName, name, (ushort)languageName))
+ return;
+ }
AddResource(typeName, name, (ushort)languageName, data);
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System;
using System.Collections.Generic;
namespace ILCompiler.Win32Resources
{
public unsafe partial class ResourceData
{
- private readonly List<ResType_Ordinal> _resTypeHeadID = new List<ResType_Ordinal>();
- private readonly List<ResType_Name> _resTypeHeadName = new List<ResType_Name>();
+ private readonly SortedDictionary<ushort, ResType> _resTypeHeadID = new SortedDictionary<ushort, ResType>();
+ private readonly SortedDictionary<string, ResType> _resTypeHeadName = new SortedDictionary<string, ResType>(StringComparer.Ordinal);
- private class OrdinalName
+ private class ResLanguage
{
- public OrdinalName(ushort ordinal) { Ordinal = ordinal; }
- public readonly ushort Ordinal;
- }
-
- private interface IUnderlyingName<T>
- {
- T Name { get; }
- }
+ public ResLanguage(byte[] data)
+ {
+ DataEntry = data;
+ }
- private class ResName
- {
public uint DataSize => (uint)DataEntry.Length;
public byte[] DataEntry;
- public ushort NumberOfLanguages;
- public ushort LanguageId;
}
- private class ResName_Name : ResName, IUnderlyingName<string>
+ private class ResName
{
- public ResName_Name(string name)
- {
- Name = name;
- }
-
- public string Name { get; }
- }
-
- private class ResName_Ordinal : ResName, IUnderlyingName<ushort>
- {
- public ResName_Ordinal(ushort name)
- {
- Name = new OrdinalName(name);
- }
-
- public OrdinalName Name;
- ushort IUnderlyingName<ushort>.Name => Name.Ordinal;
+ public SortedDictionary<ushort, ResLanguage> Languages = new SortedDictionary<ushort, ResLanguage>();
}
private class ResType
{
- public List<ResName_Name> NameHeadName = new List<ResName_Name>();
- public List<ResName_Ordinal> NameHeadID = new List<ResName_Ordinal>();
- }
-
- private class ResType_Ordinal : ResType, IUnderlyingName<ushort>
- {
- public ResType_Ordinal(ushort type)
- {
- Type = new OrdinalName(type);
- }
-
- public OrdinalName Type;
- ushort IUnderlyingName<ushort>.Name => Type.Ordinal;
+ public SortedDictionary<string, ResName> NameHeadName = new SortedDictionary<string, ResName>(StringComparer.Ordinal);
+ public SortedDictionary<ushort, ResName> NameHeadID = new SortedDictionary<ushort, ResName>();
}
- private class ResType_Name : ResType, IUnderlyingName<string>
- {
- public ResType_Name(string type)
- {
- Type = type;
- }
-
- public string Type { get; set; }
- string IUnderlyingName<string>.Name => Type;
- }
}
}
{
private void AddResource(object type, object name, ushort language, byte[] data)
{
- ResType resType = null;
- // Allocate new object in case it is needed.
- ResType newResType;
- int newIndex;
+ ResType resType;
- IList typeList;
- bool updateExisting;
if (type is ushort)
{
- ResType_Ordinal newOrdinalType = new ResType_Ordinal((ushort)type);
- newResType = newOrdinalType;
- typeList = _resTypeHeadID;
-
- newIndex = GetIndexOfFirstItemMatchingInListOrInsertionPoint(typeList, (ushort left, ushort right) => (int)left - (int)right, (ushort)type, out updateExisting);
- }
- else
- {
- ResType_Name newStringType = new ResType_Name((string)type);
- newResType = newStringType;
- typeList = _resTypeHeadName;
-
- newIndex = GetIndexOfFirstItemMatchingInListOrInsertionPoint(typeList, string.CompareOrdinal, (string)type, out updateExisting);
- }
-
- if (updateExisting)
- {
- resType = (ResType)typeList[newIndex];
- }
- else
- {
- // This is a new type
- if (newIndex == -1)
- typeList.Add(newResType);
- else
- typeList.Insert(newIndex, newResType);
-
- resType = newResType;
- }
-
- Type resNameType;
- IList nameList;
- int nameIndex;
-
- if (name is ushort)
- {
- nameList = resType.NameHeadID;
- resNameType = typeof(ResName_Ordinal);
- nameIndex = GetIndexOfFirstItemMatchingInListOrInsertionPoint(nameList, (ushort left, ushort right) => (int)left - (int)right, (ushort)name, out updateExisting);
- }
- else
- {
- nameList = resType.NameHeadName;
- resNameType = typeof(ResName_Name);
- nameIndex = GetIndexOfFirstItemMatchingInListOrInsertionPoint(nameList, string.CompareOrdinal, (string)name, out updateExisting);
- }
-
- if (updateExisting)
- {
- // We have at least 1 language with the same type/name. Insert/delete from language list
- ResName resName = (ResName)nameList[nameIndex];
- int newNumberOfLanguages = (int)resName.NumberOfLanguages + (data != null ? 1 : -1);
-
- int newIndexForNewOrUpdatedNameWithMatchingLanguage = GetIndexOfFirstItemMatchingInListOrInsertionPoint(nameList, nameIndex,
- resName.NumberOfLanguages, (object o) => ((ResName)o).LanguageId, (ushort left, ushort right) => (int)left - (int)right, language, out bool exactLanguageExists);
-
- if (exactLanguageExists)
+ if (!_resTypeHeadID.TryGetValue((ushort)type, out resType))
{
- if (data == null)
- {
- // delete item
- nameList.RemoveAt(newIndexForNewOrUpdatedNameWithMatchingLanguage);
-
- if (newNumberOfLanguages > 0)
- {
- // if another name is still present, update the number of languages counter
- resName = (ResName)nameList[nameIndex];
- resName.NumberOfLanguages = (ushort)newNumberOfLanguages;
- }
-
- if ((resType.NameHeadID.Count == 0) && (resType.NameHeadName.Count == 0))
- {
- /* type list completely empty? */
- typeList.Remove(resType);
- }
- }
- else
- {
- // Resource file has two copies of same resource... ignore second copy
- return;
- }
- }
- else
- {
- // Insert a new name at the new spot
- AddNewName(nameList, resNameType, newIndexForNewOrUpdatedNameWithMatchingLanguage, name, language, data);
- // Update the NumberOfLanguages for the language list
- resName = (ResName)nameList[nameIndex];
- resName.NumberOfLanguages = (ushort)newNumberOfLanguages;
+ resType = new ResType();
+ _resTypeHeadID[(ushort)type] = resType;
}
}
else
{
- // This is a new name in a new language list
- if (data == null)
+ if (!_resTypeHeadName.TryGetValue((string)type, out resType))
{
- // Can't delete new name
- throw new ArgumentException();
+ resType = new ResType();
+ _resTypeHeadName[(string)type] = resType;
}
-
- AddNewName(nameList, resNameType, nameIndex, name, language, data);
}
- }
- private static int GetIndexOfFirstItemMatchingInListOrInsertionPoint<T>(IList list, Func<T, T, int> compareFunction, T comparand, out bool exists)
- {
- return GetIndexOfFirstItemMatchingInListOrInsertionPoint(list, 0, list.Count, (object o) => ((IUnderlyingName<T>)o).Name, compareFunction, comparand, out exists);
- }
+ ResName resName;
- private static int GetIndexOfFirstItemMatchingInListOrInsertionPoint<T>(IList list, int start, int count, Func<object, T> getComparandFromListElem, Func<T, T, int> compareFunction, T comparand, out bool exists)
- {
- int i = start;
- for (; i < (start + count); i++)
+ if (name is ushort)
{
- int iCompare = compareFunction(comparand, getComparandFromListElem(list[i]));
- if (iCompare == 0)
- {
- exists = true;
- return i;
- }
- else if (iCompare < 0)
+ if (!resType.NameHeadID.TryGetValue((ushort)name, out resName))
{
- exists = false;
- return i;
+ resName = new ResName();
+ resType.NameHeadID[(ushort)name] = resName;
}
}
-
- exists = false;
- if ((start + count) < list.Count)
+ else
{
- return start + count;
+ if (!resType.NameHeadName.TryGetValue((string)name, out resName))
+ {
+ resName = new ResName();
+ resType.NameHeadName[(string)name] = resName;
+ }
}
- return -1;
- }
- private void AddNewName(IList list, Type resNameType, int insertPoint, object name, ushort language, byte[] data)
- {
- ResName newResName = (ResName)Activator.CreateInstance(resNameType, name);
- newResName.LanguageId = language;
- newResName.NumberOfLanguages = 1;
- newResName.DataEntry = data;
-
- if (insertPoint == -1)
- list.Add(newResName);
- else
- list.Insert(insertPoint, newResName);
+ resName.Languages[language] = new ResLanguage(data);
}
private byte[] FindResourceInternal(object name, object type, ushort language)
if (type is ushort)
{
- foreach (ResType_Ordinal candidate in _resTypeHeadID)
- {
- if (candidate.Type.Ordinal == (ushort)type)
- {
- resType = candidate;
- break;
- }
- }
+ _resTypeHeadID.TryGetValue((ushort)type, out resType);
}
if (type is string)
{
- foreach (ResType_Name candidate in _resTypeHeadName)
- {
- if (candidate.Type == (string)type)
- {
- resType = candidate;
- break;
- }
- }
+ _resTypeHeadName.TryGetValue((string)type, out resType);
}
if (resType == null)
return null;
+ ResName resName = null;
+
if (name is ushort)
{
- foreach (ResName_Ordinal candidate in resType.NameHeadID)
- {
- if (candidate.Name.Ordinal != (ushort)type)
- continue;
-
- if (candidate.LanguageId != language)
- continue;
-
- return (byte[])candidate.DataEntry.Clone();
- }
+ resType.NameHeadID.TryGetValue((ushort)name, out resName);
}
if (name is string)
{
- foreach (ResName_Name candidate in resType.NameHeadName)
- {
- if (candidate.Name != (string)name)
- continue;
+ resType.NameHeadName.TryGetValue((string)name, out resName);
+ }
- if (candidate.LanguageId != language)
- continue;
+ if (resName == null)
+ return null;
- return (byte[])candidate.DataEntry.Clone();
- }
- }
+ if (!resName.Languages.TryGetValue(language, out ResLanguage resLanguage))
+ return null;
- return null;
+ return (byte[])resLanguage.DataEntry.Clone();
}
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Reflection.Metadata;
+using ILCompiler.DependencyAnalysis;
+
namespace ILCompiler.Win32Resources
{
public unsafe partial class ResourceData
NumberOfIdEntries = blobReader.ReadUInt16();
}
+ public static void Write(ref ObjectDataBuilder builder, ushort namedEntries, ushort idEntries)
+ {
+ builder.EmitUInt(0); // Characteristics
+ builder.EmitUInt(0); // TimeDateStamp
+ builder.EmitUShort(4); // MajorVersion
+ builder.EmitUShort(0); // MinorVersion
+ builder.EmitUShort(namedEntries);
+ builder.EmitUShort(idEntries);
+ }
+
public readonly uint Characteristics;
public readonly uint TimeDateStamp;
public readonly ushort MajorVersion;
OffsetToData = blobReader.ReadUInt32();
}
+ public static ObjectDataBuilder.Reservation Write(ref ObjectDataBuilder dataBuilder, string name, IDictionary<string, List<ObjectDataBuilder.Reservation>> nameTable)
+ {
+ List<ObjectDataBuilder.Reservation> relatedNameReferences;
+ if (!nameTable.TryGetValue(name, out relatedNameReferences))
+ {
+ relatedNameReferences = new List<ObjectDataBuilder.Reservation>();
+ nameTable[name] = relatedNameReferences;
+ }
+ relatedNameReferences.Add(dataBuilder.ReserveInt());
+ return dataBuilder.ReserveInt();
+ }
+
+ public static ObjectDataBuilder.Reservation Write(ref ObjectDataBuilder dataBuilder, ushort id)
+ {
+ dataBuilder.EmitInt(id);
+ return dataBuilder.ReserveInt();
+ }
+
public readonly uint Name;
public readonly uint OffsetToData;
}
Reserved = blobReader.ReadUInt32();
}
+ public static void Write(ref ObjectDataBuilder dataBuilder, ISymbolNode node, int offsetFromSymbol, int sizeOfData)
+ {
+ dataBuilder.EmitReloc(node, RelocType.IMAGE_REL_BASED_ADDR32NB, offsetFromSymbol);
+ dataBuilder.EmitInt(sizeOfData);
+ dataBuilder.EmitInt(1252); // CODEPAGE = DEFAULT_CODEPAGE
+ dataBuilder.EmitInt(0); // RESERVED
+ }
+
public uint OffsetToData;
public uint Size;
private uint CodePage;
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using Internal.TypeSystem.Ecma;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
+using ILCompiler.DependencyAnalysis;
+using Internal.TypeSystem.Ecma;
+
namespace ILCompiler.Win32Resources
{
/// <summary>
/// </summary>
public unsafe partial class ResourceData
{
- BlobReader _resourceDataBlob;
- PEReader _peFile;
-
/// <summary>
/// Initialize a ResourceData instance from a PE file
/// </summary>
/// <param name="ecmaModule"></param>
- public ResourceData(EcmaModule ecmaModule)
+ public ResourceData(EcmaModule ecmaModule, Func<object, object, ushort, bool> resourceFilter = null)
{
- var ecmaData = ecmaModule.PEReader.GetEntireImage().GetContent();
- _peFile = ecmaModule.PEReader;
+ System.Collections.Immutable.ImmutableArray<byte> ecmaData = ecmaModule.PEReader.GetEntireImage().GetContent();
+ PEReader peFile = ecmaModule.PEReader;
- DirectoryEntry resourceDirectory = _peFile.PEHeaders.PEHeader.ResourceTableDirectory;
+ DirectoryEntry resourceDirectory = peFile.PEHeaders.PEHeader.ResourceTableDirectory;
if (resourceDirectory.Size != 0)
{
- _resourceDataBlob = ecmaModule.PEReader.GetSectionData(resourceDirectory.RelativeVirtualAddress).GetReader(0, resourceDirectory.Size);
- ReadResourceData();
+ BlobReader resourceDataBlob = ecmaModule.PEReader.GetSectionData(resourceDirectory.RelativeVirtualAddress).GetReader(0, resourceDirectory.Size);
+ ReadResourceData(resourceDataBlob, peFile, resourceFilter);
}
}
{
return FindResourceInternal(name, type, language);
}
+
+ public bool IsEmpty
+ {
+ get
+ {
+ if (_resTypeHeadID.Count > 0)
+ return false;
+
+ if (_resTypeHeadName.Count > 0)
+ return false;
+
+ return true;
+ }
+ }
+
+ public void WriteResources(ISymbolNode nodeAssociatedWithDataBuilder, ref ObjectDataBuilder dataBuilder)
+ {
+ Debug.Assert(dataBuilder.CountBytes == 0);
+
+ SortedDictionary<string, List<ObjectDataBuilder.Reservation>> nameTable = new SortedDictionary<string, List<ObjectDataBuilder.Reservation>>();
+ Dictionary<ResLanguage, int> dataEntryTable = new Dictionary<ResLanguage, int>();
+ List<Tuple<ResType, ObjectDataBuilder.Reservation>> resTypes = new List<Tuple<ResType, ObjectDataBuilder.Reservation>>();
+ List<Tuple<ResName, ObjectDataBuilder.Reservation>> resNames = new List<Tuple<ResName, ObjectDataBuilder.Reservation>>();
+ List<Tuple<ResLanguage, ObjectDataBuilder.Reservation>> resLanguages = new List<Tuple<ResLanguage, ObjectDataBuilder.Reservation>>();
+
+ IMAGE_RESOURCE_DIRECTORY.Write(ref dataBuilder, checked((ushort)_resTypeHeadName.Count), checked((ushort)_resTypeHeadID.Count));
+ foreach (KeyValuePair<string, ResType> res in _resTypeHeadName)
+ {
+ resTypes.Add(new Tuple<ResType, ObjectDataBuilder.Reservation>(res.Value, IMAGE_RESOURCE_DIRECTORY_ENTRY.Write(ref dataBuilder, res.Key, nameTable)));
+ }
+ foreach (KeyValuePair<ushort, ResType> res in _resTypeHeadID)
+ {
+ resTypes.Add(new Tuple<ResType, ObjectDataBuilder.Reservation>(res.Value, IMAGE_RESOURCE_DIRECTORY_ENTRY.Write(ref dataBuilder, res.Key)));
+ }
+
+ foreach (Tuple<ResType, ObjectDataBuilder.Reservation> type in resTypes)
+ {
+ dataBuilder.EmitUInt(type.Item2, (uint)dataBuilder.CountBytes | 0x80000000);
+ IMAGE_RESOURCE_DIRECTORY.Write(ref dataBuilder, checked((ushort)type.Item1.NameHeadName.Count), checked((ushort)type.Item1.NameHeadID.Count));
+
+ foreach (KeyValuePair<string, ResName> res in type.Item1.NameHeadName)
+ {
+ resNames.Add(new Tuple<ResName, ObjectDataBuilder.Reservation>(res.Value, IMAGE_RESOURCE_DIRECTORY_ENTRY.Write(ref dataBuilder, res.Key, nameTable)));
+ }
+ foreach (KeyValuePair<ushort, ResName> res in type.Item1.NameHeadID)
+ {
+ resNames.Add(new Tuple<ResName, ObjectDataBuilder.Reservation>(res.Value, IMAGE_RESOURCE_DIRECTORY_ENTRY.Write(ref dataBuilder, res.Key)));
+ }
+ }
+
+ foreach (Tuple<ResName, ObjectDataBuilder.Reservation> type in resNames)
+ {
+ dataBuilder.EmitUInt(type.Item2, (uint)dataBuilder.CountBytes | 0x80000000);
+ IMAGE_RESOURCE_DIRECTORY.Write(ref dataBuilder, 0, checked((ushort)type.Item1.Languages.Count));
+ foreach (KeyValuePair<ushort, ResLanguage> res in type.Item1.Languages)
+ {
+ resLanguages.Add(new Tuple<ResLanguage, ObjectDataBuilder.Reservation>(res.Value, IMAGE_RESOURCE_DIRECTORY_ENTRY.Write(ref dataBuilder, res.Key)));
+ }
+ }
+
+ // Emit name table
+ dataBuilder.PadAlignment(2); // name table is 2 byte aligned
+ foreach (KeyValuePair<string, List<ObjectDataBuilder.Reservation>> name in nameTable)
+ {
+ foreach (ObjectDataBuilder.Reservation reservation in name.Value)
+ {
+ dataBuilder.EmitUInt(reservation, (uint)dataBuilder.CountBytes | 0x80000000);
+ }
+
+ dataBuilder.EmitUShort(checked((ushort)name.Key.Length));
+ foreach (char c in name.Key)
+ {
+ dataBuilder.EmitUShort((ushort)c);
+ }
+ }
+
+ // Emit byte arrays of resource data, capture the offsets
+ foreach (Tuple<ResLanguage, ObjectDataBuilder.Reservation> language in resLanguages)
+ {
+ dataBuilder.PadAlignment(4); // Data in resource files is 4 byte aligned
+ dataEntryTable.Add(language.Item1, dataBuilder.CountBytes);
+ dataBuilder.EmitBytes(language.Item1.DataEntry);
+ }
+
+ dataBuilder.PadAlignment(4); // resource data entries are 4 byte aligned
+ foreach (Tuple<ResLanguage, ObjectDataBuilder.Reservation> language in resLanguages)
+ {
+ dataBuilder.EmitInt(language.Item2, dataBuilder.CountBytes);
+ IMAGE_RESOURCE_DATA_ENTRY.Write(ref dataBuilder, nodeAssociatedWithDataBuilder, dataEntryTable[language.Item1], language.Item1.DataEntry.Length);
+ }
+ dataBuilder.PadAlignment(4); // resource data entries are 4 byte aligned
+ }
}
}