Skip to content

Commit

Permalink
Add full support for PE writing
Browse files Browse the repository at this point in the history
  • Loading branch information
xoofx committed Sep 30, 2024
1 parent 6b5147b commit f7d0c27
Show file tree
Hide file tree
Showing 94 changed files with 1,091 additions and 582 deletions.
1 change: 1 addition & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<ItemGroup>
<PackageVersion Include="MinVer" Version="6.0.0" />
<PackageVersion Include="MSTest" Version="3.6.0" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageVersion Include="Verify.DiffPlex" Version="3.1.0" />
<PackageVersion Include="Verify.MSTest" Version="26.6.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
Expand Down
83 changes: 82 additions & 1 deletion src/LibObjectFile.Tests/PE/PEReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// See the license.txt file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using LibObjectFile.Diagnostics;
Expand All @@ -25,7 +27,7 @@ public async Task TestPrinter(string name)

var sourceFile = Path.Combine(AppContext.BaseDirectory, "PE", name);
await using var stream = File.OpenRead(sourceFile);
var peImage = PEFile.Read(stream);
var peImage = PEFile.Read(stream, new() { EnableStackTrace = true });
var afterReadWriter = new StringWriter();
peImage.Print(afterReadWriter);

Expand Down Expand Up @@ -64,7 +66,86 @@ public async Task TestPrinter(string name)
// Compare the input and output buffer
CollectionAssert.AreEqual(inputBuffer, outputBuffer);
}

[DataTestMethod]
[DynamicData(nameof(GetWindowsExeAndDlls), DynamicDataSourceType.Method)]
public async Task TestWindows(string sourceFile)
{
if (!OperatingSystem.IsWindows())
{
Assert.Inconclusive("This test can only run on Windows");
return;
}

TestContext.WriteLine($"Testing {sourceFile}");
await using var stream = File.OpenRead(sourceFile);
var peImage = PEFile.Read(stream, new() { EnableStackTrace = true });

if (peImage.CoffHeader.PointerToSymbolTable != 0)
{
Assert.Inconclusive($"The file {sourceFile} contains a non supported symbol table");
return;
}

var sizeOfInitializedData = peImage.OptionalHeader.SizeOfInitializedData;

// Read in input as raw bytes
stream.Position = 0;
var inputBuffer = new byte[stream.Length];
stream.ReadExactly(inputBuffer);

peImage.UpdateLayout(new DiagnosticBag());

var newSizeOfInitializedData = peImage.OptionalHeader.SizeOfInitializedData;

if (newSizeOfInitializedData != sizeOfInitializedData)
{
TestContext.WriteLine($"SizeOfInitializedData changed from {sizeOfInitializedData} to {newSizeOfInitializedData}. Trying to reuse old size");
peImage.ForceSizeOfInitializedData = sizeOfInitializedData;
}


// Write the PE back to a byte buffer
var output = new MemoryStream();
peImage.Write(output);
output.Position = 0;
var outputBuffer = output.ToArray();

if (!inputBuffer.AsSpan().SequenceEqual(outputBuffer))
{
// Uncomment the following code to save the output file to compare it with the original file
//{
// var dir = Path.Combine(AppContext.BaseDirectory, "Errors");
// Directory.CreateDirectory(dir);

// var sourceFileName = Path.GetFileName(sourceFile);
// var outputFileName = Path.Combine(dir, $"{sourceFileName}.new");

// await File.WriteAllBytesAsync(outputFileName, outputBuffer);
//}

CollectionAssert.AreEqual(inputBuffer, outputBuffer);
}
}

public static IEnumerable<object[]> GetWindowsExeAndDlls()
{
if (!OperatingSystem.IsWindows())
{
yield break;
}

foreach (var file in Directory.EnumerateFiles(Environment.SystemDirectory, "*.exe", SearchOption.TopDirectoryOnly))
{
yield return [file];
}

foreach (var file in Directory.EnumerateFiles(Environment.SystemDirectory, "*.dll", SearchOption.TopDirectoryOnly))
{
yield return [file];
}
}

[TestMethod]
public async Task TestTinyExe97Bytes()
{
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,11 @@ Sections
[02] PEResourceDataEntry Position = 0x00002E48, Size = 0x00000010, RVA = 0x00007048, VirtualSize = 0x00000010
> CodePage = null, Data = PEResourceData { RVA = 0x7060, VirtualSize = 0x17D, Position = 0x2E60, Size = 0x17D }

[03] PEResourceData Position = 0x00002E60, Size = 0x0000017D, RVA = 0x00007060, VirtualSize = 0x0000017D
[03] PEStreamSectionData Position = 0x00002E58, Size = 0x00000008, RVA = 0x00007058, VirtualSize = 0x00000008

[04] PEResourceData Position = 0x00002E60, Size = 0x0000017D, RVA = 0x00007060, VirtualSize = 0x0000017D

[05] PEStreamSectionData Position = 0x00002FDD, Size = 0x00000003, RVA = 0x000071DD, VirtualSize = 0x00000003


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,9 +450,11 @@ Sections
[02] PEResourceDataEntry Position = 0x00002648, Size = 0x00000010, RVA = 0x00005048, VirtualSize = 0x00000010
> CodePage = null, Data = PEResourceData { RVA = 0x5060, VirtualSize = 0x91, Position = 0x2660, Size = 0x91 }

[03] PEResourceData Position = 0x00002660, Size = 0x00000091, RVA = 0x00005060, VirtualSize = 0x00000091
[03] PEStreamSectionData Position = 0x00002658, Size = 0x00000008, RVA = 0x00005058, VirtualSize = 0x00000008

[04] PEStreamSectionData Position = 0x000026F4, Size = 0x00000004, RVA = 0x000050F4, VirtualSize = 0x00000004
[04] PEResourceData Position = 0x00002660, Size = 0x00000091, RVA = 0x00005060, VirtualSize = 0x00000091

[05] PEStreamSectionData Position = 0x000026F1, Size = 0x00000007, RVA = 0x000050F1, VirtualSize = 0x00000007


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Ar/ArArchiveFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ public void Write(Stream stream)
writer.Write();
}

public override void UpdateLayout(ArVisitorContext context)
protected override void UpdateLayoutCore(ArVisitorContext context)
{

}
Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Ar/ArBinaryFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public override void Write(ArArchiveFileWriter writer)
}
}

public override void UpdateLayout(ArVisitorContext context)
protected override void UpdateLayoutCore(ArVisitorContext context)
{
Size = Stream != null ? (ulong) Stream.Length : 0;
}
Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Ar/ArElfFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public override void Write(ArArchiveFileWriter writer)
}
}

public override void UpdateLayout(ArVisitorContext context)
protected override void UpdateLayoutCore(ArVisitorContext context)
{
Size = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Ar/ArLongNamesTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public override void Write(ArArchiveFileWriter writer)
ArrayPool<byte>.Shared.Return(buffer);
}

public override void UpdateLayout(ArVisitorContext context)
protected override void UpdateLayoutCore(ArVisitorContext context)
{
Size = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Ar/ArSymbolTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ protected override bool PrintMembers(StringBuilder builder)
}


public override void UpdateLayout(ArVisitorContext context)
protected override void UpdateLayoutCore(ArVisitorContext context)
{
if (Parent == null) return;

Expand Down
17 changes: 14 additions & 3 deletions src/LibObjectFile/Diagnostics/DiagnosticBag.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.

Expand Down Expand Up @@ -33,6 +33,11 @@ public DiagnosticBag()
/// </summary>
public bool HasErrors { get; private set; }

/// <summary>
/// Gets or sets a value indicating whether to enable stack trace for each message.
/// </summary>
public bool EnableStackTrace { get; set; }

/// <summary>
/// Clear all messages.
/// </summary>
Expand Down Expand Up @@ -78,7 +83,10 @@ public void Log(DiagnosticMessage message)
public void Error(DiagnosticId id, string message, object? context = null)
{
if (message == null) throw new ArgumentNullException(nameof(message));
Log(new DiagnosticMessage(DiagnosticKind.Error, id, message, context));
Log(new DiagnosticMessage(DiagnosticKind.Error, id, message, context)
{
StackTrace = EnableStackTrace ? new StackTrace(1, true) : null
});
}

/// <summary>
Expand All @@ -90,7 +98,10 @@ public void Error(DiagnosticId id, string message, object? context = null)
public void Warning(DiagnosticId id, string message, object? context = null)
{
if (message == null) throw new ArgumentNullException(nameof(message));
Log(new DiagnosticMessage(DiagnosticKind.Warning, id, message, context));
Log(new DiagnosticMessage(DiagnosticKind.Warning, id, message, context)
{
StackTrace = EnableStackTrace ? new StackTrace(1, true) : null
});
}

public override string ToString()
Expand Down
1 change: 1 addition & 0 deletions src/LibObjectFile/Diagnostics/DiagnosticId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ public enum DiagnosticId
PE_ERR_ImportDirectoryInvalidImportLookupTableRVA = 3805,
PE_ERR_ImportAddressTableNotFound = 3806,
PE_ERR_InvalidInternalState = 3807,
PE_WRN_ImportLookupTableInvalidRVAOutOfRange = 3808,

// PE Export
PE_ERR_ExportAddressTableInvalidRVA = 3900,
Expand Down
18 changes: 16 additions & 2 deletions src/LibObjectFile/Diagnostics/DiagnosticMessage.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.

using System.Diagnostics;

namespace LibObjectFile.Diagnostics;

/// <summary>
Expand Down Expand Up @@ -45,8 +47,20 @@ public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string message, o
/// </summary>
public string Message { get; }

/// <summary>
/// Gets the associated stack trace of this message.
/// </summary>
public StackTrace? StackTrace { get; init; }

public override string ToString()
{
return $"{Kind} LB{(uint)Id:0000}: {Message}";
if (StackTrace is not null)
{
return $"{Kind} LB{(uint)Id:0000}: {Message}\n{StackTrace}";
}
else
{
return $"{Kind} LB{(uint)Id:0000}: {Message}";
}
}
}
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfAbbreviation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public override void Write(DwarfWriter writer)
Debug.Assert(writer.Position - startOffset == Size);
}

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
var endOffset = Position;

Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal DwarfAbbreviationItem(ulong code, DwarfTagEx tag, bool hasChildren, Dwa

public DwarfAttributeDescriptors Descriptors { get; private set; }

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
var endOffset = Position;

Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal void Reset()
_abbreviations.Clear();
}

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
ulong endOffset = Position;
foreach (var abbreviation in _abbreviations)
Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public override void Verify(DwarfVerifyContext context)
}
}

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
ulong sizeOf = 0;
// unit_length
Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ protected override bool PrintMembers(StringBuilder builder)
return true;
}

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
var addressSize = layoutContext.CurrentUnit!.AddressSize;
var is64BitEncoding = layoutContext.CurrentUnit.Is64BitEncoding;
Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfDIE.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ protected unsafe void SetAttributeValueOpt<TValue>(DwarfAttributeKind kind, TVal
}
}

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
var abbrev = Abbrev;

Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ internal void UpdateLayout(DwarfLayoutContext layoutContext, bool inLocationSect
Size = OperationLengthInBytes + deltaLength;
}

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
UpdateLayout(layoutContext, inLocationSection: false);
}
Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ private static void CheckErrors(DiagnosticBag diagnostics)
}
}

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
}

Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfInfoSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public override void Verify(DwarfVerifyContext context)
}
}

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
var offset = Position;
foreach (var unit in Units)
Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ protected override bool PrintMembers(StringBuilder builder)

private static string Bool2Str(bool value) => value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant();

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
}

Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ public override void Verify(DwarfVerifyContext context)
}
}

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
ulong sizeOf = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfLineSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public override void Verify(DwarfVerifyContext context)
}
}

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
ulong sizeOf = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfLineSequence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void Add(DwarfLine line)
_lines.Add(line);
}

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
// This is implemented in DwarfLineSection
}
Expand Down
2 changes: 1 addition & 1 deletion src/LibObjectFile/Dwarf/DwarfLocationList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public DwarfLocationList()

public ObjectList<DwarfLocationListEntry> LocationListEntries => _locationListEntries;

public override void UpdateLayout(DwarfLayoutContext layoutContext)
protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext)
{
var endOffset = Position;

Expand Down
Loading

0 comments on commit f7d0c27

Please sign in to comment.