Skip to content

Commit 6b5147b

Browse files
committed
Fix PE writer, first version working in unit tests
1 parent ad8d599 commit 6b5147b

17 files changed

+190
-65
lines changed

src/LibObjectFile.Tests/PE/PEReaderTests.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ public partial class PEReaderTests
2323
public async Task TestPrinter(string name)
2424
{
2525

26-
await using var stream = File.OpenRead(Path.Combine(AppContext.BaseDirectory, "PE", name));
26+
var sourceFile = Path.Combine(AppContext.BaseDirectory, "PE", name);
27+
await using var stream = File.OpenRead(sourceFile);
2728
var peImage = PEFile.Read(stream);
2829
var afterReadWriter = new StringWriter();
2930
peImage.Print(afterReadWriter);
@@ -45,6 +46,23 @@ public async Task TestPrinter(string name)
4546
TestContext.WriteLine("Error while verifying UpdateLayout");
4647
await Verifier.Verify(afterUpdateText).UseParameters(name).DisableRequireUniquePrefix();
4748
}
49+
50+
// Read in input as raw bytes
51+
stream.Position = 0;
52+
var inputBuffer = new byte[stream.Length];
53+
stream.ReadExactly(inputBuffer);
54+
55+
// Write the PE back to a byte buffer
56+
var output = new MemoryStream();
57+
peImage.Write(output);
58+
output.Position = 0;
59+
var outputBuffer = output.ToArray();
60+
61+
//await Verifier.Verify(outputBuffer, sourceFile sourceFile).
62+
await File.WriteAllBytesAsync($"{sourceFile}.bak", outputBuffer);
63+
64+
// Compare the input and output buffer
65+
CollectionAssert.AreEqual(inputBuffer, outputBuffer);
4866
}
4967

5068
[TestMethod]

src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,26 @@ public override unsafe void Read(PEImageReader reader)
152152
Debug.Assert(Size == block.SizeOfBlock);
153153
}
154154

155+
public override unsafe void Write(PEImageWriter writer)
156+
{
157+
var block = new ImageBaseRelocation
158+
{
159+
PageRVA = SectionLink.RVA(),
160+
SizeOfBlock = (uint)Size
161+
};
162+
163+
writer.Write(block);
164+
165+
var span = CollectionsMarshal.AsSpan(Relocations);
166+
var spanBytes = MemoryMarshal.AsBytes(span);
167+
writer.Write(spanBytes);
168+
169+
if ((Relocations.Count & 1) != 0)
170+
{
171+
writer.WriteZero((int)sizeof(PEBaseRelocation));
172+
}
173+
}
174+
155175
protected override bool PrintMembers(StringBuilder builder)
156176
{
157177
if (base.PrintMembers(builder))

src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,28 @@
66

77
namespace LibObjectFile.PE;
88

9+
/// <summary>
10+
/// Represents the CLR metadata directory in a PE file.
11+
/// </summary>
912
public sealed class PEClrMetadata : PEDataDirectory
1013
{
14+
/// <summary>
15+
/// Initializes a new instance of the <see cref="PEClrMetadata"/> class.
16+
/// </summary>
1117
public PEClrMetadata() : base(PEDataDirectoryKind.ClrMetadata)
1218
{
1319
}
1420

15-
protected override uint ComputeHeaderSize(PELayoutContext context)
16-
{
17-
return 0;
18-
}
21+
/// <inheritdoc/>
22+
protected override uint ComputeHeaderSize(PELayoutContext context) => 0;
1923

24+
/// <inheritdoc/>
2025
public override void Read(PEImageReader reader)
2126
{
2227
}
2328

29+
/// <inheritdoc/>
2430
public override void Write(PEImageWriter writer)
2531
{
2632
}
27-
}
33+
}

src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the license.txt file in the project root for more information.
44

55
using System.Collections.Generic;
6+
using System.Diagnostics;
67
using System.Linq;
78
using LibObjectFile.Collections;
89
using LibObjectFile.Utils;
@@ -86,7 +87,24 @@ internal void WriteHeaderAndContent(PEImageWriter writer)
8687

8788
foreach (var table in Content)
8889
{
90+
var position = writer.Position;
91+
var alignment = table.GetRequiredPositionAlignment(writer.PEFile);
92+
if (alignment > 1)
93+
{
94+
var zeroSize = AlignHelper.AlignUp(position, alignment) - (uint)position;
95+
writer.WriteZero((int)zeroSize);
96+
}
97+
98+
Debug.Assert(table.Position == writer.Position);
99+
89100
table.Write(writer);
101+
102+
alignment = table.GetRequiredSizeAlignment(writer.PEFile);
103+
if (alignment > 1)
104+
{
105+
var zeroSize = AlignHelper.AlignUp((uint)table.Size, alignment) - table.Size;
106+
writer.WriteZero((int)zeroSize);
107+
}
90108
}
91109
}
92110

src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,11 @@ public override void Write(PEImageWriter writer)
134134
var entries = CollectionsMarshal.AsSpan(Entries);
135135
using var tempSpan = TempSpan<RawImageDebugDirectory>.Create(entries.Length, out var rawEntries);
136136

137-
RawImageDebugDirectory rawEntry = default;
138137
for (var i = 0; i < entries.Length; i++)
139138
{
140139
var entry = entries[i];
140+
ref var rawEntry = ref rawEntries[i];
141+
141142
rawEntry.Characteristics = entry.Characteristics;
142143
rawEntry.MajorVersion = entry.MajorVersion;
143144
rawEntry.MinorVersion = entry.MinorVersion;
@@ -147,15 +148,21 @@ public override void Write(PEImageWriter writer)
147148
if (entry.SectionData is not null)
148149
{
149150
rawEntry.SizeOfData = (uint)entry.SectionData.Size;
150-
rawEntry.AddressOfRawData = (uint)entry.SectionData.RVA;
151-
rawEntry.PointerToRawData = 0;
151+
rawEntry.AddressOfRawData = entry.SectionData.RVA;
152+
rawEntry.PointerToRawData = (uint)entry.SectionData.Position;
152153
}
153154
else if (entry.ExtraData is not null)
154155
{
155156
rawEntry.SizeOfData = (uint)entry.ExtraData.Size;
156157
rawEntry.AddressOfRawData = 0;
157158
rawEntry.PointerToRawData = (uint)entry.ExtraData.Position;
158159
}
160+
else
161+
{
162+
rawEntry.SizeOfData = 0;
163+
rawEntry.AddressOfRawData = 0;
164+
rawEntry.PointerToRawData = 0;
165+
}
159166

160167
rawEntries[i] = rawEntry;
161168
}

src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Buffers;
77
using System.Diagnostics;
8+
using System.Runtime.CompilerServices;
89
using System.Runtime.InteropServices;
910
using System.Text;
1011
using LibObjectFile.Collections;
@@ -18,6 +19,8 @@ namespace LibObjectFile.PE;
1819
[DebuggerDisplay("{ToString(),nq}")]
1920
public sealed class PEDebugSectionDataRSDS : PEDebugSectionData
2021
{
22+
private const uint Signature = 0x53445352;
23+
2124
/// <summary>
2225
/// Initializes a new instance of the <see cref="PEDebugSectionDataRSDS"/> class.
2326
/// </summary>
@@ -57,7 +60,7 @@ public override unsafe void Read(PEImageReader reader)
5760
}
5861

5962
var signature = MemoryMarshal.Read<uint>(span);
60-
if (signature != 0x53445352)
63+
if (signature != Signature)
6164
{
6265
reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidDebugDataRSDSSignature, $"Invalid signature for PEDebugDataRSDS");
6366
return;
@@ -74,8 +77,36 @@ public override unsafe void Read(PEImageReader reader)
7477
Guid = MemoryMarshal.Read<Guid>(span.Slice(4));
7578
Age = MemoryMarshal.Read<uint>(span.Slice(sizeof(uint) + sizeof(Guid)));
7679
PdbPath = System.Text.Encoding.UTF8.GetString(pdbPath.Slice(0, indexOfZero));
80+
81+
Debug.Assert(size == CalculateSize());
7782
}
7883

84+
public override unsafe void Write(PEImageWriter writer)
85+
{
86+
var size = (int)Size;
87+
using var tempSpan = TempSpan<byte>.Create(size, out var span);
88+
89+
MemoryMarshal.Write(span, Signature);
90+
span = span.Slice(sizeof(uint));
91+
MemoryMarshal.Write(span, Guid);
92+
span = span.Slice(sizeof(Guid));
93+
MemoryMarshal.Write(span, Age);
94+
span = span.Slice(sizeof(uint));
95+
int written = Encoding.UTF8.GetBytes(PdbPath, span);
96+
span[written] = 0;
97+
span.Slice(written + 1);
98+
99+
writer.Write(tempSpan.AsBytes);
100+
}
101+
102+
public override void UpdateLayout(PELayoutContext layoutContext)
103+
{
104+
Size = CalculateSize();
105+
}
106+
107+
private unsafe uint CalculateSize()
108+
=> (uint)(sizeof(uint) + sizeof(Guid) + sizeof(uint) + Encoding.UTF8.GetByteCount(PdbPath) + 1);
109+
79110
/// <inheritdoc />
80111
protected override bool PrintMembers(StringBuilder builder)
81112
{

src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Diagnostics;
1010
using System.Reflection;
1111
using System.Runtime.CompilerServices;
12+
using LibObjectFile.Collections;
1213
using LibObjectFile.PE.Internal;
1314

1415
namespace LibObjectFile.PE;
@@ -191,17 +192,22 @@ internal void Set(int index, PEObjectBase? directory)
191192

192193
internal unsafe void Write(PEImageWriter writer, ref uint position)
193194
{
195+
using var tempSpan = TempSpan<RawImageDataDirectory>.Create(stackalloc byte[16 * sizeof(RawImageDataDirectory)], Count, out var span);
196+
span.Clear();
197+
194198
for (int i = 0; i < Count; i++)
195199
{
196-
RawImageDataDirectory rawDataDirectory = default;
197200
var entry = _entries[i];
198201
if (entry is not null)
199202
{
200-
rawDataDirectory.RVA = entry is PEDataDirectory dataDirectory ? dataDirectory.RVA : (uint)entry.Position;
201-
rawDataDirectory.Size = (uint)entry.Size;
203+
ref var rawEntry = ref span[i];
204+
rawEntry.RVA = entry is PEDataDirectory dataDirectory ? dataDirectory.RVA : (uint)entry.Position;
205+
rawEntry.Size = (uint)entry.Size;
202206
}
203207
}
204208

209+
writer.Write(tempSpan);
210+
205211
position += (uint)(Count * sizeof(RawImageDataDirectory));
206212
}
207213

src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,10 @@
22
// This file is licensed under the BSD-Clause 2 license.
33
// See the license.txt file in the project root for more information.
44

5-
using System;
6-
using System.Buffers;
75
using System.Collections.Generic;
8-
using System.ComponentModel;
9-
using System.Diagnostics;
106
using System.Runtime.InteropServices;
11-
using System.Security.Cryptography;
12-
using System.Xml.Linq;
137
using LibObjectFile.Collections;
148
using LibObjectFile.Diagnostics;
15-
using static System.Collections.Specialized.BitVector32;
169

1710
namespace LibObjectFile.PE;
1811

@@ -87,6 +80,14 @@ public override unsafe void Read(PEImageReader reader)
8780

8881
public override void Write(PEImageWriter writer)
8982
{
90-
throw new NotImplementedException();
83+
using var tempSpan = TempSpan<RVA>.Create(Values.Count, out var spanRva);
84+
85+
for (int i = 0; i < Values.Count; i++)
86+
{
87+
var value = Values[i];
88+
spanRva[i] = value.IsForwarderRVA ? value.ForwarderRVA.RVA() : value.ExportRVA.RVA();
89+
}
90+
91+
writer.Write(tempSpan);
9192
}
9293
}

src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// This file is licensed under the BSD-Clause 2 license.
33
// See the license.txt file in the project root for more information.
44

5-
using System;
6-
using System.Buffers;
75
using System.Collections.Generic;
86
using System.Runtime.InteropServices;
97
using LibObjectFile.Collections;
@@ -66,6 +64,14 @@ public override unsafe void Read(PEImageReader reader)
6664

6765
public override void Write(PEImageWriter writer)
6866
{
69-
throw new NotImplementedException();
67+
using var tempSpan = TempSpan<RVA>.Create(Values.Count, out var spanRva);
68+
69+
for (int i = 0; i < Values.Count; i++)
70+
{
71+
var value = Values[i];
72+
spanRva[i] = value.RVA();
73+
}
74+
75+
writer.Write(tempSpan.AsBytes);
7076
}
7177
}

src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Alexandre Mutel. All rights reserved.
1+
// Copyright (c) Alexandre Mutel. All rights reserved.
22
// This file is licensed under the BSD-Clause 2 license.
33
// See the license.txt file in the project root for more information.
44

@@ -42,4 +42,10 @@ public override void Read(PEImageReader reader)
4242
return;
4343
}
4444
}
45+
46+
public override void Write(PEImageWriter writer)
47+
{
48+
var span = CollectionsMarshal.AsSpan(Values);
49+
writer.Write(MemoryMarshal.AsBytes(span));
50+
}
4551
}

0 commit comments

Comments
 (0)