Skip to content

Commit

Permalink
Changes for ILRepack
Browse files Browse the repository at this point in the history
Condensed from https://github.com/gluck/cecil master branch
  • Loading branch information
KirillOsenkov committed Dec 29, 2023
1 parent 56d4409 commit ebbba77
Show file tree
Hide file tree
Showing 8 changed files with 378 additions and 11 deletions.
9 changes: 9 additions & 0 deletions Mono.Cecil.PE/ByteBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -332,5 +332,14 @@ void Grow (int desired)
Buffer.BlockCopy (current, 0, buffer, 0, current_length);
this.buffer = buffer;
}

public void Align(int alignment)
{
if (position + alignment > buffer.Length)
Grow(alignment);
int newpos = (position + alignment - 1) & ~(alignment - 1);
while (position < newpos)
buffer[position++] = 0;
}
}
}
13 changes: 13 additions & 0 deletions Mono.Cecil.PE/Image.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ sealed class Image : IDisposable {
public DataDirectory Resources;
public DataDirectory StrongName;

public ByteBuffer GetWin32ResourcesBytes()
{
var size = Win32Resources.Size;
if (size > 0)
{
var rva = Win32Resources.VirtualAddress;
var buffer = GetReaderAt(rva, size, (position, reader) => new ByteBuffer(reader.ReadBytes((int)position)));
return buffer;
}

return null;
}

public StringHeap StringHeap;
public BlobHeap BlobHeap;
public UserStringHeap UserStringHeap;
Expand Down
162 changes: 152 additions & 10 deletions Mono.Cecil.PE/ImageWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//

using System;
using System.Collections.Generic;
using System.IO;

using Mono.Cecil.Cil;
Expand Down Expand Up @@ -87,15 +88,13 @@ void GetDebugHeader ()

void GetWin32Resources ()
{
if (!module.HasImage)
if (module.Win32ResourceDirectory != null)
{
return;

DataDirectory win32_resources_directory = module.Image.Win32Resources;
var size = win32_resources_directory.Size;

if (size > 0) {
win32_resources = module.Image.GetReaderAt (win32_resources_directory.VirtualAddress, size, (s, reader) => new ByteBuffer (reader.ReadBytes ((int) s)));
}

if (module.HasImage)
win32_resources = module.Image.GetWin32ResourcesBytes ();
}

public static ImageWriter CreateWriter (ModuleDefinition module, MetadataBuilder metadata, Disposable<Stream> stream)
Expand All @@ -115,17 +114,26 @@ public static ImageWriter CreateDebugWriter (ModuleDefinition module, MetadataBu

void BuildSections ()
{
var has_win32_resources = win32_resources != null;
var has_win32_resources = win32_resources != null || module.Win32ResourceDirectory != null;
if (has_win32_resources)
sections++;

text = CreateSection (".text", text_map.GetLength (), null);
var previous = text;

if (has_win32_resources) {
rsrc = CreateSection (".rsrc", (uint) win32_resources.length, previous);
rsrc = CreateSection (".rsrc", (uint)(win32_resources?.length ?? 0), previous);

if (module.Win32ResourceDirectory is ResourceDirectory resourceDirectory)
{
WriteWin32ResourcesDirectory(resourceDirectory);
uint size = (uint)win32_resources.length;
rsrc.VirtualSize = size;
rsrc.SizeOfRawData = Align(size, file_alignment);
} else {
PatchWin32Resources(win32_resources);
}

PatchWin32Resources (win32_resources);
previous = rsrc;
}

Expand Down Expand Up @@ -862,5 +870,139 @@ void PatchResourceDataEntry (ByteBuffer resources)

resources.WriteUInt32 (rva - module.Image.Win32Resources.VirtualAddress + rsrc.VirtualAddress);
}

private static int GetDirectoryLength(ResourceDirectory dir)
{
int length = 16 + dir.Entries.Count * 8;
foreach (ResourceEntry entry in dir.Entries)
length += GetDirectoryLength(entry);
return length;
}

private static int GetDirectoryLength(ResourceEntry entry)
{
if (entry.Data != null)
return 16;
return GetDirectoryLength(entry.Directory);
}

private void WriteWin32ResourcesDirectory(ResourceDirectory directory)
{
win32_resources = new ByteBuffer();
if (directory.Entries.Count != 0)
{
int stringTableOffset = GetDirectoryLength(directory);
Dictionary<string, int> strings = new Dictionary<string, int>();
ByteBuffer stringTable = new ByteBuffer(16);
int offset = 16 + directory.Entries.Count * 8;
for (int pass = 0; pass < 3; pass++)
Write(directory, pass, 0, ref offset, strings, ref stringTableOffset, stringTable);
// the pecoff spec says that the string table is between the directory entries and the data entries,
// but the windows linker puts them after the data entries, so we do too.
stringTable.Align(4);
offset += stringTable.length;
WriteResourceDataEntries(directory, ref offset);
win32_resources.WriteBytes(stringTable);
WriteData(directory);
}
}

private void WriteResourceDataEntries(ResourceDirectory directory, ref int offset)
{
foreach (ResourceEntry entry in directory.Entries)
{
if (entry.Data != null)
{
win32_resources.WriteUInt32((uint)(rsrc.VirtualAddress + offset));
win32_resources.WriteInt32(entry.Data.Length);
win32_resources.WriteUInt32(entry.CodePage);
win32_resources.WriteUInt32(entry.Reserved);
offset += (entry.Data.Length + 3) & ~3;
}
else
{
WriteResourceDataEntries(entry.Directory, ref offset);
}
}
}

private void WriteData(ResourceDirectory directory)
{
foreach (ResourceEntry entry in directory.Entries)
{
if (entry.Data != null)
{
win32_resources.WriteBytes(entry.Data);
win32_resources.Align(4);
}
else
{
WriteData(entry.Directory);
}
}
}

private void Write(ResourceDirectory directory, int writeDepth, int currentDepth, ref int offset, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable)
{
if (currentDepth == writeDepth)
{
ushort namedEntries = directory.SortEntries();
// directory header
win32_resources.WriteUInt32(directory.Characteristics);
win32_resources.WriteUInt32(directory.TimeDateStamp);
win32_resources.WriteUInt16(directory.MajorVersion);
win32_resources.WriteUInt16(directory.MinVersion);
win32_resources.WriteUInt16(namedEntries);
win32_resources.WriteUInt16((ushort)(directory.Entries.Count - namedEntries));
foreach (ResourceEntry entry in directory.Entries)
{
WriteEntry(entry, ref offset, strings, ref stringTableOffset, stringTable);
}
}
else
{
foreach (ResourceEntry entry in directory.Entries)
{
Write(entry.Directory, writeDepth, currentDepth + 1, ref offset, strings, ref stringTableOffset, stringTable);
}
}
}

private void WriteEntry(ResourceEntry entry, ref int offset, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable)
{
WriteNameOrOrdinal(entry, strings, ref stringTableOffset, stringTable);
if (entry.Data == null)
{
win32_resources.WriteUInt32(0x80000000U | (uint)offset);
offset += entry.Directory.Entries.Count * 8;
}
else
{
win32_resources.WriteUInt32((uint)offset);
}
offset += 16;
}

private void WriteNameOrOrdinal(ResourceEntry entry, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable)
{
if (entry.Name == null)
{
win32_resources.WriteUInt32(entry.Id);
}
else
{
int stringOffset;
if (!strings.TryGetValue(entry.Name, out stringOffset))
{
stringOffset = stringTableOffset;
strings.Add(entry.Name, stringOffset);
stringTableOffset += entry.Name.Length * 2 + 2;
stringTable.WriteUInt16((ushort)entry.Name.Length);
foreach (char c in entry.Name)
stringTable.WriteInt16((short)c);
}
win32_resources.WriteUInt32(0x80000000U | (uint)stringOffset);
}
}
}
}
60 changes: 60 additions & 0 deletions Mono.Cecil.PE/ResourceDirectory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using Mono.Collections.Generic;

namespace Mono.Cecil.PE
{
public class ResourceEntry
{
public uint Id { get; set; }
public string Name { get; set; }
public ResourceDirectory Directory { get; set; }
public uint CodePage { get; set; }
public uint Reserved { get; set; }
public byte[] Data { get; set; }
}

public class ResourceDirectory
{
private readonly Collection<ResourceEntry> _entries = new Collection<ResourceEntry>();

public Collection<ResourceEntry> Entries
{
get
{
return _entries;
}
}

public ushort SortEntries()
{
_entries.Sort(EntryComparer.Instance);
for (ushort i = 0; i < _entries.Count; i++)
if (_entries[i].Name == null)
return i;
return 0;
}

public ushort NumNameEntries { get; set; }
public ushort NumIdEntries { get; set; }
public ushort MinVersion { get; set; }
public ushort MajorVersion { get; set; }
public uint Characteristics { get; set; }
public uint TimeDateStamp { get; set; }
}

class EntryComparer : IComparer<ResourceEntry>
{
internal static readonly EntryComparer Instance = new EntryComparer();
public int Compare(ResourceEntry x, ResourceEntry y)
{
if (x.Name != null && y.Name == null)
return -1;
if (x.Name == null && y.Name != null)
return 1;
if (x.Name == null)
return (int)(x.Id - y.Id);
return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
}
}
}
82 changes: 82 additions & 0 deletions Mono.Cecil.PE/RsrcReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System.IO;
using System.Text;

namespace Mono.Cecil.PE
{
internal class RsrcReader
{
internal static ResourceDirectory ReadResourceDirectory(ByteBuffer b, uint baseAddress)
{
return ReadResourceDirectory(b.buffer, baseAddress);
}

internal static ResourceDirectory ReadResourceDirectory(byte[] b, uint baseAddress)
{
var dr = new MemoryStream(b);
return ReadResourceDirectory(new BinaryReader(dr), baseAddress);
}

private static ResourceDirectory ReadResourceDirectory(BinaryReader dr, uint baseAddress)
{
var d = ReadResourceDirectoryTable(dr);
int ne = d.NumNameEntries + d.NumIdEntries;
for (int i = 0; i < ne; i++)
d.Entries.Add(ReadResourceEntry(dr, baseAddress));
return d;
}

private static ResourceEntry ReadResourceEntry(BinaryReader dr, uint baseAddress)
{
var re = new ResourceEntry();
uint id = dr.ReadUInt32();
uint offset = dr.ReadUInt32();
long pos = dr.BaseStream.Position;
if ((id & 0x80000000) != 0)
{
dr.BaseStream.Position = (id & 0x7fffffff);
var b = new StringBuilder();
int c;
while ((c = dr.Read()) > 0)
b.Append((char)c);
re.Name = b.ToString();
}
else
{
re.Id = id;
}
if ((offset & 0x80000000) != 0)
{
dr.BaseStream.Position = (offset & 0x7fffffff);
re.Directory = ReadResourceDirectory(dr, baseAddress);
}
else
{
dr.BaseStream.Position = offset;
uint rva = dr.ReadUInt32();
uint size = dr.ReadUInt32();
uint cp = dr.ReadUInt32();
uint res = dr.ReadUInt32();
re.CodePage = cp;
re.Reserved = res;
dr.BaseStream.Position = (rva - baseAddress);
re.Data = dr.ReadBytes((int)size);
}
dr.BaseStream.Position = pos;
return re;
}

private static ResourceDirectory ReadResourceDirectoryTable(BinaryReader dr)
{
var t = new ResourceDirectory
{
Characteristics = dr.ReadUInt32(),
TimeDateStamp = dr.ReadUInt32(),
MajorVersion = dr.ReadUInt16(),
MinVersion = dr.ReadUInt16(),
NumNameEntries = dr.ReadUInt16(),
NumIdEntries = dr.ReadUInt16()
};
return t;
}
}
}
Loading

0 comments on commit ebbba77

Please sign in to comment.