diff --git a/src/LibObjectFile/Ar/ArArchiveFile.cs b/src/LibObjectFile/Ar/ArArchiveFile.cs
index 7c9e763..9947e01 100644
--- a/src/LibObjectFile/Ar/ArArchiveFile.cs
+++ b/src/LibObjectFile/Ar/ArArchiveFile.cs
@@ -81,7 +81,7 @@ public void AddFile(ArFile file)
}
file.Parent = this;
- file.Index = (uint)_files.Count;
+ file.Index = _files.Count;
_files.Add(file);
}
@@ -123,7 +123,7 @@ public void InsertFileAt(int index, ArFile file)
}
}
- file.Index = (uint)index;
+ file.Index = index;
_files.Insert(index, file);
file.Parent = this;
diff --git a/src/LibObjectFile/DiagnosticBag.cs b/src/LibObjectFile/DiagnosticBag.cs
index 370dcdf..34d1aa1 100644
--- a/src/LibObjectFile/DiagnosticBag.cs
+++ b/src/LibObjectFile/DiagnosticBag.cs
@@ -8,100 +8,99 @@
using System.Text;
using LibObjectFile.Utils;
-namespace LibObjectFile
+namespace LibObjectFile;
+
+///
+/// A container for used for error reporting while reading/writing object files.
+///
+[DebuggerDisplay("Count = {Messages.Count}, HasErrors = {" + nameof(HasErrors) + "}")]
+public class DiagnosticBag
{
- ///
- /// A container for used for error reporting while reading/writing object files.
- ///
- [DebuggerDisplay("Count = {Messages.Count}, HasErrors = {" + nameof(HasErrors) + "}")]
- public class DiagnosticBag
- {
- private readonly List _messages;
+ private readonly List _messages;
- public DiagnosticBag()
- {
- _messages = new List();
- }
+ public DiagnosticBag()
+ {
+ _messages = new List();
+ }
- ///
- /// List of messages.
- ///
- public ReadOnlyList Messages => _messages;
+ ///
+ /// List of messages.
+ ///
+ public ReadOnlyList Messages => _messages;
- ///
- /// If this instance contains error messages.
- ///
- public bool HasErrors { get; private set; }
+ ///
+ /// If this instance contains error messages.
+ ///
+ public bool HasErrors { get; private set; }
- ///
- /// Clear all messages.
- ///
- public void Clear()
- {
- _messages.Clear();
- HasErrors = false;
- }
+ ///
+ /// Clear all messages.
+ ///
+ public void Clear()
+ {
+ _messages.Clear();
+ HasErrors = false;
+ }
- ///
- /// Copy all the in this bag to another bag.
- ///
- /// The diagnostics receiving the copy of the
- public void CopyTo(DiagnosticBag diagnostics)
+ ///
+ /// Copy all the in this bag to another bag.
+ ///
+ /// The diagnostics receiving the copy of the
+ public void CopyTo(DiagnosticBag diagnostics)
+ {
+ if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics));
+ foreach (var diagnosticMessage in Messages)
{
- if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics));
- foreach (var diagnosticMessage in Messages)
- {
- diagnostics.Log(diagnosticMessage);
- }
+ diagnostics.Log(diagnosticMessage);
}
+ }
- ///
- /// Logs the specified .
- ///
- /// The diagnostic message
- public void Log(DiagnosticMessage message)
+ ///
+ /// Logs the specified .
+ ///
+ /// The diagnostic message
+ public void Log(DiagnosticMessage message)
+ {
+ if (message.Message == null) throw new InvalidOperationException($"{nameof(DiagnosticMessage)}.{nameof(DiagnosticMessage.Message)} cannot be null");
+ _messages.Add(message);
+ if (message.Kind == DiagnosticKind.Error)
{
- if (message.Message == null) throw new InvalidOperationException($"{nameof(DiagnosticMessage)}.{nameof(DiagnosticMessage.Message)} cannot be null");
- _messages.Add(message);
- if (message.Kind == DiagnosticKind.Error)
- {
- HasErrors = true;
- }
+ HasErrors = true;
}
+ }
- ///
- /// Log an error .
- ///
- /// The identifier of the diagnostic.
- /// The text of the message
- /// An optional context
- 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 an error .
+ ///
+ /// The identifier of the diagnostic.
+ /// The text of the message
+ /// An optional context
+ 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 an error .
- ///
- /// The identifier of the diagnostic.
- /// The text of the message
- /// An optional context
- 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 an error .
+ ///
+ /// The identifier of the diagnostic.
+ /// The text of the message
+ /// An optional context
+ 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));
+ }
- public override string ToString()
+ public override string ToString()
+ {
+ var builder = new StringBuilder();
+ foreach (var diagnosticMessage in Messages)
{
- var builder = new StringBuilder();
- foreach (var diagnosticMessage in Messages)
- {
- builder.AppendLine(diagnosticMessage.ToString());
- }
-
- return builder.ToString();
+ builder.AppendLine(diagnosticMessage.ToString());
}
+
+ return builder.ToString();
}
}
\ No newline at end of file
diff --git a/src/LibObjectFile/DiagnosticId.cs b/src/LibObjectFile/DiagnosticId.cs
index d6280b1..35a53ff 100644
--- a/src/LibObjectFile/DiagnosticId.cs
+++ b/src/LibObjectFile/DiagnosticId.cs
@@ -135,5 +135,9 @@ public enum DiagnosticId
// PE Import
PE_ERR_ImportDirectoryInvalidEndOfStream = 3040,
PE_ERR_ImportLookupTableInvalidEndOfStream = 3041,
+ PE_ERR_ImportLookupTableInvalidHintNameTableRVA = 3042,
+ PE_ERR_ImportLookupTableInvalidParent = 3043,
+ PE_ERR_ImportDirectoryInvalidImportAddressTableRVA = 3044,
+ PE_ERR_ImportDirectoryInvalidImportLookupTableRVA = 3045,
}
-}
\ No newline at end of file
+}
diff --git a/src/LibObjectFile/Elf/ElfObjectFile.cs b/src/LibObjectFile/Elf/ElfObjectFile.cs
index 91ed97d..98c5dfc 100644
--- a/src/LibObjectFile/Elf/ElfObjectFile.cs
+++ b/src/LibObjectFile/Elf/ElfObjectFile.cs
@@ -332,7 +332,7 @@ public void AddSegment(ElfSegment segment)
}
segment.Parent = this;
- segment.Index = (uint)_segments.Count;
+ segment.Index = _segments.Count;
_segments.Add(segment);
}
@@ -351,7 +351,7 @@ public void InsertSegmentAt(int index, ElfSegment segment)
if (segment.Parent != this) throw new InvalidOperationException($"Cannot add the segment as it is already added to another {nameof(ElfObjectFile)} instance");
}
- segment.Index = (uint)index;
+ segment.Index = index;
_segments.Insert(index, segment);
segment.Parent = this;
@@ -415,7 +415,7 @@ public TSection AddSection(TSection section) where TSection : ElfSecti
}
section.Parent = this;
- section.Index = (uint)_sections.Count;
+ section.Index = _sections.Count;
_sections.Add(section);
if (section.IsShadow)
@@ -454,7 +454,7 @@ public void InsertSectionAt(int index, ElfSection section)
}
section.Parent = this;
- section.Index = (uint)index;
+ section.Index = index;
_sections.Insert(index, section);
if (section.IsShadow)
diff --git a/src/LibObjectFile/ObjectFileExtensions.cs b/src/LibObjectFile/ObjectFileExtensions.cs
index 2338d31..2378333 100644
--- a/src/LibObjectFile/ObjectFileExtensions.cs
+++ b/src/LibObjectFile/ObjectFileExtensions.cs
@@ -23,7 +23,7 @@ public static void Add(this List list, TParent parent,
}
element.Parent = parent;
- element.Index = (uint)list.Count;
+ element.Index = list.Count;
list.Add(element);
}
@@ -63,7 +63,7 @@ public static void AddSorted(this List list, TParent pa
list.Insert(index, element);
}
- element.Index = (uint)index;
+ element.Index = index;
// Update the index of following attributes
for (int i = index + 1; i < list.Count; i++)
@@ -88,7 +88,7 @@ public static void InsertAt(this List list, TParent par
if (element.Parent != parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added to another {parent.GetType()} instance");
}
- element.Index = (uint)index;
+ element.Index = index;
list.Insert(index, element);
element.Parent = parent;
diff --git a/src/LibObjectFile/ObjectFileNodeBase.cs b/src/LibObjectFile/ObjectFileNodeBase.cs
index f3b3a81..c2a5eb1 100644
--- a/src/LibObjectFile/ObjectFileNodeBase.cs
+++ b/src/LibObjectFile/ObjectFileNodeBase.cs
@@ -13,6 +13,11 @@ public abstract class ObjectFileNodeBase
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObjectFileNodeBase? _parent;
+ protected ObjectFileNodeBase()
+ {
+ Index = -1;
+ }
+
///
/// Gets or sets the position of this element relative to the top level parent.
///
@@ -45,9 +50,14 @@ protected virtual void ValidateParent(ObjectFileNodeBase parent)
}
///
- /// Index within the containing list in a parent.
+ /// Index within the containing list in a parent. If this object is not part of a list, this value is -1.
///
- public uint Index { get; internal set; }
+ public int Index { get; internal set; }
+
+ internal void ResetIndex()
+ {
+ Index = -1;
+ }
///
/// Gets or sets the size of this section or segment in the parent .
diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs
index 693af6b..7ab7bcd 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs
@@ -39,11 +39,19 @@ internal static PEDirectory Create(ImageDataDirectoryKind kind, RVALink new PELoadConfigDirectory(),
ImageDataDirectoryKind.BoundImport => new PEBoundImportDirectory(),
ImageDataDirectoryKind.DelayImport => new PEDelayImportDirectory(),
- ImageDataDirectoryKind.ImportAddressTable => new PEImportAddressTable(),
+ ImageDataDirectoryKind.ImportAddressTable => new PEImportAddressTableDirectory(),
ImageDataDirectoryKind.ClrMetadata => new PEClrMetadata(),
_ => throw new ArgumentOutOfRangeException(nameof(kind))
};
}
+
+ protected override void ValidateParent(ObjectFileNodeBase parent)
+ {
+ if (parent is not PESection)
+ {
+ throw new ArgumentException($"Invalid parent type [{parent?.GetType()}] for [{GetType()}]");
+ }
+ }
}
public sealed class PEExportDirectory : PEDirectory
@@ -178,28 +186,6 @@ protected override void Write(PEImageWriter writer)
}
}
-public sealed class PEImportAddressTable : PEDirectory
-{
- public PEImportAddressTable() : base(ImageDataDirectoryKind.ImportAddressTable)
- {
- }
-
- public override void UpdateLayout(DiagnosticBag diagnostics)
- {
- throw new NotImplementedException();
- }
-
- protected override void Read(PEImageReader reader)
- {
- throw new NotImplementedException();
- }
-
- protected override void Write(PEImageWriter writer)
- {
- throw new NotImplementedException();
- }
-}
-
public sealed class PETlsDirectory : PEDirectory
{
public PETlsDirectory() : base(ImageDataDirectoryKind.Tls)
diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs
index ba09832..e7edd4a 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs
@@ -99,7 +99,7 @@ internal PEDirectoryTable()
///
/// Gets the import address table directory information from the PE file.
///
- public PEImportAddressTable? ImportAddressTable => (PEImportAddressTable?)this[ImageDataDirectoryKind.ImportAddressTable];
+ public PEImportAddressTableDirectory? ImportAddressTable => (PEImportAddressTableDirectory?)this[ImageDataDirectoryKind.ImportAddressTable];
///
/// Gets the CLR metadata directory information from the PE file, if present.
diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs
new file mode 100644
index 0000000..7b5eb1d
--- /dev/null
+++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs
@@ -0,0 +1,50 @@
+// 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.Collections.Generic;
+
+namespace LibObjectFile.PE;
+
+public class PEImportAddressTable : PEObject
+{
+ internal readonly PEImportFunctionTable FunctionTable;
+
+ public PEImportAddressTable()
+ {
+ FunctionTable = new PEImportFunctionTable();
+ }
+
+ public new PEImportAddressTableDirectory? Parent => (PEImportAddressTableDirectory?)base.Parent;
+
+ public List Entries => FunctionTable.Entries;
+
+ public override void UpdateLayout(DiagnosticBag diagnostics)
+ {
+ var peFile = Parent?.Parent?.Parent;
+ if (peFile is null)
+ {
+ diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidParent, "The parent of the Import Address Table is null.");
+ return;
+ }
+
+ Size = FunctionTable.CalculateSize(peFile, diagnostics);
+ }
+
+ protected override void Read(PEImageReader reader)
+ {
+ FunctionTable.Read(reader, Position);
+ UpdateLayout(reader.Diagnostics);
+ }
+
+ protected override void Write(PEImageWriter writer) => FunctionTable.Write(writer);
+
+ protected override void ValidateParent(ObjectFileNodeBase parent)
+ {
+ if (parent is not PEImportAddressTableDirectory)
+ {
+ throw new ArgumentException($"Invalid parent type [{parent?.GetType()}] for [{GetType()}]");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs
new file mode 100644
index 0000000..fa25382
--- /dev/null
+++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs
@@ -0,0 +1,35 @@
+// 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 LibObjectFile.Utils;
+
+namespace LibObjectFile.PE;
+
+public sealed class PEImportAddressTableDirectory : PEDirectory
+{
+ private readonly ObjectList _tables;
+
+ public PEImportAddressTableDirectory() : base(ImageDataDirectoryKind.ImportAddressTable)
+ {
+ _tables = new ObjectList(this);
+ }
+
+ public ObjectList Tables => _tables;
+
+ public override void UpdateLayout(DiagnosticBag diagnostics)
+ {
+ ulong size = 0;
+ foreach (var table in _tables)
+ {
+ table.UpdateLayout(diagnostics);
+ size += table.Size;
+ }
+ Size = size;
+ }
+
+ protected override void Read(PEImageReader reader) => throw new NotSupportedException(); // Not called directly for this object, we are calling on tables directly
+
+ protected override void Write(PEImageWriter writer) => throw new NotSupportedException(); // Not called directly for this object, we are calling on tables directly
+}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs
index c9537b4..a8f1a58 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs
@@ -3,29 +3,37 @@
// See the license.txt file in the project root for more information.
using System;
+using System.Collections.Generic;
using System.Runtime.InteropServices;
using LibObjectFile.PE.Internal;
+using LibObjectFile.Utils;
namespace LibObjectFile.PE;
public sealed class PEImportDirectory : PEDirectory
{
+ private readonly ObjectList _entries;
+
public PEImportDirectory() : base(ImageDataDirectoryKind.Import)
{
+ _entries = new(this);
}
-
- public override void UpdateLayout(DiagnosticBag diagnostics)
+ public ObjectList Entries => _entries;
+
+ public override unsafe void UpdateLayout(DiagnosticBag diagnostics)
{
- throw new NotImplementedException();
+ Size = (ulong)((_entries.Count + 1) * sizeof(RawImportDirectoryEntry));
}
protected override void Read(PEImageReader reader)
{
var diagnostics = reader.Diagnostics;
+ reader.Position = Position;
+
// Read Import Directory Entries
- RawImportDirectoryEntry entry = default;
- var entrySpan = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref entry, 1));
+ RawImportDirectoryEntry rawEntry = default;
+ var entrySpan = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref rawEntry, 1));
while (true)
{
@@ -36,12 +44,60 @@ protected override void Read(PEImageReader reader)
return;
}
+ // TODO: handle bound imports through entry.TimeDateStamp
+
// Check for null entry (last entry in the import directory)
- if (entry.ImportLookupTableRVA == 0 && entry.TimeDateStamp == 0 && entry.ForwarderChain == 0 && entry.NameRVA == 0 && entry.ImportAddressTableRVA == 0)
+ if (rawEntry.ImportLookupTableRVA == 0 && rawEntry.TimeDateStamp == 0 && rawEntry.ForwarderChain == 0 && rawEntry.NameRVA == 0 && rawEntry.ImportAddressTableRVA == 0)
{
// Last entry
break;
}
+
+ // Find the section data for the ImportLookupTableRVA
+ if (!reader.PEFile.TryFindSectionData(rawEntry.ImportAddressTableRVA, out var sectionData))
+ {
+ diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidImportAddressTableRVA, $"Unable to find the section data for ImportAddressTableRVA {rawEntry.ImportAddressTableRVA}");
+ return;
+ }
+
+ // Calculate its position within the original stream
+ var importLookupAddressTablePositionInFile = sectionData.Position + rawEntry.ImportLookupTableRVA - sectionData.VirtualAddress;
+
+ // Find the section data for the ImportLookupTableRVA
+ if (!reader.PEFile.TryFindSectionData(rawEntry.ImportLookupTableRVA, out sectionData))
+ {
+ diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidImportLookupTableRVA, $"Unable to find the section data for ImportLookupTableRVA {rawEntry.ImportLookupTableRVA}");
+ return;
+ }
+
+ // Calculate its position within the original stream
+ var importLookupTablePositionInFile = sectionData.Position + rawEntry.ImportLookupTableRVA - sectionData.VirtualAddress;
+
+ // Store a fake entry for post-processing section data to allow to recreate PEImportLookupTable from existing PESectionStreamData
+ _entries.Add(
+ new PEImportDirectoryEntry(
+ // Name
+ new(new(PESectionDataTemp.Instance, rawEntry.NameRVA)),
+ // ImportAddressTable
+ new PEImportAddressTable()
+ {
+ Position = importLookupAddressTablePositionInFile
+ },
+ // ImportLookupTable
+ new PEImportLookupTable()
+ {
+ Position = importLookupTablePositionInFile
+ }
+ )
+ );
+ }
+
+ // Resolve ImportLookupTable and ImportAddressTable section data links
+ var entries = CollectionsMarshal.AsSpan(_entries.UnsafeList);
+ foreach (ref var entry in entries)
+ {
+ entry.ImportAddressTable.ReadInternal(reader);
+ entry.ImportLookupTable.ReadInternal(reader);
}
}
diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs
index 537b99a..ba1a215 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs
@@ -2,13 +2,67 @@
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
+using LibObjectFile.PE.Internal;
+using System;
+
namespace LibObjectFile.PE;
-#pragma warning disable CS0649
-public class PEImportDirectoryEntry
+public sealed class PEImportDirectoryEntry : PEObject
{
- public ZeroTerminatedAsciiStringLink ImportDllNameLink;
+ private PEImportLookupTable? _importLookupTable;
+
+ public PEImportDirectoryEntry(ZeroTerminatedAsciiStringLink importDllNameLink, PEImportAddressTable importAddressTable, PEImportLookupTable importLookupTable)
+ {
+ ImportDllNameLink = importDllNameLink;
+ ImportAddressTable = importAddressTable;
+ ImportLookupTable = importLookupTable;
+ }
+
+ public new PEImportDirectory? Parent
+ {
+ get => (PEImportDirectory?)base.Parent;
+ set => base.Parent = value;
+ }
+
+ public ZeroTerminatedAsciiStringLink ImportDllNameLink { get; set; }
+
+ public PEImportAddressTable ImportAddressTable { get; set; }
+
+ public PEImportLookupTable ImportLookupTable
+ {
+ get => _importLookupTable!;
+ set
+ {
+ ArgumentNullException.ThrowIfNull(value);
+ if (value == _importLookupTable)
+ {
+ return;
+ }
+
+ if (value.Parent is not null)
+ {
+ throw new InvalidOperationException("The import lookup table is already attached to another parent");
+ }
+
+ if (_importLookupTable is not null)
+ {
+ _importLookupTable.Parent = null;
+ }
+
+ value.Parent = this;
+ _importLookupTable = value;
+ }
+ }
+
+ public override unsafe void UpdateLayout(DiagnosticBag diagnostics)
+ {
+ Size = (ulong)sizeof(RawImportDirectoryEntry);
+
+ // Update the layout of the import lookup table
+ ImportLookupTable.UpdateLayout(diagnostics);
+ }
- public uint ImportAddressTableEntryIndex;
+ protected override void Read(PEImageReader reader) => throw new System.NotSupportedException();
+ protected override void Write(PEImageWriter writer) => throw new System.NotSupportedException();
}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs
new file mode 100644
index 0000000..6909088
--- /dev/null
+++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs
@@ -0,0 +1,52 @@
+// 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 PE Import Function Entry used in and .
+///
+public readonly struct PEImportFunctionEntry
+{
+ // Encodes the RVA through a link to the PE section data and the offset in the section data
+ // If the PE section data is null, the offset is the ordinal
+ private readonly PESectionData? _peSectionData;
+ private readonly uint _offset;
+
+ ///
+ /// Initializes a new instance of the class by name.
+ ///
+ /// The name of the import.
+ public PEImportFunctionEntry(ZeroTerminatedAsciiStringLink name)
+ {
+ _peSectionData = name.Link.Element;
+ _offset = name.Link.OffsetInElement;
+ }
+
+ ///
+ /// Initializes a new instance of the class by ordinal.
+ ///
+ /// The ordinal of the import.
+ public PEImportFunctionEntry(ushort ordinal)
+ {
+ _peSectionData = null;
+ _offset = ordinal;
+ }
+
+ ///
+ /// Gets a value indicating whether this import is by ordinal.
+ ///
+ public bool IsImportByOrdinal => _peSectionData is null;
+
+ ///
+ /// Gets the name of the import if not by ordinal.
+ ///
+ public ZeroTerminatedAsciiStringLink Name => _peSectionData is null ? default : new ZeroTerminatedAsciiStringLink(new(_peSectionData, _offset));
+
+ ///
+ /// Gets the ordinal of the import if by ordinal.
+ ///
+ public ushort Ordinal => _peSectionData is null ? (ushort)(_offset) : (ushort)0;
+
+}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs
new file mode 100644
index 0000000..fb37aa2
--- /dev/null
+++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs
@@ -0,0 +1,169 @@
+// 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.Generic;
+using System.Runtime.InteropServices;
+using LibObjectFile.PE.Internal;
+
+namespace LibObjectFile.PE;
+
+internal readonly struct PEImportFunctionTable()
+{
+ public List Entries { get; } = new();
+
+ public unsafe ulong CalculateSize(PEFile peFile, DiagnosticBag diagnostics)
+ {
+ // +1 for the null terminator
+ return (ulong)((Entries.Count + 1) * (peFile.IsPE32 ? sizeof(RawImportFunctionEntry32) : sizeof(RawImportFunctionEntry64)));
+ }
+
+ public void Read(PEImageReader reader, ulong position)
+ {
+ var peFile = reader.PEFile;
+ reader.Position = position;
+
+ if (peFile.IsPE32)
+ {
+ Read32(reader);
+ }
+ else
+ {
+ Read64(reader);
+ }
+
+ CalculateSize(peFile, reader.Diagnostics);
+ }
+
+ public void ResolveSectionDataLinks(PEFile peFile, DiagnosticBag diagnostics)
+ {
+ var entries = CollectionsMarshal.AsSpan(Entries);
+ foreach (ref var entry in entries)
+ {
+ if (!entry.IsImportByOrdinal)
+ {
+ var va = entry.Name.Link.OffsetInElement;
+ if (!peFile.TryFindSectionData(va, out var sectionData))
+ {
+ diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"Unable to find the section data for HintNameTableRVA {va}");
+ return;
+ }
+
+ entry = new PEImportFunctionEntry(new ZeroTerminatedAsciiStringLink(new(sectionData, va - sectionData.VirtualAddress)));
+ }
+ }
+ }
+
+ private unsafe void Read32(PEImageReader reader)
+ {
+ while (true)
+ {
+ RawImportFunctionEntry32 entry = default;
+ var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref entry, 1));
+ int read = reader.Read(span);
+ if (read != sizeof(RawImportFunctionEntry32))
+ {
+ reader.Diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidEndOfStream, $"Unable to read the full content of the Import Lookup Table. Expected {sizeof(RawImportFunctionEntry32)} bytes, but read {read} bytes");
+ return;
+ }
+
+ if (entry.IsNull)
+ {
+ break;
+ }
+
+ Entries.Add(
+ entry.IsImportByOrdinal
+ ? new PEImportFunctionEntry(entry.Ordinal)
+ : new PEImportFunctionEntry(new ZeroTerminatedAsciiStringLink(new(PESectionDataTemp.Instance, entry.HintNameTableRVA)))
+ );
+ }
+ }
+
+ private unsafe void Read64(PEImageReader reader)
+ {
+ while (true)
+ {
+ RawImportFunctionEntry64 entry = default;
+ var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref entry, 1));
+ int read = reader.Read(span);
+ if (read != sizeof(RawImportFunctionEntry64))
+ {
+ reader.Diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidEndOfStream, $"Unable to read the full content of the Import Lookup Table. Expected {sizeof(RawImportFunctionEntry64)} bytes, but read {read} bytes");
+ return;
+ }
+
+ if (entry.IsNull)
+ {
+ break;
+ }
+
+ Entries.Add(
+ entry.IsImportByOrdinal
+ ? new PEImportFunctionEntry(entry.Ordinal)
+ : new PEImportFunctionEntry(new ZeroTerminatedAsciiStringLink(new(PESectionDataTemp.Instance, entry.HintNameTableRVA)))
+ );
+ }
+ }
+
+ public void Write(PEImageWriter writer)
+ {
+ if (writer.PEFile.IsPE32)
+ {
+ Write32(writer);
+ }
+ else
+ {
+ Write64(writer);
+ }
+ }
+
+ private unsafe void Write32(PEImageWriter writer)
+ {
+ var buffer = ArrayPool.Shared.Rent(Entries.Count * sizeof(RawImportFunctionEntry32));
+ try
+ {
+ var span = MemoryMarshal.Cast(buffer.AsSpan(0, (Entries.Count + 1) * sizeof(RawImportFunctionEntry32)));
+ for (var i = 0; i < Entries.Count; i++)
+ {
+ var entry = Entries[i];
+ var va = entry.Name.Link.VirtualAddress;
+ span[i] = new RawImportFunctionEntry32(entry.IsImportByOrdinal ? 0x8000_0000U | entry.Ordinal : va);
+ }
+
+ // Last entry is null terminator
+ span[^1] = default;
+
+ writer.Write(MemoryMarshal.AsBytes(span));
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+ }
+
+ private unsafe void Write64(PEImageWriter writer)
+ {
+ var buffer = ArrayPool.Shared.Rent(Entries.Count * sizeof(RawImportFunctionEntry64));
+ try
+ {
+ var span = MemoryMarshal.Cast(buffer.AsSpan(0, (Entries.Count + 1) * sizeof(RawImportFunctionEntry64)));
+ for (var i = 0; i < Entries.Count; i++)
+ {
+ var entry = Entries[i];
+ var va = entry.Name.Link.VirtualAddress;
+ span[i] = new RawImportFunctionEntry64(entry.IsImportByOrdinal ? 0x8000_0000_0000_0000UL | entry.Ordinal : va);
+ }
+ // Last entry is null terminator
+ span[^1] = default;
+
+ writer.Write(MemoryMarshal.AsBytes(span));
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupEntry.cs
deleted file mode 100644
index 134d6d6..0000000
--- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupEntry.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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;
-
-#pragma warning disable CS0649
-public struct PEImportLookupEntry
-{
- public PEImportLookupEntry(ZeroTerminatedAsciiStringLink functionNameLink)
- {
- FunctionNameLink = functionNameLink;
- }
-
- public PEImportLookupEntry(ushort ordinal)
- {
- Ordinal = ordinal;
- }
-
- public ZeroTerminatedAsciiStringLink FunctionNameLink;
-
- public ushort Ordinal;
- public bool IsImportByOrdinal => FunctionNameLink.Link.IsNull;
-}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs
index 6bb6c9b..c497843 100644
--- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs
+++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs
@@ -3,116 +3,52 @@
// See the license.txt file in the project root for more information.
using System;
-using System.Buffers;
using System.Collections.Generic;
-using System.Runtime.InteropServices;
-using LibObjectFile.PE.Internal;
namespace LibObjectFile.PE;
-#pragma warning disable CS0649
-public class PEImportLookupTable : PESectionData
+public sealed class PEImportLookupTable : PEObject
{
- public List Entries { get; } = new();
+ internal readonly PEImportFunctionTable FunctionTable;
- public override unsafe void UpdateLayout(DiagnosticBag diagnostics)
+ public PEImportLookupTable()
{
- var parent = Parent?.Parent;
- if (parent is null)
- {
- diagnostics.Error(DiagnosticId.PE_ERR_InvalidParent, $"Parent is null for {nameof(PEImportLookupTable)} section data");
- return;
- }
-
- // +1 for the null terminator
- Size = (ulong)((Entries.Count + 1) * (parent.OptionalHeader.Magic == ImageOptionalHeaderMagic.PE32 ? sizeof(RawImportFunctionEntry32) : sizeof(RawImportFunctionEntry64)));
+ FunctionTable = new PEImportFunctionTable();
}
- protected override unsafe void Read(PEImageReader reader)
+ public new PEImportDirectoryEntry? Parent
{
- var peFile = reader.PEFile;
- var diagnostics = reader.Diagnostics;
-
- if (peFile.OptionalHeader.Magic == ImageOptionalHeaderMagic.PE32)
- {
- while (true)
- {
- var entry = new RawImportFunctionEntry32(reader.ReadU32());
-
- if (entry.HintNameTableRVA == 0)
- {
- break;
- }
-
-
- if (peFile.TryFindSection(entry.HintNameTableRVA, out var section))
- {
-
- }
-
-
-
- //Entries.Add(new PEImportLookupEntry(new ZeroTerminatedAsciiStringLink(new RVALink(entry.HintNameTableRVA, this))));
-
-
-
- }
-
- }
-
+ get => (PEImportDirectoryEntry?)base.Parent;
+ set => base.Parent = value;
}
- protected override void Write(PEImageWriter writer)
+ public List Entries => FunctionTable.Entries;
+
+ public override void UpdateLayout(DiagnosticBag diagnostics)
{
- if (writer.PEFile.OptionalHeader.Magic == ImageOptionalHeaderMagic.PE32)
- {
- Write32(writer);
- }
- else
+ var peFile = Parent?.Parent?.Parent?.Parent;
+ if (peFile is null)
{
- Write64(writer);
+ diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidParent, "The parent of the Import Lookup Table is null.");
+ return;
}
+
+ Size = FunctionTable.CalculateSize(peFile, diagnostics);
}
- private unsafe void Write32(PEImageWriter writer)
+ protected override void Read(PEImageReader reader)
{
- var buffer = ArrayPool.Shared.Rent(Entries.Count * sizeof(RawImportFunctionEntry32));
- try
- {
- var span = MemoryMarshal.Cast(buffer.AsSpan(0, Entries.Count * sizeof(RawImportFunctionEntry32)));
- for (var i = 0; i < Entries.Count; i++)
- {
- var entry = Entries[i];
- var va = entry.FunctionNameLink.Link.VirtualAddress;
- span[i] = new RawImportFunctionEntry32(entry.IsImportByOrdinal ? 0x8000_0000U | entry.Ordinal : va);
- }
-
- writer.Write(MemoryMarshal.AsBytes(span));
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
+ FunctionTable.Read(reader, Position);
+ UpdateLayout(reader.Diagnostics);
}
- private unsafe void Write64(PEImageWriter writer)
+ protected override void Write(PEImageWriter writer) => FunctionTable.Write(writer);
+
+ protected override void ValidateParent(ObjectFileNodeBase parent)
{
- var buffer = ArrayPool.Shared.Rent(Entries.Count * sizeof(RawImportFunctionEntry64));
- try
- {
- var span = MemoryMarshal.Cast(buffer.AsSpan(0, Entries.Count * sizeof(RawImportFunctionEntry64)));
- for (var i = 0; i < Entries.Count; i++)
- {
- var entry = Entries[i];
- var va = entry.FunctionNameLink.Link.VirtualAddress;
- span[i] = new RawImportFunctionEntry64(entry.IsImportByOrdinal ? 0x8000_0000_0000_0000UL | entry.Ordinal : va);
- }
-
- writer.Write(MemoryMarshal.AsBytes(span));
- }
- finally
+ if (parent is not PEImportDirectoryEntry)
{
- ArrayPool.Shared.Return(buffer);
+ throw new ArgumentException($"Invalid parent type [{parent?.GetType()}] for [{GetType()}]");
}
}
}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/Internal/RawImportFunctionEntry32.cs b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry32.cs
index 933da76..a53ce58 100644
--- a/src/LibObjectFile/PE/Internal/RawImportFunctionEntry32.cs
+++ b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry32.cs
@@ -5,14 +5,18 @@
namespace LibObjectFile.PE.Internal;
#pragma warning disable CS0649
-internal struct RawImportFunctionEntry32
+internal readonly struct RawImportFunctionEntry32
{
+ private readonly uint _hintNameTableRVA;
+
public RawImportFunctionEntry32(uint hintNameTableRVA)
{
- HintNameTableRVA = hintNameTableRVA;
+ _hintNameTableRVA = hintNameTableRVA;
}
- public uint HintNameTableRVA;
+ public uint HintNameTableRVA => IsImportByOrdinal ? 0U : _hintNameTableRVA;
+
+ public bool IsNull => HintNameTableRVA == 0;
public bool IsImportByOrdinal => (HintNameTableRVA & 0x8000_0000U) != 0;
diff --git a/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs
index c38a9b9..38161de 100644
--- a/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs
+++ b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs
@@ -5,14 +5,18 @@
namespace LibObjectFile.PE.Internal;
#pragma warning disable CS0649
-internal struct RawImportFunctionEntry64
+internal readonly struct RawImportFunctionEntry64
{
+ private readonly ulong _hintNameTableRVA;
+
public RawImportFunctionEntry64(ulong hintNameTableRVA)
{
- HintNameTableRVA = hintNameTableRVA;
+ _hintNameTableRVA = hintNameTableRVA;
}
- public ulong HintNameTableRVA;
+ public uint HintNameTableRVA => IsImportByOrdinal ? 0U : (uint)_hintNameTableRVA;
+
+ public bool IsNull => HintNameTableRVA == 0;
public ushort Ordinal => IsImportByOrdinal ? (ushort)HintNameTableRVA : (ushort)0;
diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs
index b890e36..c5744bc 100644
--- a/src/LibObjectFile/PE/PEFile.cs
+++ b/src/LibObjectFile/PE/PEFile.cs
@@ -7,6 +7,7 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection.PortableExecutable;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using LibObjectFile.Utils;
@@ -72,6 +73,16 @@ public Stream? DosStubExtra
///
public ImageOptionalHeader OptionalHeader;
+ ///
+ /// Gets a boolean indicating whether this instance is a PE32 image.
+ ///
+ public bool IsPE32 => OptionalHeader.Magic == ImageOptionalHeaderMagic.PE32;
+
+ ///
+ /// Gets a boolean indicating whether this instance is a PE32+ image.
+ ///
+ public bool IsPE32Plus => OptionalHeader.Magic == ImageOptionalHeaderMagic.PE32Plus;
+
///
/// Gets the directories.
///
@@ -106,20 +117,42 @@ public bool TryFindSection(RVA virtualAddress, [NotNullWhen(true)] out PESection
public bool TryFindSection(RVA virtualAddress, uint virtualSize, [NotNullWhen(true)] out PESection? section)
{
+ nint low = 0;
var sections = CollectionsMarshal.AsSpan(_sections);
- foreach (var trySection in sections)
+ nint high = sections.Length - 1;
+ ref var firstSection = ref MemoryMarshal.GetReference(sections);
+
+ while (low <= high)
{
- if (trySection.ContainsVirtual(virtualAddress, virtualSize))
+ nint mid = low + ((high - low) >>> 1);
+ var midSection = Unsafe.Add(ref firstSection, mid);
+
+ if (midSection.ContainsVirtual(virtualAddress, virtualSize))
{
- section = trySection;
+ section = midSection;
return true;
}
+
+ if (midSection.VirtualAddress < virtualAddress)
+ {
+ low = mid + 1;
+ }
+ else
+ {
+ high = mid - 1;
+ }
}
section = null;
return false;
}
+ public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESectionData? sectionData)
+ {
+ sectionData = null;
+ return TryFindSection(virtualAddress, out var section) && section.TryFindSectionData(virtualAddress, out sectionData);
+ }
+
public void RemoveSection(PESectionName name)
{
ArgumentNullException.ThrowIfNull(name);
@@ -191,7 +224,8 @@ public List GetAllSectionData()
public override void UpdateLayout(DiagnosticBag diagnostics)
{
- // TODO
+
+
}
protected override bool PrintMembers(StringBuilder builder)
diff --git a/src/LibObjectFile/PE/PEObject.cs b/src/LibObjectFile/PE/PEObject.cs
index 576f2eb..38002ce 100644
--- a/src/LibObjectFile/PE/PEObject.cs
+++ b/src/LibObjectFile/PE/PEObject.cs
@@ -10,6 +10,11 @@ namespace LibObjectFile.PE;
[DebuggerDisplay("{ToString(),nq}")]
public abstract class PEObject : ObjectFileNode
{
+ internal void ReadInternal(PEImageReader reader) => Read(reader);
+
+ internal void WriteInternal(PEImageWriter writer) => Write(writer);
+
+
protected abstract void Read(PEImageReader reader);
protected abstract void Write(PEImageWriter writer);
diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs
index a8bf6fd..3053c4d 100644
--- a/src/LibObjectFile/PE/PESection.cs
+++ b/src/LibObjectFile/PE/PESection.cs
@@ -102,7 +102,7 @@ public void AddData(PESectionData data)
ArgumentNullException.ThrowIfNull(data);
if (data.Parent != null) throw new ArgumentException("Data is already associated with a section", nameof(data));
data.Parent = this;
- data.Index = (uint)_dataParts.Count;
+ data.Index = _dataParts.Count;
_dataParts.Add(data);
UpdateSectionDataIndicesAndVirtualAddress((int)data.Index);
}
@@ -114,7 +114,7 @@ public void InsertData(int index, PESectionData data)
if (data.Parent != null) throw new ArgumentException("Data is already associated with a section", nameof(data));
data.Parent = this;
- data.Index = (uint)index;
+ data.Index = index;
_dataParts.Insert(index, data);
UpdateSectionDataIndicesAndVirtualAddress(index);
}
@@ -157,15 +157,15 @@ public void RemoveDataAt(int index)
public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESectionData? sectionData)
{
// Binary search
- nint left = 0;
+ nint low = 0;
var dataParts = CollectionsMarshal.AsSpan(_dataParts);
- nint right = dataParts.Length - 1;
+ nint high = dataParts.Length - 1;
ref var firstData = ref MemoryMarshal.GetReference(dataParts);
- while (left <= right)
+ while (low <= high)
{
- nint mid = left + (right - left) >>> 1;
+ nint mid = low + (high - low) >>> 1;
var trySectionData = Unsafe.Add(ref firstData, mid);
if (trySectionData.ContainsVirtual(virtualAddress))
@@ -176,11 +176,11 @@ public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESec
if (trySectionData.VirtualAddress < virtualAddress)
{
- left = mid + 1;
+ low = mid + 1;
}
else
{
- right = mid - 1;
+ high = mid - 1;
}
}
@@ -193,10 +193,9 @@ public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESec
///
/// The virtual address to check if it belongs to this section.
/// true if the virtual address is within the section range.
- public bool ContainsVirtual(RVA virtualAddress)
- {
- return virtualAddress >= VirtualAddress && virtualAddress < VirtualAddress + VirtualSize;
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool ContainsVirtual(RVA virtualAddress)
+ => virtualAddress >= VirtualAddress && virtualAddress < VirtualAddress + VirtualSize;
///
/// Checks if the specified virtual address and size is contained by this section.
@@ -204,11 +203,10 @@ public bool ContainsVirtual(RVA virtualAddress)
/// The virtual address to check if it belongs to this section.
/// The size to check if it belongs to this section.
/// true if the virtual address and size is within the section range.
- public bool ContainsVirtual(RVA virtualAddress, uint size)
- {
- return virtualAddress >= VirtualAddress && virtualAddress + size <= VirtualAddress + VirtualSize;
- }
-
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool ContainsVirtual(RVA virtualAddress, uint size)
+ => virtualAddress >= VirtualAddress && virtualAddress + size <= VirtualAddress + VirtualSize;
+
///
public override void UpdateLayout(DiagnosticBag diagnostics)
{
@@ -287,7 +285,7 @@ private void UpdateSectionDataIndicesAndVirtualAddress(int startIndex)
for (int i = startIndex; i < list.Count; i++)
{
var data = list[i];
- data.Index = (uint)i;
+ data.Index = i;
data.VirtualAddress = va;
va += (uint)data.Size;
}
diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs
index e61c40a..c9b4c86 100644
--- a/src/LibObjectFile/PE/PESectionData.cs
+++ b/src/LibObjectFile/PE/PESectionData.cs
@@ -3,7 +3,6 @@
// See the license.txt file in the project root for more information.
using System;
-using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
@@ -54,10 +53,6 @@ public virtual void WriteAt(uint offset, ReadOnlySpan source)
throw new NotSupportedException($"The write operation is not supported for {this.GetType().FullName}");
}
- internal void ReadInternal(PEImageReader reader) => Read(reader);
-
- internal void WriteInternal(PEImageWriter writer) => Write(writer);
-
protected override bool PrintMembers(StringBuilder builder)
{
builder.Append($"VirtualAddress: {VirtualAddress}, Size = 0x{Size:X4}");
@@ -65,104 +60,15 @@ protected override bool PrintMembers(StringBuilder builder)
}
}
-///
-/// Defines a raw section data in a Portable Executable (PE) image.
-///
-public sealed class PESectionMemoryData : PESectionData
-{
- ///
- /// Initializes a new instance of the class.
- ///
- public PESectionMemoryData() : this(Array.Empty())
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The raw data.
- public PESectionMemoryData(Memory data)
- {
- Data = data;
- }
- ///
- /// Gets the raw data.
- ///
- public Memory Data { get; set; }
-
- ///
- public override ulong Size
- {
- get => (ulong)Data.Length;
- set => throw new InvalidOperationException();
- }
-
- ///
- public override void UpdateLayout(DiagnosticBag diagnostics)
- {
- }
-
- protected override void Read(PEImageReader reader)
- {
- // No need to read, as the data is already provided via a stream
- }
-
- protected override void Write(PEImageWriter writer) => writer.Write(Data.Span);
-}
-///
-/// Gets a stream section data in a Portable Executable (PE) image.
-///
-public sealed class PESectionStreamData : PESectionData
+internal sealed class PESectionDataTemp : PESectionData
{
- private Stream _stream;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public PESectionStreamData()
- {
- _stream = Stream.Null;
- }
+ public static readonly PESectionDataTemp Instance = new ();
+
+ protected override void Read(PEImageReader reader) => throw new NotSupportedException();
- ///
- /// Initializes a new instance of the class.
- ///
- /// The stream containing the data of this section data.
- public PESectionStreamData(Stream stream)
- {
- ArgumentNullException.ThrowIfNull(stream);
- _stream = stream;
- }
+ protected override void Write(PEImageWriter writer) => throw new NotSupportedException();
- ///
- /// Gets the stream containing the data of this section data.
- ///
- public Stream Stream
- {
- get => _stream;
- set => _stream = value ?? throw new ArgumentNullException(nameof(value));
- }
-
- public override ulong Size
- {
- get => (ulong)Stream.Length;
- set => throw new InvalidOperationException();
- }
-
- public override void UpdateLayout(DiagnosticBag diagnostics)
- {
- }
-
- protected override void Read(PEImageReader reader)
- {
- // No need to read, as the data is already provided via a stream
- }
-
- protected override void Write(PEImageWriter writer)
- {
- Stream.Position = 0;
- Stream.CopyTo(writer.Stream);
- }
+ public override void UpdateLayout(DiagnosticBag diagnostics) => throw new NotSupportedException();
}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/PESectionStreamData.cs b/src/LibObjectFile/PE/PESectionStreamData.cs
new file mode 100644
index 0000000..58f3ce4
--- /dev/null
+++ b/src/LibObjectFile/PE/PESectionStreamData.cs
@@ -0,0 +1,64 @@
+// 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;
+
+namespace LibObjectFile.PE;
+
+///
+/// Gets a stream section data in a Portable Executable (PE) image.
+///
+public class PESectionStreamData : PESectionData
+{
+ private Stream _stream;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PESectionStreamData()
+ {
+ _stream = Stream.Null;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The stream containing the data of this section data.
+ public PESectionStreamData(Stream stream)
+ {
+ ArgumentNullException.ThrowIfNull(stream);
+ _stream = stream;
+ }
+
+ ///
+ /// Gets the stream containing the data of this section data.
+ ///
+ public Stream Stream
+ {
+ get => _stream;
+ set => _stream = value ?? throw new ArgumentNullException(nameof(value));
+ }
+
+ public override ulong Size
+ {
+ get => (ulong)Stream.Length;
+ set => throw new InvalidOperationException();
+ }
+
+ public override void UpdateLayout(DiagnosticBag diagnostics)
+ {
+ }
+
+ protected override void Read(PEImageReader reader)
+ {
+ // No need to read, as the data is already provided via a stream
+ }
+
+ protected override void Write(PEImageWriter writer)
+ {
+ Stream.Position = 0;
+ Stream.CopyTo(writer.Stream);
+ }
+}
\ No newline at end of file
diff --git a/src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs b/src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs
index 3ea9613..d7eb8a6 100644
--- a/src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs
+++ b/src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs
@@ -6,5 +6,7 @@ namespace LibObjectFile.PE;
public record struct ZeroTerminatedAsciiStringLink(RVALink Link)
{
- public string? GetName() => Link.Element?.ReadZeroTerminatedAsciiString(Link.OffsetInElement);
+ public bool IsNull => Link.IsNull;
+
+ public string? ToText() => Link.Element?.ReadZeroTerminatedAsciiString(Link.OffsetInElement);
}
\ No newline at end of file
diff --git a/src/LibObjectFile/Utils/ObjectList.cs b/src/LibObjectFile/Utils/ObjectList.cs
new file mode 100644
index 0000000..62c19d6
--- /dev/null
+++ b/src/LibObjectFile/Utils/ObjectList.cs
@@ -0,0 +1,196 @@
+// 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.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace LibObjectFile.Utils;
+
+///
+/// A list of objects that are attached to a parent object.
+///
+/// The type of the object file.
+[DebuggerDisplay("Count = {Count}")]
+[DebuggerTypeProxy(typeof(ObjectList<>.ObjectListDebuggerView))]
+public readonly struct ObjectList : IList
+ where TObject : ObjectFileNode
+{
+ // We are using an internal list to keep track of the parent object
+ private readonly InternalList _items;
+
+ [Obsolete("This constructor is not supported", true)]
+ public ObjectList() => throw new NotSupportedException("This constructor is not supported");
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The parent object file node.
+ public ObjectList(ObjectFileNode parent, Action? added = null, Action? removing = null)
+ {
+ ArgumentNullException.ThrowIfNull(parent);
+ _items = new InternalList(parent, added, removing);
+ }
+
+ public int Count => _items.Count;
+
+ public bool IsReadOnly => false;
+
+ public List UnsafeList => _items;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Add(TObject item)
+ {
+ var items = _items;
+ int index = items.Count;
+ items.Add(CheckAdd(item));
+ item.Index = index;
+ items.Added(item);
+ }
+
+ public void Clear()
+ {
+ var items = _items;
+ for (var i = items.Count - 1; i >= 0; i++)
+ {
+ var item = items[i];
+ items.Removing(item);
+ items.RemoveAt(i);
+
+ item.Parent = null;
+ item.ResetIndex();
+ }
+
+ items.Clear();
+ }
+
+ public bool Contains(TObject item) => _items.Contains(item);
+
+ public void CopyTo(TObject[] array, int arrayIndex) => _items.CopyTo(array, arrayIndex);
+
+ public bool Remove(TObject item)
+ {
+ var items = _items;
+ if (item.Parent != items.Parent)
+ {
+ return false;
+ }
+
+ item.Parent = null;
+ item.ResetIndex();
+
+ return items.Remove(item);
+ }
+
+ public int IndexOf(TObject item) => _items.IndexOf(item);
+
+ public void Insert(int index, TObject item)
+ {
+ var items = _items;
+ items.Insert(index, CheckAdd(item));
+
+ for (int i = index; i < items.Count; i++)
+ {
+ items[i].Index = i;
+ }
+
+ items.Added(item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ var items = _items;
+ var item = items[index];
+ item.Parent = null;
+ item.Index = 0;
+
+ items.RemoveAt(index);
+
+ for (int i = index; i < items.Count; i++)
+ {
+ items[i].Index = i;
+ }
+ }
+
+ public TObject this[int index]
+ {
+ get => _items[index];
+ set
+ {
+ value = CheckAdd(value);
+
+ // Unbind previous entry
+ var items = _items;
+ var previousItem = items[index];
+ items.Removing(previousItem);
+ previousItem.Parent = null;
+ previousItem.ResetIndex();
+
+ // Bind new entry
+ items[index] = value;
+ value.Index = index;
+ items.Added(value);
+ }
+ }
+
+ public List.Enumerator GetEnumerator() => _items.GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return _items.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable)_items).GetEnumerator();
+ }
+
+ public TObject CheckAdd(TObject item)
+ {
+ ArgumentNullException.ThrowIfNull(item);
+ if (item.Parent != null)
+ {
+ throw new ArgumentException($"The object is already attached to another parent", nameof(item));
+ }
+ item.Parent = _items.Parent;
+ return item;
+ }
+
+ private sealed class InternalList(ObjectFileNode parent, Action? added, Action? removing) : List
+ {
+ private readonly Action? _added = added;
+ private readonly Action? _removing = removing;
+ public readonly ObjectFileNode Parent = parent;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Added(TObject item) => _added?.Invoke(Parent, item);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Removing(TObject item) => _removing?.Invoke(Parent, item);
+ }
+
+ internal sealed class ObjectListDebuggerView
+ {
+ private readonly List _collection;
+
+ public ObjectListDebuggerView(ObjectList collection)
+ {
+ ArgumentNullException.ThrowIfNull((object)collection, nameof(collection));
+ this._collection = collection.UnsafeList;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public TObject[] Items
+ {
+ get
+ {
+ var array = new TObject[this._collection.Count];
+ _collection.CopyTo(array, 0);
+ return array;
+ }
+ }
+ }
+}