Skip to content

Commit f7cb66d

Browse files
committed
Fix UpdateLayout
1 parent b7cef71 commit f7cb66d

18 files changed

+1236
-1008
lines changed

src/LibObjectFile.Tests/PE/PEReaderTests.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
using System.Linq;
88
using System.Runtime.InteropServices;
99
using System.Threading.Tasks;
10+
using LibObjectFile.Diagnostics;
1011
using LibObjectFile.PE;
1112
using VerifyMSTest;
13+
using VerifyTests;
1214

1315
namespace LibObjectFile.Tests.PE;
1416

@@ -23,10 +25,28 @@ public partial class PEReaderTests
2325

2426
public async Task TestPrinter(string name)
2527
{
26-
var stream = File.OpenRead(Path.Combine(AppContext.BaseDirectory, "PE", name));
28+
29+
await using var stream = File.OpenRead(Path.Combine(AppContext.BaseDirectory, "PE", name));
2730
var peImage = PEFile.Read(stream);
28-
var text = new StringWriter();
29-
peImage.Print(text);
30-
await Verifier.Verify(text).UseParameters(name);
31+
var afterReadWriter = new StringWriter();
32+
peImage.Print(afterReadWriter);
33+
34+
var afterReadText = afterReadWriter.ToString();
35+
36+
await Verifier.Verify(afterReadText).UseParameters(name);
37+
38+
// Update the layout
39+
var diagnostics = new DiagnosticBag();
40+
peImage.UpdateLayout(diagnostics);
41+
42+
var afterUpdateWriter = new StringWriter();
43+
peImage.Print(afterUpdateWriter);
44+
var afterUpdateText = afterUpdateWriter.ToString();
45+
46+
if (!string.Equals(afterReadText, afterUpdateText, StringComparison.Ordinal))
47+
{
48+
TestContext.WriteLine("Error while verifying UpdateLayout");
49+
await Verifier.Verify(afterUpdateText).UseParameters(name).DisableRequireUniquePrefix();
50+
}
3151
}
3252
}

src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsole2Win64.exe.verified.txt

Lines changed: 361 additions & 369 deletions
Large diffs are not rendered by default.

src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsoleWin64.exe.verified.txt

Lines changed: 280 additions & 288 deletions
Large diffs are not rendered by default.

src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeLibraryWin64.dll.verified.txt

Lines changed: 207 additions & 214 deletions
Large diffs are not rendered by default.

src/LibObjectFile/Ar/ArArchiveFile.cs

Lines changed: 2 additions & 2 deletions
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

@@ -325,7 +325,7 @@ public void Write(Stream stream)
325325
writer.Write();
326326
}
327327

328-
public override void UpdateLayout(ArVisitorContext visitorContext)
328+
public override void UpdateLayout(ArVisitorContext context)
329329
{
330330

331331
}

src/LibObjectFile/Diagnostics/DiagnosticId.cs

Lines changed: 37 additions & 38 deletions
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

@@ -125,37 +125,12 @@ public enum DiagnosticId
125125
PE_ERR_InvalidSectionHeadersSize = 3007,
126126
PE_ERR_InvalidParent = 3008,
127127
PE_ERR_InvalidExtraData = 3009,
128-
129-
// PE BaseRelocation
130-
PE_ERR_BaseRelocationDirectoryInvalidEndOfStream = 3020,
131-
PE_ERR_BaseRelocationDirectoryInvalidSection = 3021,
132-
PE_ERR_BaseRelocationDirectoryInvalidSectionData = 3022,
133-
PE_ERR_InvalidDataDirectorySection = 3023,
134-
PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress = 3024,
135-
136-
// PE Import
137-
PE_ERR_ImportDirectoryInvalidEndOfStream = 3040,
138-
PE_ERR_ImportLookupTableInvalidEndOfStream = 3041,
139-
PE_ERR_ImportLookupTableInvalidHintNameTableRVA = 3042,
140-
PE_ERR_ImportLookupTableInvalidParent = 3043,
141-
PE_ERR_ImportDirectoryInvalidImportAddressTableRVA = 3044,
142-
PE_ERR_ImportDirectoryInvalidImportLookupTableRVA = 3045,
143-
PE_ERR_ImportAddressTableNotFound = 3046,
144-
PE_ERR_InvalidInternalState = 3047,
145-
146-
// PE Export
147-
PE_ERR_ExportAddressTableInvalidRVA = 3060,
148-
PE_ERR_ExportDirectoryInvalidAddressOfNames = 3061,
149-
PE_ERR_ExportDirectoryInvalidAddressOfFunctions = 3062,
150-
PE_ERR_ExportDirectoryInvalidAddressOfNameOrdinals = 3063,
151-
PE_ERR_ExportDirectoryInvalidName = 3064,
152-
PE_ERR_ExportNameTableInvalidRVA = 3065,
153-
154-
// PE Resource directory
155-
PE_ERR_InvalidResourceDirectory = 3080,
156-
PE_ERR_InvalidResourceDirectoryEntry = 3081,
157-
PE_ERR_InvalidResourceDirectoryEntryRVAOffsetToData = 3082,
158-
128+
PE_ERR_SectionSizeLargerThanVirtualSize = 3010,
129+
PE_ERR_SectionRVALessThanPrevious = 3011,
130+
PE_ERR_TooManySections = 3012,
131+
PE_ERR_FileAlignmentNotPowerOfTwo = 3013,
132+
PE_ERR_SectionAlignmentNotPowerOfTwo = 3014,
133+
PE_ERR_SectionAlignmentLessThanFileAlignment = 315,
159134

160135
// PE Exception directory
161136
PE_ERR_InvalidExceptionDirectory_Entries = 3100,
@@ -165,12 +140,6 @@ public enum DiagnosticId
165140
// PE Certificate directory
166141
PE_ERR_InvalidCertificateEntry = 3200,
167142

168-
// PE TLS directory
169-
PE_ERR_InvalidTlsStartAddressOfRawData = 3300,
170-
PE_ERR_InvalidTlsEndAddressOfRawData = 3301,
171-
PE_ERR_InvalidTlsAddressOfIndex = 3302,
172-
PE_ERR_InvalidTlsAddressOfCallBacks = 3303,
173-
174143
// PE BoundImport directory
175144
PE_ERR_BoundImportDirectoryInvalidEndOfStream = 3400,
176145
PE_ERR_BoundImportDirectoryInvalidModuleName = 3401,
@@ -193,4 +162,34 @@ public enum DiagnosticId
193162
PE_ERR_InvalidDebugDataRSDSSignature = 3603,
194163
PE_ERR_InvalidDebugDataRSDSPdbPath = 3604,
195164
PE_ERR_DebugDirectoryExtraData = 3605,
165+
166+
// PE BaseRelocation
167+
PE_ERR_BaseRelocationDirectoryInvalidEndOfStream = 3700,
168+
PE_ERR_BaseRelocationDirectoryInvalidSection = 3701,
169+
PE_ERR_BaseRelocationDirectoryInvalidSectionData = 3702,
170+
PE_ERR_InvalidDataDirectorySection = 3703,
171+
PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress = 3704,
172+
173+
// PE Import
174+
PE_ERR_ImportDirectoryInvalidEndOfStream = 3800,
175+
PE_ERR_ImportLookupTableInvalidEndOfStream = 3801,
176+
PE_ERR_ImportLookupTableInvalidHintNameTableRVA = 3802,
177+
PE_ERR_ImportLookupTableInvalidParent = 3803,
178+
PE_ERR_ImportDirectoryInvalidImportAddressTableRVA = 3804,
179+
PE_ERR_ImportDirectoryInvalidImportLookupTableRVA = 3805,
180+
PE_ERR_ImportAddressTableNotFound = 3806,
181+
PE_ERR_InvalidInternalState = 3807,
182+
183+
// PE Export
184+
PE_ERR_ExportAddressTableInvalidRVA = 3900,
185+
PE_ERR_ExportDirectoryInvalidAddressOfNames = 3901,
186+
PE_ERR_ExportDirectoryInvalidAddressOfFunctions = 3902,
187+
PE_ERR_ExportDirectoryInvalidAddressOfNameOrdinals = 3903,
188+
PE_ERR_ExportDirectoryInvalidName = 3904,
189+
PE_ERR_ExportNameTableInvalidRVA = 3905,
190+
191+
// PE Resource directory
192+
PE_ERR_InvalidResourceDirectory = 4000,
193+
PE_ERR_InvalidResourceDirectoryEntry = 4001,
194+
PE_ERR_InvalidResourceDirectoryEntryRVAOffsetToData = 4002,
196195
}

src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs

Lines changed: 7 additions & 2 deletions
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

@@ -14,6 +14,11 @@ namespace LibObjectFile.PE;
1414
[DebuggerDisplay("{ToString(),nq}")]
1515
public readonly record struct PEBaseRelocation(PEBaseRelocationType Type, PESectionData? Container, RVO RVO) : IPELink<PESectionData>
1616
{
17+
/// <summary>
18+
/// Gets a value indicating whether the base relocation is zero padding.
19+
/// </summary>
20+
public bool IsZero => Type == PEBaseRelocationType.Absolute;
21+
1722
/// <summary>
1823
/// Reads the address from the section data.
1924
/// </summary>
@@ -60,5 +65,5 @@ public ulong ReadAddress(PEFile file)
6065
}
6166
}
6267

63-
public override string ToString() => $"{Type} {this.ToDisplayTextWithRVA()}";
68+
public override string ToString() => Type == PEBaseRelocationType.Absolute ? $"{Type} Zero Padding" : $"{Type} {this.ToDisplayTextWithRVA()}";
6469
}

src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs

Lines changed: 27 additions & 21 deletions
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

@@ -53,7 +53,15 @@ internal unsafe uint CalculateSizeOf()
5353
return (uint)BlockBuffer.Length;
5454
}
5555

56-
return (uint)(Relocations.Count * sizeof(ushort));
56+
var count = Relocations.Count;
57+
58+
// If we have an odd number of relocations, we need to add an extra 0x0
59+
if (count > 0 && (count & 1) != 0)
60+
{
61+
count++;
62+
}
63+
64+
return (uint)(count * sizeof(ushort));
5765
}
5866

5967
internal void ReadAndBind(PEImageReader reader)
@@ -62,35 +70,33 @@ internal void ReadAndBind(PEImageReader reader)
6270

6371
var relocSpan = MemoryMarshal.Cast<byte, RawImageBaseRelocation>(buffer.Span);
6472

65-
// Remove padding zeros at the end of the block
66-
if (relocSpan.Length > 0 && relocSpan[^1].IsZero)
67-
{
68-
relocSpan = relocSpan.Slice(0, relocSpan.Length - 1);
69-
}
70-
7173
var section = SectionLink.Container!;
7274
var blockBaseAddress = SectionLink.RVA();
7375

7476
// Iterate on all relocations
75-
foreach (var relocation in relocSpan)
77+
foreach (var rawReloc in relocSpan)
7678
{
77-
if (relocation.IsZero)
79+
PEBaseRelocation reloc;
80+
if (rawReloc.IsZero)
7881
{
79-
continue;
82+
reloc = new PEBaseRelocation();
8083
}
84+
else
85+
{
86+
var va = blockBaseAddress + rawReloc.OffsetInBlockPart;
8187

82-
var va = blockBaseAddress + relocation.OffsetInBlockPart;
88+
// Find the section data containing the virtual address
89+
if (!section.TryFindSectionData(va, out var sectionData))
90+
{
91+
reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section data containing the virtual address 0x{va:X4}");
92+
continue;
93+
}
8394

84-
// Find the section data containing the virtual address
85-
if (!section.TryFindSectionData(va, out var sectionData))
86-
{
87-
reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section data containing the virtual address 0x{va:X4}");
88-
continue;
89-
}
95+
var offsetInSectionData = va - sectionData.RVA;
96+
reloc = new PEBaseRelocation(rawReloc.Type, sectionData, offsetInSectionData);
9097

91-
var offsetInSectionData = va - sectionData.RVA;
92-
var newRelocation = new PEBaseRelocation(relocation.Type, sectionData, offsetInSectionData);
93-
Relocations.Add(newRelocation);
98+
}
99+
Relocations.Add(reloc);
94100
}
95101

96102
// Clear the buffer, as we don't need it anymore

src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs

Lines changed: 3 additions & 3 deletions
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

@@ -19,12 +19,12 @@ public PEBaseRelocationDirectory() : base(PEDataDirectoryKind.BaseRelocation)
1919

2020
public List<PEBaseRelocationBlock> Blocks { get; } = new();
2121

22-
protected override uint ComputeHeaderSize(PEVisitorContext context)
22+
protected override unsafe uint ComputeHeaderSize(PEVisitorContext context)
2323
{
2424
var size = 0U;
2525
foreach (var block in Blocks)
2626
{
27-
size += block.CalculateSizeOf();
27+
size += (uint)(block.CalculateSizeOf() + sizeof(ImageBaseRelocation));
2828
}
2929

3030
return size;

src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs

Lines changed: 9 additions & 9 deletions
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

@@ -43,22 +43,22 @@ public sealed override void UpdateLayout(PELayoutContext context)
4343

4444
// A directory could have a content in addition to the header
4545
// So we update the VirtualAddress of each content and update the layout
46-
var position = Position;
47-
foreach (var table in Content)
46+
var position = Position + headerSize;
47+
foreach (var subData in Content)
4848
{
49-
table.RVA = va;
49+
subData.RVA = va;
5050

5151
// Update layout will update virtual address
5252
if (!context.UpdateSizeOnly)
5353
{
54-
table.Position = position;
54+
subData.Position = position;
5555
}
5656

57-
table.UpdateLayout(context);
57+
subData.UpdateLayout(context);
5858

59-
va += (uint)table.Size;
60-
size += table.Size;
61-
position += table.Size;
59+
va += (uint)subData.Size;
60+
size += subData.Size;
61+
position += subData.Size;
6262
}
6363

6464
Size = size;

src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs

Lines changed: 16 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

@@ -146,6 +146,21 @@ internal void Set(PEDataDirectoryKind kind, PEDataDirectory? directory)
146146
_count++;
147147
}
148148
}
149+
150+
internal int CalculateNumberOfEntries()
151+
{
152+
int count = 0;
153+
ReadOnlySpan<PEObjectBase?> span = _entries;
154+
for(int i = 0; i < span.Length; i++)
155+
{
156+
if (_entries[i] is not null)
157+
{
158+
count = i + 1;
159+
}
160+
}
161+
162+
return count;
163+
}
149164

150165
[InlineArray(15)]
151166
private struct InternalArray

0 commit comments

Comments
 (0)