diff --git a/Examples/Example6.cs b/Examples/Example6.cs
index 83a3ce48e..1a823fc18 100644
--- a/Examples/Example6.cs
+++ b/Examples/Example6.cs
@@ -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);
}
diff --git a/src/DotNet/Utils.cs b/src/DotNet/Utils.cs
index f9eeb8227..90d3797ce 100644
--- a/src/DotNet/Utils.cs
+++ b/src/DotNet/Utils.cs
@@ -275,5 +275,19 @@ static string GetCanonicalLocale(string locale) {
/// Value
/// Alignment
public static int AlignUp(int v, uint alignment) => (int)AlignUp((uint)v, alignment);
+
+ ///
+ /// Rounds up the provided number to the next power of 2
+ ///
+ /// The number to round
+ 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;
+ }
}
}
diff --git a/src/DotNet/Writer/ArrayWriter.cs b/src/DotNet/Writer/ArrayWriter.cs
index 721c0b46f..d997ee5d8 100644
--- a/src/DotNet/Writer/ArrayWriter.cs
+++ b/src/DotNet/Writer/ArrayWriter.cs
@@ -11,7 +11,10 @@ public unsafe struct ArrayWriter {
///
/// Gets the current position
///
- public int Position => position;
+ public int Position {
+ get => position;
+ set => position = value;
+ }
readonly byte[] data;
int position;
diff --git a/src/DotNet/Writer/ByteArrayChunk.cs b/src/DotNet/Writer/ByteArrayChunk.cs
index 0bd6c5f37..4a6b4c3a4 100644
--- a/src/DotNet/Writer/ByteArrayChunk.cs
+++ b/src/DotNet/Writer/ByteArrayChunk.cs
@@ -10,6 +10,7 @@ namespace dnlib.DotNet.Writer {
///
public sealed class ByteArrayChunk : IReuseChunk {
readonly byte[] array;
+ readonly uint alignment;
FileOffset offset;
RVA rva;
@@ -32,7 +33,11 @@ public sealed class ByteArrayChunk : IReuseChunk {
/// return value will be different if you modify the array). If
/// it's never inserted as a key in a dictionary, then the contents can be modified,
/// but shouldn't be resized after has been called.
- public ByteArrayChunk(byte[] array) => this.array = array ?? Array2.Empty();
+ /// The alignment of the data
+ public ByteArrayChunk(byte[] array, uint alignment = 0) {
+ this.array = array ?? Array2.Empty();
+ this.alignment = alignment;
+ }
bool IReuseChunk.CanReuse(RVA origRva, uint origSize) => (uint)array.Length <= origSize;
@@ -48,6 +53,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => alignment;
+
///
public void WriteTo(DataWriter writer) => writer.WriteBytes(array);
diff --git a/src/DotNet/Writer/ChunkListBase.cs b/src/DotNet/Writer/ChunkListBase.cs
index 321809ac2..b7bd2b40d 100644
--- a/src/DotNet/Writer/ChunkListBase.cs
+++ b/src/DotNet/Writer/ChunkListBase.cs
@@ -1,5 +1,6 @@
// dnlib: See LICENSE.txt for more info
+using System;
using System.Collections.Generic;
using dnlib.IO;
using dnlib.PE;
@@ -114,5 +115,19 @@ public void WriteTo(DataWriter writer) {
offset2 += (uint)paddingF + elem.chunk.GetFileLength();
}
}
+
+ ///
+ 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;
+ }
}
}
diff --git a/src/DotNet/Writer/DataReaderChunk.cs b/src/DotNet/Writer/DataReaderChunk.cs
index defaa43ec..cf9a20c0d 100644
--- a/src/DotNet/Writer/DataReaderChunk.cs
+++ b/src/DotNet/Writer/DataReaderChunk.cs
@@ -86,6 +86,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
///
public uint GetVirtualSize() => virtualSize;
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
data.Position = 0;
diff --git a/src/DotNet/Writer/DebugDirectory.cs b/src/DotNet/Writer/DebugDirectory.cs
index 3d62d6582..f8d09faaa 100644
--- a/src/DotNet/Writer/DebugDirectory.cs
+++ b/src/DotNet/Writer/DebugDirectory.cs
@@ -139,6 +139,9 @@ static uint GetLength(List entries, FileOffset offset, RVA
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
uint offset = 0;
diff --git a/src/DotNet/Writer/HeapBase.cs b/src/DotNet/Writer/HeapBase.cs
index 99831e387..567850e4c 100644
--- a/src/DotNet/Writer/HeapBase.cs
+++ b/src/DotNet/Writer/HeapBase.cs
@@ -51,6 +51,9 @@ public virtual void SetOffset(FileOffset offset, RVA rva) {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
/// Gets the raw length of the heap
///
diff --git a/src/DotNet/Writer/IChunk.cs b/src/DotNet/Writer/IChunk.cs
index d358d3020..061321a48 100644
--- a/src/DotNet/Writer/IChunk.cs
+++ b/src/DotNet/Writer/IChunk.cs
@@ -40,6 +40,13 @@ public interface IChunk {
/// Virtual size of this chunk
uint GetVirtualSize();
+ ///
+ /// Calculates the requires alignment of this chunk.
+ /// Returns 0 for default/no alignment.
+ ///
+ /// Required alignment
+ uint CalculateAlignment();
+
///
/// Writes all data to at its current location. It's only
/// called after and have been called.
diff --git a/src/DotNet/Writer/ImageCor20Header.cs b/src/DotNet/Writer/ImageCor20Header.cs
index 4385fd9c5..154c3fa6d 100644
--- a/src/DotNet/Writer/ImageCor20Header.cs
+++ b/src/DotNet/Writer/ImageCor20Header.cs
@@ -113,6 +113,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
writer.WriteInt32(0x48); // cb
diff --git a/src/DotNet/Writer/ImportAddressTable.cs b/src/DotNet/Writer/ImportAddressTable.cs
index 05ce2e9ee..d789e7890 100644
--- a/src/DotNet/Writer/ImportAddressTable.cs
+++ b/src/DotNet/Writer/ImportAddressTable.cs
@@ -47,6 +47,9 @@ public uint GetFileLength() {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
if (!Enable)
diff --git a/src/DotNet/Writer/ImportDirectory.cs b/src/DotNet/Writer/ImportDirectory.cs
index c871aed8f..23fe2b931 100644
--- a/src/DotNet/Writer/ImportDirectory.cs
+++ b/src/DotNet/Writer/ImportDirectory.cs
@@ -104,6 +104,9 @@ public uint GetFileLength() {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
if (!Enable)
diff --git a/src/DotNet/Writer/ManagedExportsWriter.cs b/src/DotNet/Writer/ManagedExportsWriter.cs
index 8f9bdf87a..6455b2ccb 100644
--- a/src/DotNet/Writer/ManagedExportsWriter.cs
+++ b/src/DotNet/Writer/ManagedExportsWriter.cs
@@ -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();
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
diff --git a/src/DotNet/Writer/Metadata.cs b/src/DotNet/Writer/Metadata.cs
index d683548e9..28e874ae3 100644
--- a/src/DotNet/Writer/Metadata.cs
+++ b/src/DotNet/Writer/Metadata.cs
@@ -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);
@@ -3774,6 +3779,9 @@ IList GetHeaps() {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
var rva2 = rva;
diff --git a/src/DotNet/Writer/MetadataHeader.cs b/src/DotNet/Writer/MetadataHeader.cs
index aabcd7372..4fe2841fe 100644
--- a/src/DotNet/Writer/MetadataHeader.cs
+++ b/src/DotNet/Writer/MetadataHeader.cs
@@ -135,6 +135,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
writer.WriteUInt32(options.Signature ?? MetadataHeaderOptions.DEFAULT_SIGNATURE);
diff --git a/src/DotNet/Writer/MethodBody.cs b/src/DotNet/Writer/MethodBody.cs
index b6c4cfef0..3c6464dd7 100644
--- a/src/DotNet/Writer/MethodBody.cs
+++ b/src/DotNet/Writer/MethodBody.cs
@@ -132,6 +132,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
writer.WriteBytes(code);
diff --git a/src/DotNet/Writer/MethodBodyChunks.cs b/src/DotNet/Writer/MethodBodyChunks.cs
index ef08416c3..63ab29ae2 100644
--- a/src/DotNet/Writer/MethodBodyChunks.cs
+++ b/src/DotNet/Writer/MethodBodyChunks.cs
@@ -171,6 +171,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
var rva2 = rva;
diff --git a/src/DotNet/Writer/ModuleWriterBase.cs b/src/DotNet/Writer/ModuleWriterBase.cs
index 5c77af110..e1b56d6fb 100644
--- a/src/DotNet/Writer/ModuleWriterBase.cs
+++ b/src/DotNet/Writer/ModuleWriterBase.cs
@@ -783,8 +783,15 @@ protected void CreateMetadataChunks(ModuleDef module) {
/// Section alignment
protected void CalculateRvasAndFileOffsets(List 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) {
diff --git a/src/DotNet/Writer/NetResources.cs b/src/DotNet/Writer/NetResources.cs
index e04a9cf6b..f2986f2ea 100644
--- a/src/DotNet/Writer/NetResources.cs
+++ b/src/DotNet/Writer/NetResources.cs
@@ -74,6 +74,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
var rva2 = rva;
diff --git a/src/DotNet/Writer/PEHeaders.cs b/src/DotNet/Writer/PEHeaders.cs
index 899f7ec9d..34502b779 100644
--- a/src/DotNet/Writer/PEHeaders.cs
+++ b/src/DotNet/Writer/PEHeaders.cs
@@ -325,6 +325,9 @@ int SectionsCount {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
IEnumerable GetSectionSizeInfos() {
foreach (var section in sections) {
uint virtSize = section.GetVirtualSize();
diff --git a/src/DotNet/Writer/RelocDirectory.cs b/src/DotNet/Writer/RelocDirectory.cs
index ab3c332b1..6b7d4c03e 100644
--- a/src/DotNet/Writer/RelocDirectory.cs
+++ b/src/DotNet/Writer/RelocDirectory.cs
@@ -81,6 +81,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
bool is64bit = machine.Is64Bit();
diff --git a/src/DotNet/Writer/StartupStub.cs b/src/DotNet/Writer/StartupStub.cs
index e5c36f64d..fc212cead 100644
--- a/src/DotNet/Writer/StartupStub.cs
+++ b/src/DotNet/Writer/StartupStub.cs
@@ -82,6 +82,9 @@ public uint GetFileLength() {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
if (!Enable)
diff --git a/src/DotNet/Writer/StrongNameSignature.cs b/src/DotNet/Writer/StrongNameSignature.cs
index 088f8e3ff..c485cba6b 100644
--- a/src/DotNet/Writer/StrongNameSignature.cs
+++ b/src/DotNet/Writer/StrongNameSignature.cs
@@ -38,6 +38,9 @@ public void SetOffset(FileOffset offset, RVA rva) {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) => writer.WriteZeroes(size);
}
diff --git a/src/DotNet/Writer/TablesHeap.cs b/src/DotNet/Writer/TablesHeap.cs
index c79ac83a8..a578a7938 100644
--- a/src/DotNet/Writer/TablesHeap.cs
+++ b/src/DotNet/Writer/TablesHeap.cs
@@ -331,6 +331,9 @@ public uint GetFileLength() {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
/// Calculates the length. This will set all MD tables to read-only.
///
diff --git a/src/DotNet/Writer/UniqueChunkList.cs b/src/DotNet/Writer/UniqueChunkList.cs
index c8cf0f15b..225bf801c 100644
--- a/src/DotNet/Writer/UniqueChunkList.cs
+++ b/src/DotNet/Writer/UniqueChunkList.cs
@@ -53,5 +53,30 @@ public T Add(T chunk, uint alignment) {
chunks.Add(elem);
return elem.chunk;
}
+
+ ///
+ public override uint CalculateAlignment() {
+ uint alignment = base.CalculateAlignment();
+
+ var keys = new KeyValuePair[chunks.Count];
+ for (var i = 0; i < chunks.Count; i++)
+ keys[i] = new KeyValuePair(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> {
+ internal static readonly DescendingStableComparer Instance = new DescendingStableComparer();
+
+ public int Compare(KeyValuePair x, KeyValuePair y) {
+ var result = -x.Value.alignment.CompareTo(y.Value.alignment);
+ if (result != 0)
+ return result;
+ return x.Key.CompareTo(y.Key);
+ }
+ }
}
}
diff --git a/src/DotNet/Writer/Win32ResourcesChunk.cs b/src/DotNet/Writer/Win32ResourcesChunk.cs
index 3898d9cd3..cf75e819b 100644
--- a/src/DotNet/Writer/Win32ResourcesChunk.cs
+++ b/src/DotNet/Writer/Win32ResourcesChunk.cs
@@ -353,6 +353,9 @@ void FindDirectoryEntries(ResourceDirectory dir) {
///
public uint GetVirtualSize() => GetFileLength();
+ ///
+ public uint CalculateAlignment() => 0;
+
///
public void WriteTo(DataWriter writer) {
uint offset = 0;