Skip to content

Commit

Permalink
Merge pull request #518 from ElektroKill/field-initialvalue-alignment
Browse files Browse the repository at this point in the history
Align field initial value to match the Microsoft's ECMA augments
  • Loading branch information
wtfsck authored Aug 6, 2023
2 parents d344770 + 1727923 commit c2d5c9e
Show file tree
Hide file tree
Showing 26 changed files with 143 additions and 3 deletions.
1 change: 1 addition & 0 deletions Examples/Example6.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public void SetOffset(FileOffset offset, RVA rva) {

public uint GetFileLength() => (uint)heapData.Length;
public uint GetVirtualSize() => GetFileLength();
public uint CalculateAlignment() => 0;
public void WriteTo(DataWriter writer) => writer.WriteBytes(heapData);
}

Expand Down
14 changes: 14 additions & 0 deletions src/DotNet/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,5 +275,19 @@ static string GetCanonicalLocale(string locale) {
/// <param name="v">Value</param>
/// <param name="alignment">Alignment</param>
public static int AlignUp(int v, uint alignment) => (int)AlignUp((uint)v, alignment);

/// <summary>
/// Rounds up the provided number to the next power of 2
/// </summary>
/// <param name="num">The number to round</param>
public static uint RoundToNextPowerOfTwo(uint num) {
num--;
num |= num >> 1;
num |= num >> 2;
num |= num >> 4;
num |= num >> 8;
num |= num >> 16;
return num + 1;
}
}
}
5 changes: 4 additions & 1 deletion src/DotNet/Writer/ArrayWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ public unsafe struct ArrayWriter {
/// <summary>
/// Gets the current position
/// </summary>
public int Position => position;
public int Position {
get => position;
set => position = value;
}

readonly byte[] data;
int position;
Expand Down
10 changes: 9 additions & 1 deletion src/DotNet/Writer/ByteArrayChunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace dnlib.DotNet.Writer {
/// </summary>
public sealed class ByteArrayChunk : IReuseChunk {
readonly byte[] array;
readonly uint alignment;
FileOffset offset;
RVA rva;

Expand All @@ -32,7 +33,11 @@ public sealed class ByteArrayChunk : IReuseChunk {
/// <see cref="GetHashCode"/> return value will be different if you modify the array). If
/// it's never inserted as a <c>key</c> in a dictionary, then the contents can be modified,
/// but shouldn't be resized after <see cref="SetOffset"/> has been called.</param>
public ByteArrayChunk(byte[] array) => this.array = array ?? Array2.Empty<byte>();
/// <param name="alignment">The alignment of the data</param>
public ByteArrayChunk(byte[] array, uint alignment = 0) {
this.array = array ?? Array2.Empty<byte>();
this.alignment = alignment;
}

bool IReuseChunk.CanReuse(RVA origRva, uint origSize) => (uint)array.Length <= origSize;

Expand All @@ -48,6 +53,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => alignment;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) => writer.WriteBytes(array);

Expand Down
15 changes: 15 additions & 0 deletions src/DotNet/Writer/ChunkListBase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// dnlib: See LICENSE.txt for more info

using System;
using System.Collections.Generic;
using dnlib.IO;
using dnlib.PE;
Expand Down Expand Up @@ -114,5 +115,19 @@ public void WriteTo(DataWriter writer) {
offset2 += (uint)paddingF + elem.chunk.GetFileLength();
}
}

/// <inheritdoc/>
public virtual uint CalculateAlignment() {
uint alignment = 0;
for (int i = 0; i < chunks.Count; i++) {
var elem = chunks[i];
uint newAlignment = Math.Max(elem.alignment, elem.chunk.CalculateAlignment());
chunks[i] = new Elem(elem.chunk, newAlignment);

alignment = Math.Max(alignment, newAlignment);
}

return alignment;
}
}
}
3 changes: 3 additions & 0 deletions src/DotNet/Writer/DataReaderChunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
/// <inheritdoc/>
public uint GetVirtualSize() => virtualSize;

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
data.Position = 0;
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/DebugDirectory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ static uint GetLength(List<DebugDirectoryEntry> entries, FileOffset offset, RVA
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
uint offset = 0;
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/HeapBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public virtual void SetOffset(FileOffset offset, RVA rva) {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <summary>
/// Gets the raw length of the heap
/// </summary>
Expand Down
7 changes: 7 additions & 0 deletions src/DotNet/Writer/IChunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ public interface IChunk {
/// <returns>Virtual size of this chunk</returns>
uint GetVirtualSize();

/// <summary>
/// Calculates the requires alignment of this chunk.
/// Returns 0 for default/no alignment.
/// </summary>
/// <returns>Required alignment</returns>
uint CalculateAlignment();

/// <summary>
/// Writes all data to <paramref name="writer"/> at its current location. It's only
/// called after <see cref="SetOffset"/> and <see cref="GetFileLength"/> have been called.
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/ImageCor20Header.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
writer.WriteInt32(0x48); // cb
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/ImportAddressTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public uint GetFileLength() {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
if (!Enable)
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/ImportDirectory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ public uint GetFileLength() {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
if (!Enable)
Expand Down
4 changes: 4 additions & 0 deletions src/DotNet/Writer/ManagedExportsWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ sealed class ExportDir : IChunk {
void IChunk.SetOffset(FileOffset offset, RVA rva) => throw new NotSupportedException();
public uint GetFileLength() => owner.ExportDirSize;
public uint GetVirtualSize() => GetFileLength();
public uint CalculateAlignment() => 0;
void IChunk.WriteTo(DataWriter writer) => throw new NotSupportedException();
}

Expand All @@ -61,6 +62,7 @@ public void SetOffset(FileOffset offset, RVA rva) {
}
public uint GetFileLength() => length;
public uint GetVirtualSize() => GetFileLength();
public uint CalculateAlignment() => 0;
public void WriteTo(DataWriter writer) => owner.WriteVtableFixups(writer);
}

Expand All @@ -78,6 +80,7 @@ public void SetOffset(FileOffset offset, RVA rva) {
}
public uint GetFileLength() => length;
public uint GetVirtualSize() => GetFileLength();
public uint CalculateAlignment() => 0;
public void WriteTo(DataWriter writer) => owner.WriteStubs(writer);
}

Expand All @@ -95,6 +98,7 @@ public void SetOffset(FileOffset offset, RVA rva) {
}
public uint GetFileLength() => length;
public uint GetVirtualSize() => GetFileLength();
public uint CalculateAlignment() => 0;
public void WriteTo(DataWriter writer) => owner.WriteSdata(writer);
}

Expand Down
10 changes: 9 additions & 1 deletion src/DotNet/Writer/Metadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2704,7 +2704,12 @@ protected void AddFieldRVA(FieldDef field) {
if (!VerifyFieldSize(field, ivBytes.Length))
Error("Field '{0}' (0x{1:X8}) initial value size != size of field type.", field, field.MDToken.Raw);
uint rid = GetRid(field);
var iv = constants.Add(new ByteArrayChunk(ivBytes), ModuleWriterBase.DEFAULT_CONSTANTS_ALIGNMENT);

uint alignment = ModuleWriterBase.DEFAULT_CONSTANTS_ALIGNMENT;
if (field.FieldType is TypeDefOrRefSig tdrSig && tdrSig.TypeDef?.ClassLayout is {} classLayout)
alignment = Math.Max(alignment, Utils.RoundToNextPowerOfTwo(classLayout.PackingSize));

var iv = constants.Add(new ByteArrayChunk(ivBytes, alignment), alignment);
fieldToInitialValue[field] = iv;
var row = new RawFieldRVARow(0, rid);
fieldRVAInfos.Add(field, row);
Expand Down Expand Up @@ -3774,6 +3779,9 @@ IList<IHeap> GetHeaps() {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
var rva2 = rva;
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/MetadataHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
writer.WriteUInt32(options.Signature ?? MetadataHeaderOptions.DEFAULT_SIGNATURE);
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/MethodBody.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
writer.WriteBytes(code);
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/MethodBodyChunks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
var rva2 = rva;
Expand Down
7 changes: 7 additions & 0 deletions src/DotNet/Writer/ModuleWriterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -783,8 +783,15 @@ protected void CreateMetadataChunks(ModuleDef module) {
/// <param name="sectionAlignment">Section alignment</param>
protected void CalculateRvasAndFileOffsets(List<IChunk> chunks, FileOffset offset, RVA rva, uint fileAlignment, uint sectionAlignment) {
int count = chunks.Count;
var maxAlignment = Math.Min(fileAlignment, sectionAlignment);
for (int i = 0; i < count; i++) {
var chunk = chunks[i];
// We don't need to align to result of CalculateAlignment as all the chunks in `chunks` either
// don't need a specific alignment or align themselves.
var alignment = chunk.CalculateAlignment();
if (alignment > maxAlignment)
Error("Chunk alignment is too big. Chunk: {0}, alignment: {1:X4}", chunk, alignment);

chunk.SetOffset(offset, rva);
// If it has zero size, it's not present in the file (eg. a section that wasn't needed)
if (chunk.GetVirtualSize() != 0) {
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/NetResources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
var rva2 = rva;
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/PEHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ int SectionsCount {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

IEnumerable<SectionSizeInfo> GetSectionSizeInfos() {
foreach (var section in sections) {
uint virtSize = section.GetVirtualSize();
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/RelocDirectory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
bool is64bit = machine.Is64Bit();
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/StartupStub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ public uint GetFileLength() {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
if (!Enable)
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/StrongNameSignature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) => writer.WriteZeroes(size);
}
Expand Down
3 changes: 3 additions & 0 deletions src/DotNet/Writer/TablesHeap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,9 @@ public uint GetFileLength() {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <summary>
/// Calculates the length. This will set all MD tables to read-only.
/// </summary>
Expand Down
25 changes: 25 additions & 0 deletions src/DotNet/Writer/UniqueChunkList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,30 @@ public T Add(T chunk, uint alignment) {
chunks.Add(elem);
return elem.chunk;
}

/// <inheritdoc/>
public override uint CalculateAlignment() {
uint alignment = base.CalculateAlignment();

var keys = new KeyValuePair<int, Elem>[chunks.Count];
for (var i = 0; i < chunks.Count; i++)
keys[i] = new KeyValuePair<int, Elem>(i, chunks[i]);
Array.Sort(keys, DescendingStableComparer.Instance);
for (var i = 0; i < keys.Length; i++)
chunks[i] = keys[i].Value;

return alignment;
}

sealed class DescendingStableComparer : IComparer<KeyValuePair<int, Elem>> {
internal static readonly DescendingStableComparer Instance = new DescendingStableComparer();

public int Compare(KeyValuePair<int, Elem> x, KeyValuePair<int, Elem> y) {
var result = -x.Value.alignment.CompareTo(y.Value.alignment);
if (result != 0)
return result;
return x.Key.CompareTo(y.Key);
}
}
}
}
3 changes: 3 additions & 0 deletions src/DotNet/Writer/Win32ResourcesChunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ void FindDirectoryEntries(ResourceDirectory dir) {
/// <inheritdoc/>
public uint GetVirtualSize() => GetFileLength();

/// <inheritdoc/>
public uint CalculateAlignment() => 0;

/// <inheritdoc/>
public void WriteTo(DataWriter writer) {
uint offset = 0;
Expand Down

0 comments on commit c2d5c9e

Please sign in to comment.