diff --git a/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsole2Win64.exe.verified.txt b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsole2Win64.exe.verified.txt
index 31bd030..b8a3e31 100644
--- a/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsole2Win64.exe.verified.txt
+++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsole2Win64.exe.verified.txt
@@ -605,11 +605,20 @@ Sections
[04] .rsrc PESection Position = 0x00003E00, Size = 0x00000200, RVA = 0x00007000, VirtualSize = 0x000001E0, Characteristics = 0x40000040 (ContainsInitializedData, MemRead)
[00] PEResourceDirectory Position = 0x00003E00, Size = 0x000001E0, RVA = 0x00007000, VirtualSize = 0x000001E0
- > PEResourceDirectoryEntry { Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0}
- > PEResourceDirectoryEntry { Id = 0x18 (RT_MANIFEST), Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0}
- > PEResourceDirectoryEntry { Id = 0x1, Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0}
- > PEResourceDataEntry { Id = 0x409 (en-US), Data = Stream (381 bytes) }
- [00] PEStreamSectionData Position = 0x00003FD8, Size = 0x00000008, RVA = 0x000071D8, VirtualSize = 0x00000008
+ > ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, Version = 0.0
+ [0] Id = 0x18, Entry = PEResourceDirectoryEntry { RVA = 0x7018, VirtualSize = 0x18, Position = 0x3E18, Size = 0x18, ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0 }
+ [00] PEResourceDirectoryEntry Position = 0x00003E18, Size = 0x00000018, RVA = 0x00007018, VirtualSize = 0x00000018
+ > ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, Version = 0.0
+ [0] Id = 0x1, Entry = PEResourceDirectoryEntry { RVA = 0x7030, VirtualSize = 0x18, Position = 0x3E30, Size = 0x18, ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0 }
+
+ [01] PEResourceDirectoryEntry Position = 0x00003E30, Size = 0x00000018, RVA = 0x00007030, VirtualSize = 0x00000018
+ > ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, Version = 0.0
+ [0] Id = 0x409, Entry = PEResourceDataEntry { RVA = 0x7048, VirtualSize = 0x10, Position = 0x3E48, Size = 0x10, CodePage = , Data = PEResourceData { RVA = 0x7060, VirtualSize = 0x17D, Position = 0x3E60, Size = 0x17D } }
+
+ [02] PEResourceDataEntry Position = 0x00003E48, Size = 0x00000010, RVA = 0x00007048, VirtualSize = 0x00000010
+ > CodePage = null, Data = PEResourceData { RVA = 0x7060, VirtualSize = 0x17D, Position = 0x3E60, Size = 0x17D }
+
+ [03] PEResourceData Position = 0x00003E60, Size = 0x0000017D, RVA = 0x00007060, VirtualSize = 0x0000017D
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
diff --git a/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsoleWin64.exe.verified.txt b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsoleWin64.exe.verified.txt
index c62d765..87af661 100644
--- a/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsoleWin64.exe.verified.txt
+++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsoleWin64.exe.verified.txt
@@ -512,11 +512,20 @@ Sections
[04] .rsrc PESection Position = 0x00002E00, Size = 0x00000200, RVA = 0x00007000, VirtualSize = 0x000001E0, Characteristics = 0x40000040 (ContainsInitializedData, MemRead)
[00] PEResourceDirectory Position = 0x00002E00, Size = 0x000001E0, RVA = 0x00007000, VirtualSize = 0x000001E0
- > PEResourceDirectoryEntry { Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0}
- > PEResourceDirectoryEntry { Id = 0x18 (RT_MANIFEST), Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0}
- > PEResourceDirectoryEntry { Id = 0x1, Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0}
- > PEResourceDataEntry { Id = 0x409 (en-US), Data = Stream (381 bytes) }
- [00] PEStreamSectionData Position = 0x00002FD8, Size = 0x00000008, RVA = 0x000071D8, VirtualSize = 0x00000008
+ > ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, Version = 0.0
+ [0] Id = 0x18, Entry = PEResourceDirectoryEntry { RVA = 0x7018, VirtualSize = 0x18, Position = 0x2E18, Size = 0x18, ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0 }
+ [00] PEResourceDirectoryEntry Position = 0x00002E18, Size = 0x00000018, RVA = 0x00007018, VirtualSize = 0x00000018
+ > ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, Version = 0.0
+ [0] Id = 0x1, Entry = PEResourceDirectoryEntry { RVA = 0x7030, VirtualSize = 0x18, Position = 0x2E30, Size = 0x18, ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0 }
+
+ [01] PEResourceDirectoryEntry Position = 0x00002E30, Size = 0x00000018, RVA = 0x00007030, VirtualSize = 0x00000018
+ > ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, Version = 0.0
+ [0] Id = 0x409, Entry = PEResourceDataEntry { RVA = 0x7048, VirtualSize = 0x10, Position = 0x2E48, Size = 0x10, CodePage = , Data = PEResourceData { RVA = 0x7060, VirtualSize = 0x17D, Position = 0x2E60, Size = 0x17D } }
+
+ [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
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
diff --git a/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeLibraryWin64.dll.verified.txt b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeLibraryWin64.dll.verified.txt
index 4d8d3d7..ba98b56 100644
--- a/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeLibraryWin64.dll.verified.txt
+++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeLibraryWin64.dll.verified.txt
@@ -436,11 +436,22 @@ Sections
[04] .rsrc PESection Position = 0x00002600, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x000000F8, Characteristics = 0x40000040 (ContainsInitializedData, MemRead)
[00] PEResourceDirectory Position = 0x00002600, Size = 0x000000F8, RVA = 0x00005000, VirtualSize = 0x000000F8
- > PEResourceDirectoryEntry { Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0}
- > PEResourceDirectoryEntry { Id = 0x18 (RT_MANIFEST), Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0}
- > PEResourceDirectoryEntry { Id = 0x2, Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0}
- > PEResourceDataEntry { Id = 0x409 (en-US), Data = Stream (145 bytes) }
- [00] PEStreamSectionData Position = 0x000026EC, Size = 0x0000000C, RVA = 0x000050EC, VirtualSize = 0x0000000C
+ > ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, Version = 0.0
+ [0] Id = 0x18, Entry = PEResourceDirectoryEntry { RVA = 0x5018, VirtualSize = 0x18, Position = 0x2618, Size = 0x18, ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0 }
+ [00] PEResourceDirectoryEntry Position = 0x00002618, Size = 0x00000018, RVA = 0x00005018, VirtualSize = 0x00000018
+ > ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, Version = 0.0
+ [0] Id = 0x2, Entry = PEResourceDirectoryEntry { RVA = 0x5030, VirtualSize = 0x18, Position = 0x2630, Size = 0x18, ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0 }
+
+ [01] PEResourceDirectoryEntry Position = 0x00002630, Size = 0x00000018, RVA = 0x00005030, VirtualSize = 0x00000018
+ > ByNames[0], ByIds[1] , TimeDateStamp = 01/01/1970 00:00:00, Version = 0.0
+ [0] Id = 0x409, Entry = PEResourceDataEntry { RVA = 0x5048, VirtualSize = 0x10, Position = 0x2648, Size = 0x10, CodePage = , Data = PEResourceData { RVA = 0x5060, VirtualSize = 0x91, Position = 0x2660, Size = 0x91 } }
+
+ [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
+
+ [04] PEStreamSectionData Position = 0x000026F4, Size = 0x00000004, RVA = 0x000050F4, VirtualSize = 0x00000004
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
diff --git a/src/LibObjectFile/ObjectFileElement.cs b/src/LibObjectFile/ObjectFileElement.cs
index bb6603e..bd3cad8 100644
--- a/src/LibObjectFile/ObjectFileElement.cs
+++ b/src/LibObjectFile/ObjectFileElement.cs
@@ -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.
diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs
index 32ce896..8c9424d 100644
--- a/src/LibObjectFile/ObjectFileReaderWriter.cs
+++ b/src/LibObjectFile/ObjectFileReaderWriter.cs
@@ -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.
@@ -18,6 +18,7 @@ namespace LibObjectFile;
public abstract class ObjectFileReaderWriter : VisitorContextBase
{
private Stream _stream;
+ private readonly byte[] _zeroBuffer = new byte[1024];
internal ObjectFileReaderWriter(ObjectFileElement file, Stream stream) : this(file, stream, new DiagnosticBag())
{
@@ -192,6 +193,20 @@ public void Write(byte[] buffer, int offset, int count)
Stream.Write(buffer, offset, count);
}
+ ///
+ /// Writes count bytes with zero.
+ ///
+ /// The number of bytes to write with zero.
+ public void WriteZero(int count)
+ {
+ while (count > 0)
+ {
+ var size = Math.Min(count, _zeroBuffer.Length);
+ Stream.Write(_zeroBuffer, 0, size);
+ count -= size;
+ }
+ }
+
///
/// Writes to the and current position from the specified buffer.
///
diff --git a/src/LibObjectFile/PE/DataDirectory/PEArchitectureDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEArchitectureDirectory.cs
index 8285b80..a6f782e 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEArchitectureDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEArchitectureDirectory.cs
@@ -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.
@@ -18,7 +18,7 @@ public PEArchitectureDirectory() : base(PEDataDirectoryKind.Architecture)
{
}
- protected override uint ComputeHeaderSize(PEVisitorContext context)
+ protected override uint ComputeHeaderSize(PELayoutContext context)
{
return 0;
}
diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs
index 29fcd97..e034cc5 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs
@@ -19,7 +19,7 @@ public PEBaseRelocationDirectory() : base(PEDataDirectoryKind.BaseRelocation)
public List Blocks { get; } = new();
- protected override unsafe uint ComputeHeaderSize(PEVisitorContext context)
+ protected override unsafe uint ComputeHeaderSize(PELayoutContext context)
{
var size = 0U;
foreach (var block in Blocks)
@@ -44,8 +44,6 @@ public override unsafe void Read(PEImageReader reader)
return;
}
- var allSectionData = reader.File.GetAllSectionData();
-
int blockIndex = 0;
while (buffer.Length > 0)
{
@@ -59,7 +57,6 @@ public override unsafe void Read(PEImageReader reader)
}
var sizeOfRelocations = (int)location.SizeOfBlock - sizeof(ImageBaseRelocation);
-
// Create a block
var block = new PEBaseRelocationBlock(new PESectionLink(section, (uint)(location.PageRVA - section.RVA)))
@@ -89,7 +86,12 @@ internal override void Bind(PEImageReader reader)
public override void Write(PEImageWriter writer)
{
- throw new NotImplementedException();
+ ImageBaseRelocation rawBlock = default;
+ foreach (var block in Blocks)
+ {
+ rawBlock.PageRVA = block.SectionLink.RVA();
+ rawBlock.SizeOfBlock = block.CalculateSizeOf();
+ }
}
protected override bool PrintMembers(StringBuilder builder)
diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs
index 4d998aa..7ce6601 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs
@@ -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.
@@ -30,9 +30,10 @@ public PEBoundImportDirectory() : base(PEDataDirectoryKind.BoundImport)
public List Entries { get; }
///
- protected override uint ComputeHeaderSize(PEVisitorContext context)
+ protected override unsafe uint ComputeHeaderSize(PELayoutContext context)
{
- var size = 0u;
+ // Last raw entry is zero
+ var size = (uint)sizeof(RawPEBoundImportDirectory);
var entries = CollectionsMarshal.AsSpan(Entries);
foreach (var entry in entries)
{
@@ -156,6 +157,23 @@ internal override void Bind(PEImageReader reader)
///
public override void Write(PEImageWriter writer)
{
- throw new NotImplementedException();
+ RawPEBoundImportDirectory rawEntry = default;
+ RawPEBoundImportForwarderRef rawForwarderRef = default;
+ foreach (var entry in Entries)
+ {
+ rawEntry.OffsetModuleName = (ushort)entry.ModuleName.RVO;
+ rawEntry.NumberOfModuleForwarderRefs = (ushort)entry.ForwarderRefs.Count;
+ writer.Write(rawEntry);
+
+ foreach (var forwarderRef in entry.ForwarderRefs)
+ {
+ rawForwarderRef.OffsetModuleName = (ushort)forwarderRef.ModuleName.RVO;
+ writer.Write(rawForwarderRef);
+ }
+ }
+
+ // Last entry is null
+ rawEntry = default;
+ writer.Write(rawEntry);
}
}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectoryEntry.cs
index adb076a..66edea8 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectoryEntry.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectoryEntry.cs
@@ -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.
diff --git a/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs b/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs
index 74fb09e..8964545 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs
@@ -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.
@@ -12,7 +12,7 @@ public PEClrMetadata() : base(PEDataDirectoryKind.ClrMetadata)
{
}
- protected override uint ComputeHeaderSize(PEVisitorContext context)
+ protected override uint ComputeHeaderSize(PELayoutContext context)
{
return 0;
}
diff --git a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs
new file mode 100644
index 0000000..56895bc
--- /dev/null
+++ b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs
@@ -0,0 +1,107 @@
+// 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.Collections.Generic;
+using System.Linq;
+using LibObjectFile.Collections;
+using LibObjectFile.Utils;
+using static System.Runtime.InteropServices.JavaScript.JSType;
+
+namespace LibObjectFile.PE;
+
+///
+/// A section data that contains a list of and an optional header of data.
+///
+public abstract class PECompositeSectionData : PESectionData
+{
+ protected PECompositeSectionData()
+ {
+ Content = CreateObjectList(this);
+ }
+
+ public sealed override bool HasChildren => true;
+
+ internal uint HeaderSize { get; private protected set; }
+
+ ///
+ /// Gets the content of this directory.
+ ///
+ public ObjectList Content { get; }
+
+ public sealed override void UpdateLayout(PELayoutContext context)
+ {
+ var va = RVA;
+
+ // We compute the size of the directory header
+ // Each directory have a specific layout, so we delegate the computation to the derived class
+ var headerSize = ComputeHeaderSize(context);
+ HeaderSize = headerSize;
+ va += headerSize;
+ ulong size = headerSize;
+
+ // A directory could have a content in addition to the header
+ // So we update the VirtualAddress of each content and update the layout
+ var position = Position + headerSize;
+ foreach (var data in Content)
+ {
+ // Make sure we align the position and the virtual address
+ var alignment = data.GetRequiredPositionAlignment(context.File);
+
+ if (alignment > 1)
+ {
+ var newPosition = AlignHelper.AlignUp(position, alignment);
+ size += (uint)newPosition - position;
+ position = newPosition;
+ va = AlignHelper.AlignUp(va, alignment);
+ }
+
+ data.RVA = va;
+
+ // Update layout will update virtual address
+ if (!context.UpdateSizeOnly)
+ {
+ data.Position = position;
+ }
+ data.UpdateLayout(context);
+
+ var dataSize = AlignHelper.AlignUp((uint)data.Size, data.GetRequiredSizeAlignment(context.File));
+ va += (uint)dataSize;
+ size += dataSize;
+ position += dataSize;
+ }
+
+ Size = size;
+ }
+
+ internal virtual IEnumerable CollectImplicitSectionDataList() => Enumerable.Empty();
+
+ internal virtual void Bind(PEImageReader reader)
+ {
+ }
+
+ internal void WriteHeaderAndContent(PEImageWriter writer)
+ {
+ Write(writer);
+
+ foreach (var table in Content)
+ {
+ table.Write(writer);
+ }
+ }
+
+ protected abstract uint ComputeHeaderSize(PELayoutContext context);
+
+ protected sealed override bool TryFindByRVAInChildren(RVA rva, out PEObject? result)
+ => Content.TryFindByRVA(rva, true, out result);
+
+ protected sealed override void UpdateRVAInChildren()
+ {
+ var va = RVA;
+ foreach (var table in Content)
+ {
+ table.UpdateRVA(va);
+ va += (uint)table.Size;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs
index 8f10e87..a9bec2f 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs
@@ -3,75 +3,18 @@
// See the license.txt file in the project root for more information.
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection.Metadata.Ecma335;
-using LibObjectFile.Collections;
-using LibObjectFile.Diagnostics;
namespace LibObjectFile.PE;
-public abstract class PEDataDirectory : PESectionData
+public abstract class PEDataDirectory : PECompositeSectionData
{
protected PEDataDirectory(PEDataDirectoryKind kind)
{
Kind = kind;
- Content = CreateObjectList(this);
}
public PEDataDirectoryKind Kind { get; }
- public override bool HasChildren => true;
-
- internal uint HeaderSize { get; private protected set; }
-
- ///
- /// Gets the content of this directory.
- ///
- public ObjectList Content { get; }
-
- public sealed override void UpdateLayout(PELayoutContext context)
- {
- var va = RVA;
-
- // We compute the size of the directory header
- // Each directory have a specific layout, so we delegate the computation to the derived class
- var headerSize = ComputeHeaderSize(context);
- HeaderSize = headerSize;
- va += headerSize;
- ulong size = headerSize;
-
- // A directory could have a content in addition to the header
- // So we update the VirtualAddress of each content and update the layout
- var position = Position + headerSize;
- foreach (var subData in Content)
- {
- subData.RVA = va;
-
- // Update layout will update virtual address
- if (!context.UpdateSizeOnly)
- {
- subData.Position = position;
- }
-
- subData.UpdateLayout(context);
-
- va += (uint)subData.Size;
- size += subData.Size;
- position += subData.Size;
- }
-
- Size = size;
- }
-
- internal virtual IEnumerable CollectImplicitSectionDataList() => Enumerable.Empty();
-
- internal virtual void Bind(PEImageReader reader)
- {
- }
-
- protected abstract uint ComputeHeaderSize(PEVisitorContext context);
-
protected override void ValidateParent(ObjectElement parent)
{
if (parent is not PESection)
@@ -80,19 +23,6 @@ protected override void ValidateParent(ObjectElement parent)
}
}
- protected override bool TryFindByRVAInChildren(RVA rva, out PEObject? result)
- => Content.TryFindByRVA(rva, true, out result);
-
- protected override void UpdateRVAInChildren()
- {
- var va = RVA;
- foreach (var table in Content)
- {
- table.UpdateRVA(va);
- va += (uint)table.Size;
- }
- }
-
///
/// Factory method to create a new instance of based on the kind.
///
diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs
index 0b75f2a..d30d140 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs
@@ -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.
@@ -27,10 +27,6 @@ public override unsafe void Read(PEImageReader reader)
var entryCount = size / sizeof(RawImageDebugDirectory);
- var positionBeforeFirstSection = reader.File.Sections.Count > 0 ? reader.File.Sections[0].Position : 0;
- var positionAfterLastSection = reader.File.Sections.Count > 0 ? reader.File.Sections[^1].Position + reader.File.Sections[^1].Size : 0;
-
-
var buffer = ArrayPool.Shared.Rent(size);
try
{
@@ -137,12 +133,51 @@ internal override IEnumerable CollectImplicitSectionDataList()
}
}
- public override void Write(PEImageWriter writer)
+ public override unsafe void Write(PEImageWriter writer)
{
- throw new NotImplementedException();
+ var entries = CollectionsMarshal.AsSpan(Entries);
+ var rawBufferSize = sizeof(RawImageDebugDirectory) * entries.Length;
+ var rawBuffer = ArrayPool.Shared.Rent(rawBufferSize);
+ try
+ {
+ var buffer = new Span(rawBuffer, 0, rawBufferSize);
+ var rawEntries = MemoryMarshal.Cast(buffer);
+
+ RawImageDebugDirectory rawEntry = default;
+ for (var i = 0; i < entries.Length; i++)
+ {
+ var entry = entries[i];
+ rawEntry.Characteristics = entry.Characteristics;
+ rawEntry.MajorVersion = entry.MajorVersion;
+ rawEntry.MinorVersion = entry.MinorVersion;
+ rawEntry.TimeDateStamp = entry.TimeDateStamp;
+ rawEntry.Type = entry.Type;
+
+ if (entry.SectionData is not null)
+ {
+ rawEntry.SizeOfData = (uint)entry.SectionData.Size;
+ rawEntry.AddressOfRawData = (uint)entry.SectionData.RVA;
+ rawEntry.PointerToRawData = 0;
+ }
+ else if (entry.ExtraData is not null)
+ {
+ rawEntry.SizeOfData = (uint)entry.ExtraData.Size;
+ rawEntry.AddressOfRawData = 0;
+ rawEntry.PointerToRawData = (uint)entry.ExtraData.Position;
+ }
+
+ rawEntries[i] = rawEntry;
+ }
+
+ writer.Write(rawBuffer);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(rawBuffer);
+ }
}
- protected override unsafe uint ComputeHeaderSize(PEVisitorContext context)
+ protected override unsafe uint ComputeHeaderSize(PELayoutContext context)
{
return (uint)(Entries.Count * sizeof(RawImageDebugDirectory));
}
diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionData.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionData.cs
index 97adf2b..d366726 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionData.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionData.cs
@@ -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.
diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs
index ed253ba..9e407f8 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs
@@ -1,10 +1,11 @@
-// 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 LibObjectFile.Diagnostics;
using LibObjectFile.PE.Internal;
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.Runtime.InteropServices;
@@ -203,7 +204,7 @@ internal override void Bind(PEImageReader reader)
}
}
- protected override uint ComputeHeaderSize(PEVisitorContext context) => CalculateSize();
+ protected override uint ComputeHeaderSize(PELayoutContext context) => CalculateSize();
private unsafe uint CalculateSize()
@@ -212,8 +213,40 @@ private unsafe uint CalculateSize()
}
- public override void Write(PEImageWriter writer)
+ public override unsafe void Write(PEImageWriter writer)
{
- throw new NotImplementedException();
+ var entries = CollectionsMarshal.AsSpan(Entries);
+ var rawBufferSize = sizeof(RawDelayLoadDescriptor) * (entries.Length + 1);
+ var rawBuffer = ArrayPool.Shared.Rent((int)rawBufferSize);
+ try
+ {
+ var buffer = new Span(rawBuffer, 0, (int)rawBufferSize);
+ var rawEntries = MemoryMarshal.Cast(buffer);
+
+ RawDelayLoadDescriptor rawEntry = default;
+ for (var i = 0; i < entries.Length; i++)
+ {
+ var entry = entries[i];
+ rawEntry.Attributes = entry.Attributes;
+ rawEntry.NameRVA = (uint)entry.DllName.RVA();
+ rawEntry.ModuleHandleRVA = (uint)entry.ModuleHandle.RVA();
+ rawEntry.DelayLoadImportAddressTableRVA = (uint)entry.DelayImportAddressTable.RVA;
+ rawEntry.DelayLoadImportNameTableRVA = (uint)entry.DelayImportNameTable.RVA;
+ rawEntry.BoundDelayLoadImportAddressTableRVA = entry.BoundImportAddressTable?.RVA ?? 0;
+ rawEntry.UnloadDelayLoadImportAddressTableRVA = entry.UnloadDelayInformationTable?.RVA ?? 0;
+ rawEntry.TimeDateStamp = 0;
+
+ rawEntries[i] = rawEntry;
+ }
+
+ // Write the null entry
+ rawEntries[entries.Length] = default;
+
+ writer.Write(rawBuffer);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(rawBuffer);
+ }
}
}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs
index 7bfc095..2c8614a 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs
@@ -161,6 +161,23 @@ internal int CalculateNumberOfEntries()
return count;
}
+
+ internal unsafe void Write(PEImageWriter writer, ref uint position)
+ {
+ var numberOfEntries = CalculateNumberOfEntries();
+ for (int i = 0; i < numberOfEntries; i++)
+ {
+ ImageDataDirectory rawDataDirectory = default;
+ var entry = _entries[i];
+ if (entry is not null)
+ {
+ rawDataDirectory.RVA = entry is PEDataDirectory dataDirectory ? dataDirectory.RVA : (uint)entry.Position;
+ rawDataDirectory.Size = (uint)entry.Size;
+ }
+ }
+
+ position += (uint)(numberOfEntries * sizeof(ImageDataDirectory));
+ }
[InlineArray(15)]
private struct InternalArray
diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs
index c974bdd..f248f82 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs
@@ -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.
@@ -32,7 +32,7 @@ public PEExceptionDirectory() : base(PEDataDirectoryKind.Exception)
public List Entries { get; }
///
- protected override unsafe uint ComputeHeaderSize(PEVisitorContext context)
+ protected override unsafe uint ComputeHeaderSize(PELayoutContext context)
{
var machine = context.File.CoffHeader.Machine;
uint entrySize;
@@ -227,7 +227,48 @@ internal override void Bind(PEImageReader reader)
///
public override void Write(PEImageWriter writer)
{
- throw new NotImplementedException();
+ var machine = writer.PEFile.CoffHeader.Machine;
+ switch (machine)
+ {
+ case Machine.Amd64:
+ case Machine.I386:
+ WriteX86(writer);
+ break;
+ case Machine.Arm:
+ case Machine.Arm64:
+ WriteArm(writer);
+ break;
+ default:
+ // We don't write the exception directory for other architectures
+ return;
+ }
+ }
+
+ private void WriteX86(PEImageWriter writer)
+ {
+ foreach (var entry in Entries)
+ {
+ var entryX86 = (PEExceptionFunctionEntryX86)entry;
+ writer.Write(new RawExceptionFunctionEntryX86
+ {
+ BeginAddress = (uint)entryX86.BeginAddress.RVA(),
+ EndAddress = (uint)entryX86.EndAddress.RVA(),
+ UnwindInfoAddress = (uint)entryX86.UnwindInfoAddress.RVA()
+ });
+ }
+ }
+
+ private void WriteArm(PEImageWriter writer)
+ {
+ foreach (var entry in Entries)
+ {
+ var entryARM = (PEExceptionFunctionEntryArm)entry;
+ writer.Write(new RawExceptionFunctionEntryARM
+ {
+ BeginAddress = (uint)entryARM.BeginAddress.RVA(),
+ UnwindData = entryARM.UnwindData
+ });
+ }
}
private void ReadEntriesArm(Span rawEntries)
diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs
index 22d4579..545d30f 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs
@@ -32,7 +32,7 @@ public PEExportDirectory() : base(PEDataDirectoryKind.Export)
public PEExportOrdinalTable? ExportOrdinalTable { get; set; }
- protected override unsafe uint ComputeHeaderSize(PEVisitorContext context)
+ protected override unsafe uint ComputeHeaderSize(PELayoutContext context)
{
return (uint)sizeof(RawImageExportDirectory);
}
@@ -157,7 +157,21 @@ internal override void Bind(PEImageReader reader)
public override void Write(PEImageWriter writer)
{
- throw new NotImplementedException();
+ var exportDirectory = new RawImageExportDirectory
+ {
+ TimeDateStamp = (uint)(TimeStamp - DateTime.UnixEpoch).TotalSeconds,
+ MajorVersion = MajorVersion,
+ MinorVersion = MinorVersion,
+ Base = OrdinalBase,
+ Name = NameLink.RVA(),
+ NumberOfFunctions = (uint)ExportFunctionAddressTable!.Values.Count,
+ NumberOfNames = (uint)ExportNameTable!.Values.Count,
+ AddressOfFunctions = (RVA)(uint)(ExportFunctionAddressTable?.RVA ?? (RVA)0),
+ AddressOfNames = (RVA)(uint)(ExportNameTable?.RVA ?? 0),
+ AddressOfNameOrdinals = (RVA)(uint)(ExportOrdinalTable?.RVA ?? 0)
+ };
+
+ writer.Write(exportDirectory);
}
private struct RawImageExportDirectory
diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs
index a9976d0..600bc21 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs
@@ -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.
diff --git a/src/LibObjectFile/PE/DataDirectory/PEGlobalPointerDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEGlobalPointerDirectory.cs
index 3018097..e0f4b9e 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEGlobalPointerDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEGlobalPointerDirectory.cs
@@ -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.
@@ -18,7 +18,7 @@ public PEGlobalPointerDirectory() : base(PEDataDirectoryKind.GlobalPointer)
{
}
- protected override uint ComputeHeaderSize(PEVisitorContext context)
+ protected override uint ComputeHeaderSize(PELayoutContext context)
{
return 0;
}
diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs
index a60c264..36b5629 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs
@@ -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.
@@ -18,7 +18,9 @@ public override void Read(PEImageReader reader)
{
}
- public override void Write(PEImageWriter writer) => throw new NotSupportedException(); // Not called directly for this object, we are calling on tables directly
+ public override void Write(PEImageWriter writer)
+ {
+ }
- protected override uint ComputeHeaderSize(PEVisitorContext context) => 0;
+ protected override uint ComputeHeaderSize(PELayoutContext context) => 0;
}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs
index 96ca935..7dd5feb 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs
@@ -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.
@@ -101,7 +101,7 @@ public override void Read(PEImageReader reader)
}
}
- protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) => CalculateSize();
+ protected override unsafe uint ComputeHeaderSize(PELayoutContext context) => CalculateSize();
internal override IEnumerable CollectImplicitSectionDataList()
{
@@ -151,17 +151,22 @@ internal override void Bind(PEImageReader reader)
private unsafe uint CalculateSize()
{
- return _entries.Count == 0 ? 0 : (uint)(((_entries.Count + 1) * sizeof(RawImportDirectoryEntry)));
+ return (uint)(((_entries.Count + 1) * sizeof(RawImportDirectoryEntry)));
}
public override void Write(PEImageWriter writer)
{
- throw new NotImplementedException();
- }
+ RawImportDirectoryEntry rawEntry = default;
+ foreach (var entry in Entries)
+ {
+ rawEntry.NameRVA = (uint)entry.ImportDllNameLink.RVA();
+ rawEntry.ImportLookupTableRVA = (uint)entry.ImportLookupTable.RVA;
+ rawEntry.ImportAddressTableRVA = (uint)entry.ImportAddressTable.RVA;
+ writer.Write(rawEntry);
+ }
- //private struct HintNameTableEntry
- //{
- // public ushort Hint;
- // public byte Name1stByte;
- //}
+ // Null entry
+ rawEntry = default;
+ writer.Write(rawEntry);
+ }
}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs
index 967fb00..8e06b9a 100644
--- a/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs
@@ -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.
@@ -96,7 +96,7 @@ public override void Write(PEImageWriter writer)
public override void WriteAt(uint offset, ReadOnlySpan source) => DataUtils.WriteAt(_rawData, offset, source);
- protected override uint ComputeHeaderSize(PEVisitorContext context)
+ protected override uint ComputeHeaderSize(PELayoutContext context)
// Size if the first field of the Load Configuration Directory
=> (uint)RawDataSize;
}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceData.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceData.cs
new file mode 100644
index 0000000..643aa0f
--- /dev/null
+++ b/src/LibObjectFile/PE/DataDirectory/PEResourceData.cs
@@ -0,0 +1,17 @@
+// 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.
+
+namespace LibObjectFile.PE;
+
+///
+/// A section data that contains a resource data.
+///
+public sealed class PEResourceData : PEStreamSectionData
+{
+ public PEResourceData()
+ {
+ RequiredPositionAlignment = 4;
+ RequiredSizeAlignment = 4;
+ }
+}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs
index dfbf466..d45c9da 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs
@@ -1,12 +1,11 @@
-// 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;
-using System.Diagnostics;
-using System.IO;
using System.Text;
+using LibObjectFile.Diagnostics;
using LibObjectFile.PE.Internal;
+using LibObjectFile.Utils;
namespace LibObjectFile.PE;
@@ -15,27 +14,28 @@ namespace LibObjectFile.PE;
///
public sealed class PEResourceDataEntry : PEResourceEntry
{
- [DebuggerBrowsable(DebuggerBrowsableState.Never)]
- private object? _data;
+ internal PEResourceDataEntry()
+ {
+ Data = null!;
+ }
///
- /// Initializes a new instance of the class with the specified name.
+ /// Initializes a new instance of the class.
///
- /// The name of the resource data entry.
- public PEResourceDataEntry(string name) : base(name)
+ public PEResourceDataEntry(PEResourceData data)
{
- Data = Stream.Null;
+ Data = data;
}
///
- /// Initializes a new instance of the class with the specified ID.
+ /// Initializes a new instance of the class.
///
- /// The ID of the resource data entry.
- public PEResourceDataEntry(PEResourceId id) : base(id)
+ public PEResourceDataEntry(Encoding? codePage, PEResourceData data)
{
- Data = Stream.Null;
+ CodePage = codePage;
+ Data = data;
}
-
+
///
/// Gets or sets the code page used for encoding the data.
///
@@ -50,60 +50,69 @@ public PEResourceDataEntry(PEResourceId id) : base(id)
///
/// The data can be a string, a stream, or a byte array.
///
- public object? Data
+ public PEResourceData Data { get; set; }
+
+ protected override bool PrintMembers(StringBuilder builder)
{
- get => _data;
- set
+ if (base.PrintMembers(builder))
{
- if (value is not string && value is not Stream && value is not byte[])
- {
- throw new ArgumentException("Invalid data type. Expecting a string, a Stream or a byte[]");
- }
-
- _data = value;
+ builder.Append(", ");
}
+
+ builder.Append($"{nameof(CodePage)} = {CodePage?.EncodingName}, {nameof(Data)} = {Data}");
+ return true;
}
- private protected override unsafe uint ComputeSize()
+ public override unsafe void UpdateLayout(PELayoutContext layoutContext)
{
- uint dataSize = 0;
+ Size = (uint)sizeof(RawImageResourceDataEntry);
+ }
- if (Data is string text)
- {
- dataSize = (uint)(CodePage?.GetByteCount(text) ?? text.Length * 2);
- }
- else if (Data is Stream stream)
+ internal override unsafe void Read(in ReaderContext context)
+ {
+ var reader = context.Reader;
+
+ reader.Position = Position;
+ Size = (uint)sizeof(RawImageResourceDataEntry);
+
+ RawImageResourceDataEntry rawDataEntry;
+ if (!reader.TryReadData(sizeof(RawImageResourceDataEntry), out rawDataEntry))
{
- dataSize = (uint)stream.Length;
+ reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource data entry at position {reader.Position}");
+ return;
}
- else if (Data is byte[] buffer)
+
+ CodePage = rawDataEntry.CodePage != 0 ? Encoding.GetEncoding((int)rawDataEntry.CodePage) : null;
+
+ var peFile = context.Reader.File;
+ if (!peFile.TryFindSection(rawDataEntry.OffsetToData, out var section))
{
- dataSize = (uint)buffer.Length;
+ reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntryRVAOffsetToData, $"Invalid resource data entry RVA OffsetToData {rawDataEntry.OffsetToData} at position {reader.Position}");
+ return;
}
- return (uint)(sizeof(RawImageResourceDataEntry) + dataSize);
- }
+ Data = new PEResourceData
+ {
+ Position = section.Position + rawDataEntry.OffsetToData - section.RVA,
+ Size = rawDataEntry.Size,
+ };
- protected override bool PrintMembers(StringBuilder builder)
- {
- if (base.PrintMembers(builder))
+ // If we find that the position is not aligned on 4 bytes as we expect, reset it to 1 byte alignment
+ var checkPosition = AlignHelper.AlignUp(Data.Position, Data.RequiredPositionAlignment);
+ if (checkPosition != Data.Position)
{
- builder.Append(", ");
+ Data.RequiredPositionAlignment = 1;
}
-
- switch (Data)
+ else if (context.ResourceDataList.Count == 0 && (Data.Position & 0xF) == 0)
{
- case string text:
- builder.Append($"Data = {text}");
- break;
- case Stream stream:
- builder.Append($"Data = Stream ({stream.Length} bytes)");
- break;
- case byte[] buffer:
- builder.Append($"Data = byte[{buffer.Length}]");
- break;
+ // If we are the first resource data entry and the position is aligned on 16 bytes, we can assume this alignment
+ Data.RequiredPositionAlignment = 16;
}
+
+ // Read the data
+ Data.Read(reader);
- return true;
+ // Register the list of data being loaded
+ context.ResourceDataList.Add(Data);
}
-}
+}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs
index 1cef981..434c314 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs
@@ -1,25 +1,20 @@
-// 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;
-using System.Buffers;
-using System.Collections;
using System.Collections.Generic;
-using System.ComponentModel;
-using System.Runtime.InteropServices;
-using System.Text;
-using LibObjectFile.Diagnostics;
-using LibObjectFile.PE.Internal;
-using LibObjectFile.Utils;
namespace LibObjectFile.PE;
///
/// Represents a resource directory in a Portable Executable (PE) file.
///
-public sealed class PEResourceDirectory : PEDataDirectory, IEnumerable
+public sealed class PEResourceDirectory : PEDataDirectory
{
+ private List? _tempResourceDataList;
+
+
///
/// Initializes a new instance of the class.
///
@@ -35,173 +30,46 @@ public PEResourceDirectory() : base(PEDataDirectoryKind.Resource)
/// Gets the root resource directory entry.
///
public PEResourceDirectoryEntry Root { get; }
-
+
///
- protected override uint ComputeHeaderSize(PEVisitorContext context)
+ protected override uint ComputeHeaderSize(PELayoutContext context)
{
- var size = Root.ComputeFullSize();
- size = (uint)AlignHelper.AlignUp(size, 4);
- return size;
+ Root.UpdateLayout(context);
+ return (uint)Root.Size;
}
///
public override void Read(PEImageReader reader)
{
reader.Position = Position;
- var currentDirectory = Root;
- ReadDirectory(reader, currentDirectory);
-
- HeaderSize = ComputeHeaderSize(reader);
- }
-
- private unsafe void ReadDirectory(PEImageReader reader, PEResourceDirectoryEntry currentDirectory)
- {
- if (!reader.TryReadData(sizeof(RawImageResourceDirectory), out var data))
- {
- reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource directory at position {reader.Position}");
- return;
- }
-
- currentDirectory.TimeDateStamp = DateTime.UnixEpoch.AddSeconds(data.TimeDateStamp);
- currentDirectory.MajorVersion = data.MajorVersion;
- currentDirectory.MinorVersion = data.MinorVersion;
- var buffer = new byte[(data.NumberOfNamedEntries + data.NumberOfIdEntries) * sizeof(RawImageResourceDirectoryEntry)];
- var spanEntries = MemoryMarshal.Cast(buffer);
-
- int read = reader.Read(buffer);
- if (read != buffer.Length)
- {
- reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource directory at position {reader.Position}");
- return;
- }
+ Root.Position = Position;
- for (int i = 0; i < data.NumberOfNamedEntries + data.NumberOfIdEntries; i++)
- {
- var entry = spanEntries[i];
- ReadEntry(reader, currentDirectory, entry);
+ _tempResourceDataList = new();
+ var readerContext = new PEResourceEntry.ReaderContext(reader, this, _tempResourceDataList);
+ Root.Read(readerContext);
- if (reader.Diagnostics.HasErrors)
- {
- return;
- }
- }
+ HeaderSize = ComputeHeaderSize(reader);
}
- private unsafe void ReadEntry(PEImageReader reader, PEResourceDirectoryEntry parent, RawImageResourceDirectoryEntry rawEntry)
+ internal override IEnumerable CollectImplicitSectionDataList()
{
- string? name = null;
- int id = 0;
-
- if ((rawEntry.NameOrId & IMAGE_RESOURCE_NAME_IS_STRING) != 0)
- {
- // Read the string
- var length = reader.ReadU16();
- var buffer = ArrayPool.Shared.Rent(length);
- try
- {
- int readLength = reader.Read(buffer, 0, length);
- if (readLength != length)
- {
- reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource directory string at position {reader.Position}");
- return;
- }
- name = Encoding.Unicode.GetString(buffer, 0, readLength);
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
- }
- else
- {
- id = (int)(rawEntry.NameOrId & ~IMAGE_RESOURCE_NAME_IS_STRING);
- }
-
-
- bool isDirectory = (rawEntry.OffsetToDataOrDirectoryEntry & IMAGE_RESOURCE_DATA_IS_DIRECTORY) != 0;
- var offset = rawEntry.OffsetToDataOrDirectoryEntry & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY;
- PEResourceEntry entry = isDirectory
- ? (name is null) ? new PEResourceDirectoryEntry(new PEResourceId(id)) : new PEResourceDirectoryEntry(name)
- : (name is null) ? new PEResourceDataEntry(new PEResourceId(id)) : new PEResourceDataEntry(name);
-
- parent.Entries.Add(entry);
-
- reader.Position = Position + offset;
-
- if (isDirectory)
- {
- var directory = (PEResourceDirectoryEntry)entry;
- ReadDirectory(reader, directory);
- }
- else
+ if (_tempResourceDataList is not null)
{
- var dataEntry = (PEResourceDataEntry)entry;
- RawImageResourceDataEntry rawDataEntry;
- if (!reader.TryReadData(sizeof(RawImageResourceDataEntry), out rawDataEntry))
+ foreach (var data in _tempResourceDataList)
{
- reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource data entry at position {reader.Position}");
- return;
+ yield return data;
}
- dataEntry.CodePage = rawDataEntry.CodePage != 0 ? Encoding.GetEncoding((int)rawDataEntry.CodePage) : null;
-
- if (!reader.File.TryFindSection(rawDataEntry.OffsetToData, out var section))
- {
- reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntryRVAOffsetToData, $"Invalid resource data entry at position {reader.Position}. The RVA {rawDataEntry.OffsetToData} does not map to an existing section.");
- return;
- }
-
- var position = section.Position + rawDataEntry.OffsetToData - section.RVA;
- reader.Position = position;
-
- if (dataEntry.CodePage != null)
- {
- var buffer = ArrayPool.Shared.Rent((int)rawDataEntry.Size);
- try
- {
- int read = reader.Read(buffer, 0, (int)rawDataEntry.Size);
- if (read != rawDataEntry.Size)
- {
- reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource data entry at position {reader.Position}");
- return;
- }
- dataEntry.Data = dataEntry.CodePage.GetString(buffer, 0, (int)rawDataEntry.Size);
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
- }
- else
- {
- dataEntry.Data = reader.ReadAsStream(rawDataEntry.Size);
- }
+ // We clear the list after being used - as this method is called once and we don't want to hold a reference
+ _tempResourceDataList.Clear();
+ _tempResourceDataList = null;
}
}
-
+
///
public override void Write(PEImageWriter writer)
{
throw new NotImplementedException();
}
-
- ///
- [EditorBrowsable(EditorBrowsableState.Never)]
- public List.Enumerator GetEnumerator() => Root.GetEnumerator();
-
- ///
- IEnumerator IEnumerable.GetEnumerator()
- {
- return Root.GetEnumerator();
- }
-
- ///
- IEnumerator IEnumerable.GetEnumerator()
- {
- return ((IEnumerable)Root).GetEnumerator();
- }
-
- private const uint IMAGE_RESOURCE_NAME_IS_STRING = 0x80000000;
- private const uint IMAGE_RESOURCE_DATA_IS_DIRECTORY = 0x80000000;
}
diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs
index e04f6ff..54ed5db 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs
@@ -1,16 +1,14 @@
-// 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 LibObjectFile.Diagnostics;
+using LibObjectFile.PE.Internal;
using System;
-using System.Collections;
+using System.Buffers;
using System.Collections.Generic;
-using System.ComponentModel;
-using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
-using LibObjectFile.Collections;
-using LibObjectFile.PE.Internal;
namespace LibObjectFile.PE;
@@ -21,39 +19,15 @@ namespace LibObjectFile.PE;
/// This class provides functionality to manage a directory entry in the .
/// It allows adding, removing, and updating resource entries within the directory.
///
-public sealed class PEResourceDirectoryEntry : PEResourceEntry, IEnumerable
+public sealed class PEResourceDirectoryEntry : PEResourceEntry
{
- [DebuggerBrowsable(DebuggerBrowsableState.Never)]
- private readonly Dictionary _nameToIndex = new();
-
- [DebuggerBrowsable(DebuggerBrowsableState.Never)]
- private readonly Dictionary _idToIndex = new();
-
///
/// Initializes a new instance of the class.
///
- internal PEResourceDirectoryEntry()
- {
- Entries = new ObjectList(this, adding: AddingEntry, removing: RemovingEntry, updating: UpdatingEntry);
- }
-
- ///
- /// Initializes a new instance of the class with the specified name.
- ///
- /// The name of the resource directory entry.
- public PEResourceDirectoryEntry(string name) : base(name)
- {
- ArgumentNullException.ThrowIfNull(name);
- Entries = new ObjectList(this, adding: AddingEntry, removing: RemovingEntry, updating: UpdatingEntry);
- }
-
- ///
- /// Initializes a new instance of the class with the specified ID.
- ///
- /// The ID of the resource directory entry.
- public PEResourceDirectoryEntry(PEResourceId id) : base(id)
+ public PEResourceDirectoryEntry()
{
- Entries = new ObjectList(this);
+ ByNames = new();
+ ByIds = new();
}
///
@@ -74,120 +48,142 @@ public PEResourceDirectoryEntry(PEResourceId id) : base(id)
///
/// Gets the list of resource entries within the directory.
///
- public ObjectList Entries { get; }
-
- ///
- /// Determines whether the directory contains a resource entry with the specified name.
- ///
- /// The name of the resource entry.
- /// true if the directory contains a resource entry with the specified name; otherwise, false.
- public bool Contains(string name) => _nameToIndex.ContainsKey(name);
-
- ///
- /// Determines whether the directory contains a resource entry with the specified ID.
- ///
- /// The ID of the resource entry.
- /// true if the directory contains a resource entry with the specified ID; otherwise, false.
- public bool Contains(PEResourceId id) => _idToIndex.ContainsKey(id);
+ public List ByNames { get; }
///
- /// Tries to get the resource entry with the specified name from the directory.
+ /// Gets the list of resource entries within the directory.
///
- /// The name of the resource entry.
- /// When this method returns, contains the resource entry with the specified name, if found; otherwise, null.
- /// true if the resource entry with the specified name is found; otherwise, false.
- public bool TryGetEntry(string name, out PEResourceEntry? entry)
+ public List ByIds { get; }
+
+ internal override unsafe void Read(in ReaderContext context)
{
- if (_nameToIndex.TryGetValue(name, out var index))
+ var reader = context.Reader;
+ var directory = context.Directory;
+
+ reader.Position = Position;
+ if (!reader.TryReadData(sizeof(RawImageResourceDirectory), out var data))
{
- entry = Entries[index];
- return true;
+ reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource directory at position {reader.Position}");
+ return;
}
- entry = null;
- return false;
- }
+ TimeDateStamp = DateTime.UnixEpoch.AddSeconds(data.TimeDateStamp);
+ MajorVersion = data.MajorVersion;
+ MinorVersion = data.MinorVersion;
- ///
- /// Tries to get the resource entry with the specified ID from the directory.
- ///
- /// The ID of the resource entry.
- /// When this method returns, contains the resource entry with the specified ID, if found; otherwise, null.
- /// true if the resource entry with the specified ID is found; otherwise, false.
- public bool TryGetEntry(PEResourceId id, out PEResourceEntry? entry)
- {
- if (_idToIndex.TryGetValue(id, out var index))
+ var buffer = new byte[(data.NumberOfNamedEntries + data.NumberOfIdEntries) * sizeof(RawImageResourceDirectoryEntry)];
+ var spanEntries = MemoryMarshal.Cast(buffer);
+
+ int read = reader.Read(buffer);
+ if (read != buffer.Length)
{
- entry = Entries[index];
- return true;
+ reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource directory at position {reader.Position}");
+ return;
}
- entry = null;
- return false;
- }
+ // Read all entries
+ for (int i = 0; i < data.NumberOfNamedEntries + data.NumberOfIdEntries; i++)
+ {
+ var entry = spanEntries[i];
+ ReadEntry(reader, directory, entry);
- ///
- /// Adds the specified resource entry to the directory.
- ///
- /// The resource entry to add.
- public void Add(PEResourceEntry entry) => Entries.Add(entry);
+ if (reader.Diagnostics.HasErrors)
+ {
+ return;
+ }
+ }
- ///
- /// Gets an enumerator that iterates through the resource entries in the directory.
- ///
- ///
- [EditorBrowsable(EditorBrowsableState.Never)]
- public List.Enumerator GetEnumerator() => Entries.GetEnumerator();
+ // Update the size
+ Size = reader.Position - Position;
- IEnumerator IEnumerable.GetEnumerator()
- {
- return Entries.GetEnumerator();
- }
+ var size = CalculateSize();
+ if (Size != size)
+ {
- IEnumerator IEnumerable.GetEnumerator()
- {
- return ((IEnumerable)Entries).GetEnumerator();
+ }
+
+
+ // Process all the entries recursively
+ var byNames = CollectionsMarshal.AsSpan(ByNames);
+ foreach (ref var entry in byNames)
+ {
+ entry.Entry.Read(context);
+ }
+
+ var byIds = CollectionsMarshal.AsSpan(ByIds);
+ foreach (ref var entry in byIds)
+ {
+ entry.Entry.Read(context);
+ }
}
- private static void AddingEntry(ObjectElement parent, int index, PEResourceEntry entry)
+ private void ReadEntry(PEImageReader reader, PEResourceDirectory directory, RawImageResourceDirectoryEntry rawEntry)
{
- var directory = (PEResourceDirectoryEntry)parent;
- if (entry.Name != null)
+ string? name = null;
+ int id = 0;
+
+ if ((rawEntry.NameOrId & IMAGE_RESOURCE_NAME_IS_STRING) != 0)
{
- directory._nameToIndex.Add(entry.Name, index);
+ // Read the string
+ var length = reader.ReadU16() * 2;
+ var buffer = ArrayPool.Shared.Rent(length);
+ try
+ {
+ int readLength = reader.Read(buffer, 0, length);
+ if (readLength != length)
+ {
+ reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource directory string at position {reader.Position}");
+ return;
+ }
+ name = Encoding.Unicode.GetString(buffer, 0, readLength);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
}
else
{
- directory._idToIndex.Add(entry.Id, index);
+ id = (int)(rawEntry.NameOrId & ~IMAGE_RESOURCE_NAME_IS_STRING);
}
- }
+
+ bool isDirectory = (rawEntry.OffsetToDataOrDirectoryEntry & IMAGE_RESOURCE_DATA_IS_DIRECTORY) != 0;
+ var offset = rawEntry.OffsetToDataOrDirectoryEntry & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY;
- private static void RemovingEntry(ObjectElement parent, PEResourceEntry entry)
- {
- var directory = (PEResourceDirectoryEntry)parent;
- if (entry.Name != null)
+ PEResourceEntry entry = isDirectory ? new PEResourceDirectoryEntry() : new PEResourceDataEntry();
+ entry.Position = directory.Position + offset;
+
+ if (name is not null)
{
- directory._nameToIndex.Remove(entry.Name);
+ ByNames.Add(new(name, entry));
}
else
{
- directory._idToIndex.Remove(entry.Id);
+ ByIds.Add(new(new(id), entry));
}
+
+ // Add the content to the directory (as we have the guarantee that the content belongs to the resource directory)
+ directory.Content.Add(entry);
}
- private static void UpdatingEntry(ObjectElement parent, int index, PEResourceEntry previousEntry, PEResourceEntry entry)
+ public override unsafe void UpdateLayout(PELayoutContext layoutContext)
{
- RemovingEntry(parent, previousEntry);
- AddingEntry(parent, index, entry);
+ Size = CalculateSize();
}
- private protected override unsafe uint ComputeSize()
+ private unsafe uint CalculateSize()
{
- var entries = CollectionsMarshal.AsSpan(Entries.UnsafeList);
- uint size = (uint)sizeof(RawImageResourceDirectory);
- foreach (var entry in entries)
+ var size = 0U;
+ size += (uint)sizeof(RawImageResourceDirectory);
+ size += (uint)(ByNames.Count + ByIds.Count) * (uint)sizeof(RawImageResourceDirectoryEntry);
+
+ if (ByNames.Count > 0)
{
- size += entry.ComputeFullSize();
+ var byNames = CollectionsMarshal.AsSpan(ByNames);
+ foreach (ref readonly var entry in byNames)
+ {
+ size += sizeof(ushort) + (uint)entry.Name.Length * 2;
+ }
}
return size;
@@ -200,8 +196,11 @@ protected override bool PrintMembers(StringBuilder builder)
builder.Append(", ");
}
- builder.Append($"Entries[{Entries.Count}] , TimeDateStamp = {TimeDateStamp}, MajorVersion = {MajorVersion}, MinorVersion = {MinorVersion}");
+ builder.Append($"ByNames[{ByNames.Count}], ByIds[{ByIds.Count}] , TimeDateStamp = {TimeDateStamp}, MajorVersion = {MajorVersion}, MinorVersion = {MinorVersion}");
- return false;
+ return true;
}
-}
+
+ private const uint IMAGE_RESOURCE_NAME_IS_STRING = 0x80000000;
+ private const uint IMAGE_RESOURCE_DATA_IS_DIRECTORY = 0x80000000;
+}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntryById.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntryById.cs
new file mode 100644
index 0000000..67538ad
--- /dev/null
+++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntryById.cs
@@ -0,0 +1,12 @@
+// 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.
+
+namespace LibObjectFile.PE;
+
+///
+/// Represents a resource directory entry with an ID in a PE file.
+///
+/// The identifier of the resource directory entry.
+/// The resource entry associated with the identifier.
+public readonly record struct PEResourceDirectoryEntryById(PEResourceId Id, PEResourceEntry Entry);
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntryByName.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntryByName.cs
new file mode 100644
index 0000000..59b0463
--- /dev/null
+++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntryByName.cs
@@ -0,0 +1,12 @@
+// 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.
+
+namespace LibObjectFile.PE;
+
+///
+/// Represents a resource directory entry with a name in a PE file.
+///
+/// The name of the resource directory entry.
+/// The resource entry associated with the name.
+public readonly record struct PEResourceDirectoryEntryByName(string Name, PEResourceEntry Entry);
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs
index 8241942..64abb72 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs
@@ -1,147 +1,19 @@
-// 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;
-using System.Globalization;
-using System.Text;
-using LibObjectFile.PE.Internal;
+using System.Collections.Generic;
namespace LibObjectFile.PE;
///
/// Represents an abstract base class for a PE resource entry.
///
-public abstract class PEResourceEntry : ObjectElement
+public abstract class PEResourceEntry : PESectionData
{
- ///
- /// Initializes a new instance of the class with a default ID of -1.
- ///
- private protected PEResourceEntry()
- {
- Id = new(-1);
- }
+ public sealed override bool HasChildren => false;
+
+ internal abstract void Read(in ReaderContext context);
- ///
- /// Initializes a new instance of the class with the specified name.
- ///
- /// The name of the resource entry.
- protected PEResourceEntry(string? name)
- {
- Name = name;
- }
-
- ///
- /// Initializes a new instance of the class with the specified ID.
- ///
- /// The ID of the resource entry.
- protected PEResourceEntry(PEResourceId id)
- {
- ArgumentOutOfRangeException.ThrowIfLessThan(id.Value, 0, nameof(id));
- Id = id;
- }
-
- ///
- /// Gets the name of the resource entry.
- ///
- public string? Name { get; }
-
- ///
- /// Gets the ID of the resource entry.
- ///
- public PEResourceId Id { get; }
-
- ///
- /// Gets a value indicating whether the resource entry is the root entry.
- ///
- public bool IsRoot => Id.Value < 0;
-
- ///
- /// Gets the level of the resource entry in the resource directory hierarchy.
- ///
- /// The level of the resource entry.
- public int GetLevel()
- {
- var level = 0;
- ObjectElement? parent = this;
- while (parent is not null && parent is not PEResourceDirectory)
- {
- parent = parent.Parent;
- level++;
- }
-
- if (parent is PEResourceDirectory)
- {
- level--;
- }
- else
- {
- level = -1;
- }
-
- return level;
- }
-
- ///
- /// Computes the full size of the resource entry.
- ///
- /// The full size of the resource entry.
- internal unsafe uint ComputeFullSize()
- {
- var size = Name != null ? (uint)Name.Length * 2 + sizeof(ushort) : 0;
- return (uint)(ComputeSize() + size + (IsRoot ? 0 : sizeof(RawImageResourceDirectoryEntry)));
- }
-
- ///
- /// Computes the size of the resource entry.
- ///
- /// The size of the resource entry.
- private protected abstract uint ComputeSize();
-
-
- ///
- protected override bool PrintMembers(StringBuilder builder)
- {
- if (!IsRoot)
- {
- if (Name != null)
- {
- builder.Append($"Name = {Name}");
- }
- else
- {
- builder.Append($"Id = {Id}");
- var level = GetLevel();
- if (level >= 0)
- {
- switch (level)
- {
- case 1:
- if (Id.TryGetWellKnownTypeName(out var name))
- {
- builder.Append($" ({name})");
- }
- break;
- case 2:
- break;
- case 3:
- try
- {
- var cultureInfo = CultureInfo.GetCultureInfo(Id.Value);
- builder.Append($" ({cultureInfo.Name})");
- }
- catch (CultureNotFoundException)
- {
- }
-
- break;
- }
- }
- }
-
- return true;
- }
-
- return false;
- }
+ internal readonly record struct ReaderContext(PEImageReader Reader, PEResourceDirectory Directory, List ResourceDataList);
}
diff --git a/src/LibObjectFile/PE/Internal/RawImageDebugDirectory.cs b/src/LibObjectFile/PE/Internal/RawImageDebugDirectory.cs
index f382ced..dc9d9d1 100644
--- a/src/LibObjectFile/PE/Internal/RawImageDebugDirectory.cs
+++ b/src/LibObjectFile/PE/Internal/RawImageDebugDirectory.cs
@@ -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.
diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs
index f28135b..febb7a6 100644
--- a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs
+++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs
@@ -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.
diff --git a/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs b/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs
index 67e7d6c..b703bf6 100644
--- a/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs
+++ b/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs
@@ -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.
diff --git a/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs b/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs
index da3a990..5e64618 100644
--- a/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs
+++ b/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs
@@ -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.
diff --git a/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.cs b/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.cs
index 2385dd2..d141c1f 100644
--- a/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.cs
+++ b/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.cs
@@ -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.
diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs
index b6f873c..3690eba 100644
--- a/src/LibObjectFile/PE/PEFile.Read.cs
+++ b/src/LibObjectFile/PE/PEFile.Read.cs
@@ -370,7 +370,7 @@ private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan dataParts, ulong startPosition, ulong totalSize)
{
var currentPosition = startPosition;
+ var endPosition = startPosition + totalSize;
// We are working on position, while the list is ordered by VirtualAddress
var listOrderedByPosition = dataParts.UnsafeList;
+ // Early exit if we don't have any data
+ if (totalSize == 0 && listOrderedByPosition.Count == 0)
+ {
+ return;
+ }
+
listOrderedByPosition.Sort((a, b) => a.Position.CompareTo(b.Position));
for (var i = 0; i < listOrderedByPosition.Count; i++)
{
var data = listOrderedByPosition[i];
data.Index = i;
}
- for (var i = 0; i < listOrderedByPosition.Count; i++)
+
+ if (listOrderedByPosition.Count == 0)
{
- var data = listOrderedByPosition[i];
- if (currentPosition < data.Position)
+ var size = endPosition - currentPosition;
+ imageReader.Position = currentPosition;
+ var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size))
{
- var size = data.Position - currentPosition;
- imageReader.Position = currentPosition;
+ Position = currentPosition,
+ Parent = container,
+ };
- var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size))
+ listOrderedByPosition.Add(sectionData);
+ currentPosition = endPosition;
+ }
+ else
+ {
+ for (var i = 0; i < listOrderedByPosition.Count; i++)
+ {
+ var data = listOrderedByPosition[i];
+
+ // Make sure we align the position to the required alignment of the next data
+ currentPosition = AlignHelper.AlignUp(currentPosition, data.GetRequiredPositionAlignment(imageReader.File));
+
+ if (currentPosition < data.Position)
{
- Position = currentPosition,
- Parent = container,
- };
+ var size = data.Position - currentPosition;
+ imageReader.Position = currentPosition;
- listOrderedByPosition.Insert(i, sectionData);
- currentPosition = data.Position;
- i++;
- }
- else if (currentPosition > data.Position)
- {
- imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data position {currentPosition} > {data.Position} in {container}");
- return;
- }
+ var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size))
+ {
+ Position = currentPosition,
+ Parent = container,
+ };
- currentPosition += data.Size;
+ listOrderedByPosition.Insert(i, sectionData);
+ currentPosition = data.Position;
+ i++;
+ }
+ else if (currentPosition > data.Position)
+ {
+ imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data position {currentPosition} > {data.Position} in {container}");
+ return;
+ }
+
+ var dataSize = AlignHelper.AlignUp(data.Size, data.GetRequiredSizeAlignment(imageReader.File));
+ currentPosition += dataSize;
+ }
}
- if (currentPosition < startPosition + totalSize)
+ if (currentPosition < endPosition)
{
- var size = startPosition + totalSize - currentPosition;
+ var size = endPosition - currentPosition;
imageReader.Position = currentPosition;
var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size))
{
@@ -520,9 +549,9 @@ private static void FillSectionDataWithMissingStreams(PEImageReader imageReader,
listOrderedByPosition.Add(sectionData);
}
- else if (currentPosition > startPosition + totalSize)
+ else if (currentPosition > endPosition)
{
- imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data position {currentPosition} > {startPosition + totalSize} in {container}");
+ imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data position {currentPosition} > {endPosition} in {container}");
}
// Make sure to update the indices after inserting the missing streams
diff --git a/src/LibObjectFile/PE/PEFile.Write.cs b/src/LibObjectFile/PE/PEFile.Write.cs
index 16bc19f..8fa31ef 100644
--- a/src/LibObjectFile/PE/PEFile.Write.cs
+++ b/src/LibObjectFile/PE/PEFile.Write.cs
@@ -1,10 +1,15 @@
-// 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;
using System.IO;
+using System.Numerics;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
using LibObjectFile.Diagnostics;
+using LibObjectFile.PE.Internal;
+using LibObjectFile.Utils;
namespace LibObjectFile.PE;
@@ -54,8 +59,139 @@ public bool TryWrite(Stream stream, out DiagnosticBag diagnostics)
return !diagnostics.HasErrors;
}
- public override void Write(PEImageWriter writer)
+ public override unsafe void Write(PEImageWriter writer)
{
- throw new NotImplementedException();
+ var context = new PELayoutContext(this, writer.Diagnostics);
+ UpdateLayout(context);
+
+ var position = 0U;
+
+ // Update DOS header
+ writer.Write(DosHeader);
+ position += (uint)sizeof(ImageDosHeader);
+
+ // Write DOS stub
+ writer.Write(_dosStub);
+ position += (uint)_dosStub.Length;
+
+ // Write extra DOS stub
+ if (_dosStubExtra != null)
+ {
+ _dosStubExtra.CopyTo(writer.Stream);
+ position += (uint)_dosStubExtra.Length;
+ }
+
+ var zeroSize = (int)((int)AlignHelper.AlignUp(position, 8) - (int)position);
+ writer.WriteZero((int)zeroSize);
+ position += (uint)zeroSize;
+
+ // PE00 header
+ writer.Write(ImagePESignature.PE);
+ position += sizeof(ImagePESignature); // PE00 header
+
+ // COFF header
+ writer.Write(CoffHeader);
+ position += (uint)sizeof(ImageCoffHeader);
+
+
+ if (IsPE32)
+ {
+ RawImageOptionalHeader32 header32;
+ header32.Common = OptionalHeader.OptionalHeaderCommonPart1;
+ header32.Base32 = OptionalHeader.OptionalHeaderBase32;
+ header32.Common2 = OptionalHeader.OptionalHeaderCommonPart2;
+ header32.Size32 = OptionalHeader.OptionalHeaderSize32;
+ header32.Common3 = OptionalHeader.OptionalHeaderCommonPart3;
+ writer.Write(header32);
+ position += (uint)sizeof(RawImageOptionalHeader32);
+ }
+ else
+ {
+ RawImageOptionalHeader64 header64;
+ header64.Common = OptionalHeader.OptionalHeaderCommonPart1;
+ header64.Base64 = OptionalHeader.OptionalHeaderBase64;
+ header64.Common2 = OptionalHeader.OptionalHeaderCommonPart2;
+ header64.Size64 = OptionalHeader.OptionalHeaderSize64;
+ header64.Common3 = OptionalHeader.OptionalHeaderCommonPart3;
+ writer.Write(header64);
+ position += (uint)sizeof(RawImageOptionalHeader64);
+ }
+
+
+ // Update directories
+ Directories.Write(writer, ref position);
+
+ // Write Section Headers
+ RawImageSectionHeader sectionHeader = default;
+ foreach (var section in _sections)
+ {
+ section.Name.CopyTo(new Span(sectionHeader.Name, 8));
+ sectionHeader.VirtualSize = section.VirtualSize;
+ sectionHeader.RVA = section.RVA;
+ sectionHeader.SizeOfRawData = (uint)section.Size;
+ sectionHeader.PointerToRawData = (uint)section.Position;
+ sectionHeader.Characteristics = section.Characteristics;
+ writer.Write(sectionHeader);
+ position += (uint)sizeof(RawImageSectionHeader);
+ }
+
+ // Data before sections
+ foreach (var extraData in ExtraDataBeforeSections)
+ {
+ extraData.Write(writer);
+ position += (uint)extraData.Size;
+ }
+
+ // Ensure that SectionAlignment is a multiple of FileAlignment
+ zeroSize = (int)(AlignHelper.AlignUp(position, OptionalHeader.FileAlignment) - position);
+ writer.WriteZero(zeroSize);
+ position += (uint)zeroSize;
+
+ // Write sections
+ foreach (var section in _sections)
+ {
+ var span = CollectionsMarshal.AsSpan(section.Content.UnsafeList);
+ for (var i = 0; i < span.Length; i++)
+ {
+ var data = span[i];
+ if (data.Position != position)
+ {
+ writer.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Current position {position} for data Section[{i}] in {section} does not match expecting position {data.Position}");
+ return;
+ }
+
+ if (data is PEDataDirectory directory)
+ {
+ directory.WriteHeaderAndContent(writer);
+ }
+ else
+ {
+ data.Write(writer);
+ }
+
+ position += (uint)data.Size;
+ }
+
+ zeroSize = (int)(AlignHelper.AlignUp(position, writer.PEFile.OptionalHeader.FileAlignment) - position);
+ writer.WriteZero(zeroSize);
+ }
+
+ // Data after sections
+ foreach (var extraData in ExtraDataAfterSections)
+ {
+ if (extraData.Position != position)
+ {
+ writer.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Current position {position} doest not match expecting position {extraData.Position}");
+ return;
+ }
+
+ extraData.Write(writer);
+ position += (uint)extraData.Size;
+ }
+
+ if (position != Size)
+ {
+ writer.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Generated size {position} does not match expecting size {Size}");
+ }
}
}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/PEImageWriter.cs b/src/LibObjectFile/PE/PEImageWriter.cs
index 2f13d65..676bc1d 100644
--- a/src/LibObjectFile/PE/PEImageWriter.cs
+++ b/src/LibObjectFile/PE/PEImageWriter.cs
@@ -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.
diff --git a/src/LibObjectFile/PE/PEObjectBase.cs b/src/LibObjectFile/PE/PEObjectBase.cs
index 3fd6f64..155b757 100644
--- a/src/LibObjectFile/PE/PEObjectBase.cs
+++ b/src/LibObjectFile/PE/PEObjectBase.cs
@@ -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.
@@ -31,4 +31,20 @@ public virtual void WriteAt(uint offset, ReadOnlySpan source)
{
throw new NotSupportedException($"The write operation is not supported for {this.GetType().FullName}");
}
+
+ ///
+ /// Gets the required alignment for this object.
+ ///
+ /// The PE file containing this object.
+ /// The required alignment for this object.
+ /// By default, this method returns 1.
+ public virtual uint GetRequiredPositionAlignment(PEFile file) => 1;
+
+ ///
+ /// Gets the required size alignment for this object.
+ ///
+ /// The PE file containing this object.
+ /// The required size alignment for this object.
+ /// By default, this method returns 1.
+ public virtual uint GetRequiredSizeAlignment(PEFile file) => 1;
}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs
index cb7d1f4..f8f4cde 100644
--- a/src/LibObjectFile/PE/PEPrinter.cs
+++ b/src/LibObjectFile/PE/PEPrinter.cs
@@ -5,6 +5,7 @@
using System;
using System.IO;
using LibObjectFile.IO;
+using static System.Runtime.InteropServices.JavaScript.JSType;
namespace LibObjectFile.PE;
@@ -269,6 +270,9 @@ private static void PrintSectionData(PEFile file, PESectionData data, ref TextWr
case PEDebugSectionDataRSDS peDebugSectionDataRSDS:
Print(peDebugSectionDataRSDS, ref writer);
break;
+ case PEResourceEntry peResourceEntry:
+ Print(peResourceEntry, ref writer);
+ break;
default:
writer.WriteLine($"Unsupported section data {data}");
break;
@@ -562,15 +566,24 @@ private static void Print(PEResourceEntry data, ref TextWriterIndenter writer)
switch (data)
{
case PEResourceDataEntry resourceFile:
- writer.WriteLine($"> {resourceFile}");
+ writer.WriteLine($"> CodePage = {resourceFile.CodePage?.EncodingName ?? "null"}, Data = {resourceFile.Data}");
break;
case PEResourceDirectoryEntry dir:
- writer.WriteLine($"> {dir}");
+ writer.WriteLine($"> ByNames[{dir.ByNames.Count}], ByIds[{dir.ByIds.Count}] , TimeDateStamp = {dir.TimeDateStamp}, Version = {dir.MajorVersion}.{dir.MinorVersion}");
writer.Indent();
- foreach (var entry in dir.Entries)
+
+ for (var i = 0; i < dir.ByNames.Count; i++)
+ {
+ var entry = dir.ByNames[i];
+ writer.WriteLine($"[{i}] Name = {entry.Name}, Entry = {entry.Entry}");
+ }
+
+ for (var i = 0; i < dir.ByIds.Count; i++)
{
- Print(entry, ref writer);
+ var entry = dir.ByIds[i];
+ writer.WriteLine($"[{i}] Id = {entry.Id}, Entry = {entry.Entry}");
}
+
writer.Unindent();
break;
default:
diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs
index 1be9ee5..cc88a8f 100644
--- a/src/LibObjectFile/PE/PESection.cs
+++ b/src/LibObjectFile/PE/PESection.cs
@@ -6,11 +6,8 @@
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Reflection.PortableExecutable;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using System.Text;
using LibObjectFile.Collections;
-using LibObjectFile.Diagnostics;
using LibObjectFile.Utils;
namespace LibObjectFile.PE;
@@ -54,6 +51,12 @@ public PESection(PESectionName name, RVA rva, RVA virtualSize)
/// Gets the list of data associated with this section.
///
public ObjectList Content => _content;
+
+ ///
+ public override uint GetRequiredPositionAlignment(PEFile file) => file.OptionalHeader.FileAlignment;
+
+ ///
+ public override uint GetRequiredSizeAlignment(PEFile file) => file.OptionalHeader.FileAlignment;
///
/// Tries to find the section data that contains the specified virtual address.
@@ -73,14 +76,22 @@ public override void UpdateLayout(PELayoutContext context)
{
var peFile = context.File;
- var sectionAlignment = peFile.OptionalHeader.SectionAlignment;
- var fileAlignment = peFile.OptionalHeader.FileAlignment;
-
var va = RVA;
- var position = Position;
+ var position = (uint)Position;
var size = 0U;
foreach (var data in Content)
{
+ // Make sure we align the position and the virtual address
+ var alignment = data.GetRequiredPositionAlignment(context.File);
+
+ if (alignment > 1)
+ {
+ var newPosition = AlignHelper.AlignUp(position, alignment);
+ size += newPosition - position;
+ position = newPosition;
+ va = AlignHelper.AlignUp(va, alignment);
+ }
+
data.RVA = va;
if (!context.UpdateSizeOnly)
@@ -90,13 +101,14 @@ public override void UpdateLayout(PELayoutContext context)
data.UpdateLayout(context);
- var dataSize = (uint)data.Size;
+ var dataSize = AlignHelper.AlignUp((uint)data.Size, data.GetRequiredSizeAlignment(peFile));
va += dataSize;
position += dataSize;
size += dataSize;
}
// The size of a section is the size of the content aligned on the file alignment
+ var fileAlignment = peFile.OptionalHeader.FileAlignment;
Size = (Characteristics & SectionCharacteristics.ContainsUninitializedData) == 0 ? AlignHelper.AlignUp(size, fileAlignment) : (ulong)0;
//if (Size > VirtualSize)
@@ -117,6 +129,8 @@ public override void Write(PEImageWriter writer)
throw new NotImplementedException();
}
+
+
///
protected override bool PrintMembers(StringBuilder builder)
{
diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs
index 0af00ff..70993b9 100644
--- a/src/LibObjectFile/PE/PESectionData.cs
+++ b/src/LibObjectFile/PE/PESectionData.cs
@@ -1,7 +1,8 @@
-// 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 LibObjectFile.Utils;
using System;
using System.Runtime.CompilerServices;
using System.Text;
diff --git a/src/LibObjectFile/PE/PESectionName.cs b/src/LibObjectFile/PE/PESectionName.cs
index 5a31c0a..56c5df1 100644
--- a/src/LibObjectFile/PE/PESectionName.cs
+++ b/src/LibObjectFile/PE/PESectionName.cs
@@ -1,9 +1,10 @@
-// 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;
using System.Text;
+using System.Xml.Linq;
namespace LibObjectFile.PE;
@@ -40,6 +41,12 @@ internal PESectionName(string name, bool validate = true)
///
public override string ToString() => Name;
+ internal void CopyTo(Span buffer)
+ {
+ var total = Encoding.ASCII.GetBytes(Name, buffer);
+ buffer.Slice(total).Fill(0);
+ }
+
///
/// Checks if the specified section name is a valid section name.
///
diff --git a/src/LibObjectFile/PE/PEStreamSectionData.cs b/src/LibObjectFile/PE/PEStreamSectionData.cs
index 2b7b491..800aaf7 100644
--- a/src/LibObjectFile/PE/PEStreamSectionData.cs
+++ b/src/LibObjectFile/PE/PEStreamSectionData.cs
@@ -1,9 +1,10 @@
-// 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;
using System.IO;
+using LibObjectFile.Utils;
namespace LibObjectFile.PE;
@@ -13,16 +14,16 @@ namespace LibObjectFile.PE;
public class PEStreamSectionData : PESectionData
{
private Stream _stream;
+ private uint _requiredPositionAlignment;
+ private uint _requiredSizeAlignment;
internal static PEStreamSectionData Empty = new();
///
/// Initializes a new instance of the class.
///
- public PEStreamSectionData()
+ public PEStreamSectionData() : this(System.IO.Stream.Null)
{
- _stream = Stream.Null;
- Size = 0;
}
///
@@ -34,6 +35,8 @@ public PEStreamSectionData(Stream stream)
ArgumentNullException.ThrowIfNull(stream);
_stream = stream;
Size = (ulong)stream.Length;
+ _requiredPositionAlignment = 1;
+ _requiredSizeAlignment = 1;
}
public override bool HasChildren => false;
@@ -52,6 +55,32 @@ public Stream Stream
}
}
+ ///
+ /// Gets or sets the preferred position alignment for this section data.
+ ///
+ public uint RequiredPositionAlignment
+ {
+ get => _requiredPositionAlignment;
+ set
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(value, 1U);
+ _requiredPositionAlignment = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the preferred size alignment for this section data.
+ ///
+ public uint RequiredSizeAlignment
+ {
+ get => _requiredSizeAlignment;
+ set
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(value, 1U);
+ _requiredSizeAlignment = value;
+ }
+ }
+
public override void UpdateLayout(PELayoutContext layoutContext)
{
Size = (ulong)Stream.Length;
@@ -80,4 +109,8 @@ public override void WriteAt(uint offset, ReadOnlySpan source)
Stream.Position = offset;
Stream.Write(source);
}
+
+ public override uint GetRequiredPositionAlignment(PEFile file) => _requiredPositionAlignment;
+
+ public override uint GetRequiredSizeAlignment(PEFile file) => _requiredSizeAlignment;
}
\ No newline at end of file