From 0a2a74ea69cf9ce8f9ce440ad84e6f24b7241290 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sat, 14 Sep 2024 09:16:35 +0200 Subject: [PATCH 01/87] Improve SliceStream --- src/LibObjectFile/Utils/SliceStream.cs | 310 +++++++++++++++---------- 1 file changed, 193 insertions(+), 117 deletions(-) diff --git a/src/LibObjectFile/Utils/SliceStream.cs b/src/LibObjectFile/Utils/SliceStream.cs index c5c9765..cabdd6e 100644 --- a/src/LibObjectFile/Utils/SliceStream.cs +++ b/src/LibObjectFile/Utils/SliceStream.cs @@ -3,146 +3,222 @@ // See the license.txt file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Threading; +using System.Threading.Tasks; -namespace LibObjectFile.Utils +namespace LibObjectFile.Utils; + +/// +/// Defines a stream as a slice of another existing stream. +/// +public class SliceStream : Stream { - /// - /// Defines a stream as a slice of another existing stream. - /// - public class SliceStream : Stream + private Stream _baseStream; + private readonly long _length; + private readonly long _basePosition; + private long _localPosition; + + public SliceStream(Stream baseStream, long position, long length) { - private Stream _baseStream; - private readonly long _length; - private readonly long _basePosition; - private long _localPosition; + if (baseStream == null) throw new ArgumentNullException(nameof(baseStream)); + if (!baseStream.CanSeek) throw new ArgumentException("Invalid base stream that can't be seek."); + if (position < 0) throw new ArgumentOutOfRangeException(nameof(position)); + if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); + if (position + length > baseStream.Length) throw new ArgumentOutOfRangeException(nameof(position), $"The position {position} + length {length} > baseStream.Length {baseStream.Length}"); - public SliceStream(Stream baseStream, long position, long length) - { - if (baseStream == null) throw new ArgumentNullException(nameof(baseStream)); - if (!baseStream.CanSeek) throw new ArgumentException("Invalid base stream that can't be seek."); - if (position < 0) throw new ArgumentOutOfRangeException(nameof(position)); - if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); - if (position + length > baseStream.Length) throw new ArgumentOutOfRangeException(nameof(position), $"The position {position} + length {length} > baseStream.Length {baseStream.Length}"); - - _baseStream = baseStream; - _length = length; - _basePosition = position; - } - public override int Read(byte[] buffer, int offset, int count) - { - ThrowIfDisposed(); + _baseStream = baseStream; + _length = length; + _basePosition = position; + } + public override int Read(byte[] buffer, int offset, int count) => Read(new Span(buffer, offset, count)); - long remaining = _length - _localPosition; - if (remaining <= 0) return 0; - if (remaining < count) count = (int)remaining; + public override int Read(Span buffer) + { + ThrowIfDisposed(); - _baseStream.Position = _basePosition + _localPosition; - int read = _baseStream.Read(buffer, offset, count); - _localPosition += read; + long remaining = _length - _localPosition; + if (remaining <= 0) return 0; + if (remaining < buffer.Length) buffer = buffer.Slice(0, (int)remaining); - return read; - } - private void ThrowIfDisposed() - { - if (_baseStream == null) throw new ObjectDisposedException(GetType().Name); - } - public override long Length - { - get { ThrowIfDisposed(); return _length; } - } - public override bool CanRead - { - get { ThrowIfDisposed(); return _baseStream.CanRead; } - } - public override bool CanWrite - { - get { ThrowIfDisposed(); return _baseStream.CanWrite; } - } - public override bool CanSeek + _baseStream.Position = _basePosition + _localPosition; + int read = _baseStream.Read(buffer); + _localPosition += read; + + return read; + } + + public override int ReadByte() + { + ThrowIfDisposed(); + + if (_localPosition >= _length) return -1; + + _baseStream.Position = _basePosition + _localPosition; + int read = _baseStream.ReadByte(); + _localPosition++; + + return read; + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => ReadAsync(new Memory(buffer, offset, count), cancellationToken).AsTask(); + + public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + + long remaining = _length - _localPosition; + if (remaining <= 0) return 0; + if (remaining < buffer.Length) buffer = buffer.Slice(0, (int)remaining); + + _baseStream.Position = _basePosition + _localPosition; + var read = await _baseStream.ReadAsync(buffer, cancellationToken); + + _localPosition += read; + return read; + } + + private void ThrowIfDisposed() + { + ObjectDisposedException.ThrowIf(_baseStream == null, this); + } + + public override long Length + { + get { ThrowIfDisposed(); return _length; } + } + public override bool CanRead + { + get { ThrowIfDisposed(); return _baseStream.CanRead; } + } + public override bool CanWrite + { + get { ThrowIfDisposed(); return _baseStream.CanWrite; } + } + public override bool CanSeek + { + get { ThrowIfDisposed(); return _baseStream.CanSeek; } + } + public override long Position + { + get { - get { ThrowIfDisposed(); return _baseStream.CanSeek; } + ThrowIfDisposed(); + return _localPosition; } - public override long Position + set => Seek(value, SeekOrigin.Begin); + } + public override long Seek(long offset, SeekOrigin origin) + { + long newPosition = _localPosition; + switch (origin) { - get - { - ThrowIfDisposed(); - return _localPosition; - } - set => Seek(value, SeekOrigin.Begin); + case SeekOrigin.Begin: + newPosition = offset; + break; + case SeekOrigin.Current: + newPosition += offset; + break; + case SeekOrigin.End: + newPosition = _length - offset; + break; + default: + throw new ArgumentOutOfRangeException(nameof(origin), origin, null); } - public override long Seek(long offset, SeekOrigin origin) - { - long newPosition = _localPosition; - switch (origin) - { - case SeekOrigin.Begin: - newPosition = offset; - break; - case SeekOrigin.Current: - newPosition += offset; - break; - case SeekOrigin.End: - newPosition = _length - offset; - break; - default: - throw new ArgumentOutOfRangeException(nameof(origin), origin, null); - } - if (newPosition < 0) throw new ArgumentOutOfRangeException(nameof(offset), $"New resulting position {newPosition} is < 0"); - if (newPosition > _length) throw new ArgumentOutOfRangeException(nameof(offset), $"New resulting position {newPosition} is > Length {_length}"); + if (newPosition < 0) throw new ArgumentOutOfRangeException(nameof(offset), $"New resulting position {newPosition} is < 0"); + if (newPosition > _length) throw new ArgumentOutOfRangeException(nameof(offset), $"New resulting position {newPosition} is > Length {_length}"); - // Check that we can seek on the origin stream - _baseStream.Position = _basePosition + newPosition; - _localPosition = newPosition; + // Check that we can seek on the origin stream + _baseStream.Position = _basePosition + newPosition; + _localPosition = newPosition; - return newPosition; - } + return newPosition; + } - public override void SetLength(long value) - { - throw new NotSupportedException(); - } + public override void SetLength(long value) + { + throw new NotSupportedException(); + } - public override void Flush() - { - ThrowIfDisposed(); _baseStream.Flush(); - } - protected override void Dispose(bool disposing) + public override void Flush() + { + ThrowIfDisposed(); _baseStream.Flush(); + } + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) { - base.Dispose(disposing); - if (disposing) + if (_baseStream != null) { - if (_baseStream != null) + try + { + _baseStream.Dispose(); + } + catch { - try - { - _baseStream.Dispose(); - } - catch - { - // ignored - } - _baseStream = null; + // ignored } + _baseStream = null; } } - public override void Write(byte[] buffer, int offset, int count) + } + + public override void Write(byte[] buffer, int offset, int count) + => Write(new ReadOnlySpan(buffer, offset, count)); + + public override void Write(ReadOnlySpan buffer) + { + ThrowIfDisposed(); + if (buffer.Length == 0) return; + + long length = Length; + var isOverLength = _localPosition + buffer.Length > length; + var maxLength = isOverLength ? (int)(length - _localPosition) : buffer.Length; + _baseStream.Position = _basePosition + _localPosition; + _baseStream.Write(buffer.Slice(0, maxLength)); + _localPosition += maxLength; + if (isOverLength) { - ThrowIfDisposed(); - if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (count == 0) return; - - var isOverLength = _localPosition + count > Length; - var maxLength = isOverLength ? (int)(Length - _localPosition) : count; - _baseStream.Position = _basePosition + _localPosition; - _baseStream.Write(buffer, offset, maxLength); - _localPosition += maxLength; - if (isOverLength) - { - throw new InvalidOperationException("Cannot write outside of this stream slice"); - } + ThrowCannotWriteOutside(); } } -} + + public override void WriteByte(byte value) + { + ThrowIfDisposed(); + if (_localPosition >= _length) ThrowCannotWriteOutside(); + + _baseStream.Position = _basePosition + _localPosition; + _baseStream.WriteByte(value); + _localPosition++; + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + + public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + if (buffer.Length == 0) return; + + long length = Length; + var isOverLength = _localPosition + buffer.Length > length; + var maxLength = isOverLength ? (int)(length - _localPosition) : buffer.Length; + _baseStream.Position = _basePosition + _localPosition; + await _baseStream.WriteAsync(buffer.Slice(0, maxLength), cancellationToken); + _localPosition += maxLength; + if (isOverLength) + { + ThrowCannotWriteOutside(); + } + } + + [DoesNotReturn] + private void ThrowCannotWriteOutside() + { + throw new InvalidOperationException("Cannot write outside of this stream slice"); + } +} \ No newline at end of file From 715155d9c0fb3e87d3ddea6e23906dcb517270a0 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sat, 14 Sep 2024 17:55:47 +0200 Subject: [PATCH 02/87] Add basic support for nullable --- src/LibObjectFile.CodeGen/Program.Dwarf.cs | 4 +- src/LibObjectFile.CodeGen/Program.cs | 6 +- src/LibObjectFile.Tests/Dwarf/DwarfTests.cs | 6 +- src/LibObjectFile/Ar/ArArchiveFile.cs | 4 +- src/LibObjectFile/Ar/ArArchiveFileReader.cs | 13 +- src/LibObjectFile/Ar/ArArchiveFileWriter.cs | 4 +- src/LibObjectFile/Ar/ArBinaryFile.cs | 2 +- src/LibObjectFile/Ar/ArElfFile.cs | 2 +- src/LibObjectFile/Ar/ArFile.cs | 6 +- src/LibObjectFile/Ar/ArLongNamesTable.cs | 8 +- src/LibObjectFile/Ar/ArObject.cs | 4 +- src/LibObjectFile/Ar/ArSymbolTable.cs | 6 +- src/LibObjectFile/DiagnosticBag.cs | 4 +- src/LibObjectFile/DiagnosticMessage.cs | 4 +- src/LibObjectFile/Dwarf/DwarfAbbreviation.cs | 3 +- .../Dwarf/DwarfAbbreviationItem.cs | 2 +- .../Dwarf/DwarfAbbreviationItemKey.cs | 2 +- .../Dwarf/DwarfAddressRangeTable.cs | 2 +- src/LibObjectFile/Dwarf/DwarfAttribute.cs | 63 +-- .../Dwarf/DwarfAttributeDescriptor.cs | 2 +- .../Dwarf/DwarfAttributeDescriptors.cs | 4 +- .../Dwarf/DwarfAttributeFormEx.cs | 2 +- .../Dwarf/DwarfAttributeKindEx.cs | 2 +- .../Dwarf/DwarfCallingConventionEx.cs | 2 +- .../Dwarf/DwarfCompilationUnit.cs | 4 +- src/LibObjectFile/Dwarf/DwarfConstant.cs | 10 +- src/LibObjectFile/Dwarf/DwarfDIE.cs | 39 +- .../Dwarf/DwarfDIEDeclaration.cs | 2 +- src/LibObjectFile/Dwarf/DwarfElfContext.cs | 30 +- src/LibObjectFile/Dwarf/DwarfFile.cs | 88 ++-- src/LibObjectFile/Dwarf/DwarfFileName.cs | 10 +- src/LibObjectFile/Dwarf/DwarfHelper.cs | 2 +- .../Dwarf/DwarfLanguageKindEx.cs | 2 +- src/LibObjectFile/Dwarf/DwarfLayoutContext.cs | 2 +- src/LibObjectFile/Dwarf/DwarfLine.cs | 4 +- .../Dwarf/DwarfLineProgramTable.cs | 21 +- src/LibObjectFile/Dwarf/DwarfLineState.cs | 4 +- src/LibObjectFile/Dwarf/DwarfLocation.cs | 8 +- src/LibObjectFile/Dwarf/DwarfLocationList.cs | 2 +- .../Dwarf/DwarfLocationListEntry.cs | 4 +- src/LibObjectFile/Dwarf/DwarfObject.cs | 10 +- src/LibObjectFile/Dwarf/DwarfOperation.cs | 40 +- .../Dwarf/DwarfOperationKindEx.cs | 2 +- src/LibObjectFile/Dwarf/DwarfPrinter.cs | 4 +- src/LibObjectFile/Dwarf/DwarfReader.cs | 15 +- src/LibObjectFile/Dwarf/DwarfReaderWriter.cs | 6 +- .../Dwarf/DwarfReaderWriterContext.cs | 14 +- .../Dwarf/DwarfRelocatableSection.cs | 2 +- src/LibObjectFile/Dwarf/DwarfSection.cs | 4 +- src/LibObjectFile/Dwarf/DwarfSectionLink.cs | 2 +- src/LibObjectFile/Dwarf/DwarfStringTable.cs | 7 +- src/LibObjectFile/Dwarf/DwarfTagEx.cs | 2 +- src/LibObjectFile/Dwarf/DwarfUnit.cs | 12 +- src/LibObjectFile/Dwarf/DwarfUnitKind.cs | 2 +- src/LibObjectFile/Elf/ElfArch.cs | 2 +- src/LibObjectFile/Elf/ElfFilePart.cs | 4 +- src/LibObjectFile/Elf/ElfHeaderFlags.cs | 2 +- src/LibObjectFile/Elf/ElfNativeExtensions.cs | 4 +- src/LibObjectFile/Elf/ElfOSAbi2.cs | 2 +- src/LibObjectFile/Elf/ElfObject.cs | 4 +- src/LibObjectFile/Elf/ElfObjectFile.cs | 13 +- src/LibObjectFile/Elf/ElfPrinter.cs | 6 +- src/LibObjectFile/Elf/ElfReaderOptions.cs | 4 +- src/LibObjectFile/Elf/ElfReader{TDecoder}.cs | 6 +- src/LibObjectFile/Elf/ElfSection.cs | 4 +- src/LibObjectFile/Elf/ElfSectionLink.cs | 11 +- src/LibObjectFile/Elf/ElfSegment.cs | 28 +- src/LibObjectFile/Elf/ElfSegmentFlags.cs | 2 +- src/LibObjectFile/Elf/ElfSegmentRange.cs | 14 +- src/LibObjectFile/Elf/ElfSegmentType.cs | 2 +- src/LibObjectFile/Elf/ElfString.cs | 10 +- .../Elf/Sections/ElfBinarySection.cs | 2 +- .../Elf/Sections/ElfBinaryShadowSection.cs | 2 +- .../Elf/Sections/ElfCustomNote.cs | 12 +- .../Elf/Sections/ElfGnuNoteBuildId.cs | 2 +- .../Elf/Sections/ElfNoteTable.cs | 8 +- src/LibObjectFile/Elf/Sections/ElfNoteType.cs | 2 +- .../Elf/Sections/ElfProgramHeaderTable.cs | 2 +- .../Elf/Sections/ElfRelocationTable.cs | 14 +- .../Sections/ElfRelocationTableExtensions.cs | 8 +- .../Elf/Sections/ElfRelocationType.cs | 4 +- .../Elf/Sections/ElfStringTable.cs | 10 +- src/LibObjectFile/Elf/Sections/ElfSymbol.cs | 2 +- .../Elf/Sections/ElfSymbolTable.cs | 14 +- .../ElfSymbolTableSectionHeaderIndices.cs | 34 +- src/LibObjectFile/LibObjectFile.csproj | 1 + src/LibObjectFile/ObjectFileNode.cs | 2 +- src/LibObjectFile/ObjectFileNodeBase.cs | 45 +- src/LibObjectFile/ObjectFileReaderWriter.cs | 4 +- src/LibObjectFile/Utils/SliceStream.cs | 6 +- src/LibObjectFile/Utils/ThrowHelper.cs | 3 +- .../LibObjectFile.Dwarf.generated.cs | 442 +++++++++--------- .../generated/LibObjectFile.Elf.generated.cs | 12 +- 93 files changed, 679 insertions(+), 581 deletions(-) diff --git a/src/LibObjectFile.CodeGen/Program.Dwarf.cs b/src/LibObjectFile.CodeGen/Program.Dwarf.cs index 28fe2c0..ce8f67f 100644 --- a/src/LibObjectFile.CodeGen/Program.Dwarf.cs +++ b/src/LibObjectFile.CodeGen/Program.Dwarf.cs @@ -55,6 +55,8 @@ private static void GenerateDwarf() var csFile = csCompilation.Members.OfType().First(); var ns = csFile.Members.OfType().First(); csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable 1591") ); + csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable CS8765")); + csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#nullable enable")); ProcessEnum(cppOptions, csCompilation, "DW_AT_", "DwarfAttributeKind"); ProcessEnum(cppOptions, csCompilation, "DW_FORM_", "DwarfAttributeForm"); @@ -509,7 +511,7 @@ private static CSharpProperty CreatePropertyFromDwarfAttributeName(string compac var csProperty = new CSharpProperty(propertyName) { Visibility = CSharpVisibility.Public, - ReturnType = new CSharpFreeType(map.Kind == AttributeKind.Managed ? attrType : $"{attrType}?"), + ReturnType = new CSharpNullableType(new CSharpFreeType(attrType)), }; var attrName = CSharpifyName(rawAttrName); diff --git a/src/LibObjectFile.CodeGen/Program.cs b/src/LibObjectFile.CodeGen/Program.cs index 3ff2e74..efc9681 100644 --- a/src/LibObjectFile.CodeGen/Program.cs +++ b/src/LibObjectFile.CodeGen/Program.cs @@ -101,7 +101,9 @@ private static void GenerateElf() // Add pragma var csFile = csCompilation.Members.OfType().First(); var ns = csFile.Members.OfType().First(); - csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable 1591") ); + csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable 1591") ); + csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable CS8765")); + csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#nullable enable")); ProcessEnum(cppOptions, csCompilation, "EM_", "ElfArch"); ProcessEnum(cppOptions, csCompilation, "ELFOSABI_", "ElfOSABI"); @@ -258,7 +260,7 @@ private static void ProcessEnum(CSharpConverterOptions cppOptions, CSharpCompila var toStringInternal = new CSharpMethod("ToStringInternal") { Visibility = CSharpVisibility.Private, - ReturnType = CSharpPrimitiveType.String() + ReturnType = new CSharpNullableType(CSharpPrimitiveType.String()) }; enumClass.Members.Add(toStringInternal); diff --git a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs index 1793215..89dc9fe 100644 --- a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs +++ b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs @@ -389,14 +389,12 @@ public void CreateDwarf() var dwarfFile = new DwarfFile(); // Create .debug_line information - var fileName = new DwarfFileName() + var fileName = new DwarfFileName("check1.cpp") { - Name = "check1.cpp", Directory = Environment.CurrentDirectory, }; - var fileName2 = new DwarfFileName() + var fileName2 = new DwarfFileName("check2.cpp") { - Name = "check2.cpp", Directory = Environment.CurrentDirectory, }; diff --git a/src/LibObjectFile/Ar/ArArchiveFile.cs b/src/LibObjectFile/Ar/ArArchiveFile.cs index 1564d02..ed697bb 100644 --- a/src/LibObjectFile/Ar/ArArchiveFile.cs +++ b/src/LibObjectFile/Ar/ArArchiveFile.cs @@ -48,12 +48,12 @@ public ArArchiveFile() /// /// Gets the associated to this instance. Must be first entry in /// - public ArSymbolTable SymbolTable { get; private set; } + public ArSymbolTable? SymbolTable { get; private set; } /// /// Internal extended file names used for GNU entries. /// - internal ArLongNamesTable LongNamesTable { get; set; } + internal ArLongNamesTable? LongNamesTable { get; set; } /// /// Gets the file entries. Use or to manipulate the entries. diff --git a/src/LibObjectFile/Ar/ArArchiveFileReader.cs b/src/LibObjectFile/Ar/ArArchiveFileReader.cs index 557d794..93843a5 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileReader.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileReader.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; using LibObjectFile.Elf; @@ -16,7 +17,7 @@ namespace LibObjectFile.Ar /// public class ArArchiveFileReader : ObjectFileReaderWriter { - private ArLongNamesTable _futureHeaders; + private ArLongNamesTable? _futureHeaders; internal ArArchiveFileReader(ArArchiveFile arArchiveFile, Stream stream, ArArchiveFileReaderOptions options) : base(stream) { @@ -31,7 +32,7 @@ internal ArArchiveFileReader(ArArchiveFile arArchiveFile, Stream stream, ArArchi internal ArArchiveFile ArArchiveFile { get; } - internal static bool IsAr(Stream stream, DiagnosticBag diagnostics) + internal static bool IsAr(Stream stream, DiagnosticBag? diagnostics) { Span magic = stackalloc byte[ArArchiveFile.Magic.Length]; int magicLength = stream.Read(magic); @@ -94,7 +95,7 @@ internal void Read() } } - private bool TryReadFileEntry(Span buffer, out ArFile entry) + private bool TryReadFileEntry(Span buffer, [NotNullWhen(true)] out ArFile? entry) { entry = null; @@ -124,7 +125,7 @@ private bool TryReadFileEntry(Span buffer, out ArFile entry) idLength--; } - string name = null; + string? name = null; ulong? bsdNameLength = null; if (idLength > 3 && ArArchiveFile.Kind == ArArchiveKind.BSD) @@ -236,7 +237,7 @@ private bool TryReadFileEntry(Span buffer, out ArFile entry) if (!entry.IsSystem) { - if (name.Contains('/')) + if (name!.Contains('/')) { Diagnostics.Error(DiagnosticId.AR_ERR_InvalidCharacterInFileEntryName, $"The character `/` was found in the entry `{name}` while it is invalid."); return false; @@ -318,7 +319,7 @@ private bool TryDecodeOctal(long entryOffset, Span buffer, int offset, int return true; } - private ArFile CreateFileEntryFromName(string name) + private ArFile CreateFileEntryFromName(string? name) { if (ArArchiveFile.Kind == ArArchiveKind.GNU) { diff --git a/src/LibObjectFile/Ar/ArArchiveFileWriter.cs b/src/LibObjectFile/Ar/ArArchiveFileWriter.cs index f0181cc..894bfa1 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileWriter.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileWriter.cs @@ -78,7 +78,7 @@ private void WriteFileEntry(Span buffer, ArFile file) if (name == null) { - name = file.Name; + name = file.Name!; if (ArArchiveFile.Kind != ArArchiveKind.Common && !name.EndsWith("/")) { postFixSlash = true; @@ -130,7 +130,7 @@ private void WriteFileEntry(Span buffer, ArFile file) { uint nameLength = bsdNameLength.Value; var bufferName = ArrayPool.Shared.Rent((int) nameLength); - Encoding.UTF8.GetBytes(file.Name, 0, file.Name.Length, bufferName, 0); + Encoding.UTF8.GetBytes(file.Name!, 0, file.Name!.Length, bufferName, 0); try { Stream.Write(bufferName, 0, (int)nameLength); diff --git a/src/LibObjectFile/Ar/ArBinaryFile.cs b/src/LibObjectFile/Ar/ArBinaryFile.cs index c85c7d9..94b715d 100644 --- a/src/LibObjectFile/Ar/ArBinaryFile.cs +++ b/src/LibObjectFile/Ar/ArBinaryFile.cs @@ -15,7 +15,7 @@ public sealed class ArBinaryFile : ArFile /// /// Gets or sets the stream associated to this entry. /// - public Stream Stream { get; set; } + public Stream? Stream { get; set; } protected override void Read(ArArchiveFileReader reader) { diff --git a/src/LibObjectFile/Ar/ArElfFile.cs b/src/LibObjectFile/Ar/ArElfFile.cs index 4a496eb..5a178bb 100644 --- a/src/LibObjectFile/Ar/ArElfFile.cs +++ b/src/LibObjectFile/Ar/ArElfFile.cs @@ -25,7 +25,7 @@ public ArElfFile(ElfObjectFile elfObjectFile) /// /// Gets or sets the ELF object file. /// - public ElfObjectFile ElfObjectFile { get; set; } + public ElfObjectFile? ElfObjectFile { get; set; } protected override void Read(ArArchiveFileReader reader) { diff --git a/src/LibObjectFile/Ar/ArFile.cs b/src/LibObjectFile/Ar/ArFile.cs index 3977b5c..dbc2dfa 100644 --- a/src/LibObjectFile/Ar/ArFile.cs +++ b/src/LibObjectFile/Ar/ArFile.cs @@ -11,7 +11,7 @@ namespace LibObjectFile.Ar /// public abstract partial class ArFile : ArObject { - private string _name; + private string? _name; private DateTimeOffset _timestamp; protected ArFile() @@ -22,7 +22,7 @@ protected ArFile() /// /// Gets or sets the name of the file in the archive entry. /// - public virtual string Name + public virtual string? Name { get => _name; set @@ -44,7 +44,7 @@ public virtual string Name /// /// Gets or sets the real (internal) name used for storing this entry (used by ) /// - internal string InternalName { get; set; } + internal string? InternalName { get; set; } /// /// Gets or sets the timestamp of this file (clamped to seconds since 1970/01/01) diff --git a/src/LibObjectFile/Ar/ArLongNamesTable.cs b/src/LibObjectFile/Ar/ArLongNamesTable.cs index c6ef197..0d64b32 100644 --- a/src/LibObjectFile/Ar/ArLongNamesTable.cs +++ b/src/LibObjectFile/Ar/ArLongNamesTable.cs @@ -18,9 +18,10 @@ internal class ArLongNamesTable : ArFile public ArLongNamesTable() { + FileNames = new Dictionary(); } - public override string Name + public override string? Name { get => DefaultName; set => base.Name = value; @@ -32,8 +33,7 @@ public override string Name protected override void Read(ArArchiveFileReader reader) { - FileNames = new Dictionary(); - + FileNames.Clear(); var buffer = ArrayPool.Shared.Rent((int)Size); int readCount = reader.Stream.Read(buffer, 0, (int)Size); int startFileIndex = 0; @@ -63,7 +63,7 @@ protected override void Write(ArArchiveFileWriter writer) { var buffer = ArrayPool.Shared.Rent((int)Size); uint offset = 0; - for (var i = (int)Index; i < Parent.Files.Count; i++) + for (var i = (int)Index; i < Parent!.Files.Count; i++) { var file = Parent.Files[i]; diff --git a/src/LibObjectFile/Ar/ArObject.cs b/src/LibObjectFile/Ar/ArObject.cs index 44e0fa0..01bb09d 100644 --- a/src/LibObjectFile/Ar/ArObject.cs +++ b/src/LibObjectFile/Ar/ArObject.cs @@ -24,9 +24,9 @@ protected override void ValidateParent(ObjectFileNodeBase parent) /// does not belong to an existing . /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public new ArArchiveFile Parent + public new ArArchiveFile? Parent { - get => (ArArchiveFile)base.Parent; + get => (ArArchiveFile?)base.Parent; internal set => base.Parent = value; } } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArSymbolTable.cs b/src/LibObjectFile/Ar/ArSymbolTable.cs index 716da0a..c3cc32e 100644 --- a/src/LibObjectFile/Ar/ArSymbolTable.cs +++ b/src/LibObjectFile/Ar/ArSymbolTable.cs @@ -22,7 +22,7 @@ public ArSymbolTable() Symbols = new List(); } - public override string Name + public override string? Name { get { @@ -117,7 +117,7 @@ protected override void Read(ArArchiveFileReader reader) protected override void AfterRead(DiagnosticBag diagnostics) { var offsets = new Dictionary(); - foreach (var fileEntry in Parent.Files) + foreach (var fileEntry in Parent!.Files) { offsets[fileEntry.Offset] = fileEntry; } @@ -143,7 +143,7 @@ protected override void Write(ArArchiveFileWriter writer) writer.Stream.WriteU32(false, (uint)Symbols.Count); uint stringOffset = 0; - bool isBSD = Parent.Kind == ArArchiveKind.BSD; + bool isBSD = Parent!.Kind == ArArchiveKind.BSD; foreach (var symbol in Symbols) { if (isBSD) diff --git a/src/LibObjectFile/DiagnosticBag.cs b/src/LibObjectFile/DiagnosticBag.cs index 75e6ed0..3d22be8 100644 --- a/src/LibObjectFile/DiagnosticBag.cs +++ b/src/LibObjectFile/DiagnosticBag.cs @@ -74,7 +74,7 @@ public void Log(DiagnosticMessage message) /// The identifier of the diagnostic. /// The text of the message /// An optional context - public void Error(DiagnosticId id, string message, object context = null) + 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)); @@ -86,7 +86,7 @@ public void Error(DiagnosticId id, string message, object context = null) /// The identifier of the diagnostic. /// The text of the message /// An optional context - public void Warning(DiagnosticId id, string message, object context = null) + public void Warning(DiagnosticId id, string message, object? context = null) { if (message == null) throw new ArgumentNullException(nameof(message)); Log(new DiagnosticMessage(DiagnosticKind.Warning, id, message, context)); diff --git a/src/LibObjectFile/DiagnosticMessage.cs b/src/LibObjectFile/DiagnosticMessage.cs index 52633fd..65e9b7d 100644 --- a/src/LibObjectFile/DiagnosticMessage.cs +++ b/src/LibObjectFile/DiagnosticMessage.cs @@ -17,7 +17,7 @@ public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string message) Message = message; } - public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string message, object context) + public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string message, object? context) { Kind = kind; Id = id; @@ -38,7 +38,7 @@ public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string message, o /// /// Gets the context of this message. /// - public object Context { get; } + public object? Context { get; } /// /// Gets the associated text of this message. diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs index 593b015..09c63b3 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; namespace LibObjectFile.Dwarf @@ -84,7 +85,7 @@ public DwarfAbbreviationItem GetOrCreate(DwarfAbbreviationItemKey itemKey) return item; } - public bool TryFindByCode(ulong code, out DwarfAbbreviationItem item) + public bool TryFindByCode(ulong code, [NotNullWhen(true)] out DwarfAbbreviationItem? item) { item = null; if (code == 0) diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs index 32d1df7..beb6071 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs @@ -74,7 +74,7 @@ protected override void Read(DwarfReader reader) HasChildren = hasChildren; - List descriptors = null; + List? descriptors = null; while (true) { diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationItemKey.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationItemKey.cs index a94024a..c271a18 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationItemKey.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationItemKey.cs @@ -27,7 +27,7 @@ public bool Equals(DwarfAbbreviationItemKey other) return Tag == other.Tag && HasChildren == other.HasChildren && Descriptors.Equals(other.Descriptors); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DwarfAbbreviationItemKey other && Equals(other); } diff --git a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs index 8bc3a3d..9265625 100644 --- a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs @@ -29,7 +29,7 @@ public DwarfAddressRangeTable() public ulong DebugInfoOffset { get; private set; } - public DwarfUnit Unit { get; set; } + public DwarfUnit? Unit { get; set; } public List Ranges { get; } diff --git a/src/LibObjectFile/Dwarf/DwarfAttribute.cs b/src/LibObjectFile/Dwarf/DwarfAttribute.cs index db0c381..9e96f42 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttribute.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttribute.cs @@ -14,7 +14,7 @@ public sealed class DwarfAttribute : DwarfObject, IComparable _valueAsObject; set @@ -83,7 +83,7 @@ public override void Verify(DiagnosticBag diagnostics) // Check DwarfDIE reference if (ValueAsObject is DwarfDIE attrDIE) { - var thisSection = this.GetParentSection(); + var thisSection = this.GetParentSection()!; var attrSection = attrDIE.GetParentSection(); if (thisSection != attrSection) @@ -97,7 +97,7 @@ public override void Verify(DiagnosticBag diagnostics) } else if (ValueAsObject is DwarfLocationList locationList) { - var thisSection = this.GetParentFile(); + var thisSection = this.GetParentFile()!; var locationListSection = locationList.GetParentFile(); if (thisSection != locationListSection) @@ -107,8 +107,9 @@ public override void Verify(DiagnosticBag diagnostics) } } - public int CompareTo(DwarfAttribute other) + public int CompareTo(DwarfAttribute? other) { + if (other == null) return 1; return ((uint)Kind).CompareTo((uint)other.Kind); } @@ -126,7 +127,7 @@ public override string ToString() protected override void UpdateLayout(DwarfLayoutContext layoutContext) { - var addressSize = layoutContext.CurrentUnit.AddressSize; + var addressSize = layoutContext.CurrentUnit!.AddressSize; var is64BitEncoding = layoutContext.CurrentUnit.Is64BitEncoding; var endOffset = Offset; @@ -148,32 +149,32 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) endOffset += 8; // WriteU64(ValueAsU64); break; case DwarfAttributeForm.String: - endOffset += DwarfHelper.SizeOfStringUTF8NullTerminated((string)ValueAsObject); + endOffset += DwarfHelper.SizeOfStringUTF8NullTerminated((string?)ValueAsObject); break; case DwarfAttributeForm.Block: { - var stream = (Stream)ValueAsObject; + var stream = (Stream)ValueAsObject!; endOffset += DwarfHelper.SizeOfULEB128((ulong)stream.Length); endOffset += (ulong)stream.Length; break; } case DwarfAttributeForm.Block1: { - var stream = (Stream)ValueAsObject; + var stream = (Stream)ValueAsObject!; endOffset += 1; endOffset += (ulong)stream.Length; break; } case DwarfAttributeForm.Block2: { - var stream = (Stream)ValueAsObject; + var stream = (Stream)ValueAsObject!; endOffset += 2; endOffset += (ulong)stream.Length; break; } case DwarfAttributeForm.Block4: { - var stream = (Stream)ValueAsObject; + var stream = (Stream)ValueAsObject!; endOffset += 4; endOffset += (ulong)stream.Length; break; @@ -207,7 +208,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) break; case DwarfAttributeForm.RefUdata: { - var dieRef = (DwarfDIE)ValueAsObject; + var dieRef = (DwarfDIE)ValueAsObject!; endOffset += DwarfHelper.SizeOfULEB128(dieRef.Offset - layoutContext.CurrentUnit.Offset); // WriteULEB128((dieRef.Offset - _currentUnit.Offset)); break; } @@ -231,7 +232,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) break; case DwarfAttributeForm.Exprloc: - var expr = (DwarfExpression)ValueAsObject; + var expr = (DwarfExpression)ValueAsObject!; expr.Offset = endOffset; expr.UpdateLayoutInternal(layoutContext); endOffset += expr.Size; @@ -822,33 +823,33 @@ protected override void Write(DwarfWriter writer) } case DwarfAttributeForm.String: { - writer.WriteStringUTF8NullTerminated((string) ValueAsObject); + writer.WriteStringUTF8NullTerminated((string)ValueAsObject!); break; } case DwarfAttributeForm.Block: { - var stream = (Stream) ValueAsObject; + var stream = (Stream) ValueAsObject!; writer.WriteULEB128((ulong) stream.Length); writer.Write(stream); break; } case DwarfAttributeForm.Block1: { - var stream = (Stream) ValueAsObject; + var stream = (Stream) ValueAsObject!; writer.WriteU8((byte) stream.Length); writer.Write(stream); break; } case DwarfAttributeForm.Block2: { - var stream = (Stream) ValueAsObject; + var stream = (Stream) ValueAsObject!; writer.WriteU16((ushort) stream.Length); writer.Write(stream); break; } case DwarfAttributeForm.Block4: { - var stream = (Stream) ValueAsObject; + var stream = (Stream) ValueAsObject!; writer.WriteU32((uint) stream.Length); writer.Write(stream); break; @@ -865,7 +866,7 @@ protected override void Write(DwarfWriter writer) } case DwarfAttributeForm.Strp: { - var offset = writer.File.StringTable.GetOrCreateString((string) ValueAsObject); + var offset = writer.File.StringTable.GetOrCreateString((string?) ValueAsObject); if (writer.EnableRelocation) { writer.RecordRelocation(DwarfRelocationTarget.DebugString, writer.SizeOfUIntEncoding(), offset); @@ -881,38 +882,38 @@ protected override void Write(DwarfWriter writer) } case DwarfAttributeForm.RefAddr: { - var dieRef = (DwarfDIE) ValueAsObject; + var dieRef = (DwarfDIE) ValueAsObject!; writer.WriteUIntFromEncoding(dieRef.Offset); break; } case DwarfAttributeForm.Ref1: { - var dieRef = (DwarfDIE) ValueAsObject; - writer.WriteU8((byte) (dieRef.Offset - writer.CurrentUnit.Offset)); + var dieRef = (DwarfDIE) ValueAsObject!; + writer.WriteU8((byte) (dieRef.Offset - writer.CurrentUnit!.Offset)); break; } case DwarfAttributeForm.Ref2: { - var dieRef = (DwarfDIE) ValueAsObject; - writer.WriteU16((ushort) (dieRef.Offset - writer.CurrentUnit.Offset)); + var dieRef = (DwarfDIE) ValueAsObject!; + writer.WriteU16((ushort) (dieRef.Offset - writer.CurrentUnit!.Offset)); break; } case DwarfAttributeForm.Ref4: { - var dieRef = (DwarfDIE) ValueAsObject; - writer.WriteU32((uint) (dieRef.Offset - writer.CurrentUnit.Offset)); + var dieRef = (DwarfDIE) ValueAsObject!; + writer.WriteU32((uint) (dieRef.Offset - writer.CurrentUnit!.Offset)); break; } case DwarfAttributeForm.Ref8: { - var dieRef = (DwarfDIE) ValueAsObject; - writer.WriteU64((dieRef.Offset - writer.CurrentUnit.Offset)); + var dieRef = (DwarfDIE) ValueAsObject!; + writer.WriteU64((dieRef.Offset - writer.CurrentUnit!.Offset)); break; } case DwarfAttributeForm.RefUdata: { - var dieRef = (DwarfDIE) ValueAsObject; - writer.WriteULEB128((dieRef.Offset - writer.CurrentUnit.Offset)); + var dieRef = (DwarfDIE) ValueAsObject!; + writer.WriteULEB128((dieRef.Offset - writer.CurrentUnit!.Offset)); break; } @@ -944,7 +945,7 @@ protected override void Write(DwarfWriter writer) } case DwarfAttributeForm.Exprloc: - ((DwarfExpression) ValueAsObject).WriteInternal(writer); + ((DwarfExpression) ValueAsObject!).WriteInternal(writer); break; case DwarfAttributeForm.FlagPresent: diff --git a/src/LibObjectFile/Dwarf/DwarfAttributeDescriptor.cs b/src/LibObjectFile/Dwarf/DwarfAttributeDescriptor.cs index 8add13e..4a62ecf 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeDescriptor.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeDescriptor.cs @@ -29,7 +29,7 @@ public bool Equals(DwarfAttributeDescriptor other) return Kind.Equals(other.Kind) && Form.Equals(other.Form); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DwarfAttributeDescriptor other && Equals(other); } diff --git a/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs b/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs index 50ffe43..be927bf 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs @@ -46,7 +46,7 @@ public bool Equals(DwarfAttributeDescriptors other) } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DwarfAttributeDescriptors other && Equals(other); } @@ -55,7 +55,7 @@ public override int GetHashCode() { int hashCode = _descriptors == null ? 0 : _descriptors.Length; if (hashCode == 0) return hashCode; - foreach (var descriptor in _descriptors) + foreach (var descriptor in _descriptors!) { hashCode = (hashCode * 397) ^ descriptor.GetHashCode(); } diff --git a/src/LibObjectFile/Dwarf/DwarfAttributeFormEx.cs b/src/LibObjectFile/Dwarf/DwarfAttributeFormEx.cs index fa00158..f9877b7 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeFormEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeFormEx.cs @@ -30,7 +30,7 @@ public bool Equals(DwarfAttributeFormEx other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DwarfAttributeFormEx other && Equals(other); } diff --git a/src/LibObjectFile/Dwarf/DwarfAttributeKindEx.cs b/src/LibObjectFile/Dwarf/DwarfAttributeKindEx.cs index eb12ac9..85ca704 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeKindEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeKindEx.cs @@ -32,7 +32,7 @@ public bool Equals(DwarfAttributeKindEx other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DwarfAttributeKindEx other && Equals(other); } diff --git a/src/LibObjectFile/Dwarf/DwarfCallingConventionEx.cs b/src/LibObjectFile/Dwarf/DwarfCallingConventionEx.cs index ce84db3..17746b6 100644 --- a/src/LibObjectFile/Dwarf/DwarfCallingConventionEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfCallingConventionEx.cs @@ -30,7 +30,7 @@ public bool Equals(DwarfCallingConventionEx other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DwarfCallingConventionEx other && Equals(other); } diff --git a/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs b/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs index 86603c4..f22d2e0 100644 --- a/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs @@ -42,7 +42,7 @@ protected override void WriteHeader(DwarfWriter writer) if (Version < 5) { // 3. debug_abbrev_offset (section offset) - var abbrevOffset = Abbreviation.Offset; + var abbrevOffset = Abbreviation!.Offset; if (writer.EnableRelocation) { writer.RecordRelocation(DwarfRelocationTarget.DebugAbbrev, writer.SizeOfUIntEncoding(), abbrevOffset); @@ -61,7 +61,7 @@ protected override void WriteHeader(DwarfWriter writer) writer.WriteAddressSize(AddressSize); // 5. debug_abbrev_offset (section offset) - var abbrevOffset = Abbreviation.Offset; + var abbrevOffset = Abbreviation!.Offset; if (writer.EnableRelocation) { writer.RecordRelocation(DwarfRelocationTarget.DebugAbbrev, writer.SizeOfUIntEncoding(), abbrevOffset); diff --git a/src/LibObjectFile/Dwarf/DwarfConstant.cs b/src/LibObjectFile/Dwarf/DwarfConstant.cs index 8c83fa0..b2d304b 100644 --- a/src/LibObjectFile/Dwarf/DwarfConstant.cs +++ b/src/LibObjectFile/Dwarf/DwarfConstant.cs @@ -34,15 +34,15 @@ public DwarfConstant(Stream stream) public DwarfInteger AsValue; - public object AsObject; + public object? AsObject; - public DwarfExpression AsExpression => AsObject as DwarfExpression; + public DwarfExpression? AsExpression => AsObject as DwarfExpression; - public DwarfDIE AsReference => AsObject as DwarfDIE; + public DwarfDIE? AsReference => AsObject as DwarfDIE; - public Stream AsStream => AsObject as Stream; + public Stream? AsStream => AsObject as Stream; - public string AsString => AsObject as string; + public string? AsString => AsObject as string; public override string ToString() { diff --git a/src/LibObjectFile/Dwarf/DwarfDIE.cs b/src/LibObjectFile/Dwarf/DwarfDIE.cs index 2c73998..28c0dad 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIE.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIE.cs @@ -20,7 +20,7 @@ public class DwarfDIE : DwarfContainer /// /// The current line program table when reading. /// - internal DwarfLineProgramTable CurrentLineProgramTable; + internal DwarfLineProgramTable? CurrentLineProgramTable; public DwarfDIE() { @@ -28,11 +28,6 @@ public DwarfDIE() _children = new List(); } - protected DwarfDIE(DwarfTagEx tag) - { - _tag = tag; - } - public virtual DwarfTagEx Tag { get => _tag; @@ -43,7 +38,7 @@ public virtual DwarfTagEx Tag public IReadOnlyList Children => _children; - public DwarfAbbreviationItem Abbrev { get; internal set; } + public DwarfAbbreviationItem? Abbrev { get; internal set; } /// /// Adds a child to . @@ -130,13 +125,13 @@ public override string ToString() return $"{nameof(Tag)}: {Tag}, {nameof(Attributes)}: {Attributes.Count}, {nameof(Children)}: {Children.Count}"; } - protected TValue GetAttributeValue(DwarfAttributeKind kind) + protected TValue? GetAttributeValue(DwarfAttributeKind kind) { foreach (var attr in _attributes) { if (attr.Kind == kind) { - return (TValue)attr.ValueAsObject; + return (TValue?)attr.ValueAsObject; } } @@ -265,7 +260,7 @@ protected void SetAttributeLocationOpt(DwarfAttributeKind kind, DwarfLocation? c } } - public DwarfAttribute FindAttributeByKey(DwarfAttributeKind kind) + public DwarfAttribute? FindAttributeByKey(DwarfAttributeKind kind) { foreach (var attr in _attributes) { @@ -278,7 +273,7 @@ public DwarfAttribute FindAttributeByKey(DwarfAttributeKind kind) return null; } - protected unsafe void SetAttributeValue(DwarfAttributeKind kind, TValue value) + protected unsafe void SetAttributeValue(DwarfAttributeKind kind, TValue? value) { for (int i = 0; i < _attributes.Count; i++) { @@ -368,6 +363,10 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) var abbrev = Abbrev; var endOffset = Offset; + if (abbrev is null) + { + throw new InvalidOperationException("Abbreviation is not set"); + } endOffset += DwarfHelper.SizeOfULEB128(abbrev.Code); // WriteULEB128(abbreviationItem.Code); foreach (var attr in _attributes) @@ -400,6 +399,11 @@ protected override void Read(DwarfReader reader) // Console.WriteLine($" <{level}><{die.Offset:x}> Abbrev Number: {abbreviationCode} ({die.Tag})"); + if (Abbrev is null) + { + throw new InvalidOperationException("Abbreviation is not set"); + } + var descriptors = Abbrev.Descriptors; if (descriptors.Length > 0) { @@ -436,16 +440,16 @@ protected override void Read(DwarfReader reader) Size = reader.Offset - Offset; } - internal static DwarfDIE ReadInstance(DwarfReader reader) + internal static DwarfDIE? ReadInstance(DwarfReader reader) { var startDIEOffset = reader.Offset; var abbreviationCode = reader.ReadULEB128(); - DwarfDIE die = null; + DwarfDIE? die = null; if (abbreviationCode != 0) { - if (!reader.CurrentUnit.Abbreviation.TryFindByCode(abbreviationCode, out var abbreviationItem)) + if (!reader.CurrentUnit!.Abbreviation!.TryFindByCode(abbreviationCode, out var abbreviationItem)) { throw new InvalidOperationException($"Invalid abbreviation code {abbreviationCode}"); } @@ -479,7 +483,7 @@ internal void UpdateAbbreviationItem(DwarfLayoutContext context) } var key = new DwarfAbbreviationItemKey(Tag, Children.Count > 0, new DwarfAttributeDescriptors(descriptorArray)); - var item = context.CurrentUnit.Abbreviation.GetOrCreate(key); + var item = context.CurrentUnit!.Abbreviation!.GetOrCreate(key); Abbrev = item; @@ -494,6 +498,11 @@ protected override void Write(DwarfWriter writer) var startDIEOffset = Offset; Debug.Assert(Offset == startDIEOffset); var abbrev = Abbrev; + if (abbrev is null) + { + throw new InvalidOperationException("Abbreviation is not set"); + } + writer.WriteULEB128(abbrev.Code); foreach (var attr in _attributes) diff --git a/src/LibObjectFile/Dwarf/DwarfDIEDeclaration.cs b/src/LibObjectFile/Dwarf/DwarfDIEDeclaration.cs index 179a1c0..0532dba 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIEDeclaration.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIEDeclaration.cs @@ -13,7 +13,7 @@ public ulong? DeclColumn set => SetAttributeValueOpt(DwarfAttributeKind.DeclColumn, value); } - public DwarfFileName DeclFile + public DwarfFileName? DeclFile { get => GetAttributeValue(DwarfAttributeKind.DeclFile); set => SetAttributeValue(DwarfAttributeKind.DeclFile, value); diff --git a/src/LibObjectFile/Dwarf/DwarfElfContext.cs b/src/LibObjectFile/Dwarf/DwarfElfContext.cs index a8bf895..d862ac0 100644 --- a/src/LibObjectFile/Dwarf/DwarfElfContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfElfContext.cs @@ -18,7 +18,7 @@ public class DwarfElfContext private int _lineTableSymbolIndex; private int _stringTableSymbolIndex; private int _locationSectionSymbolIndex; - private readonly ElfSymbolTable _symbolTable; + private readonly ElfSymbolTable? _symbolTable; public DwarfElfContext(ElfObjectFile elf) { @@ -117,25 +117,25 @@ public DwarfElfContext(ElfObjectFile elf) public DwarfAddressSize AddressSize => Elf.FileClass == ElfFileClass.Is64 ? DwarfAddressSize.Bit64 : DwarfAddressSize.Bit32; - public ElfBinarySection InfoSection { get; private set; } + public ElfBinarySection? InfoSection { get; private set; } - public ElfRelocationTable RelocInfoSection { get; set; } + public ElfRelocationTable? RelocInfoSection { get; set; } - public ElfBinarySection AbbreviationTable { get; set; } + public ElfBinarySection? AbbreviationTable { get; set; } - public ElfBinarySection AddressRangeTable { get; private set; } + public ElfBinarySection? AddressRangeTable { get; private set; } - public ElfRelocationTable RelocAddressRangeTable { get; set; } + public ElfRelocationTable? RelocAddressRangeTable { get; set; } - public ElfBinarySection StringTable { get; set; } + public ElfBinarySection? StringTable { get; set; } - public ElfBinarySection LineTable { get; set; } + public ElfBinarySection? LineTable { get; set; } - public ElfRelocationTable RelocLineTable { get; set; } + public ElfRelocationTable? RelocLineTable { get; set; } - public ElfBinarySection LocationSection { get; private set; } + public ElfBinarySection? LocationSection { get; private set; } - public ElfRelocationTable RelocLocationSection { get; set; } + public ElfRelocationTable? RelocLocationSection { get; set; } public int CodeSectionSymbolIndex => _codeSectionSymbolIndex; @@ -156,7 +156,7 @@ public ElfBinarySection GetOrCreateInfoSection() public ElfRelocationTable GetOrCreateRelocInfoSection() { - return RelocInfoSection ??= GetOrCreateRelocationTable(InfoSection); + return RelocInfoSection ??= GetOrCreateRelocationTable(InfoSection!); } public ElfBinarySection GetOrCreateAbbreviationTable() @@ -171,7 +171,7 @@ public ElfBinarySection GetOrCreateAddressRangeTable() public ElfRelocationTable GetOrCreateRelocAddressRangeTable() { - return RelocAddressRangeTable ??= GetOrCreateRelocationTable(AddressRangeTable); + return RelocAddressRangeTable ??= GetOrCreateRelocationTable(AddressRangeTable!); } public ElfBinarySection GetOrCreateLineSection() @@ -181,7 +181,7 @@ public ElfBinarySection GetOrCreateLineSection() public ElfRelocationTable GetOrCreateRelocLineSection() { - return RelocLineTable ??= GetOrCreateRelocationTable(LineTable); + return RelocLineTable ??= GetOrCreateRelocationTable(LineTable!); } public ElfBinarySection GetOrCreateStringTable() @@ -196,7 +196,7 @@ public ElfBinarySection GetOrCreateLocationSection() public ElfRelocationTable GetOrCreateRelocLocationSection() { - return RelocLocationSection ??= GetOrCreateRelocationTable(LocationSection); + return RelocLocationSection ??= GetOrCreateRelocationTable(LocationSection!); } public void RemoveStringTable() diff --git a/src/LibObjectFile/Dwarf/DwarfFile.cs b/src/LibObjectFile/Dwarf/DwarfFile.cs index 0d37d0d..0fd531c 100644 --- a/src/LibObjectFile/Dwarf/DwarfFile.cs +++ b/src/LibObjectFile/Dwarf/DwarfFile.cs @@ -18,48 +18,48 @@ public class DwarfFile : DwarfContainer public DwarfFile() { - AbbreviationTable = new DwarfAbbreviationTable(); - StringTable = new DwarfStringTable(); - LineSection = new DwarfLineSection(); - InfoSection = new DwarfInfoSection(); - LocationSection = new DwarfLocationSection(); - AddressRangeTable = new DwarfAddressRangeTable(); + AssignChild(this, new DwarfAbbreviationTable(), out _abbreviationTable); + AssignChild(this, new DwarfStringTable(), out _stringTable); + AssignChild(this, new DwarfLineSection(), out _lineSection); + AssignChild(this, new DwarfInfoSection(), out _infoSection); + AssignChild(this, new DwarfAddressRangeTable(), out _addressRangeTable); + AssignChild(this, new DwarfLocationSection(), out _locationSection); } public DwarfAbbreviationTable AbbreviationTable { get => _abbreviationTable; - set => AttachChild(this, value, ref _abbreviationTable, false); + set => AttachChild(this, value, ref _abbreviationTable); } public DwarfStringTable StringTable { get => _stringTable; - set => AttachChild(this, value, ref _stringTable, false); + set => AttachChild(this, value, ref _stringTable); } public DwarfLineSection LineSection { get => _lineSection; - set => AttachChild(this, value, ref _lineSection, false); + set => AttachChild(this, value, ref _lineSection); } public DwarfAddressRangeTable AddressRangeTable { get => _addressRangeTable; - set => AttachChild(this, value, ref _addressRangeTable, false); + set => AttachChild(this, value, ref _addressRangeTable); } public DwarfInfoSection InfoSection { get => _infoSection; - set => AttachChild(this, value, ref _infoSection, false); + set => AttachChild(this, value, ref _infoSection); } public DwarfLocationSection LocationSection { get => _locationSection; - set => AttachChild(this, value, ref _locationSection, false); + set => AttachChild(this, value, ref _locationSection); } protected override void Read(DwarfReader reader) @@ -148,9 +148,9 @@ public void Write(DwarfWriterContext writerContext) writer.EnableRelocation = writerContext.EnableRelocation; writer.Log = writerContext.DebugLinePrinter; - writer.Stream = writerContext.DebugLineStream; - if (writer.Stream != null) + if (writerContext.DebugLineStream != null) { + writer.Stream = writerContext.DebugLineStream; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = LineSection; @@ -159,18 +159,18 @@ public void Write(DwarfWriterContext writerContext) } writer.Log = null; - writer.Stream = writerContext.DebugAbbrevStream; - if (writer.Stream != null) + if (writerContext.DebugAbbrevStream != null) { + writer.Stream = writerContext.DebugAbbrevStream; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = AbbreviationTable; AbbreviationTable.WriteInternal(writer); } - writer.Stream = writerContext.DebugAddressRangeStream; - if (writer.Stream != null) + if (writerContext.DebugAddressRangeStream != null) { + writer.Stream = writerContext.DebugAddressRangeStream; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = AddressRangeTable; @@ -178,18 +178,18 @@ public void Write(DwarfWriterContext writerContext) AddressRangeTable.WriteInternal(writer); } - writer.Stream = writerContext.DebugStringStream; - if (writer.Stream != null) + if (writerContext.DebugStringStream != null) { + writer.Stream = writerContext.DebugStringStream; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = StringTable; StringTable.WriteInternal(writer); } - writer.Stream = writerContext.DebugInfoStream; - if (writer.Stream != null) + if (writerContext.DebugInfoStream != null) { + writer.Stream = writerContext.DebugInfoStream; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = InfoSection; @@ -197,9 +197,9 @@ public void Write(DwarfWriterContext writerContext) InfoSection.WriteInternal(writer); } - writer.Stream = writerContext.DebugLocationStream; - if (writer.Stream != null) + if (writerContext.DebugLocationStream != null) { + writer.Stream = writerContext.DebugLocationStream; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = LocationSection; @@ -210,7 +210,7 @@ public void Write(DwarfWriterContext writerContext) CheckErrors(diagnostics); } - public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig layoutConfig = null) + public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig? layoutConfig = null) { if (elfContext == null) throw new ArgumentNullException(nameof(elfContext)); @@ -243,7 +243,7 @@ public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig layoutConfi // String table if (StringTable.Size > 0) { - writer.Stream = elfContext.GetOrCreateStringTable().Stream; + writer.Stream = elfContext.GetOrCreateStringTable().Stream!; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = StringTable; @@ -257,7 +257,7 @@ public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig layoutConfi // Abbreviation table if (AbbreviationTable.Size > 0) { - writer.Stream = elfContext.GetOrCreateAbbreviationTable().Stream; + writer.Stream = elfContext.GetOrCreateAbbreviationTable().Stream!; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = AbbreviationTable; @@ -271,7 +271,7 @@ public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig layoutConfi // Line table if (LineSection.Size > 0) { - writer.Stream = elfContext.GetOrCreateLineSection().Stream; + writer.Stream = elfContext.GetOrCreateLineSection().Stream!; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = LineSection; @@ -294,7 +294,7 @@ public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig layoutConfi // AddressRange table if (AddressRangeTable.Size > 0) { - writer.Stream = elfContext.GetOrCreateAddressRangeTable().Stream; + writer.Stream = elfContext.GetOrCreateAddressRangeTable().Stream!; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = AddressRangeTable; @@ -318,7 +318,7 @@ public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig layoutConfi // InfoSection if (InfoSection.Size > 0) { - writer.Stream = elfContext.GetOrCreateInfoSection().Stream; + writer.Stream = elfContext.GetOrCreateInfoSection().Stream!; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = InfoSection; @@ -342,7 +342,7 @@ public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig layoutConfi // LocationSection if (LocationSection.Size > 0) { - writer.Stream = elfContext.GetOrCreateLocationSection().Stream; + writer.Stream = elfContext.GetOrCreateLocationSection().Stream!; writer.Stream.Position = 0; writer.Stream.SetLength(0); writer.CurrentSection = LocationSection; @@ -374,48 +374,48 @@ public static DwarfFile Read(DwarfReaderContext readerContext) var reader = new DwarfReader(readerContext, dwarf, new DiagnosticBag()); reader.Log = null; - reader.Stream = readerContext.DebugAbbrevStream; - if (reader.Stream != null) + if (readerContext.DebugAbbrevStream != null) { + reader.Stream = readerContext.DebugAbbrevStream; reader.CurrentSection = dwarf.AbbreviationTable; dwarf.AbbreviationTable.ReadInternal(reader); } - reader.Stream = readerContext.DebugStringStream; - if (reader.Stream != null) + if (readerContext.DebugStringStream != null) { + reader.Stream = readerContext.DebugStringStream; reader.CurrentSection = dwarf.StringTable; dwarf.StringTable.ReadInternal(reader); } reader.Log = readerContext.DebugLinePrinter; - reader.Stream = readerContext.DebugLineStream; - if (reader.Stream != null) + if (readerContext.DebugLineStream != null) { + reader.Stream = readerContext.DebugLineStream; reader.CurrentSection = dwarf.LineSection; dwarf.LineSection.ReadInternal(reader); } - reader.Log = null; - reader.Stream = readerContext.DebugAddressRangeStream; - if (reader.Stream != null) + + if (readerContext.DebugAddressRangeStream != null) { + reader.Stream = readerContext.DebugAddressRangeStream; reader.CurrentSection = dwarf.AddressRangeTable; dwarf.AddressRangeTable.ReadInternal(reader); } reader.Log = null; - reader.Stream = readerContext.DebugLocationStream; - if (reader.Stream != null) + if (readerContext.DebugLocationStream != null) { + reader.Stream = readerContext.DebugLocationStream; reader.CurrentSection = dwarf.LocationSection; dwarf.LocationSection.ReadInternal(reader); } reader.Log = null; - reader.Stream = readerContext.DebugInfoStream; - if (reader.Stream != null) + if (readerContext.DebugInfoStream != null) { + reader.Stream = readerContext.DebugInfoStream; reader.DefaultUnitKind = DwarfUnitKind.Compile; reader.CurrentSection = dwarf.InfoSection; dwarf.InfoSection.ReadInternal(reader); diff --git a/src/LibObjectFile/Dwarf/DwarfFileName.cs b/src/LibObjectFile/Dwarf/DwarfFileName.cs index ece43bc..10a93c2 100644 --- a/src/LibObjectFile/Dwarf/DwarfFileName.cs +++ b/src/LibObjectFile/Dwarf/DwarfFileName.cs @@ -8,9 +8,14 @@ namespace LibObjectFile.Dwarf { public sealed class DwarfFileName { - public string Name { get; set; } + public DwarfFileName(string name) + { + Name = name; + } + + public string Name { get; } - public string Directory { get; set; } + public string? Directory { get; set; } public ulong Time { get; set; } @@ -18,7 +23,6 @@ public sealed class DwarfFileName public override string ToString() { - if (string.IsNullOrEmpty(Name)) return ""; if (Directory != null) { return Directory.Contains(Path.AltDirectorySeparatorChar) ? $"{Directory}{Path.AltDirectorySeparatorChar}{Name}" : $"{Directory}{Path.DirectorySeparatorChar}{Name}"; diff --git a/src/LibObjectFile/Dwarf/DwarfHelper.cs b/src/LibObjectFile/Dwarf/DwarfHelper.cs index daad1a9..1ec6f08 100644 --- a/src/LibObjectFile/Dwarf/DwarfHelper.cs +++ b/src/LibObjectFile/Dwarf/DwarfHelper.cs @@ -9,7 +9,7 @@ namespace LibObjectFile.Dwarf { public static partial class DwarfHelper { - public static ulong SizeOfStringUTF8NullTerminated(string text) + public static ulong SizeOfStringUTF8NullTerminated(string? text) { if (text == null) return 0; return (ulong)Encoding.UTF8.GetByteCount(text) + 1; diff --git a/src/LibObjectFile/Dwarf/DwarfLanguageKindEx.cs b/src/LibObjectFile/Dwarf/DwarfLanguageKindEx.cs index 511fd57..887cd6e 100644 --- a/src/LibObjectFile/Dwarf/DwarfLanguageKindEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfLanguageKindEx.cs @@ -30,7 +30,7 @@ public bool Equals(DwarfLanguageKindEx other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DwarfLanguageKindEx other && Equals(other); } diff --git a/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs b/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs index 66977b6..e55b2a6 100644 --- a/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs @@ -21,6 +21,6 @@ internal DwarfLayoutContext(DwarfFile file, DwarfLayoutConfig config, Diagnostic public bool HasErrors => Diagnostics.HasErrors; - public DwarfUnit CurrentUnit { get; internal set; } + public DwarfUnit? CurrentUnit { get; internal set; } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLine.cs b/src/LibObjectFile/Dwarf/DwarfLine.cs index e6965b7..3581424 100644 --- a/src/LibObjectFile/Dwarf/DwarfLine.cs +++ b/src/LibObjectFile/Dwarf/DwarfLine.cs @@ -31,7 +31,7 @@ public DwarfLine() /// /// The identity of the source file corresponding to a machine instruction. /// - public DwarfFileName File { get; set; } + public DwarfFileName? File { get; set; } /// /// An unsigned integer indicating a source line number. @@ -118,7 +118,7 @@ internal void Delta(DwarfLine against, out ulong deltaAddress, isDiscriminatorChanged = against.Discriminator != this.Discriminator; } - internal void Reset(DwarfFileName firstFile, bool isStatement) + internal void Reset(DwarfFileName? firstFile, bool isStatement) { Address = 0; File = firstFile; diff --git a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs index 185f5d3..5787802 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs @@ -221,7 +221,7 @@ protected override void Read(DwarfReader reader) break; } - var fileName = new DwarfFileName {Name = name}; + var fileName = new DwarfFileName(name); var directoryIndex = reader.ReadULEB128(); if (!name.Contains('/') && !name.Contains('\\') && directoryIndex > 0 && (directoryIndex - 1) < (ulong) directories.Count) @@ -475,10 +475,12 @@ protected override void Read(DwarfReader reader) var fileTime = reader.ReadULEB128(); var fileSize = reader.ReadULEB128(); - var debugFileName = new DwarfFileName() {Name = fileName}; - debugFileName.Directory = fileDirectoryIndex == 0 || fileDirectoryIndex >= directories.Count ? null : directories[fileDirectoryIndex - 1]; - debugFileName.Time = fileTime; - debugFileName.Size = fileSize; + var debugFileName = new DwarfFileName(fileName) + { + Directory = fileDirectoryIndex == 0 || fileDirectoryIndex >= directories.Count ? null : directories[fileDirectoryIndex - 1], + Time = fileTime, + Size = fileSize + }; state.File = debugFileName; @@ -897,6 +899,10 @@ private void WriteDebugLineOpCodes(DwarfWriter writer, uint opCodeBase) if (fileNameChanged) { var fileName = debugLine.File; + if (fileName is null) + { + throw new InvalidOperationException("File name cannot be null"); + } // DW_LNS_set_file if (_fileNameToIndex.TryGetValue(fileName, out var fileIndex)) @@ -1167,6 +1173,11 @@ private void LayoutDebugLineOpCodes(ref ulong sizeOf, uint opCodeBase) { var fileName = debugLine.File; + if (fileName is null) + { + throw new InvalidOperationException("File name cannot be null"); + } + // DW_LNS_set_file if (_fileNameToIndex.TryGetValue(fileName, out var fileIndex)) { diff --git a/src/LibObjectFile/Dwarf/DwarfLineState.cs b/src/LibObjectFile/Dwarf/DwarfLineState.cs index aebdab8..9bb37e4 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineState.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineState.cs @@ -29,7 +29,7 @@ internal struct DwarfLineState /// /// The identity of the source file corresponding to a machine instruction. /// - public DwarfFileName File { get; set; } + public DwarfFileName? File { get; set; } /// /// An unsigned integer indicating a source line number. @@ -120,7 +120,7 @@ internal void Delta(in DwarfLineState against, isDiscriminatorChanged = against.Discriminator != this.Discriminator; } - internal void Reset(DwarfFileName firstFile, bool isStatement) + internal void Reset(DwarfFileName? firstFile, bool isStatement) { Address = 0; File = firstFile; diff --git a/src/LibObjectFile/Dwarf/DwarfLocation.cs b/src/LibObjectFile/Dwarf/DwarfLocation.cs index e6a9f46..1fccf4a 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocation.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocation.cs @@ -26,13 +26,13 @@ public DwarfLocation(DwarfLocationList locationList) public DwarfInteger AsValue; - public object AsObject; + public object? AsObject; - public DwarfExpression AsExpression => AsObject as DwarfExpression; + public DwarfExpression? AsExpression => AsObject as DwarfExpression; - public DwarfLocationList AsLocationList => AsObject as DwarfLocationList; + public DwarfLocationList? AsLocationList => AsObject as DwarfLocationList; - public DwarfDIE AsReference => AsObject as DwarfDIE; + public DwarfDIE? AsReference => AsObject as DwarfDIE; public override string ToString() { diff --git a/src/LibObjectFile/Dwarf/DwarfLocationList.cs b/src/LibObjectFile/Dwarf/DwarfLocationList.cs index d7ab162..e84385e 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationList.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationList.cs @@ -46,7 +46,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) } // End of list - endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.AddressSize); + endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); Size = endOffset - Offset; } diff --git a/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs b/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs index 829acb2..1385032 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs @@ -10,7 +10,7 @@ public class DwarfLocationListEntry : DwarfObject public ulong End; - public DwarfExpression Expression; + public DwarfExpression? Expression; public DwarfLocationListEntry() { @@ -44,7 +44,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) { var endOffset = Offset; - endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.AddressSize); + endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); if (Expression != null) { Expression.Offset = endOffset; diff --git a/src/LibObjectFile/Dwarf/DwarfObject.cs b/src/LibObjectFile/Dwarf/DwarfObject.cs index a66f7b8..27a4c76 100644 --- a/src/LibObjectFile/Dwarf/DwarfObject.cs +++ b/src/LibObjectFile/Dwarf/DwarfObject.cs @@ -9,7 +9,7 @@ namespace LibObjectFile.Dwarf; public abstract class DwarfObject : ObjectFileNodeBase { - public DwarfFile GetParentFile() + public DwarfFile? GetParentFile() { var check = (ObjectFileNodeBase)this; while (check != null) @@ -20,7 +20,7 @@ public DwarfFile GetParentFile() return null; } - public DwarfUnit GetParentUnit() + public DwarfUnit? GetParentUnit() { var check = (ObjectFileNodeBase)this; while (check != null) @@ -31,7 +31,7 @@ public DwarfUnit GetParentUnit() return null; } - public DwarfSection GetParentSection() + public DwarfSection? GetParentSection() { var check = (ObjectFileNodeBase)this; while (check != null) @@ -59,9 +59,9 @@ protected override void ValidateParent(ObjectFileNodeBase parent) /// does not belong to an existing . /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public new TContainer Parent + public new TContainer? Parent { - get => (TContainer)base.Parent; + get => (TContainer?)base.Parent; internal set => base.Parent = value; } diff --git a/src/LibObjectFile/Dwarf/DwarfOperation.cs b/src/LibObjectFile/Dwarf/DwarfOperation.cs index 241d89b..a28eddb 100644 --- a/src/LibObjectFile/Dwarf/DwarfOperation.cs +++ b/src/LibObjectFile/Dwarf/DwarfOperation.cs @@ -13,7 +13,7 @@ public class DwarfOperation : DwarfObject { public DwarfOperationKindEx Kind { get; set; } - public object Operand0 { get; set; } + public object? Operand0 { get; set; } public DwarfInteger Operand1; @@ -290,7 +290,7 @@ protected override void Read(DwarfReader reader) { ulong offset; // a reference to a debugging information entry that describes the dereferenced object’s value - if (reader.CurrentUnit.Version == 2) + if (reader.CurrentUnit!.Version == 2) { offset = reader.ReadUInt(); } @@ -434,7 +434,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) switch (Kind.Value) { case DwarfOperationKind.Addr: - endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.AddressSize); + endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); break; case DwarfOperationKind.Const1u: case DwarfOperationKind.Const1s: @@ -617,7 +617,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) break; case DwarfOperationKind.CallRef: - endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.AddressSize); + endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); break; case DwarfOperationKind.BitPiece: @@ -646,7 +646,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) case DwarfOperationKind.ImplicitPointer: case DwarfOperationKind.GNUImplicitPointer: // a reference to a debugging information entry that describes the dereferenced object’s value - if (layoutContext.CurrentUnit.Version == 2) + if (layoutContext.CurrentUnit!.Version == 2) { endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.AddressSize); } @@ -688,7 +688,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) // must be a DW_TAG_base_type entry that provides the type of the constant provided endOffset += SizeOfDIEReference(layoutContext); - endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, layoutContext.CurrentUnit.AddressSize); + endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, layoutContext.CurrentUnit!.AddressSize); break; } @@ -733,7 +733,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) break; case DwarfOperationKind.GNUEncodedAddr: - endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, layoutContext.CurrentUnit.AddressSize); + endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, layoutContext.CurrentUnit!.AddressSize); break; case DwarfOperationKind.GNUParameterRef: @@ -881,7 +881,7 @@ protected override void Write(DwarfWriter writer) case DwarfOperationKind.Bra: case DwarfOperationKind.Skip: - writer.WriteU16((ushort)((long)Offset + 2 - (long)((DwarfOperation)Operand0).Offset)); + writer.WriteU16((ushort)((long)Offset + 2 - (long)((DwarfOperation)Operand0!).Offset)); break; case DwarfOperationKind.Lit0: @@ -992,15 +992,15 @@ protected override void Write(DwarfWriter writer) break; case DwarfOperationKind.Call2: - writer.WriteU16((ushort)((DwarfDIE)Operand0).Offset); + writer.WriteU16((ushort)((DwarfDIE)Operand0!).Offset); break; case DwarfOperationKind.Call4: - writer.WriteU32((uint)((DwarfDIE)Operand0).Offset); + writer.WriteU32((uint)((DwarfDIE)Operand0!).Offset); break; case DwarfOperationKind.CallRef: - writer.WriteUInt(((DwarfDIE)Operand0).Offset); + writer.WriteUInt(((DwarfDIE)Operand0!).Offset); break; case DwarfOperationKind.BitPiece: @@ -1010,7 +1010,7 @@ protected override void Write(DwarfWriter writer) case DwarfOperationKind.ImplicitValue: { - var stream = (Stream)Operand0; + var stream = (Stream)Operand0!; writer.WriteULEB128((ulong)stream.Position); writer.Write(stream); break; @@ -1019,9 +1019,9 @@ protected override void Write(DwarfWriter writer) case DwarfOperationKind.ImplicitPointer: case DwarfOperationKind.GNUImplicitPointer: { - ulong offset = ((DwarfDIE)Operand0).Offset; + ulong offset = ((DwarfDIE)Operand0!).Offset; // a reference to a debugging information entry that describes the dereferenced object’s value - if (writer.CurrentUnit.Version == 2) + if (writer.CurrentUnit!.Version == 2) { writer.WriteUInt(offset); } @@ -1037,7 +1037,7 @@ protected override void Write(DwarfWriter writer) case DwarfOperationKind.EntryValue: case DwarfOperationKind.GNUEntryValue: { - var expression = (DwarfExpression)Operand0; + var expression = (DwarfExpression)Operand0!; writer.WriteULEB128(expression.Size); expression.WriteInternal(writer); break; @@ -1051,7 +1051,7 @@ protected override void Write(DwarfWriter writer) // The first operand is an unsigned LEB128 integer that represents the offset // of a debugging information entry in the current compilation unit, which // must be a DW_TAG_base_type entry that provides the type of the constant provided - writer.WriteULEB128(((DwarfDIE)Operand0).Offset); + writer.WriteULEB128(((DwarfDIE)Operand0!).Offset); WriteEncodedValue(writer, Kind, Operand1.U64, (byte)Operand2.U64); break; } @@ -1068,7 +1068,7 @@ protected override void Write(DwarfWriter writer) // The second operand is an unsigned LEB128 number that represents the offset // of a debugging information entry in the current compilation unit - writer.WriteULEB128(((DwarfDIE)Operand0).Offset); + writer.WriteULEB128(((DwarfDIE)Operand0!).Offset); break; } @@ -1085,7 +1085,7 @@ protected override void Write(DwarfWriter writer) // The second operand is an unsigned LEB128 number that represents the offset // of a debugging information entry in the current compilation unit - writer.WriteULEB128(((DwarfDIE)Operand0).Offset); + writer.WriteULEB128(((DwarfDIE)Operand0!).Offset); break; } @@ -1093,7 +1093,7 @@ protected override void Write(DwarfWriter writer) case DwarfOperationKind.GNUConvert: case DwarfOperationKind.Reinterpret: case DwarfOperationKind.GNUReinterpret: - writer.WriteULEB128(((DwarfDIE)Operand0).Offset); + writer.WriteULEB128(((DwarfDIE)Operand0!).Offset); break; case DwarfOperationKind.GNUPushTlsAddress: @@ -1166,7 +1166,7 @@ private static void WriteEncodedValue(DwarfWriter writer, DwarfOperationKindEx k private static void DwarfExpressionLocationDIEReferenceResolver(ref DwarfReader.DwarfDIEReference dieRef) { var op = (DwarfOperation)dieRef.DwarfObject; - op.Operand0 = dieRef.Resolved; + op.Operand0 = dieRef.Resolved!; } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfOperationKindEx.cs b/src/LibObjectFile/Dwarf/DwarfOperationKindEx.cs index 7f91c06..f932e6f 100644 --- a/src/LibObjectFile/Dwarf/DwarfOperationKindEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfOperationKindEx.cs @@ -30,7 +30,7 @@ public bool Equals(DwarfOperationKindEx other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DwarfOperationKindEx other && Equals(other); } diff --git a/src/LibObjectFile/Dwarf/DwarfPrinter.cs b/src/LibObjectFile/Dwarf/DwarfPrinter.cs index a1b1eb5..f4b2e59 100644 --- a/src/LibObjectFile/Dwarf/DwarfPrinter.cs +++ b/src/LibObjectFile/Dwarf/DwarfPrinter.cs @@ -93,11 +93,11 @@ public static void Print(this DwarfDIE die, TextWriter writer, int level = 0) { if (writer == null) throw new ArgumentNullException(nameof(writer)); - writer.WriteLine($" <{level}><{die.Offset:x}>: Abbrev Number: {die.Abbrev.Code} ({die.Tag})"); + writer.WriteLine($" <{level}><{die.Offset:x}>: Abbrev Number: {die.Abbrev!.Code} ({die.Tag})"); foreach (var attr in die.Attributes) { - string attrValue = null; + string? attrValue = null; switch (attr.ValueAsObject) { case DwarfDIE dieRef: diff --git a/src/LibObjectFile/Dwarf/DwarfReader.cs b/src/LibObjectFile/Dwarf/DwarfReader.cs index 306939b..9e40132 100644 --- a/src/LibObjectFile/Dwarf/DwarfReader.cs +++ b/src/LibObjectFile/Dwarf/DwarfReader.cs @@ -37,9 +37,9 @@ internal DwarfReader(DwarfReaderContext context, DwarfFile file, DiagnosticBag d internal int DIELevel { get; set; } - internal DwarfDIE CurrentDIE => _stack.Count > 0 ? _stack.Peek() : null; + internal DwarfDIE? CurrentDIE => _stack.Count > 0 ? _stack.Peek() : null; - internal DwarfLineProgramTable CurrentLineProgramTable => _stackWithLineProgramTable.Count > 0 ? _stackWithLineProgramTable.Peek().CurrentLineProgramTable : null; + internal DwarfLineProgramTable? CurrentLineProgramTable => _stackWithLineProgramTable.Count > 0 ? _stackWithLineProgramTable.Peek().CurrentLineProgramTable : null; internal DwarfAttributeDescriptor CurrentAttributeDescriptor { get; set; } @@ -49,7 +49,7 @@ internal DwarfReader(DwarfReaderContext context, DwarfFile file, DiagnosticBag d internal void PushDIE(DwarfDIE die) { - _registeredDIEPerCompilationUnit.Add(die.Offset - CurrentUnit.Offset, die); + _registeredDIEPerCompilationUnit.Add(die.Offset - CurrentUnit!.Offset, die); _registeredDIEPerSection.Add(die.Offset, die); _stack.Push(die); } @@ -62,8 +62,11 @@ internal void PushLineProgramTable(DwarfLineProgramTable lineTable) return; } - _stackWithLineProgramTable.Push(dieWithLineProgramTable); - dieWithLineProgramTable.CurrentLineProgramTable = lineTable; + if (dieWithLineProgramTable != null) + { + _stackWithLineProgramTable.Push(dieWithLineProgramTable); + dieWithLineProgramTable.CurrentLineProgramTable = lineTable; + } } internal void PopDIE() @@ -162,7 +165,7 @@ public DwarfDIEReference(ulong offset, object dwarfObject, DwarfDIEReferenceReso public readonly DwarfDIEReferenceResolver Resolver; - public DwarfDIE Resolved; + public DwarfDIE? Resolved; } internal delegate void DwarfDIEReferenceResolver(ref DwarfDIEReference reference); diff --git a/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs b/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs index 049ebd6..f2ce923 100644 --- a/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs +++ b/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs @@ -9,7 +9,7 @@ namespace LibObjectFile.Dwarf { public abstract class DwarfReaderWriter : ObjectFileReaderWriter { - internal DwarfReaderWriter(DwarfFile file, DiagnosticBag diagnostics) : base(null, diagnostics) + internal DwarfReaderWriter(DwarfFile file, DiagnosticBag diagnostics) : base(System.IO.Stream.Null, diagnostics) { File = file; } @@ -20,9 +20,9 @@ internal DwarfReaderWriter(DwarfFile file, DiagnosticBag diagnostics) : base(nul public DwarfAddressSize AddressSize { get; internal set; } - public DwarfSection CurrentSection { get; internal set; } + public DwarfSection? CurrentSection { get; internal set; } - public DwarfUnit CurrentUnit { get; internal set; } + public DwarfUnit? CurrentUnit { get; internal set; } public DwarfAddressSize SizeOfUIntEncoding() { diff --git a/src/LibObjectFile/Dwarf/DwarfReaderWriterContext.cs b/src/LibObjectFile/Dwarf/DwarfReaderWriterContext.cs index 97d3bf3..4d7a9bf 100644 --- a/src/LibObjectFile/Dwarf/DwarfReaderWriterContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfReaderWriterContext.cs @@ -12,18 +12,18 @@ public abstract class DwarfReaderWriterContext public DwarfAddressSize AddressSize { get; set; } - public Stream DebugAbbrevStream { get; set; } + public Stream? DebugAbbrevStream { get; set; } - public Stream DebugStringStream { get; set; } + public Stream? DebugStringStream { get; set; } - public Stream DebugAddressRangeStream { get; set; } + public Stream? DebugAddressRangeStream { get; set; } - public Stream DebugLineStream { get; set; } + public Stream? DebugLineStream { get; set; } - public TextWriter DebugLinePrinter { get; set; } + public TextWriter? DebugLinePrinter { get; set; } - public Stream DebugInfoStream { get; set; } + public Stream? DebugInfoStream { get; set; } - public Stream DebugLocationStream { get; set; } + public Stream? DebugLocationStream { get; set; } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs b/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs index 0908472..d5ddd3b 100644 --- a/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs @@ -33,7 +33,7 @@ public static void CopyRelocationsTo(this DwarfRelocatableSection dwarfRelocSect CopyRelocationsX86_64(dwarfRelocSection, elfContext, relocTable); break; default: - throw new NotImplementedException($"The relocation for architecture {relocTable.Parent.Arch} is not supported/implemented."); + throw new NotImplementedException($"The relocation for architecture {relocTable.Parent!.Arch} is not supported/implemented."); } } diff --git a/src/LibObjectFile/Dwarf/DwarfSection.cs b/src/LibObjectFile/Dwarf/DwarfSection.cs index b2f33bf..68a9e43 100644 --- a/src/LibObjectFile/Dwarf/DwarfSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfSection.cs @@ -22,9 +22,9 @@ protected override void ValidateParent(ObjectFileNodeBase parent) /// does not belong to an existing . /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public new DwarfFile Parent + public new DwarfFile? Parent { - get => (DwarfFile)base.Parent; + get => (DwarfFile?)base.Parent; internal set => base.Parent = value; } } diff --git a/src/LibObjectFile/Dwarf/DwarfSectionLink.cs b/src/LibObjectFile/Dwarf/DwarfSectionLink.cs index 3f92cef..2bcfa17 100644 --- a/src/LibObjectFile/Dwarf/DwarfSectionLink.cs +++ b/src/LibObjectFile/Dwarf/DwarfSectionLink.cs @@ -25,7 +25,7 @@ public bool Equals(DwarfSectionLink other) return Offset == other.Offset; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DwarfSectionLink other && Equals(other); } diff --git a/src/LibObjectFile/Dwarf/DwarfStringTable.cs b/src/LibObjectFile/Dwarf/DwarfStringTable.cs index f2a9abf..61314ac 100644 --- a/src/LibObjectFile/Dwarf/DwarfStringTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfStringTable.cs @@ -21,7 +21,8 @@ public DwarfStringTable() : this(new MemoryStream()) public DwarfStringTable(Stream stream) { - Stream = stream ?? throw new ArgumentNullException(nameof(stream)); + ArgumentNullException.ThrowIfNull(stream); + _stream = stream; _stringToOffset = new Dictionary(); _offsetToString = new Dictionary(); } @@ -46,13 +47,13 @@ public string GetStringFromOffset(ulong offset) return text; } - public bool Contains(string text) + public bool Contains(string? text) { if (text == null) return false; return _stringToOffset.ContainsKey(text); } - public ulong GetOrCreateString(string text) + public ulong GetOrCreateString(string? text) { if (text == null) return 0; diff --git a/src/LibObjectFile/Dwarf/DwarfTagEx.cs b/src/LibObjectFile/Dwarf/DwarfTagEx.cs index 5681d85..69a6a83 100644 --- a/src/LibObjectFile/Dwarf/DwarfTagEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfTagEx.cs @@ -28,7 +28,7 @@ public bool Equals(DwarfTagEx other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DwarfTagEx other && Equals(other); } diff --git a/src/LibObjectFile/Dwarf/DwarfUnit.cs b/src/LibObjectFile/Dwarf/DwarfUnit.cs index 03bfa44..30e5a45 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnit.cs @@ -12,7 +12,7 @@ namespace LibObjectFile.Dwarf /// public abstract class DwarfUnit : DwarfContainer { - private DwarfDIE _root; + private DwarfDIE? _root; /// /// Gets or sets the encoding of this unit. @@ -47,10 +47,10 @@ public abstract class DwarfUnit : DwarfContainer /// /// Gets or sets the root of this compilation unit. /// - public DwarfDIE Root + public DwarfDIE? Root { get => _root; - set => AttachChild(this, value, ref _root, true); + set => AttachNullableChild(this, value, ref _root); } /// @@ -59,7 +59,7 @@ public DwarfDIE Root /// /// This abbreviation is automatically setup after reading or after updating the layout through . /// - public DwarfAbbreviation Abbreviation { get; internal set; } + public DwarfAbbreviation? Abbreviation { get; internal set; } public override void Verify(DiagnosticBag diagnostics) { @@ -155,11 +155,11 @@ protected override void Read(DwarfReader reader) Size = reader.Offset - Offset; } - internal static DwarfUnit ReadInstance(DwarfReader reader, out ulong offsetEndOfUnit) + internal static DwarfUnit? ReadInstance(DwarfReader reader, out ulong offsetEndOfUnit) { var startOffset = reader.Offset; - DwarfUnit unit = null; + DwarfUnit? unit = null; // 1. unit_length var unit_length = reader.ReadUnitLength(); diff --git a/src/LibObjectFile/Dwarf/DwarfUnitKind.cs b/src/LibObjectFile/Dwarf/DwarfUnitKind.cs index 07a6b8f..57cddc6 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnitKind.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnitKind.cs @@ -34,7 +34,7 @@ public bool Equals(DwarfUnitKindEx other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DwarfUnitKindEx other && Equals(other); } diff --git a/src/LibObjectFile/Elf/ElfArch.cs b/src/LibObjectFile/Elf/ElfArch.cs index 2b2cb73..5478ed7 100644 --- a/src/LibObjectFile/Elf/ElfArch.cs +++ b/src/LibObjectFile/Elf/ElfArch.cs @@ -38,7 +38,7 @@ public bool Equals(ElfArchEx other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfArchEx other && Equals(other); } diff --git a/src/LibObjectFile/Elf/ElfFilePart.cs b/src/LibObjectFile/Elf/ElfFilePart.cs index ca8607a..13db437 100644 --- a/src/LibObjectFile/Elf/ElfFilePart.cs +++ b/src/LibObjectFile/Elf/ElfFilePart.cs @@ -43,7 +43,7 @@ public ElfFilePart(ElfSection section) public readonly ulong EndOffset; - public readonly ElfSection Section; + public readonly ElfSection? Section; public int CompareTo(ElfFilePart other) { @@ -67,7 +67,7 @@ public bool Equals(ElfFilePart other) return StartOffset == other.StartOffset && EndOffset == other.EndOffset; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfFilePart other && Equals(other); } diff --git a/src/LibObjectFile/Elf/ElfHeaderFlags.cs b/src/LibObjectFile/Elf/ElfHeaderFlags.cs index 70347e2..d8ddd7a 100644 --- a/src/LibObjectFile/Elf/ElfHeaderFlags.cs +++ b/src/LibObjectFile/Elf/ElfHeaderFlags.cs @@ -26,7 +26,7 @@ public bool Equals(ElfHeaderFlags other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfHeaderFlags other && Equals(other); } diff --git a/src/LibObjectFile/Elf/ElfNativeExtensions.cs b/src/LibObjectFile/Elf/ElfNativeExtensions.cs index 4ae30ce..6f521cf 100644 --- a/src/LibObjectFile/Elf/ElfNativeExtensions.cs +++ b/src/LibObjectFile/Elf/ElfNativeExtensions.cs @@ -22,7 +22,7 @@ public bool Equals(Elf32_Shdr other) return sh_name.Equals(other.sh_name) && sh_type.Equals(other.sh_type) && sh_flags.Equals(other.sh_flags) && sh_addr.Equals(other.sh_addr) && sh_offset.Equals(other.sh_offset) && sh_size.Equals(other.sh_size) && sh_link.Equals(other.sh_link) && sh_info.Equals(other.sh_info) && sh_addralign.Equals(other.sh_addralign) && sh_entsize.Equals(other.sh_entsize); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Elf32_Shdr other && Equals(other); } @@ -67,7 +67,7 @@ public bool Equals(Elf64_Shdr other) return sh_name.Equals(other.sh_name) && sh_type.Equals(other.sh_type) && sh_flags.Equals(other.sh_flags) && sh_addr.Equals(other.sh_addr) && sh_offset.Equals(other.sh_offset) && sh_size.Equals(other.sh_size) && sh_link.Equals(other.sh_link) && sh_info.Equals(other.sh_info) && sh_addralign.Equals(other.sh_addralign) && sh_entsize.Equals(other.sh_entsize); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Elf64_Shdr other && Equals(other); } diff --git a/src/LibObjectFile/Elf/ElfOSAbi2.cs b/src/LibObjectFile/Elf/ElfOSAbi2.cs index 7d5f4b0..8090671 100644 --- a/src/LibObjectFile/Elf/ElfOSAbi2.cs +++ b/src/LibObjectFile/Elf/ElfOSAbi2.cs @@ -35,7 +35,7 @@ public bool Equals(ElfOSABIEx other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfOSABIEx other && Equals(other); } diff --git a/src/LibObjectFile/Elf/ElfObject.cs b/src/LibObjectFile/Elf/ElfObject.cs index fcc6cd3..1ad4fb5 100644 --- a/src/LibObjectFile/Elf/ElfObject.cs +++ b/src/LibObjectFile/Elf/ElfObject.cs @@ -26,9 +26,9 @@ protected override void ValidateParent(ObjectFileNodeBase parent) /// does not belong to an existing . /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public new ElfObjectFile Parent + public new ElfObjectFile? Parent { - get => (ElfObjectFile)base.Parent; + get => (ElfObjectFile?)base.Parent; internal set => base.Parent = value; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfObjectFile.cs b/src/LibObjectFile/Elf/ElfObjectFile.cs index a4f9e2c..b7bb751 100644 --- a/src/LibObjectFile/Elf/ElfObjectFile.cs +++ b/src/LibObjectFile/Elf/ElfObjectFile.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using LibObjectFile.Utils; @@ -19,7 +20,7 @@ namespace LibObjectFile.Elf; public sealed class ElfObjectFile : ObjectFileNode { private readonly List _sections; - private ElfSectionHeaderStringTable _sectionHeaderStringTable; + private ElfSectionHeaderStringTable? _sectionHeaderStringTable; private readonly List _segments; public const int IdentSizeInBytes = ElfNative.EI_NIDENT; @@ -140,7 +141,7 @@ internal ElfObjectFile(bool addDefaultSections) /// Gets or sets the section header string table used to store the names of the sections. /// Must have been added to . /// - public ElfSectionHeaderStringTable SectionHeaderStringTable + public ElfSectionHeaderStringTable? SectionHeaderStringTable { get => _sectionHeaderStringTable; set @@ -470,7 +471,7 @@ public void InsertSectionAt(int index, ElfSection section) } else { - ElfSection previousSection = null; + ElfSection? previousSection = null; for (int j = 0; j < index; j++) { var sectionBefore = _sections[j]; @@ -654,7 +655,7 @@ public static bool IsElf(Stream stream, out ElfEncoding encoding) return false; } - private static bool TryReadElfObjectFileHeader(Stream stream, out ElfObjectFile file) + private static bool TryReadElfObjectFileHeader(Stream stream, [NotNullWhen(true)] out ElfObjectFile? file) { if (stream == null) throw new ArgumentNullException(nameof(stream)); @@ -687,7 +688,7 @@ private static bool TryReadElfObjectFileHeader(Stream stream, out ElfObjectFile /// The stream to read ELF object file from /// The options for the reader /// An instance of if the read was successful. - public static ElfObjectFile Read(Stream stream, ElfReaderOptions options = null) + public static ElfObjectFile Read(Stream stream, ElfReaderOptions? options = null) { if (!TryRead(stream, out var objectFile, out var diagnostics, options)) { @@ -704,7 +705,7 @@ public static ElfObjectFile Read(Stream stream, ElfReaderOptions options = null) /// A instance /// The options for the reader /// true An instance of if the read was successful. - public static bool TryRead(Stream stream, out ElfObjectFile objectFile, out DiagnosticBag diagnostics, ElfReaderOptions options = null) + public static bool TryRead(Stream stream, [NotNullWhen(true)] out ElfObjectFile? objectFile, [NotNullWhen(false)] out DiagnosticBag? diagnostics, ElfReaderOptions? options = null) { if (stream == null) throw new ArgumentNullException(nameof(stream)); diff --git a/src/LibObjectFile/Elf/ElfPrinter.cs b/src/LibObjectFile/Elf/ElfPrinter.cs index f6666b3..71f219a 100644 --- a/src/LibObjectFile/Elf/ElfPrinter.cs +++ b/src/LibObjectFile/Elf/ElfPrinter.cs @@ -119,7 +119,7 @@ public static void PrintSectionGroups(ElfObjectFile elf, TextWriter writer) private static string GetElfSectionName(ElfSection section) { - return section.Parent.SectionHeaderStringTable == null ? "" : section.Name.Value; + return section.Parent?.SectionHeaderStringTable == null ? "" : section.Name.Value!; } public static void PrintProgramHeaders(ElfObjectFile elf, TextWriter writer) @@ -203,7 +203,7 @@ public static void PrintRelocations(ElfObjectFile elf, TextWriter writer) if (entry.SymbolIndex < symbolTable.Entries.Count) { var symbolEntry = symbolTable.Entries[(int) entry.SymbolIndex]; - symbolName = symbolEntry.Name; + symbolName = symbolEntry.Name!; symbolValue = symbolEntry.Value; if (string.IsNullOrEmpty(symbolName)) @@ -213,7 +213,7 @@ public static void PrintRelocations(ElfObjectFile elf, TextWriter writer) case ElfSymbolType.Section: if (symbolEntry.Section.Section != null) { - symbolName = symbolEntry.Section.Section.Name; + symbolName = symbolEntry.Section.Section.Name!; } break; } diff --git a/src/LibObjectFile/Elf/ElfReaderOptions.cs b/src/LibObjectFile/Elf/ElfReaderOptions.cs index ea9f764..a8276c4 100644 --- a/src/LibObjectFile/Elf/ElfReaderOptions.cs +++ b/src/LibObjectFile/Elf/ElfReaderOptions.cs @@ -19,13 +19,13 @@ public class ElfReaderOptions /// Gets or sets a delegate that can be used to replace the creation of when /// reading from a Stream. /// - public TryCreateNoteDelegate TryCreateNote { get; set; } + public TryCreateNoteDelegate? TryCreateNote { get; set; } /// /// Gets or sets a delegate that can be used to replace the creation of when /// reading from a Stream. /// - public TryCreateSectionDelegate TryCreateSection { get; set; } + public TryCreateSectionDelegate? TryCreateSection { get; set; } /// /// Tries to create a section instance from the specified type. Might return null. diff --git a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs index 64a4b4b..b6d629c 100644 --- a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs +++ b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs @@ -433,7 +433,7 @@ private void VerifyAndFixProgramHeadersAndSections() if (_hasValidSectionStringTable) { - Stream.Position = (long)ObjectFile.SectionHeaderStringTable.Offset; + Stream.Position = (long)ObjectFile.SectionHeaderStringTable!.Offset; ObjectFile.SectionHeaderStringTable.ReadInternal(this); } @@ -663,7 +663,7 @@ private void VerifyAndFixProgramHeadersAndSections() if (segment.Offset >= section.Offset && segment.Offset <= sectionEndOffset) { ElfSection beginSection = section; - ElfSection endSection = null; + ElfSection? endSection = null; for (int j = i; j < orderedSections.Count; j++) { var nextSection = orderedSections[j]; @@ -698,7 +698,7 @@ private void VerifyAndFixProgramHeadersAndSections() private ElfSection CreateElfSection(uint sectionIndex, ElfSectionType sectionType, bool isNullSection) { - ElfSection section = null; + ElfSection? section = null; switch (sectionType) { diff --git a/src/LibObjectFile/Elf/ElfSection.cs b/src/LibObjectFile/Elf/ElfSection.cs index cbe385f..841a687 100644 --- a/src/LibObjectFile/Elf/ElfSection.cs +++ b/src/LibObjectFile/Elf/ElfSection.cs @@ -47,9 +47,9 @@ protected override void ValidateParent(ObjectFileNodeBase parent) /// does not belong to an existing . /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public new ElfObjectFile Parent + public new ElfObjectFile? Parent { - get => (ElfObjectFile)base.Parent; + get => (ElfObjectFile?)base.Parent; internal set => base.Parent = value; } diff --git a/src/LibObjectFile/Elf/ElfSectionLink.cs b/src/LibObjectFile/Elf/ElfSectionLink.cs index 2ffc79f..852e44d 100644 --- a/src/LibObjectFile/Elf/ElfSectionLink.cs +++ b/src/LibObjectFile/Elf/ElfSectionLink.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; namespace LibObjectFile.Elf { @@ -23,13 +24,13 @@ public ElfSectionLink(uint index) SpecialIndex = index; } - public ElfSectionLink(ElfSection section) + public ElfSectionLink(ElfSection? section) { Section = section; SpecialIndex = 0; } - public readonly ElfSection Section; + public readonly ElfSection? Section; public readonly uint SpecialIndex; @@ -50,7 +51,7 @@ public bool Equals(ElfSectionLink other) return Equals(Section, other.Section) && SpecialIndex == other.SpecialIndex; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfSectionLink other && Equals(other); } @@ -103,13 +104,13 @@ public override string ToString() return $"Unknown Section Value 0x{SpecialIndex:X8}"; } - public static implicit operator ElfSectionLink(ElfSection section) + public static implicit operator ElfSectionLink(ElfSection? section) { return new ElfSectionLink(section); } - public bool TryGetSectionSafe(string className, string propertyName, object context, DiagnosticBag diagnostics, out TSection section, params ElfSectionType[] sectionTypes) where TSection : ElfSection + public bool TryGetSectionSafe(string className, string propertyName, object context, DiagnosticBag diagnostics, [NotNullWhen(true)] out TSection? section, params ElfSectionType[] sectionTypes) where TSection : ElfSection { section = null; diff --git a/src/LibObjectFile/Elf/ElfSegment.cs b/src/LibObjectFile/Elf/ElfSegment.cs index 57a809a..bb9f54a 100644 --- a/src/LibObjectFile/Elf/ElfSegment.cs +++ b/src/LibObjectFile/Elf/ElfSegment.cs @@ -74,12 +74,12 @@ public override void UpdateLayout(DiagnosticBag diagnostics) diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentAlignmentForLoad, $"Invalid segment alignment requirements: Alignment = {alignment} must be a power of 2"); } - if (Range.BeginSection.Parent == null) + if (Range.BeginSection?.Parent == null) { diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeBeginSectionParent, $"Invalid null parent {nameof(Range)}.{nameof(Range.BeginSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); } - if (Range.EndSection.Parent == null) + if (Range.EndSection?.Parent == null) { diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndSectionParent, $"Invalid null parent {nameof(Range)}.{nameof(Range.EndSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); } @@ -103,25 +103,35 @@ public override void UpdateLayout(DiagnosticBag diagnostics) if (Size > 0) { - if (Range.BeginOffset >= Range.BeginSection.Size) + var range = Range; + if (range.BeginSection is null) { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeBeginOffset, $"Invalid {nameof(Range)}.{nameof(Range.BeginOffset)}: {Range.BeginOffset} cannot be >= {nameof(Range.BeginSection)}.{nameof(ElfSection.Size)}: {Range.BeginSection.Size} in {this}. The offset must be within the section"); + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeBeginSectionParent, $"Invalid null {nameof(Range)}.{nameof(Range.BeginSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); } - if ((Range.EndOffset >= 0 && (ulong)Range.EndOffset >= Range.EndSection.Size)) + else if (range.BeginOffset >= range.BeginSection.Size) { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndOffset, $"Invalid {nameof(Range)}.{nameof(Range.EndOffset)}: {Range.EndOffset} cannot be >= {nameof(Range)}.{nameof(ElfSegmentRange.EndSection)}.{nameof(ElfSection.Size)}: {Range.EndSection.Size} in {this}. The offset must be within the section"); + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeBeginOffset, $"Invalid {nameof(Range)}.{nameof(Range.BeginOffset)}: {Range.BeginOffset} cannot be >= {nameof(Range.BeginSection)}.{nameof(ElfSection.Size)}: {range.BeginSection.Size} in {this}. The offset must be within the section"); + } + + if (range.EndSection is null) + { + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndSectionParent, $"Invalid null {nameof(Range)}.{nameof(Range.EndSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); + } + else if ((Range.EndOffset >= 0 && (ulong)Range.EndOffset >= range.EndSection.Size)) + { + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndOffset, $"Invalid {nameof(Range)}.{nameof(Range.EndOffset)}: {Range.EndOffset} cannot be >= {nameof(Range)}.{nameof(ElfSegmentRange.EndSection)}.{nameof(ElfSection.Size)}: {range.EndSection.Size} in {this}. The offset must be within the section"); } else if (Range.EndOffset < 0) { - var endOffset = (long)Range.EndSection.Size + Range.EndOffset; + var endOffset = (long)range.EndSection.Size + Range.EndOffset; if (endOffset < 0) { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndOffset, $"Invalid relative {nameof(Range)}.{nameof(Range.EndOffset)}: {Range.EndOffset}. The resulting end offset {endOffset} with {nameof(Range)}.{nameof(ElfSegmentRange.EndSection)}.{nameof(ElfSection.Size)}: {Range.EndSection.Size} cannot be < 0 in {this}. The offset must be within the section"); + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndOffset, $"Invalid relative {nameof(Range)}.{nameof(Range.EndOffset)}: {Range.EndOffset}. The resulting end offset {endOffset} with {nameof(Range)}.{nameof(ElfSegmentRange.EndSection)}.{nameof(ElfSection.Size)}: {range.EndSection.Size} cannot be < 0 in {this}. The offset must be within the section"); } } } - if (Range.BeginSection.Parent != null && Range.EndSection.Parent != null) + if (Range.BeginSection?.Parent != null && Range.EndSection?.Parent != null) { if (Range.BeginSection.Index > Range.EndSection.Index) { diff --git a/src/LibObjectFile/Elf/ElfSegmentFlags.cs b/src/LibObjectFile/Elf/ElfSegmentFlags.cs index ccdf795..dd82cd9 100644 --- a/src/LibObjectFile/Elf/ElfSegmentFlags.cs +++ b/src/LibObjectFile/Elf/ElfSegmentFlags.cs @@ -28,7 +28,7 @@ public bool Equals(ElfSegmentFlags other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfSegmentFlags other && Equals(other); } diff --git a/src/LibObjectFile/Elf/ElfSegmentRange.cs b/src/LibObjectFile/Elf/ElfSegmentRange.cs index e33ffb4..cb4bd15 100644 --- a/src/LibObjectFile/Elf/ElfSegmentRange.cs +++ b/src/LibObjectFile/Elf/ElfSegmentRange.cs @@ -47,7 +47,7 @@ public ElfSegmentRange(ElfSection beginSection, ulong beginOffset, ElfSection en /// /// The first section. /// - public readonly ElfSection BeginSection; + public readonly ElfSection? BeginSection; /// /// The relative offset in . @@ -57,7 +57,7 @@ public ElfSegmentRange(ElfSection beginSection, ulong beginOffset, ElfSection en /// /// The last section. /// - public readonly ElfSection EndSection; + public readonly ElfSection? EndSection; /// /// The offset in the last section. If the offset is < 0, then the actual offset starts from end of the section where finalEndOffset = section.Size + EndOffset. @@ -82,7 +82,7 @@ public ulong Offset return 0; } - return BeginSection.Offset + BeginOffset; + return BeginSection!.Offset + BeginOffset; } } @@ -94,7 +94,7 @@ public ulong Size get { // If this Begin/End section are not attached we can't calculate any meaningful size - if (BeginSection?.Parent == null || EndSection?.Parent == null || BeginSection?.Parent != EndSection?.Parent) + if (BeginSection?.Parent == null || EndSection?.Parent == null || BeginSection.Parent != EndSection.Parent) { return 0; } @@ -111,7 +111,7 @@ public bool Equals(ElfSegmentRange other) return Equals(BeginSection, other.BeginSection) && BeginOffset == other.BeginOffset && Equals(EndSection, other.EndSection) && EndOffset == other.EndOffset; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfSegmentRange other && Equals(other); } @@ -138,9 +138,9 @@ public override int GetHashCode() return !left.Equals(right); } - public static implicit operator ElfSegmentRange(ElfSection section) + public static implicit operator ElfSegmentRange(ElfSection? section) { - return section == null ? Empty : new ElfSegmentRange(section); + return section is null ? Empty : new ElfSegmentRange(section); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSegmentType.cs b/src/LibObjectFile/Elf/ElfSegmentType.cs index 88fdba7..7ede7b6 100644 --- a/src/LibObjectFile/Elf/ElfSegmentType.cs +++ b/src/LibObjectFile/Elf/ElfSegmentType.cs @@ -28,7 +28,7 @@ public bool Equals(ElfSegmentType other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfSegmentType other && Equals(other); } diff --git a/src/LibObjectFile/Elf/ElfString.cs b/src/LibObjectFile/Elf/ElfString.cs index 435febb..7547d0c 100644 --- a/src/LibObjectFile/Elf/ElfString.cs +++ b/src/LibObjectFile/Elf/ElfString.cs @@ -11,13 +11,13 @@ namespace LibObjectFile.Elf /// public readonly struct ElfString : IEquatable { - private ElfString(string value, uint index) + private ElfString(string? value, uint index) { Value = value; Index = index; } - public ElfString(string value) + public ElfString(string? value) { Value = value; Index = 0; @@ -29,7 +29,7 @@ public ElfString(uint index) Index = index; } - public readonly string Value; + public readonly string? Value; public readonly uint Index; @@ -40,7 +40,7 @@ public bool Equals(ElfString other) return (Value ?? string.Empty) == (other.Value ?? string.Empty) && Index == other.Index; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfString other && Equals(other); } @@ -83,7 +83,7 @@ public override int GetHashCode() return !string.Equals(left.Value, right); } - public static implicit operator string(ElfString elfString) + public static implicit operator string?(ElfString elfString) { return elfString.Value; } diff --git a/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs b/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs index db864d9..9fe9818 100644 --- a/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs @@ -46,7 +46,7 @@ public override ElfSectionType Type /// /// Gets or sets the associated stream to this section. /// - public Stream Stream { get; set; } + public Stream? Stream { get; set; } protected override void Read(ElfReader reader) { diff --git a/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs b/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs index e0d8435..00a6aa3 100644 --- a/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs @@ -16,7 +16,7 @@ public ElfBinaryShadowSection() { } - public Stream Stream { get; set; } + public Stream? Stream { get; set; } protected override void Read(ElfReader reader) { diff --git a/src/LibObjectFile/Elf/Sections/ElfCustomNote.cs b/src/LibObjectFile/Elf/Sections/ElfCustomNote.cs index 27d614c..6ddc185 100644 --- a/src/LibObjectFile/Elf/Sections/ElfCustomNote.cs +++ b/src/LibObjectFile/Elf/Sections/ElfCustomNote.cs @@ -13,20 +13,26 @@ namespace LibObjectFile.Elf /// public class ElfCustomNote : ElfNote { + public ElfCustomNote(string name, ElfNoteTypeEx type) + { + Name = name; + Type = type; + } + /// /// Gets or sets the name of this note. /// - public string Name { get; set; } + public string Name { get; } /// /// Gets or sets the associated descriptor data. /// - public Stream Descriptor { get; set; } + public Stream? Descriptor { get; set; } /// /// Gets or sets the type of this note. /// - public ElfNoteTypeEx Type { get; set; } + public ElfNoteTypeEx Type { get; } public override string GetName() { diff --git a/src/LibObjectFile/Elf/Sections/ElfGnuNoteBuildId.cs b/src/LibObjectFile/Elf/Sections/ElfGnuNoteBuildId.cs index 7976c74..bf8958d 100644 --- a/src/LibObjectFile/Elf/Sections/ElfGnuNoteBuildId.cs +++ b/src/LibObjectFile/Elf/Sections/ElfGnuNoteBuildId.cs @@ -12,7 +12,7 @@ public class ElfGnuNoteBuildId : ElfGnuNote { public override ElfNoteTypeEx GetNoteType() => new ElfNoteTypeEx(ElfNoteType.GNU_BUILD_ID); - public Stream BuildId { get; set; } + public Stream? BuildId { get; set; } public override uint GetDescriptorSize() => BuildId != null ? (uint)BuildId.Length : 0; diff --git a/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs b/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs index 7f79618..57219a8 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs @@ -158,18 +158,14 @@ private static ElfNote CreateNote(ElfReader reader, string name, ElfNoteType typ } } - ElfNote note = null; + ElfNote? note = null; if (reader.Options.TryCreateNote != null) { note = reader.Options.TryCreateNote(name, type); } - return note ?? new ElfCustomNote() - { - Name = name, - Type = type - }; + return note ?? new ElfCustomNote(name, type); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfNoteType.cs b/src/LibObjectFile/Elf/Sections/ElfNoteType.cs index 2baa9cf..5639f71 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNoteType.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNoteType.cs @@ -36,7 +36,7 @@ public bool Equals(ElfNoteTypeEx other) return Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfNoteTypeEx other && Equals(other); } diff --git a/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs b/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs index f1f7754..a52a657 100644 --- a/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs @@ -44,7 +44,7 @@ public override void UpdateLayout(DiagnosticBag diagnostics) protected override void Write(ElfWriter writer) { - for (int i = 0; i < Parent.Segments.Count; i++) + for (int i = 0; i < Parent!.Segments.Count; i++) { var header = Parent.Segments[i]; if (Parent.FileClass == ElfFileClass.Is32) diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs b/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs index 194d7af..686c4e7 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs @@ -50,7 +50,7 @@ public override ElfSectionType Type protected override void Read(ElfReader reader) { - if (Parent.FileClass == ElfFileClass.Is32) + if (Parent!.FileClass == ElfFileClass.Is32) { Read32(reader); } @@ -62,7 +62,7 @@ protected override void Read(ElfReader reader) protected override void Write(ElfWriter writer) { - if (Parent.FileClass == ElfFileClass.Is32) + if (Parent!.FileClass == ElfFileClass.Is32) { Write32(writer); } @@ -92,7 +92,7 @@ private void Read32(ElfReader reader) var offset = reader.Decode(rel.r_offset); var r_info = reader.Decode(rel.r_info); - var type = new ElfRelocationType(Parent.Arch, r_info & 0xFF); + var type = new ElfRelocationType(Parent!.Arch, r_info & 0xFF); var symbolIndex = r_info >> 8; var addend = reader.Decode(rel.r_addend); @@ -114,7 +114,7 @@ private void Read32(ElfReader reader) var offset = reader.Decode(rel.r_offset); var r_info = reader.Decode(rel.r_info); - var type = new ElfRelocationType(Parent.Arch, r_info & 0xFF); + var type = new ElfRelocationType(Parent!.Arch, r_info & 0xFF); var symbolIndex = r_info >> 8; var entry = new ElfRelocation(offset, type, symbolIndex, 0); @@ -140,7 +140,7 @@ private void Read64(ElfReader reader) var offset = reader.Decode(rel.r_offset); var r_info = reader.Decode(rel.r_info); - var type = new ElfRelocationType(Parent.Arch, (uint)(r_info & 0xFFFFFFFF)); + var type = new ElfRelocationType(Parent!.Arch, (uint)(r_info & 0xFFFFFFFF)); var symbolIndex = (uint)(r_info >> 32); var addend = reader.Decode(rel.r_addend); @@ -162,7 +162,7 @@ private void Read64(ElfReader reader) var offset = reader.Decode(rel.r_offset); var r_info = reader.Decode(rel.r_info); - var type = new ElfRelocationType(Parent.Arch, (uint)(r_info & 0xFFFFFFFF)); + var type = new ElfRelocationType(Parent!.Arch, (uint)(r_info & 0xFFFFFFFF)); var symbolIndex = (uint)(r_info >> 32); var entry = new ElfRelocation(offset, type, symbolIndex, 0); @@ -288,7 +288,7 @@ public override void Verify(DiagnosticBag diagnostics) diagnostics.Error(DiagnosticId.ELF_ERR_InvalidRelocationEntryAddend, $"Invalid relocation entry {i} in section [{Index}] `{nameof(ElfRelocationTable)}`. The addend != 0 while the section is not a `{ElfSectionType.RelocationAddends}`", this); } - if (entry.Type.Arch != Parent.Arch) + if (entry.Type.Arch != Parent!.Arch) { diagnostics.Error(DiagnosticId.ELF_ERR_InvalidRelocationEntryArch, $"Invalid Arch `{entry.Type.Arch}` for relocation entry {i} in section [{Index}] `{nameof(ElfRelocationTable)}`. The arch doesn't match the arch `{Parent.Arch}`", this); } diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs b/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs index 0b13a44..31ac3bb 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs @@ -25,7 +25,7 @@ public static void Relocate(this ElfRelocationTable relocTable, in ElfRelocation throw new InvalidOperationException($"Invalid ElfRelocationTable.Info section. Can only relocate a section that inherits from {nameof(ElfBinarySection)}."); } - Relocate(relocTable, relocTargetBinarySection.Stream, context); + Relocate(relocTable, relocTargetBinarySection.Stream!, context); } /// @@ -36,7 +36,7 @@ public static void Relocate(this ElfRelocationTable relocTable, Stream stream, i { if (stream == null) throw new ArgumentNullException(nameof(stream)); - switch (relocTable.Parent.Arch.Value) + switch (relocTable.Parent!.Arch.Value) { case ElfArch.X86_64: ApplyX86_64(relocTable, stream, context); @@ -54,13 +54,13 @@ public static void Relocate(this ElfRelocationTable relocTable, Stream stream, i private static void ApplyX86_64(this ElfRelocationTable relocTable, Stream stream, in ElfRelocationContext context) { if (stream == null) throw new ArgumentNullException(nameof(stream)); - bool isLsb = relocTable.Parent.Encoding == ElfEncoding.Lsb; + bool isLsb = relocTable.Parent!.Encoding == ElfEncoding.Lsb; var GOT = (long)context.GlobalObjectTableAddress; var B = (long)context.BaseAddress; var G = (long)context.GlobalObjectTableOffset; - var symbolTable = (ElfSymbolTable)relocTable.Link.Section; + var symbolTable = (ElfSymbolTable)relocTable.Link.Section!; foreach (var reloc in relocTable.Entries) { diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocationType.cs b/src/LibObjectFile/Elf/Sections/ElfRelocationType.cs index da37e22..a4c87d9 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationType.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationType.cs @@ -32,7 +32,7 @@ public bool Equals(ElfRelocationType other) return Arch.Equals(other.Arch) && Value == other.Value; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfRelocationType other && Equals(other); } @@ -55,7 +55,7 @@ public override int GetHashCode() return !left.Equals(right); } - public string Name => ToStringInternal(); + public string? Name => ToStringInternal(); public override string ToString() { diff --git a/src/LibObjectFile/Elf/Sections/ElfStringTable.cs b/src/LibObjectFile/Elf/Sections/ElfStringTable.cs index 807e42f..ccadc0f 100644 --- a/src/LibObjectFile/Elf/Sections/ElfStringTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfStringTable.cs @@ -77,9 +77,9 @@ protected override void Write(ElfWriter writer) writer.Stream.Write(_table.GetBuffer(), 0, (int)_table.Length); } - internal void ReserveString(string text) + internal void ReserveString(string? text) { - if (text is object && !_mapStringToIndex.ContainsKey(text) && !_reservedStrings.Contains(text)) + if (text is not null && !_mapStringToIndex.ContainsKey(text) && !_reservedStrings.Contains(text)) { _reservedStrings.Add(text); } @@ -94,7 +94,7 @@ internal void FlushReservedStrings() MultiKeySort(reservedStrings, 0); // Add the strings to string table - string lastText = null; + string? lastText = null; for (int i = 0; i < reservedStrings.Length; i++) { var text = reservedStrings[i]; @@ -229,7 +229,7 @@ private uint CreateIndex(string text) return index; } - public uint GetOrCreateIndex(string text) + public uint GetOrCreateIndex(string? text) { // Same as empty string if (text == null) return 0; @@ -275,7 +275,7 @@ public bool TryFind(uint index, out string text) if (_reservedStrings.Count > 0) FlushReservedStrings(); - if (_mapIndexToString.TryGetValue(index, out text)) + if (_mapIndexToString.TryGetValue(index, out text!)) { return true; } diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbol.cs b/src/LibObjectFile/Elf/Sections/ElfSymbol.cs index 1cf390b..b949970 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbol.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbol.cs @@ -59,7 +59,7 @@ public bool Equals(ElfSymbol other) return Value == other.Value && Size == other.Size && Type == other.Type && Bind == other.Bind && Visibility == other.Visibility && Section.Equals(other.Section) && Name == other.Name; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ElfSymbol other && Equals(other); } diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs index cd1c8f6..491db01 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs @@ -45,7 +45,7 @@ public override ElfSectionType Type protected override void Read(ElfReader reader) { - if (Parent.FileClass == ElfFileClass.Is32) + if (Parent!.FileClass == ElfFileClass.Is32) { Read32(reader); } @@ -57,7 +57,7 @@ protected override void Read(ElfReader reader) protected override void Write(ElfWriter writer) { - if (Parent.FileClass == ElfFileClass.Is32) + if (Parent!.FileClass == ElfFileClass.Is32) { Write32(writer); } @@ -136,7 +136,7 @@ private void Read64(ElfReader reader) private void Write32(ElfWriter writer) { - var stringTable = (ElfStringTable)Link.Section; + var stringTable = (ElfStringTable)Link.Section!; // Write all entries for (int i = 0; i < Entries.Count; i++) @@ -144,7 +144,7 @@ private void Write32(ElfWriter writer) var entry = Entries[i]; var sym = new ElfNative.Elf32_Sym(); - writer.Encode(out sym.st_name, (ushort)stringTable.GetOrCreateIndex(entry.Name)); + writer.Encode(out sym.st_name, (ushort)stringTable.GetOrCreateIndex(entry.Name!)); writer.Encode(out sym.st_value, (uint)entry.Value); writer.Encode(out sym.st_size, (uint)entry.Size); sym.st_info = (byte)(((byte) entry.Bind << 4) | (byte) entry.Type); @@ -158,14 +158,14 @@ private void Write32(ElfWriter writer) private void Write64(ElfWriter writer) { - var stringTable = (ElfStringTable)Link.Section; + var stringTable = (ElfStringTable)Link.Section!; for (int i = 0; i < Entries.Count; i++) { var entry = Entries[i]; var sym = new ElfNative.Elf64_Sym(); - writer.Encode(out sym.st_name, stringTable.GetOrCreateIndex(entry.Name)); + writer.Encode(out sym.st_name, stringTable.GetOrCreateIndex(entry.Name!)); writer.Encode(out sym.st_value, entry.Value); writer.Encode(out sym.st_size, entry.Size); sym.st_info = (byte)(((byte)entry.Bind << 4) | (byte)entry.Type); @@ -260,7 +260,7 @@ public override void Verify(DiagnosticBag diagnostics) if (needsSectionHeaderIndices) { bool foundSectionHeaderIndices = false; - foreach (ElfSection otherSection in Parent.Sections) + foreach (ElfSection otherSection in Parent!.Sections) { if (otherSection is ElfSymbolTableSectionHeaderIndices && otherSection.Link.Section == this) { diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs index 483c529..1b44a55 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs @@ -62,6 +62,11 @@ protected override void AfterRead(ElfReader reader) // Verify that the link is safe and configured as expected Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, reader.Diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable); + if (symbolTable is null) + { + return; + } + for (int i = 0; i < _entries.Count; i++) { var entry = _entries[i]; @@ -96,27 +101,34 @@ public override unsafe void UpdateLayout(DiagnosticBag diagnostics) Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable); int numberOfEntries = 0; - for (int i = 0; i < symbolTable.Entries.Count; i++) + + if (symbolTable is not null) { - if (symbolTable.Entries[i].Section.Section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) + for (int i = 0; i < symbolTable.Entries.Count; i++) { - numberOfEntries = i + 1; + if (symbolTable.Entries[i].Section.Section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) + { + numberOfEntries = i + 1; + } } } _entries.Capacity = numberOfEntries; _entries.Clear(); - for (int i = 0; i < numberOfEntries; i++) + if (symbolTable is not null) { - var section = symbolTable.Entries[i].Section.Section; - if (section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) - { - _entries.Add(section.SectionIndex); - } - else + for (int i = 0; i < numberOfEntries; i++) { - _entries.Add(0); + var section = symbolTable.Entries[i].Section.Section; + if (section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) + { + _entries.Add(section.SectionIndex); + } + else + { + _entries.Add(0); + } } } diff --git a/src/LibObjectFile/LibObjectFile.csproj b/src/LibObjectFile/LibObjectFile.csproj index 1778701..769948b 100644 --- a/src/LibObjectFile/LibObjectFile.csproj +++ b/src/LibObjectFile/LibObjectFile.csproj @@ -3,6 +3,7 @@ net8.0 true + enable diff --git a/src/LibObjectFile/ObjectFileNode.cs b/src/LibObjectFile/ObjectFileNode.cs index b355305..e309749 100644 --- a/src/LibObjectFile/ObjectFileNode.cs +++ b/src/LibObjectFile/ObjectFileNode.cs @@ -10,7 +10,7 @@ namespace LibObjectFile; public abstract class ObjectFileNode : ObjectFileNodeBase { /// - /// Updates the layout of this node. + /// Updates the size of this node. /// /// The diagnostics. public abstract void UpdateLayout(DiagnosticBag diagnostics); diff --git a/src/LibObjectFile/ObjectFileNodeBase.cs b/src/LibObjectFile/ObjectFileNodeBase.cs index ac981e5..62b714d 100644 --- a/src/LibObjectFile/ObjectFileNodeBase.cs +++ b/src/LibObjectFile/ObjectFileNodeBase.cs @@ -4,13 +4,14 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace LibObjectFile; public abstract class ObjectFileNodeBase { [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private ObjectFileNodeBase _parent; + private ObjectFileNodeBase? _parent; /// /// Gets or sets the offset of this section or segment in the parent . @@ -20,7 +21,7 @@ public abstract class ObjectFileNodeBase /// /// Gets the containing parent. /// - public ObjectFileNodeBase Parent + public ObjectFileNodeBase? Parent { get => _parent; @@ -70,7 +71,7 @@ public bool Contains(ulong offset) /// true if the either the offset or end of the part is within this segment or section range. public bool Contains(ObjectFileNodeBase node) { - if (node == null) throw new ArgumentNullException(nameof(node)); + ArgumentNullException.ThrowIfNull(node); return Contains((ulong)node.Offset) || node.Size != 0 && Contains((ulong)(node.Offset + node.Size - 1)); } @@ -91,25 +92,53 @@ public DiagnosticBag Verify() /// A DiagnosticBag instance to receive the diagnostics. public virtual void Verify(DiagnosticBag diagnostics) { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + ArgumentNullException.ThrowIfNull(diagnostics); } - protected static void AttachChild(TParent parent, T child, ref T field, bool allowNull) where T : ObjectFileNodeBase where TParent : ObjectFileNodeBase + protected static void AssignChild(TParent parent, T child, out T field) where T : ObjectFileNodeBase where TParent : ObjectFileNodeBase { if (parent == null) throw new ArgumentNullException(nameof(parent)); - if (!allowNull && child == null) throw new ArgumentNullException(nameof(child)); + if (child == null) throw new ArgumentNullException(nameof(child)); - if (field != null) + if (child?.Parent != null) throw new InvalidOperationException($"Cannot set the {child.GetType()} as it already belongs to another {child.Parent.GetType()} instance"); + field = child!; + + if (child != null) + { + child.Parent = parent; + } + } + + protected static void AttachChild(TParent parent, T child, ref T field) where T : ObjectFileNodeBase where TParent : ObjectFileNodeBase + { + if (parent == null) throw new ArgumentNullException(nameof(parent)); + field.Parent = null; + + if (child?.Parent != null) throw new InvalidOperationException($"Cannot set the {child.GetType()} as it already belongs to another {child.Parent.GetType()} instance"); + field = child!; + + if (child != null) + { + child.Parent = parent; + } + } + + protected static void AttachNullableChild(TParent parent, T? child, ref T? field) where T : ObjectFileNodeBase where TParent : ObjectFileNodeBase + { + if (parent == null) throw new ArgumentNullException(nameof(parent)); + + if (field is not null) { field.Parent = null; } if (child?.Parent != null) throw new InvalidOperationException($"Cannot set the {child.GetType()} as it already belongs to another {child.Parent.GetType()} instance"); - field = child; + field = child!; if (child != null) { child.Parent = parent; } } + } \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs index 3e6ff92..4973dcd 100644 --- a/src/LibObjectFile/ObjectFileReaderWriter.cs +++ b/src/LibObjectFile/ObjectFileReaderWriter.cs @@ -25,7 +25,7 @@ public abstract class ObjectFileReaderWriter protected ObjectFileReaderWriter(Stream stream, DiagnosticBag diagnostics) { - Stream = stream; + _stream = stream; Diagnostics = diagnostics; IsLittleEndian = true; } @@ -62,7 +62,7 @@ public ulong Length public bool IsLittleEndian { get; protected set; } - public TextWriter Log { get; set; } + public TextWriter? Log { get; set; } /// /// Reads from the and current position to the specified buffer. diff --git a/src/LibObjectFile/Utils/SliceStream.cs b/src/LibObjectFile/Utils/SliceStream.cs index cabdd6e..5f97836 100644 --- a/src/LibObjectFile/Utils/SliceStream.cs +++ b/src/LibObjectFile/Utils/SliceStream.cs @@ -81,7 +81,7 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation private void ThrowIfDisposed() { - ObjectDisposedException.ThrowIf(_baseStream == null, this); + ObjectDisposedException.ThrowIf(_baseStream == Stream.Null, this); } public override long Length @@ -151,7 +151,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); if (disposing) { - if (_baseStream != null) + if (_baseStream != Stream.Null) { try { @@ -161,7 +161,7 @@ protected override void Dispose(bool disposing) { // ignored } - _baseStream = null; + _baseStream = Stream.Null; } } } diff --git a/src/LibObjectFile/Utils/ThrowHelper.cs b/src/LibObjectFile/Utils/ThrowHelper.cs index 7382392..dd40aab 100644 --- a/src/LibObjectFile/Utils/ThrowHelper.cs +++ b/src/LibObjectFile/Utils/ThrowHelper.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; @@ -39,7 +40,7 @@ public static string ReadStringUTF8NullTerminated(this Stream stream) /// Reads a null terminated UTF8 string from the stream. /// /// true if the string was successfully read from the stream, false otherwise - public static bool TryReadStringUTF8NullTerminated(this Stream stream, out string text) + public static bool TryReadStringUTF8NullTerminated(this Stream stream, [NotNullWhen(true)] out string? text) { text = null; var buffer = ArrayPool.Shared.Rent((int)128); diff --git a/src/LibObjectFile/generated/LibObjectFile.Dwarf.generated.cs b/src/LibObjectFile/generated/LibObjectFile.Dwarf.generated.cs index f719106..5edce90 100644 --- a/src/LibObjectFile/generated/LibObjectFile.Dwarf.generated.cs +++ b/src/LibObjectFile/generated/LibObjectFile.Dwarf.generated.cs @@ -11,6 +11,10 @@ #pragma warning disable 1591 +#pragma warning disable CS8765 + +#nullable enable + namespace LibObjectFile.Dwarf { public static unsafe partial class DwarfNative @@ -3858,7 +3862,7 @@ public readonly partial struct DwarfAttributeKindEx /// public static readonly DwarfAttributeKindEx APPLEClosure = new DwarfAttributeKindEx(DwarfNative.DW_AT_APPLE_closure); - private string ToStringInternal() + private string? ToStringInternal() { switch ((ushort)Value) { @@ -4815,7 +4819,7 @@ public readonly partial struct DwarfAttributeFormEx /// public static readonly DwarfAttributeFormEx GNUStrpAlt = new DwarfAttributeFormEx(DwarfNative.DW_FORM_GNU_strp_alt); - private string ToStringInternal() + private string? ToStringInternal() { switch ((ushort)Value) { @@ -5375,7 +5379,7 @@ public readonly partial struct DwarfTagEx /// public static readonly DwarfTagEx SUNHi = new DwarfTagEx(DwarfNative.DW_TAG_SUN_hi); - private string ToStringInternal() + private string? ToStringInternal() { switch ((ushort)Value) { @@ -6209,7 +6213,7 @@ public readonly partial struct DwarfOperationKindEx /// public static readonly DwarfOperationKindEx PGIOmpThreadNum = new DwarfOperationKindEx(DwarfNative.DW_OP_PGI_omp_thread_num); - private string ToStringInternal() + private string? ToStringInternal() { switch ((byte)Value) { @@ -6958,7 +6962,7 @@ public readonly partial struct DwarfLanguageKindEx /// public static readonly DwarfLanguageKindEx SUNAssembler = new DwarfLanguageKindEx(DwarfNative.DW_LANG_SUN_Assembler); - private string ToStringInternal() + private string? ToStringInternal() { switch ((ushort)Value) { @@ -7141,7 +7145,7 @@ public readonly partial struct DwarfCallingConventionEx /// public static readonly DwarfCallingConventionEx ALTIUMHugeUserStack = new DwarfCallingConventionEx(DwarfNative.DW_CC_ALTIUM_huge_user_stack); - private string ToStringInternal() + private string? ToStringInternal() { switch ((byte)Value) { @@ -7218,7 +7222,7 @@ public readonly partial struct DwarfUnitKindEx /// public static readonly DwarfUnitKindEx SplitType = new DwarfUnitKindEx(DwarfNative.DW_UT_split_type); - private string ToStringInternal() + private string? ToStringInternal() { switch ((byte)Value) { @@ -7452,7 +7456,7 @@ public DwarfAccessibility? Accessibility } } - public string Name + public string? Name { get { @@ -7464,7 +7468,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -7568,7 +7572,7 @@ public DwarfConstant? ByteSize } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -7592,7 +7596,7 @@ public bool? Declaration } } - public string Name + public string? Name { get { @@ -7604,7 +7608,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -7640,7 +7644,7 @@ public DwarfConstant? Rank } } - public DwarfDIE Specification + public DwarfDIE? Specification { get { @@ -7664,7 +7668,7 @@ public DwarfConstant? StartScope } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -7708,7 +7712,7 @@ public DwarfConstant? Alignment } } - public string Name + public string? Name { get { @@ -7720,7 +7724,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -7732,7 +7736,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -7836,7 +7840,7 @@ public DwarfConstant? DataBitOffset } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -7908,7 +7912,7 @@ public DwarfConstant? Endianity } } - public string Name + public string? Name { get { @@ -7920,7 +7924,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -7932,7 +7936,7 @@ public string Description } } - public string PictureString + public string? PictureString { get { @@ -7944,7 +7948,7 @@ public string PictureString } } - public DwarfDIE Small + public DwarfDIE? Small { get { @@ -8000,7 +8004,7 @@ public DwarfConstant? CallLine } } - public DwarfExpression CallOrigin + public DwarfExpression? CallOrigin { get { @@ -8048,7 +8052,7 @@ public bool? CallTailCall } } - public DwarfExpression CallTarget + public DwarfExpression? CallTarget { get { @@ -8060,7 +8064,7 @@ public DwarfExpression CallTarget } } - public DwarfExpression CallTargetClobbered + public DwarfExpression? CallTargetClobbered { get { @@ -8072,7 +8076,7 @@ public DwarfExpression CallTargetClobbered } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -8092,7 +8096,7 @@ public DwarfDIECallSiteParameter() this.Tag = (DwarfTag)DwarfNative.DW_TAG_call_site_parameter; } - public DwarfExpression CallDataLocation + public DwarfExpression? CallDataLocation { get { @@ -8104,7 +8108,7 @@ public DwarfExpression CallDataLocation } } - public DwarfExpression CallDataValue + public DwarfExpression? CallDataValue { get { @@ -8116,7 +8120,7 @@ public DwarfExpression CallDataValue } } - public DwarfDIE CallParameter + public DwarfDIE? CallParameter { get { @@ -8128,7 +8132,7 @@ public DwarfDIE CallParameter } } - public DwarfExpression CallValue + public DwarfExpression? CallValue { get { @@ -8152,7 +8156,7 @@ public DwarfLocation? Location } } - public string Name + public string? Name { get { @@ -8164,7 +8168,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -8176,7 +8180,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -8348,7 +8352,7 @@ public DwarfCallingConvention? CallingConvention } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -8384,7 +8388,7 @@ public bool? ExportSymbols } } - public string Name + public string? Name { get { @@ -8396,7 +8400,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -8408,7 +8412,7 @@ public string Description } } - public DwarfDIE Signature + public DwarfDIE? Signature { get { @@ -8420,7 +8424,7 @@ public DwarfDIE Signature } } - public DwarfDIE Specification + public DwarfDIE? Specification { get { @@ -8500,7 +8504,7 @@ public DwarfConstant? ByteSize } } - public string Name + public string? Name { get { @@ -8512,7 +8516,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -8524,7 +8528,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -8556,7 +8560,7 @@ public bool? Declaration } } - public string LinkageName + public string? LinkageName { get { @@ -8580,7 +8584,7 @@ public DwarfLocation? Location } } - public string Name + public string? Name { get { @@ -8592,7 +8596,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -8636,7 +8640,7 @@ public DwarfDIECommonInclusion() this.Tag = (DwarfTag)DwarfNative.DW_TAG_common_inclusion; } - public DwarfDIE CommonReference + public DwarfDIE? CommonReference { get { @@ -8692,7 +8696,7 @@ public ulong? AddrBase } } - public DwarfDIE BaseTypes + public DwarfDIE? BaseTypes { get { @@ -8704,7 +8708,7 @@ public DwarfDIE BaseTypes } } - public string CompDir + public string? CompDir { get { @@ -8800,7 +8804,7 @@ public bool? MainSubprogram } } - public string Name + public string? Name { get { @@ -8812,7 +8816,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -8824,7 +8828,7 @@ public string Description } } - public string Producer + public string? Producer { get { @@ -8872,7 +8876,7 @@ public DwarfLocation? Segment } } - public DwarfLineProgramTable StmtList + public DwarfLineProgramTable? StmtList { get { @@ -8916,7 +8920,7 @@ public DwarfDIECondition() this.Tag = (DwarfTag)DwarfNative.DW_TAG_condition; } - public string Name + public string? Name { get { @@ -8928,7 +8932,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -8960,7 +8964,7 @@ public DwarfConstant? Alignment } } - public string Name + public string? Name { get { @@ -8972,7 +8976,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -8984,7 +8988,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -9064,7 +9068,7 @@ public bool? External } } - public string LinkageName + public string? LinkageName { get { @@ -9076,7 +9080,7 @@ public string LinkageName } } - public string Name + public string? Name { get { @@ -9088,7 +9092,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -9112,7 +9116,7 @@ public DwarfConstant? StartScope } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -9200,7 +9204,7 @@ public DwarfConstant? Associated } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -9212,7 +9216,7 @@ public DwarfExpression DataLocation } } - public string Name + public string? Name { get { @@ -9224,7 +9228,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -9236,7 +9240,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -9280,7 +9284,7 @@ public DwarfLocation? FrameBase } } - public string LinkageName + public string? LinkageName { get { @@ -9304,7 +9308,7 @@ public ulong? LowPC } } - public string Name + public string? Name { get { @@ -9316,7 +9320,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -9364,7 +9368,7 @@ public DwarfLocation? StaticLink } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -9480,7 +9484,7 @@ public DwarfConstant? ByteStride } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -9516,7 +9520,7 @@ public bool? EnumClass } } - public string Name + public string? Name { get { @@ -9528,7 +9532,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -9540,7 +9544,7 @@ public string Description } } - public DwarfDIE Signature + public DwarfDIE? Signature { get { @@ -9552,7 +9556,7 @@ public DwarfDIE Signature } } - public DwarfDIE Specification + public DwarfDIE? Specification { get { @@ -9576,7 +9580,7 @@ public DwarfConstant? StartScope } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -9620,7 +9624,7 @@ public DwarfConstant? ConstValue } } - public string Name + public string? Name { get { @@ -9632,7 +9636,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -9712,7 +9716,7 @@ public DwarfConstant? ByteSize } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -9724,7 +9728,7 @@ public DwarfExpression DataLocation } } - public string Name + public string? Name { get { @@ -9736,7 +9740,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -9760,7 +9764,7 @@ public DwarfConstant? StartScope } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -9816,7 +9820,7 @@ public DwarfConstant? ConstValue } } - public DwarfDIE DefaultValue + public DwarfDIE? DefaultValue { get { @@ -9864,7 +9868,7 @@ public DwarfLocation? Location } } - public string Name + public string? Name { get { @@ -9876,7 +9880,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -9900,7 +9904,7 @@ public DwarfLocation? Segment } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -9932,7 +9936,7 @@ public DwarfDIEFriend() this.Tag = (DwarfTag)DwarfNative.DW_TAG_friend; } - public DwarfDIE Friend + public DwarfDIE? Friend { get { @@ -10060,7 +10064,7 @@ public DwarfConstant? Count } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -10096,7 +10100,7 @@ public DwarfConstant? LowerBound } } - public string Name + public string? Name { get { @@ -10108,7 +10112,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -10132,7 +10136,7 @@ public bool? ThreadsScaled } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -10176,7 +10180,7 @@ public DwarfDIEImmutableType() this.Tag = (DwarfTag)DwarfNative.DW_TAG_immutable_type; } - public string Name + public string? Name { get { @@ -10188,7 +10192,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -10200,7 +10204,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -10232,7 +10236,7 @@ public DwarfAccessibility? Accessibility } } - public DwarfDIE Import + public DwarfDIE? Import { get { @@ -10244,7 +10248,7 @@ public DwarfDIE Import } } - public string Name + public string? Name { get { @@ -10256,7 +10260,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -10288,7 +10292,7 @@ public DwarfDIEImportedModule() this.Tag = (DwarfTag)DwarfNative.DW_TAG_imported_module; } - public DwarfDIE Import + public DwarfDIE? Import { get { @@ -10320,7 +10324,7 @@ public DwarfDIEImportedUnit() this.Tag = (DwarfTag)DwarfNative.DW_TAG_imported_unit; } - public DwarfDIE Import + public DwarfDIE? Import { get { @@ -10364,7 +10368,7 @@ public DwarfLocation? DataMemberLocation } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -10528,7 +10532,7 @@ public DwarfConstant? StartScope } } - public object Trampoline + public object? Trampoline { get { @@ -10572,7 +10576,7 @@ public DwarfConstant? Alignment } } - public string Name + public string? Name { get { @@ -10584,7 +10588,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -10596,7 +10600,7 @@ public string Description } } - public DwarfDIE Signature + public DwarfDIE? Signature { get { @@ -10640,7 +10644,7 @@ public ulong? LowPC } } - public string Name + public string? Name { get { @@ -10652,7 +10656,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -10732,7 +10736,7 @@ public ulong? LowPC } } - public string Name + public string? Name { get { @@ -10744,7 +10748,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -10884,7 +10888,7 @@ public bool? Mutable } } - public string Name + public string? Name { get { @@ -10896,7 +10900,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -10908,7 +10912,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -11000,7 +11004,7 @@ public ulong? LowPC } } - public string Name + public string? Name { get { @@ -11012,7 +11016,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -11024,7 +11028,7 @@ public string Description } } - public DwarfDIE Priority + public DwarfDIE? Priority { get { @@ -11060,7 +11064,7 @@ public DwarfLocation? Segment } } - public DwarfDIE Specification + public DwarfDIE? Specification { get { @@ -11116,7 +11120,7 @@ public bool? Declaration } } - public string Name + public string? Name { get { @@ -11128,7 +11132,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -11160,7 +11164,7 @@ public DwarfDIENamelistItem() this.Tag = (DwarfTag)DwarfNative.DW_TAG_namelist_item; } - public DwarfDIE NamelistItem + public DwarfDIE? NamelistItem { get { @@ -11192,7 +11196,7 @@ public bool? ExportSymbols } } - public DwarfDIE Extension + public DwarfDIE? Extension { get { @@ -11204,7 +11208,7 @@ public DwarfDIE Extension } } - public string Name + public string? Name { get { @@ -11216,7 +11220,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -11260,7 +11264,7 @@ public DwarfConstant? Alignment } } - public string Name + public string? Name { get { @@ -11272,7 +11276,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -11284,7 +11288,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -11316,7 +11320,7 @@ public ulong? AddrBase } } - public DwarfDIE BaseTypes + public DwarfDIE? BaseTypes { get { @@ -11328,7 +11332,7 @@ public DwarfDIE BaseTypes } } - public string CompDir + public string? CompDir { get { @@ -11340,7 +11344,7 @@ public string CompDir } } - public string DwoName + public string? DwoName { get { @@ -11436,7 +11440,7 @@ public bool? MainSubprogram } } - public string Name + public string? Name { get { @@ -11448,7 +11452,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -11460,7 +11464,7 @@ public string Description } } - public string Producer + public string? Producer { get { @@ -11508,7 +11512,7 @@ public DwarfLocation? Segment } } - public DwarfLineProgramTable StmtList + public DwarfLineProgramTable? StmtList { get { @@ -11600,7 +11604,7 @@ public DwarfConstant? ByteSize } } - public string Name + public string? Name { get { @@ -11612,7 +11616,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -11624,7 +11628,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -11692,7 +11696,7 @@ public DwarfConstant? Associated } } - public DwarfDIE ContainingType + public DwarfDIE? ContainingType { get { @@ -11704,7 +11708,7 @@ public DwarfDIE ContainingType } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -11728,7 +11732,7 @@ public bool? Declaration } } - public string Name + public string? Name { get { @@ -11740,7 +11744,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -11752,7 +11756,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -11844,7 +11848,7 @@ public DwarfConstant? ByteSize } } - public string Name + public string? Name { get { @@ -11856,7 +11860,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -11868,7 +11872,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -11900,7 +11904,7 @@ public DwarfConstant? Alignment } } - public string Name + public string? Name { get { @@ -11912,7 +11916,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -11924,7 +11928,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -11992,7 +11996,7 @@ public DwarfConstant? ByteSize } } - public string Name + public string? Name { get { @@ -12004,7 +12008,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -12016,7 +12020,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -12108,7 +12112,7 @@ public DwarfConstant? ByteSize } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -12132,7 +12136,7 @@ public bool? Declaration } } - public string Name + public string? Name { get { @@ -12144,7 +12148,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -12168,7 +12172,7 @@ public DwarfConstant? StartScope } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -12224,7 +12228,7 @@ public DwarfConstant? Alignment } } - public string Name + public string? Name { get { @@ -12236,7 +12240,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -12248,7 +12252,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -12280,7 +12284,7 @@ public ulong? AddrBase } } - public string CompDir + public string? CompDir { get { @@ -12292,7 +12296,7 @@ public string CompDir } } - public string DwoName + public string? DwoName { get { @@ -12352,7 +12356,7 @@ public ulong? RnglistsBase } } - public DwarfLineProgramTable StmtList + public DwarfLineProgramTable? StmtList { get { @@ -12468,7 +12472,7 @@ public DwarfConstant? ByteSize } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -12492,7 +12496,7 @@ public bool? Declaration } } - public string Name + public string? Name { get { @@ -12504,7 +12508,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -12668,7 +12672,7 @@ public DwarfCallingConvention? CallingConvention } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -12704,7 +12708,7 @@ public bool? ExportSymbols } } - public string Name + public string? Name { get { @@ -12716,7 +12720,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -12728,7 +12732,7 @@ public string Description } } - public DwarfDIE Signature + public DwarfDIE? Signature { get { @@ -12740,7 +12744,7 @@ public DwarfDIE Signature } } - public DwarfDIE Specification + public DwarfDIE? Specification { get { @@ -12964,7 +12968,7 @@ public DwarfInlineKind? Inline } } - public string LinkageName + public string? LinkageName { get { @@ -13000,7 +13004,7 @@ public bool? MainSubprogram } } - public string Name + public string? Name { get { @@ -13012,7 +13016,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -13036,7 +13040,7 @@ public bool? Noreturn } } - public DwarfDIE ObjectPointer + public DwarfDIE? ObjectPointer { get { @@ -13144,7 +13148,7 @@ public DwarfLocation? Segment } } - public DwarfDIE Specification + public DwarfDIE? Specification { get { @@ -13180,7 +13184,7 @@ public DwarfLocation? StaticLink } } - public object Trampoline + public object? Trampoline { get { @@ -13192,7 +13196,7 @@ public object Trampoline } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -13356,7 +13360,7 @@ public DwarfConstant? Count } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -13392,7 +13396,7 @@ public DwarfConstant? LowerBound } } - public string Name + public string? Name { get { @@ -13404,7 +13408,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -13428,7 +13432,7 @@ public bool? ThreadsScaled } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -13532,7 +13536,7 @@ public DwarfConstant? Associated } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -13556,7 +13560,7 @@ public bool? Declaration } } - public string Name + public string? Name { get { @@ -13568,7 +13572,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -13628,7 +13632,7 @@ public DwarfConstant? StartScope } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -13696,7 +13700,7 @@ public DwarfConstant? Associated } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -13720,7 +13724,7 @@ public bool? Declaration } } - public string Name + public string? Name { get { @@ -13732,7 +13736,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -13744,7 +13748,7 @@ public string Description } } - public DwarfDIE Signature + public DwarfDIE? Signature { get { @@ -13768,7 +13772,7 @@ public DwarfConstant? StartScope } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -13800,7 +13804,7 @@ public DwarfDIETemplateTypeParameter() this.Tag = (DwarfTag)DwarfNative.DW_TAG_template_type_parameter; } - public DwarfDIE DefaultValue + public DwarfDIE? DefaultValue { get { @@ -13812,7 +13816,7 @@ public DwarfDIE DefaultValue } } - public string Name + public string? Name { get { @@ -13824,7 +13828,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -13836,7 +13840,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -13868,7 +13872,7 @@ public DwarfConstant? ConstValue } } - public DwarfDIE DefaultValue + public DwarfDIE? DefaultValue { get { @@ -13880,7 +13884,7 @@ public DwarfDIE DefaultValue } } - public string Name + public string? Name { get { @@ -13892,7 +13896,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -13904,7 +13908,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -13960,7 +13964,7 @@ public DwarfConstant? Associated } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -13972,7 +13976,7 @@ public DwarfExpression DataLocation } } - public string Name + public string? Name { get { @@ -13984,7 +13988,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -13996,7 +14000,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -14132,7 +14136,7 @@ public DwarfConstant? Associated } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -14156,7 +14160,7 @@ public bool? Declaration } } - public string Name + public string? Name { get { @@ -14168,7 +14172,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -14192,7 +14196,7 @@ public DwarfConstant? StartScope } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -14236,7 +14240,7 @@ public DwarfLanguageKind? Language } } - public DwarfLineProgramTable StmtList + public DwarfLineProgramTable? StmtList { get { @@ -14364,7 +14368,7 @@ public DwarfCallingConvention? CallingConvention } } - public DwarfExpression DataLocation + public DwarfExpression? DataLocation { get { @@ -14400,7 +14404,7 @@ public bool? ExportSymbols } } - public string Name + public string? Name { get { @@ -14412,7 +14416,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -14424,7 +14428,7 @@ public string Description } } - public DwarfDIE Signature + public DwarfDIE? Signature { get { @@ -14436,7 +14440,7 @@ public DwarfDIE Signature } } - public DwarfDIE Specification + public DwarfDIE? Specification { get { @@ -14500,7 +14504,7 @@ public DwarfDIEUnspecifiedType() this.Tag = (DwarfTag)DwarfNative.DW_TAG_unspecified_type; } - public string Name + public string? Name { get { @@ -14512,7 +14516,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -14628,7 +14632,7 @@ public bool? External } } - public string LinkageName + public string? LinkageName { get { @@ -14652,7 +14656,7 @@ public DwarfLocation? Location } } - public string Name + public string? Name { get { @@ -14664,7 +14668,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -14688,7 +14692,7 @@ public DwarfLocation? Segment } } - public DwarfDIE Specification + public DwarfDIE? Specification { get { @@ -14712,7 +14716,7 @@ public DwarfConstant? StartScope } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -14824,7 +14828,7 @@ public bool? Declaration } } - public DwarfDIE Discr + public DwarfDIE? Discr { get { @@ -14836,7 +14840,7 @@ public DwarfDIE Discr } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -14856,7 +14860,7 @@ public DwarfDIEVolatileType() this.Tag = (DwarfTag)DwarfNative.DW_TAG_volatile_type; } - public string Name + public string? Name { get { @@ -14868,7 +14872,7 @@ public string Name } } - public string Description + public string? Description { get { @@ -14880,7 +14884,7 @@ public string Description } } - public DwarfDIE Type + public DwarfDIE? Type { get { @@ -15008,7 +15012,7 @@ public DwarfLocation? Segment } } - public DwarfDIE Type + public DwarfDIE? Type { get { diff --git a/src/LibObjectFile/generated/LibObjectFile.Elf.generated.cs b/src/LibObjectFile/generated/LibObjectFile.Elf.generated.cs index eb04842..647b1ab 100644 --- a/src/LibObjectFile/generated/LibObjectFile.Elf.generated.cs +++ b/src/LibObjectFile/generated/LibObjectFile.Elf.generated.cs @@ -11,6 +11,10 @@ #pragma warning disable 1591 +#pragma warning disable CS8765 + +#nullable enable + namespace LibObjectFile.Elf { using System.Runtime.InteropServices; @@ -11830,7 +11834,7 @@ public readonly partial struct ElfArchEx public static readonly ElfArchEx ALPHA = new ElfArchEx(ElfNative.EM_ALPHA); - private string ToStringInternal() + private string? ToStringInternal() { switch ((ushort)Value) { @@ -12164,7 +12168,7 @@ public readonly partial struct ElfOSABIEx /// public static readonly ElfOSABIEx STANDALONE = new ElfOSABIEx(ElfNative.ELFOSABI_STANDALONE); - private string ToStringInternal() + private string? ToStringInternal() { switch ((byte)Value) { @@ -13859,7 +13863,7 @@ public readonly partial struct ElfRelocationType /// public static readonly ElfRelocationType R_X86_64_RELATIVE64 = new ElfRelocationType(ElfArch.X86_64, ElfNative.R_X86_64_RELATIVE64); - private string ToStringInternal() + private string? ToStringInternal() { switch (((ulong)Value << 16) | (ulong)Arch.Value) { @@ -14410,7 +14414,7 @@ public readonly partial struct ElfNoteTypeEx public static readonly ElfNoteTypeEx GNU_GOLD_VERSION = new ElfNoteTypeEx(ElfNative.NT_GNU_GOLD_VERSION); - private string ToStringInternal() + private string? ToStringInternal() { switch ((uint)Value) { From cbd3b4305b08954723be785bce470fc9e5d3e864 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sat, 14 Sep 2024 18:11:39 +0200 Subject: [PATCH 03/87] Change sealed --- src/LibObjectFile/Ar/ArLongNamesTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LibObjectFile/Ar/ArLongNamesTable.cs b/src/LibObjectFile/Ar/ArLongNamesTable.cs index 0d64b32..f763971 100644 --- a/src/LibObjectFile/Ar/ArLongNamesTable.cs +++ b/src/LibObjectFile/Ar/ArLongNamesTable.cs @@ -12,7 +12,7 @@ namespace LibObjectFile.Ar /// /// Internal class used for loading long file names for GNU `ar` and Windows `lib` archives. /// - internal class ArLongNamesTable : ArFile + internal sealed class ArLongNamesTable : ArFile { public const string DefaultName = "//"; From 924f8ba11226dbbceab16feb440c4b0e364c1b45 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 15 Sep 2024 10:48:53 +0200 Subject: [PATCH 04/87] Improve SliceStream --- src/LibObjectFile/Utils/SliceStream.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/LibObjectFile/Utils/SliceStream.cs b/src/LibObjectFile/Utils/SliceStream.cs index 5f97836..b70ad5c 100644 --- a/src/LibObjectFile/Utils/SliceStream.cs +++ b/src/LibObjectFile/Utils/SliceStream.cs @@ -111,6 +111,12 @@ public override long Position } public override long Seek(long offset, SeekOrigin origin) { + ThrowIfDisposed(); + if (!CanSeek) + { + throw new NotSupportedException("This stream doesn't support seeking"); + } + long newPosition = _localPosition; switch (origin) { @@ -139,7 +145,8 @@ public override long Seek(long offset, SeekOrigin origin) public override void SetLength(long value) { - throw new NotSupportedException(); + ThrowIfDisposed(); + throw new NotSupportedException("This stream does not support setting the length"); } public override void Flush() From f0f595705120c2e60a2b07cfada6d2064394aede Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 15 Sep 2024 11:27:27 +0200 Subject: [PATCH 05/87] Rename SliceStream to SubStream --- src/LibObjectFile/Ar/ArElfFile.cs | 2 +- src/LibObjectFile/ObjectFileReaderWriter.cs | 25 +++++++++++-------- .../Utils/{SliceStream.cs => SubStream.cs} | 4 +-- 3 files changed, 18 insertions(+), 13 deletions(-) rename src/LibObjectFile/Utils/{SliceStream.cs => SubStream.cs} (98%) diff --git a/src/LibObjectFile/Ar/ArElfFile.cs b/src/LibObjectFile/Ar/ArElfFile.cs index 5a178bb..9b6e726 100644 --- a/src/LibObjectFile/Ar/ArElfFile.cs +++ b/src/LibObjectFile/Ar/ArElfFile.cs @@ -31,7 +31,7 @@ protected override void Read(ArArchiveFileReader reader) { var startPosition = reader.Stream.Position; var endPosition = startPosition + (long) Size; - ElfObjectFile = ElfObjectFile.Read(new SliceStream(reader.Stream, reader.Stream.Position, (long)Size)); + ElfObjectFile = ElfObjectFile.Read(new SubStream(reader.Stream, reader.Stream.Position, (long)Size)); reader.Stream.Position = endPosition; } diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs index 4973dcd..4571a9a 100644 --- a/src/LibObjectFile/ObjectFileReaderWriter.cs +++ b/src/LibObjectFile/ObjectFileReaderWriter.cs @@ -70,11 +70,10 @@ public ulong Length /// The buffer to receive the content of the read. /// The offset into the buffer. /// The number of bytes to write from the buffer. - public int Read(byte[] buffer, int offset, int count) - { - return Stream.Read(buffer, offset, count); - } - + public int Read(byte[] buffer, int offset, int count) => Stream.Read(buffer, offset, count); + + public int Read(Span buffer) => Stream.Read(buffer); + /// /// Reads a null terminated UTF8 string from the stream. /// @@ -210,17 +209,17 @@ public unsafe bool TryReadData(int sizeToRead, out T data) where T : unmanage /// /// Reads from the current bytes and return the data as - /// a if is false otherwise as a + /// a if is false otherwise as a /// . /// /// Size of the data to read. - /// A if is false otherwise as a + /// A if is false otherwise as a /// . public Stream ReadAsStream(ulong size) { if (IsReadOnly) { - var stream = ReadAsSliceStream(size); + var stream = ReadAsSubStream(size); Stream.Position += stream.Length; return stream; } @@ -239,6 +238,12 @@ public void Write(byte[] buffer, int offset, int count) Stream.Write(buffer, offset, count); } + /// + /// Writes to the and current position from the specified buffer. + /// + /// The buffer to write + public void Write(ReadOnlySpan buffer) => Stream.Write(buffer); + /// /// Writes an element of type to the stream. /// @@ -286,7 +291,7 @@ public void Write(Stream inputStream, ulong size = 0, int bufferSize = 4096) } } - private SliceStream ReadAsSliceStream(ulong size) + private SubStream ReadAsSubStream(ulong size) { var position = Stream.Position; if (position + (long)size > Stream.Length) @@ -304,7 +309,7 @@ private SliceStream ReadAsSliceStream(ulong size) } } - return new SliceStream(Stream, position, (long)size); + return new SubStream(Stream, position, (long)size); } private MemoryStream ReadAsMemoryStream(ulong size) diff --git a/src/LibObjectFile/Utils/SliceStream.cs b/src/LibObjectFile/Utils/SubStream.cs similarity index 98% rename from src/LibObjectFile/Utils/SliceStream.cs rename to src/LibObjectFile/Utils/SubStream.cs index b70ad5c..b4aaed1 100644 --- a/src/LibObjectFile/Utils/SliceStream.cs +++ b/src/LibObjectFile/Utils/SubStream.cs @@ -13,14 +13,14 @@ namespace LibObjectFile.Utils; /// /// Defines a stream as a slice of another existing stream. /// -public class SliceStream : Stream +public class SubStream : Stream { private Stream _baseStream; private readonly long _length; private readonly long _basePosition; private long _localPosition; - public SliceStream(Stream baseStream, long position, long length) + public SubStream(Stream baseStream, long position, long length) { if (baseStream == null) throw new ArgumentNullException(nameof(baseStream)); if (!baseStream.CanSeek) throw new ArgumentException("Invalid base stream that can't be seek."); From 2892df6f2b19f0f12e34bca4c98a92600951f1db Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 15 Sep 2024 11:27:45 +0200 Subject: [PATCH 06/87] Add initial files for PE --- src/LibObjectFile/DiagnosticId.cs | 9 + src/LibObjectFile/PE/ImageCoffHeader.cs | 51 +++ src/LibObjectFile/PE/ImageDataDirectory.cs | 25 ++ .../PE/ImageDataDirectoryArray.cs | 27 ++ .../PE/ImageDataDirectoryKind.cs | 72 ++++ src/LibObjectFile/PE/ImageDosHeader.cs | 110 ++++++ src/LibObjectFile/PE/ImageDosMagic.cs | 25 ++ src/LibObjectFile/PE/ImageOptionalHeader.cs | 310 +++++++++++++++++ .../PE/ImageOptionalHeaderMagic.cs | 18 + src/LibObjectFile/PE/ImagePESignature.cs | 16 + .../PE/Internal/RawImageOptionalHeader32.cs | 20 ++ .../PE/Internal/RawImageOptionalHeader64.cs | 19 ++ .../Internal/RawImageOptionalHeaderBase32.cs | 24 ++ .../Internal/RawImageOptionalHeaderBase64.cs | 19 ++ .../RawImageOptionalHeaderCommonPart1.cs | 56 +++ .../RawImageOptionalHeaderCommonPart2.cs | 83 +++++ .../RawImageOptionalHeaderCommonPart3.cs | 28 ++ .../Internal/RawImageOptionalHeaderSize32.cs | 33 ++ .../Internal/RawImageOptionalHeaderSize64.cs | 33 ++ .../PE/Internal/RawImageSectionHeader.cs | 99 ++++++ src/LibObjectFile/PE/PEFile.Read.cs | 227 +++++++++++++ src/LibObjectFile/PE/PEFile.cs | 156 +++++++++ src/LibObjectFile/PE/PEImageReader.cs | 22 ++ src/LibObjectFile/PE/PEImageReaderOptions.cs | 12 + src/LibObjectFile/PE/PEObject.cs | 9 + src/LibObjectFile/PE/PESection.cs | 144 ++++++++ src/LibObjectFile/PE/PESectionData.cs | 318 ++++++++++++++++++ src/LibObjectFile/PE/PESectionName.cs | 81 +++++ 28 files changed, 2046 insertions(+) create mode 100644 src/LibObjectFile/PE/ImageCoffHeader.cs create mode 100644 src/LibObjectFile/PE/ImageDataDirectory.cs create mode 100644 src/LibObjectFile/PE/ImageDataDirectoryArray.cs create mode 100644 src/LibObjectFile/PE/ImageDataDirectoryKind.cs create mode 100644 src/LibObjectFile/PE/ImageDosHeader.cs create mode 100644 src/LibObjectFile/PE/ImageDosMagic.cs create mode 100644 src/LibObjectFile/PE/ImageOptionalHeader.cs create mode 100644 src/LibObjectFile/PE/ImageOptionalHeaderMagic.cs create mode 100644 src/LibObjectFile/PE/ImagePESignature.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase32.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase64.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart2.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart3.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageOptionalHeaderSize32.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageOptionalHeaderSize64.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs create mode 100644 src/LibObjectFile/PE/PEFile.Read.cs create mode 100644 src/LibObjectFile/PE/PEFile.cs create mode 100644 src/LibObjectFile/PE/PEImageReader.cs create mode 100644 src/LibObjectFile/PE/PEImageReaderOptions.cs create mode 100644 src/LibObjectFile/PE/PEObject.cs create mode 100644 src/LibObjectFile/PE/PESection.cs create mode 100644 src/LibObjectFile/PE/PESectionData.cs create mode 100644 src/LibObjectFile/PE/PESectionName.cs diff --git a/src/LibObjectFile/DiagnosticId.cs b/src/LibObjectFile/DiagnosticId.cs index 170dd84..7c83b7e 100644 --- a/src/LibObjectFile/DiagnosticId.cs +++ b/src/LibObjectFile/DiagnosticId.cs @@ -114,5 +114,14 @@ public enum DiagnosticId DWARF_WRN_InvalidExtendedOpCodeLength = 2019, DWARF_ERR_InvalidParentForLocationList = 2020, + // PE errors + PE_ERR_InvalidDosHeaderSize = 3000, + PE_ERR_InvalidDosHeaderMagic = 3001, + PE_ERR_InvalidDosStubSize = 3002, + PE_ERR_InvalidPESignature = 3003, + PE_ERR_InvalidCoffHeaderSize = 3004, + PE_ERR_InvalidOptionalHeaderSize = 3005, + PE_ERR_InvalidOptionalHeaderMagic = 3006, + PE_ERR_InvalidSectionHeadersSize = 3007, } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImageCoffHeader.cs b/src/LibObjectFile/PE/ImageCoffHeader.cs new file mode 100644 index 0000000..e0c03d5 --- /dev/null +++ b/src/LibObjectFile/PE/ImageCoffHeader.cs @@ -0,0 +1,51 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +#pragma warning disable CS0649 + +/// +/// Represents the COFF (Common Object File Format) header in a Portable Executable (PE) file. +/// +[StructLayout(LayoutKind.Sequential, Pack = 4)] +public struct ImageCoffHeader +{ + /// + /// The machine type that the file is intended for. + /// + public System.Reflection.PortableExecutable.Machine Machine; + + /// + /// The number of sections in the file. + /// + public ushort NumberOfSections; + + /// + /// The low 32 bits of the time stamp indicating when the file was created. + /// + public uint TimeDateStamp; + + /// + /// The file pointer to the COFF symbol table. This is zero if no symbol table is present. + /// + public uint PointerToSymbolTable; + + /// + /// The number of entries in the symbol table. + /// + public uint NumberOfSymbols; + + /// + /// The size of the optional header, which is required for executable files but not for object files. + /// + public ushort SizeOfOptionalHeader; + + /// + /// The characteristics of the file that define its properties, such as whether it's an executable, a DLL, etc. + /// + public System.Reflection.PortableExecutable.Characteristics Characteristics; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImageDataDirectory.cs b/src/LibObjectFile/PE/ImageDataDirectory.cs new file mode 100644 index 0000000..bb5ed25 --- /dev/null +++ b/src/LibObjectFile/PE/ImageDataDirectory.cs @@ -0,0 +1,25 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +#pragma warning disable CS0649 +/// +/// Data directory entry in the optional header of a Portable Executable (PE) file. +/// +[StructLayout(LayoutKind.Sequential, Pack = 4)] +public struct ImageDataDirectory +{ + /// + /// The relative virtual address of the data directory. + /// + public uint VirtualAddress; + + /// + /// The size of the data directory, in bytes. + /// + public uint Size; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImageDataDirectoryArray.cs b/src/LibObjectFile/PE/ImageDataDirectoryArray.cs new file mode 100644 index 0000000..7417410 --- /dev/null +++ b/src/LibObjectFile/PE/ImageDataDirectoryArray.cs @@ -0,0 +1,27 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace LibObjectFile.PE; + +#pragma warning disable CS0649 + +/// +/// An array of entries. +/// +[InlineArray(16)] +public struct ImageDataDirectoryArray +{ + private ImageDataDirectory _e; + + /// + /// Gets or sets the at the specified index. + /// + /// The index of the to get or set. + /// The at the specified index. + [UnscopedRef] + public ref ImageDataDirectory this[ImageDataDirectoryKind kind] => ref this[(int)kind]; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImageDataDirectoryKind.cs b/src/LibObjectFile/PE/ImageDataDirectoryKind.cs new file mode 100644 index 0000000..985f113 --- /dev/null +++ b/src/LibObjectFile/PE/ImageDataDirectoryKind.cs @@ -0,0 +1,72 @@ +// 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; + +/// +/// Defines directory entry indices. +/// +public enum ImageDataDirectoryKind : ushort +{ + /// + /// Export Directory + /// + Export = 0, + /// + /// Import Directory + /// + Import = 1, + /// + /// Resource Directory + /// + Resource = 2, + /// + /// Exception Directory + /// + Exception = 3, + /// + /// Security Directory + /// + Security = 4, + /// + /// Base Relocation Table + /// + BaseRelocation = 5, + /// + /// Debug Directory + /// + Debug = 6, + /// + /// Architecture Specific Data + /// + Architecture = 7, + /// + /// RVA of GP + /// + GlobalPointer = 8, + /// + /// TLS Directory + /// + Tls = 9, + /// + /// Load Configuration Directory + /// + LoadConfig = 10, + /// + /// Bound Import Directory in headers + /// + BoundImport = 11, + /// + /// Import Address Table + /// + ImportAddressTable = 12, + /// + /// Delay Load Import Descriptors + /// + DelayImport = 13, + /// + /// .NET CLR Metadata + /// + ClrMetadata = 14, +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImageDosHeader.cs b/src/LibObjectFile/PE/ImageDosHeader.cs new file mode 100644 index 0000000..42b0072 --- /dev/null +++ b/src/LibObjectFile/PE/ImageDosHeader.cs @@ -0,0 +1,110 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +#pragma warning disable CS0649 +/// +/// Represents the DOS header of a PE file. +/// +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public unsafe struct ImageDosHeader +{ + /// + /// Magic number. (Original DOS field is `e_magic`) + /// + public ImageDosMagic Magic; + + /// + /// Bytes on last page of file. (Original DOS field is `e_cblp`) + /// + public ushort ByteCountOnLastPage; + + /// + /// Pages in file. (Original DOS field is `e_cp`) + /// + public ushort PageCount; + + /// + /// Relocations. (Original DOS field is `e_crlc`) + /// + public ushort RelocationCount; + + /// + /// Size of header in paragraphs. (Original DOS field is `e_cparhdr`) + /// + public ushort SizeOfParagraphsHeader; + + /// + /// Minimum extra paragraphs needed. (Original DOS field is `e_minalloc`) + /// + public ushort MinExtraParagraphs; + + /// + /// Maximum extra paragraphs needed. (Original DOS field is `e_maxalloc`) + /// + public ushort MaxExtraParagraphs; + + /// + /// Initial (relative) SS value. (Original DOS field is `e_ss`) + /// + public ushort InitialSSValue; + + /// + /// Initial SP value. (Original DOS field is `e_sp`) + /// + public ushort InitialSPValue; + + /// + /// Checksum. (Original DOS field is `e_csum`) + /// + public ushort Checksum; + + /// + /// Initial IP value. (Original DOS field is `e_ip`) + /// + public ushort InitialIPValue; + + /// + /// Initial (relative) CS value. (Original DOS field is `e_cs`) + /// + public ushort InitialCSValue; + + /// + /// File address of relocation table. (Original DOS field is `e_lfarlc`) + /// + public ushort FileAddressRelocationTable; + + /// + /// Overlay number. (Original DOS field is `e_ovno`) + /// + public ushort OverlayNumber; + + /// + /// Reserved words. (Original DOS field is `e_res`) + /// + public fixed ushort Reserved[4]; + + /// + /// OEM identifier (for e_oeminfo). (Original DOS field is `e_oemid`) + /// + public ushort OEMIdentifier; + + /// + /// OEM information; e_oemid specific. (Original DOS field is `e_oeminfo`) + /// + public ushort OEMInformation; + + /// + /// Reserved words. (Original DOS field is `e_res2`) + /// + public fixed ushort Reserved2[10]; + + /// + /// File address of new exe header. (Original DOS field is `e_lfanew`) + /// + public int FileAddressPEHeader; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImageDosMagic.cs b/src/LibObjectFile/PE/ImageDosMagic.cs new file mode 100644 index 0000000..7801426 --- /dev/null +++ b/src/LibObjectFile/PE/ImageDosMagic.cs @@ -0,0 +1,25 @@ +// 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 +/// +/// Magic number for the > +/// +public enum ImageDosMagic : ushort +{ + /// + /// MZ - DOS executable file signature. + /// + DOS = 0x5A4D, + /// + /// NE - OS/2 executable file signature. + /// + OS2 = 0x454E, + /// + /// LE - OS/2 LE or VXD. + /// + OS2OrVXD = 0x454C, +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImageOptionalHeader.cs b/src/LibObjectFile/PE/ImageOptionalHeader.cs new file mode 100644 index 0000000..d0e2ac5 --- /dev/null +++ b/src/LibObjectFile/PE/ImageOptionalHeader.cs @@ -0,0 +1,310 @@ +// 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.CodeAnalysis; +using LibObjectFile.PE.Internal; + +namespace LibObjectFile.PE; + +public struct ImageOptionalHeader +{ + internal RawImageOptionalHeaderCommonPart1 OptionalHeaderCommonPart1; + internal RawImageOptionalHeaderBase32 OptionalHeaderBase32; + internal RawImageOptionalHeaderBase64 OptionalHeaderBase64; + internal RawImageOptionalHeaderCommonPart2 OptionalHeaderCommonPart2; + internal RawImageOptionalHeaderSize32 OptionalHeaderSize32; + internal RawImageOptionalHeaderSize64 OptionalHeaderSize64; + internal RawImageOptionalHeaderCommonPart3 OptionalHeaderCommonPart3; + + /// + /// The magic number, which identifies the file format. Expected to be 0x10b for PE32. + /// + public ImageOptionalHeaderMagic Magic + { + get => OptionalHeaderCommonPart1.Magic; + set => OptionalHeaderCommonPart1.Magic = value; + } + + /// + /// The major version number of the linker. + /// + public byte MajorLinkerVersion + { + get => OptionalHeaderCommonPart1.MajorLinkerVersion; + set => OptionalHeaderCommonPart1.MajorLinkerVersion = value; + } + + /// + /// The minor version number of the linker. + /// + public byte MinorLinkerVersion + { + get => OptionalHeaderCommonPart1.MinorLinkerVersion; + set => OptionalHeaderCommonPart1.MinorLinkerVersion = value; + } + + /// + /// The size of the code (text) section, in bytes. + /// + public uint SizeOfCode + { + get => OptionalHeaderCommonPart1.SizeOfCode; + set => OptionalHeaderCommonPart1.SizeOfCode = value; + } + + /// + /// The size of the initialized data section, in bytes. + /// + public uint SizeOfInitializedData + { + get => OptionalHeaderCommonPart1.SizeOfInitializedData; + set => OptionalHeaderCommonPart1.SizeOfInitializedData = value; + } + + /// + /// The size of the uninitialized data section, in bytes. + /// + public uint SizeOfUninitializedData + { + get => OptionalHeaderCommonPart1.SizeOfUninitializedData; + set => OptionalHeaderCommonPart1.SizeOfUninitializedData = value; + } + + /// + /// The address of the entry point relative to the image base when the executable starts. + /// + public uint AddressOfEntryPoint + { + get => OptionalHeaderCommonPart1.AddressOfEntryPoint; + set => OptionalHeaderCommonPart1.AddressOfEntryPoint = value; + } + + /// + /// The address relative to the image base of the beginning of the code section. + /// + public uint BaseOfCode + { + get => OptionalHeaderCommonPart1.BaseOfCode; + set => OptionalHeaderCommonPart1.BaseOfCode = value; + } + + /// + /// The address relative to the image base of the beginning of the data section. + /// + /// + /// Only valid for PE32. + /// + public uint BaseOfData + { + get => OptionalHeaderBase32.BaseOfData; + set => OptionalHeaderBase32.BaseOfData = value; + } + + // NT additional fields. + + /// + /// The preferred address of the first byte of the image when loaded into memory. + /// + public ulong ImageBase + { + get => OptionalHeaderBase64.ImageBase; + set => OptionalHeaderBase64.ImageBase = value; + } + + /// + /// The alignment of sections in memory, in bytes. + /// + public uint SectionAlignment + { + get => OptionalHeaderCommonPart2.SectionAlignment; + set => OptionalHeaderCommonPart2.SectionAlignment = value; + } + + /// + /// The alignment of the raw data of sections in the image file, in bytes. + /// + public uint FileAlignment + { + get => OptionalHeaderCommonPart2.FileAlignment; + set => OptionalHeaderCommonPart2.FileAlignment = value; + } + + /// + /// The major version number of the required operating system. + /// + public ushort MajorOperatingSystemVersion + { + get => OptionalHeaderCommonPart2.MajorOperatingSystemVersion; + set => OptionalHeaderCommonPart2.MajorOperatingSystemVersion = value; + } + + /// + /// The minor version number of the required operating system. + /// + public ushort MinorOperatingSystemVersion + { + get => OptionalHeaderCommonPart2.MinorOperatingSystemVersion; + set => OptionalHeaderCommonPart2.MinorOperatingSystemVersion = value; + } + + /// + /// The major version number of the image. + /// + public ushort MajorImageVersion + { + get => OptionalHeaderCommonPart2.MajorImageVersion; + set => OptionalHeaderCommonPart2.MajorImageVersion = value; + } + + /// + /// The minor version number of the image. + /// + public ushort MinorImageVersion + { + get => OptionalHeaderCommonPart2.MinorImageVersion; + set => OptionalHeaderCommonPart2.MinorImageVersion = value; + } + + /// + /// The major version number of the subsystem. + /// + public ushort MajorSubsystemVersion + { + get => OptionalHeaderCommonPart2.MajorSubsystemVersion; + set => OptionalHeaderCommonPart2.MajorSubsystemVersion = value; + } + + /// + /// The minor version number of the subsystem. + /// + public ushort MinorSubsystemVersion + { + get => OptionalHeaderCommonPart2.MinorSubsystemVersion; + set => OptionalHeaderCommonPart2.MinorSubsystemVersion = value; + } + + /// + /// Reserved; must be zero. + /// + public uint Win32VersionValue + { + get => OptionalHeaderCommonPart2.Win32VersionValue; + set => OptionalHeaderCommonPart2.Win32VersionValue = value; + } + + /// + /// The size of the image, including all headers, as loaded in memory, in bytes. Must be a multiple of SectionAlignment. + /// + public uint SizeOfImage + { + get => OptionalHeaderCommonPart2.SizeOfImage; + set => OptionalHeaderCommonPart2.SizeOfImage = value; + } + + /// + /// The combined size of all headers (DOS, PE, section headers), rounded up to a multiple of FileAlignment. + /// + public uint SizeOfHeaders + { + get => OptionalHeaderCommonPart2.SizeOfHeaders; + set => OptionalHeaderCommonPart2.SizeOfHeaders = value; + } + + /// + /// The image file checksum. + /// + public uint CheckSum + { + get => OptionalHeaderCommonPart2.CheckSum; + set => OptionalHeaderCommonPart2.CheckSum = value; + } + + /// + /// The subsystem required to run this image. + /// + public System.Reflection.PortableExecutable.Subsystem Subsystem + { + get => OptionalHeaderCommonPart2.Subsystem; + set => OptionalHeaderCommonPart2.Subsystem = value; + } + + /// + /// Flags indicating the DLL characteristics of the image. + /// + public System.Reflection.PortableExecutable.DllCharacteristics DllCharacteristics + { + get => OptionalHeaderCommonPart2.DllCharacteristics; + set => OptionalHeaderCommonPart2.DllCharacteristics = value; + } + + /// + /// The size of the stack to reserve, in bytes. + /// + public ulong SizeOfStackReserve + { + get => OptionalHeaderSize64.SizeOfStackReserve; + set => OptionalHeaderSize64.SizeOfStackReserve = value; + } + + /// + /// The size of the stack to commit, in bytes. + /// + public ulong SizeOfStackCommit + { + get => OptionalHeaderSize64.SizeOfStackCommit; + set => OptionalHeaderSize64.SizeOfStackCommit = value; + } + + /// + /// The size of the local heap space to reserve, in bytes. + /// + public ulong SizeOfHeapReserve + { + get => OptionalHeaderSize64.SizeOfHeapReserve; + set => OptionalHeaderSize64.SizeOfHeapReserve = value; + } + + /// + /// The size of the local heap space to commit, in bytes. + /// + public ulong SizeOfHeapCommit + { + get => OptionalHeaderSize64.SizeOfHeapCommit; + set => OptionalHeaderSize64.SizeOfHeapCommit = value; + } + + /// + /// Reserved; must be zero. + /// + public uint LoaderFlags + { + get => OptionalHeaderCommonPart3.LoaderFlags; + set => OptionalHeaderCommonPart3.LoaderFlags = value; + } + + /// + /// The number of data-directory entries in the remainder of the optional header. + /// + public uint NumberOfRvaAndSizes + { + get => OptionalHeaderCommonPart3.NumberOfRvaAndSizes; + set => OptionalHeaderCommonPart3.NumberOfRvaAndSizes = value; + } + + /// + /// The data directories array, which contains the location and size of special tables in the file. + /// + [UnscopedRef] + public ref ImageDataDirectoryArray DataDirectory => ref OptionalHeaderCommonPart3.DataDirectory; + + internal void SyncPE32PlusToPE32() + { + OptionalHeaderBase32.ImageBase = (uint)OptionalHeaderBase64.ImageBase; + OptionalHeaderSize32.SizeOfStackReserve = (uint)OptionalHeaderSize64.SizeOfStackReserve; + OptionalHeaderSize32.SizeOfStackCommit = (uint)OptionalHeaderSize64.SizeOfStackCommit; + OptionalHeaderSize32.SizeOfHeapReserve = (uint)OptionalHeaderSize64.SizeOfHeapReserve; + OptionalHeaderSize32.SizeOfHeapCommit = (uint)OptionalHeaderSize64.SizeOfHeapCommit; + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImageOptionalHeaderMagic.cs b/src/LibObjectFile/PE/ImageOptionalHeaderMagic.cs new file mode 100644 index 0000000..2a63dfd --- /dev/null +++ b/src/LibObjectFile/PE/ImageOptionalHeaderMagic.cs @@ -0,0 +1,18 @@ +// 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; + +public enum ImageOptionalHeaderMagic : ushort +{ + /// + /// PE32 + /// + PE32 = 0x10b, + + /// + /// PE32+ + /// + PE32Plus = 0x20b, +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImagePESignature.cs b/src/LibObjectFile/PE/ImagePESignature.cs new file mode 100644 index 0000000..ad4c7b8 --- /dev/null +++ b/src/LibObjectFile/PE/ImagePESignature.cs @@ -0,0 +1,16 @@ +// 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; + +/// +/// Defines the NT signature for a PE image. +/// +public enum ImagePESignature : uint +{ + /// + /// PE00 + /// + PE = 0x00004550, +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs new file mode 100644 index 0000000..f28135b --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs @@ -0,0 +1,20 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE.Internal; + +#pragma warning disable CS0649 + +[StructLayout(LayoutKind.Sequential, Pack = 4)] +internal struct RawImageOptionalHeader32 +{ + public RawImageOptionalHeaderCommonPart1 Common; + public RawImageOptionalHeaderBase32 Base32; + public RawImageOptionalHeaderCommonPart2 Common2; + public RawImageOptionalHeaderSize32 Size32; + public RawImageOptionalHeaderCommonPart3 Common3; +} + diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.cs new file mode 100644 index 0000000..83de80e --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.cs @@ -0,0 +1,19 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE.Internal; + +#pragma warning disable CS0649 + +[StructLayout(LayoutKind.Sequential, Pack = 4)] +internal struct RawImageOptionalHeader64 +{ + public RawImageOptionalHeaderCommonPart1 Common; + public RawImageOptionalHeaderBase64 Base64; + public RawImageOptionalHeaderCommonPart2 Common2; + public RawImageOptionalHeaderSize64 Size64; + public RawImageOptionalHeaderCommonPart3 Common3; +} diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase32.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase32.cs new file mode 100644 index 0000000..a362d15 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase32.cs @@ -0,0 +1,24 @@ +// 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.Internal; + +#pragma warning disable CS0649 + +internal struct RawImageOptionalHeaderBase32 +{ + /// + /// The address relative to the image base of the beginning of the data section. + /// + public uint BaseOfData; + + // + // NT additional fields. + // + + /// + /// The preferred address of the first byte of the image when loaded into memory. + /// + public uint ImageBase; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase64.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase64.cs new file mode 100644 index 0000000..5c7afc7 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase64.cs @@ -0,0 +1,19 @@ +// 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.Internal; + +#pragma warning disable CS0649 + +internal struct RawImageOptionalHeaderBase64 +{ + // + // NT additional fields. + // + + /// + /// The preferred address of the first byte of the image when loaded into memory. + /// + public ulong ImageBase; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.cs new file mode 100644 index 0000000..87df1da --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.cs @@ -0,0 +1,56 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE.Internal; + +#pragma warning disable CS0649 + +/// +/// Represents the optional header in a PE (Portable Executable) file format (32-bit). +/// +[StructLayout(LayoutKind.Sequential, Pack = 4)] +internal struct RawImageOptionalHeaderCommonPart1 +{ + /// + /// The magic number, which identifies the file format. Expected to be 0x10b for PE32. + /// + public ImageOptionalHeaderMagic Magic; + + /// + /// The major version number of the linker. + /// + public byte MajorLinkerVersion; + + /// + /// The minor version number of the linker. + /// + public byte MinorLinkerVersion; + + /// + /// The size of the code (text) section, in bytes. + /// + public uint SizeOfCode; + + /// + /// The size of the initialized data section, in bytes. + /// + public uint SizeOfInitializedData; + + /// + /// The size of the uninitialized data section, in bytes. + /// + public uint SizeOfUninitializedData; + + /// + /// The address of the entry point relative to the image base when the executable starts. + /// + public uint AddressOfEntryPoint; + + /// + /// The address relative to the image base of the beginning of the code section. + /// + public uint BaseOfCode; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart2.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart2.cs new file mode 100644 index 0000000..a95da2f --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart2.cs @@ -0,0 +1,83 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE.Internal; + +#pragma warning disable CS0649 + +[StructLayout(LayoutKind.Sequential, Pack = 4)] +internal struct RawImageOptionalHeaderCommonPart2 +{ + /// + /// The alignment of sections in memory, in bytes. + /// + public uint SectionAlignment; + + /// + /// The alignment of the raw data of sections in the image file, in bytes. + /// + public uint FileAlignment; + + /// + /// The major version number of the required operating system. + /// + public ushort MajorOperatingSystemVersion; + + /// + /// The minor version number of the required operating system. + /// + public ushort MinorOperatingSystemVersion; + + /// + /// The major version number of the image. + /// + public ushort MajorImageVersion; + + /// + /// The minor version number of the image. + /// + public ushort MinorImageVersion; + + /// + /// The major version number of the subsystem. + /// + public ushort MajorSubsystemVersion; + + /// + /// The minor version number of the subsystem. + /// + public ushort MinorSubsystemVersion; + + /// + /// Reserved; must be zero. + /// + public uint Win32VersionValue; + + /// + /// The size of the image, including all headers, as loaded in memory, in bytes. Must be a multiple of SectionAlignment. + /// + public uint SizeOfImage; + + /// + /// The combined size of all headers (DOS, PE, section headers), rounded up to a multiple of FileAlignment. + /// + public uint SizeOfHeaders; + + /// + /// The image file checksum. + /// + public uint CheckSum; + + /// + /// The subsystem required to run this image. + /// + public System.Reflection.PortableExecutable.Subsystem Subsystem; + + /// + /// Flags indicating the DLL characteristics of the image. + /// + public System.Reflection.PortableExecutable.DllCharacteristics DllCharacteristics; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart3.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart3.cs new file mode 100644 index 0000000..ad13d8c --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart3.cs @@ -0,0 +1,28 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE.Internal; + +#pragma warning disable CS0649 + +[StructLayout(LayoutKind.Sequential, Pack = 4)] +internal struct RawImageOptionalHeaderCommonPart3 +{ + /// + /// Reserved; must be zero. + /// + public uint LoaderFlags; + + /// + /// The number of data-directory entries in the remainder of the optional header. + /// + public uint NumberOfRvaAndSizes; + + /// + /// The data directories array, which contains the location and size of special tables in the file. + /// + public ImageDataDirectoryArray DataDirectory; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderSize32.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderSize32.cs new file mode 100644 index 0000000..69df494 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderSize32.cs @@ -0,0 +1,33 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE.Internal; + +#pragma warning disable CS0649 + +[StructLayout(LayoutKind.Sequential, Pack = 4)] +internal struct RawImageOptionalHeaderSize32 +{ + /// + /// The size of the stack to reserve, in bytes. + /// + public uint SizeOfStackReserve; + + /// + /// The size of the stack to commit, in bytes. + /// + public uint SizeOfStackCommit; + + /// + /// The size of the local heap space to reserve, in bytes. + /// + public uint SizeOfHeapReserve; + + /// + /// The size of the local heap space to commit, in bytes. + /// + public uint SizeOfHeapCommit; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderSize64.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderSize64.cs new file mode 100644 index 0000000..42147d4 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderSize64.cs @@ -0,0 +1,33 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE.Internal; + +#pragma warning disable CS0649 + +[StructLayout(LayoutKind.Sequential, Pack = 4)] +internal struct RawImageOptionalHeaderSize64 +{ + /// + /// The size of the stack to reserve, in bytes. + /// + public ulong SizeOfStackReserve; + + /// + /// The size of the stack to commit, in bytes. + /// + public ulong SizeOfStackCommit; + + /// + /// The size of the local heap space to reserve, in bytes. + /// + public ulong SizeOfHeapReserve; + + /// + /// The size of the local heap space to commit, in bytes. + /// + public ulong SizeOfHeapCommit; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs b/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs new file mode 100644 index 0000000..b40d4f2 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs @@ -0,0 +1,99 @@ +// 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.Runtime.InteropServices; +using System.Text; + +namespace LibObjectFile.PE.Internal; + +#pragma warning disable CS0649 + +/// +/// Represents the section header in a PE (Portable Executable) file format. +/// +[StructLayout(LayoutKind.Sequential, Pack = 4)] +[DebuggerDisplay("{NameAsString,nq}")] +internal unsafe struct RawImageSectionHeader +{ + /// + /// An 8-byte name for the section. + /// + public fixed byte Name[8]; + + /// + /// Returns the name of the section as a string. + /// + public string NameAsString + { + get + { + var span = MemoryMarshal.CreateSpan(ref Name[0], 8); + int length = span.IndexOf((byte)0); + if (length >= 0) + { + span = span.Slice(0, length); + } + + return Encoding.ASCII.GetString(span); + } + } + + /// + /// The total size of the section when loaded into memory. + /// If this value is greater than , the section is zero-padded. + /// + public uint VirtualSize; + + /// + /// The physical address or the size of the section's data in memory. + /// This field is an alias for . + /// + public uint PhysicalAddress + { + get => VirtualSize; + set => VirtualSize = value; + } + + /// + /// The address of the first byte of the section when loaded into memory, relative to the image base. + /// + public uint VirtualAddress; + + /// + /// The size of the section's raw data in the file, in bytes. + /// + public uint SizeOfRawData; + + /// + /// The file pointer to the first page of the section's raw data. + /// + public uint PointerToRawData; + + /// + /// The file pointer to the beginning of the relocation entries for the section, if present. + /// + public uint PointerToRelocations; + + /// + /// The file pointer to the beginning of the line-number entries for the section, if present. + /// + public uint PointerToLineNumbers; + + /// + /// The number of relocation entries for the section. + /// + public ushort NumberOfRelocations; + + /// + /// The number of line-number entries for the section. + /// + public ushort NumberOfLineNumbers; + + /// + /// Flags that describe the characteristics of the section. + /// + public System.Reflection.PortableExecutable.SectionCharacteristics Characteristics; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs new file mode 100644 index 0000000..4018b8e --- /dev/null +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -0,0 +1,227 @@ +// 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.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using LibObjectFile.PE.Internal; + +namespace LibObjectFile.PE; + +partial class PEFile +{ + /// + /// Reads an from the specified stream. + /// + /// The stream to read PE file from + /// The options for the reader + /// An instance of if the read was successful. + public static PEFile Read(Stream stream, PEImageReaderOptions? options = null) + { + if (!TryRead(stream, out var objectFile, out var diagnostics, options)) + { + throw new ObjectFileException($"Unexpected error while reading PE file", diagnostics); + } + return objectFile; + } + + /// + /// Tries to read an from the specified stream. + /// + /// The stream to read PE file from + /// instance of if the read was successful. + /// A instance + /// The options for the reader + /// true An instance of if the read was successful. + public static bool TryRead(Stream stream, [NotNullWhen(true)] out PEFile? peFile, [NotNullWhen(false)] out DiagnosticBag? diagnostics, PEImageReaderOptions? options = null) + { + ArgumentNullException.ThrowIfNull(stream); + + options ??= new PEImageReaderOptions(); + peFile = new PEFile(false); + var reader = new PEImageReader(peFile, stream, options); + diagnostics = reader.Diagnostics; + + peFile.ReadFromInternal(reader); + + return !reader.Diagnostics.HasErrors; + } + + internal void ReadFromInternal(PEImageReader imageReader) + { + Debug.Assert(Unsafe.SizeOf() == 64); + + var diagnostics = imageReader.Diagnostics; + int read = imageReader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref DosHeader, 1))); + if (read != Unsafe.SizeOf()) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosHeaderSize, "Invalid DOS header"); + return; + } + + if (DosHeader.Magic != ImageDosMagic.DOS) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosHeaderMagic, "Invalid DOS header"); + return; + } + + // Read the DOS stub + var dosStubSize = DosHeader.SizeOfParagraphsHeader * 16; + if (dosStubSize > 0) + { + var dosStub = new byte[dosStubSize]; + read = imageReader.Read(dosStub); + if (read != dosStubSize) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosStubSize, "Invalid DOS stub"); + } + + _dosStub = dosStub; + } + else + { + _dosStub = []; + } + + // Read any DOS stub extra data (e.g Rich) + if (DosHeader.FileAddressPEHeader > read) + { + _dosStubExtra = imageReader.ReadAsStream((ulong)(DosHeader.FileAddressPEHeader - read)); + } + + // Read the PE signature + imageReader.Stream.Seek(DosHeader.FileAddressPEHeader, SeekOrigin.Begin); + + var signature = default(ImagePESignature); + read = imageReader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref signature, 1))); + if (read != sizeof(ImagePESignature)) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidPESignature, "Invalid PE signature"); + return; + } + + if (signature != ImagePESignature.PE) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidPESignature, $"Invalid PE signature 0x{(uint)signature:X8}"); + return; + } + + // Read the COFF header + Debug.Assert(Unsafe.SizeOf() == 20); + var coffHeader = default(ImageCoffHeader); + read = imageReader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref coffHeader, 1))); + if (read != Unsafe.SizeOf()) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidCoffHeaderSize, "Invalid COFF header"); + return; + } + CoffHeader = coffHeader; + + var tempArray = ArrayPool.Shared.Rent(CoffHeader.SizeOfOptionalHeader); + try + { + var optionalHeader = new Span(tempArray, 0, CoffHeader.SizeOfOptionalHeader); + read = imageReader.Read(optionalHeader); + if (read != CoffHeader.SizeOfOptionalHeader) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidOptionalHeaderSize, "Invalid optional header"); + return; + } + + var magic = MemoryMarshal.Cast(optionalHeader.Slice(0, 2))[0]; + + Debug.Assert(Unsafe.SizeOf() == 224); + Debug.Assert(Unsafe.SizeOf() == 240); + + // Process known PE32/PE32+ headers + if (magic == ImageOptionalHeaderMagic.PE32) + { + var optionalHeader32 = new RawImageOptionalHeader32(); + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref optionalHeader32, 1)); + if (span.Length > CoffHeader.SizeOfOptionalHeader) + { + span = span.Slice(0, CoffHeader.SizeOfOptionalHeader); + } + + optionalHeader.CopyTo(span); + + OptionalHeader.OptionalHeaderCommonPart1 = optionalHeader32.Common; + OptionalHeader.OptionalHeaderBase32 = optionalHeader32.Base32; + OptionalHeader.OptionalHeaderCommonPart2 = optionalHeader32.Common2; + OptionalHeader.OptionalHeaderSize32 = optionalHeader32.Size32; + OptionalHeader.OptionalHeaderCommonPart3 = optionalHeader32.Common3; + } + else if (magic == ImageOptionalHeaderMagic.PE32Plus) + { + var optionalHeader64 = new RawImageOptionalHeader64(); + // Skip 2 bytes as we read already the magic number + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref optionalHeader64, 1)); + if (span.Length > CoffHeader.SizeOfOptionalHeader) + { + span = span.Slice(0, CoffHeader.SizeOfOptionalHeader); + } + + optionalHeader.CopyTo(span); + + OptionalHeader.OptionalHeaderCommonPart1 = optionalHeader64.Common; + OptionalHeader.OptionalHeaderBase64 = optionalHeader64.Base64; + OptionalHeader.OptionalHeaderCommonPart2 = optionalHeader64.Common2; + OptionalHeader.OptionalHeaderSize64 = optionalHeader64.Size64; + OptionalHeader.OptionalHeaderCommonPart3 = optionalHeader64.Common3; + } + else + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidOptionalHeaderMagic, $"Invalid optional header PE magic 0x{(uint)magic:X8}"); + return; + } + + // Read sections + ArrayPool.Shared.Return(tempArray); + Debug.Assert(Unsafe.SizeOf() == 40); + var sizeOfSections = CoffHeader.NumberOfSections * Unsafe.SizeOf(); + tempArray = ArrayPool.Shared.Rent(sizeOfSections); + var spanSections = new Span(tempArray, 0, sizeOfSections); + read = imageReader.Read(spanSections); + + var sectionHeaders = MemoryMarshal.Cast(spanSections); + if (read != spanSections.Length) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidSectionHeadersSize, "Invalid section headers"); + } + + InitializeSections(sectionHeaders); + } + finally + { + ArrayPool.Shared.Return(tempArray); + } + } + + private void InitializeSections(ReadOnlySpan headers) + { + _sections.Clear(); + foreach (var section in headers) + { + // We don't validate the name + var peSection = new PESection(this, new PESectionName(section.NameAsString, false)) + { + Offset = section.PointerToRawData, + Size = section.SizeOfRawData, + VirtualAddress = section.VirtualAddress, + VirtualSize = section.VirtualSize, + Characteristics = section.Characteristics, + + PointerToRelocations = section.PointerToRelocations, + PointerToLineNumbers = section.PointerToLineNumbers, + NumberOfRelocations = section.NumberOfRelocations, + NumberOfLineNumbers = section.NumberOfLineNumbers + }; + _sections.Add(peSection); + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs new file mode 100644 index 0000000..a8931de --- /dev/null +++ b/src/LibObjectFile/PE/PEFile.cs @@ -0,0 +1,156 @@ +// 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; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection.PortableExecutable; +using System.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +/// +/// A Portable Executable file that can be read, modified and written. +/// +public partial class PEFile : PEObject +{ + private byte[] _dosStub = []; + private Stream? _dosStubExtra; + private readonly List _sections = new(); + + /// + /// Initializes a new instance of the class. + /// + public PEFile() + { + // TODO: Add default initialization + } + + /// + /// Internal constructor used to create a PEFile without initializing the DOS header. + /// + internal PEFile(bool unused) + { + } + + /// + /// Gets the DOS header. + /// + public ImageDosHeader DosHeader; + + /// + /// Gets or sets the DOS stub. + /// + public byte[] DosStub + { + get => _dosStub; + + set => _dosStub = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Gets or sets the DOS stub extra data (e.g Rich). + /// + public Stream? DosStubExtra + { + get => _dosStubExtra; + + set => _dosStubExtra = value; + } + + /// + /// Gets the COFF header. + /// + public ImageCoffHeader CoffHeader; + + /// + /// Gets the optional header. + /// + public ImageOptionalHeader OptionalHeader; + + + public IReadOnlyList Sections => _sections; + + public PESection AddSection(string name, uint virtualAddress, uint virtualSize, SectionCharacteristics characteristics = SectionCharacteristics.MemRead) + { + var section = new PESection(this, name) + { + VirtualAddress = virtualAddress, + VirtualSize = virtualSize, + Characteristics = characteristics + }; + _sections.Add(section); + return section; + } + + public void RemoveSectionAt(int index) => _sections.RemoveAt(index); + + public void RemoveSection(PESection section) => _sections.Remove(section); + + public bool TryFindSection(uint virtualAddress, [NotNullWhen(true)] out PESection? section) + => TryFindSection(virtualAddress, 0, out section); + + public bool TryFindSection(uint virtualAddress, uint virtualSize, [NotNullWhen(true)] out PESection? section) + { + var sections = CollectionsMarshal.AsSpan(_sections); + foreach (var trySection in sections) + { + if (trySection.ContainsVirtual(virtualAddress, virtualSize)) + { + section = trySection; + return true; + } + } + + section = null; + return false; + } + + public void RemoveSection(PESectionName name) + { + ArgumentNullException.ThrowIfNull(name); + if (!TryGetSection(name, out var section)) + { + throw new KeyNotFoundException($"Cannot find section with name `{name}`"); + } + + _sections.Remove(section); + } + + public void ClearSections() => _sections.Clear(); + + public PESection GetSection(PESectionName name) + { + ArgumentNullException.ThrowIfNull(name); + if (!TryGetSection(name, out var section)) + { + throw new KeyNotFoundException($"Cannot find section with name `{name}`"); + } + + return section; + } + + public bool TryGetSection(PESectionName name, [NotNullWhen(true)] out PESection? section) + { + ArgumentNullException.ThrowIfNull(name); + var sections = CollectionsMarshal.AsSpan(_sections); + foreach (var trySection in sections) + { + if (trySection.Name == name) + { + section = trySection; + return true; + } + } + + section = null; + return false; + } + + public override void UpdateLayout(DiagnosticBag diagnostics) + { + // TODO + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEImageReader.cs b/src/LibObjectFile/PE/PEImageReader.cs new file mode 100644 index 0000000..e4ee78c --- /dev/null +++ b/src/LibObjectFile/PE/PEImageReader.cs @@ -0,0 +1,22 @@ +// 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.IO; + +namespace LibObjectFile.PE; + +public sealed class PEImageReader : ObjectFileReaderWriter +{ + internal PEImageReader(PEFile file, Stream stream, PEImageReaderOptions readerOptions) : base(stream) + { + PEFile = file; + Options = readerOptions; + } + + public PEFile PEFile { get; } + + public PEImageReaderOptions Options { get; } + + public override bool IsReadOnly => Options.IsReadOnly; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEImageReaderOptions.cs b/src/LibObjectFile/PE/PEImageReaderOptions.cs new file mode 100644 index 0000000..76bf40e --- /dev/null +++ b/src/LibObjectFile/PE/PEImageReaderOptions.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. + +using System; + +namespace LibObjectFile.PE; + +public class PEImageReaderOptions +{ + public bool IsReadOnly { get; init; } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEObject.cs b/src/LibObjectFile/PE/PEObject.cs new file mode 100644 index 0000000..af77a68 --- /dev/null +++ b/src/LibObjectFile/PE/PEObject.cs @@ -0,0 +1,9 @@ +// 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; + +public abstract class PEObject : ObjectFileNode +{ +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs new file mode 100644 index 0000000..b639513 --- /dev/null +++ b/src/LibObjectFile/PE/PESection.cs @@ -0,0 +1,144 @@ +// 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; +using System.Reflection.PortableExecutable; + +namespace LibObjectFile.PE; + +/// +/// Defines a section in a Portable Executable (PE) image. +/// +public class PESection : PEObject +{ + private readonly List _dataList; + + internal PESection(PEFile peFile, PESectionName name) + { + Parent = peFile; + Name = name; + _dataList = new List(); + // Most of the time readable + Characteristics = SectionCharacteristics.MemRead; + } + + /// + /// Gets the parent of this section. + /// + public PEFile? ImageFile => (PEFile?)Parent; + + /// + /// Gets the name of this section. + /// + public PESectionName Name { get; } + + /// + /// The address of the first byte of the section when loaded into memory, relative to the image base. + /// + public uint VirtualAddress { get; set; } + + /// + /// The total size of the section when loaded into memory. + /// If this value is greater than , the section is zero-padded. + /// + public uint VirtualSize { get; set; } + + /// + /// The file pointer to the beginning of the relocation entries for the section, if present. + /// + public uint PointerToRelocations { get; set; } + + /// + /// The file pointer to the beginning of the line-number entries for the section, if present. + /// + public uint PointerToLineNumbers { get; set; } + + /// + /// The number of relocation entries for the section. + /// + public ushort NumberOfRelocations { get; set; } + + /// + /// The number of line-number entries for the section. + /// + public ushort NumberOfLineNumbers { get; set; } + + /// + /// Flags that describe the characteristics of the section. + /// + public System.Reflection.PortableExecutable.SectionCharacteristics Characteristics { get; set; } + + /// + /// Gets the list of data associated with this section. + /// + public IReadOnlyList DataList => _dataList; + + /// + /// Adds a new data to this section. + /// + /// The data to add. + /// If the data is already associated with a section. + public void AddData(PESectionData data) + { + ArgumentNullException.ThrowIfNull(data); + if (data.Section != null) throw new ArgumentException("Data is already associated with a section", nameof(data)); + data.Section = this; + _dataList.Add(data); + } + + /// + /// Removes the specified data from this section. + /// + /// The data to remove. + /// If the data is already associated with a section. + public void RemoveData(PESectionData data) + { + ArgumentNullException.ThrowIfNull(data); + if (data.Section != this) throw new ArgumentException("Data is not associated with this section", nameof(data)); + data.Section = null; + _dataList.Remove(data); + } + + /// + /// Removes the data at the specified index. + /// + /// The index of the data to remove. + public void RemoveDataAt(int index) + { + var data = _dataList[index]; + data.Section = null; + _dataList.RemoveAt(index); + } + + /// + /// Checks if the specified virtual address is contained by this section. + /// + /// The virtual address to check if it belongs to this section. + /// true if the virtual address is within the section range. + public bool ContainsVirtual(uint virtualAddress) + { + return virtualAddress >= VirtualAddress && virtualAddress < VirtualAddress + VirtualSize; + } + + /// + /// Checks if the specified virtual address and size is contained by this section. + /// + /// 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(uint virtualAddress, uint size) + { + return virtualAddress >= VirtualAddress && virtualAddress + size <= VirtualAddress + VirtualSize; + } + + /// + public override void UpdateLayout(DiagnosticBag diagnostics) + { + foreach (var data in DataList) + { + data.UpdateLayout(diagnostics); + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs new file mode 100644 index 0000000..3f3fc49 --- /dev/null +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -0,0 +1,318 @@ +// 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; + +/// +/// Base class for data contained in a . +/// +public abstract class PESectionData : PEObject +{ + /// + /// Gets the parent of this section data. + /// + public PESection? Section + { + get => (PESection?)Parent; + + internal set => Parent = value; + } + + protected abstract void WriteTo(Stream stream); +} + +/// +/// 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 WriteTo(Stream stream) => stream.Write(Data.Span); +} + +/// +/// Gets a stream section data in a Portable Executable (PE) image. +/// +public sealed 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 WriteTo(Stream stream) + { + Stream.Position = 0; + Stream.CopyTo(stream); + } +} + +public abstract class PEDirectory(ImageDataDirectoryKind kind) : PESectionData +{ + public ImageDataDirectoryKind Kind { get; } = kind; +} + +public sealed class PEExportDirectory() : PEDirectory(ImageDataDirectoryKind.Export) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PEImportDirectory() : PEDirectory(ImageDataDirectoryKind.Import) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PEResourceDirectory() : PEDirectory(ImageDataDirectoryKind.Resource) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PEExceptionDirectory() : PEDirectory(ImageDataDirectoryKind.Exception) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PEBaseRelocationDirectory() : PEDirectory(ImageDataDirectoryKind.BaseRelocation) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PEDebugDirectory() : PEDirectory(ImageDataDirectoryKind.Debug) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PELoadConfigDirectory() : PEDirectory(ImageDataDirectoryKind.LoadConfig) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PEBoundImportDirectory() : PEDirectory(ImageDataDirectoryKind.BoundImport) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PEImportAddressTable() : PEDirectory(ImageDataDirectoryKind.ImportAddressTable) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PETlsDirectory() : PEDirectory(ImageDataDirectoryKind.Tls) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PEDelayImportDirectory() : PEDirectory(ImageDataDirectoryKind.DelayImport) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PEClrMetadata() : PEDirectory(ImageDataDirectoryKind.ClrMetadata) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PEArchitectureDirectory() : PEDirectory(ImageDataDirectoryKind.Architecture) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PEGlobalPointerDirectory() : PEDirectory(ImageDataDirectoryKind.GlobalPointer) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} + +public sealed class PESecurityDirectory() : PEDirectory(ImageDataDirectoryKind.Security) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void WriteTo(Stream stream) + { + throw new NotImplementedException(); + } +} diff --git a/src/LibObjectFile/PE/PESectionName.cs b/src/LibObjectFile/PE/PESectionName.cs new file mode 100644 index 0000000..446650e --- /dev/null +++ b/src/LibObjectFile/PE/PESectionName.cs @@ -0,0 +1,81 @@ +// 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; + +namespace LibObjectFile.PE; + +/// +/// A section name in a Portable Executable (PE) image. +/// +public readonly record struct PESectionName +{ + /// + /// Internal constructor used to bypass the validation of the section name. + /// + /// The name of the section. + /// Validates the name. + internal PESectionName(string name, bool validate = true) + { + if (validate) + { + Validate(name); + } + Name = name; + } + + /// + /// Gets the name of the section. + /// + public string Name { get; } + + /// + public bool Equals(PESectionName other) => Name.Equals(other.Name, StringComparison.Ordinal); + + /// + public override int GetHashCode() => Name.GetHashCode(); + + /// + public override string ToString() => Name; + + /// + /// Checks if the specified section name is a valid section name. + /// + /// + /// + /// > + /// A section name is valid if it contains only printable ASCII characters (0x20 to 0x7E) and has a maximum length of 8 characters. + /// + public static void Validate(string name) + { + ArgumentNullException.ThrowIfNull(name); + Span buffer = stackalloc byte[Encoding.ASCII.GetMaxByteCount(name.Length)]; + int total = Encoding.ASCII.GetBytes(name, buffer); + if (total > 8) + { + throw new ArgumentException("Section name is too long (max 8 characters)", nameof(name)); + } + + for (int i = 0; i < total; i++) + { + if (buffer[i] < 0x20 || buffer[i] > 0x7E) + { + throw new ArgumentException("Section name contains invalid characters", nameof(name)); + } + } + } + + /// + /// Converts a string to a . + /// + /// The section name. + public static implicit operator PESectionName(string name) => new(name); + + /// + /// Converts a to a string. + /// + /// The section name. + public static implicit operator string(PESectionName name) => name.Name; +} \ No newline at end of file From d5886f798f2e3909c37f3f83a8c4f3c2ae1b1581 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 15 Sep 2024 11:38:57 +0200 Subject: [PATCH 07/87] Add PEImageWriter --- .../LibObjectFile.csproj.DotSettings | 3 +- .../PE/DataDirectory/PEDirectory.cs | 208 ++++++++++++++++++ src/LibObjectFile/PE/PEFile.Write.cs | 58 +++++ src/LibObjectFile/PE/PEImageWriter.cs | 19 ++ src/LibObjectFile/PE/PESectionData.cs | 208 +----------------- 5 files changed, 291 insertions(+), 205 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDirectory.cs create mode 100644 src/LibObjectFile/PE/PEFile.Write.cs create mode 100644 src/LibObjectFile/PE/PEImageWriter.cs diff --git a/src/LibObjectFile/LibObjectFile.csproj.DotSettings b/src/LibObjectFile/LibObjectFile.csproj.DotSettings index 02a34e6..96ec408 100644 --- a/src/LibObjectFile/LibObjectFile.csproj.DotSettings +++ b/src/LibObjectFile/LibObjectFile.csproj.DotSettings @@ -1,2 +1,3 @@  - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs new file mode 100644 index 0000000..7d27ae1 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs @@ -0,0 +1,208 @@ +// 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.IO; +using System; + +namespace LibObjectFile.PE; + +public abstract class PEDirectory(ImageDataDirectoryKind kind) : PESectionData +{ + public ImageDataDirectoryKind Kind { get; } = kind; +} + +public sealed class PEExportDirectory() : PEDirectory(ImageDataDirectoryKind.Export) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PEImportDirectory() : PEDirectory(ImageDataDirectoryKind.Import) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PEResourceDirectory() : PEDirectory(ImageDataDirectoryKind.Resource) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PEExceptionDirectory() : PEDirectory(ImageDataDirectoryKind.Exception) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PEBaseRelocationDirectory() : PEDirectory(ImageDataDirectoryKind.BaseRelocation) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PEDebugDirectory() : PEDirectory(ImageDataDirectoryKind.Debug) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PELoadConfigDirectory() : PEDirectory(ImageDataDirectoryKind.LoadConfig) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PEBoundImportDirectory() : PEDirectory(ImageDataDirectoryKind.BoundImport) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PEImportAddressTable() : PEDirectory(ImageDataDirectoryKind.ImportAddressTable) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PETlsDirectory() : PEDirectory(ImageDataDirectoryKind.Tls) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PEDelayImportDirectory() : PEDirectory(ImageDataDirectoryKind.DelayImport) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PEClrMetadata() : PEDirectory(ImageDataDirectoryKind.ClrMetadata) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PEArchitectureDirectory() : PEDirectory(ImageDataDirectoryKind.Architecture) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PEGlobalPointerDirectory() : PEDirectory(ImageDataDirectoryKind.GlobalPointer) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} + +public sealed class PESecurityDirectory() : PEDirectory(ImageDataDirectoryKind.Security) +{ + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} diff --git a/src/LibObjectFile/PE/PEFile.Write.cs b/src/LibObjectFile/PE/PEFile.Write.cs new file mode 100644 index 0000000..0df9f78 --- /dev/null +++ b/src/LibObjectFile/PE/PEFile.Write.cs @@ -0,0 +1,58 @@ +// 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; + +partial class PEFile +{ + /// + /// Writes this PE file to the specified stream. + /// + /// The stream to write to. + public void Write(Stream stream) + { + if (!TryWrite(stream, out var diagnostics)) + { + throw new ObjectFileException($"Invalid PE File", diagnostics); + } + } + + /// + /// Tries to write this PE file to the specified stream. + /// + /// The stream to write to. + /// The output diagnostics + /// true if writing was successful. otherwise false + public bool TryWrite(Stream stream, out DiagnosticBag diagnostics) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); + + var peWriter = new PEImageWriter(this, stream); + diagnostics = peWriter.Diagnostics; + + Verify(diagnostics); + if (diagnostics.HasErrors) + { + return false; + } + + UpdateLayout(diagnostics); + if (diagnostics.HasErrors) + { + return false; + } + + WriteInternal(peWriter); + + return !diagnostics.HasErrors; + } + + private void WriteInternal(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEImageWriter.cs b/src/LibObjectFile/PE/PEImageWriter.cs new file mode 100644 index 0000000..b9e649d --- /dev/null +++ b/src/LibObjectFile/PE/PEImageWriter.cs @@ -0,0 +1,19 @@ +// 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.IO; + +namespace LibObjectFile.PE; + +public sealed class PEImageWriter : ObjectFileReaderWriter +{ + internal PEImageWriter(PEFile file, Stream stream) : base(stream) + { + PEFile = file; + } + + public PEFile PEFile { get; } + + public override bool IsReadOnly => false; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index 3f3fc49..7bb9c54 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -22,7 +22,7 @@ public PESection? Section internal set => Parent = value; } - protected abstract void WriteTo(Stream stream); + protected abstract void Write(PEImageWriter writer); } /// @@ -63,7 +63,7 @@ public override void UpdateLayout(DiagnosticBag diagnostics) { } - protected override void WriteTo(Stream stream) => stream.Write(Data.Span); + protected override void Write(PEImageWriter writer) => writer.Write(Data.Span); } /// @@ -110,209 +110,9 @@ public override void UpdateLayout(DiagnosticBag diagnostics) { } - protected override void WriteTo(Stream stream) + protected override void Write(PEImageWriter writer) { Stream.Position = 0; - Stream.CopyTo(stream); - } -} - -public abstract class PEDirectory(ImageDataDirectoryKind kind) : PESectionData -{ - public ImageDataDirectoryKind Kind { get; } = kind; -} - -public sealed class PEExportDirectory() : PEDirectory(ImageDataDirectoryKind.Export) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PEImportDirectory() : PEDirectory(ImageDataDirectoryKind.Import) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PEResourceDirectory() : PEDirectory(ImageDataDirectoryKind.Resource) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PEExceptionDirectory() : PEDirectory(ImageDataDirectoryKind.Exception) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PEBaseRelocationDirectory() : PEDirectory(ImageDataDirectoryKind.BaseRelocation) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PEDebugDirectory() : PEDirectory(ImageDataDirectoryKind.Debug) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PELoadConfigDirectory() : PEDirectory(ImageDataDirectoryKind.LoadConfig) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PEBoundImportDirectory() : PEDirectory(ImageDataDirectoryKind.BoundImport) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PEImportAddressTable() : PEDirectory(ImageDataDirectoryKind.ImportAddressTable) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PETlsDirectory() : PEDirectory(ImageDataDirectoryKind.Tls) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PEDelayImportDirectory() : PEDirectory(ImageDataDirectoryKind.DelayImport) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PEClrMetadata() : PEDirectory(ImageDataDirectoryKind.ClrMetadata) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PEArchitectureDirectory() : PEDirectory(ImageDataDirectoryKind.Architecture) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PEGlobalPointerDirectory() : PEDirectory(ImageDataDirectoryKind.GlobalPointer) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); - } -} - -public sealed class PESecurityDirectory() : PEDirectory(ImageDataDirectoryKind.Security) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) - { - throw new NotImplementedException(); - } - - protected override void WriteTo(Stream stream) - { - throw new NotImplementedException(); + Stream.CopyTo(writer.Stream); } } From e7635b84c4771ce533af21e3318cb49195d9e347 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 16 Sep 2024 09:37:31 +0200 Subject: [PATCH 08/87] Improve support for PE, add PEBaseRelocation --- src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs | 4 +- src/LibObjectFile.sln.DotSettings | 1 + src/LibObjectFile/Ar/ArArchiveFile.cs | 4 +- src/LibObjectFile/Ar/ArArchiveFileReader.cs | 4 +- src/LibObjectFile/Ar/ArArchiveFileWriter.cs | 2 +- src/LibObjectFile/Ar/ArSymbolTable.cs | 4 +- src/LibObjectFile/DiagnosticId.cs | 7 + src/LibObjectFile/Dwarf/DwarfAbbreviation.cs | 22 +-- .../Dwarf/DwarfAbbreviationItem.cs | 12 +- .../Dwarf/DwarfAbbreviationTable.cs | 18 +- .../Dwarf/DwarfAddressRangeTable.cs | 18 +- src/LibObjectFile/Dwarf/DwarfAttribute.cs | 30 ++-- .../Dwarf/DwarfCompilationUnit.cs | 4 +- src/LibObjectFile/Dwarf/DwarfDIE.cs | 24 +-- src/LibObjectFile/Dwarf/DwarfExpression.cs | 24 +-- src/LibObjectFile/Dwarf/DwarfFile.cs | 14 +- src/LibObjectFile/Dwarf/DwarfInfoSection.cs | 18 +- src/LibObjectFile/Dwarf/DwarfLine.cs | 2 +- .../Dwarf/DwarfLineProgramTable.cs | 56 +++--- src/LibObjectFile/Dwarf/DwarfLineSection.cs | 10 +- src/LibObjectFile/Dwarf/DwarfLocationList.cs | 10 +- .../Dwarf/DwarfLocationListEntry.cs | 6 +- .../Dwarf/DwarfLocationSection.cs | 10 +- src/LibObjectFile/Dwarf/DwarfOperation.cs | 38 ++-- src/LibObjectFile/Dwarf/DwarfPrinter.cs | 12 +- src/LibObjectFile/Dwarf/DwarfReader.cs | 4 +- src/LibObjectFile/Dwarf/DwarfStringTable.cs | 2 +- src/LibObjectFile/Dwarf/DwarfUnit.cs | 28 +-- src/LibObjectFile/Dwarf/DwarfWriter.cs | 2 +- src/LibObjectFile/Elf/ElfFilePart.cs | 2 +- src/LibObjectFile/Elf/ElfObjectFile.cs | 4 +- src/LibObjectFile/Elf/ElfPrinter.cs | 16 +- src/LibObjectFile/Elf/ElfReader{TDecoder}.cs | 52 +++--- src/LibObjectFile/Elf/ElfSegment.cs | 2 +- src/LibObjectFile/Elf/ElfSegmentRange.cs | 6 +- src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs | 6 +- .../Elf/Sections/ElfAlignedShadowSection.cs | 4 +- .../Elf/Sections/ElfNullSection.cs | 2 +- .../Elf/Sections/ElfProgramHeaderTable.cs | 4 +- src/LibObjectFile/ObjectFileNodeBase.cs | 10 +- src/LibObjectFile/ObjectFileReaderWriter.cs | 30 ++-- .../PE/DataDirectory/BaseRelocationType.cs | 76 ++++++++ .../PE/DataDirectory/PEBaseRelocation.cs | 86 ++++++++++ .../PEBaseRelocationDirectory.cs | 162 ++++++++++++++++++ .../PEBaseRelocationPageBlock.cs | 57 ++++++ .../PEBaseRelocationPageBlockPart.cs | 56 ++++++ .../PE/DataDirectory/PEDirectory.cs | 125 +++++++++++--- .../PE/DataDirectory/PEDirectoryTable.cs | 129 ++++++++++++++ src/LibObjectFile/PE/IVirtualAddressable.cs | 16 ++ src/LibObjectFile/PE/PEFile.Read.cs | 95 +++++++++- src/LibObjectFile/PE/PEFile.Write.cs | 4 +- src/LibObjectFile/PE/PEFile.cs | 78 ++++++++- src/LibObjectFile/PE/PEObject.cs | 30 ++++ src/LibObjectFile/PE/PESection.cs | 97 +++++++++-- src/LibObjectFile/PE/PESectionData.cs | 28 ++- src/LibObjectFile/PE/PESectionName.cs | 1 + src/LibObjectFile/PE/RVA.cs | 27 +++ src/LibObjectFile/PE/RVALink.cs | 22 +++ src/LibObjectFile/Utils/SubStream.cs | 14 +- 59 files changed, 1308 insertions(+), 323 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/BaseRelocationType.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs create mode 100644 src/LibObjectFile/PE/IVirtualAddressable.cs create mode 100644 src/LibObjectFile/PE/RVA.cs create mode 100644 src/LibObjectFile/PE/RVALink.cs diff --git a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs index bae4ae5..37dee8f 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -100,7 +100,7 @@ public void TestBss() elf.UpdateLayout(diagnostics); Assert.False(diagnostics.HasErrors); - Assert.AreEqual(1024, bssSection.Offset); + Assert.AreEqual(1024, bssSection.Position); AssertReadElf(elf, "test_bss.elf"); } @@ -419,7 +419,7 @@ public void TestAlignedSection() elf.Print(Console.Out); - Assert.AreEqual(alignedSection.UpperAlignment, codeSection.Offset, "Invalid alignment"); + Assert.AreEqual(alignedSection.UpperAlignment, codeSection.Position, "Invalid alignment"); } [Test] diff --git a/src/LibObjectFile.sln.DotSettings b/src/LibObjectFile.sln.DotSettings index a8c814e..2569fe1 100644 --- a/src/LibObjectFile.sln.DotSettings +++ b/src/LibObjectFile.sln.DotSettings @@ -6,6 +6,7 @@ See the license.txt file in the project root for more information. DIE GNU LEB + RVA True True True diff --git a/src/LibObjectFile/Ar/ArArchiveFile.cs b/src/LibObjectFile/Ar/ArArchiveFile.cs index ed697bb..244b282 100644 --- a/src/LibObjectFile/Ar/ArArchiveFile.cs +++ b/src/LibObjectFile/Ar/ArArchiveFile.cs @@ -364,13 +364,13 @@ public override void UpdateLayout(DiagnosticBag diagnostics) var headerSize = LongNamesTable.Size; if (headerSize > 0) { - LongNamesTable.Offset = size; + LongNamesTable.Position = size; size += ArFile.FileEntrySizeInBytes + LongNamesTable.Size; if ((size & 1) != 0) size++; } } - entry.Offset = size; + entry.Position = size; size += ArFile.FileEntrySizeInBytes + entry.Size; if ((size & 1) != 0) size++; } diff --git a/src/LibObjectFile/Ar/ArArchiveFileReader.cs b/src/LibObjectFile/Ar/ArArchiveFileReader.cs index 93843a5..194573c 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileReader.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileReader.cs @@ -74,7 +74,7 @@ internal void Read() { if (_futureHeaders != null) { - Diagnostics.Error(DiagnosticId.AR_ERR_InvalidDuplicatedFutureHeadersTable, $"Invalid duplicated future headers table found at offset {fileEntry.Offset} while another table was already found at offset {_futureHeaders.Offset}. This file is invalid."); + Diagnostics.Error(DiagnosticId.AR_ERR_InvalidDuplicatedFutureHeadersTable, $"Invalid duplicated future headers table found at offset {fileEntry.Position} while another table was already found at offset {_futureHeaders.Position}. This file is invalid."); break; } @@ -217,7 +217,7 @@ private bool TryReadFileEntry(Span buffer, [NotNullWhen(true)] out ArFile? entry.OwnerId = (uint)ownerId; entry.GroupId = (uint)groupId; entry.FileMode = fileMode; - entry.Offset = (ulong)entryOffset; + entry.Position = (ulong)entryOffset; entry.Size = fileSize; // Read the BSD name if necessary diff --git a/src/LibObjectFile/Ar/ArArchiveFileWriter.cs b/src/LibObjectFile/Ar/ArArchiveFileWriter.cs index 894bfa1..d13f745 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileWriter.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileWriter.cs @@ -69,7 +69,7 @@ internal void Write() private void WriteFileEntry(Span buffer, ArFile file) { - Debug.Assert((ulong)(Stream.Position - _startStreamOffset) == file.Offset); + Debug.Assert((ulong)(Stream.Position - _startStreamOffset) == file.Position); buffer.Fill((byte)' '); var name = file.InternalName; diff --git a/src/LibObjectFile/Ar/ArSymbolTable.cs b/src/LibObjectFile/Ar/ArSymbolTable.cs index c3cc32e..da263bb 100644 --- a/src/LibObjectFile/Ar/ArSymbolTable.cs +++ b/src/LibObjectFile/Ar/ArSymbolTable.cs @@ -119,7 +119,7 @@ protected override void AfterRead(DiagnosticBag diagnostics) var offsets = new Dictionary(); foreach (var fileEntry in Parent!.Files) { - offsets[fileEntry.Offset] = fileEntry; + offsets[fileEntry.Position] = fileEntry; } for (var i = 0; i < Symbols.Count; i++) @@ -151,7 +151,7 @@ protected override void Write(ArArchiveFileWriter writer) writer.Stream.WriteU32(false, stringOffset); } - writer.Stream.WriteU32(false, (uint)symbol.File.Offset); + writer.Stream.WriteU32(false, (uint)symbol.File.Position); if (isBSD) { diff --git a/src/LibObjectFile/DiagnosticId.cs b/src/LibObjectFile/DiagnosticId.cs index 7c83b7e..caa3a3e 100644 --- a/src/LibObjectFile/DiagnosticId.cs +++ b/src/LibObjectFile/DiagnosticId.cs @@ -123,5 +123,12 @@ public enum DiagnosticId PE_ERR_InvalidOptionalHeaderSize = 3005, PE_ERR_InvalidOptionalHeaderMagic = 3006, PE_ERR_InvalidSectionHeadersSize = 3007, + + // PE BaseRelocation + PE_ERR_BaseRelocationDirectoryInvalidEndOfStream = 3008, + PE_ERR_BaseRelocationDirectoryInvalidSection = 3009, + PE_ERR_BaseRelocationDirectoryInvalidSectionData = 3010, + PE_ERR_InvalidDataDirectorySection = 3011, + PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress = 3012, } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs index 09c63b3..7cf2988 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs @@ -114,7 +114,7 @@ public bool TryFindByCode(ulong code, [NotNullWhen(true)] out DwarfAbbreviationI private bool TryReadNext(DwarfReader reader) { - var startOffset = (ulong)reader.Offset; + var startOffset = (ulong)reader.Position; var code = reader.ReadULEB128(); if (code == 0) { @@ -123,7 +123,7 @@ private bool TryReadNext(DwarfReader reader) var item = new DwarfAbbreviationItem { - Offset = startOffset, + Position = startOffset, Code = code }; @@ -168,18 +168,18 @@ private bool TryReadNext(DwarfReader reader) protected override void Read(DwarfReader reader) { - Offset = reader.Offset; + Position = reader.Position; while (TryReadNext(reader)) { } - Size = (ulong)reader.Offset - Offset; + Size = (ulong)reader.Position - Position; } protected override void Write(DwarfWriter writer) { - var startOffset = writer.Offset; - Debug.Assert(startOffset == Offset); + var startOffset = writer.Position; + Debug.Assert(startOffset == Position); if (_mapItems.Count > 0) { foreach (var itemPair in _mapItems) @@ -203,19 +203,19 @@ protected override void Write(DwarfWriter writer) // End of abbreviation item writer.WriteULEB128(0); - Debug.Assert(writer.Offset - startOffset == Size); + Debug.Assert(writer.Position - startOffset == Size); } protected override void UpdateLayout(DwarfLayoutContext layoutContext) { - var endOffset = Offset; + var endOffset = Position; if (_mapItems.Count > 0) { foreach (var itemPair in _mapItems) { var item = itemPair.Value; - item.Offset = endOffset; + item.Position = endOffset; item.UpdateLayoutInternal(layoutContext); endOffset += item.Size; } @@ -227,7 +227,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) { foreach (var item in _items) { - item.Offset = endOffset; + item.Position = endOffset; item.UpdateLayoutInternal(layoutContext); endOffset += item.Size; } @@ -236,7 +236,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) endOffset += DwarfHelper.SizeOfULEB128(0); - Size = endOffset - Offset; + Size = endOffset - Position; } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs index beb6071..0cd6358 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs @@ -31,7 +31,7 @@ internal DwarfAbbreviationItem(ulong code, DwarfTagEx tag, bool hasChildren, Dwa protected override void UpdateLayout(DwarfLayoutContext layoutContext) { - var endOffset = Offset; + var endOffset = Position; // Code endOffset += DwarfHelper.SizeOfULEB128(Code); @@ -53,7 +53,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) // Null Kind and Form endOffset += DwarfHelper.SizeOfULEB128(0) * 2; - Size = endOffset - Offset; + Size = endOffset - Position; } protected override void Read(DwarfReader reader) @@ -92,13 +92,13 @@ protected override void Read(DwarfReader reader) Descriptors = descriptors != null ? new DwarfAttributeDescriptors(descriptors.ToArray()) : new DwarfAttributeDescriptors(); - Size = reader.Offset - Offset; + Size = reader.Position - Position; } protected override void Write(DwarfWriter writer) { - var startOffset = writer.Offset; - Debug.Assert(startOffset == Offset); + var startOffset = writer.Position; + Debug.Assert(startOffset == Position); // Code writer.WriteULEB128(Code); @@ -119,7 +119,7 @@ protected override void Write(DwarfWriter writer) writer.WriteULEB128(0); writer.WriteULEB128(0); - Debug.Assert(writer.Offset - startOffset == Size); + Debug.Assert(writer.Position - startOffset == Size); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs index dc607b2..53f1cc9 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs @@ -44,43 +44,43 @@ internal void Reset() protected override void UpdateLayout(DwarfLayoutContext layoutContext) { - ulong endOffset = Offset; + ulong endOffset = Position; foreach (var abbreviation in _abbreviations) { - abbreviation.Offset = endOffset; + abbreviation.Position = endOffset; abbreviation.UpdateLayoutInternal(layoutContext); endOffset += abbreviation.Size; } - Size = endOffset - Offset; + Size = endOffset - Position; } protected override void Read(DwarfReader reader) { - var endOffset = reader.Offset; - while (reader.Offset < reader.Length) + var endOffset = reader.Position; + while (reader.Position < reader.Length) { var abbreviation = new DwarfAbbreviation { - Offset = endOffset + Position = endOffset }; abbreviation.ReadInternal(reader); endOffset += abbreviation.Size; AddAbbreviation(abbreviation); } - Size = endOffset - Offset; + Size = endOffset - Position; } protected override void Write(DwarfWriter writer) { - var startOffset = writer.Offset; + var startOffset = writer.Position; foreach (var abbreviation in _abbreviations) { abbreviation.WriteInternal(writer); } - Debug.Assert(writer.Offset - startOffset == Size); + Debug.Assert(writer.Position - startOffset == Size); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs index 9265625..2044bc6 100644 --- a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs @@ -37,7 +37,7 @@ public DwarfAddressRangeTable() protected override void Read(DwarfReader reader) { - Offset = reader.Offset; + Position = reader.Position; var unitLength = reader.ReadUnitLength(); Is64BitEncoding = reader.Is64BitEncoding; Version = reader.ReadU16(); @@ -58,7 +58,7 @@ protected override void Read(DwarfReader reader) var align = (ulong)segment_selector_size + (ulong)AddressSize * 2; // SPECS 7.21: The first tuple following the header in each set begins at an offset that is a multiple of the size of a single tuple - reader.Offset = AlignHelper.AlignToUpper(reader.Offset, align); + reader.Position = AlignHelper.AlignToUpper(reader.Position, align); while (true) { @@ -115,7 +115,7 @@ protected override void Read(DwarfReader reader) Ranges.Add(new DwarfAddressRange(segment, address, length)); } - Size = reader.Offset - Offset; + Size = reader.Position - Position; } public override void Verify(DiagnosticBag diagnostics) @@ -171,13 +171,13 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) if (Unit != null) { - DebugInfoOffset = Unit.Offset; + DebugInfoOffset = Unit.Position; } } protected override void Write(DwarfWriter writer) { - var startOffset = writer.Offset; + var startOffset = writer.Position; // unit_length writer.WriteUnitLength(Size - DwarfHelper.SizeOfUnitLength(Is64BitEncoding)); @@ -203,12 +203,12 @@ protected override void Write(DwarfWriter writer) var align = (ulong)SegmentSelectorSize + (ulong)AddressSize * 2; // SPECS 7.21: The first tuple following the header in each set begins at an offset that is a multiple of the size of a single tuple - var nextOffset = AlignHelper.AlignToUpper(writer.Offset, align); - for (ulong offset = writer.Offset; offset < nextOffset; offset++) + var nextOffset = AlignHelper.AlignToUpper(writer.Position, align); + for (ulong offset = writer.Position; offset < nextOffset; offset++) { writer.WriteU8(0); } - Debug.Assert(writer.Offset == nextOffset); + Debug.Assert(writer.Position == nextOffset); foreach (var range in Ranges) { @@ -271,7 +271,7 @@ protected override void Write(DwarfWriter writer) break; } - Debug.Assert(writer.Offset - startOffset == Size); + Debug.Assert(writer.Position - startOffset == Size); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAttribute.cs b/src/LibObjectFile/Dwarf/DwarfAttribute.cs index 9e96f42..1628d8e 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttribute.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttribute.cs @@ -130,7 +130,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) var addressSize = layoutContext.CurrentUnit!.AddressSize; var is64BitEncoding = layoutContext.CurrentUnit.Is64BitEncoding; - var endOffset = Offset; + var endOffset = Position; switch (Form.Value) { case DwarfAttributeForm.Addr: @@ -209,7 +209,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) case DwarfAttributeForm.RefUdata: { var dieRef = (DwarfDIE)ValueAsObject!; - endOffset += DwarfHelper.SizeOfULEB128(dieRef.Offset - layoutContext.CurrentUnit.Offset); // WriteULEB128((dieRef.Offset - _currentUnit.Offset)); + endOffset += DwarfHelper.SizeOfULEB128(dieRef.Position - layoutContext.CurrentUnit.Position); // WriteULEB128((dieRef.Offset - _currentUnit.Offset)); break; } @@ -233,7 +233,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) case DwarfAttributeForm.Exprloc: var expr = (DwarfExpression)ValueAsObject!; - expr.Offset = endOffset; + expr.Position = endOffset; expr.UpdateLayoutInternal(layoutContext); endOffset += expr.Size; break; @@ -271,7 +271,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) throw new NotSupportedException($"Unknown {nameof(DwarfAttributeForm)}: {Form}"); } - Size = endOffset - Offset; + Size = endOffset - Position; } protected override void Read(DwarfReader reader) @@ -283,7 +283,7 @@ protected override void Read(DwarfReader reader) ReadAttributeFormRawValue(reader); - Size = reader.Offset - Offset; + Size = reader.Position - Position; ResolveAttributeValue(reader); } @@ -791,8 +791,8 @@ private void VerifyAttributeValueNotNull(DwarfLayoutContext context) protected override void Write(DwarfWriter writer) { - var startAttributeOffset = Offset; - Debug.Assert(Offset == startAttributeOffset); + var startAttributeOffset = Position; + Debug.Assert(Position == startAttributeOffset); switch (Form.Value) { @@ -883,37 +883,37 @@ protected override void Write(DwarfWriter writer) case DwarfAttributeForm.RefAddr: { var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteUIntFromEncoding(dieRef.Offset); + writer.WriteUIntFromEncoding(dieRef.Position); break; } case DwarfAttributeForm.Ref1: { var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteU8((byte) (dieRef.Offset - writer.CurrentUnit!.Offset)); + writer.WriteU8((byte) (dieRef.Position - writer.CurrentUnit!.Position)); break; } case DwarfAttributeForm.Ref2: { var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteU16((ushort) (dieRef.Offset - writer.CurrentUnit!.Offset)); + writer.WriteU16((ushort) (dieRef.Position - writer.CurrentUnit!.Position)); break; } case DwarfAttributeForm.Ref4: { var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteU32((uint) (dieRef.Offset - writer.CurrentUnit!.Offset)); + writer.WriteU32((uint) (dieRef.Position - writer.CurrentUnit!.Position)); break; } case DwarfAttributeForm.Ref8: { var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteU64((dieRef.Offset - writer.CurrentUnit!.Offset)); + writer.WriteU64((dieRef.Position - writer.CurrentUnit!.Position)); break; } case DwarfAttributeForm.RefUdata: { var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteULEB128((dieRef.Offset - writer.CurrentUnit!.Offset)); + writer.WriteULEB128((dieRef.Position - writer.CurrentUnit!.Position)); break; } @@ -935,7 +935,7 @@ protected override void Write(DwarfWriter writer) { if (ValueAsObject != null) { - writer.WriteUIntFromEncoding(((DwarfObject) ValueAsObject).Offset); + writer.WriteUIntFromEncoding(((DwarfObject) ValueAsObject).Position); } else { @@ -982,7 +982,7 @@ protected override void Write(DwarfWriter writer) throw new NotSupportedException($"Unknown {nameof(DwarfAttributeForm)}: {Form}"); } - Debug.Assert(writer.Offset - startAttributeOffset == Size); + Debug.Assert(writer.Position - startAttributeOffset == Size); } private static readonly DwarfReader.DwarfDIEReferenceResolver DwarfAttributeDIEReferenceResolverInstance = DwarfAttributeDIEReferenceResolver; diff --git a/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs b/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs index f22d2e0..3259857 100644 --- a/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs @@ -42,7 +42,7 @@ protected override void WriteHeader(DwarfWriter writer) if (Version < 5) { // 3. debug_abbrev_offset (section offset) - var abbrevOffset = Abbreviation!.Offset; + var abbrevOffset = Abbreviation!.Position; if (writer.EnableRelocation) { writer.RecordRelocation(DwarfRelocationTarget.DebugAbbrev, writer.SizeOfUIntEncoding(), abbrevOffset); @@ -61,7 +61,7 @@ protected override void WriteHeader(DwarfWriter writer) writer.WriteAddressSize(AddressSize); // 5. debug_abbrev_offset (section offset) - var abbrevOffset = Abbreviation!.Offset; + var abbrevOffset = Abbreviation!.Position; if (writer.EnableRelocation) { writer.RecordRelocation(DwarfRelocationTarget.DebugAbbrev, writer.SizeOfUIntEncoding(), abbrevOffset); diff --git a/src/LibObjectFile/Dwarf/DwarfDIE.cs b/src/LibObjectFile/Dwarf/DwarfDIE.cs index 28c0dad..2c83e7e 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIE.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIE.cs @@ -362,7 +362,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) { var abbrev = Abbrev; - var endOffset = Offset; + var endOffset = Position; if (abbrev is null) { throw new InvalidOperationException("Abbreviation is not set"); @@ -371,7 +371,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) foreach (var attr in _attributes) { - attr.Offset = endOffset; + attr.Position = endOffset; attr.UpdateLayoutInternal(layoutContext); endOffset += attr.Size; } @@ -380,7 +380,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) { foreach (var child in _children) { - child.Offset = endOffset; + child.Position = endOffset; child.UpdateLayout(layoutContext); endOffset += child.Size; } @@ -389,7 +389,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) endOffset += DwarfHelper.SizeOfULEB128(0); } - Size = endOffset - Offset; + Size = endOffset - Position; } protected override void Read(DwarfReader reader) @@ -413,7 +413,7 @@ protected override void Read(DwarfReader reader) var attribute = new DwarfAttribute() { - Offset = reader.Offset, + Position = reader.Position, }; attribute.ReadInternal(reader); @@ -437,12 +437,12 @@ protected override void Read(DwarfReader reader) reader.PopDIE(); - Size = reader.Offset - Offset; + Size = reader.Position - Position; } internal static DwarfDIE? ReadInstance(DwarfReader reader) { - var startDIEOffset = reader.Offset; + var startDIEOffset = reader.Position; var abbreviationCode = reader.ReadULEB128(); DwarfDIE? die = null; @@ -455,7 +455,7 @@ protected override void Read(DwarfReader reader) } die = DIEHelper.ConvertTagToDwarfDIE((ushort) abbreviationItem.Tag); - die.Offset = startDIEOffset; + die.Position = startDIEOffset; die.Abbrev = abbreviationItem; die.Tag = abbreviationItem.Tag; die.ReadInternal(reader); @@ -470,7 +470,7 @@ internal void UpdateAbbreviationItem(DwarfLayoutContext context) // to it, we can detect if it is a forward or backward reference. // If it is a backward reference, we will be able to encode the offset // otherwise we will have to pad the encoding with NOP (for DwarfOperation in expressions) - Offset = ulong.MaxValue; + Position = ulong.MaxValue; // TODO: pool if not used by GetOrCreate below var descriptorArray = new DwarfAttributeDescriptor[Attributes.Count]; @@ -495,8 +495,8 @@ internal void UpdateAbbreviationItem(DwarfLayoutContext context) protected override void Write(DwarfWriter writer) { - var startDIEOffset = Offset; - Debug.Assert(Offset == startDIEOffset); + var startDIEOffset = Position; + Debug.Assert(Position == startDIEOffset); var abbrev = Abbrev; if (abbrev is null) { @@ -519,7 +519,7 @@ protected override void Write(DwarfWriter writer) writer.WriteULEB128(0); } - Debug.Assert(Size == writer.Offset - startDIEOffset); + Debug.Assert(Size == writer.Position - startDIEOffset); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfExpression.cs b/src/LibObjectFile/Dwarf/DwarfExpression.cs index 9dbe2aa..b69e75f 100644 --- a/src/LibObjectFile/Dwarf/DwarfExpression.cs +++ b/src/LibObjectFile/Dwarf/DwarfExpression.cs @@ -53,27 +53,27 @@ public override void Verify(DiagnosticBag diagnostics) internal void ReadInternal(DwarfReader reader, bool inLocationSection = false) { - Offset = reader.Offset; + Position = reader.Position; var size = inLocationSection ? reader.ReadU16() : reader.ReadULEB128(); OperationLengthInBytes = size; - var endPosition = reader.Offset + size; + var endPosition = reader.Position + size; - while (reader.Offset < endPosition) + while (reader.Position < endPosition) { - var op = new DwarfOperation() {Offset = reader.Offset}; + var op = new DwarfOperation() {Position = reader.Position}; op.ReadInternal(reader); AddOperation(op); } - Size = reader.Offset - Offset; + Size = reader.Position - Position; } internal void WriteInternal(DwarfWriter writer, bool inLocationSection = false) { - Debug.Assert(Offset == writer.Offset); + Debug.Assert(Position == writer.Position); Debug.Assert(!inLocationSection || OperationLengthInBytes <= ushort.MaxValue); - var startExpressionOffset = writer.Offset; + var startExpressionOffset = writer.Position; if (inLocationSection) { writer.WriteU16((ushort)OperationLengthInBytes); @@ -88,27 +88,27 @@ internal void WriteInternal(DwarfWriter writer, bool inLocationSection = false) op.WriteInternal(writer); } - Debug.Assert(writer.Offset - startExpressionOffset == Size); + Debug.Assert(writer.Position - startExpressionOffset == Size); } internal void UpdateLayoutInternal(DwarfLayoutContext layoutContext, bool inLocationSection = false) { - var endOffset = Offset; + var endOffset = Position; foreach (var op in _operations) { - op.Offset = endOffset; + op.Position = endOffset; op.UpdateLayoutInternal(layoutContext); endOffset += op.Size; } - OperationLengthInBytes = endOffset - Offset; + OperationLengthInBytes = endOffset - Position; // We need to shift the expression which is prefixed by its size encoded in LEB128, // or fixed-size U2 in .debug_loc section var deltaLength = inLocationSection ? sizeof(ushort) : DwarfHelper.SizeOfULEB128(Size); foreach (var op in InternalOperations) { - op.Offset += deltaLength; + op.Position += deltaLength; } Size = OperationLengthInBytes + deltaLength; diff --git a/src/LibObjectFile/Dwarf/DwarfFile.cs b/src/LibObjectFile/Dwarf/DwarfFile.cs index 0fd531c..46f0c0d 100644 --- a/src/LibObjectFile/Dwarf/DwarfFile.cs +++ b/src/LibObjectFile/Dwarf/DwarfFile.cs @@ -85,7 +85,7 @@ public void UpdateLayout(DwarfLayoutConfig config, DiagnosticBag diagnostics) var layoutContext = new DwarfLayoutContext(this, config, diagnostics); - LineSection.Offset = 0; + LineSection.Position = 0; LineSection.UpdateLayoutInternal(layoutContext); if (layoutContext.HasErrors) { @@ -94,10 +94,10 @@ public void UpdateLayout(DwarfLayoutConfig config, DiagnosticBag diagnostics) // Reset the abbreviation table // TODO: Make this configurable via the DwarfWriterContext - AbbreviationTable.Offset = 0; + AbbreviationTable.Position = 0; AbbreviationTable.Reset(); - InfoSection.Offset = 0; + InfoSection.Position = 0; InfoSection.UpdateLayoutInternal(layoutContext); if (layoutContext.HasErrors) { @@ -105,7 +105,7 @@ public void UpdateLayout(DwarfLayoutConfig config, DiagnosticBag diagnostics) } // Update AddressRangeTable layout after Info - AddressRangeTable.Offset = 0; + AddressRangeTable.Position = 0; AddressRangeTable.UpdateLayoutInternal(layoutContext); if (layoutContext.HasErrors) { @@ -113,7 +113,7 @@ public void UpdateLayout(DwarfLayoutConfig config, DiagnosticBag diagnostics) } // Update string table right after updating the layout of Info - StringTable.Offset = 0; + StringTable.Position = 0; StringTable.UpdateLayoutInternal(layoutContext); if (layoutContext.HasErrors) { @@ -121,10 +121,10 @@ public void UpdateLayout(DwarfLayoutConfig config, DiagnosticBag diagnostics) } // Update the abbrev table right after we have computed the entire layout of Info - AbbreviationTable.Offset = 0; + AbbreviationTable.Position = 0; AbbreviationTable.UpdateLayoutInternal(layoutContext); - LocationSection.Offset = 0; + LocationSection.Position = 0; LocationSection.UpdateLayoutInternal(layoutContext); } diff --git a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs index 5f727a1..7f10d03 100644 --- a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs @@ -37,26 +37,26 @@ protected override void Read(DwarfReader reader) { var addressRangeTable = reader.File.AddressRangeTable; - while (reader.Offset < reader.Length) + while (reader.Position < reader.Length) { // 7.5 Format of Debugging Information // - Each such contribution consists of a compilation unit header - var startOffset = Offset; + var startOffset = Position; reader.ClearResolveAttributeReferenceWithinCompilationUnit(); var cu = DwarfUnit.ReadInstance(reader, out var offsetEndOfUnit); if (cu == null) { - reader.Offset = offsetEndOfUnit; + reader.Position = offsetEndOfUnit; continue; } reader.CurrentUnit = cu; // Link AddressRangeTable to Unit - if (addressRangeTable.DebugInfoOffset == cu.Offset) + if (addressRangeTable.DebugInfoOffset == cu.Position) { addressRangeTable.Unit = cu; } @@ -79,27 +79,27 @@ public override void Verify(DiagnosticBag diagnostics) protected override void UpdateLayout(DwarfLayoutContext layoutContext) { - var offset = Offset; + var offset = Position; foreach (var unit in Units) { layoutContext.CurrentUnit = unit; - unit.Offset = offset; + unit.Position = offset; unit.UpdateLayoutInternal(layoutContext); offset += unit.Size; } - Size = offset - Offset; + Size = offset - Position; } protected override void Write(DwarfWriter writer) { - Debug.Assert(Offset == writer.Offset); + Debug.Assert(Position == writer.Position); foreach (var unit in _units) { writer.CurrentUnit = unit; unit.WriteInternal(writer); } writer.CurrentUnit = null; - Debug.Assert(Size == writer.Offset - Offset); + Debug.Assert(Size == writer.Position - Position); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLine.cs b/src/LibObjectFile/Dwarf/DwarfLine.cs index 3581424..4f4f7e2 100644 --- a/src/LibObjectFile/Dwarf/DwarfLine.cs +++ b/src/LibObjectFile/Dwarf/DwarfLine.cs @@ -164,7 +164,7 @@ internal DwarfLineState ToState() public override string ToString() { - return $"{nameof(Offset)}: 0x{Offset:x8}, {nameof(Address)}: 0x{Address:x16}, {nameof(File)}: {File}, {nameof(Line)}: {Line,4}, {nameof(Column)}: {Column,2}, {nameof(IsStatement)}: {Bool2Str(IsStatement),5}, {nameof(IsBasicBlock)}: {Bool2Str(IsBasicBlock),5}, {nameof(IsPrologueEnd)}: {Bool2Str(IsPrologueEnd),5}, {nameof(IsEpilogueBegin)}: {Bool2Str(IsEpilogueBegin),5}, {nameof(Isa)}: {Isa,3}, {nameof(Discriminator)}: {Discriminator,3}"; + return $"{nameof(Position)}: 0x{Position:x8}, {nameof(Address)}: 0x{Address:x16}, {nameof(File)}: {File}, {nameof(Line)}: {Line,4}, {nameof(Column)}: {Column,2}, {nameof(IsStatement)}: {Bool2Str(IsStatement),5}, {nameof(IsBasicBlock)}: {Bool2Str(IsBasicBlock),5}, {nameof(IsPrologueEnd)}: {Bool2Str(IsPrologueEnd),5}, {nameof(IsEpilogueBegin)}: {Bool2Str(IsEpilogueBegin),5}, {nameof(Isa)}: {Isa,3}, {nameof(Discriminator)}: {Discriminator,3}"; } private static string Bool2Str(bool value) => value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(); diff --git a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs index 5787802..a1f517f 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs @@ -104,14 +104,14 @@ public DwarfLineSequence RemoveLineSequenceAt(int index) protected override void Read(DwarfReader reader) { var log = reader.Log; - var startOfSection = reader.Offset; + var startOfSection = reader.Position; reader.OffsetToLineProgramTable.Add(startOfSection, this); var unitLength = reader.ReadUnitLength(); Is64BitEncoding = reader.Is64BitEncoding; AddressSize = reader.AddressSize; - var startPosition = reader.Offset; + var startPosition = reader.Position; var version = reader.ReadU16(); if (version < 2 || version >= 5) @@ -180,7 +180,7 @@ protected override void Read(DwarfReader reader) } } - var directoriesOffset = reader.Offset; + var directoriesOffset = reader.Position; var directories = new List(); while (true) { @@ -210,7 +210,7 @@ protected override void Read(DwarfReader reader) } } - var fileNamesOffset = reader.Offset; + var fileNamesOffset = reader.Position; bool printDumpHeader = true; while (true) { @@ -258,18 +258,18 @@ protected override void Read(DwarfReader reader) } var state = _stateLine; - state.Offset = reader.Offset; + state.Position = reader.Position; var firstFileName = FileNames.Count > 0 ? FileNames[0] : null; state.Reset(firstFileName, default_is_stmt != 0); var intFileNameCount = FileNames.Count; printDumpHeader = true; - var currentSequence = new DwarfLineSequence {Offset = state.Offset}; + var currentSequence = new DwarfLineSequence {Position = state.Position}; while (true) { - var currentLength = reader.Offset - startPosition; + var currentLength = reader.Position - startPosition; if (currentLength >= unitLength) { break; @@ -284,7 +284,7 @@ protected override void Read(DwarfReader reader) printDumpHeader = false; } - log.Write($" [0x{reader.Offset:x8}]"); + log.Write($" [0x{reader.Position:x8}]"); } var opcode = reader.ReadU8(); @@ -292,7 +292,7 @@ protected override void Read(DwarfReader reader) { case DwarfNative.DW_LNS_copy: currentSequence.Add(state.Clone()); - state.Offset = reader.Offset; + state.Position = reader.Position; state.SpecialReset(); if (log != null) { @@ -431,10 +431,10 @@ protected override void Read(DwarfReader reader) break; case 0: var sizeOfExtended = reader.ReadULEB128(); - var lengthOffset = reader.Offset; - var endOffset = reader.Offset + sizeOfExtended; + var lengthOffset = reader.Position; + var endOffset = reader.Position + sizeOfExtended; bool hasValidOpCode = true; - if (reader.Offset < endOffset) + if (reader.Position < endOffset) { var sub_opcode = reader.ReadU8(); @@ -448,12 +448,12 @@ protected override void Read(DwarfReader reader) { case DwarfNative.DW_LNE_end_sequence: currentSequence.Add(state.Clone()); - currentSequence.Size = reader.Offset - currentSequence.Offset; + currentSequence.Size = reader.Position - currentSequence.Position; AddLineSequence(currentSequence); - currentSequence = new DwarfLineSequence() {Offset = reader.Offset}; + currentSequence = new DwarfLineSequence() {Position = reader.Position}; - state.Offset = reader.Offset; + state.Position = reader.Position; state.Reset(firstFileName, default_is_stmt != 0); if (log != null) { @@ -515,12 +515,12 @@ protected override void Read(DwarfReader reader) } // Log a warning if the end offset doesn't match what we are expecting - if (hasValidOpCode && reader.Offset != endOffset) + if (hasValidOpCode && reader.Position != endOffset) { reader.Diagnostics.Warning(DiagnosticId.DWARF_WRN_InvalidExtendedOpCodeLength, $"Invalid length {sizeOfExtended} at offset 0x{lengthOffset:x}"); } - reader.Offset = endOffset; + reader.Position = endOffset; break; default: if (opcode < opcode_base) @@ -577,7 +577,7 @@ protected override void Read(DwarfReader reader) } currentSequence.Add(state.Clone()); - state.Offset = reader.Offset; + state.Position = reader.Position; state.SpecialReset(); } @@ -757,7 +757,7 @@ private void RecordDirectory(string directoryName, ref ulong sizeOf, out uint di protected override void Write(DwarfWriter writer) { - var startOffset = writer.Offset; + var startOffset = writer.Position; writer.Is64BitEncoding = Is64BitEncoding; writer.WriteUnitLength(Size - DwarfHelper.SizeOfUnitLength(Is64BitEncoding)); @@ -765,7 +765,7 @@ protected override void Write(DwarfWriter writer) writer.WriteU16(Version); writer.WriteUIntFromEncoding(HeaderLength); - var startOfHeader = writer.Offset; + var startOfHeader = writer.Position; writer.WriteU8(MinimumInstructionLength); @@ -818,12 +818,12 @@ protected override void Write(DwarfWriter writer) // empty string writer.WriteU8(0); - var headSizeWritten = writer.Offset - startOfHeader; + var headSizeWritten = writer.Position - startOfHeader; Debug.Assert(HeaderLength == headSizeWritten, $"Expected Header Length: {HeaderLength} != Written Header Length: {headSizeWritten}"); WriteDebugLineOpCodes(writer, OpCodeBase); - Debug.Assert(Size == writer.Offset - startOffset, $"Expected Size: {Size} != Written Size: {writer.Offset - startOffset}"); + Debug.Assert(Size == writer.Position - startOffset, $"Expected Size: {Size} != Written Size: {writer.Position - startOffset}"); } private void WriteDebugLineOpCodes(DwarfWriter writer, uint opCodeBase) @@ -886,7 +886,7 @@ private void WriteDebugLineOpCodes(DwarfWriter writer, uint opCodeBase) out isaChanged, out isDiscriminatorChanged); - Debug.Assert(debugLine.Offset == writer.Offset, $"Expected Debug Line Offset: {debugLine.Offset} != Written Offset: {writer.Offset}"); + Debug.Assert(debugLine.Position == writer.Position, $"Expected Debug Line Offset: {debugLine.Position} != Written Offset: {writer.Position}"); // DW_LNS_set_column if (deltaColumn != 0) @@ -1093,7 +1093,7 @@ private void WriteDebugLineOpCodes(DwarfWriter writer, uint opCodeBase) writer.WriteU8(DwarfNative.DW_LNS_copy); } - Debug.Assert(debugLine.Size == writer.Offset - debugLine.Offset, $"Expected Debug Line Size: {debugLine.Size} != Written Size: {writer.Offset - debugLine.Offset}"); + Debug.Assert(debugLine.Size == writer.Position - debugLine.Position, $"Expected Debug Line Size: {debugLine.Size} != Written Size: {writer.Position - debugLine.Position}"); } } } @@ -1123,7 +1123,7 @@ private void LayoutDebugLineOpCodes(ref ulong sizeOf, uint opCodeBase) { var lines = lineSequence.Lines; - lineSequence.Offset = Offset + sizeOf; + lineSequence.Position = Position + sizeOf; hasSetAddress = false; for (var lineIndex = 0; lineIndex < lines.Count; lineIndex++) @@ -1159,7 +1159,7 @@ private void LayoutDebugLineOpCodes(ref ulong sizeOf, uint opCodeBase) out isaChanged, out isDiscriminatorChanged); - debugLine.Offset = Offset + sizeOf; + debugLine.Position = Position + sizeOf; // DW_LNS_set_column if (deltaColumn != 0) @@ -1357,9 +1357,9 @@ private void LayoutDebugLineOpCodes(ref ulong sizeOf, uint opCodeBase) sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_copy); } - debugLine.Size = Offset + sizeOf - debugLine.Offset; + debugLine.Size = Position + sizeOf - debugLine.Position; } - lineSequence.Size = Offset + sizeOf - lineSequence.Offset; + lineSequence.Size = Position + sizeOf - lineSequence.Position; } } diff --git a/src/LibObjectFile/Dwarf/DwarfLineSection.cs b/src/LibObjectFile/Dwarf/DwarfLineSection.cs index a259852..22d094d 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSection.cs @@ -38,10 +38,10 @@ public DwarfLineProgramTable RemoveLineProgramTableAt(int index) protected override void Read(DwarfReader reader) { - while (reader.Offset < reader.Length) + while (reader.Position < reader.Length) { var programTable = new DwarfLineProgramTable(); - programTable.Offset = reader.Offset; + programTable.Position = reader.Position; programTable.ReadInternal(reader); AddLineProgramTable(programTable); } @@ -63,7 +63,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) foreach (var dwarfLineProgramTable in _tables) { - dwarfLineProgramTable.Offset = Offset + sizeOf; + dwarfLineProgramTable.Position = Position + sizeOf; dwarfLineProgramTable.UpdateLayoutInternal(layoutContext); sizeOf += dwarfLineProgramTable.Size; } @@ -72,14 +72,14 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) protected override void Write(DwarfWriter writer) { - var startOffset = writer.Offset; + var startOffset = writer.Position; foreach (var dwarfLineProgramTable in _tables) { dwarfLineProgramTable.WriteInternal(writer); } - Debug.Assert(Size == writer.Offset - startOffset, $"Expected Size: {Size} != Written Size: {writer.Offset - startOffset}"); + Debug.Assert(Size == writer.Position - startOffset, $"Expected Size: {Size} != Written Size: {writer.Position - startOffset}"); } public override string ToString() diff --git a/src/LibObjectFile/Dwarf/DwarfLocationList.cs b/src/LibObjectFile/Dwarf/DwarfLocationList.cs index e84385e..4d872f8 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationList.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationList.cs @@ -36,11 +36,11 @@ public DwarfLocationListEntry RemoveLocationListEntryAt(int index) protected override void UpdateLayout(DwarfLayoutContext layoutContext) { - var endOffset = Offset; + var endOffset = Position; foreach (var locationListEntry in _locationListEntries) { - locationListEntry.Offset = endOffset; + locationListEntry.Position = endOffset; locationListEntry.UpdateLayoutInternal(layoutContext); endOffset += locationListEntry.Size; } @@ -48,14 +48,14 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) // End of list endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); - Size = endOffset - Offset; + Size = endOffset - Position; } protected override void Read(DwarfReader reader) { - reader.OffsetToLocationList.Add(reader.Offset, this); + reader.OffsetToLocationList.Add(reader.Position, this); - while (reader.Offset < reader.Length) + while (reader.Position < reader.Length) { var locationListEntry = new DwarfLocationListEntry(); locationListEntry.ReadInternal(reader); diff --git a/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs b/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs index 1385032..c127e02 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs @@ -42,17 +42,17 @@ protected override void Read(DwarfReader reader) protected override void UpdateLayout(DwarfLayoutContext layoutContext) { - var endOffset = Offset; + var endOffset = Position; endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); if (Expression != null) { - Expression.Offset = endOffset; + Expression.Position = endOffset; Expression.UpdateLayoutInternal(layoutContext, inLocationSection: true); endOffset += Expression.Size; } - Size = endOffset - Offset; + Size = endOffset - Position; } protected override void Write(DwarfWriter writer) diff --git a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs index e2057af..b0ab892 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs @@ -37,10 +37,10 @@ public DwarfLocationList RemoveLineProgramTableAt(int index) protected override void Read(DwarfReader reader) { - while (reader.Offset < reader.Length) + while (reader.Position < reader.Length) { var locationList = new DwarfLocationList(); - locationList.Offset = reader.Offset; + locationList.Position = reader.Position; locationList.ReadInternal(reader); AddLocationList(locationList); } @@ -62,7 +62,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) foreach (var locationList in _locationLists) { - locationList.Offset = Offset + sizeOf; + locationList.Position = Position + sizeOf; locationList.UpdateLayoutInternal(layoutContext); sizeOf += locationList.Size; } @@ -71,14 +71,14 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) protected override void Write(DwarfWriter writer) { - var startOffset = writer.Offset; + var startOffset = writer.Position; foreach (var locationList in _locationLists) { locationList.WriteInternal(writer); } - Debug.Assert(Size == writer.Offset - startOffset, $"Expected Size: {Size} != Written Size: {writer.Offset - startOffset}"); + Debug.Assert(Size == writer.Position - startOffset, $"Expected Size: {Size} != Written Size: {writer.Position - startOffset}"); } public override string ToString() diff --git a/src/LibObjectFile/Dwarf/DwarfOperation.cs b/src/LibObjectFile/Dwarf/DwarfOperation.cs index a28eddb..84ee680 100644 --- a/src/LibObjectFile/Dwarf/DwarfOperation.cs +++ b/src/LibObjectFile/Dwarf/DwarfOperation.cs @@ -23,7 +23,7 @@ public class DwarfOperation : DwarfObject protected override void Read(DwarfReader reader) { - Offset = reader.Offset; + Position = reader.Position; var kind = new DwarfOperationKindEx(reader.ReadU8()); Kind = kind; @@ -422,12 +422,12 @@ protected override void Read(DwarfReader reader) } // Store the size of the current op - Size = reader.Offset - Offset; + Size = reader.Position - Position; } protected override void UpdateLayout(DwarfLayoutContext layoutContext) { - var endOffset = Offset; + var endOffset = Position; // 1 byte per opcode endOffset += 1; @@ -667,7 +667,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) } else if (Operand0 is DwarfExpression expr) { - expr.Offset = endOffset; + expr.Position = endOffset; expr.UpdateLayoutInternal(layoutContext); endOffset += DwarfHelper.SizeOfULEB128(expr.Size); } @@ -744,7 +744,7 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) throw new NotSupportedException($"The {nameof(DwarfOperationKind)} {Kind} is not supported"); } - Size = endOffset - Offset; + Size = endOffset - Position; } private ulong SizeOfDIEReference(DwarfLayoutContext context) @@ -757,9 +757,9 @@ private ulong SizeOfDIEReference(DwarfLayoutContext context) { // TODO: check that die reference is within this section - if (die.Offset < Offset) + if (die.Position < Position) { - return DwarfHelper.SizeOfULEB128(die.Offset); + return DwarfHelper.SizeOfULEB128(die.Position); } else { @@ -797,8 +797,8 @@ private ulong SizeOfEncodedValue(DwarfOperationKindEx kind, ulong value, byte si protected override void Write(DwarfWriter writer) { - var startOpOffset = Offset; - Debug.Assert(startOpOffset == Offset); + var startOpOffset = Position; + Debug.Assert(startOpOffset == Position); writer.WriteU8((byte)Kind); @@ -881,7 +881,7 @@ protected override void Write(DwarfWriter writer) case DwarfOperationKind.Bra: case DwarfOperationKind.Skip: - writer.WriteU16((ushort)((long)Offset + 2 - (long)((DwarfOperation)Operand0!).Offset)); + writer.WriteU16((ushort)((long)Position + 2 - (long)((DwarfOperation)Operand0!).Position)); break; case DwarfOperationKind.Lit0: @@ -992,15 +992,15 @@ protected override void Write(DwarfWriter writer) break; case DwarfOperationKind.Call2: - writer.WriteU16((ushort)((DwarfDIE)Operand0!).Offset); + writer.WriteU16((ushort)((DwarfDIE)Operand0!).Position); break; case DwarfOperationKind.Call4: - writer.WriteU32((uint)((DwarfDIE)Operand0!).Offset); + writer.WriteU32((uint)((DwarfDIE)Operand0!).Position); break; case DwarfOperationKind.CallRef: - writer.WriteUInt(((DwarfDIE)Operand0!).Offset); + writer.WriteUInt(((DwarfDIE)Operand0!).Position); break; case DwarfOperationKind.BitPiece: @@ -1019,7 +1019,7 @@ protected override void Write(DwarfWriter writer) case DwarfOperationKind.ImplicitPointer: case DwarfOperationKind.GNUImplicitPointer: { - ulong offset = ((DwarfDIE)Operand0!).Offset; + ulong offset = ((DwarfDIE)Operand0!).Position; // a reference to a debugging information entry that describes the dereferenced object’s value if (writer.CurrentUnit!.Version == 2) { @@ -1051,7 +1051,7 @@ protected override void Write(DwarfWriter writer) // The first operand is an unsigned LEB128 integer that represents the offset // of a debugging information entry in the current compilation unit, which // must be a DW_TAG_base_type entry that provides the type of the constant provided - writer.WriteULEB128(((DwarfDIE)Operand0!).Offset); + writer.WriteULEB128(((DwarfDIE)Operand0!).Position); WriteEncodedValue(writer, Kind, Operand1.U64, (byte)Operand2.U64); break; } @@ -1068,7 +1068,7 @@ protected override void Write(DwarfWriter writer) // The second operand is an unsigned LEB128 number that represents the offset // of a debugging information entry in the current compilation unit - writer.WriteULEB128(((DwarfDIE)Operand0!).Offset); + writer.WriteULEB128(((DwarfDIE)Operand0!).Position); break; } @@ -1085,7 +1085,7 @@ protected override void Write(DwarfWriter writer) // The second operand is an unsigned LEB128 number that represents the offset // of a debugging information entry in the current compilation unit - writer.WriteULEB128(((DwarfDIE)Operand0!).Offset); + writer.WriteULEB128(((DwarfDIE)Operand0!).Position); break; } @@ -1093,7 +1093,7 @@ protected override void Write(DwarfWriter writer) case DwarfOperationKind.GNUConvert: case DwarfOperationKind.Reinterpret: case DwarfOperationKind.GNUReinterpret: - writer.WriteULEB128(((DwarfDIE)Operand0!).Offset); + writer.WriteULEB128(((DwarfDIE)Operand0!).Position); break; case DwarfOperationKind.GNUPushTlsAddress: @@ -1112,7 +1112,7 @@ protected override void Write(DwarfWriter writer) throw new NotSupportedException($"The {nameof(DwarfOperationKind)} {Kind} is not supported"); } - Debug.Assert(writer.Offset - startOpOffset == Size); + Debug.Assert(writer.Position - startOpOffset == Size); } private static ulong ReadEncodedValue(DwarfReader reader, DwarfOperationKind kind, out byte size) diff --git a/src/LibObjectFile/Dwarf/DwarfPrinter.cs b/src/LibObjectFile/Dwarf/DwarfPrinter.cs index f4b2e59..604ed93 100644 --- a/src/LibObjectFile/Dwarf/DwarfPrinter.cs +++ b/src/LibObjectFile/Dwarf/DwarfPrinter.cs @@ -28,7 +28,7 @@ public static void Print(this DwarfAbbreviation abbreviation, TextWriter writer) writer.WriteLine(); - writer.WriteLine($" Number TAG (0x{abbreviation.Offset})"); + writer.WriteLine($" Number TAG (0x{abbreviation.Position})"); foreach (var item in abbreviation.Items) { @@ -78,10 +78,10 @@ public static void Print(this DwarfUnit unit, TextWriter writer) if (writer == null) throw new ArgumentNullException(nameof(writer)); writer.WriteLine("Contents of the .debug_info section:"); writer.WriteLine(); - writer.WriteLine($" Compilation Unit @ offset 0x{unit.Offset:x}:"); + writer.WriteLine($" Compilation Unit @ offset 0x{unit.Position:x}:"); writer.WriteLine($" Length: 0x{unit.UnitLength:x}"); writer.WriteLine($" Version: {unit.Version}"); - writer.WriteLine($" Abbrev Offset: 0x{unit.Abbreviation?.Offset ?? 0:x}"); + writer.WriteLine($" Abbrev Offset: 0x{unit.Abbreviation?.Position ?? 0:x}"); writer.WriteLine($" Pointer Size: {(uint)unit.AddressSize}"); if (unit.Root != null) { @@ -93,7 +93,7 @@ public static void Print(this DwarfDIE die, TextWriter writer, int level = 0) { if (writer == null) throw new ArgumentNullException(nameof(writer)); - writer.WriteLine($" <{level}><{die.Offset:x}>: Abbrev Number: {die.Abbrev!.Code} ({die.Tag})"); + writer.WriteLine($" <{level}><{die.Position:x}>: Abbrev Number: {die.Abbrev!.Code} ({die.Tag})"); foreach (var attr in die.Attributes) { @@ -101,7 +101,7 @@ public static void Print(this DwarfDIE die, TextWriter writer, int level = 0) switch (attr.ValueAsObject) { case DwarfDIE dieRef: - attrValue = $"<0x{dieRef.Offset:x}>"; + attrValue = $"<0x{dieRef.Position:x}>"; break; case string str: attrValue = str; @@ -133,7 +133,7 @@ public static void Print(this DwarfDIE die, TextWriter writer, int level = 0) } } - writer.WriteLine($" <{attr.Offset:x}> {attr.Kind,-18} : {attrValue}"); + writer.WriteLine($" <{attr.Position:x}> {attr.Kind,-18} : {attrValue}"); } foreach (var child in die.Children) diff --git a/src/LibObjectFile/Dwarf/DwarfReader.cs b/src/LibObjectFile/Dwarf/DwarfReader.cs index 9e40132..922c86a 100644 --- a/src/LibObjectFile/Dwarf/DwarfReader.cs +++ b/src/LibObjectFile/Dwarf/DwarfReader.cs @@ -49,8 +49,8 @@ internal DwarfReader(DwarfReaderContext context, DwarfFile file, DiagnosticBag d internal void PushDIE(DwarfDIE die) { - _registeredDIEPerCompilationUnit.Add(die.Offset - CurrentUnit!.Offset, die); - _registeredDIEPerSection.Add(die.Offset, die); + _registeredDIEPerCompilationUnit.Add(die.Position - CurrentUnit!.Position, die); + _registeredDIEPerSection.Add(die.Position, die); _stack.Push(die); } diff --git a/src/LibObjectFile/Dwarf/DwarfStringTable.cs b/src/LibObjectFile/Dwarf/DwarfStringTable.cs index 61314ac..2ff4a1d 100644 --- a/src/LibObjectFile/Dwarf/DwarfStringTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfStringTable.cs @@ -74,7 +74,7 @@ public ulong GetOrCreateString(string? text) protected override void Read(DwarfReader reader) { Stream = reader.ReadAsStream((ulong) reader.Stream.Length); - Size = reader.Offset - Offset; + Size = reader.Position - Position; } protected override void Write(DwarfWriter writer) diff --git a/src/LibObjectFile/Dwarf/DwarfUnit.cs b/src/LibObjectFile/Dwarf/DwarfUnit.cs index 30e5a45..31f9331 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnit.cs @@ -88,7 +88,7 @@ protected override void ValidateParent(ObjectFileNodeBase parent) protected override void UpdateLayout(DwarfLayoutContext layoutContext) { - var offset = this.Offset; + var offset = this.Position; // 1. unit_length offset += DwarfHelper.SizeOfUnitLength(Is64BitEncoding); @@ -118,14 +118,14 @@ protected override void UpdateLayout(DwarfLayoutContext layoutContext) Root.UpdateAbbreviationItem(layoutContext); - DebugAbbreviationOffset = Abbreviation.Offset; + DebugAbbreviationOffset = Abbreviation.Position; - Root.Offset = offset; + Root.Position = offset; Root.UpdateLayoutInternal(layoutContext); offset += Root.Size; } - Size = offset - Offset; + Size = offset - Position; UnitLength = offset - offsetAfterUnitLength; } @@ -141,7 +141,7 @@ protected override void Read(DwarfReader reader) foreach (var abbreviation in reader.File.AbbreviationTable.Abbreviations) { - if (abbreviation.Offset == DebugAbbreviationOffset) + if (abbreviation.Position == DebugAbbreviationOffset) { Abbreviation = abbreviation; break; @@ -152,19 +152,19 @@ protected override void Read(DwarfReader reader) reader.ResolveAttributeReferenceWithinCompilationUnit(); - Size = reader.Offset - Offset; + Size = reader.Position - Position; } internal static DwarfUnit? ReadInstance(DwarfReader reader, out ulong offsetEndOfUnit) { - var startOffset = reader.Offset; + var startOffset = reader.Position; DwarfUnit? unit = null; // 1. unit_length var unit_length = reader.ReadUnitLength(); - offsetEndOfUnit = (ulong)reader.Offset + unit_length; + offsetEndOfUnit = (ulong)reader.Position + unit_length; // 2. version (uhalf) var version = reader.ReadU16(); @@ -198,7 +198,7 @@ protected override void Read(DwarfReader reader) unit.UnitLength = unit_length; unit.Kind = unitKind; unit.Is64BitEncoding = reader.Is64BitEncoding; - unit.Offset = startOffset; + unit.Position = startOffset; unit.Version = version; unit.ReadHeader(reader); @@ -210,14 +210,14 @@ protected override void Read(DwarfReader reader) protected override void Write(DwarfWriter writer) { - var startOffset = writer.Offset; - Debug.Assert(Offset == writer.Offset); + var startOffset = writer.Position; + Debug.Assert(Position == writer.Position); // 1. unit_length Is64BitEncoding = Is64BitEncoding; writer.WriteUnitLength(UnitLength); - var offsetAfterUnitLength = writer.Offset; + var offsetAfterUnitLength = writer.Position; // 2. version (uhalf) writer.WriteU16(Version); @@ -234,8 +234,8 @@ protected override void Write(DwarfWriter writer) Root?.WriteInternal(writer); // TODO: check size of unit length - Debug.Assert(Size == writer.Offset - startOffset); - Debug.Assert(UnitLength == writer.Offset - offsetAfterUnitLength); + Debug.Assert(Size == writer.Position - startOffset); + Debug.Assert(UnitLength == writer.Position - offsetAfterUnitLength); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfWriter.cs b/src/LibObjectFile/Dwarf/DwarfWriter.cs index d12d32f..ee8d8fa 100644 --- a/src/LibObjectFile/Dwarf/DwarfWriter.cs +++ b/src/LibObjectFile/Dwarf/DwarfWriter.cs @@ -22,7 +22,7 @@ public void RecordRelocation(DwarfRelocationTarget target, DwarfAddressSize addr if (CurrentSection is DwarfRelocatableSection relocSection) { - relocSection.Relocations.Add(new DwarfRelocation(Offset, target, addressSize, address)); + relocSection.Relocations.Add(new DwarfRelocation(Position, target, addressSize, address)); } else diff --git a/src/LibObjectFile/Elf/ElfFilePart.cs b/src/LibObjectFile/Elf/ElfFilePart.cs index 13db437..b879196 100644 --- a/src/LibObjectFile/Elf/ElfFilePart.cs +++ b/src/LibObjectFile/Elf/ElfFilePart.cs @@ -35,7 +35,7 @@ public ElfFilePart(ElfSection section) { Section = section ?? throw new ArgumentNullException(nameof(section)); Debug.Assert(section.Size > 0); - StartOffset = section.Offset; + StartOffset = section.Position; EndOffset = StartOffset + Section.Size - 1; } diff --git a/src/LibObjectFile/Elf/ElfObjectFile.cs b/src/LibObjectFile/Elf/ElfObjectFile.cs index b7bb751..38d227b 100644 --- a/src/LibObjectFile/Elf/ElfObjectFile.cs +++ b/src/LibObjectFile/Elf/ElfObjectFile.cs @@ -242,13 +242,13 @@ public override unsafe void UpdateLayout(DiagnosticBag diagnostics) var align = section.Alignment == 0 ? 1 : section.Alignment; offset = AlignHelper.AlignToUpper(offset, align); - section.Offset = offset; + section.Position = offset; if (section is ElfProgramHeaderTable programHeaderTable) { if (Segments.Count > 0) { - Layout.OffsetOfProgramHeaderTable = section.Offset; + Layout.OffsetOfProgramHeaderTable = section.Position; Layout.SizeOfProgramHeaderEntry = (ushort) section.TableEntrySize; programHeaderTableFoundAndUpdated = true; } diff --git a/src/LibObjectFile/Elf/ElfPrinter.cs b/src/LibObjectFile/Elf/ElfPrinter.cs index 71f219a..288228f 100644 --- a/src/LibObjectFile/Elf/ElfPrinter.cs +++ b/src/LibObjectFile/Elf/ElfPrinter.cs @@ -98,7 +98,7 @@ public static void PrintSectionHeaders(ElfObjectFile elf, TextWriter writer) { var section = elf.Sections[i]; if (section.IsShadow) continue; - writer.WriteLine($" [{section.SectionIndex,2:#0}] {GetElfSectionName(section),-17} {GetElfSectionType(section.Type),-15} {section.VirtualAddress:x16} {section.Offset:x6} {section.Size:x6} {section.TableEntrySize:x2} {GetElfSectionFlags(section.Flags),3} {section.Link.GetIndex(),2} {section.Info.GetIndex(),3} {section.Alignment,2}"); + writer.WriteLine($" [{section.SectionIndex,2:#0}] {GetElfSectionName(section),-17} {GetElfSectionType(section.Type),-15} {section.VirtualAddress:x16} {section.Position:x6} {section.Size:x6} {section.TableEntrySize:x2} {GetElfSectionFlags(section.Flags),3} {section.Link.GetIndex(),2} {section.Info.GetIndex(),3} {section.Alignment,2}"); } writer.WriteLine(@"Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), @@ -141,7 +141,7 @@ public static void PrintProgramHeaders(ElfObjectFile elf, TextWriter writer) for (int i = 0; i < elf.Segments.Count; i++) { var phdr = elf.Segments[i]; - writer.WriteLine($" {GetElfSegmentType(phdr.Type),-14} 0x{phdr.Offset:x6} 0x{phdr.VirtualAddress:x16} 0x{phdr.PhysicalAddress:x16} 0x{phdr.Size:x6} 0x{phdr.SizeInMemory:x6} {GetElfSegmentFlags(phdr.Flags),3} 0x{phdr.Alignment:x}"); + writer.WriteLine($" {GetElfSegmentType(phdr.Type),-14} 0x{phdr.Position:x6} 0x{phdr.VirtualAddress:x16} 0x{phdr.PhysicalAddress:x16} 0x{phdr.Size:x6} 0x{phdr.SizeInMemory:x6} {GetElfSegmentFlags(phdr.Flags),3} 0x{phdr.Alignment:x}"); } if (elf.Segments.Count > 0 && elf.VisibleSectionCount > 0 && elf.SectionHeaderStringTable != null) @@ -183,7 +183,7 @@ public static void PrintRelocations(ElfObjectFile elf, TextWriter writer) var relocTable = (ElfRelocationTable) section; writer.WriteLine(); - writer.WriteLine($"Relocation section {(elf.SectionHeaderStringTable == null ? "0" : $"'{section.Name}'")} at offset 0x{section.Offset:x} contains {relocTable.Entries.Count} {(relocTable.Entries.Count > 1 ? "entries" : "entry")}:"); + writer.WriteLine($"Relocation section {(elf.SectionHeaderStringTable == null ? "0" : $"'{section.Name}'")} at offset 0x{section.Position:x} contains {relocTable.Entries.Count} {(relocTable.Entries.Count > 1 ? "entries" : "entry")}:"); if (elf.FileClass == ElfFileClass.Is32) { @@ -488,15 +488,15 @@ sections at all. */ /* Any section besides one of type SHT_NOBITS must have file offsets within the segment. */ && (section.Type == ElfSectionType.NoBits - || ((section).Offset >= segment.Offset + || ((section).Position >= segment.Position && (!(isStrict) - || (section.Offset - segment.Offset + || (section.Position - segment.Position <= segment.Size - 1)) - && ((section.Offset - segment.Offset + && ((section.Position - segment.Position + GetSectionSize(section, segment)) <= segment.Size))) @@ -524,9 +524,9 @@ offsets within the segment. */ || segment.SizeInMemory == 0 || ((section.Type == ElfSectionType.NoBits - || (section.Offset > segment.Offset + || (section.Position > segment.Position - && (section.Offset - segment.Offset + && (section.Position - segment.Position < segment.Size))) diff --git a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs index b6d629c..b7160f6 100644 --- a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs +++ b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs @@ -161,7 +161,7 @@ private ElfSegment ReadProgramHeader32(int phdrIndex) return new ElfSegment { Type = new ElfSegmentType(_decoder.Decode(hdr.p_type)), - Offset =_decoder.Decode(hdr.p_offset), + Position =_decoder.Decode(hdr.p_offset), VirtualAddress = _decoder.Decode(hdr.p_vaddr), PhysicalAddress = _decoder.Decode(hdr.p_paddr), Size = _decoder.Decode(hdr.p_filesz), @@ -182,7 +182,7 @@ private ElfSegment ReadProgramHeader64(int phdrIndex) return new ElfSegment { Type = new ElfSegmentType(_decoder.Decode(hdr.p_type)), - Offset = _decoder.Decode(hdr.p_offset), + Position = _decoder.Decode(hdr.p_offset), VirtualAddress = _decoder.Decode(hdr.p_vaddr), PhysicalAddress = _decoder.Decode(hdr.p_paddr), Size = _decoder.Decode(hdr.p_filesz), @@ -273,7 +273,7 @@ private ElfSection ReadSectionTableEntry32(uint sectionIndex) section.Type = (ElfSectionType)_decoder.Decode(rawSection.sh_type); section.Flags = (ElfSectionFlags)_decoder.Decode(rawSection.sh_flags); section.VirtualAddress = _decoder.Decode(rawSection.sh_addr); - section.Offset = _decoder.Decode(rawSection.sh_offset); + section.Position = _decoder.Decode(rawSection.sh_offset); section.Alignment = _decoder.Decode(rawSection.sh_addralign); section.Link = new ElfSectionLink(_decoder.Decode(rawSection.sh_link)); section.Info = new ElfSectionLink(_decoder.Decode(rawSection.sh_info)); @@ -307,7 +307,7 @@ private ElfSection ReadSectionTableEntry64(uint sectionIndex) section.Type = (ElfSectionType)_decoder.Decode(rawSection.sh_type); section.Flags = (ElfSectionFlags)_decoder.Decode(rawSection.sh_flags); section.VirtualAddress = _decoder.Decode(rawSection.sh_addr); - section.Offset = _decoder.Decode(rawSection.sh_offset); + section.Position = _decoder.Decode(rawSection.sh_offset); section.Alignment = _decoder.Decode(rawSection.sh_addralign); section.Link = new ElfSectionLink(_decoder.Decode(rawSection.sh_link)); section.Info = new ElfSectionLink(_decoder.Decode(rawSection.sh_info)); @@ -433,7 +433,7 @@ private void VerifyAndFixProgramHeadersAndSections() if (_hasValidSectionStringTable) { - Stream.Position = (long)ObjectFile.SectionHeaderStringTable!.Offset; + Stream.Position = (long)ObjectFile.SectionHeaderStringTable!.Position; ObjectFile.SectionHeaderStringTable.ReadInternal(this); } @@ -480,7 +480,7 @@ private void VerifyAndFixProgramHeadersAndSections() if (section.HasContent) { - Stream.Position = (long)section.Offset; + Stream.Position = (long)section.Position; section.ReadInternal(this); } } @@ -496,7 +496,7 @@ private void VerifyAndFixProgramHeadersAndSections() { var programHeaderTable = new ElfProgramHeaderTable() { - Offset = Layout.OffsetOfProgramHeaderTable, + Position = Layout.OffsetOfProgramHeaderTable, }; // Add the shadow section ElfProgramHeaderTable @@ -528,12 +528,12 @@ private void VerifyAndFixProgramHeadersAndSections() var section = orderedSections[i]; section.Verify(this.Diagnostics); - if (lastOffset > 0 && section.Offset > lastOffset) + if (lastOffset > 0 && section.Position > lastOffset) { - if (section.Offset > lastOffset) + if (section.Position > lastOffset) { // Create parts for the segment - fileParts.CreateParts(lastOffset + 1, section.Offset - 1); + fileParts.CreateParts(lastOffset + 1, section.Position - 1); hasShadowSections = true; } } @@ -545,15 +545,15 @@ private void VerifyAndFixProgramHeadersAndSections() // Collect sections parts fileParts.Insert(new ElfFilePart(section)); - lastOffset = section.Offset + section.Size - 1; + lastOffset = section.Position + section.Size - 1; // Verify overlapping sections and generate and error if (i + 1 < orderedSections.Count) { var otherSection = orderedSections[i + 1]; - if (otherSection.Offset < section.Offset + section.Size) + if (otherSection.Position < section.Position + section.Size) { - Diagnostics.Warning(DiagnosticId.ELF_ERR_InvalidOverlappingSections, $"The section {section} [{section.Offset} : {section.Offset + section.Size - 1}] is overlapping with the section {otherSection} [{otherSection.Offset} : {otherSection.Offset + otherSection.Size - 1}]"); + Diagnostics.Warning(DiagnosticId.ELF_ERR_InvalidOverlappingSections, $"The section {section} [{section.Position} : {section.Position + section.Size - 1}] is overlapping with the section {otherSection} [{otherSection.Position} : {otherSection.Position + otherSection.Size - 1}]"); } } } @@ -565,13 +565,13 @@ private void VerifyAndFixProgramHeadersAndSections() { if (segment.Size == 0) continue; - var segmentEndOffset = segment.Offset + segment.Size - 1; + var segmentEndOffset = segment.Position + segment.Size - 1; foreach (var section in orderedSections) { if (section.Size == 0 || !section.HasContent) continue; - var sectionEndOffset = section.Offset + section.Size - 1; - if (segment.Offset == section.Offset && segmentEndOffset == sectionEndOffset) + var sectionEndOffset = section.Position + section.Size - 1; + if (segment.Position == section.Position && segmentEndOffset == sectionEndOffset) { // Single case: segment == section // If we found a section, we will bind the program header to this section @@ -584,7 +584,7 @@ private void VerifyAndFixProgramHeadersAndSections() if (segment.Range.IsEmpty) { - var offset = segment.Offset; + var offset = segment.Position; // If a segment offset is set to 0, we need to take into // account the fact that the Elf header is already being handled @@ -620,12 +620,12 @@ private void VerifyAndFixProgramHeadersAndSections() var shadowSection = new ElfBinaryShadowSection() { Name = ".shadow." + shadowCount, - Offset = part.StartOffset, + Position = part.StartOffset, Size = part.EndOffset - part.StartOffset + 1 }; shadowCount++; - Stream.Position = (long)shadowSection.Offset; + Stream.Position = (long)shadowSection.Position; shadowSection.ReadInternal(this); // Insert the shadow section with this order @@ -653,14 +653,14 @@ private void VerifyAndFixProgramHeadersAndSections() if (segment.Size == 0) continue; if (!segment.Range.IsEmpty) continue; - var segmentEndOffset = segment.Offset + segment.Size - 1; + var segmentEndOffset = segment.Position + segment.Size - 1; for (var i = 0; i < orderedSections.Count; i++) { var section = orderedSections[i]; if (section.Size == 0 || !section.HasContent) continue; - var sectionEndOffset = section.Offset + section.Size - 1; - if (segment.Offset >= section.Offset && segment.Offset <= sectionEndOffset) + var sectionEndOffset = section.Position + section.Size - 1; + if (segment.Position >= section.Position && segment.Position <= sectionEndOffset) { ElfSection beginSection = section; ElfSection? endSection = null; @@ -669,9 +669,9 @@ private void VerifyAndFixProgramHeadersAndSections() var nextSection = orderedSections[j]; if (nextSection.Size == 0 || !nextSection.HasContent) continue; - sectionEndOffset = nextSection.Offset + nextSection.Size - 1; + sectionEndOffset = nextSection.Position + nextSection.Size - 1; - if (segmentEndOffset >= nextSection.Offset && segmentEndOffset <= sectionEndOffset) + if (segmentEndOffset >= nextSection.Position && segmentEndOffset <= sectionEndOffset) { endSection = nextSection; break; @@ -685,7 +685,7 @@ private void VerifyAndFixProgramHeadersAndSections() } else { - segment.Range = new ElfSegmentRange(beginSection, segment.Offset - beginSection.Offset, endSection, (long)(segmentEndOffset - endSection.Offset)); + segment.Range = new ElfSegmentRange(beginSection, segment.Position - beginSection.Position, endSection, (long)(segmentEndOffset - endSection.Position)); } segment.OffsetKind = ValueKind.Auto; @@ -848,7 +848,7 @@ public override ushort Decode(ElfNative.Elf64_Versym src) private static int CompareSectionOffsetsAndSizes(ElfSection left, ElfSection right) { - int result = left.Offset.CompareTo(right.Offset); + int result = left.Position.CompareTo(right.Position); if (result == 0) { result = left.Size.CompareTo(right.Size); diff --git a/src/LibObjectFile/Elf/ElfSegment.cs b/src/LibObjectFile/Elf/ElfSegment.cs index bb9f54a..7d52bc0 100644 --- a/src/LibObjectFile/Elf/ElfSegment.cs +++ b/src/LibObjectFile/Elf/ElfSegment.cs @@ -56,7 +56,7 @@ public override void UpdateLayout(DiagnosticBag diagnostics) if (OffsetKind == ValueKind.Auto) { - Offset = Range.Offset; + Position = Range.Offset; } if (Range.IsEmpty) diff --git a/src/LibObjectFile/Elf/ElfSegmentRange.cs b/src/LibObjectFile/Elf/ElfSegmentRange.cs index cb4bd15..88035b3 100644 --- a/src/LibObjectFile/Elf/ElfSegmentRange.cs +++ b/src/LibObjectFile/Elf/ElfSegmentRange.cs @@ -70,7 +70,7 @@ public ElfSegmentRange(ElfSection beginSection, ulong beginOffset, ElfSection en public bool IsEmpty => this == Empty; /// - /// Returns the absolute offset of this range taking into account the .. + /// Returns the absolute offset of this range taking into account the .. /// public ulong Offset { @@ -82,7 +82,7 @@ public ulong Offset return 0; } - return BeginSection!.Offset + BeginOffset; + return BeginSection!.Position + BeginOffset; } } @@ -99,7 +99,7 @@ public ulong Size return 0; } - ulong size = EndSection.Offset - BeginSection.Offset; + ulong size = EndSection.Position - BeginSection.Position; size -= BeginOffset; size += EndOffset < 0 ? (ulong)((long)EndSection.Size + EndOffset + 1) : (ulong)(EndOffset + 1); return size; diff --git a/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs b/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs index de3c122..1f5d416 100644 --- a/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs +++ b/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs @@ -218,7 +218,7 @@ private void WriteSections() // Write only section with content if (section.HasContent) { - Stream.Position = (long)(_startOfFile + section.Offset); + Stream.Position = (long)(_startOfFile + section.Position); section.WriteInternal(this); } } @@ -270,7 +270,7 @@ private void WriteSectionTableEntry32(ElfSection section) _encoder.Encode(out shdr.sh_type, (uint)section.Type); _encoder.Encode(out shdr.sh_flags, (uint)section.Flags); _encoder.Encode(out shdr.sh_addr, (uint)section.VirtualAddress); - _encoder.Encode(out shdr.sh_offset, (uint)section.Offset); + _encoder.Encode(out shdr.sh_offset, (uint)section.Position); if (section.Index == 0 && ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE) { _encoder.Encode(out shdr.sh_size, ObjectFile.VisibleSectionCount); @@ -295,7 +295,7 @@ private void WriteSectionTableEntry64(ElfSection section) _encoder.Encode(out shdr.sh_type, (uint)section.Type); _encoder.Encode(out shdr.sh_flags, (uint)section.Flags); _encoder.Encode(out shdr.sh_addr, section.VirtualAddress); - _encoder.Encode(out shdr.sh_offset, section.Offset); + _encoder.Encode(out shdr.sh_offset, section.Position); if (section.Index == 0 && ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE) { _encoder.Encode(out shdr.sh_size, ObjectFile.VisibleSectionCount); diff --git a/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs b/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs index eb276b9..3d896be 100644 --- a/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs @@ -36,8 +36,8 @@ public override void UpdateLayout(DiagnosticBag diagnostics) { if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - var nextSectionOffset = AlignHelper.AlignToUpper(Offset, UpperAlignment); - Size = nextSectionOffset - Offset; + var nextSectionOffset = AlignHelper.AlignToUpper(Position, UpperAlignment); + Size = nextSectionOffset - Position; if (Size >= int.MaxValue) { diagnostics.Error(DiagnosticId.ELF_ERR_InvalidAlignmentOutOfRange, $"Invalid alignment 0x{UpperAlignment:x} resulting in an offset beyond int.MaxValue"); diff --git a/src/LibObjectFile/Elf/Sections/ElfNullSection.cs b/src/LibObjectFile/Elf/Sections/ElfNullSection.cs index 89f92a9..cf6ae37 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNullSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNullSection.cs @@ -22,7 +22,7 @@ public override void Verify(DiagnosticBag diagnostics) Alignment != 0 || !Link.IsEmpty || !Info.IsEmpty || - Offset != 0 || + Position != 0 || Size != 0) { diagnostics.Error(DiagnosticId.ELF_ERR_InvalidNullSection, "Invalid Null section. This section should not be modified and all properties must be null"); diff --git a/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs b/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs index a52a657..e8a257a 100644 --- a/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs @@ -63,7 +63,7 @@ private void WriteProgramHeader32(ElfWriter writer, ref ElfSegment segment) var hdr = new ElfNative.Elf32_Phdr(); writer.Encode(out hdr.p_type, segment.Type.Value); - writer.Encode(out hdr.p_offset, (uint)segment.Offset); + writer.Encode(out hdr.p_offset, (uint)segment.Position); writer.Encode(out hdr.p_vaddr, (uint)segment.VirtualAddress); writer.Encode(out hdr.p_paddr, (uint)segment.PhysicalAddress); writer.Encode(out hdr.p_filesz, (uint)segment.Size); @@ -79,7 +79,7 @@ private void WriteProgramHeader64(ElfWriter writer, ref ElfSegment segment) var hdr = new ElfNative.Elf64_Phdr(); writer.Encode(out hdr.p_type, segment.Type.Value); - writer.Encode(out hdr.p_offset, segment.Offset); + writer.Encode(out hdr.p_offset, segment.Position); writer.Encode(out hdr.p_vaddr, segment.VirtualAddress); writer.Encode(out hdr.p_paddr, segment.PhysicalAddress); writer.Encode(out hdr.p_filesz, segment.Size); diff --git a/src/LibObjectFile/ObjectFileNodeBase.cs b/src/LibObjectFile/ObjectFileNodeBase.cs index 62b714d..f3b3a81 100644 --- a/src/LibObjectFile/ObjectFileNodeBase.cs +++ b/src/LibObjectFile/ObjectFileNodeBase.cs @@ -14,9 +14,9 @@ public abstract class ObjectFileNodeBase private ObjectFileNodeBase? _parent; /// - /// Gets or sets the offset of this section or segment in the parent . + /// Gets or sets the position of this element relative to the top level parent. /// - public ulong Offset { get; set; } + public ulong Position { get; set; } /// /// Gets the containing parent. @@ -45,7 +45,7 @@ protected virtual void ValidateParent(ObjectFileNodeBase parent) } /// - /// Index within the containing list in the + /// Index within the containing list in a parent. /// public uint Index { get; internal set; } @@ -61,7 +61,7 @@ protected virtual void ValidateParent(ObjectFileNodeBase parent) /// true if the offset is within the segment or section range. public bool Contains(ulong offset) { - return offset >= Offset && offset < Offset + Size; + return offset >= Position && offset < Position + Size; } /// @@ -72,7 +72,7 @@ public bool Contains(ulong offset) public bool Contains(ObjectFileNodeBase node) { ArgumentNullException.ThrowIfNull(node); - return Contains((ulong)node.Offset) || node.Size != 0 && Contains((ulong)(node.Offset + node.Size - 1)); + return Contains((ulong)node.Position) || node.Size != 0 && Contains((ulong)(node.Position + node.Size - 1)); } /// diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs index 4571a9a..9ececa6 100644 --- a/src/LibObjectFile/ObjectFileReaderWriter.cs +++ b/src/LibObjectFile/ObjectFileReaderWriter.cs @@ -39,10 +39,10 @@ public Stream Stream set => _stream = value; } - public ulong Offset + public ulong Position { get => (ulong) Stream.Position; - set => Stream.Position = (long) value; + set => Stream.Seek((long)value, SeekOrigin.Begin); } public ulong Length @@ -73,6 +73,8 @@ public ulong Length public int Read(byte[] buffer, int offset, int count) => Stream.Read(buffer, offset, count); public int Read(Span buffer) => Stream.Read(buffer); + + public void ReadExactly(Span buffer) => Stream.ReadExactly(buffer); /// /// Reads a null terminated UTF8 string from the stream. @@ -220,7 +222,7 @@ public Stream ReadAsStream(ulong size) if (IsReadOnly) { var stream = ReadAsSubStream(size); - Stream.Position += stream.Length; + Stream.Seek(stream.Length, SeekOrigin.Current); return stream; } @@ -270,7 +272,7 @@ public void Write(Stream inputStream, ulong size = 0, int bufferSize = 4096) if (inputStream == null) throw new ArgumentNullException(nameof(inputStream)); if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize)); - inputStream.Position = 0; + inputStream.Seek(0, SeekOrigin.Begin); size = size == 0 ? (ulong)inputStream.Length : size; var buffer = ArrayPool.Shared.Rent(bufferSize); @@ -284,7 +286,7 @@ public void Write(Stream inputStream, ulong size = 0, int bufferSize = 4096) size -= (ulong)sizeRead; } - inputStream.Position = 0; + inputStream.Seek(0, SeekOrigin.Begin); if (size != 0) { throw new InvalidOperationException("Unable to write stream entirely"); @@ -320,22 +322,10 @@ private MemoryStream ReadAsMemoryStream(ulong size) memoryStream.SetLength((long)size); var buffer = memoryStream.GetBuffer(); - while (size != 0) - { - var lengthToRead = size >= int.MaxValue ? int.MaxValue : (int)size; - var lengthRead = Stream.Read(buffer, 0, lengthToRead); - if (lengthRead < 0) break; - if ((uint)lengthRead >= size) - { - size -= (uint)lengthRead; - } - else - { - break; - } - } + var span = new Span(buffer, 0, (int)size); + var readSize = Stream.Read(span); - if (size != 0) + if ((int)size != readSize) { Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Expecting to read {size} bytes at offset {Stream.Position}"); } diff --git a/src/LibObjectFile/PE/DataDirectory/BaseRelocationType.cs b/src/LibObjectFile/PE/DataDirectory/BaseRelocationType.cs new file mode 100644 index 0000000..02749a0 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/BaseRelocationType.cs @@ -0,0 +1,76 @@ +// 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 the different relocation types for an image file. +/// +public enum BaseRelocationType : ushort +{ + /// + /// The base relocation is skipped. This type can be used to pad a block. + /// + Absolute = 0, + + /// + /// The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. + /// The 16-bit field represents the high value of a 32-bit word. + /// + High = 1 << 12, + + /// + /// The base relocation adds the low 16 bits of the difference to the 16-bit field at offset. + /// The 16-bit field represents the low half of a 32-bit word. + /// + Low = 2 << 12, + + /// + /// The base relocation applies all 32 bits of the difference to the 32-bit field at offset. + /// + HighLow = 3 << 12, + + /// + /// The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. + /// The 16-bit field represents the high value of a 32-bit word. The low 16 bits of the 32-bit value + /// are stored in the 16-bit word that follows this base relocation. This means that this base + /// relocation occupies two slots. + /// + HighAdj = 4 << 12, + + /// + /// When the machine type is MIPS, the base relocation applies to a MIPS jump instruction. + /// When the machine type is ARM or Thumb, the base relocation applies the 32-bit address of a symbol across a consecutive MOVW/MOVT instruction pair. + /// When the machine type is RISC-V, the base relocation applies to the high 20 bits of a 32-bit absolute address. + /// + MipsJmpAddrOrArmMov32OrRiscvHigh20 = 5 << 12, + + /// + /// Reserved, must be zero. + /// + Reserved = 6 << 12, + + /// + /// When the machine type is Thumb, the base relocation applies the 32-bit address of a symbol to a consecutive MOVW/MOVT instruction pair. + /// When the machine type is RISC-V, the base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V I-type instruction format. + /// + ThumbMov32OrRiscvLow12I = 7 << 12, + + /// + /// When the machine type is RISC-V, the base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V S-type instruction format. + /// When the machine type is LoongArch 32-bit, the base relocation applies to a 32-bit absolute address formed in two consecutive instructions. + /// When the machine type is LoongArch 64-bit, the base relocation applies to a 64-bit absolute address formed in four consecutive instructions. + /// + RiscvLow12SOrLoongArchMarkLa = 8 << 12, + + /// + /// When the machine type is MIPS, the base relocation applies to a MIPS16 jump instruction. + /// + MipsJmpAddr16 = 9 << 12, + + /// + /// The base relocation applies the difference to the 64-bit field at offset. + /// + Dir64 = 10 << 12 +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs new file mode 100644 index 0000000..882f1fe --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs @@ -0,0 +1,86 @@ +// 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.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace LibObjectFile.PE; + +#pragma warning disable CS0649 +/// +/// A base relocation in a Portable Executable (PE) image. +/// +public readonly struct PEBaseRelocation : IEquatable +{ + public const ushort MaxVirtualOffset = (1 << 12) - 1; + private const ushort TypeMask = unchecked((ushort)(~MaxVirtualOffset)); + private const ushort VirtualOffsetMask = MaxVirtualOffset; + + private readonly ushort _value; + + public PEBaseRelocation(BaseRelocationType type, ushort virtualOffset) + { + if (virtualOffset > MaxVirtualOffset) + { + ThrowVirtualOffsetOutOfRange(); + } + _value = (ushort)((ushort)type | virtualOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal PEBaseRelocation(ushort value) => _value = value; + + /// + /// Gets a value indicating whether the base relocation is zero (used for padding). + /// + public bool IsZero => _value == 0; + + /// + /// Gets the type of the base relocation. + /// + public BaseRelocationType Type => (BaseRelocationType)(_value & TypeMask); + + /// + /// Gets the virtual offset of the base relocation relative to the offset of the associated . + /// + public ushort OffsetInBlockPart => (ushort)(_value & VirtualOffsetMask); + + /// + public bool Equals(PEBaseRelocation other) => _value == other._value; + + /// + public override bool Equals(object? obj) => obj is PEBaseRelocation other && Equals(other); + + /// + public override int GetHashCode() => _value.GetHashCode(); + + /// + /// Compares two objects for equality. + /// + /// The left value to compare. + /// The right value to compare. + /// true if values are equal; otherwise false. + public static bool operator ==(PEBaseRelocation left, PEBaseRelocation right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The left value to compare. + /// The right value to compare. + /// true if values are not equal; otherwise false. + public static bool operator !=(PEBaseRelocation left, PEBaseRelocation right) => !left.Equals(right); + + /// + public override string ToString() + { + return IsZero ? "Zero Padding" : $"{Type} {OffsetInBlockPart}"; + } + + [DoesNotReturn, MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowVirtualOffsetOutOfRange() + { + throw new ArgumentOutOfRangeException(nameof(OffsetInBlockPart), $"The virtual offset must be less than {MaxVirtualOffset}"); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs new file mode 100644 index 0000000..5cad1d2 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs @@ -0,0 +1,162 @@ +// 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.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace LibObjectFile.PE; + +public sealed class PEBaseRelocationDirectory : PEDirectory +{ + public PEBaseRelocationDirectory() : base(ImageDataDirectoryKind.BaseRelocation) + { + } + + public List Blocks { get; } = new(); + + public override void UpdateLayout(DiagnosticBag diagnostics) + { + var size = 0UL; + foreach (var block in Blocks) + { + size += block.SizeOf; + } + Size = size; + } + + public unsafe void Read(PEImageReader reader) + { + reader.Position = Position; + var size = (int)Size; + + var buffer = ArrayPool.Shared.Rent((int)size); + try + { + var span = buffer.AsSpan(0, (int)size); + int read = reader.Read(span); + if (read != size) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidEndOfStream, $"Unable to read the full content of the BaseRelocation directory. Expected {size} bytes, but read {read} bytes"); + return; + } + + var allSectionData = reader.PEFile.GetAllSectionData(); + + int blockIndex = 0; + while (span.Length > 0) + { + var location = MemoryMarshal.Read(span); + span = span.Slice(sizeof(ImageBaseRelocation)); + + if (!reader.PEFile.TryFindSection(location.PageVirtualAddress, out var section)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section containing the virtual address {location.PageVirtualAddress} in block #{blockIndex}"); + continue; + } + + var sizeOfRelocations = (int)location.SizeOfBlock - sizeof(ImageBaseRelocation); + + var relocSpan = MemoryMarshal.Cast(span.Slice(0, sizeOfRelocations)); + + // Remove padding zeros at the end of the block + if (relocSpan.Length > 0 && relocSpan[^1].IsZero) + { + relocSpan = relocSpan.Slice(0, relocSpan.Length - 1); + } + + // Create a block + var block = new PEBaseRelocationPageBlock(new(section, location.PageVirtualAddress - section.VirtualAddress)); + Blocks.Add(block); + + PEBaseRelocationPageBlockPart? currentBlockPart = null; + var currentSectionDataIndex = 0; + + // Iterate on all relocations + foreach (var relocation in relocSpan) + { + if (relocation.IsZero) + { + continue; + } + + var va = location.PageVirtualAddress + relocation.OffsetInBlockPart; + + // Find the section data containing the virtual address + if (!TryFindSectionData(allSectionData, currentSectionDataIndex, va, out var newSectionDataIndex)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section data containing the virtual address 0x{va:X4}"); + continue; + } + + var sectionData = allSectionData[newSectionDataIndex]; + var offsetInSectionData = va - sectionData.VirtualAddress; + currentSectionDataIndex = newSectionDataIndex; + + // Create a new block part if the section data is different, or it is the first relocation + if (currentBlockPart is null || currentBlockPart.SectionDataRVALink.Element != sectionData) + { + currentBlockPart = new PEBaseRelocationPageBlockPart(new(sectionData, offsetInSectionData)); + block.Parts.Add(currentBlockPart); + } + + var newRelocation = new PEBaseRelocation(relocation.Type, (ushort)(offsetInSectionData - currentBlockPart.SectionDataRVALink.OffsetInElement)); + + currentBlockPart.Relocations.Add(newRelocation); + } + + // Move to the next block + span = span.Slice(sizeOfRelocations); + blockIndex++; + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + private bool TryFindSectionData(List allSectionData, int startIndex, uint virtualAddress, out int indexFound) + { + var span = CollectionsMarshal.AsSpan(allSectionData); + for (int i = startIndex; i < span.Length; i++) + { + ref var sectionData = ref span[i]; + if (sectionData.ContainsVirtual(virtualAddress)) + { + indexFound = i; + return true; + } + } + + indexFound = -1; + return false; + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } + +#pragma warning disable CS0649 + private struct ImageBaseRelocation + { + public RVA PageVirtualAddress; + public uint SizeOfBlock; + } + + protected override bool PrintMembers(StringBuilder builder) + { + if (base.PrintMembers(builder)) + { + builder.Append(", "); + } + + builder.Append($", Blocks[{Blocks.Count}]"); + return true; + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs new file mode 100644 index 0000000..8921f15 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs @@ -0,0 +1,57 @@ +// 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.Diagnostics; +using static System.Reflection.Metadata.BlobBuilder; + +namespace LibObjectFile.PE; + +/// +/// A block of base relocations for a page. +/// +[DebuggerDisplay("{nameof(PEBaseRelocationPageBlock)}, Section = {SectionRVALink.Element.Name}, RVA = {SectionRVALink.VirtualAddress}, Size = {SizeOf}, Parts[{Parts.Count}]")] +public sealed class PEBaseRelocationPageBlock +{ + /// + /// Initializes a new instance of the class. + /// + /// The section link. + public PEBaseRelocationPageBlock(RVALink sectionRVALink) + { + SectionRVALink = sectionRVALink; + } + + /// + /// Gets or sets the linked and its virtual offset within it. + /// + public RVALink SectionRVALink { get; set; } + + /// + /// Gets the list of relocations for this block. + /// + public List Parts { get; } = new(); + + /// + /// Gets the size of this block. + /// + internal uint SizeOf + { + get + { + uint size = 0; + foreach (var part in Parts) + { + size += part.SizeOf; + } + + return size; + } + } + + public override string ToString() + { + return $"{nameof(PEBaseRelocationPageBlock)}, Section = {SectionRVALink.Element.Name}, RVA = {SectionRVALink.VirtualAddress}, Size = {SizeOf}, Parts[{Parts.Count}]"; + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs new file mode 100644 index 0000000..d3863c0 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs @@ -0,0 +1,56 @@ +// 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.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// A block of base relocations for a page. +/// +[DebuggerDisplay("{nameof(PEBaseRelocationPageBlock),nq} Link = {SectionDataRVALink}, Relocations[{Relocations.Count}]")] +public sealed class PEBaseRelocationPageBlockPart +{ + public PEBaseRelocationPageBlockPart(RVALink sectionDataRVALink) + { + SectionDataRVALink = sectionDataRVALink; + Relocations = new List(); + } + + internal PEBaseRelocationPageBlockPart(RVALink sectionDataRVALink, List relocations) + { + SectionDataRVALink = sectionDataRVALink; + Relocations = relocations; + } + + /// + /// Gets or sets the linked and its virtual offset within it. + /// + public RVALink SectionDataRVALink { get; } + + /// + /// Gets the list of relocations for this block. + /// + public List Relocations { get; } + + /// + /// Gets the size of this block. + /// + internal uint SizeOf + { + get + { + var size = sizeof(uint) + sizeof(uint) + Relocations.Count * sizeof(ushort); + // Align to 4 bytes + size = (size + 3) & ~3; + return (uint)size; + } + } + + public override string ToString() + { + return $"{nameof(PEBaseRelocationPageBlock)} Link = {SectionDataRVALink}, Relocations[{Relocations.Count}]"; + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs index 7d27ae1..f58442c 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs @@ -2,31 +2,55 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -using System.IO; using System; +using System.Text; namespace LibObjectFile.PE; -public abstract class PEDirectory(ImageDataDirectoryKind kind) : PESectionData +public abstract class PEDirectory : PESectionData { - public ImageDataDirectoryKind Kind { get; } = kind; -} - -public sealed class PEExportDirectory() : PEDirectory(ImageDataDirectoryKind.Export) -{ - public override void UpdateLayout(DiagnosticBag diagnostics) + protected PEDirectory(ImageDataDirectoryKind kind) { - throw new NotImplementedException(); + Kind = kind; } - protected override void Write(PEImageWriter writer) + public ImageDataDirectoryKind Kind { get; } + + /// + /// Factory method to create a new instance of based on the kind. + /// + /// The kind of PE directory entry. + /// A PE directory entry. + internal static PEDirectory Create(ImageDataDirectoryKind kind, RVALink link) { - throw new NotImplementedException(); + return kind switch + { + ImageDataDirectoryKind.Export => new PEExportDirectory(), + ImageDataDirectoryKind.Import => new PEImportDirectory(), + ImageDataDirectoryKind.Resource => new PEResourceDirectory(), + ImageDataDirectoryKind.Exception => new PEExceptionDirectory(), + ImageDataDirectoryKind.Security => new PESecurityDirectory(), + ImageDataDirectoryKind.BaseRelocation => new PEBaseRelocationDirectory(), + ImageDataDirectoryKind.Debug => new PEDebugDirectory(), + ImageDataDirectoryKind.Architecture => new PEArchitectureDirectory(), + ImageDataDirectoryKind.GlobalPointer => new PEGlobalPointerDirectory(), + ImageDataDirectoryKind.Tls => new PETlsDirectory(), + ImageDataDirectoryKind.LoadConfig => new PELoadConfigDirectory(), + ImageDataDirectoryKind.BoundImport => new PEBoundImportDirectory(), + ImageDataDirectoryKind.DelayImport => new PEDelayImportDirectory(), + ImageDataDirectoryKind.ImportAddressTable => new PEImportAddressTable(), + ImageDataDirectoryKind.ClrMetadata => new PEClrMetadata(), + _ => throw new ArgumentOutOfRangeException(nameof(kind)) + }; } } -public sealed class PEImportDirectory() : PEDirectory(ImageDataDirectoryKind.Import) +public sealed class PEExportDirectory : PEDirectory { + public PEExportDirectory() : base(ImageDataDirectoryKind.Export) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -38,8 +62,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PEResourceDirectory() : PEDirectory(ImageDataDirectoryKind.Resource) +public sealed class PEImportDirectory : PEDirectory { + public PEImportDirectory() : base(ImageDataDirectoryKind.Import) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -51,8 +79,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PEExceptionDirectory() : PEDirectory(ImageDataDirectoryKind.Exception) +public sealed class PEResourceDirectory : PEDirectory { + public PEResourceDirectory() : base(ImageDataDirectoryKind.Resource) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -64,8 +96,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PEBaseRelocationDirectory() : PEDirectory(ImageDataDirectoryKind.BaseRelocation) +public sealed class PEExceptionDirectory : PEDirectory { + public PEExceptionDirectory() : base(ImageDataDirectoryKind.Exception) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -77,8 +113,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PEDebugDirectory() : PEDirectory(ImageDataDirectoryKind.Debug) +public sealed class PEDebugDirectory : PEDirectory { + public PEDebugDirectory() : base(ImageDataDirectoryKind.Debug) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -90,8 +130,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PELoadConfigDirectory() : PEDirectory(ImageDataDirectoryKind.LoadConfig) +public sealed class PELoadConfigDirectory : PEDirectory { + public PELoadConfigDirectory() : base(ImageDataDirectoryKind.LoadConfig) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -103,8 +147,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PEBoundImportDirectory() : PEDirectory(ImageDataDirectoryKind.BoundImport) +public sealed class PEBoundImportDirectory : PEDirectory { + public PEBoundImportDirectory() : base(ImageDataDirectoryKind.BoundImport) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -116,8 +164,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PEImportAddressTable() : PEDirectory(ImageDataDirectoryKind.ImportAddressTable) +public sealed class PEImportAddressTable : PEDirectory { + public PEImportAddressTable() : base(ImageDataDirectoryKind.ImportAddressTable) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -129,8 +181,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PETlsDirectory() : PEDirectory(ImageDataDirectoryKind.Tls) +public sealed class PETlsDirectory : PEDirectory { + public PETlsDirectory() : base(ImageDataDirectoryKind.Tls) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -142,8 +198,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PEDelayImportDirectory() : PEDirectory(ImageDataDirectoryKind.DelayImport) +public sealed class PEDelayImportDirectory : PEDirectory { + public PEDelayImportDirectory() : base(ImageDataDirectoryKind.DelayImport) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -155,8 +215,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PEClrMetadata() : PEDirectory(ImageDataDirectoryKind.ClrMetadata) +public sealed class PEClrMetadata : PEDirectory { + public PEClrMetadata() : base(ImageDataDirectoryKind.ClrMetadata) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -168,8 +232,11 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PEArchitectureDirectory() : PEDirectory(ImageDataDirectoryKind.Architecture) +public sealed class PEArchitectureDirectory : PEDirectory { + public PEArchitectureDirectory() : base(ImageDataDirectoryKind.Architecture) + { + } public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -181,8 +248,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PEGlobalPointerDirectory() : PEDirectory(ImageDataDirectoryKind.GlobalPointer) +public sealed class PEGlobalPointerDirectory : PEDirectory { + public PEGlobalPointerDirectory() : base(ImageDataDirectoryKind.GlobalPointer) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); @@ -194,8 +265,12 @@ protected override void Write(PEImageWriter writer) } } -public sealed class PESecurityDirectory() : PEDirectory(ImageDataDirectoryKind.Security) +public sealed class PESecurityDirectory : PEDirectory { + public PESecurityDirectory() : base(ImageDataDirectoryKind.Security) + { + } + public override void UpdateLayout(DiagnosticBag diagnostics) { throw new NotImplementedException(); diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs new file mode 100644 index 0000000..d651e1f --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -0,0 +1,129 @@ +// 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.Runtime.CompilerServices; + +namespace LibObjectFile.PE; + +/// +/// Contains the array of directory entries in a Portable Executable (PE) file. +/// +[DebuggerDisplay($"{nameof(PEDirectoryTable)} {nameof(Count)} = {{{nameof(Count)}}}")] +public sealed class PEDirectoryTable +{ + private InternalArray _entries; + private int _count; + + internal PEDirectoryTable() + { + } + + public PEDirectory? this[ImageDataDirectoryKind kind] => _entries[(int)kind]; + + /// + /// Gets the number of directory entries in the array. + /// + public int Count => _count; + + /// + /// Gets the export directory information from the PE file. + /// + public PEExportDirectory? Export => (PEExportDirectory?)this[ImageDataDirectoryKind.Export]; + + /// + /// Gets the import directory information from the PE file. + /// + public PEImportDirectory? Import => (PEImportDirectory?)this[ImageDataDirectoryKind.Import]; + + /// + /// Gets the resource directory information from the PE file. + /// + public PEResourceDirectory? Resource => (PEResourceDirectory?)this[ImageDataDirectoryKind.Resource]; + + /// + /// Gets the exception directory information from the PE file. + /// + public PEExceptionDirectory? Exception => (PEExceptionDirectory?)this[ImageDataDirectoryKind.Exception]; + + /// + /// Gets the security directory information from the PE file. + /// + public PESecurityDirectory? Security => (PESecurityDirectory?)this[ImageDataDirectoryKind.Security]; + + /// + /// Gets the base relocation directory information from the PE file. + /// + public PEBaseRelocationDirectory? BaseRelocation => (PEBaseRelocationDirectory?)this[ImageDataDirectoryKind.BaseRelocation]; + + /// + /// Gets the debug directory information from the PE file. + /// + public PEDebugDirectory? Debug => (PEDebugDirectory?)this[ImageDataDirectoryKind.Debug]; + + /// + /// Gets the architecture-specific directory information from the PE file. + /// + public PEArchitectureDirectory? Architecture => (PEArchitectureDirectory?)this[ImageDataDirectoryKind.Architecture]; + + /// + /// Gets the global pointer directory information from the PE file. + /// + public PEGlobalPointerDirectory? GlobalPointer => (PEGlobalPointerDirectory?)this[ImageDataDirectoryKind.GlobalPointer]; + + /// + /// Gets the TLS (Thread Local Storage) directory information from the PE file. + /// + public PETlsDirectory? Tls => (PETlsDirectory?)this[ImageDataDirectoryKind.Tls]; + + /// + /// Gets the load configuration directory information from the PE file. + /// + public PELoadConfigDirectory? LoadConfig => (PELoadConfigDirectory?)this[ImageDataDirectoryKind.LoadConfig]; + + /// + /// Gets the bound import directory information from the PE file. + /// + public PEBoundImportDirectory? BoundImport => (PEBoundImportDirectory?)this[ImageDataDirectoryKind.BoundImport]; + + /// + /// Gets the delay import directory information from the PE file. + /// + public PEDelayImportDirectory? DelayImport => (PEDelayImportDirectory?)this[ImageDataDirectoryKind.DelayImport]; + + /// + /// Gets the import address table directory information from the PE file. + /// + public PEImportAddressTable? ImportAddressTable => (PEImportAddressTable?)this[ImageDataDirectoryKind.ImportAddressTable]; + + /// + /// Gets the CLR metadata directory information from the PE file, if present. + /// + public PEClrMetadata? ClrMetadata => (PEClrMetadata?)this[ImageDataDirectoryKind.ClrMetadata]; + + + internal void Set(ImageDataDirectoryKind kind, PEDirectory? directory) + { + ref var entry = ref _entries[(int)kind]; + var previousEntry = entry; + entry = directory; + + if (previousEntry is not null) + { + _count--; + } + + if (directory is not null) + { + _count++; + } + } + + [InlineArray(15)] + private struct InternalArray + { + private PEDirectory? _element; + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/IVirtualAddressable.cs b/src/LibObjectFile/PE/IVirtualAddressable.cs new file mode 100644 index 0000000..a04d181 --- /dev/null +++ b/src/LibObjectFile/PE/IVirtualAddressable.cs @@ -0,0 +1,16 @@ +// 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; + +/// +/// Interface for an object that has a virtual address. +/// +public interface IVirtualAddressable +{ + /// + /// Gets the virtual address of this object. + /// + RVA VirtualAddress { get; } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 4018b8e..01d1171 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibObjectFile.PE.Internal; @@ -194,7 +195,7 @@ internal void ReadFromInternal(PEImageReader imageReader) diagnostics.Error(DiagnosticId.PE_ERR_InvalidSectionHeadersSize, "Invalid section headers"); } - InitializeSections(sectionHeaders); + InitializeSections(imageReader, sectionHeaders); } finally { @@ -202,15 +203,17 @@ internal void ReadFromInternal(PEImageReader imageReader) } } - private void InitializeSections(ReadOnlySpan headers) + private void InitializeSections(PEImageReader imageReader, ReadOnlySpan headers) { _sections.Clear(); + + // Create sections foreach (var section in headers) { // We don't validate the name var peSection = new PESection(this, new PESectionName(section.NameAsString, false)) { - Offset = section.PointerToRawData, + Position = section.PointerToRawData, Size = section.SizeOfRawData, VirtualAddress = section.VirtualAddress, VirtualSize = section.VirtualSize, @@ -223,5 +226,91 @@ private void InitializeSections(ReadOnlySpan headers) }; _sections.Add(peSection); } + + // Create directories and find the section for each directory + var maxNumberOfDirectory = (int)Math.Min(OptionalHeader.NumberOfRvaAndSizes, 15); + Span dataDirectories = OptionalHeader.DataDirectory; + for (int i = 0; i < maxNumberOfDirectory && i < dataDirectories.Length; i++) + { + var directoryEntry = dataDirectories[i]; + if (directoryEntry.VirtualAddress == 0 || directoryEntry.Size == 0) + { + continue; + } + + if (!TryFindSection(directoryEntry.VirtualAddress, directoryEntry.Size, out var peSection)) + { + imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidDataDirectorySection, $"Unable to find the section for the DataDirectory at virtual address {directoryEntry.VirtualAddress}, size {directoryEntry.Size}"); + continue; + } + + var offsetInSection = directoryEntry.VirtualAddress - peSection.VirtualAddress; + var directory = PEDirectory.Create((ImageDataDirectoryKind)i, new(peSection, offsetInSection)); + directory.Position = peSection.Position + offsetInSection; + directory.Size = directoryEntry.Size; + Directories.Set(directory.Kind, directory); + + // Insert the directory at the right position in the section + int sectionDataIndex = 0; + for (; sectionDataIndex < peSection.DataParts.Count; sectionDataIndex++) + { + var sectionData = peSection.DataParts[sectionDataIndex]; + if (directory.Position < sectionData.Position) + { + break; + } + } + + peSection.InsertData(sectionDataIndex, directory); + } + + // Create Stream data sections for remaining data per section based on directories already loaded + foreach (var section in _sections) + { + var sectionPosition = section.Position; + + for (var i = 0; i < section.DataParts.Count; i++) + { + var data = section.DataParts[i]; + if (data.Position != sectionPosition) + { + var sectionData = new PESectionStreamData() + { + Position = sectionPosition, + }; + var size = data.Position - sectionPosition; + imageReader.Position = sectionPosition; + sectionData.Stream = imageReader.ReadAsStream(size); + + section.InsertData(i, sectionData); + sectionPosition = data.Position; + i++; + } + + sectionPosition += data.Size; + } + + if (sectionPosition < section.Position + section.Size) + { + var sectionData = new PESectionStreamData() + { + Position = sectionPosition, + }; + var size = section.Position + section.Size - sectionPosition; + imageReader.Position = sectionPosition; + sectionData.Stream = imageReader.ReadAsStream(size); + + section.AddData(sectionData); + } + + for (var i = 0; i < section.DataParts.Count; i++) + { + var sectionData = section.DataParts[i]; + Console.WriteLine($"section: {section.Name} {sectionData}"); + } + } + + // Read directories + Directories.BaseRelocation?.Read(imageReader); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Write.cs b/src/LibObjectFile/PE/PEFile.Write.cs index 0df9f78..6243c16 100644 --- a/src/LibObjectFile/PE/PEFile.Write.cs +++ b/src/LibObjectFile/PE/PEFile.Write.cs @@ -46,12 +46,12 @@ public bool TryWrite(Stream stream, out DiagnosticBag diagnostics) return false; } - WriteInternal(peWriter); + Write(peWriter); return !diagnostics.HasErrors; } - private void WriteInternal(PEImageWriter writer) + protected override void Write(PEImageWriter writer) { throw new NotImplementedException(); } diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index a8931de..e92c384 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -8,6 +8,7 @@ using System.IO; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; +using System.Text; namespace LibObjectFile.PE; @@ -70,10 +71,20 @@ public Stream? DosStubExtra /// public ImageOptionalHeader OptionalHeader; + /// + /// Gets the directories. + /// + /// + /// Directories must be added/removed from or . + /// + public PEDirectoryTable Directories { get; } = new(); + /// + /// Gets the sections. + /// public IReadOnlyList Sections => _sections; - public PESection AddSection(string name, uint virtualAddress, uint virtualSize, SectionCharacteristics characteristics = SectionCharacteristics.MemRead) + public PESection AddSection(PESectionName name, uint virtualAddress, uint virtualSize, SectionCharacteristics characteristics = SectionCharacteristics.MemRead) { var section = new PESection(this, name) { @@ -89,10 +100,10 @@ public PESection AddSection(string name, uint virtualAddress, uint virtualSize, public void RemoveSection(PESection section) => _sections.Remove(section); - public bool TryFindSection(uint virtualAddress, [NotNullWhen(true)] out PESection? section) + public bool TryFindSection(RVA virtualAddress, [NotNullWhen(true)] out PESection? section) => TryFindSection(virtualAddress, 0, out section); - public bool TryFindSection(uint virtualAddress, uint virtualSize, [NotNullWhen(true)] out PESection? section) + public bool TryFindSection(RVA virtualAddress, uint virtualSize, [NotNullWhen(true)] out PESection? section) { var sections = CollectionsMarshal.AsSpan(_sections); foreach (var trySection in sections) @@ -118,7 +129,7 @@ public void RemoveSection(PESectionName name) _sections.Remove(section); } - + public void ClearSections() => _sections.Clear(); public PESection GetSection(PESectionName name) @@ -148,9 +159,66 @@ public bool TryGetSection(PESectionName name, [NotNullWhen(true)] out PESection? section = null; return false; } + + public List GetAllSectionData() + { + var dataList = new List(); + + // Precalculate the capacity + int count = 0; + var sections = _sections; + foreach (var section in sections) + { + count += section.DataParts.Count; + } + dataList.Capacity = count; + + foreach (var section in sections) + { + var va = section.VirtualAddress; + foreach (var data in section.DataParts) + { + var size = (uint)data.Size; + data.VirtualAddress = va; + va += size; + dataList.Add(data); + } + } + + return dataList; + } public override void UpdateLayout(DiagnosticBag diagnostics) { // TODO } -} \ No newline at end of file + + protected override bool PrintMembers(StringBuilder builder) + { + if ((CoffHeader.Characteristics & Characteristics.Dll) != 0) + { + builder.Append("Dll "); + } + else if ((CoffHeader.Characteristics & Characteristics.ExecutableImage) != 0) + { + builder.Append("Exe "); + } + else + { + builder.Append("Unknown "); + } + + switch (OptionalHeader.Magic) + { + case ImageOptionalHeaderMagic.PE32: + builder.Append("PE32"); + break; + case ImageOptionalHeaderMagic.PE32Plus: + builder.Append("PE32+"); + break; + } + + builder.Append($"Directories[{Directories.Count}], Sections[{Sections.Count}]"); + return true; + } +} diff --git a/src/LibObjectFile/PE/PEObject.cs b/src/LibObjectFile/PE/PEObject.cs index af77a68..e1e8424 100644 --- a/src/LibObjectFile/PE/PEObject.cs +++ b/src/LibObjectFile/PE/PEObject.cs @@ -2,8 +2,38 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using System.Diagnostics; +using System.Text; + namespace LibObjectFile.PE; +[DebuggerDisplay("{ToString(),nq}")] public abstract class PEObject : ObjectFileNode { + protected abstract void Write(PEImageWriter writer); + + // TODO: move PrintName and PrintMembers to ObjectFileNode + + public sealed override string ToString() + { + var builder = new StringBuilder(); + PrintName(builder); + builder.Append(" { "); + if (PrintMembers(builder)) + { + builder.Append(' '); + } + builder.Append('}'); + return builder.ToString(); + } + + protected virtual void PrintName(StringBuilder builder) + { + builder.Append(GetType().Name); + } + + protected virtual bool PrintMembers(StringBuilder builder) + { + return false; + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index b639513..caa33fc 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -4,22 +4,26 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Reflection; using System.Reflection.PortableExecutable; +using System.Text; namespace LibObjectFile.PE; /// /// Defines a section in a Portable Executable (PE) image. /// -public class PESection : PEObject +public class PESection : PEObject, IVirtualAddressable { - private readonly List _dataList; + private readonly List _dataParts; + private RVA _virtualAddress; internal PESection(PEFile peFile, PESectionName name) { Parent = peFile; Name = name; - _dataList = new List(); + _dataParts = new List(); // Most of the time readable Characteristics = SectionCharacteristics.MemRead; } @@ -37,7 +41,15 @@ internal PESection(PEFile peFile, PESectionName name) /// /// The address of the first byte of the section when loaded into memory, relative to the image base. /// - public uint VirtualAddress { get; set; } + public RVA VirtualAddress + { + get => _virtualAddress; + set + { + _virtualAddress = value; + UpdateSectionDataIndicesAndVirtualAddress(0); + } + } /// /// The total size of the section when loaded into memory. @@ -73,8 +85,8 @@ internal PESection(PEFile peFile, PESectionName name) /// /// Gets the list of data associated with this section. /// - public IReadOnlyList DataList => _dataList; - + public IReadOnlyList DataParts => _dataParts; + /// /// Adds a new data to this section. /// @@ -85,7 +97,21 @@ public void AddData(PESectionData data) ArgumentNullException.ThrowIfNull(data); if (data.Section != null) throw new ArgumentException("Data is already associated with a section", nameof(data)); data.Section = this; - _dataList.Add(data); + data.Index = (uint)_dataParts.Count; + _dataParts.Add(data); + UpdateSectionDataIndicesAndVirtualAddress((int)data.Index); + } + + public void InsertData(int index, PESectionData data) + { + ArgumentNullException.ThrowIfNull(data); + if (index < 0 || index > _dataParts.Count) throw new ArgumentOutOfRangeException(nameof(index)); + + if (data.Section != null) throw new ArgumentException("Data is already associated with a section", nameof(data)); + data.Section = this; + data.Index = (uint)index; + _dataParts.Insert(index, data); + UpdateSectionDataIndicesAndVirtualAddress(index); } /// @@ -97,8 +123,8 @@ public void RemoveData(PESectionData data) { ArgumentNullException.ThrowIfNull(data); if (data.Section != this) throw new ArgumentException("Data is not associated with this section", nameof(data)); - data.Section = null; - _dataList.Remove(data); + var index = (int)data.Index; + RemoveDataAt(index); } /// @@ -107,9 +133,14 @@ public void RemoveData(PESectionData data) /// The index of the data to remove. public void RemoveDataAt(int index) { - var data = _dataList[index]; + if (index < 0 || index > _dataParts.Count) throw new ArgumentOutOfRangeException(nameof(index)); + + var list = _dataParts; + var data = list[index]; data.Section = null; - _dataList.RemoveAt(index); + data.Index = 0; + list.RemoveAt(index); + UpdateSectionDataIndicesAndVirtualAddress(index); } /// @@ -117,7 +148,7 @@ public void RemoveDataAt(int index) /// /// The virtual address to check if it belongs to this section. /// true if the virtual address is within the section range. - public bool ContainsVirtual(uint virtualAddress) + public bool ContainsVirtual(RVA virtualAddress) { return virtualAddress >= VirtualAddress && virtualAddress < VirtualAddress + VirtualSize; } @@ -128,7 +159,7 @@ public bool ContainsVirtual(uint 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(uint virtualAddress, uint size) + public bool ContainsVirtual(RVA virtualAddress, uint size) { return virtualAddress >= VirtualAddress && virtualAddress + size <= VirtualAddress + VirtualSize; } @@ -136,9 +167,47 @@ public bool ContainsVirtual(uint virtualAddress, uint size) /// public override void UpdateLayout(DiagnosticBag diagnostics) { - foreach (var data in DataList) + foreach (var data in DataParts) { data.UpdateLayout(diagnostics); } } + + /// + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } + + /// + protected override void PrintName(StringBuilder builder) + { + builder.Append(Name); + } + + /// + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"VirtualAddress = {VirtualAddress}, VirtualSize = 0x{VirtualSize:X4}, DataParts[{DataParts.Count}]"); + return true; + } + + private void UpdateSectionDataIndicesAndVirtualAddress(int startIndex) + { + var va = VirtualAddress; + var list = _dataParts; + if (startIndex > 0) + { + var previousData = list[startIndex - 1]; + va = previousData.VirtualAddress + (uint)previousData.Size; + } + + for (int i = startIndex; i < list.Count; i++) + { + var data = list[i]; + data.Index = (uint)i; + data.VirtualAddress = va; + va += (uint)data.Size; + } + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index 7bb9c54..df3e7fa 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -4,13 +4,14 @@ using System; using System.IO; +using System.Text; namespace LibObjectFile.PE; /// /// Base class for data contained in a . /// -public abstract class PESectionData : PEObject +public abstract class PESectionData : PEObject, IVirtualAddressable { /// /// Gets the parent of this section data. @@ -22,7 +23,30 @@ public PESection? Section internal set => Parent = value; } - protected abstract void Write(PEImageWriter writer); + /// + /// Gets the virtual address of this section data. + /// + /// + /// This property is updated dynamically based on the previous section data. + /// + public RVA VirtualAddress + { + get; + internal set; + } + + /// + /// Checks if the specified virtual address is contained by this instance. + /// + /// The virtual address to check if it belongs to this instance. + /// true if the specified virtual address is contained by this instance; otherwise, false. + public bool ContainsVirtual(RVA virtualAddress) => VirtualAddress <= virtualAddress && virtualAddress < VirtualAddress + Size; + + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"VirtualAddress: {VirtualAddress}, Size = 0x{Size:X4}"); + return true; + } } /// diff --git a/src/LibObjectFile/PE/PESectionName.cs b/src/LibObjectFile/PE/PESectionName.cs index 446650e..e3c5ba8 100644 --- a/src/LibObjectFile/PE/PESectionName.cs +++ b/src/LibObjectFile/PE/PESectionName.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using System.Numerics; using System.Text; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/RVA.cs b/src/LibObjectFile/PE/RVA.cs new file mode 100644 index 0000000..83e9723 --- /dev/null +++ b/src/LibObjectFile/PE/RVA.cs @@ -0,0 +1,27 @@ +// 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; + +/// +/// Defines a Relative Virtual Address (RVA) in a Portable Executable (PE) image. +/// +/// The value of the address +public record struct RVA(uint Value) +{ + /// + /// Converts a to a . + /// + /// The value to convert. + public static implicit operator uint(RVA value) => value.Value; + + /// + /// Converts a to a . + /// + /// The value to convert. + public static implicit operator RVA(uint value) => new(value); + + /// + public override string ToString() => $"{Value:X4}"; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/RVALink.cs b/src/LibObjectFile/PE/RVALink.cs new file mode 100644 index 0000000..ae39f02 --- /dev/null +++ b/src/LibObjectFile/PE/RVALink.cs @@ -0,0 +1,22 @@ +// 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.Text; + +namespace LibObjectFile.PE; + +/// +/// Defines a link into a virtual addressable object at a specific virtual offset. +/// +/// The type of the virtual addressable object. +/// The virtual addressable object linked. +/// The offset within this element. +public record struct RVALink(TVirtualAddressable Element, uint OffsetInElement) + where TVirtualAddressable : IVirtualAddressable +{ + /// + /// Gets the virtual address of within the element. + /// + public RVA VirtualAddress => Element.VirtualAddress + OffsetInElement; +} diff --git a/src/LibObjectFile/Utils/SubStream.cs b/src/LibObjectFile/Utils/SubStream.cs index b4aaed1..3a75c9a 100644 --- a/src/LibObjectFile/Utils/SubStream.cs +++ b/src/LibObjectFile/Utils/SubStream.cs @@ -42,7 +42,7 @@ public override int Read(Span buffer) if (remaining <= 0) return 0; if (remaining < buffer.Length) buffer = buffer.Slice(0, (int)remaining); - _baseStream.Position = _basePosition + _localPosition; + _baseStream.Seek(_basePosition + _localPosition, SeekOrigin.Begin); int read = _baseStream.Read(buffer); _localPosition += read; @@ -55,7 +55,7 @@ public override int ReadByte() if (_localPosition >= _length) return -1; - _baseStream.Position = _basePosition + _localPosition; + _baseStream.Seek(_basePosition + _localPosition, SeekOrigin.Begin); int read = _baseStream.ReadByte(); _localPosition++; @@ -72,7 +72,7 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation if (remaining <= 0) return 0; if (remaining < buffer.Length) buffer = buffer.Slice(0, (int)remaining); - _baseStream.Position = _basePosition + _localPosition; + _baseStream.Seek(_basePosition + _localPosition, SeekOrigin.Begin); var read = await _baseStream.ReadAsync(buffer, cancellationToken); _localPosition += read; @@ -137,7 +137,7 @@ public override long Seek(long offset, SeekOrigin origin) if (newPosition > _length) throw new ArgumentOutOfRangeException(nameof(offset), $"New resulting position {newPosition} is > Length {_length}"); // Check that we can seek on the origin stream - _baseStream.Position = _basePosition + newPosition; + _baseStream.Seek(_basePosition + newPosition, SeekOrigin.Begin); _localPosition = newPosition; return newPosition; @@ -184,7 +184,7 @@ public override void Write(ReadOnlySpan buffer) long length = Length; var isOverLength = _localPosition + buffer.Length > length; var maxLength = isOverLength ? (int)(length - _localPosition) : buffer.Length; - _baseStream.Position = _basePosition + _localPosition; + _baseStream.Seek(_basePosition + _localPosition, SeekOrigin.Begin); _baseStream.Write(buffer.Slice(0, maxLength)); _localPosition += maxLength; if (isOverLength) @@ -198,7 +198,7 @@ public override void WriteByte(byte value) ThrowIfDisposed(); if (_localPosition >= _length) ThrowCannotWriteOutside(); - _baseStream.Position = _basePosition + _localPosition; + _baseStream.Seek(_basePosition + _localPosition, SeekOrigin.Begin); _baseStream.WriteByte(value); _localPosition++; } @@ -214,7 +214,7 @@ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, Cancella long length = Length; var isOverLength = _localPosition + buffer.Length > length; var maxLength = isOverLength ? (int)(length - _localPosition) : buffer.Length; - _baseStream.Position = _basePosition + _localPosition; + _baseStream.Seek(_basePosition + _localPosition, SeekOrigin.Begin); await _baseStream.WriteAsync(buffer.Slice(0, maxLength), cancellationToken); _localPosition += maxLength; if (isOverLength) From f80727c338481722f37d69f665c751dcf5294a23 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 17 Sep 2024 07:04:54 +0200 Subject: [PATCH 09/87] Add ReadOnlyList to replace IReadOnlyList interface --- src/LibObjectFile/Ar/ArArchiveFile.cs | 3 +- src/LibObjectFile/DiagnosticBag.cs | 3 +- .../Dwarf/DwarfAbbreviationTable.cs | 3 +- src/LibObjectFile/Dwarf/DwarfDIE.cs | 5 +- src/LibObjectFile/Dwarf/DwarfExpression.cs | 3 +- src/LibObjectFile/Dwarf/DwarfInfoSection.cs | 3 +- .../Dwarf/DwarfLineProgramTable.cs | 3 +- src/LibObjectFile/Dwarf/DwarfLineSection.cs | 3 +- src/LibObjectFile/Dwarf/DwarfLineSequence.cs | 3 +- src/LibObjectFile/Dwarf/DwarfLocationList.cs | 3 +- .../Dwarf/DwarfLocationSection.cs | 3 +- src/LibObjectFile/Elf/ElfObjectFile.cs | 4 +- src/LibObjectFile/PE/PEFile.cs | 3 +- src/LibObjectFile/PE/PESection.cs | 3 +- src/LibObjectFile/Utils/ReadOnlyList.cs | 73 +++++++++++++++++++ 15 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 src/LibObjectFile/Utils/ReadOnlyList.cs diff --git a/src/LibObjectFile/Ar/ArArchiveFile.cs b/src/LibObjectFile/Ar/ArArchiveFile.cs index 244b282..7c9e763 100644 --- a/src/LibObjectFile/Ar/ArArchiveFile.cs +++ b/src/LibObjectFile/Ar/ArArchiveFile.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Text; +using LibObjectFile.Utils; namespace LibObjectFile.Ar; @@ -58,7 +59,7 @@ public ArArchiveFile() /// /// Gets the file entries. Use or to manipulate the entries. /// - public IReadOnlyList Files => _files; + public ReadOnlyList Files => _files; /// /// Adds a file to . diff --git a/src/LibObjectFile/DiagnosticBag.cs b/src/LibObjectFile/DiagnosticBag.cs index 3d22be8..370dcdf 100644 --- a/src/LibObjectFile/DiagnosticBag.cs +++ b/src/LibObjectFile/DiagnosticBag.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using LibObjectFile.Utils; namespace LibObjectFile { @@ -25,7 +26,7 @@ public DiagnosticBag() /// /// List of messages. /// - public IReadOnlyList Messages => _messages; + public ReadOnlyList Messages => _messages; /// /// If this instance contains error messages. diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs index 53f1cc9..096a327 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using LibObjectFile.Utils; namespace LibObjectFile.Dwarf { @@ -16,7 +17,7 @@ public DwarfAbbreviationTable() _abbreviations = new List(); } - public IReadOnlyList Abbreviations => _abbreviations; + public ReadOnlyList Abbreviations => _abbreviations; internal void AddAbbreviation(DwarfAbbreviation abbreviation) { diff --git a/src/LibObjectFile/Dwarf/DwarfDIE.cs b/src/LibObjectFile/Dwarf/DwarfDIE.cs index 2c83e7e..d1e4e42 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIE.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIE.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using LibObjectFile.Utils; namespace LibObjectFile.Dwarf { @@ -34,9 +35,9 @@ public virtual DwarfTagEx Tag set => _tag = value; } - public IReadOnlyList Attributes => _attributes; + public ReadOnlyList Attributes => _attributes; - public IReadOnlyList Children => _children; + public ReadOnlyList Children => _children; public DwarfAbbreviationItem? Abbrev { get; internal set; } diff --git a/src/LibObjectFile/Dwarf/DwarfExpression.cs b/src/LibObjectFile/Dwarf/DwarfExpression.cs index b69e75f..6333875 100644 --- a/src/LibObjectFile/Dwarf/DwarfExpression.cs +++ b/src/LibObjectFile/Dwarf/DwarfExpression.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using LibObjectFile.Utils; namespace LibObjectFile.Dwarf { @@ -18,7 +19,7 @@ public DwarfExpression() _operations = new List(); } - public IReadOnlyList Operations => _operations; + public ReadOnlyList Operations => _operations; internal List InternalOperations => _operations; diff --git a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs index 7f10d03..2d7a4f0 100644 --- a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using LibObjectFile.Utils; namespace LibObjectFile.Dwarf { @@ -16,7 +17,7 @@ public DwarfInfoSection() _units = new List(); } - public IReadOnlyList Units => _units; + public ReadOnlyList Units => _units; public void AddUnit(DwarfUnit unit) { diff --git a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs index a1f517f..f78d8d9 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.IO; using System.Text; +using LibObjectFile.Utils; namespace LibObjectFile.Dwarf { @@ -84,7 +85,7 @@ public byte MaximumOperationsPerInstruction public List FileNames { get; } - public IReadOnlyList LineSequences => _lineSequences; + public ReadOnlyList LineSequences => _lineSequences; public void AddLineSequence(DwarfLineSequence line) { diff --git a/src/LibObjectFile/Dwarf/DwarfLineSection.cs b/src/LibObjectFile/Dwarf/DwarfLineSection.cs index 22d094d..19fba82 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSection.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; +using LibObjectFile.Utils; namespace LibObjectFile.Dwarf { @@ -19,7 +20,7 @@ public DwarfLineSection() _tables = new List(); } - public IReadOnlyList LineTables => _tables; + public ReadOnlyList LineTables => _tables; public void AddLineProgramTable(DwarfLineProgramTable line) { diff --git a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs index 8846b71..23583e0 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using LibObjectFile.Utils; namespace LibObjectFile.Dwarf { @@ -21,7 +22,7 @@ public DwarfLineSequence() _lines = new List(); } - public IReadOnlyList Lines => _lines; + public ReadOnlyList Lines => _lines; public void Add(DwarfLine line) { diff --git a/src/LibObjectFile/Dwarf/DwarfLocationList.cs b/src/LibObjectFile/Dwarf/DwarfLocationList.cs index 4d872f8..2b4e3ec 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationList.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationList.cs @@ -4,6 +4,7 @@ using System.Text; using System.Collections.Generic; +using LibObjectFile.Utils; namespace LibObjectFile.Dwarf { @@ -17,7 +18,7 @@ public DwarfLocationList() _locationListEntries = new List(); } - public IReadOnlyList LocationListEntries => _locationListEntries; + public ReadOnlyList LocationListEntries => _locationListEntries; public void AddLocationListEntry(DwarfLocationListEntry locationListEntry) { diff --git a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs index b0ab892..95fe731 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; +using LibObjectFile.Utils; namespace LibObjectFile.Dwarf { @@ -18,7 +19,7 @@ public DwarfLocationSection() _locationLists = new List(); } - public IReadOnlyList LocationLists => _locationLists; + public ReadOnlyList LocationLists => _locationLists; public void AddLocationList(DwarfLocationList locationList) { diff --git a/src/LibObjectFile/Elf/ElfObjectFile.cs b/src/LibObjectFile/Elf/ElfObjectFile.cs index 38d227b..91ed97d 100644 --- a/src/LibObjectFile/Elf/ElfObjectFile.cs +++ b/src/LibObjectFile/Elf/ElfObjectFile.cs @@ -120,12 +120,12 @@ internal ElfObjectFile(bool addDefaultSections) /// /// List of the segments - program headers defined by this instance. /// - public IReadOnlyList Segments => _segments; + public ReadOnlyList Segments => _segments; /// /// List of the sections - program headers defined by this instance. /// - public IReadOnlyList Sections => _sections; + public ReadOnlyList Sections => _sections; /// /// Number of visible sections excluding in the . diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index e92c384..0540891 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -9,6 +9,7 @@ using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; using System.Text; +using LibObjectFile.Utils; namespace LibObjectFile.PE; @@ -82,7 +83,7 @@ public Stream? DosStubExtra /// /// Gets the sections. /// - public IReadOnlyList Sections => _sections; + public ReadOnlyList Sections => _sections; public PESection AddSection(PESectionName name, uint virtualAddress, uint virtualSize, SectionCharacteristics characteristics = SectionCharacteristics.MemRead) { diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index caa33fc..1f5bc89 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Reflection.PortableExecutable; using System.Text; +using LibObjectFile.Utils; namespace LibObjectFile.PE; @@ -85,7 +86,7 @@ public RVA VirtualAddress /// /// Gets the list of data associated with this section. /// - public IReadOnlyList DataParts => _dataParts; + public ReadOnlyList DataParts => _dataParts; /// /// Adds a new data to this section. diff --git a/src/LibObjectFile/Utils/ReadOnlyList.cs b/src/LibObjectFile/Utils/ReadOnlyList.cs new file mode 100644 index 0000000..5d6b29c --- /dev/null +++ b/src/LibObjectFile/Utils/ReadOnlyList.cs @@ -0,0 +1,73 @@ +// 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; + +namespace LibObjectFile.Utils; + +/// +/// A lightweight read-only wrapper around a List<T> that avoids the cost of interface dispatch from IReadOnlyList<T>. +/// +/// The type of the collection +[DebuggerTypeProxy(typeof(ReadOnlyList<>.ReadOnlyListView))] +[DebuggerDisplay("Count = {Count}")] +public readonly struct ReadOnlyList : IReadOnlyList +{ + private readonly List _items; + + /// + /// Initializes a new instance of the class. + /// + /// The items to wrap. + public ReadOnlyList(List items) => _items = items; + + /// + public int Count => _items.Count; + + /// + public T this[int index] => _items[index]; + + public List.Enumerator GetEnumerator() => _items.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + { + return _items.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_items).GetEnumerator(); + } + + /// + /// Converts a List<T> to a ReadOnlyList<T>. + /// + /// The list to convert. + public static implicit operator ReadOnlyList(List items) => new(items); + + internal sealed class ReadOnlyListView + { + private readonly List _collection; + + public ReadOnlyListView(List collection) + { + ArgumentNullException.ThrowIfNull((object)collection, nameof(collection)); + this._collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items + { + get + { + T[] array = new T[this._collection.Count]; + this._collection.CopyTo(array, 0); + return array; + } + } + } +} \ No newline at end of file From 3d2312e44afaba4a04cd8bff3b22142a6b7132af Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 17 Sep 2024 07:07:05 +0200 Subject: [PATCH 10/87] Add base PEObject.Read virtual method --- .../PEBaseRelocationDirectory.cs | 2 +- .../PE/DataDirectory/PEDirectory.cs | 77 +++++++++++++++---- src/LibObjectFile/PE/PEFile.Read.cs | 38 ++++----- src/LibObjectFile/PE/PEObject.cs | 2 + src/LibObjectFile/PE/PESection.cs | 5 ++ src/LibObjectFile/PE/PESectionData.cs | 17 +++- 6 files changed, 106 insertions(+), 35 deletions(-) diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs index 5cad1d2..fadd182 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs @@ -29,7 +29,7 @@ public override void UpdateLayout(DiagnosticBag diagnostics) Size = size; } - public unsafe void Read(PEImageReader reader) + protected override unsafe void Read(PEImageReader reader) { reader.Position = Position; var size = (int)Size; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs index f58442c..693af6b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs @@ -15,7 +15,8 @@ protected PEDirectory(ImageDataDirectoryKind kind) } public ImageDataDirectoryKind Kind { get; } - + + /// /// Factory method to create a new instance of based on the kind. /// @@ -56,19 +57,7 @@ public override void UpdateLayout(DiagnosticBag diagnostics) throw new NotImplementedException(); } - protected override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} - -public sealed class PEImportDirectory : PEDirectory -{ - public PEImportDirectory() : base(ImageDataDirectoryKind.Import) - { - } - - public override void UpdateLayout(DiagnosticBag diagnostics) + protected override void Read(PEImageReader reader) { throw new NotImplementedException(); } @@ -90,6 +79,11 @@ 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(); @@ -107,6 +101,11 @@ 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(); @@ -124,6 +123,11 @@ 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(); @@ -141,6 +145,11 @@ 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(); @@ -158,6 +167,11 @@ 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(); @@ -175,6 +189,11 @@ 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(); @@ -192,6 +211,11 @@ 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(); @@ -209,6 +233,11 @@ 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(); @@ -226,6 +255,11 @@ 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(); @@ -242,6 +276,11 @@ 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(); @@ -259,6 +298,11 @@ 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(); @@ -276,6 +320,11 @@ 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(); diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 01d1171..15d546c 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibObjectFile.PE.Internal; @@ -48,17 +47,17 @@ public static bool TryRead(Stream stream, [NotNullWhen(true)] out PEFile? peFile var reader = new PEImageReader(peFile, stream, options); diagnostics = reader.Diagnostics; - peFile.ReadFromInternal(reader); + peFile.Read(reader); return !reader.Diagnostics.HasErrors; } - internal void ReadFromInternal(PEImageReader imageReader) + protected override void Read(PEImageReader reader) { Debug.Assert(Unsafe.SizeOf() == 64); - var diagnostics = imageReader.Diagnostics; - int read = imageReader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref DosHeader, 1))); + var diagnostics = reader.Diagnostics; + int read = reader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref DosHeader, 1))); if (read != Unsafe.SizeOf()) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosHeaderSize, "Invalid DOS header"); @@ -76,7 +75,7 @@ internal void ReadFromInternal(PEImageReader imageReader) if (dosStubSize > 0) { var dosStub = new byte[dosStubSize]; - read = imageReader.Read(dosStub); + read = reader.Read(dosStub); if (read != dosStubSize) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosStubSize, "Invalid DOS stub"); @@ -92,14 +91,14 @@ internal void ReadFromInternal(PEImageReader imageReader) // Read any DOS stub extra data (e.g Rich) if (DosHeader.FileAddressPEHeader > read) { - _dosStubExtra = imageReader.ReadAsStream((ulong)(DosHeader.FileAddressPEHeader - read)); + _dosStubExtra = reader.ReadAsStream((ulong)(DosHeader.FileAddressPEHeader - read)); } // Read the PE signature - imageReader.Stream.Seek(DosHeader.FileAddressPEHeader, SeekOrigin.Begin); + reader.Stream.Seek(DosHeader.FileAddressPEHeader, SeekOrigin.Begin); var signature = default(ImagePESignature); - read = imageReader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref signature, 1))); + read = reader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref signature, 1))); if (read != sizeof(ImagePESignature)) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidPESignature, "Invalid PE signature"); @@ -115,7 +114,7 @@ internal void ReadFromInternal(PEImageReader imageReader) // Read the COFF header Debug.Assert(Unsafe.SizeOf() == 20); var coffHeader = default(ImageCoffHeader); - read = imageReader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref coffHeader, 1))); + read = reader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref coffHeader, 1))); if (read != Unsafe.SizeOf()) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidCoffHeaderSize, "Invalid COFF header"); @@ -127,7 +126,7 @@ internal void ReadFromInternal(PEImageReader imageReader) try { var optionalHeader = new Span(tempArray, 0, CoffHeader.SizeOfOptionalHeader); - read = imageReader.Read(optionalHeader); + read = reader.Read(optionalHeader); if (read != CoffHeader.SizeOfOptionalHeader) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidOptionalHeaderSize, "Invalid optional header"); @@ -187,7 +186,7 @@ internal void ReadFromInternal(PEImageReader imageReader) var sizeOfSections = CoffHeader.NumberOfSections * Unsafe.SizeOf(); tempArray = ArrayPool.Shared.Rent(sizeOfSections); var spanSections = new Span(tempArray, 0, sizeOfSections); - read = imageReader.Read(spanSections); + read = reader.Read(spanSections); var sectionHeaders = MemoryMarshal.Cast(spanSections); if (read != spanSections.Length) @@ -195,7 +194,7 @@ internal void ReadFromInternal(PEImageReader imageReader) diagnostics.Error(DiagnosticId.PE_ERR_InvalidSectionHeadersSize, "Invalid section headers"); } - InitializeSections(imageReader, sectionHeaders); + InitializeSections(reader, sectionHeaders); } finally { @@ -303,14 +302,15 @@ private void InitializeSections(PEImageReader imageReader, ReadOnlySpan protected override void Write(PEImageWriter writer) { diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index df3e7fa..0c53e46 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -42,6 +42,11 @@ public RVA VirtualAddress /// true if the specified virtual address is contained by this instance; otherwise, false. public bool ContainsVirtual(RVA virtualAddress) => VirtualAddress <= virtualAddress && virtualAddress < VirtualAddress + Size; + + 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}"); @@ -87,6 +92,11 @@ 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); } @@ -134,9 +144,14 @@ 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 From 194bd29011bda457138c3fdcc9349ffb34dd217b Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 17 Sep 2024 07:07:59 +0200 Subject: [PATCH 11/87] Rename BaseRelocationType enum --- src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs | 4 ++-- .../{BaseRelocationType.cs => PEBaseRelocationType.cs} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/LibObjectFile/PE/DataDirectory/{BaseRelocationType.cs => PEBaseRelocationType.cs} (98%) diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs index 882f1fe..e7a0c9d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs @@ -20,7 +20,7 @@ namespace LibObjectFile.PE; private readonly ushort _value; - public PEBaseRelocation(BaseRelocationType type, ushort virtualOffset) + public PEBaseRelocation(PEBaseRelocationType type, ushort virtualOffset) { if (virtualOffset > MaxVirtualOffset) { @@ -40,7 +40,7 @@ public PEBaseRelocation(BaseRelocationType type, ushort virtualOffset) /// /// Gets the type of the base relocation. /// - public BaseRelocationType Type => (BaseRelocationType)(_value & TypeMask); + public PEBaseRelocationType Type => (PEBaseRelocationType)(_value & TypeMask); /// /// Gets the virtual offset of the base relocation relative to the offset of the associated . diff --git a/src/LibObjectFile/PE/DataDirectory/BaseRelocationType.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationType.cs similarity index 98% rename from src/LibObjectFile/PE/DataDirectory/BaseRelocationType.cs rename to src/LibObjectFile/PE/DataDirectory/PEBaseRelocationType.cs index 02749a0..2473ee4 100644 --- a/src/LibObjectFile/PE/DataDirectory/BaseRelocationType.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationType.cs @@ -7,7 +7,7 @@ namespace LibObjectFile.PE; /// /// Represents the different relocation types for an image file. /// -public enum BaseRelocationType : ushort +public enum PEBaseRelocationType : ushort { /// /// The base relocation is skipped. This type can be used to pad a block. From c855ceaee3ed3660943202f9eb7f3dba754e21c2 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 17 Sep 2024 07:08:23 +0200 Subject: [PATCH 12/87] Start to add support for import directory --- src/LibObjectFile.sln.DotSettings | 3 + src/LibObjectFile/DiagnosticId.cs | 15 ++- .../PEBaseRelocationPageBlock.cs | 2 +- .../PE/DataDirectory/PEDirectoryTable.cs | 65 +++++++++- .../PE/DataDirectory/PEImportDirectory.cs | 60 +++++++++ .../DataDirectory/PEImportDirectoryEntry.cs | 14 +++ .../PE/DataDirectory/PEImportLookupEntry.cs | 24 ++++ .../PE/DataDirectory/PEImportLookupTable.cs | 118 ++++++++++++++++++ .../PE/Internal/RawImportDirectoryEntry.cs | 39 ++++++ .../PE/Internal/RawImportFunctionEntry32.cs | 20 +++ .../PE/Internal/RawImportFunctionEntry64.cs | 20 +++ src/LibObjectFile/PE/PEFile.cs | 4 +- src/LibObjectFile/PE/PESection.cs | 92 ++++++++++++-- src/LibObjectFile/PE/PESectionData.cs | 17 ++- .../PE/PESectionDataExtensions.cs | 64 ++++++++++ .../PE/PESectionName.Defaults.cs | 71 +++++++++++ src/LibObjectFile/PE/PESectionName.cs | 2 +- src/LibObjectFile/PE/RVALink.cs | 11 +- .../PE/ZeroTerminatedAsciiStringLink.cs | 10 ++ 19 files changed, 626 insertions(+), 25 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEImportLookupEntry.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImportDirectoryEntry.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImportFunctionEntry32.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs create mode 100644 src/LibObjectFile/PE/PESectionDataExtensions.cs create mode 100644 src/LibObjectFile/PE/PESectionName.Defaults.cs create mode 100644 src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs diff --git a/src/LibObjectFile.sln.DotSettings b/src/LibObjectFile.sln.DotSettings index 2569fe1..31038ce 100644 --- a/src/LibObjectFile.sln.DotSettings +++ b/src/LibObjectFile.sln.DotSettings @@ -10,9 +10,12 @@ See the license.txt file in the project root for more information. True True True + True True + True True True True True + True True \ No newline at end of file diff --git a/src/LibObjectFile/DiagnosticId.cs b/src/LibObjectFile/DiagnosticId.cs index caa3a3e..d6280b1 100644 --- a/src/LibObjectFile/DiagnosticId.cs +++ b/src/LibObjectFile/DiagnosticId.cs @@ -123,12 +123,17 @@ public enum DiagnosticId PE_ERR_InvalidOptionalHeaderSize = 3005, PE_ERR_InvalidOptionalHeaderMagic = 3006, PE_ERR_InvalidSectionHeadersSize = 3007, + PE_ERR_InvalidParent = 3008, // PE BaseRelocation - PE_ERR_BaseRelocationDirectoryInvalidEndOfStream = 3008, - PE_ERR_BaseRelocationDirectoryInvalidSection = 3009, - PE_ERR_BaseRelocationDirectoryInvalidSectionData = 3010, - PE_ERR_InvalidDataDirectorySection = 3011, - PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress = 3012, + PE_ERR_BaseRelocationDirectoryInvalidEndOfStream = 3020, + PE_ERR_BaseRelocationDirectoryInvalidSection = 3021, + PE_ERR_BaseRelocationDirectoryInvalidSectionData = 3022, + PE_ERR_InvalidDataDirectorySection = 3023, + PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress = 3024, + + // PE Import + PE_ERR_ImportDirectoryInvalidEndOfStream = 3040, + PE_ERR_ImportLookupTableInvalidEndOfStream = 3041, } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs index 8921f15..04862b1 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs @@ -52,6 +52,6 @@ internal uint SizeOf public override string ToString() { - return $"{nameof(PEBaseRelocationPageBlock)}, Section = {SectionRVALink.Element.Name}, RVA = {SectionRVALink.VirtualAddress}, Size = {SizeOf}, Parts[{Parts.Count}]"; + return $"{nameof(PEBaseRelocationPageBlock)}, Section = {SectionRVALink.Element?.Name}, RVA = {SectionRVALink.VirtualAddress}, Size = {SizeOf}, Parts[{Parts.Count}]"; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs index d651e1f..ba09832 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -3,6 +3,9 @@ // See the license.txt file in the project root for more information. using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -12,7 +15,7 @@ namespace LibObjectFile.PE; /// Contains the array of directory entries in a Portable Executable (PE) file. /// [DebuggerDisplay($"{nameof(PEDirectoryTable)} {nameof(Count)} = {{{nameof(Count)}}}")] -public sealed class PEDirectoryTable +public sealed class PEDirectoryTable : IEnumerable { private InternalArray _entries; private int _count; @@ -103,7 +106,12 @@ internal PEDirectoryTable() /// public PEClrMetadata? ClrMetadata => (PEClrMetadata?)this[ImageDataDirectoryKind.ClrMetadata]; - + /// + /// Gets the enumerator for the directory entries. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public Enumerator GetEnumerator() => new(this); + internal void Set(ImageDataDirectoryKind kind, PEDirectory? directory) { ref var entry = ref _entries[(int)kind]; @@ -126,4 +134,57 @@ private struct InternalArray { private PEDirectory? _element; } + + /// + /// Enumerator for the directory entries. + /// + public struct Enumerator : IEnumerator + { + private readonly PEDirectoryTable _table; + private int _index; + + internal Enumerator(PEDirectoryTable table) + { + _table = table; + _index = -1; + } + + public PEDirectory Current => _index >= 0 ? _table._entries[_index]! : null!; + + object? IEnumerator.Current => Current; + + + public void Dispose() + { + } + + public bool MoveNext() + { + Span entries = _table._entries; + while (++_index < entries.Length) + { + if (_table._entries[_index] is not null) + { + return true; + } + } + + return false; + } + + public void Reset() + { + _index = -1; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs new file mode 100644 index 0000000..c9537b4 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -0,0 +1,60 @@ +// 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.Runtime.InteropServices; +using LibObjectFile.PE.Internal; + +namespace LibObjectFile.PE; + +public sealed class PEImportDirectory : PEDirectory +{ + public PEImportDirectory() : base(ImageDataDirectoryKind.Import) + { + } + + public override void UpdateLayout(DiagnosticBag diagnostics) + { + throw new NotImplementedException(); + } + + protected override void Read(PEImageReader reader) + { + var diagnostics = reader.Diagnostics; + + // Read Import Directory Entries + RawImportDirectoryEntry entry = default; + var entrySpan = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref entry, 1)); + + while (true) + { + int read = reader.Read(entrySpan); + if (read != entrySpan.Length) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidEndOfStream, $"Unable to read the full content of the Import Directory. Expected {entrySpan.Length} bytes, but read {read} bytes"); + return; + } + + // 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) + { + // Last entry + break; + } + } + } + + protected override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } + + + + //private struct HintNameTableEntry + //{ + // public ushort Hint; + // public byte Name1stByte; + //} +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs new file mode 100644 index 0000000..537b99a --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs @@ -0,0 +1,14 @@ +// 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 class PEImportDirectoryEntry +{ + public ZeroTerminatedAsciiStringLink ImportDllNameLink; + + public uint ImportAddressTableEntryIndex; + +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupEntry.cs new file mode 100644 index 0000000..134d6d6 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupEntry.cs @@ -0,0 +1,24 @@ +// 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 new file mode 100644 index 0000000..6bb6c9b --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -0,0 +1,118 @@ +// 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; + +#pragma warning disable CS0649 +public class PEImportLookupTable : PESectionData +{ + public List Entries { get; } = new(); + + public override unsafe void UpdateLayout(DiagnosticBag diagnostics) + { + 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))); + } + + protected override unsafe void Read(PEImageReader reader) + { + 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)))); + + + + } + + } + + } + + protected override void Write(PEImageWriter writer) + { + if (writer.PEFile.OptionalHeader.Magic == ImageOptionalHeaderMagic.PE32) + { + 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 * 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); + } + } + + 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 * 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 + { + ArrayPool.Shared.Return(buffer); + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImportDirectoryEntry.cs b/src/LibObjectFile/PE/Internal/RawImportDirectoryEntry.cs new file mode 100644 index 0000000..7574f59 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImportDirectoryEntry.cs @@ -0,0 +1,39 @@ +// 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.Internal; + +internal struct RawImportDirectoryEntry +{ +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value + + /// + /// The RVA of the import lookup table. This table contains a name or ordinal for each import. (The name "Characteristics" is used in Winnt.h, but no longer describes this field.) + /// + public RVA ImportLookupTableRVA; + + /// + /// The stamp that is set to zero until the image is bound. After the image is bound, this field is set to the time/data stamp of the DLL. + /// + /// 0 if not bound, + /// -1 if bound, and real date\time stamp in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) + /// O.W. date/time stamp of DLL bound to (Old BIND) + /// + public uint TimeDateStamp; + + /// + /// The index of the first forwarder reference. -1 if no forwarders + /// + public uint ForwarderChain; + + /// + /// The address of an ASCII string that contains the name of the DLL. This address is relative to the image base. + /// + public RVA NameRVA; + + /// + /// The RVA of the import address table. The contents of this table are identical to the contents of the import lookup table until the image is bound. + /// + public RVA ImportAddressTableRVA; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImportFunctionEntry32.cs b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry32.cs new file mode 100644 index 0000000..933da76 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry32.cs @@ -0,0 +1,20 @@ +// 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.Internal; + +#pragma warning disable CS0649 +internal struct RawImportFunctionEntry32 +{ + public RawImportFunctionEntry32(uint hintNameTableRVA) + { + HintNameTableRVA = hintNameTableRVA; + } + + public uint HintNameTableRVA; + + public bool IsImportByOrdinal => (HintNameTableRVA & 0x8000_0000U) != 0; + + public ushort Ordinal => IsImportByOrdinal ? (ushort)HintNameTableRVA : (ushort)0; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs new file mode 100644 index 0000000..c38a9b9 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs @@ -0,0 +1,20 @@ +// 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.Internal; + +#pragma warning disable CS0649 +internal struct RawImportFunctionEntry64 +{ + public RawImportFunctionEntry64(ulong hintNameTableRVA) + { + HintNameTableRVA = hintNameTableRVA; + } + + public ulong HintNameTableRVA; + + public ushort Ordinal => IsImportByOrdinal ? (ushort)HintNameTableRVA : (ushort)0; + + public bool IsImportByOrdinal => (HintNameTableRVA & 0x8000_0000_0000_0000UL) != 0; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 0540891..b890e36 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -85,13 +85,13 @@ public Stream? DosStubExtra /// public ReadOnlyList Sections => _sections; - public PESection AddSection(PESectionName name, uint virtualAddress, uint virtualSize, SectionCharacteristics characteristics = SectionCharacteristics.MemRead) + public PESection AddSection(PESectionName name, uint virtualAddress, uint virtualSize) { var section = new PESection(this, name) { VirtualAddress = virtualAddress, VirtualSize = virtualSize, - Characteristics = characteristics + Characteristics = PESection.GetDefaultSectionCharacteristics(name) }; _sections.Add(section); return section; diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index 1deb75b..a8bf6fd 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -5,8 +5,12 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; using System.Reflection; using System.Reflection.PortableExecutable; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using LibObjectFile.Utils; @@ -22,7 +26,7 @@ public class PESection : PEObject, IVirtualAddressable internal PESection(PEFile peFile, PESectionName name) { - Parent = peFile; + base.Parent = peFile; Name = name; _dataParts = new List(); // Most of the time readable @@ -32,7 +36,7 @@ internal PESection(PEFile peFile, PESectionName name) /// /// Gets the parent of this section. /// - public PEFile? ImageFile => (PEFile?)Parent; + public new PEFile? Parent => (PEFile?)base.Parent; /// /// Gets the name of this section. @@ -96,8 +100,8 @@ public RVA VirtualAddress public void AddData(PESectionData data) { ArgumentNullException.ThrowIfNull(data); - if (data.Section != null) throw new ArgumentException("Data is already associated with a section", nameof(data)); - data.Section = this; + if (data.Parent != null) throw new ArgumentException("Data is already associated with a section", nameof(data)); + data.Parent = this; data.Index = (uint)_dataParts.Count; _dataParts.Add(data); UpdateSectionDataIndicesAndVirtualAddress((int)data.Index); @@ -108,8 +112,8 @@ public void InsertData(int index, PESectionData data) ArgumentNullException.ThrowIfNull(data); if (index < 0 || index > _dataParts.Count) throw new ArgumentOutOfRangeException(nameof(index)); - if (data.Section != null) throw new ArgumentException("Data is already associated with a section", nameof(data)); - data.Section = this; + if (data.Parent != null) throw new ArgumentException("Data is already associated with a section", nameof(data)); + data.Parent = this; data.Index = (uint)index; _dataParts.Insert(index, data); UpdateSectionDataIndicesAndVirtualAddress(index); @@ -123,7 +127,7 @@ public void InsertData(int index, PESectionData data) public void RemoveData(PESectionData data) { ArgumentNullException.ThrowIfNull(data); - if (data.Section != this) throw new ArgumentException("Data is not associated with this section", nameof(data)); + if (data.Parent != this) throw new ArgumentException("Data is not associated with this section", nameof(data)); var index = (int)data.Index; RemoveDataAt(index); } @@ -138,12 +142,52 @@ public void RemoveDataAt(int index) var list = _dataParts; var data = list[index]; - data.Section = null; + data.Parent = null; data.Index = 0; list.RemoveAt(index); UpdateSectionDataIndicesAndVirtualAddress(index); } + /// + /// Tries to find the section data that contains the specified virtual address. + /// + /// The virtual address to search for. + /// The section data that contains the virtual address, if found. + /// true if the section data was found; otherwise, false. + public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESectionData? sectionData) + { + // Binary search + nint left = 0; + + var dataParts = CollectionsMarshal.AsSpan(_dataParts); + nint right = dataParts.Length - 1; + ref var firstData = ref MemoryMarshal.GetReference(dataParts); + + while (left <= right) + { + nint mid = left + (right - left) >>> 1; + var trySectionData = Unsafe.Add(ref firstData, mid); + + if (trySectionData.ContainsVirtual(virtualAddress)) + { + sectionData = trySectionData; + return true; + } + + if (trySectionData.VirtualAddress < virtualAddress) + { + left = mid + 1; + } + else + { + right = mid - 1; + } + } + + sectionData = null; + return false; + } + /// /// Checks if the specified virtual address is contained by this section. /// @@ -198,6 +242,38 @@ protected override bool PrintMembers(StringBuilder builder) return true; } + /// + /// Gets the default characteristics for a section name. + /// + /// The name of the section + /// The default characteristics for the specified section name. + /// + /// The default characteristics are: + /// + /// .text: ContainsCode | MemExecute | MemRead + /// .data: ContainsInitializedData | MemRead | MemWrite + /// .bss: ContainsUninitializedData | MemRead | MemWrite + /// .idata: ContainsInitializedData | MemRead | MemWrite + /// .reloc: ContainsInitializedData | MemDiscardable | MemRead + /// .tls: ContainsInitializedData | MemRead | MemWrite + /// + /// + /// Otherwise the default characteristics is ContainsInitializedData | MemRead. + /// + public static SectionCharacteristics GetDefaultSectionCharacteristics(PESectionName sectionName) + { + return sectionName.Name switch + { + ".text" => SectionCharacteristics.ContainsCode | SectionCharacteristics.MemExecute | SectionCharacteristics.MemRead, + ".data" => SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemRead | SectionCharacteristics.MemWrite, + ".bss" => SectionCharacteristics.ContainsUninitializedData | SectionCharacteristics.MemRead | SectionCharacteristics.MemWrite, + ".idata" => SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemRead | SectionCharacteristics.MemWrite, + ".reloc" => SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemDiscardable | SectionCharacteristics.MemRead, + ".tls" => SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemRead | SectionCharacteristics.MemWrite, + _ => SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemRead + }; + } + private void UpdateSectionDataIndicesAndVirtualAddress(int startIndex) { var va = VirtualAddress; diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index 0c53e46..e61c40a 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using System.Runtime.CompilerServices; using System.Text; namespace LibObjectFile.PE; @@ -16,11 +17,11 @@ public abstract class PESectionData : PEObject, IVirtualAddressable /// /// Gets the parent of this section data. /// - public PESection? Section + public new PESection? Parent { - get => (PESection?)Parent; + get => (PESection?)base.Parent; - internal set => Parent = value; + internal set => base.Parent = value; } /// @@ -40,8 +41,18 @@ public RVA VirtualAddress /// /// The virtual address to check if it belongs to this instance. /// true if the specified virtual address is contained by this instance; otherwise, false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ContainsVirtual(RVA virtualAddress) => VirtualAddress <= virtualAddress && virtualAddress < VirtualAddress + Size; + public virtual int ReadAt(uint offset, Span destination) + { + throw new NotSupportedException($"The read operation is not supported for {this.GetType().FullName}"); + } + + 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); diff --git a/src/LibObjectFile/PE/PESectionDataExtensions.cs b/src/LibObjectFile/PE/PESectionDataExtensions.cs new file mode 100644 index 0000000..2bc0bcd --- /dev/null +++ b/src/LibObjectFile/PE/PESectionDataExtensions.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.Buffers; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace LibObjectFile.PE; + +public static class PESectionDataExtensions +{ + public static string ReadZeroTerminatedAsciiString(this PESectionData sectionData, uint offset) + { + Span buffer = stackalloc byte[256]; + byte[]? charBuffer = null; + Span currentString = default; + try + { + while (true) + { + int read = sectionData.ReadAt(offset, buffer); + if (read == 0) + { + break; + } + + var indexOfZero = buffer.IndexOf((byte)0); + var length = indexOfZero >= 0 ? indexOfZero : read; + + var sliceRead = buffer.Slice(0, length); + if (charBuffer is null) + { + return Encoding.ASCII.GetString(sliceRead); + } + + var byteCountRequired = Encoding.ASCII.GetCharCount(sliceRead) * 2; + if (byteCountRequired > charBuffer.Length - currentString.Length) + { + var minimumLength = Math.Min(byteCountRequired + charBuffer.Length, charBuffer.Length); + var newCharBuffer = ArrayPool.Shared.Rent(minimumLength); + currentString.CopyTo(newCharBuffer); + ArrayPool.Shared.Return(charBuffer); + charBuffer = newCharBuffer; + } + + var previousLength = currentString.Length; + currentString = charBuffer.AsSpan(0, previousLength + byteCountRequired); + Encoding.ASCII.GetChars(sliceRead, MemoryMarshal.Cast(currentString.Slice(previousLength))); + } + + return Encoding.ASCII.GetString(currentString); + } + finally + { + if (charBuffer != null) + { + ArrayPool.Shared.Return(charBuffer); + } + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESectionName.Defaults.cs b/src/LibObjectFile/PE/PESectionName.Defaults.cs new file mode 100644 index 0000000..7a0bd30 --- /dev/null +++ b/src/LibObjectFile/PE/PESectionName.Defaults.cs @@ -0,0 +1,71 @@ +// 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.Numerics; +using System.Text; + +namespace LibObjectFile.PE; + +/// +/// A section name in a Portable Executable (PE) image. +/// +partial record struct PESectionName +{ + /// + /// Represents the .text section, which contains executable code. + /// + public static PESectionName Text => new(".text", false); + + /// + /// Represents the .data section, which contains initialized data such as global variables. + /// + public static PESectionName Data => new(".data", false); + + /// + /// Represents the .rdata section, which contains read-only initialized data. + /// + public static PESectionName RData => new(".rdata", false); + + /// + /// Represents the .bss section, which contains uninitialized data. + /// + public static PESectionName Bss => new(".bss", false); + + /// + /// Represents the .edata section, which contains the export directory. + /// + public static PESectionName EData => new(".edata", false); + + /// + /// Represents the .idata section, which contains the import directory. + /// + // ReSharper disable once InconsistentNaming + public static PESectionName IData => new(".idata", false); + + /// + /// Represents the .reloc section, which contains the base relocation table. + /// + public static PESectionName Reloc => new(".reloc", false); + + /// + /// Represents the .rsrc section, which contains resources like icons, bitmaps, and strings. + /// + public static PESectionName Rsrc => new(".rsrc", false); + + /// + /// Represents the .tls section, which contains thread-local storage (TLS) data. + /// + public static PESectionName Tls => new(".tls", false); + + /// + /// Represents the .debug section, which contains debug information. + /// + public static PESectionName Debug => new(".debug", false); + + /// + /// Represents the .pdata section, which contains exception-handling information for 64-bit code. + /// + public static PESectionName PData => new(".pdata", false); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESectionName.cs b/src/LibObjectFile/PE/PESectionName.cs index e3c5ba8..6fe9af7 100644 --- a/src/LibObjectFile/PE/PESectionName.cs +++ b/src/LibObjectFile/PE/PESectionName.cs @@ -11,7 +11,7 @@ namespace LibObjectFile.PE; /// /// A section name in a Portable Executable (PE) image. /// -public readonly record struct PESectionName +public readonly partial record struct PESectionName { /// /// Internal constructor used to bypass the validation of the section name. diff --git a/src/LibObjectFile/PE/RVALink.cs b/src/LibObjectFile/PE/RVALink.cs index ae39f02..08923ae 100644 --- a/src/LibObjectFile/PE/RVALink.cs +++ b/src/LibObjectFile/PE/RVALink.cs @@ -12,11 +12,16 @@ namespace LibObjectFile.PE; /// The type of the virtual addressable object. /// The virtual addressable object linked. /// The offset within this element. -public record struct RVALink(TVirtualAddressable Element, uint OffsetInElement) - where TVirtualAddressable : IVirtualAddressable +public record struct RVALink(TVirtualAddressable? Element, uint OffsetInElement) + where TVirtualAddressable : class, IVirtualAddressable { + /// + /// Gets a value indicating whether this instance is null. + /// + public bool IsNull => Element == null; + /// /// Gets the virtual address of within the element. /// - public RVA VirtualAddress => Element.VirtualAddress + OffsetInElement; + public RVA VirtualAddress => (Element?.VirtualAddress ?? 0) + OffsetInElement; } diff --git a/src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs b/src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs new file mode 100644 index 0000000..3ea9613 --- /dev/null +++ b/src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs @@ -0,0 +1,10 @@ +// 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; + +public record struct ZeroTerminatedAsciiStringLink(RVALink Link) +{ + public string? GetName() => Link.Element?.ReadZeroTerminatedAsciiString(Link.OffsetInElement); +} \ No newline at end of file From b46e2bc62b52373671f4fd554386f5bd609c207d Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 18 Sep 2024 06:22:03 +0200 Subject: [PATCH 13/87] Add ObjectList to manage ownership/parent more easily --- src/LibObjectFile/Ar/ArArchiveFile.cs | 4 +- src/LibObjectFile/DiagnosticBag.cs | 157 +++++++------- src/LibObjectFile/DiagnosticId.cs | 6 +- src/LibObjectFile/Elf/ElfObjectFile.cs | 8 +- src/LibObjectFile/ObjectFileExtensions.cs | 6 +- src/LibObjectFile/ObjectFileNodeBase.cs | 14 +- .../PE/DataDirectory/PEDirectory.cs | 32 +-- .../PE/DataDirectory/PEDirectoryTable.cs | 2 +- .../PE/DataDirectory/PEImportAddressTable.cs | 50 +++++ .../PEImportAddressTableDirectory.cs | 35 ++++ .../PE/DataDirectory/PEImportDirectory.cs | 68 +++++- .../DataDirectory/PEImportDirectoryEntry.cs | 62 +++++- .../PE/DataDirectory/PEImportFunctionEntry.cs | 52 +++++ .../PE/DataDirectory/PEImportFunctionTable.cs | 169 +++++++++++++++ .../PE/DataDirectory/PEImportLookupEntry.cs | 24 --- .../PE/DataDirectory/PEImportLookupTable.cs | 112 +++------- .../PE/Internal/RawImportFunctionEntry32.cs | 10 +- .../PE/Internal/RawImportFunctionEntry64.cs | 10 +- src/LibObjectFile/PE/PEFile.cs | 42 +++- src/LibObjectFile/PE/PEObject.cs | 5 + src/LibObjectFile/PE/PESection.cs | 34 ++- src/LibObjectFile/PE/PESectionData.cs | 106 +--------- src/LibObjectFile/PE/PESectionStreamData.cs | 64 ++++++ .../PE/ZeroTerminatedAsciiStringLink.cs | 4 +- src/LibObjectFile/Utils/ObjectList.cs | 196 ++++++++++++++++++ 25 files changed, 906 insertions(+), 366 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs delete mode 100644 src/LibObjectFile/PE/DataDirectory/PEImportLookupEntry.cs create mode 100644 src/LibObjectFile/PE/PESectionStreamData.cs create mode 100644 src/LibObjectFile/Utils/ObjectList.cs 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; + } + } + } +} From f5f23820fe2dabc2a324ef7f746ef4d1bc5a1d34 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 18 Sep 2024 09:02:18 +0200 Subject: [PATCH 14/87] Extend usage of ObjectList --- src/LibObjectFile/PE/PEFile.Read.cs | 25 ++--- src/LibObjectFile/PE/PEFile.cs | 18 ++-- src/LibObjectFile/PE/PESection.cs | 127 +++++++------------------- src/LibObjectFile/Utils/ObjectList.cs | 18 +++- 4 files changed, 61 insertions(+), 127 deletions(-) diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 15d546c..6c4292d 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -210,18 +210,11 @@ private void InitializeSections(PEImageReader imageReader, ReadOnlySpan _sections = new(); + private readonly ObjectList _sections; /// /// Initializes a new instance of the class. /// public PEFile() { + _sections = new(this); // TODO: Add default initialization } @@ -36,6 +37,7 @@ public PEFile() /// internal PEFile(bool unused) { + _sections = new(this); } /// @@ -94,23 +96,17 @@ public Stream? DosStubExtra /// /// Gets the sections. /// - public ReadOnlyList Sections => _sections; + public ObjectList Sections => _sections; public PESection AddSection(PESectionName name, uint virtualAddress, uint virtualSize) { - var section = new PESection(this, name) + var section = new PESection(name, virtualAddress, virtualSize) { - VirtualAddress = virtualAddress, - VirtualSize = virtualSize, Characteristics = PESection.GetDefaultSectionCharacteristics(name) }; _sections.Add(section); return section; } - - public void RemoveSectionAt(int index) => _sections.RemoveAt(index); - - public void RemoveSection(PESection section) => _sections.Remove(section); public bool TryFindSection(RVA virtualAddress, [NotNullWhen(true)] out PESection? section) => TryFindSection(virtualAddress, 0, out section); @@ -118,7 +114,7 @@ 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); + var sections = CollectionsMarshal.AsSpan(_sections.UnsafeList); nint high = sections.Length - 1; ref var firstSection = ref MemoryMarshal.GetReference(sections); @@ -180,7 +176,7 @@ public PESection GetSection(PESectionName name) public bool TryGetSection(PESectionName name, [NotNullWhen(true)] out PESection? section) { ArgumentNullException.ThrowIfNull(name); - var sections = CollectionsMarshal.AsSpan(_sections); + var sections = CollectionsMarshal.AsSpan(_sections.UnsafeList); foreach (var trySection in sections) { if (trySection.Name == name) diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index 3053c4d..7a37481 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -21,14 +21,14 @@ namespace LibObjectFile.PE; /// public class PESection : PEObject, IVirtualAddressable { - private readonly List _dataParts; - private RVA _virtualAddress; + private readonly ObjectList _dataParts; - internal PESection(PEFile peFile, PESectionName name) + public PESection(PESectionName name, RVA virtualAddress, RVA virtualSize) { - base.Parent = peFile; Name = name; - _dataParts = new List(); + VirtualAddress = virtualAddress; + VirtualSize = virtualSize; + _dataParts = new ObjectList(this, SectionDataAdded, null, SectionDataRemoved, SectionDataUpdated); // Most of the time readable Characteristics = SectionCharacteristics.MemRead; } @@ -46,42 +46,14 @@ internal PESection(PEFile peFile, PESectionName name) /// /// The address of the first byte of the section when loaded into memory, relative to the image base. /// - public RVA VirtualAddress - { - get => _virtualAddress; - set - { - _virtualAddress = value; - UpdateSectionDataIndicesAndVirtualAddress(0); - } - } + public RVA VirtualAddress { get; } /// /// The total size of the section when loaded into memory. /// If this value is greater than , the section is zero-padded. /// - public uint VirtualSize { get; set; } + public uint VirtualSize { get; } - /// - /// The file pointer to the beginning of the relocation entries for the section, if present. - /// - public uint PointerToRelocations { get; set; } - - /// - /// The file pointer to the beginning of the line-number entries for the section, if present. - /// - public uint PointerToLineNumbers { get; set; } - - /// - /// The number of relocation entries for the section. - /// - public ushort NumberOfRelocations { get; set; } - - /// - /// The number of line-number entries for the section. - /// - public ushort NumberOfLineNumbers { get; set; } - /// /// Flags that describe the characteristics of the section. /// @@ -90,63 +62,7 @@ public RVA VirtualAddress /// /// Gets the list of data associated with this section. /// - public ReadOnlyList DataParts => _dataParts; - - /// - /// Adds a new data to this section. - /// - /// The data to add. - /// If the data is already associated with a section. - 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 = _dataParts.Count; - _dataParts.Add(data); - UpdateSectionDataIndicesAndVirtualAddress((int)data.Index); - } - - public void InsertData(int index, PESectionData data) - { - ArgumentNullException.ThrowIfNull(data); - if (index < 0 || index > _dataParts.Count) throw new ArgumentOutOfRangeException(nameof(index)); - - if (data.Parent != null) throw new ArgumentException("Data is already associated with a section", nameof(data)); - data.Parent = this; - data.Index = index; - _dataParts.Insert(index, data); - UpdateSectionDataIndicesAndVirtualAddress(index); - } - - /// - /// Removes the specified data from this section. - /// - /// The data to remove. - /// If the data is already associated with a section. - public void RemoveData(PESectionData data) - { - ArgumentNullException.ThrowIfNull(data); - if (data.Parent != this) throw new ArgumentException("Data is not associated with this section", nameof(data)); - var index = (int)data.Index; - RemoveDataAt(index); - } - - /// - /// Removes the data at the specified index. - /// - /// The index of the data to remove. - public void RemoveDataAt(int index) - { - if (index < 0 || index > _dataParts.Count) throw new ArgumentOutOfRangeException(nameof(index)); - - var list = _dataParts; - var data = list[index]; - data.Parent = null; - data.Index = 0; - list.RemoveAt(index); - UpdateSectionDataIndicesAndVirtualAddress(index); - } + public ObjectList DataParts => _dataParts; /// /// Tries to find the section data that contains the specified virtual address. @@ -159,7 +75,7 @@ public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESec // Binary search nint low = 0; - var dataParts = CollectionsMarshal.AsSpan(_dataParts); + var dataParts = CollectionsMarshal.AsSpan(_dataParts.UnsafeList); nint high = dataParts.Length - 1; ref var firstData = ref MemoryMarshal.GetReference(dataParts); @@ -210,9 +126,12 @@ public bool ContainsVirtual(RVA virtualAddress, uint size) /// public override void UpdateLayout(DiagnosticBag diagnostics) { + var va = VirtualAddress; foreach (var data in DataParts) { + data.VirtualAddress = va; data.UpdateLayout(diagnostics); + va += (uint)data.Size; } } @@ -271,11 +190,28 @@ public static SectionCharacteristics GetDefaultSectionCharacteristics(PESectionN _ => SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemRead }; } + + private static void SectionDataAdded(ObjectFileNode parent, PESectionData sectionData) + { + var section = (PESection) parent; + section.UpdateSectionDataVirtualAddress(sectionData.Index); + } + + private static void SectionDataRemoved(ObjectFileNode parent, int index, PESectionData removedSectionData) + { + var section = (PESection) parent; + section.UpdateSectionDataVirtualAddress(index); + } + private static void SectionDataUpdated(ObjectFileNode parent, int index, PESectionData previousSectionData, PESectionData newSectionData) + { + var section = (PESection) parent; + section.UpdateSectionDataVirtualAddress(index); + } - private void UpdateSectionDataIndicesAndVirtualAddress(int startIndex) + private void UpdateSectionDataVirtualAddress(int startIndex) { var va = VirtualAddress; - var list = _dataParts; + var list = _dataParts.UnsafeList; if (startIndex > 0) { var previousData = list[startIndex - 1]; @@ -285,7 +221,6 @@ private void UpdateSectionDataIndicesAndVirtualAddress(int startIndex) for (int i = startIndex; i < list.Count; i++) { var data = list[i]; - data.Index = i; data.VirtualAddress = va; va += (uint)data.Size; } diff --git a/src/LibObjectFile/Utils/ObjectList.cs b/src/LibObjectFile/Utils/ObjectList.cs index 62c19d6..efe1b52 100644 --- a/src/LibObjectFile/Utils/ObjectList.cs +++ b/src/LibObjectFile/Utils/ObjectList.cs @@ -29,10 +29,10 @@ namespace LibObjectFile.Utils; /// Initializes a new instance of the class. /// /// The parent object file node. - public ObjectList(ObjectFileNode parent, Action? added = null, Action? removing = null) + public ObjectList(ObjectFileNode parent, Action? added = null, Action? removing = null, Action? removed = null, Action? updated = null) { ArgumentNullException.ThrowIfNull(parent); - _items = new InternalList(parent, added, removing); + _items = new InternalList(parent, added, removing, removed, updated); } public int Count => _items.Count; @@ -59,9 +59,9 @@ public void Clear() var item = items[i]; items.Removing(item); items.RemoveAt(i); - item.Parent = null; item.ResetIndex(); + items.Removed(i, item); } items.Clear(); @@ -132,7 +132,7 @@ public TObject this[int index] // Bind new entry items[index] = value; value.Index = index; - items.Added(value); + items.Updated(index, previousItem, value); } } @@ -159,10 +159,12 @@ public TObject CheckAdd(TObject item) return item; } - private sealed class InternalList(ObjectFileNode parent, Action? added, Action? removing) : List + private sealed class InternalList(ObjectFileNode parent, Action? added, Action? removing, Action? removed, Action? updated) : List { private readonly Action? _added = added; private readonly Action? _removing = removing; + private readonly Action? _removed = removed; + private readonly Action? _updated = updated; public readonly ObjectFileNode Parent = parent; [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -170,6 +172,12 @@ private sealed class InternalList(ObjectFileNode parent, Action _removing?.Invoke(Parent, item); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Updated(int index, TObject previousItem, TObject newItem) => _updated?.Invoke(Parent, index, previousItem, newItem); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Removed(int index, TObject removedItem) => _removed?.Invoke(Parent, index, removedItem); } internal sealed class ObjectListDebuggerView From 5ad7a90fe13d610b1f5f782a600bddb18b758245 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 18 Sep 2024 21:55:28 +0200 Subject: [PATCH 15/87] Breaking change refactoring - Uniformize all ObjectFileElement --- src/LibObjectFile/Ar/ArArchiveFile.cs | 39 +- src/LibObjectFile/Ar/ArArchiveFileReader.cs | 492 ++-- .../Ar/ArArchiveFileReaderOptions.cs | 57 +- src/LibObjectFile/Ar/ArArchiveFileWriter.cs | 288 ++- src/LibObjectFile/Ar/ArArchiveKind.cs | 43 +- src/LibObjectFile/Ar/ArBinaryFile.cs | 42 +- src/LibObjectFile/Ar/ArElfFile.cs | 76 +- src/LibObjectFile/Ar/ArFile.cs | 213 +- src/LibObjectFile/Ar/ArFileEntry.Constants.cs | 155 +- src/LibObjectFile/Ar/ArLongNamesTable.cs | 198 +- src/LibObjectFile/Ar/ArObject.cs | 21 +- src/LibObjectFile/Ar/ArSymbol.cs | 65 +- src/LibObjectFile/Ar/ArSymbolTable.cs | 331 ++- src/LibObjectFile/Ar/ArVisitorContext.cs | 12 + src/LibObjectFile/DiagnosticId.cs | 261 +- src/LibObjectFile/DiagnosticKind.cs | 25 +- src/LibObjectFile/DiagnosticMessage.cs | 77 +- src/LibObjectFile/Dwarf/DwarfAbbreviation.cs | 323 ++- .../Dwarf/DwarfAbbreviationItem.cs | 171 +- .../Dwarf/DwarfAbbreviationItemKey.cs | 77 +- .../Dwarf/DwarfAbbreviationTable.cs | 113 +- src/LibObjectFile/Dwarf/DwarfAccessibility.cs | 13 +- src/LibObjectFile/Dwarf/DwarfAddressRange.cs | 29 +- .../Dwarf/DwarfAddressRangeTable.cs | 362 ++- src/LibObjectFile/Dwarf/DwarfAddressSize.cs | 17 +- .../Dwarf/DwarfArrayOrderingKind.cs | 11 +- src/LibObjectFile/Dwarf/DwarfAttribute.cs | 1672 ++++++------- .../Dwarf/DwarfAttributeDescriptor.cs | 67 +- .../Dwarf/DwarfAttributeDescriptors.cs | 103 +- .../Dwarf/DwarfAttributeEncoding.cs | 47 +- .../Dwarf/DwarfAttributeFormEx.cs | 87 +- .../Dwarf/DwarfAttributeKindEx.cs | 87 +- .../Dwarf/DwarfAttributeValue.cs | 21 +- .../Dwarf/DwarfCallingConventionEx.cs | 95 +- .../Dwarf/DwarfCompilationUnit.cs | 115 +- src/LibObjectFile/Dwarf/DwarfConstant.cs | 95 +- src/LibObjectFile/Dwarf/DwarfContainer.cs | 11 +- src/LibObjectFile/Dwarf/DwarfDIE.cs | 759 +++--- .../Dwarf/DwarfDIEDeclaration.cs | 35 +- .../Dwarf/DwarfDiscriminantListKind.cs | 11 +- src/LibObjectFile/Dwarf/DwarfElfContext.cs | 489 ++-- src/LibObjectFile/Dwarf/DwarfExpression.cs | 181 +- src/LibObjectFile/Dwarf/DwarfFile.cs | 715 +++--- src/LibObjectFile/Dwarf/DwarfFileName.cs | 33 +- src/LibObjectFile/Dwarf/DwarfHelper.cs | 191 +- .../Dwarf/DwarfIdentifierCaseKind.cs | 15 +- src/LibObjectFile/Dwarf/DwarfInfoSection.cs | 141 +- src/LibObjectFile/Dwarf/DwarfInlineKind.cs | 15 +- src/LibObjectFile/Dwarf/DwarfInteger.cs | 47 +- .../Dwarf/DwarfLanguageKindEx.cs | 95 +- src/LibObjectFile/Dwarf/DwarfLayoutConfig.cs | 43 +- src/LibObjectFile/Dwarf/DwarfLayoutContext.cs | 36 +- src/LibObjectFile/Dwarf/DwarfLine.cs | 331 +-- .../Dwarf/DwarfLineProgramTable.cs | 2197 ++++++++--------- src/LibObjectFile/Dwarf/DwarfLineSection.cs | 120 +- src/LibObjectFile/Dwarf/DwarfLineSequence.cs | 97 +- src/LibObjectFile/Dwarf/DwarfLineState.cs | 287 ++- src/LibObjectFile/Dwarf/DwarfLocation.cs | 93 +- src/LibObjectFile/Dwarf/DwarfLocationList.cs | 136 +- .../Dwarf/DwarfLocationListEntry.cs | 124 +- .../Dwarf/DwarfLocationSection.cs | 120 +- src/LibObjectFile/Dwarf/DwarfObject.cs | 36 +- src/LibObjectFile/Dwarf/DwarfOperation.cs | 2147 ++++++++-------- .../Dwarf/DwarfOperationKindEx.cs | 77 +- src/LibObjectFile/Dwarf/DwarfPrinter.cs | 357 ++- src/LibObjectFile/Dwarf/DwarfReader.cs | 231 +- src/LibObjectFile/Dwarf/DwarfReaderContext.cs | 37 +- src/LibObjectFile/Dwarf/DwarfReaderWriter.cs | 248 +- .../Dwarf/DwarfReaderWriterContext.cs | 25 +- .../Dwarf/DwarfRelocatableSection.cs | 83 +- src/LibObjectFile/Dwarf/DwarfRelocation.cs | 35 +- .../Dwarf/DwarfRelocationTarget.cs | 15 +- src/LibObjectFile/Dwarf/DwarfSection.cs | 33 +- src/LibObjectFile/Dwarf/DwarfSectionLink.cs | 73 +- .../Dwarf/DwarfStreamExtensions.cs | 137 +- src/LibObjectFile/Dwarf/DwarfStringTable.cs | 123 +- src/LibObjectFile/Dwarf/DwarfTagEx.cs | 89 +- src/LibObjectFile/Dwarf/DwarfUnit.cs | 350 ++- src/LibObjectFile/Dwarf/DwarfUnitKind.cs | 81 +- src/LibObjectFile/Dwarf/DwarfVirtuality.cs | 13 +- src/LibObjectFile/Dwarf/DwarfVisibility.cs | 13 +- src/LibObjectFile/Dwarf/DwarfWriter.cs | 49 +- src/LibObjectFile/Dwarf/DwarfWriterContext.cs | 25 +- src/LibObjectFile/Elf/ElfArch.cs | 93 +- src/LibObjectFile/Elf/ElfDecoderDirect.cs | 189 +- src/LibObjectFile/Elf/ElfDecoderSwap.cs | 189 +- src/LibObjectFile/Elf/ElfEncoderDirect.cs | 189 +- src/LibObjectFile/Elf/ElfEncoderSwap.cs | 189 +- src/LibObjectFile/Elf/ElfEncoding.cs | 37 +- src/LibObjectFile/Elf/ElfFileClass.cs | 38 +- src/LibObjectFile/Elf/ElfFilePart.cs | 127 +- src/LibObjectFile/Elf/ElfFilePartList.cs | 109 +- src/LibObjectFile/Elf/ElfFileType.cs | 53 +- src/LibObjectFile/Elf/ElfHeaderFlags.cs | 77 +- src/LibObjectFile/Elf/ElfNativeExtensions.cs | 149 +- src/LibObjectFile/Elf/ElfOSAbi2.cs | 87 +- src/LibObjectFile/Elf/ElfObject.cs | 11 +- src/LibObjectFile/Elf/ElfObjectFile.cs | 35 +- .../Elf/ElfObjectFileExtensions.cs | 123 +- src/LibObjectFile/Elf/ElfPrinter.cs | 1301 +++++----- src/LibObjectFile/Elf/ElfReader.cs | 90 +- src/LibObjectFile/Elf/ElfReaderDirect.cs | 15 +- src/LibObjectFile/Elf/ElfReaderOptions.cs | 67 +- src/LibObjectFile/Elf/ElfReaderSwap.cs | 15 +- src/LibObjectFile/Elf/ElfReader{TDecoder}.cs | 1297 +++++----- src/LibObjectFile/Elf/ElfSection.cs | 301 ++- src/LibObjectFile/Elf/ElfSectionExtension.cs | 277 ++- src/LibObjectFile/Elf/ElfSectionFlags.cs | 103 +- src/LibObjectFile/Elf/ElfSectionLink.cs | 203 +- .../Elf/ElfSectionSpecialType.cs | 63 +- src/LibObjectFile/Elf/ElfSectionType.cs | 249 +- src/LibObjectFile/Elf/ElfSegment.cs | 216 +- src/LibObjectFile/Elf/ElfSegmentFlags.cs | 101 +- src/LibObjectFile/Elf/ElfSegmentFlagsCore.cs | 43 +- src/LibObjectFile/Elf/ElfSegmentRange.cs | 223 +- src/LibObjectFile/Elf/ElfSegmentType.cs | 99 +- src/LibObjectFile/Elf/ElfSegmentTypeCore.cs | 89 +- src/LibObjectFile/Elf/ElfString.cs | 159 +- src/LibObjectFile/Elf/ElfVisitorContext.cs | 12 + src/LibObjectFile/Elf/ElfWriter.cs | 70 +- src/LibObjectFile/Elf/ElfWriterDirect.cs | 15 +- src/LibObjectFile/Elf/ElfWriterSwap.cs | 15 +- src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs | 501 ++-- src/LibObjectFile/Elf/IElfDecoder.cs | 51 +- src/LibObjectFile/Elf/IElfEncoder.cs | 51 +- .../Elf/Sections/ElfAlignedShadowSection.cs | 87 +- .../Elf/Sections/ElfBinarySection.cs | 103 +- .../Elf/Sections/ElfBinaryShadowSection.cs | 45 +- .../Elf/Sections/ElfCustomNote.cs | 147 +- src/LibObjectFile/Elf/Sections/ElfGnuNote.cs | 11 +- .../Elf/Sections/ElfGnuNoteABITag.cs | 119 +- .../Elf/Sections/ElfGnuNoteBuildId.cs | 63 +- .../Elf/Sections/ElfGnuNoteOSKind.cs | 41 +- src/LibObjectFile/Elf/Sections/ElfNote.cs | 67 +- .../Elf/Sections/ElfNoteTable.cs | 223 +- src/LibObjectFile/Elf/Sections/ElfNoteType.cs | 89 +- .../Elf/Sections/ElfNullSection.cs | 52 +- .../Elf/Sections/ElfProgramHeaderTable.cs | 125 +- .../Elf/Sections/ElfRelocation.cs | 95 +- .../Elf/Sections/ElfRelocationContext.cs | 21 +- .../Elf/Sections/ElfRelocationTable.cs | 447 ++-- .../Sections/ElfRelocationTableExtensions.cs | 407 ++- .../Elf/Sections/ElfRelocationType.cs | 89 +- .../Sections/ElfSectionHeaderStringTable.cs | 21 +- .../Elf/Sections/ElfShadowSection.cs | 21 +- .../Elf/Sections/ElfStringTable.cs | 448 ++-- src/LibObjectFile/Elf/Sections/ElfSymbol.cs | 145 +- .../Elf/Sections/ElfSymbolBind.cs | 93 +- .../Elf/Sections/ElfSymbolTable.cs | 408 ++- .../ElfSymbolTableSectionHeaderIndices.cs | 175 +- .../Elf/Sections/ElfSymbolType.cs | 125 +- .../Elf/Sections/ElfSymbolVisibility.cs | 45 +- src/LibObjectFile/IObjectFileNodeLink.cs | 11 +- ...ctFileNodeBase.cs => ObjectFileElement.cs} | 88 +- src/LibObjectFile/ObjectFileException.cs | 29 +- src/LibObjectFile/ObjectFileExtensions.cs | 213 +- src/LibObjectFile/ObjectFileNode.cs | 17 - src/LibObjectFile/ObjectFileReaderWriter.cs | 519 ++-- .../ObjectFileStreamExtensions.cs | 339 ++- .../PEBaseRelocationDirectory.cs | 10 +- .../PE/DataDirectory/PEDirectory.cs | 85 +- .../PE/DataDirectory/PEImportAddressTable.cs | 26 +- .../PEImportAddressTableDirectory.cs | 10 +- .../PE/DataDirectory/PEImportDirectory.cs | 14 +- .../DataDirectory/PEImportDirectoryEntry.cs | 10 +- .../PE/DataDirectory/PEImportFunctionTable.cs | 2 +- .../PE/DataDirectory/PEImportLookupTable.cs | 26 +- src/LibObjectFile/PE/PEFile.Read.cs | 4 +- src/LibObjectFile/PE/PEFile.Write.cs | 8 +- src/LibObjectFile/PE/PEFile.cs | 7 +- src/LibObjectFile/PE/PEImageReader.cs | 5 +- src/LibObjectFile/PE/PEImageWriter.cs | 5 +- src/LibObjectFile/PE/PEObject.cs | 38 +- src/LibObjectFile/PE/PESection.cs | 14 +- src/LibObjectFile/PE/PESectionData.cs | 6 - src/LibObjectFile/PE/PESectionStreamData.cs | 8 +- src/LibObjectFile/PE/PEVisitorContext.cs | 12 + src/LibObjectFile/RelocationSize.cs | 15 +- src/LibObjectFile/Utils/AlignHelper.cs | 51 +- src/LibObjectFile/Utils/ObjectList.cs | 16 +- src/LibObjectFile/Utils/ThrowHelper.cs | 109 +- src/LibObjectFile/ValueKind.cs | 25 +- src/LibObjectFile/VisitorContextBase.cs | 33 + 183 files changed, 14553 insertions(+), 14734 deletions(-) create mode 100644 src/LibObjectFile/Ar/ArVisitorContext.cs create mode 100644 src/LibObjectFile/Elf/ElfVisitorContext.cs rename src/LibObjectFile/{ObjectFileNodeBase.cs => ObjectFileElement.cs} (65%) delete mode 100644 src/LibObjectFile/ObjectFileNode.cs create mode 100644 src/LibObjectFile/PE/PEVisitorContext.cs create mode 100644 src/LibObjectFile/VisitorContextBase.cs diff --git a/src/LibObjectFile/Ar/ArArchiveFile.cs b/src/LibObjectFile/Ar/ArArchiveFile.cs index 9947e01..301b646 100644 --- a/src/LibObjectFile/Ar/ArArchiveFile.cs +++ b/src/LibObjectFile/Ar/ArArchiveFile.cs @@ -13,7 +13,7 @@ namespace LibObjectFile.Ar; /// /// An 'ar' archive file. /// -public sealed class ArArchiveFile : ObjectFileNode +public sealed class ArArchiveFile : ArObjectBase { private readonly List _files; @@ -179,9 +179,23 @@ public ArFile RemoveFileAt(int index) return file; } - public override void Verify(DiagnosticBag diagnostics) + public DiagnosticBag Verify() { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + var diagnostics = new DiagnosticBag(); + Verify(diagnostics); + return diagnostics; + } + + public void Verify(DiagnosticBag diagnostics) + { + ArgumentNullException.ThrowIfNull(diagnostics); + var context = new ArVisitorContext(this, diagnostics); + Verify(context); + } + + public override void Verify(ArVisitorContext context) + { + var diagnostics = context.Diagnostics; for (var i = 0; i < Files.Count; i++) { @@ -199,7 +213,7 @@ public override void Verify(DiagnosticBag diagnostics) } } - item.Verify(diagnostics); + item.Verify(context); } } @@ -309,10 +323,17 @@ public void Write(Stream stream) var writer = new ArArchiveFileWriter(this, stream); writer.Write(); } - - public override void UpdateLayout(DiagnosticBag diagnostics) + + public override void UpdateLayout(ArVisitorContext visitorContext) + { + + } + + + public void UpdateLayout(DiagnosticBag diagnostics) { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + ArgumentNullException.ThrowIfNull(diagnostics); + var layoutContext = new ArVisitorContext(this, diagnostics); Size = 0; @@ -353,13 +374,13 @@ public override void UpdateLayout(DiagnosticBag diagnostics) { var entry = Files[i]; - entry.UpdateLayout(diagnostics); + entry.UpdateLayout(layoutContext); if (diagnostics.HasErrors) return; // If we have a GNU headers and they are required, add them to the offset and size if (LongNamesTable != null && LongNamesTable.Index == i) { - LongNamesTable.UpdateLayout(diagnostics); + LongNamesTable.UpdateLayout(layoutContext); if (diagnostics.HasErrors) return; var headerSize = LongNamesTable.Size; diff --git a/src/LibObjectFile/Ar/ArArchiveFileReader.cs b/src/LibObjectFile/Ar/ArArchiveFileReader.cs index 194573c..a137582 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileReader.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileReader.cs @@ -10,344 +10,342 @@ using System.Text; using LibObjectFile.Elf; -namespace LibObjectFile.Ar -{ - /// - /// Class for reading and building an from a . - /// - public class ArArchiveFileReader : ObjectFileReaderWriter - { - private ArLongNamesTable? _futureHeaders; +namespace LibObjectFile.Ar; - internal ArArchiveFileReader(ArArchiveFile arArchiveFile, Stream stream, ArArchiveFileReaderOptions options) : base(stream) - { - ArArchiveFile = arArchiveFile; - Options = options; - IsReadOnly = options.IsReadOnly; - } +/// +/// Class for reading and building an from a . +/// +public class ArArchiveFileReader : ObjectFileReaderWriter +{ + private ArLongNamesTable? _futureHeaders; - public ArArchiveFileReaderOptions Options { get; } + internal ArArchiveFileReader(ArArchiveFile arArchiveFile, Stream stream, ArArchiveFileReaderOptions options) : base(arArchiveFile, stream) + { + Options = options; + IsReadOnly = options.IsReadOnly; + } + + public ArArchiveFileReaderOptions Options { get; } - public override bool IsReadOnly { get; } + public override bool IsReadOnly { get; } - internal ArArchiveFile ArArchiveFile { get; } + public ArArchiveFile ArArchiveFile => (ArArchiveFile)base.File; - internal static bool IsAr(Stream stream, DiagnosticBag? diagnostics) + internal static bool IsAr(Stream stream, DiagnosticBag? diagnostics) + { + Span magic = stackalloc byte[ArArchiveFile.Magic.Length]; + int magicLength = stream.Read(magic); + if (magicLength != magic.Length) { - Span magic = stackalloc byte[ArArchiveFile.Magic.Length]; - int magicLength = stream.Read(magic); - if (magicLength != magic.Length) + if (diagnostics != null) { - if (diagnostics != null) - { - diagnostics.Error(DiagnosticId.AR_ERR_InvalidMagicLength, $"Invalid length {magicLength} while trying to read ! from stream while expecting at least {magic.Length} bytes"); - } - return false; + diagnostics.Error(DiagnosticId.AR_ERR_InvalidMagicLength, $"Invalid length {magicLength} while trying to read ! from stream while expecting at least {magic.Length} bytes"); } + return false; + } - if (!magic.SequenceEqual(ArArchiveFile.Magic)) + if (!magic.SequenceEqual(ArArchiveFile.Magic)) + { + if (diagnostics != null) { - if (diagnostics != null) - { - diagnostics.Error(DiagnosticId.AR_ERR_MagicNotFound, $"Magic !\\n not found"); - } - return false; + diagnostics.Error(DiagnosticId.AR_ERR_MagicNotFound, $"Magic !\\n not found"); } - - return true; + return false; } - internal void Read() + return true; + } + + internal void Read() + { + if (!IsAr(Stream, Diagnostics)) { - if (!IsAr(Stream, Diagnostics)) - { - return; - } + return; + } - Span entryBuffer = stackalloc byte[ArFile.FileEntrySizeInBytes]; + Span entryBuffer = stackalloc byte[ArFile.FileEntrySizeInBytes]; - _futureHeaders = null; + _futureHeaders = null; - while (TryReadFileEntry(entryBuffer, out var fileEntry)) + while (TryReadFileEntry(entryBuffer, out var fileEntry)) + { + if (fileEntry is ArLongNamesTable arGnuFutureHeaders) { - if (fileEntry is ArLongNamesTable arGnuFutureHeaders) - { - if (_futureHeaders != null) - { - Diagnostics.Error(DiagnosticId.AR_ERR_InvalidDuplicatedFutureHeadersTable, $"Invalid duplicated future headers table found at offset {fileEntry.Position} while another table was already found at offset {_futureHeaders.Position}. This file is invalid."); - break; - } - - _futureHeaders = arGnuFutureHeaders; - } - else + if (_futureHeaders != null) { - ArArchiveFile.AddFile(fileEntry); + Diagnostics.Error(DiagnosticId.AR_ERR_InvalidDuplicatedFutureHeadersTable, $"Invalid duplicated future headers table found at offset {fileEntry.Position} while another table was already found at offset {_futureHeaders.Position}. This file is invalid."); + break; } - } - - if (Diagnostics.HasErrors) return; - // Perform a pass after all entries have been read - foreach (var arFileEntry in ArArchiveFile.Files) + _futureHeaders = arGnuFutureHeaders; + } + else { - arFileEntry.AfterReadInternal(this.Diagnostics); + ArArchiveFile.AddFile(fileEntry); } } - private bool TryReadFileEntry(Span buffer, [NotNullWhen(true)] out ArFile? entry) + if (Diagnostics.HasErrors) return; + + // Perform a pass after all entries have been read + foreach (var arFileEntry in ArArchiveFile.Files) { - entry = null; + arFileEntry.AfterReadInternal(this.Diagnostics); + } + } - Debug.Assert((Stream.Position & 1) == 0); + private bool TryReadFileEntry(Span buffer, [NotNullWhen(true)] out ArFile? entry) + { + entry = null; - long entryOffset = Stream.Position; - int length = Stream.Read(buffer); - if (length == 0) - { - return false; - } + Debug.Assert((Stream.Position & 1) == 0); - if (length < buffer.Length) - { - Diagnostics.Error(DiagnosticId.AR_ERR_InvalidFileEntryLength, $"Invalid length {length} while trying to read a file entry from stream at offset {entryOffset}. Expecting {buffer.Length} bytes"); - return false; - } - // 0 16 File identifier ASCII - // discard right padding characters - int idLength = 16; - while (idLength > 0) - { - if (buffer[idLength - 1] != ' ') - { - break; - } - idLength--; - } - - string? name = null; - ulong? bsdNameLength = null; + long entryOffset = Stream.Position; + int length = Stream.Read(buffer); + if (length == 0) + { + return false; + } - if (idLength > 3 && ArArchiveFile.Kind == ArArchiveKind.BSD) + if (length < buffer.Length) + { + Diagnostics.Error(DiagnosticId.AR_ERR_InvalidFileEntryLength, $"Invalid length {length} while trying to read a file entry from stream at offset {entryOffset}. Expecting {buffer.Length} bytes"); + return false; + } + // 0 16 File identifier ASCII + // discard right padding characters + int idLength = 16; + while (idLength > 0) + { + if (buffer[idLength - 1] != ' ') { - if (buffer[0] == '#' && buffer[1] == '1' && buffer[2] == '/') - { - // If we have a future header table, we are using it and expecting only numbers - if (!TryDecodeDecimal(entryOffset, buffer, 3, ArFile.FieldNameLength - 3, $"BSD Name length following #1/ at offset {entryOffset}", out ulong bsdNameLengthDecoded)) - { - // Don't try to process more entries, the archive might be corrupted - return false; - } - - bsdNameLength = bsdNameLengthDecoded; - } + break; } + idLength--; + } - // If the last char is `/` - // Keep file names with / or // - // But remove trailing `/`for regular file names - if (!bsdNameLength.HasValue && ArArchiveFile.Kind != ArArchiveKind.Common && idLength > 0 && buffer[idLength - 1] == '/') - { - if (!(idLength == 1 || idLength == 2 && buffer[idLength - 2] == '/')) - { - idLength--; - } - } + string? name = null; + ulong? bsdNameLength = null; - if (_futureHeaders != null && buffer[0] == (byte)'/') + if (idLength > 3 && ArArchiveFile.Kind == ArArchiveKind.BSD) + { + if (buffer[0] == '#' && buffer[1] == '1' && buffer[2] == '/') { // If we have a future header table, we are using it and expecting only numbers - if (!TryDecodeDecimal(entryOffset, buffer, 1, ArFile.FieldNameLength - 1, $"Name with offset to Future Headers Table at file offset {entryOffset}", out ulong offsetInFutureHeaders)) + if (!TryDecodeDecimal(entryOffset, buffer, 3, ArFile.FieldNameLength - 3, $"BSD Name length following #1/ at offset {entryOffset}", out ulong bsdNameLengthDecoded)) { // Don't try to process more entries, the archive might be corrupted return false; } - // If the number is ok, check that we have actually a string for this offset - if (!_futureHeaders.FileNames.TryGetValue((int)offsetInFutureHeaders, out name)) - { - Diagnostics.Error(DiagnosticId.AR_ERR_InvalidReferenceToFutureHeadersTable, $"Invalid reference {offsetInFutureHeaders} found at file offset {entryOffset}. This file is invalid."); - } + bsdNameLength = bsdNameLengthDecoded; } + } - if (!bsdNameLength.HasValue && name == null) + // If the last char is `/` + // Keep file names with / or // + // But remove trailing `/`for regular file names + if (!bsdNameLength.HasValue && ArArchiveFile.Kind != ArArchiveKind.Common && idLength > 0 && buffer[idLength - 1] == '/') + { + if (!(idLength == 1 || idLength == 2 && buffer[idLength - 2] == '/')) { - name = idLength == 0 ? string.Empty : Encoding.UTF8.GetString(buffer.Slice(0, idLength)); + idLength--; } - - // 16 12 File modification timestamp Decimal - if (!TryDecodeDecimal(entryOffset, buffer, ArFile.FieldTimestampOffset, ArFile.FieldTimestampLength, "File modification timestamp Decimal", out ulong timestamp)) + } + + if (_futureHeaders != null && buffer[0] == (byte)'/') + { + // If we have a future header table, we are using it and expecting only numbers + if (!TryDecodeDecimal(entryOffset, buffer, 1, ArFile.FieldNameLength - 1, $"Name with offset to Future Headers Table at file offset {entryOffset}", out ulong offsetInFutureHeaders)) { + // Don't try to process more entries, the archive might be corrupted return false; } - // 28 6 Owner ID Decimal - if (!TryDecodeDecimal(entryOffset, buffer, ArFile.FieldOwnerIdOffset, ArFile.FieldOwnerIdLength, "Owner ID", out ulong ownerId)) + // If the number is ok, check that we have actually a string for this offset + if (!_futureHeaders.FileNames.TryGetValue((int)offsetInFutureHeaders, out name)) { - return false; + Diagnostics.Error(DiagnosticId.AR_ERR_InvalidReferenceToFutureHeadersTable, $"Invalid reference {offsetInFutureHeaders} found at file offset {entryOffset}. This file is invalid."); } + } + + if (!bsdNameLength.HasValue && name == null) + { + name = idLength == 0 ? string.Empty : Encoding.UTF8.GetString(buffer.Slice(0, idLength)); + } + + // 16 12 File modification timestamp Decimal + if (!TryDecodeDecimal(entryOffset, buffer, ArFile.FieldTimestampOffset, ArFile.FieldTimestampLength, "File modification timestamp Decimal", out ulong timestamp)) + { + return false; + } - // 34 6 Group ID Decimal - if (!TryDecodeDecimal(entryOffset, buffer, ArFile.FieldGroupIdOffset, ArFile.FieldGroupIdLength, "Group ID", out ulong groupId)) + // 28 6 Owner ID Decimal + if (!TryDecodeDecimal(entryOffset, buffer, ArFile.FieldOwnerIdOffset, ArFile.FieldOwnerIdLength, "Owner ID", out ulong ownerId)) + { + return false; + } + + // 34 6 Group ID Decimal + if (!TryDecodeDecimal(entryOffset, buffer, ArFile.FieldGroupIdOffset, ArFile.FieldGroupIdLength, "Group ID", out ulong groupId)) + { + return false; + } + + // 40 8 File mode Octal + if (!TryDecodeOctal(entryOffset, buffer, ArFile.FieldFileModeOffset, ArFile.FieldFileModeLength, "File mode", out uint fileMode)) + { + return false; + } + + // 48 10 File size in bytes Decimal + if (!TryDecodeDecimal(entryOffset, buffer, ArFile.FieldFileSizeOffset, ArFile.FieldFileSizeLength, "File size in bytes", out ulong fileSize)) + { + return false; + } + + // 58 2 Ending characters 0x60 0x0A + if (buffer[ArFile.FieldEndCharactersOffset] != 0x60 || buffer[ArFile.FieldEndCharactersOffset + 1] != '\n') + { + Diagnostics.Error(DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry, $"Invalid ASCII characters found 0x{buffer[ArFile.FieldEndCharactersOffset]:x} 0x{buffer[ArFile.FieldEndCharactersOffset+1]:x} instead of `\\n at the end of file entry at offset {entryOffset + ArFile.FieldEndCharactersOffset}"); + return false; + } + + entry = CreateFileEntryFromName(name); + entry.Timestamp = DateTimeOffset.FromUnixTimeSeconds((long)timestamp); + entry.OwnerId = (uint)ownerId; + entry.GroupId = (uint)groupId; + entry.FileMode = fileMode; + entry.Position = (ulong)entryOffset; + entry.Size = fileSize; + + // Read the BSD name if necessary + if (bsdNameLength.HasValue) + { + var nameLength = (int) bsdNameLength.Value; + var bufferForName = ArrayPool.Shared.Rent(nameLength); + var streamPosition = Stream.Position; + var dataReadCount = Stream.Read(bufferForName, 0, nameLength); + if (dataReadCount != nameLength) { + Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while trying to read the filename from the data section of the file entry at offset of {streamPosition}. Expecting {nameLength} bytes while only {dataReadCount} bytes were read from the stream."); return false; } + name = Encoding.UTF8.GetString(bufferForName, 0, nameLength); + } - // 40 8 File mode Octal - if (!TryDecodeOctal(entryOffset, buffer, ArFile.FieldFileModeOffset, ArFile.FieldFileModeLength, "File mode", out uint fileMode)) + if (!entry.IsSystem) + { + if (name!.Contains('/')) { + Diagnostics.Error(DiagnosticId.AR_ERR_InvalidCharacterInFileEntryName, $"The character `/` was found in the entry `{name}` while it is invalid."); return false; } + entry.Name = name; + } - // 48 10 File size in bytes Decimal - if (!TryDecodeDecimal(entryOffset, buffer, ArFile.FieldFileSizeOffset, ArFile.FieldFileSizeLength, "File size in bytes", out ulong fileSize)) + entry.ReadInternal(this); + + // The end of an entry is always aligned + if ((Stream.Position & 1) != 0) + { + long padOffset = Stream.Position; + int pad = Stream.ReadByte(); + if (pad < 0) { + Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while trying to Invalid character 0x{pad:x} found at offset {padOffset} while expecting \\n 0xa"); return false; } - - // 58 2 Ending characters 0x60 0x0A - if (buffer[ArFile.FieldEndCharactersOffset] != 0x60 || buffer[ArFile.FieldEndCharactersOffset + 1] != '\n') + if (pad != '\n') { - Diagnostics.Error(DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry, $"Invalid ASCII characters found 0x{buffer[ArFile.FieldEndCharactersOffset]:x} 0x{buffer[ArFile.FieldEndCharactersOffset+1]:x} instead of `\\n at the end of file entry at offset {entryOffset + ArFile.FieldEndCharactersOffset}"); + Diagnostics.Error(DiagnosticId.AR_ERR_ExpectingNewLineCharacter, $"Invalid character 0x{pad:x} found at offset {padOffset} while expecting \\n 0xa"); return false; } + } - entry = CreateFileEntryFromName(name); - entry.Timestamp = DateTimeOffset.FromUnixTimeSeconds((long)timestamp); - entry.OwnerId = (uint)ownerId; - entry.GroupId = (uint)groupId; - entry.FileMode = fileMode; - entry.Position = (ulong)entryOffset; - entry.Size = fileSize; + return true; + } - // Read the BSD name if necessary - if (bsdNameLength.HasValue) + private bool TryDecodeDecimal(long entryOffset, Span buffer, int offset, int length, string fieldName, out ulong value) + { + value = 0; + // == 0, expect number or space + // == 1, expect space + int state = 0; + for (int i = 0; i < length; i++) + { + var c = buffer[offset + i]; + if (state == 0 && c >= '0' && c <= '9') { - var nameLength = (int) bsdNameLength.Value; - var bufferForName = ArrayPool.Shared.Rent(nameLength); - var streamPosition = Stream.Position; - var dataReadCount = Stream.Read(bufferForName, 0, nameLength); - if (dataReadCount != nameLength) - { - Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while trying to read the filename from the data section of the file entry at offset of {streamPosition}. Expecting {nameLength} bytes while only {dataReadCount} bytes were read from the stream."); - return false; - } - name = Encoding.UTF8.GetString(bufferForName, 0, nameLength); + value = value * 10 + (ulong) (c - '0'); } - - if (!entry.IsSystem) + else if (state >= 0 && c == ' ') { - if (name!.Contains('/')) - { - Diagnostics.Error(DiagnosticId.AR_ERR_InvalidCharacterInFileEntryName, $"The character `/` was found in the entry `{name}` while it is invalid."); - return false; - } - entry.Name = name; + state = 1; } - - entry.ReadInternal(this); - - // The end of an entry is always aligned - if ((Stream.Position & 1) != 0) + else { - long padOffset = Stream.Position; - int pad = Stream.ReadByte(); - if (pad < 0) - { - Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while trying to Invalid character 0x{pad:x} found at offset {padOffset} while expecting \\n 0xa"); - return false; - } - if (pad != '\n') - { - Diagnostics.Error(DiagnosticId.AR_ERR_ExpectingNewLineCharacter, $"Invalid character 0x{pad:x} found at offset {padOffset} while expecting \\n 0xa"); - return false; - } + Diagnostics.Error(DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry, $"Invalid ASCII character 0x{c:x} found instead of {state switch { 0 => "' '/space or decimal 0-9", _ => "' '/space" }} in file entry at file offset {entryOffset + i} while decoding field entry `{fieldName}`"); + return false; } - - return true; } + return true; + } - private bool TryDecodeDecimal(long entryOffset, Span buffer, int offset, int length, string fieldName, out ulong value) + private bool TryDecodeOctal(long entryOffset, Span buffer, int offset, int length, string fieldName, out uint value) + { + value = 0; + // == 0, expect number or space + // == 1, expect space + int state = 0; + for (int i = 0; i < length; i++) { - value = 0; - // == 0, expect number or space - // == 1, expect space - int state = 0; - for (int i = 0; i < length; i++) + var c = buffer[offset + i]; + if (state == 0 && c >= '0' && c <= '7') { - var c = buffer[offset + i]; - if (state == 0 && c >= '0' && c <= '9') - { - value = value * 10 + (ulong) (c - '0'); - } - else if (state >= 0 && c == ' ') - { - state = 1; - } - else - { - Diagnostics.Error(DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry, $"Invalid ASCII character 0x{c:x} found instead of {state switch { 0 => "' '/space or decimal 0-9", _ => "' '/space" }} in file entry at file offset {entryOffset + i} while decoding field entry `{fieldName}`"); - return false; - } + value = value * 8 + (uint)(c - '0'); } - return true; - } - - private bool TryDecodeOctal(long entryOffset, Span buffer, int offset, int length, string fieldName, out uint value) - { - value = 0; - // == 0, expect number or space - // == 1, expect space - int state = 0; - for (int i = 0; i < length; i++) + else if (state >= 0 && c == ' ') { - var c = buffer[offset + i]; - if (state == 0 && c >= '0' && c <= '7') - { - value = value * 8 + (uint)(c - '0'); - } - else if (state >= 0 && c == ' ') - { - state = 1; - } - else - { - Diagnostics.Error(DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry, $"Invalid ASCII character 0x{c:x} found instead of {state switch { 0 => "' '/space or octal 0-7", _ => "' '/space" }} in file entry at file offset {entryOffset + i} while decoding field entry `{fieldName}`"); - return false; - } + state = 1; + } + else + { + Diagnostics.Error(DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry, $"Invalid ASCII character 0x{c:x} found instead of {state switch { 0 => "' '/space or octal 0-7", _ => "' '/space" }} in file entry at file offset {entryOffset + i} while decoding field entry `{fieldName}`"); + return false; } - return true; } + return true; + } - private ArFile CreateFileEntryFromName(string? name) + private ArFile CreateFileEntryFromName(string? name) + { + if (ArArchiveFile.Kind == ArArchiveKind.GNU) { - if (ArArchiveFile.Kind == ArArchiveKind.GNU) + switch (name) { - switch (name) - { - case ArSymbolTable.DefaultGNUSymbolTableName: - return new ArSymbolTable(); - case ArLongNamesTable.DefaultName: - return new ArLongNamesTable(); - } + case ArSymbolTable.DefaultGNUSymbolTableName: + return new ArSymbolTable(); + case ArLongNamesTable.DefaultName: + return new ArLongNamesTable(); } - else if (ArArchiveFile.Kind == ArArchiveKind.BSD) + } + else if (ArArchiveFile.Kind == ArArchiveKind.BSD) + { + if (name == ArSymbolTable.DefaultBSDSymbolTableName) { - if (name == ArSymbolTable.DefaultBSDSymbolTableName) - { - return new ArSymbolTable(); - } + return new ArSymbolTable(); } + } - if (Options.ProcessObjectFiles) + if (Options.ProcessObjectFiles) + { + if (ElfObjectFile.IsElf(Stream)) { - if (ElfObjectFile.IsElf(Stream)) - { - return new ArElfFile(); - } + return new ArElfFile(); } - - return new ArBinaryFile(); } + + return new ArBinaryFile(); } } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArArchiveFileReaderOptions.cs b/src/LibObjectFile/Ar/ArArchiveFileReaderOptions.cs index 5c47683..f5b7fd9 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileReaderOptions.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileReaderOptions.cs @@ -2,40 +2,39 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Ar +namespace LibObjectFile.Ar; + +/// +/// Reader options used by and other methods. +/// +public class ArArchiveFileReaderOptions { /// - /// Reader options used by and other methods. + /// Initializes a new instance. /// - public class ArArchiveFileReaderOptions + /// Type of the 'ar' file to load (GNU, BSD...) + public ArArchiveFileReaderOptions(ArArchiveKind archiveKind) { - /// - /// Initializes a new instance. - /// - /// Type of the 'ar' file to load (GNU, BSD...) - public ArArchiveFileReaderOptions(ArArchiveKind archiveKind) - { - ArchiveKind = archiveKind; - ProcessObjectFiles = true; - } + ArchiveKind = archiveKind; + ProcessObjectFiles = true; + } - /// - /// Gets or sets a boolean indicating if the file entries must keep a readonly view - /// on the original stream for the content of the file entries, or it should copy - /// them to modifiable . - /// - public bool IsReadOnly { get; set; } + /// + /// Gets or sets a boolean indicating if the file entries must keep a readonly view + /// on the original stream for the content of the file entries, or it should copy + /// them to modifiable . + /// + public bool IsReadOnly { get; set; } - /// - /// Gets or sets the type of file to load - /// - public ArArchiveKind ArchiveKind { get; set; } + /// + /// Gets or sets the type of file to load + /// + public ArArchiveKind ArchiveKind { get; set; } - /// - /// Gets or sets a boolean indicating if object files are being processed to return - /// typed entries () instead of generic binary file entry (). - /// Default is true - /// - public bool ProcessObjectFiles { get; set; } - } + /// + /// Gets or sets a boolean indicating if object files are being processed to return + /// typed entries () instead of generic binary file entry (). + /// Default is true + /// + public bool ProcessObjectFiles { get; set; } } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArArchiveFileWriter.cs b/src/LibObjectFile/Ar/ArArchiveFileWriter.cs index d13f745..4c59bf1 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileWriter.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileWriter.cs @@ -8,196 +8,194 @@ using System.IO; using System.Text; -namespace LibObjectFile.Ar +namespace LibObjectFile.Ar; + +/// +/// Class for writing an to a . +/// +public class ArArchiveFileWriter: ObjectFileReaderWriter { - /// - /// Class for writing an to a . - /// - public class ArArchiveFileWriter: ObjectFileReaderWriter - { - private long _startStreamOffset; + private long _startStreamOffset; - internal ArArchiveFileWriter(ArArchiveFile archiveFile, Stream stream) : base(stream) - { - ArArchiveFile = archiveFile; - IsReadOnly = false; - } + internal ArArchiveFileWriter(ArArchiveFile archiveFile, Stream stream) : base(archiveFile, stream) + { + IsReadOnly = false; + } - private ArArchiveFile ArArchiveFile { get; } + public ArArchiveFile ArArchiveFile => (ArArchiveFile)base.File; - public override bool IsReadOnly { get; } + public override bool IsReadOnly { get; } - internal void Write() + internal void Write() + { + var localDiagnostics = new DiagnosticBag(); + ArArchiveFile.UpdateLayout(localDiagnostics); + if (localDiagnostics.HasErrors) { - var localDiagnostics = new DiagnosticBag(); - ArArchiveFile.UpdateLayout(localDiagnostics); - if (localDiagnostics.HasErrors) - { - throw new ObjectFileException("Invalid ar file", localDiagnostics); - } - // Copy for warnings - localDiagnostics.CopyTo(Diagnostics); + throw new ObjectFileException("Invalid ar file", localDiagnostics); + } + // Copy for warnings + localDiagnostics.CopyTo(Diagnostics); - _startStreamOffset = Stream.Position; + _startStreamOffset = Stream.Position; - Stream.Write(ArArchiveFile.Magic); - Span entryBuffer = stackalloc byte[ArFile.FileEntrySizeInBytes]; + Stream.Write(ArArchiveFile.Magic); + Span entryBuffer = stackalloc byte[ArFile.FileEntrySizeInBytes]; - var headers = ArArchiveFile.LongNamesTable; - - // Serialize all file entries - for (var i = 0; i < ArArchiveFile.Files.Count; i++) - { - var file = ArArchiveFile.Files[i]; + var headers = ArArchiveFile.LongNamesTable; - // Serialize the headers at the correct position only if they are required - if (headers != null && headers.Index == i && headers.Size > 0) - { - WriteFileEntry(entryBuffer, headers); - if (Diagnostics.HasErrors) break; - } + // Serialize all file entries + for (var i = 0; i < ArArchiveFile.Files.Count; i++) + { + var file = ArArchiveFile.Files[i]; - WriteFileEntry(entryBuffer, file); + // Serialize the headers at the correct position only if they are required + if (headers != null && headers.Index == i && headers.Size > 0) + { + WriteFileEntry(entryBuffer, headers); if (Diagnostics.HasErrors) break; } - if (Diagnostics.HasErrors) - { - throw new ObjectFileException("Unexpected error while writing ar file", Diagnostics); - } + WriteFileEntry(entryBuffer, file); + if (Diagnostics.HasErrors) break; } - private void WriteFileEntry(Span buffer, ArFile file) + if (Diagnostics.HasErrors) { - Debug.Assert((ulong)(Stream.Position - _startStreamOffset) == file.Position); - buffer.Fill((byte)' '); + throw new ObjectFileException("Unexpected error while writing ar file", Diagnostics); + } + } + + private void WriteFileEntry(Span buffer, ArFile file) + { + Debug.Assert((ulong)(Stream.Position - _startStreamOffset) == file.Position); + buffer.Fill((byte)' '); - var name = file.InternalName; + var name = file.InternalName; - bool postFixSlash = false; + bool postFixSlash = false; - if (name == null) + if (name == null) + { + name = file.Name!; + if (ArArchiveFile.Kind != ArArchiveKind.Common && !name.EndsWith("/")) { - name = file.Name!; - if (ArArchiveFile.Kind != ArArchiveKind.Common && !name.EndsWith("/")) - { - postFixSlash = true; - } + postFixSlash = true; } + } - uint? bsdNameLength = null; + uint? bsdNameLength = null; - if (ArArchiveFile.Kind == ArArchiveKind.BSD) + if (ArArchiveFile.Kind == ArArchiveKind.BSD) + { + var nameLength = Encoding.UTF8.GetByteCount(name); + if (nameLength > ArFile.FieldNameLength) { - var nameLength = Encoding.UTF8.GetByteCount(name); - if (nameLength > ArFile.FieldNameLength) - { - name = $"#1/{nameLength}"; - bsdNameLength = (uint)nameLength; - postFixSlash = false; - } + name = $"#1/{nameLength}"; + bsdNameLength = (uint)nameLength; + postFixSlash = false; } + } - // Encode Length - int length = Encoding.UTF8.GetBytes(name, buffer.Slice(0, ArFile.FieldNameLength)); - if (postFixSlash) - { - buffer[length] = (byte) '/'; - } + // Encode Length + int length = Encoding.UTF8.GetBytes(name, buffer.Slice(0, ArFile.FieldNameLength)); + if (postFixSlash) + { + buffer[length] = (byte) '/'; + } - if (!(file is ArLongNamesTable)) - { - // 16 12 File modification timestamp Decimal - EncodeDecimal(buffer, ArFile.FieldTimestampOffset, ArFile.FieldTimestampLength, (ulong)file.Timestamp.ToUnixTimeSeconds()); - // 28 6 Owner ID Decimal - EncodeDecimal(buffer, ArFile.FieldOwnerIdOffset, ArFile.FieldOwnerIdLength, file.OwnerId); - // 34 6 Group ID Decimal - EncodeDecimal(buffer, ArFile.FieldGroupIdOffset, ArFile.FieldGroupIdLength, file.GroupId); - // 40 8 File mode Octal - EncodeOctal(buffer, ArFile.FieldFileModeOffset, ArFile.FieldFileModeLength, file.FileMode); - } - // 48 10 File size in bytes Decimal - EncodeDecimal(buffer, ArFile.FieldFileSizeOffset, ArFile.FieldFileSizeLength, file.Size); + if (!(file is ArLongNamesTable)) + { + // 16 12 File modification timestamp Decimal + EncodeDecimal(buffer, ArFile.FieldTimestampOffset, ArFile.FieldTimestampLength, (ulong)file.Timestamp.ToUnixTimeSeconds()); + // 28 6 Owner ID Decimal + EncodeDecimal(buffer, ArFile.FieldOwnerIdOffset, ArFile.FieldOwnerIdLength, file.OwnerId); + // 34 6 Group ID Decimal + EncodeDecimal(buffer, ArFile.FieldGroupIdOffset, ArFile.FieldGroupIdLength, file.GroupId); + // 40 8 File mode Octal + EncodeOctal(buffer, ArFile.FieldFileModeOffset, ArFile.FieldFileModeLength, file.FileMode); + } + // 48 10 File size in bytes Decimal + EncodeDecimal(buffer, ArFile.FieldFileSizeOffset, ArFile.FieldFileSizeLength, file.Size); - buffer[ArFile.FieldEndCharactersOffset] = 0x60; - buffer[ArFile.FieldEndCharactersOffset + 1] = (byte) '\n'; + buffer[ArFile.FieldEndCharactersOffset] = 0x60; + buffer[ArFile.FieldEndCharactersOffset + 1] = (byte) '\n'; - // Write the entry - Stream.Write(buffer); + // Write the entry + Stream.Write(buffer); - // Handle BSD file name by serializing the name before the data if it is required - if (bsdNameLength.HasValue) + // Handle BSD file name by serializing the name before the data if it is required + if (bsdNameLength.HasValue) + { + uint nameLength = bsdNameLength.Value; + var bufferName = ArrayPool.Shared.Rent((int) nameLength); + Encoding.UTF8.GetBytes(file.Name!, 0, file.Name!.Length, bufferName, 0); + try { - uint nameLength = bsdNameLength.Value; - var bufferName = ArrayPool.Shared.Rent((int) nameLength); - Encoding.UTF8.GetBytes(file.Name!, 0, file.Name!.Length, bufferName, 0); - try - { - Stream.Write(bufferName, 0, (int)nameLength); - } - finally - { - ArrayPool.Shared.Return(bufferName); - } + Stream.Write(bufferName, 0, (int)nameLength); } - - // Write the content following the entry - file.WriteInternal(this); - - // Align to even byte - if ((Stream.Position & 1) != 0) + finally { - Stream.WriteByte((byte)'\n'); + ArrayPool.Shared.Return(bufferName); } } - private void EncodeDecimal(in Span buffer, int offset, int size, ulong value) + // Write the content following the entry + file.WriteInternal(this); + + // Align to even byte + if ((Stream.Position & 1) != 0) { - int count = value == 0 ? 1 : 0; - var check = value; - while (check > 0) - { - check /= 10; - count++; - } + Stream.WriteByte((byte)'\n'); + } + } - if (count > size) - { - Diagnostics.Error(DiagnosticId.AR_ERR_ExpectingNewLineCharacter, $"Cannot encode decimal `{value}` as the size is exceeding the available size {size}"); - return; - } + private void EncodeDecimal(in Span buffer, int offset, int size, ulong value) + { + int count = value == 0 ? 1 : 0; + var check = value; + while (check > 0) + { + check /= 10; + count++; + } - check = value; - for (int i = 0; i < count; i++) - { - var dec = check % 10; - buffer[offset + count - i - 1] = (byte)((byte) '0' + dec); - check = check / 10; - } + if (count > size) + { + Diagnostics.Error(DiagnosticId.AR_ERR_ExpectingNewLineCharacter, $"Cannot encode decimal `{value}` as the size is exceeding the available size {size}"); + return; } - private void EncodeOctal(in Span buffer, int offset, int size, ulong value) + check = value; + for (int i = 0; i < count; i++) { - int count = value == 0 ? 1 : 0; - var check = value; - while (check > 0) - { - check /= 8; - count++; - } + var dec = check % 10; + buffer[offset + count - i - 1] = (byte)((byte) '0' + dec); + check = check / 10; + } + } - if (count > size) - { - Diagnostics.Error(DiagnosticId.AR_ERR_ExpectingNewLineCharacter, $"Cannot encode octal `{value}` as the size is exceeding the available size {size}"); - } + private void EncodeOctal(in Span buffer, int offset, int size, ulong value) + { + int count = value == 0 ? 1 : 0; + var check = value; + while (check > 0) + { + check /= 8; + count++; + } - check = value; - for (int i = 0; i < count; i++) - { - var dec = check % 8; - buffer[offset + count - i - 1] = (byte)((byte)'0' + dec); - check = check / 8; - } + if (count > size) + { + Diagnostics.Error(DiagnosticId.AR_ERR_ExpectingNewLineCharacter, $"Cannot encode octal `{value}` as the size is exceeding the available size {size}"); + } + + check = value; + for (int i = 0; i < count; i++) + { + var dec = check % 8; + buffer[offset + count - i - 1] = (byte)((byte)'0' + dec); + check = check / 8; } } } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArArchiveKind.cs b/src/LibObjectFile/Ar/ArArchiveKind.cs index 6887e3f..ec8bc19 100644 --- a/src/LibObjectFile/Ar/ArArchiveKind.cs +++ b/src/LibObjectFile/Ar/ArArchiveKind.cs @@ -2,31 +2,30 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Ar +namespace LibObjectFile.Ar; + +/// +/// The type of archive. +/// +public enum ArArchiveKind { /// - /// The type of archive. + /// The common variant, used for example by 'deb' package files. + /// Supports only file names up to 16 characters. /// - public enum ArArchiveKind - { - /// - /// The common variant, used for example by 'deb' package files. - /// Supports only file names up to 16 characters. - /// - Common, + Common, - /// - /// The GNU variant, used by the `ar` utility on GNU and other systems (including Windows) - /// Based on file format, but using a different strategy - /// for storing long file names, incompatible with format. - /// - GNU, + /// + /// The GNU variant, used by the `ar` utility on GNU and other systems (including Windows) + /// Based on file format, but using a different strategy + /// for storing long file names, incompatible with format. + /// + GNU, - /// - /// The BSD variant, used by the `ar` utility on BSD systems (including MacOS) - /// Based on file format and backward compatible with it, - /// but allows to store longer file names and file names containing space. - /// - BSD, - } + /// + /// The BSD variant, used by the `ar` utility on BSD systems (including MacOS) + /// Based on file format and backward compatible with it, + /// but allows to store longer file names and file names containing space. + /// + BSD, } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArBinaryFile.cs b/src/LibObjectFile/Ar/ArBinaryFile.cs index 94b715d..3b920c8 100644 --- a/src/LibObjectFile/Ar/ArBinaryFile.cs +++ b/src/LibObjectFile/Ar/ArBinaryFile.cs @@ -5,35 +5,33 @@ using System; using System.IO; -namespace LibObjectFile.Ar +namespace LibObjectFile.Ar; + +/// +/// An binary stream . +/// +public sealed class ArBinaryFile : ArFile { /// - /// An binary stream . + /// Gets or sets the stream associated to this entry. /// - public sealed class ArBinaryFile : ArFile - { - /// - /// Gets or sets the stream associated to this entry. - /// - public Stream? Stream { get; set; } + public Stream? Stream { get; set; } - protected override void Read(ArArchiveFileReader reader) - { - Stream = reader.ReadAsStream(Size); - } + public override void Read(ArArchiveFileReader reader) + { + Stream = reader.ReadAsStream(Size); + } - protected override void Write(ArArchiveFileWriter writer) + public override void Write(ArArchiveFileWriter writer) + { + if (Stream != null) { - if (Stream != null) - { - writer.Write(Stream); - } + writer.Write(Stream); } + } - public override void UpdateLayout(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - Size = Stream != null ? (ulong) Stream.Length : 0; - } + public override void UpdateLayout(ArVisitorContext context) + { + Size = Stream != null ? (ulong) Stream.Length : 0; } } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArElfFile.cs b/src/LibObjectFile/Ar/ArElfFile.cs index 9b6e726..7ece843 100644 --- a/src/LibObjectFile/Ar/ArElfFile.cs +++ b/src/LibObjectFile/Ar/ArElfFile.cs @@ -6,56 +6,54 @@ using LibObjectFile.Elf; using LibObjectFile.Utils; -namespace LibObjectFile.Ar +namespace LibObjectFile.Ar; + +/// +/// An ELF file entry. +/// +public sealed class ArElfFile : ArFile { - /// - /// An ELF file entry. - /// - public sealed class ArElfFile : ArFile + public ArElfFile() { - public ArElfFile() - { - } + } - public ArElfFile(ElfObjectFile elfObjectFile) - { - ElfObjectFile = elfObjectFile; - } + public ArElfFile(ElfObjectFile elfObjectFile) + { + ElfObjectFile = elfObjectFile; + } - /// - /// Gets or sets the ELF object file. - /// - public ElfObjectFile? ElfObjectFile { get; set; } + /// + /// Gets or sets the ELF object file. + /// + public ElfObjectFile? ElfObjectFile { get; set; } - protected override void Read(ArArchiveFileReader reader) - { - var startPosition = reader.Stream.Position; - var endPosition = startPosition + (long) Size; - ElfObjectFile = ElfObjectFile.Read(new SubStream(reader.Stream, reader.Stream.Position, (long)Size)); - reader.Stream.Position = endPosition; - } + public override void Read(ArArchiveFileReader reader) + { + var startPosition = reader.Stream.Position; + var endPosition = startPosition + (long) Size; + ElfObjectFile = ElfObjectFile.Read(new SubStream(reader.Stream, reader.Stream.Position, (long)Size)); + reader.Stream.Position = endPosition; + } - protected override void Write(ArArchiveFileWriter writer) + public override void Write(ArArchiveFileWriter writer) + { + if (ElfObjectFile != null) { - if (ElfObjectFile != null) - { - ElfObjectFile.TryWrite(writer.Stream, out var diagnostics); - diagnostics.CopyTo(writer.Diagnostics); - } + ElfObjectFile.TryWrite(writer.Stream, out var diagnostics); + diagnostics.CopyTo(writer.Diagnostics); } + } - public override void UpdateLayout(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - Size = 0; + public override void UpdateLayout(ArVisitorContext context) + { + Size = 0; - if (ElfObjectFile != null) + if (ElfObjectFile != null) + { + ElfObjectFile.UpdateLayout(context.Diagnostics); + if (!context.HasErrors) { - ElfObjectFile.UpdateLayout(diagnostics); - if (!diagnostics.HasErrors) - { - Size = ElfObjectFile.Layout.TotalSize; - } + Size = ElfObjectFile.Layout.TotalSize; } } } diff --git a/src/LibObjectFile/Ar/ArFile.cs b/src/LibObjectFile/Ar/ArFile.cs index dbc2dfa..59fefe7 100644 --- a/src/LibObjectFile/Ar/ArFile.cs +++ b/src/LibObjectFile/Ar/ArFile.cs @@ -3,146 +3,131 @@ // See the license.txt file in the project root for more information. using System; +using System.Text; -namespace LibObjectFile.Ar +namespace LibObjectFile.Ar; + +/// +/// Base class for a file entry in +/// +public abstract partial class ArFile : ArObject { + private string? _name; + private DateTimeOffset _timestamp; + + protected ArFile() + { + Timestamp = DateTimeOffset.UtcNow; + } + /// - /// Base class for a file entry in + /// Gets or sets the name of the file in the archive entry. /// - public abstract partial class ArFile : ArObject + public virtual string? Name { - private string? _name; - private DateTimeOffset _timestamp; - - protected ArFile() - { - Timestamp = DateTimeOffset.UtcNow; - } - - /// - /// Gets or sets the name of the file in the archive entry. - /// - public virtual string? Name + get => _name; + set { - get => _name; - set + if (IsSystem) { - if (IsSystem) - { - throw CannotModifyProperty(nameof(Name)); - } - - if (value != null && value.Contains('/')) - { - throw new ArgumentException("The character `/` is not allowed in a file name entry"); - } + throw CannotModifyProperty(nameof(Name)); + } - _name = value; + if (value != null && value.Contains('/')) + { + throw new ArgumentException("The character `/` is not allowed in a file name entry"); } + + _name = value; } + } - /// - /// Gets or sets the real (internal) name used for storing this entry (used by ) - /// - internal string? InternalName { get; set; } + /// + /// Gets or sets the real (internal) name used for storing this entry (used by ) + /// + internal string? InternalName { get; set; } - /// - /// Gets or sets the timestamp of this file (clamped to seconds since 1970/01/01) - /// - public DateTimeOffset Timestamp - { - get => _timestamp; + /// + /// Gets or sets the timestamp of this file (clamped to seconds since 1970/01/01) + /// + public DateTimeOffset Timestamp + { + get => _timestamp; - // We clamp the timestamp to the precision supported by the system - set => _timestamp = DateTimeOffset.FromUnixTimeSeconds(value.ToUnixTimeSeconds()); - } + // We clamp the timestamp to the precision supported by the system + set => _timestamp = DateTimeOffset.FromUnixTimeSeconds(value.ToUnixTimeSeconds()); + } - /// - /// Gets or sets the owner id. - /// - public uint OwnerId { get; set; } + /// + /// Gets or sets the owner id. + /// + public uint OwnerId { get; set; } - /// - /// Gets or sets the group id. - /// - public uint GroupId { get; set; } + /// + /// Gets or sets the group id. + /// + public uint GroupId { get; set; } - /// - /// Gets or sets the file mode. - /// - public uint FileMode { get; set; } + /// + /// Gets or sets the file mode. + /// + public uint FileMode { get; set; } - /// - /// Gets a boolean indicating if this entry is a system entry (symbol table, header references) - /// and so does not respect naming (that should exclude for example `/`) - /// - public virtual bool IsSystem => false; + /// + /// Gets a boolean indicating if this entry is a system entry (symbol table, header references) + /// and so does not respect naming (that should exclude for example `/`) + /// + public virtual bool IsSystem => false; - internal void AfterReadInternal(DiagnosticBag diagnostics) - { - AfterRead(diagnostics); - } + internal void AfterReadInternal(DiagnosticBag diagnostics) + { + AfterRead(diagnostics); + } - internal void ReadInternal(ArArchiveFileReader reader) + internal void ReadInternal(ArArchiveFileReader reader) + { + var expectedSize = (long)Size; + var beforePosition = reader.Stream.Position; + Read(reader); + var afterPosition = reader.Stream.Position; + var size = afterPosition - beforePosition; + // Verifies that the Size property is actually valid with what is being read + if (size != expectedSize) { - var expectedSize = (long)Size; - var beforePosition = reader.Stream.Position; - Read(reader); - var afterPosition = reader.Stream.Position; - var size = afterPosition - beforePosition; - // Verifies that the Size property is actually valid with what is being read - if (size != expectedSize) - { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected EOF / size (expected: {expectedSize} != read: {size}) while trying to read file entry {Name}"); - } + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected EOF / size (expected: {expectedSize} != read: {size}) while trying to read file entry {Name}"); } + } - internal void WriteInternal(ArArchiveFileWriter writer) + internal void WriteInternal(ArArchiveFileWriter writer) + { + var expectedSize = (long)Size; + var beforePosition = writer.Stream.Position; + Write(writer); + var afterPosition = writer.Stream.Position; + var size = afterPosition - beforePosition; + + // Verifies that the Size property is actually valid with what is being written + if (size != expectedSize) { - var expectedSize = (long)Size; - var beforePosition = writer.Stream.Position; - Write(writer); - var afterPosition = writer.Stream.Position; - var size = afterPosition - beforePosition; - - // Verifies that the Size property is actually valid with what is being written - if (size != expectedSize) - { - // In that case, we don't log a diagnostics but throw an error, as it is an implementation problem. - throw new InvalidOperationException($"Invalid implementation of {GetType()}.{nameof(Write)} method. The Size written to the disk doesn't match (expected: {expectedSize} != written: {size}) while trying to write file entry {Name}"); - } + // In that case, we don't log a diagnostics but throw an error, as it is an implementation problem. + throw new InvalidOperationException($"Invalid implementation of {GetType()}.{nameof(Write)} method. The Size written to the disk doesn't match (expected: {expectedSize} != written: {size}) while trying to write file entry {Name}"); } + } - /// - /// Reads this entry from a stream. - /// - /// The reader associated with the stream to read from. - protected abstract void Read(ArArchiveFileReader reader); - - /// - /// Performs after-read operation after all the other entries have been loaded. - /// - /// A diagnostic bag - protected virtual void AfterRead(DiagnosticBag diagnostics) { } - - /// - /// Writes this entry to a stream. - /// - /// The writer associated with the stream to write to. - protected abstract void Write(ArArchiveFileWriter writer); - - protected InvalidOperationException CannotModifyProperty(string propertyName) - { - return new InvalidOperationException($"Cannot modify the property {propertyName} for this {GetType()} file entry instance"); - } + /// + /// Performs after-read operation after all the other entries have been loaded. + /// + /// A diagnostic bag + protected virtual void AfterRead(DiagnosticBag diagnostics) { } - public override string ToString() - { - return $"{this.GetType().Name} [{Index}] `{Name}`"; - } + protected InvalidOperationException CannotModifyProperty(string propertyName) + { + return new InvalidOperationException($"Cannot modify the property {propertyName} for this {GetType()} file entry instance"); + } - public override void Verify(DiagnosticBag diagnostics) - { - } + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"[{Index}] `{Name}"); + return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArFileEntry.Constants.cs b/src/LibObjectFile/Ar/ArFileEntry.Constants.cs index 4338152..0ed6437 100644 --- a/src/LibObjectFile/Ar/ArFileEntry.Constants.cs +++ b/src/LibObjectFile/Ar/ArFileEntry.Constants.cs @@ -2,83 +2,82 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Ar +namespace LibObjectFile.Ar; + +public abstract partial class ArFile { - public abstract partial class ArFile - { - /// - /// Size in bytes of an AR file entry - /// - public const int FileEntrySizeInBytes = 60; - - /// - /// Offset of the filename in the entry - /// - public const int FieldNameOffset = 0; - - /// - /// Length in bytes of the filename in the entry - /// - public const int FieldNameLength = 16; - - /// - /// Offset of the timestamp in the entry - /// - public const int FieldTimestampOffset = 16; - - /// - /// Length in bytes of the timestamp in the entry - /// - public const int FieldTimestampLength = 12; - - /// - /// Offset of the owner ID in the entry - /// - public const int FieldOwnerIdOffset = 28; - - /// - /// Length in bytes of the timestamp in the entry - /// - public const int FieldOwnerIdLength = 6; - - /// - /// Offset of the group ID in the entry - /// - public const int FieldGroupIdOffset = 34; - - /// - /// Length in bytes of the timestamp in the entry - /// - public const int FieldGroupIdLength = 6; - - /// - /// Offset of the file mode in the entry - /// - public const int FieldFileModeOffset = 40; - - /// - /// Length in bytes of the timestamp in the entry - /// - public const int FieldFileModeLength = 8; - - /// - /// Offset of the file size in the entry - /// - public const int FieldFileSizeOffset = 48; - - /// - /// Length in bytes of the timestamp in the entry - /// - public const int FieldFileSizeLength = 10; - - /// - /// Offset of the end characters in the entry - /// - public const int FieldEndCharactersOffset = 58; - - /// - /// Length in bytes of the end characters in the entry - /// - public const int FieldEndCharactersLength = 2; - } + /// + /// Size in bytes of an AR file entry + /// + public const int FileEntrySizeInBytes = 60; + + /// + /// Offset of the filename in the entry + /// + public const int FieldNameOffset = 0; + + /// + /// Length in bytes of the filename in the entry + /// + public const int FieldNameLength = 16; + + /// + /// Offset of the timestamp in the entry + /// + public const int FieldTimestampOffset = 16; + + /// + /// Length in bytes of the timestamp in the entry + /// + public const int FieldTimestampLength = 12; + + /// + /// Offset of the owner ID in the entry + /// + public const int FieldOwnerIdOffset = 28; + + /// + /// Length in bytes of the timestamp in the entry + /// + public const int FieldOwnerIdLength = 6; + + /// + /// Offset of the group ID in the entry + /// + public const int FieldGroupIdOffset = 34; + + /// + /// Length in bytes of the timestamp in the entry + /// + public const int FieldGroupIdLength = 6; + + /// + /// Offset of the file mode in the entry + /// + public const int FieldFileModeOffset = 40; + + /// + /// Length in bytes of the timestamp in the entry + /// + public const int FieldFileModeLength = 8; + + /// + /// Offset of the file size in the entry + /// + public const int FieldFileSizeOffset = 48; + + /// + /// Length in bytes of the timestamp in the entry + /// + public const int FieldFileSizeLength = 10; + + /// + /// Offset of the end characters in the entry + /// + public const int FieldEndCharactersOffset = 58; + + /// + /// Length in bytes of the end characters in the entry + /// + public const int FieldEndCharactersLength = 2; } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArLongNamesTable.cs b/src/LibObjectFile/Ar/ArLongNamesTable.cs index f763971..f7b8932 100644 --- a/src/LibObjectFile/Ar/ArLongNamesTable.cs +++ b/src/LibObjectFile/Ar/ArLongNamesTable.cs @@ -7,142 +7,140 @@ using System.Collections.Generic; using System.Text; -namespace LibObjectFile.Ar +namespace LibObjectFile.Ar; + +/// +/// Internal class used for loading long file names for GNU `ar` and Windows `lib` archives. +/// +internal sealed class ArLongNamesTable : ArFile { - /// - /// Internal class used for loading long file names for GNU `ar` and Windows `lib` archives. - /// - internal sealed class ArLongNamesTable : ArFile - { - public const string DefaultName = "//"; + public const string DefaultName = "//"; - public ArLongNamesTable() - { - FileNames = new Dictionary(); - } + public ArLongNamesTable() + { + FileNames = new Dictionary(); + } - public override string? Name - { - get => DefaultName; - set => base.Name = value; - } + public override string? Name + { + get => DefaultName; + set => base.Name = value; + } - public Dictionary FileNames { get; private set; } + public Dictionary FileNames { get; private set; } - public override bool IsSystem => true; + public override bool IsSystem => true; - protected override void Read(ArArchiveFileReader reader) + public override void Read(ArArchiveFileReader reader) + { + FileNames.Clear(); + var buffer = ArrayPool.Shared.Rent((int)Size); + int readCount = reader.Stream.Read(buffer, 0, (int)Size); + int startFileIndex = 0; + for (int i = 0; i < readCount; i++) { - FileNames.Clear(); - var buffer = ArrayPool.Shared.Rent((int)Size); - int readCount = reader.Stream.Read(buffer, 0, (int)Size); - int startFileIndex = 0; - for (int i = 0; i < readCount; i++) + if (buffer[i] == '\n') { - if (buffer[i] == '\n') + var fileNameLength = i - startFileIndex; + if (fileNameLength > 0) { - var fileNameLength = i - startFileIndex; - if (fileNameLength > 0) + // Discard trailing `/` + if (buffer[startFileIndex + fileNameLength - 1] == '/') { - // Discard trailing `/` - if (buffer[startFileIndex + fileNameLength - 1] == '/') - { - fileNameLength--; - } - - // TODO: Is it UTF8 or ASCII? - FileNames.Add(startFileIndex, Encoding.UTF8.GetString(buffer, startFileIndex, fileNameLength)); + fileNameLength--; } - startFileIndex = i + 1; + + // TODO: Is it UTF8 or ASCII? + FileNames.Add(startFileIndex, Encoding.UTF8.GetString(buffer, startFileIndex, fileNameLength)); } + startFileIndex = i + 1; } - ArrayPool.Shared.Return(buffer); } + ArrayPool.Shared.Return(buffer); + } - protected override void Write(ArArchiveFileWriter writer) + public override void Write(ArArchiveFileWriter writer) + { + var buffer = ArrayPool.Shared.Rent((int)Size); + uint offset = 0; + for (var i = (int)Index; i < Parent!.Files.Count; i++) { - var buffer = ArrayPool.Shared.Rent((int)Size); - uint offset = 0; - for (var i = (int)Index; i < Parent!.Files.Count; i++) - { - var file = Parent.Files[i]; + var file = Parent.Files[i]; - if (file is ArLongNamesTable) break; + if (file is ArLongNamesTable) break; - var fileName = file.Name; - if (fileName == null || fileName.StartsWith("/")) - { - continue; - } + var fileName = file.Name; + if (fileName == null || fileName.StartsWith("/")) + { + continue; + } - // byte count + `/` - var fileNameLength = Encoding.UTF8.GetByteCount(fileName) + 1; - if (fileNameLength <= FieldNameLength) - { - file.InternalName = null; - continue; - } + // byte count + `/` + var fileNameLength = Encoding.UTF8.GetByteCount(fileName) + 1; + if (fileNameLength <= FieldNameLength) + { + file.InternalName = null; + continue; + } - // Add `\n` - fileNameLength++; + // Add `\n` + fileNameLength++; - if (fileNameLength > buffer.Length) - { - ArrayPool.Shared.Return(buffer); - buffer = ArrayPool.Shared.Rent(fileNameLength); - } + if (fileNameLength > buffer.Length) + { + ArrayPool.Shared.Return(buffer); + buffer = ArrayPool.Shared.Rent(fileNameLength); + } - file.InternalName = $"/{offset}"; + file.InternalName = $"/{offset}"; - Encoding.UTF8.GetBytes(fileName, 0, fileName.Length, buffer, 0); - buffer[fileNameLength - 2] = (byte)'/'; - buffer[fileNameLength - 1] = (byte)'\n'; + Encoding.UTF8.GetBytes(fileName, 0, fileName.Length, buffer, 0); + buffer[fileNameLength - 2] = (byte)'/'; + buffer[fileNameLength - 1] = (byte)'\n'; - writer.Write(buffer, 0, fileNameLength); - offset += (uint)fileNameLength; - } - - if ((offset & 1) != 0) - { - writer.Stream.WriteByte((byte)'\n'); - } - ArrayPool.Shared.Return(buffer); + writer.Write(buffer, 0, fileNameLength); + offset += (uint)fileNameLength; } - public override void UpdateLayout(DiagnosticBag diagnostics) + if ((offset & 1) != 0) { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - Size = 0; + writer.Stream.WriteByte((byte)'\n'); + } + ArrayPool.Shared.Return(buffer); + } - if (Parent == null) return; + public override void UpdateLayout(ArVisitorContext context) + { + Size = 0; - ulong size = 0; - for (var i = (int)Index; i < Parent.Files.Count; i++) - { - var file = Parent.Files[i]; - if (file is ArLongNamesTable) break; + if (Parent == null) return; - if (file.Name == null || file.Name.StartsWith("/")) - { - continue; - } + ulong size = 0; + for (var i = (int)Index; i < Parent.Files.Count; i++) + { + var file = Parent.Files[i]; + if (file is ArLongNamesTable) break; - var byteCount = Encoding.UTF8.GetByteCount(file.Name); - // byte count + `/` - if (byteCount + 1 > FieldNameLength) - { - // byte count + `/` + `\n` - size += (ulong)byteCount + 2; - } + if (file.Name == null || file.Name.StartsWith("/")) + { + continue; } - if ((size & 1) != 0) + var byteCount = Encoding.UTF8.GetByteCount(file.Name); + // byte count + `/` + if (byteCount + 1 > FieldNameLength) { - size++; + // byte count + `/` + `\n` + size += (ulong)byteCount + 2; } + } - // Once it is calculated freeze it - Size = size; + if ((size & 1) != 0) + { + size++; } + + // Once it is calculated freeze it + Size = size; } } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArObject.cs b/src/LibObjectFile/Ar/ArObject.cs index 01bb09d..f734d8b 100644 --- a/src/LibObjectFile/Ar/ArObject.cs +++ b/src/LibObjectFile/Ar/ArObject.cs @@ -8,17 +8,12 @@ namespace LibObjectFile.Ar; -public abstract class ArObject : ObjectFileNode +public abstract class ArObjectBase : ObjectFileElement { - protected override void ValidateParent(ObjectFileNodeBase parent) - { - if (!(parent is ArArchiveFile)) - { - throw new ArgumentException($"Parent must inherit from type {nameof(ArArchiveFile)}"); - } - } - +} +public abstract class ArObject : ArObjectBase +{ /// /// Gets the containing . Might be null if this section or segment /// does not belong to an existing . @@ -29,4 +24,12 @@ protected override void ValidateParent(ObjectFileNodeBase parent) get => (ArArchiveFile?)base.Parent; internal set => base.Parent = value; } + + protected override void ValidateParent(ObjectFileElement parent) + { + if (!(parent is ArArchiveFile)) + { + throw new ArgumentException($"Parent must inherit from type {nameof(ArArchiveFile)}"); + } + } } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArSymbol.cs b/src/LibObjectFile/Ar/ArSymbol.cs index a5750d9..b6f064d 100644 --- a/src/LibObjectFile/Ar/ArSymbol.cs +++ b/src/LibObjectFile/Ar/ArSymbol.cs @@ -2,47 +2,46 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Ar +namespace LibObjectFile.Ar; + +/// +/// A symbol stored in a +/// +public struct ArSymbol { /// - /// A symbol stored in a + /// Initializes a new instance. /// - public struct ArSymbol + /// The name of the symbol. + /// The associated file entry this symbol is coming from . + public ArSymbol(string name, ArFile file) : this() { - /// - /// Initializes a new instance. - /// - /// The name of the symbol. - /// The associated file entry this symbol is coming from . - public ArSymbol(string name, ArFile file) : this() - { - Name = name; - File = file; - } + Name = name; + File = file; + } - /// - /// Gets or sets the name of this symbol. - /// - public string Name { get; set; } + /// + /// Gets or sets the name of this symbol. + /// + public string Name { get; set; } - /// - /// Internal offset for the name (used for reading) - /// - internal uint NameOffset { get; set; } + /// + /// Internal offset for the name (used for reading) + /// + internal uint NameOffset { get; set; } - /// - /// Gets or sets the associated file entry this symbol is coming from . - /// - public ArFile File { get; set; } + /// + /// Gets or sets the associated file entry this symbol is coming from . + /// + public ArFile File { get; set; } - /// - /// Internal offset of the file (used for reading) - /// - internal ulong FileOffset { get; set; } + /// + /// Internal offset of the file (used for reading) + /// + internal ulong FileOffset { get; set; } - public override string ToString() - { - return $"Symbol: {Name} => {nameof(File)}: {File}"; - } + public override string ToString() + { + return $"Symbol: {Name} => {nameof(File)}: {File}"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArSymbolTable.cs b/src/LibObjectFile/Ar/ArSymbolTable.cs index da263bb..28d9d06 100644 --- a/src/LibObjectFile/Ar/ArSymbolTable.cs +++ b/src/LibObjectFile/Ar/ArSymbolTable.cs @@ -7,236 +7,229 @@ using System.Diagnostics; using System.Text; -namespace LibObjectFile.Ar +namespace LibObjectFile.Ar; + +/// +/// The symbol table. When used it must be the first entry in . +/// +public sealed class ArSymbolTable : ArFile { - /// - /// The symbol table. When used it must be the first entry in . - /// - public sealed class ArSymbolTable : ArFile - { - public const string DefaultBSDSymbolTableName = "__.SYMDEF"; - public const string DefaultGNUSymbolTableName = "/"; + public const string DefaultBSDSymbolTableName = "__.SYMDEF"; + public const string DefaultGNUSymbolTableName = "/"; - public ArSymbolTable() - { - Symbols = new List(); - } + public ArSymbolTable() + { + Symbols = new List(); + } - public override string? Name + public override string? Name + { + get { - get - { - if (Parent == null) return ""; + if (Parent == null) return ""; - return Parent.Kind == ArArchiveKind.BSD ? DefaultBSDSymbolTableName : DefaultGNUSymbolTableName; - } - set => base.Name = value; + return Parent.Kind == ArArchiveKind.BSD ? DefaultBSDSymbolTableName : DefaultGNUSymbolTableName; } + set => base.Name = value; + } - public override bool IsSystem => true; - - /// - /// Gets the symbols associated to this table. - /// - public List Symbols { get; } - - protected override void Read(ArArchiveFileReader reader) - { - long startOffset = reader.Stream.Position; + public override bool IsSystem => true; - bool isBSD = reader.ArArchiveFile.Kind == ArArchiveKind.BSD; + /// + /// Gets the symbols associated to this table. + /// + public List Symbols { get; } - // A 32-bit big endian integer, giving the number of entries in the table. - uint entryCount = reader.Stream.ReadU32(false); + public override void Read(ArArchiveFileReader reader) + { + long startOffset = reader.Stream.Position; - // A set of 32-bit big endian integers. One for each symbol, recording the position within the archive of the header for the file containing this symbol. - for (uint i = 0; i < entryCount; i++) - { - uint stringOffset = 0; + bool isBSD = reader.ArArchiveFile.Kind == ArArchiveKind.BSD; - if (isBSD) - { - stringOffset = reader.Stream.ReadU32(false); - } + // A 32-bit big endian integer, giving the number of entries in the table. + uint entryCount = reader.Stream.ReadU32(false); - uint offsetOfFile = reader.Stream.ReadU32(false); + // A set of 32-bit big endian integers. One for each symbol, recording the position within the archive of the header for the file containing this symbol. + for (uint i = 0; i < entryCount; i++) + { + uint stringOffset = 0; - var symbol = new ArSymbol - { - NameOffset = stringOffset, - FileOffset = offsetOfFile, - }; - Symbols.Add(symbol); + if (isBSD) + { + stringOffset = reader.Stream.ReadU32(false); } - // A set of Zero-terminated strings. Each is a symbol name, and occurs in the same order as the list of positions in part 2. - var startStringTableOffset = isBSD ? reader.Stream.Position : 0; + uint offsetOfFile = reader.Stream.ReadU32(false); - for (uint i = 0; i < entryCount; i++) + var symbol = new ArSymbol { - bool hasError = false; - var symbol = Symbols[(int)i]; + NameOffset = stringOffset, + FileOffset = offsetOfFile, + }; + Symbols.Add(symbol); + } - var absoluteStringOffset = startStringTableOffset + symbol.NameOffset; + // A set of Zero-terminated strings. Each is a symbol name, and occurs in the same order as the list of positions in part 2. + var startStringTableOffset = isBSD ? reader.Stream.Position : 0; - if (isBSD && absoluteStringOffset >= startOffset + (long)Size) - { - hasError = true; - } - else - { - // Only BSD requires to position correctly - if (isBSD) - { - reader.Stream.Position = absoluteStringOffset; - } - - var text = reader.ReadStringUTF8NullTerminated(); - symbol.Name = text; - Symbols[(int)i] = symbol; - } + for (uint i = 0; i < entryCount; i++) + { + bool hasError = false; + var symbol = Symbols[(int)i]; - if (hasError) - { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected EOF while trying to read the string name [{i}] at file offset {absoluteStringOffset} in {this}"); - return; - } - } + var absoluteStringOffset = startStringTableOffset + symbol.NameOffset; - var sizeRead = (ulong) (reader.Stream.Position - startOffset); - if ((sizeRead & 1) != 0) + if (isBSD && absoluteStringOffset >= startOffset + (long)Size) + { + hasError = true; + } + else { - if (reader.Stream.ReadByte() >= 0) + // Only BSD requires to position correctly + if (isBSD) { - sizeRead++; + reader.Stream.Position = absoluteStringOffset; } + + var text = reader.ReadStringUTF8NullTerminated(); + symbol.Name = text; + Symbols[(int)i] = symbol; } - Debug.Assert(Size == sizeRead); - } - protected override void AfterRead(DiagnosticBag diagnostics) - { - var offsets = new Dictionary(); - foreach (var fileEntry in Parent!.Files) + if (hasError) { - offsets[fileEntry.Position] = fileEntry; + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected EOF while trying to read the string name [{i}] at file offset {absoluteStringOffset} in {this}"); + return; } + } - for (var i = 0; i < Symbols.Count; i++) + var sizeRead = (ulong) (reader.Stream.Position - startOffset); + if ((sizeRead & 1) != 0) + { + if (reader.Stream.ReadByte() >= 0) { - var symbol = Symbols[i]; - if (offsets.TryGetValue(symbol.FileOffset, out var fileEntry)) - { - symbol.File = fileEntry; - Symbols[i] = symbol; - } - else - { - diagnostics.Error(DiagnosticId.AR_ERR_InvalidFileOffsetInSystemVSymbolLookupTable, $"Unable to find file at offset {symbol.FileOffset} for symbol entry [{i}] in {this}"); - } + sizeRead++; } } + Debug.Assert(Size == sizeRead); + } - protected override void Write(ArArchiveFileWriter writer) + protected override void AfterRead(DiagnosticBag diagnostics) + { + var offsets = new Dictionary(); + foreach (var fileEntry in Parent!.Files) { - long startOffset = writer.Stream.Position; - writer.Stream.WriteU32(false, (uint)Symbols.Count); + offsets[fileEntry.Position] = fileEntry; + } - uint stringOffset = 0; - bool isBSD = Parent!.Kind == ArArchiveKind.BSD; - foreach (var symbol in Symbols) + for (var i = 0; i < Symbols.Count; i++) + { + var symbol = Symbols[i]; + if (offsets.TryGetValue(symbol.FileOffset, out var fileEntry)) { - if (isBSD) - { - writer.Stream.WriteU32(false, stringOffset); - } - - writer.Stream.WriteU32(false, (uint)symbol.File.Position); - - if (isBSD) - { - stringOffset += (uint) Encoding.UTF8.GetByteCount(symbol.Name) + 1; - } + symbol.File = fileEntry; + Symbols[i] = symbol; } - - foreach (var symbol in Symbols) + else { - writer.WriteStringUTF8NullTerminated(symbol.Name); + diagnostics.Error(DiagnosticId.AR_ERR_InvalidFileOffsetInSystemVSymbolLookupTable, $"Unable to find file at offset {symbol.FileOffset} for symbol entry [{i}] in {this}"); } + } + } + + public override void Write(ArArchiveFileWriter writer) + { + long startOffset = writer.Stream.Position; + writer.Stream.WriteU32(false, (uint)Symbols.Count); - var sizeWritten = writer.Stream.Position - startOffset; - if ((sizeWritten & 1) != 0) + uint stringOffset = 0; + bool isBSD = Parent!.Kind == ArArchiveKind.BSD; + foreach (var symbol in Symbols) + { + if (isBSD) { - writer.Stream.WriteByte(0); - sizeWritten++; + writer.Stream.WriteU32(false, stringOffset); } - // Double check that the size is actually matching what we have been serializing - Debug.Assert(sizeWritten == (long)Size); - } + writer.Stream.WriteU32(false, (uint)symbol.File.Position); - public override void Verify(DiagnosticBag diagnostics) - { - base.Verify(diagnostics); - for (var i = 0; i < Symbols.Count; i++) + if (isBSD) { - var symbol = Symbols[i]; - if (string.IsNullOrEmpty(symbol.Name)) - { - diagnostics.Error(DiagnosticId.AR_ERR_InvalidNullOrEmptySymbolName, $"Invalid null or empty symbol name [{i}] in {this}"); - } - - if (symbol.File == null) - { - diagnostics.Error(DiagnosticId.AR_ERR_InvalidNullFileForSymbol, $"Invalid null file for symbol `{symbol.Name}` [{i}] in {this}"); - } - else if (symbol.File.Parent == null) - { - diagnostics.Error(DiagnosticId.AR_ERR_InvalidNullParentFileForSymbol, $"Invalid null Parent for file `{symbol.File}` for symbol `{symbol.Name}` [{i}] in {this}"); - } - else if (symbol.File.Parent != Parent) - { - diagnostics.Error(DiagnosticId.AR_ERR_InvalidParentFileForSymbol, $"Invalid parent for file `{symbol.File}` for symbol `{symbol.Name}` [{i}] in {this}. The parent {nameof(ArArchiveFile)} is not the same instance as this symbol table"); - } + stringOffset += (uint) Encoding.UTF8.GetByteCount(symbol.Name) + 1; } } - public override string ToString() + foreach (var symbol in Symbols) { - return $"{base.ToString()}, {nameof(Symbols)} Count: {Symbols.Count}"; + writer.WriteStringUTF8NullTerminated(symbol.Name); } - public override void UpdateLayout(DiagnosticBag diagnostics) + var sizeWritten = writer.Stream.Position - startOffset; + if ((sizeWritten & 1) != 0) { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - if (Parent == null) return; + writer.Stream.WriteByte(0); + sizeWritten++; + } - // number of entries (both for BSD and GNU) - ulong sizeOfTable = sizeof(uint); + // Double check that the size is actually matching what we have been serializing + Debug.Assert(sizeWritten == (long)Size); + } - foreach (var symbol in Symbols) + public override void Verify(ArVisitorContext context) + { + var diagnostics = context.Diagnostics; + for (var i = 0; i < Symbols.Count; i++) + { + var symbol = Symbols[i]; + if (string.IsNullOrEmpty(symbol.Name)) { - if (symbol.Name != null) - { - sizeOfTable += (ulong)Encoding.UTF8.GetByteCount(symbol.Name) + 1; - - // uint file_offset - sizeOfTable += sizeof(uint); + diagnostics.Error(DiagnosticId.AR_ERR_InvalidNullOrEmptySymbolName, $"Invalid null or empty symbol name [{i}] in {this}"); + } - if (Parent.Kind == ArArchiveKind.BSD) - { - // uint string_offset - sizeOfTable += sizeof(uint); - } - } + if (symbol.File.Parent == null) + { + diagnostics.Error(DiagnosticId.AR_ERR_InvalidNullParentFileForSymbol, $"Invalid null Parent for file `{symbol.File}` for symbol `{symbol.Name}` [{i}] in {this}"); + } + else if (symbol.File.Parent != Parent) + { + diagnostics.Error(DiagnosticId.AR_ERR_InvalidParentFileForSymbol, $"Invalid parent for file `{symbol.File}` for symbol `{symbol.Name}` [{i}] in {this}. The parent {nameof(ArArchiveFile)} is not the same instance as this symbol table"); } + } + } - if ((sizeOfTable & 1) != 0) + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"{nameof(Symbols)} Count: {Symbols.Count}"); + return true; + } + + + public override void UpdateLayout(ArVisitorContext context) + { + if (Parent == null) return; + + // number of entries (both for BSD and GNU) + ulong sizeOfTable = sizeof(uint); + + foreach (var symbol in Symbols) + { + sizeOfTable += (ulong)Encoding.UTF8.GetByteCount(symbol.Name) + 1; + + // uint file_offset + sizeOfTable += sizeof(uint); + + if (Parent.Kind == ArArchiveKind.BSD) { - sizeOfTable++; + // uint string_offset + sizeOfTable += sizeof(uint); } + } - Size = sizeOfTable; + if ((sizeOfTable & 1) != 0) + { + sizeOfTable++; } + + Size = sizeOfTable; } } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArVisitorContext.cs b/src/LibObjectFile/Ar/ArVisitorContext.cs new file mode 100644 index 0000000..9c62fd0 --- /dev/null +++ b/src/LibObjectFile/Ar/ArVisitorContext.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.Ar; + +public class ArVisitorContext : VisitorContextBase +{ + internal ArVisitorContext(ArArchiveFile file, DiagnosticBag diagnostics) : base(file, diagnostics) + { + } +} \ No newline at end of file diff --git a/src/LibObjectFile/DiagnosticId.cs b/src/LibObjectFile/DiagnosticId.cs index 35a53ff..d7f2910 100644 --- a/src/LibObjectFile/DiagnosticId.cs +++ b/src/LibObjectFile/DiagnosticId.cs @@ -2,142 +2,141 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile +namespace LibObjectFile; + +/// +/// Defines the various diagnostic message ids. +/// +public enum DiagnosticId { - /// - /// Defines the various diagnostic message ids. - /// - public enum DiagnosticId - { - CMN_ERR_UnexpectedEndOfFile = 1, + CMN_ERR_UnexpectedEndOfFile = 1, - // Elf - ELF_ERR_LinkOrInfoSectionNull = 102, - ELF_ERR_LinkOrInfoInvalidSectionType = 103, - ELF_ERR_LinkOrInfoInvalidSectionInstance = 104, - ELF_ERR_InvalidHeaderFileClassNone = 105, - ELF_ERR_InvalidHeaderIdentLength = 106, - ELF_ERR_InvalidHeaderMagic = 107, - //ELF_ERR_InvalidHeaderFileClass = 8, - //ELF_ERR_InvalidHeaderEncoding = 9, - ELF_ERR_MissingProgramHeaderTableSection = 110, - ELF_ERR_InvalidSectionHeaderCount = 111, - ELF_ERR_IncompleteHeader32Size = 112, - ELF_ERR_IncompleteHeader64Size = 113, - ELF_ERR_InvalidZeroProgramHeaderTableEntrySize = 114, - ELF_ERR_InvalidProgramHeaderStreamOffset = 115, - ELF_ERR_IncompleteProgramHeader32Size = 116, - ELF_ERR_IncompleteProgramHeader64Size = 117, - ELF_ERR_InvalidZeroSectionHeaderTableEntrySize = 118, - ELF_ERR_InvalidSectionHeaderStreamOffset = 119, - ELF_ERR_IncompleteSectionHeader32Size = 120, - ELF_ERR_IncompleteSectionHeader64Size = 121, - ELF_ERR_InvalidResolvedLink = 122, - ELF_ERR_InvalidFirstSectionExpectingUndefined = 123, - ELF_ERR_InvalidStringIndexMissingStringHeaderTable = 124, - ELF_ERR_InvalidStringIndex = 125, - ELF_ERR_InvalidOverlappingSections = 126, - ELF_ERR_InvalidSegmentRange = 127, - ELF_ERR_InvalidSectionSizeKind = 128, - ELF_ERR_InvalidSectionLinkParent = 129, - ELF_ERR_InvalidSectionInfoParent = 130, - ELF_ERR_InvalidSegmentRangeBeginSectionParent = 131, - ELF_ERR_InvalidSegmentRangeEndSectionParent = 132, - ELF_ERR_InvalidSegmentRangeBeginOffset = 133, - ELF_ERR_InvalidSegmentRangeEndOffset = 134, - ELF_ERR_InvalidSegmentRangeIndices = 135, - ELF_ERR_IncompleteRelocationAddendsEntry32Size = 136, - ELF_ERR_IncompleteRelocationEntry32Size = 137, - ELF_ERR_IncompleteRelocationAddendsEntry64Size = 138, - ELF_ERR_IncompleteRelocationEntry64Size = 139, - ELF_WRN_InvalidRelocationTablePrefixName = 140, - ELF_WRN_InvalidRelocationTablePrefixTargetName = 141, - ELF_ERR_InvalidRelocationInfoParent = 142, - ELF_ERR_InvalidRelocationEntryAddend = 143, - ELF_ERR_InvalidRelocationEntryArch = 144, - ELF_ERR_InvalidRelocationSymbolIndex = 145, - ELF_ERR_IncompleteSymbolEntry32Size = 146, - ELF_ERR_IncompleteSymbolEntry64Size = 147, - ELF_ERR_InvalidFirstSymbolEntryNonNull = 148, - ELF_ERR_InvalidSymbolEntryNameIndex = 149, - ELF_ERR_InvalidSymbolEntrySectionParent = 150, - ELF_ERR_InvalidSymbolEntryLocalPosition = 151, - ELF_ERR_IncompleteNoteEntrySize = 152, - ELF_ERR_IncompleNoteGnuAbiTag = 153, - ELF_ERR_InvalidSegmentVirtualAddressOrOffset = 154, - ELF_ERR_InvalidSegmentAlignmentForLoad = 155, - ELF_ERR_InvalidStreamForSectionNoBits = 156, - ELF_ERR_InvalidNullSection = 157, - ELF_ERR_InvalidAlignmentOutOfRange = 158, - ELF_ERR_MissingSectionHeaderIndices = 159, - ELF_ERR_MissingNullSection = 159, + // Elf + ELF_ERR_LinkOrInfoSectionNull = 102, + ELF_ERR_LinkOrInfoInvalidSectionType = 103, + ELF_ERR_LinkOrInfoInvalidSectionInstance = 104, + ELF_ERR_InvalidHeaderFileClassNone = 105, + ELF_ERR_InvalidHeaderIdentLength = 106, + ELF_ERR_InvalidHeaderMagic = 107, + //ELF_ERR_InvalidHeaderFileClass = 8, + //ELF_ERR_InvalidHeaderEncoding = 9, + ELF_ERR_MissingProgramHeaderTableSection = 110, + ELF_ERR_InvalidSectionHeaderCount = 111, + ELF_ERR_IncompleteHeader32Size = 112, + ELF_ERR_IncompleteHeader64Size = 113, + ELF_ERR_InvalidZeroProgramHeaderTableEntrySize = 114, + ELF_ERR_InvalidProgramHeaderStreamOffset = 115, + ELF_ERR_IncompleteProgramHeader32Size = 116, + ELF_ERR_IncompleteProgramHeader64Size = 117, + ELF_ERR_InvalidZeroSectionHeaderTableEntrySize = 118, + ELF_ERR_InvalidSectionHeaderStreamOffset = 119, + ELF_ERR_IncompleteSectionHeader32Size = 120, + ELF_ERR_IncompleteSectionHeader64Size = 121, + ELF_ERR_InvalidResolvedLink = 122, + ELF_ERR_InvalidFirstSectionExpectingUndefined = 123, + ELF_ERR_InvalidStringIndexMissingStringHeaderTable = 124, + ELF_ERR_InvalidStringIndex = 125, + ELF_ERR_InvalidOverlappingSections = 126, + ELF_ERR_InvalidSegmentRange = 127, + ELF_ERR_InvalidSectionSizeKind = 128, + ELF_ERR_InvalidSectionLinkParent = 129, + ELF_ERR_InvalidSectionInfoParent = 130, + ELF_ERR_InvalidSegmentRangeBeginSectionParent = 131, + ELF_ERR_InvalidSegmentRangeEndSectionParent = 132, + ELF_ERR_InvalidSegmentRangeBeginOffset = 133, + ELF_ERR_InvalidSegmentRangeEndOffset = 134, + ELF_ERR_InvalidSegmentRangeIndices = 135, + ELF_ERR_IncompleteRelocationAddendsEntry32Size = 136, + ELF_ERR_IncompleteRelocationEntry32Size = 137, + ELF_ERR_IncompleteRelocationAddendsEntry64Size = 138, + ELF_ERR_IncompleteRelocationEntry64Size = 139, + ELF_WRN_InvalidRelocationTablePrefixName = 140, + ELF_WRN_InvalidRelocationTablePrefixTargetName = 141, + ELF_ERR_InvalidRelocationInfoParent = 142, + ELF_ERR_InvalidRelocationEntryAddend = 143, + ELF_ERR_InvalidRelocationEntryArch = 144, + ELF_ERR_InvalidRelocationSymbolIndex = 145, + ELF_ERR_IncompleteSymbolEntry32Size = 146, + ELF_ERR_IncompleteSymbolEntry64Size = 147, + ELF_ERR_InvalidFirstSymbolEntryNonNull = 148, + ELF_ERR_InvalidSymbolEntryNameIndex = 149, + ELF_ERR_InvalidSymbolEntrySectionParent = 150, + ELF_ERR_InvalidSymbolEntryLocalPosition = 151, + ELF_ERR_IncompleteNoteEntrySize = 152, + ELF_ERR_IncompleNoteGnuAbiTag = 153, + ELF_ERR_InvalidSegmentVirtualAddressOrOffset = 154, + ELF_ERR_InvalidSegmentAlignmentForLoad = 155, + ELF_ERR_InvalidStreamForSectionNoBits = 156, + ELF_ERR_InvalidNullSection = 157, + ELF_ERR_InvalidAlignmentOutOfRange = 158, + ELF_ERR_MissingSectionHeaderIndices = 159, + ELF_ERR_MissingNullSection = 159, - AR_ERR_InvalidMagicLength = 1000, - AR_ERR_MagicNotFound = 1001, - AR_ERR_ExpectingNewLineCharacter = 1002, - //AR_ERR_UnexpectedEndOfFile = 1003, - AR_ERR_InvalidFileEntryLength = 1004, - AR_ERR_InvalidNonPrintableASCIIFoundInFileEntry = 1005, - AR_ERR_InvalidCharacterFoundInFileEntry = 1006, - AR_ERR_InvalidNullFileEntryName = 1007, - AR_ERR_InvalidFileOffsetInSystemVSymbolLookupTable = 1008, - AR_ERR_InvalidDuplicatedFutureHeadersTable = 1009, - AR_ERR_InvalidReferenceToFutureHeadersTable = 1010, - AR_ERR_InvalidFileEntryNameTooLong = 1011, - AR_ERR_InvalidCharacterInFileEntryName = 1012, - AR_ERR_InvalidNullOrEmptySymbolName = 1013, - AR_ERR_InvalidNullFileForSymbol = 1014, - AR_ERR_InvalidNullParentFileForSymbol = 1015, - AR_ERR_InvalidParentFileForSymbol = 1016, - AR_ERR_InvalidFileEntrySize = 1017, + AR_ERR_InvalidMagicLength = 1000, + AR_ERR_MagicNotFound = 1001, + AR_ERR_ExpectingNewLineCharacter = 1002, + //AR_ERR_UnexpectedEndOfFile = 1003, + AR_ERR_InvalidFileEntryLength = 1004, + AR_ERR_InvalidNonPrintableASCIIFoundInFileEntry = 1005, + AR_ERR_InvalidCharacterFoundInFileEntry = 1006, + AR_ERR_InvalidNullFileEntryName = 1007, + AR_ERR_InvalidFileOffsetInSystemVSymbolLookupTable = 1008, + AR_ERR_InvalidDuplicatedFutureHeadersTable = 1009, + AR_ERR_InvalidReferenceToFutureHeadersTable = 1010, + AR_ERR_InvalidFileEntryNameTooLong = 1011, + AR_ERR_InvalidCharacterInFileEntryName = 1012, + AR_ERR_InvalidNullOrEmptySymbolName = 1013, + AR_ERR_InvalidNullFileForSymbol = 1014, + AR_ERR_InvalidNullParentFileForSymbol = 1015, + AR_ERR_InvalidParentFileForSymbol = 1016, + AR_ERR_InvalidFileEntrySize = 1017, - DWARF_ERR_AttributeLEB128OutOfRange = 2000, - DWARF_ERR_VersionNotSupported = 2001, - DWARF_ERR_InvalidData = 2002, - DWARF_WRN_UnsupportedLineExtendedCode = 2003, - DWARF_ERR_InvalidReference = 2004, - DWARF_ERR_MissingStringTable = 2005, - DWARF_ERR_InvalidNumberOfStandardOpCodeLengths = 2006, - DWARF_ERR_InvalidStandardOpCodeLength = 2007, - DWARF_WRN_CannotEncodeAddressIncrement = 2008, - DWARF_ERR_InvalidNullFileNameEntry = 2009, - DWARF_ERR_InvalidFileName = 2010, - DWARF_ERR_InvalidMaximumOperationsPerInstruction = 2011, - DWARF_ERR_InvalidNegativeAddressDelta = 2012, - DWARF_ERR_InvalidOperationIndex = 2013, - DWARF_ERR_InvalidAddressSize = 2014, - DWARF_ERR_UnsupportedUnitType = 2015, - DWARF_ERR_InvalidNullUnitForAddressRangeTable = 2016, - DWARF_ERR_InvalidParentUnitForAddressRangeTable = 2017, - DWARF_ERR_InvalidParentForDIE = 2018, - DWARF_WRN_InvalidExtendedOpCodeLength = 2019, - DWARF_ERR_InvalidParentForLocationList = 2020, + DWARF_ERR_AttributeLEB128OutOfRange = 2000, + DWARF_ERR_VersionNotSupported = 2001, + DWARF_ERR_InvalidData = 2002, + DWARF_WRN_UnsupportedLineExtendedCode = 2003, + DWARF_ERR_InvalidReference = 2004, + DWARF_ERR_MissingStringTable = 2005, + DWARF_ERR_InvalidNumberOfStandardOpCodeLengths = 2006, + DWARF_ERR_InvalidStandardOpCodeLength = 2007, + DWARF_WRN_CannotEncodeAddressIncrement = 2008, + DWARF_ERR_InvalidNullFileNameEntry = 2009, + DWARF_ERR_InvalidFileName = 2010, + DWARF_ERR_InvalidMaximumOperationsPerInstruction = 2011, + DWARF_ERR_InvalidNegativeAddressDelta = 2012, + DWARF_ERR_InvalidOperationIndex = 2013, + DWARF_ERR_InvalidAddressSize = 2014, + DWARF_ERR_UnsupportedUnitType = 2015, + DWARF_ERR_InvalidNullUnitForAddressRangeTable = 2016, + DWARF_ERR_InvalidParentUnitForAddressRangeTable = 2017, + DWARF_ERR_InvalidParentForDIE = 2018, + DWARF_WRN_InvalidExtendedOpCodeLength = 2019, + DWARF_ERR_InvalidParentForLocationList = 2020, - // PE errors - PE_ERR_InvalidDosHeaderSize = 3000, - PE_ERR_InvalidDosHeaderMagic = 3001, - PE_ERR_InvalidDosStubSize = 3002, - PE_ERR_InvalidPESignature = 3003, - PE_ERR_InvalidCoffHeaderSize = 3004, - PE_ERR_InvalidOptionalHeaderSize = 3005, - PE_ERR_InvalidOptionalHeaderMagic = 3006, - PE_ERR_InvalidSectionHeadersSize = 3007, - PE_ERR_InvalidParent = 3008, + // PE errors + PE_ERR_InvalidDosHeaderSize = 3000, + PE_ERR_InvalidDosHeaderMagic = 3001, + PE_ERR_InvalidDosStubSize = 3002, + PE_ERR_InvalidPESignature = 3003, + PE_ERR_InvalidCoffHeaderSize = 3004, + PE_ERR_InvalidOptionalHeaderSize = 3005, + PE_ERR_InvalidOptionalHeaderMagic = 3006, + PE_ERR_InvalidSectionHeadersSize = 3007, + PE_ERR_InvalidParent = 3008, - // PE BaseRelocation - PE_ERR_BaseRelocationDirectoryInvalidEndOfStream = 3020, - PE_ERR_BaseRelocationDirectoryInvalidSection = 3021, - PE_ERR_BaseRelocationDirectoryInvalidSectionData = 3022, - PE_ERR_InvalidDataDirectorySection = 3023, - PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress = 3024, + // PE BaseRelocation + PE_ERR_BaseRelocationDirectoryInvalidEndOfStream = 3020, + PE_ERR_BaseRelocationDirectoryInvalidSection = 3021, + PE_ERR_BaseRelocationDirectoryInvalidSectionData = 3022, + PE_ERR_InvalidDataDirectorySection = 3023, + PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress = 3024, - // 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, - } -} + // 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/DiagnosticKind.cs b/src/LibObjectFile/DiagnosticKind.cs index 2ec7425..5b25bba 100644 --- a/src/LibObjectFile/DiagnosticKind.cs +++ b/src/LibObjectFile/DiagnosticKind.cs @@ -2,21 +2,20 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile +namespace LibObjectFile; + +/// +/// Defines the kind of a +/// +public enum DiagnosticKind { /// - /// Defines the kind of a + /// A warning message. /// - public enum DiagnosticKind - { - /// - /// A warning message. - /// - Warning, + Warning, - /// - /// An error message. - /// - Error, - } + /// + /// An error message. + /// + Error, } \ No newline at end of file diff --git a/src/LibObjectFile/DiagnosticMessage.cs b/src/LibObjectFile/DiagnosticMessage.cs index 65e9b7d..3e69010 100644 --- a/src/LibObjectFile/DiagnosticMessage.cs +++ b/src/LibObjectFile/DiagnosticMessage.cs @@ -2,52 +2,51 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile +namespace LibObjectFile; + +/// +/// A diagnostic message. +/// +public readonly struct DiagnosticMessage { - /// - /// A diagnostic message. - /// - public readonly struct DiagnosticMessage + public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string message) { - public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string message) - { - Kind = kind; - Id = id; - Context = null; - Message = message; - } + Kind = kind; + Id = id; + Context = null; + Message = message; + } - public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string message, object? context) - { - Kind = kind; - Id = id; - Context = context; - Message = message; - } + public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string message, object? context) + { + Kind = kind; + Id = id; + Context = context; + Message = message; + } - /// - /// Gets the kind of this message. - /// - public DiagnosticKind Kind { get; } + /// + /// Gets the kind of this message. + /// + public DiagnosticKind Kind { get; } - /// - /// Gets the id of this message. - /// - public DiagnosticId Id { get; } + /// + /// Gets the id of this message. + /// + public DiagnosticId Id { get; } - /// - /// Gets the context of this message. - /// - public object? Context { get; } + /// + /// Gets the context of this message. + /// + public object? Context { get; } - /// - /// Gets the associated text of this message. - /// - public string Message { get; } + /// + /// Gets the associated text of this message. + /// + public string Message { get; } - public override string ToString() - { - return $"{Kind} LB{(uint)Id:0000}: {Message}"; - } + public override string ToString() + { + return $"{Kind} LB{(uint)Id:0000}: {Message}"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs index 7cf2988..728f92f 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs @@ -8,235 +8,234 @@ using System.Diagnostics.CodeAnalysis; using System.IO; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] +public sealed class DwarfAbbreviation : DwarfObject { - [DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] - public sealed class DwarfAbbreviation : DwarfObject + private readonly List _items; + private readonly Dictionary _mapItems; // Only used if code are non contiguous + private readonly Dictionary _mapKeyToItem; + private ulong _nextCode; + + public DwarfAbbreviation() { - private readonly List _items; - private readonly Dictionary _mapItems; // Only used if code are non contiguous - private readonly Dictionary _mapKeyToItem; - private ulong _nextCode; + _items = new List(); + _mapItems = new Dictionary(); + _mapKeyToItem = new Dictionary(); + _nextCode = 1; + } - public DwarfAbbreviation() + public void Reset() + { + // Reset parent dependency + foreach (var dwarfAbbreviationItem in _items) { - _items = new List(); - _mapItems = new Dictionary(); - _mapKeyToItem = new Dictionary(); - _nextCode = 1; + dwarfAbbreviationItem.Parent = null; } - public void Reset() + if (_mapItems.Count > 0) { - // Reset parent dependency - foreach (var dwarfAbbreviationItem in _items) - { - dwarfAbbreviationItem.Parent = null; - } - - if (_mapItems.Count > 0) + foreach (var keyPair in _mapItems) { - foreach (var keyPair in _mapItems) - { - keyPair.Value.Parent = null; - } + keyPair.Value.Parent = null; } - - _items.Clear(); - _mapItems.Clear(); - _mapKeyToItem.Clear(); - _nextCode = 1; } - public IEnumerable Items => _mapItems.Count > 0 ? GetMapItems() : _items; + _items.Clear(); + _mapItems.Clear(); + _mapKeyToItem.Clear(); + _nextCode = 1; + } + + public IEnumerable Items => _mapItems.Count > 0 ? GetMapItems() : _items; - private IEnumerable GetMapItems() + private IEnumerable GetMapItems() + { + foreach (var item in _mapItems.Values) { - foreach (var item in _mapItems.Values) - { - yield return item; - } + yield return item; } + } - public DwarfAbbreviationItem GetOrCreate(DwarfAbbreviationItemKey itemKey) + public DwarfAbbreviationItem GetOrCreate(DwarfAbbreviationItemKey itemKey) + { + if (!_mapKeyToItem.TryGetValue(itemKey, out var item)) { - if (!_mapKeyToItem.TryGetValue(itemKey, out var item)) + item = new DwarfAbbreviationItem(_nextCode, itemKey.Tag, itemKey.HasChildren, itemKey.Descriptors) { - item = new DwarfAbbreviationItem(_nextCode, itemKey.Tag, itemKey.HasChildren, itemKey.Descriptors) - { - Parent = this - }; + Parent = this + }; - if (_mapItems.Count > 0) - { + if (_mapItems.Count > 0) + { - _mapItems[_nextCode] = item; - } - else - { - _items.Add(item); - } + _mapItems[_nextCode] = item; + } + else + { + _items.Add(item); + } - _mapKeyToItem[itemKey] = item; + _mapKeyToItem[itemKey] = item; - _nextCode++; - } + _nextCode++; + } + + return item; + } - return item; + public bool TryFindByCode(ulong code, [NotNullWhen(true)] out DwarfAbbreviationItem? item) + { + item = null; + if (code == 0) + { + return false; } - public bool TryFindByCode(ulong code, [NotNullWhen(true)] out DwarfAbbreviationItem? item) + code--; + + if (_mapItems.Count > 0) { - item = null; - if (code == 0) - { - return false; - } + return _mapItems.TryGetValue(code, out item); + } - code--; + if (code < int.MaxValue && (int)code < _items.Count) + { + item = _items[(int) code]; + return true; + } - if (_mapItems.Count > 0) - { - return _mapItems.TryGetValue(code, out item); - } + item = null; + return false; + } - if (code < int.MaxValue && (int)code < _items.Count) - { - item = _items[(int) code]; - return true; - } + private string DebuggerDisplay => $"Count = {(_mapItems.Count > 0 ? _mapItems.Count : _items.Count)}"; - item = null; + private bool TryReadNext(DwarfReader reader) + { + var startOffset = (ulong)reader.Position; + var code = reader.ReadULEB128(); + if (code == 0) + { return false; } - private string DebuggerDisplay => $"Count = {(_mapItems.Count > 0 ? _mapItems.Count : _items.Count)}"; - - private bool TryReadNext(DwarfReader reader) + var item = new DwarfAbbreviationItem { - var startOffset = (ulong)reader.Position; - var code = reader.ReadULEB128(); - if (code == 0) - { - return false; - } + Position = startOffset, + Code = code + }; - var item = new DwarfAbbreviationItem - { - Position = startOffset, - Code = code - }; - - var index = code - 1; - bool canAddToList = _mapItems.Count == 0 && index < int.MaxValue && _items.Count == (int)index; + var index = code - 1; + bool canAddToList = _mapItems.Count == 0 && index < int.MaxValue && _items.Count == (int)index; - item.ReadInternal(reader); + item.Read(reader); - if (canAddToList) - { - _items.Add(item); - _nextCode++; - } - else + if (canAddToList) + { + _items.Add(item); + _nextCode++; + } + else + { + if (_mapItems.Count == 0) { - if (_mapItems.Count == 0) - { - for (var i = 0; i < _items.Count; i++) - { - var previousItem = _items[i]; - _mapItems.Add((ulong)i + 1, previousItem); - } - _items.Clear(); - } - - // TODO: check collisions - if (_mapItems.ContainsKey(code)) + for (var i = 0; i < _items.Count; i++) { - reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"Invalid code {code} found while another code already exists in this abbreviation."); - return false; + var previousItem = _items[i]; + _mapItems.Add((ulong)i + 1, previousItem); } - _mapItems.Add(code, item); + _items.Clear(); + } - _nextCode = Math.Max(code, _nextCode) + 1; + // TODO: check collisions + if (_mapItems.ContainsKey(code)) + { + reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"Invalid code {code} found while another code already exists in this abbreviation."); + return false; } + _mapItems.Add(code, item); - var key = new DwarfAbbreviationItemKey(item.Tag, item.HasChildren, item.Descriptors); - _mapKeyToItem.Add(key, item); + _nextCode = Math.Max(code, _nextCode) + 1; + } - return true; + var key = new DwarfAbbreviationItemKey(item.Tag, item.HasChildren, item.Descriptors); + _mapKeyToItem.Add(key, item); + + return true; + } + + public override void Read(DwarfReader reader) + { + Position = reader.Position; + while (TryReadNext(reader)) + { } - protected override void Read(DwarfReader reader) + Size = (ulong)reader.Position - Position; + } + + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; + Debug.Assert(startOffset == Position); + if (_mapItems.Count > 0) { - Position = reader.Position; - while (TryReadNext(reader)) + foreach (var itemPair in _mapItems) { + var item = itemPair.Value; + item.Write(writer); } - Size = (ulong)reader.Position - Position; } - - protected override void Write(DwarfWriter writer) + else { - var startOffset = writer.Position; - Debug.Assert(startOffset == Position); - if (_mapItems.Count > 0) - { - foreach (var itemPair in _mapItems) - { - var item = itemPair.Value; - item.WriteInternal(writer); - } - - } - else + if (_items.Count > 0) { - if (_items.Count > 0) + foreach (var item in _items) { - foreach (var item in _items) - { - item.WriteInternal(writer); - } + item.Write(writer); } } + } - // End of abbreviation item - writer.WriteULEB128(0); + // End of abbreviation item + writer.WriteULEB128(0); - Debug.Assert(writer.Position - startOffset == Size); - } + Debug.Assert(writer.Position - startOffset == Size); + } + + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + var endOffset = Position; - protected override void UpdateLayout(DwarfLayoutContext layoutContext) + if (_mapItems.Count > 0) { - var endOffset = Position; + foreach (var itemPair in _mapItems) + { + var item = itemPair.Value; + item.Position = endOffset; + item.UpdateLayout(layoutContext); + endOffset += item.Size; + } - if (_mapItems.Count > 0) + } + else + { + if (_items.Count > 0) { - foreach (var itemPair in _mapItems) + foreach (var item in _items) { - var item = itemPair.Value; item.Position = endOffset; - item.UpdateLayoutInternal(layoutContext); + item.UpdateLayout(layoutContext); endOffset += item.Size; } - - } - else - { - if (_items.Count > 0) - { - foreach (var item in _items) - { - item.Position = endOffset; - item.UpdateLayoutInternal(layoutContext); - endOffset += item.Size; - } - } } + } - endOffset += DwarfHelper.SizeOfULEB128(0); + endOffset += DwarfHelper.SizeOfULEB128(0); - Size = endOffset - Position; - } + Size = endOffset - Position; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs index 0cd6358..3ff5e5a 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs @@ -5,121 +5,120 @@ using System.Collections.Generic; using System.Diagnostics; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public sealed class DwarfAbbreviationItem : DwarfObject { - public sealed class DwarfAbbreviationItem : DwarfObject + internal DwarfAbbreviationItem() { - internal DwarfAbbreviationItem() - { - } + } - internal DwarfAbbreviationItem(ulong code, DwarfTagEx tag, bool hasChildren, DwarfAttributeDescriptors descriptors) - { - Code = code; - Tag = tag; - HasChildren = hasChildren; - Descriptors = descriptors; - } + internal DwarfAbbreviationItem(ulong code, DwarfTagEx tag, bool hasChildren, DwarfAttributeDescriptors descriptors) + { + Code = code; + Tag = tag; + HasChildren = hasChildren; + Descriptors = descriptors; + } - public ulong Code { get; internal set; } + public ulong Code { get; internal set; } - public DwarfTagEx Tag { get; private set; } + public DwarfTagEx Tag { get; private set; } - public bool HasChildren { get; private set; } + public bool HasChildren { get; private set; } - public DwarfAttributeDescriptors Descriptors { get; private set; } + public DwarfAttributeDescriptors Descriptors { get; private set; } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - var endOffset = Position; + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + var endOffset = Position; - // Code - endOffset += DwarfHelper.SizeOfULEB128(Code); + // Code + endOffset += DwarfHelper.SizeOfULEB128(Code); - // Tag - endOffset += DwarfHelper.SizeOfULEB128((uint)Tag.Value); + // Tag + endOffset += DwarfHelper.SizeOfULEB128((uint)Tag.Value); - // HasChildren - endOffset += 1; + // HasChildren + endOffset += 1; - var descriptors = Descriptors; - for (int i = 0; i < descriptors.Length; i++) - { - var descriptor = descriptors[i]; - endOffset += DwarfHelper.SizeOfULEB128((uint)descriptor.Kind.Value); - endOffset += DwarfHelper.SizeOfULEB128((uint)descriptor.Form.Value); - } + var descriptors = Descriptors; + for (int i = 0; i < descriptors.Length; i++) + { + var descriptor = descriptors[i]; + endOffset += DwarfHelper.SizeOfULEB128((uint)descriptor.Kind.Value); + endOffset += DwarfHelper.SizeOfULEB128((uint)descriptor.Form.Value); + } - // Null Kind and Form - endOffset += DwarfHelper.SizeOfULEB128(0) * 2; + // Null Kind and Form + endOffset += DwarfHelper.SizeOfULEB128(0) * 2; - Size = endOffset - Position; - } + Size = endOffset - Position; + } - protected override void Read(DwarfReader reader) + public override void Read(DwarfReader reader) + { + var itemTag = new DwarfTagEx(reader.ReadULEB128AsU32()); + Tag = itemTag; + var hasChildrenRaw = reader.ReadU8(); + bool hasChildren = false; + if (hasChildrenRaw == DwarfNative.DW_CHILDREN_yes) { - var itemTag = new DwarfTagEx(reader.ReadULEB128AsU32()); - Tag = itemTag; - var hasChildrenRaw = reader.ReadU8(); - bool hasChildren = false; - if (hasChildrenRaw == DwarfNative.DW_CHILDREN_yes) - { - hasChildren = true; - } - else if (hasChildrenRaw != DwarfNative.DW_CHILDREN_no) - { - reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"Invalid children {hasChildrenRaw}. Must be either {DwarfNative.DW_CHILDREN_yes} or {DwarfNative.DW_CHILDREN_no}"); - return; - } - - HasChildren = hasChildren; + hasChildren = true; + } + else if (hasChildrenRaw != DwarfNative.DW_CHILDREN_no) + { + reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"Invalid children {hasChildrenRaw}. Must be either {DwarfNative.DW_CHILDREN_yes} or {DwarfNative.DW_CHILDREN_no}"); + return; + } - List? descriptors = null; + HasChildren = hasChildren; - while (true) - { - var attributeName = new DwarfAttributeKindEx(reader.ReadULEB128AsU32()); - var attributeForm = new DwarfAttributeFormEx(reader.ReadULEB128AsU32()); + List? descriptors = null; - if (attributeForm.Value == 0 && attributeForm.Value == 0) - { - break; - } + while (true) + { + var attributeName = new DwarfAttributeKindEx(reader.ReadULEB128AsU32()); + var attributeForm = new DwarfAttributeFormEx(reader.ReadULEB128AsU32()); - if (descriptors == null) descriptors = new List(1); - descriptors.Add(new DwarfAttributeDescriptor(attributeName, attributeForm)); + if (attributeForm.Value == 0 && attributeForm.Value == 0) + { + break; } - Descriptors = descriptors != null ? new DwarfAttributeDescriptors(descriptors.ToArray()) : new DwarfAttributeDescriptors(); - - Size = reader.Position - Position; + if (descriptors == null) descriptors = new List(1); + descriptors.Add(new DwarfAttributeDescriptor(attributeName, attributeForm)); } - protected override void Write(DwarfWriter writer) - { - var startOffset = writer.Position; - Debug.Assert(startOffset == Position); + Descriptors = descriptors != null ? new DwarfAttributeDescriptors(descriptors.ToArray()) : new DwarfAttributeDescriptors(); - // Code - writer.WriteULEB128(Code); + Size = reader.Position - Position; + } - // Tag - writer.WriteULEB128((uint)Tag.Value); + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; + Debug.Assert(startOffset == Position); - // HasChildren - writer.WriteU8(HasChildren ? DwarfNative.DW_CHILDREN_yes : DwarfNative.DW_CHILDREN_no); + // Code + writer.WriteULEB128(Code); - var descriptors = Descriptors; - for (int i = 0; i < descriptors.Length; i++) - { - var descriptor = descriptors[i]; - writer.WriteULEB128((uint)descriptor.Kind.Value); - writer.WriteULEB128((uint)descriptor.Form.Value); - } - writer.WriteULEB128(0); - writer.WriteULEB128(0); + // Tag + writer.WriteULEB128((uint)Tag.Value); + + // HasChildren + writer.WriteU8(HasChildren ? DwarfNative.DW_CHILDREN_yes : DwarfNative.DW_CHILDREN_no); - Debug.Assert(writer.Position - startOffset == Size); + var descriptors = Descriptors; + for (int i = 0; i < descriptors.Length; i++) + { + var descriptor = descriptors[i]; + writer.WriteULEB128((uint)descriptor.Kind.Value); + writer.WriteULEB128((uint)descriptor.Form.Value); } + writer.WriteULEB128(0); + writer.WriteULEB128(0); + + Debug.Assert(writer.Position - startOffset == Size); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationItemKey.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationItemKey.cs index c271a18..6d84f03 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationItemKey.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationItemKey.cs @@ -4,58 +4,57 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public readonly struct DwarfAbbreviationItemKey : IEquatable { - public readonly struct DwarfAbbreviationItemKey : IEquatable + public DwarfAbbreviationItemKey(DwarfTagEx tag, bool hasChildren, DwarfAttributeDescriptors descriptors) { - public DwarfAbbreviationItemKey(DwarfTagEx tag, bool hasChildren, DwarfAttributeDescriptors descriptors) - { - Tag = tag; - HasChildren = hasChildren; - Descriptors = descriptors; - } + Tag = tag; + HasChildren = hasChildren; + Descriptors = descriptors; + } - public readonly DwarfTagEx Tag; + public readonly DwarfTagEx Tag; - public readonly bool HasChildren; + public readonly bool HasChildren; - public readonly DwarfAttributeDescriptors Descriptors; + public readonly DwarfAttributeDescriptors Descriptors; - public bool Equals(DwarfAbbreviationItemKey other) - { - return Tag == other.Tag && HasChildren == other.HasChildren && Descriptors.Equals(other.Descriptors); - } + public bool Equals(DwarfAbbreviationItemKey other) + { + return Tag == other.Tag && HasChildren == other.HasChildren && Descriptors.Equals(other.Descriptors); + } - public override bool Equals(object? obj) - { - return obj is DwarfAbbreviationItemKey other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is DwarfAbbreviationItemKey other && Equals(other); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = Tag.GetHashCode(); - hashCode = (hashCode * 397) ^ HasChildren.GetHashCode(); - hashCode = (hashCode * 397) ^ Descriptors.GetHashCode(); - return hashCode; - } + var hashCode = Tag.GetHashCode(); + hashCode = (hashCode * 397) ^ HasChildren.GetHashCode(); + hashCode = (hashCode * 397) ^ Descriptors.GetHashCode(); + return hashCode; } + } - public static bool operator ==(DwarfAbbreviationItemKey left, DwarfAbbreviationItemKey right) - { - return left.Equals(right); - } + public static bool operator ==(DwarfAbbreviationItemKey left, DwarfAbbreviationItemKey right) + { + return left.Equals(right); + } - public static bool operator !=(DwarfAbbreviationItemKey left, DwarfAbbreviationItemKey right) - { - return !left.Equals(right); - } + public static bool operator !=(DwarfAbbreviationItemKey left, DwarfAbbreviationItemKey right) + { + return !left.Equals(right); + } - public override string ToString() - { - return $"{nameof(Tag)}: {Tag}, {nameof(HasChildren)}: {HasChildren}, {nameof(Descriptors)}: {Descriptors}"; - } + public override string ToString() + { + return $"{nameof(Tag)}: {Tag}, {nameof(HasChildren)}: {HasChildren}, {nameof(Descriptors)}: {Descriptors}"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs index 096a327..fa6a41b 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs @@ -6,82 +6,81 @@ using System.Diagnostics; using LibObjectFile.Utils; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfAbbreviationTable : DwarfSection { - public class DwarfAbbreviationTable : DwarfSection + private readonly List _abbreviations; + + public DwarfAbbreviationTable() { - private readonly List _abbreviations; + _abbreviations = new List(); + } - public DwarfAbbreviationTable() - { - _abbreviations = new List(); - } + public ReadOnlyList Abbreviations => _abbreviations; - public ReadOnlyList Abbreviations => _abbreviations; + internal void AddAbbreviation(DwarfAbbreviation abbreviation) + { + _abbreviations.Add(this, abbreviation); + } - internal void AddAbbreviation(DwarfAbbreviation abbreviation) - { - _abbreviations.Add(this, abbreviation); - } + internal void RemoveAbbreviation(DwarfAbbreviation abbreviation) + { + _abbreviations.Remove(this, abbreviation); + } - internal void RemoveAbbreviation(DwarfAbbreviation abbreviation) - { - _abbreviations.Remove(this, abbreviation); - } + internal DwarfAbbreviation RemoveAbbreviationAt(int index) + { + return _abbreviations.RemoveAt(this, index); + } - internal DwarfAbbreviation RemoveAbbreviationAt(int index) + internal void Reset() + { + foreach(var abbreviation in _abbreviations) { - return _abbreviations.RemoveAt(this, index); + abbreviation.Reset(); } + _abbreviations.Clear(); + } - internal void Reset() + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + ulong endOffset = Position; + foreach (var abbreviation in _abbreviations) { - foreach(var abbreviation in _abbreviations) - { - abbreviation.Reset(); - } - _abbreviations.Clear(); + abbreviation.Position = endOffset; + abbreviation.UpdateLayout(layoutContext); + endOffset += abbreviation.Size; } - - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - ulong endOffset = Position; - foreach (var abbreviation in _abbreviations) - { - abbreviation.Position = endOffset; - abbreviation.UpdateLayoutInternal(layoutContext); - endOffset += abbreviation.Size; - } - Size = endOffset - Position; - } + Size = endOffset - Position; + } - protected override void Read(DwarfReader reader) + public override void Read(DwarfReader reader) + { + var endOffset = reader.Position; + while (reader.Position < reader.Length) { - var endOffset = reader.Position; - while (reader.Position < reader.Length) + var abbreviation = new DwarfAbbreviation { - var abbreviation = new DwarfAbbreviation - { - Position = endOffset - }; - abbreviation.ReadInternal(reader); - endOffset += abbreviation.Size; - AddAbbreviation(abbreviation); - } - - Size = endOffset - Position; + Position = endOffset + }; + abbreviation.Read(reader); + endOffset += abbreviation.Size; + AddAbbreviation(abbreviation); } - protected override void Write(DwarfWriter writer) - { - var startOffset = writer.Position; - foreach (var abbreviation in _abbreviations) - { - abbreviation.WriteInternal(writer); - } + Size = endOffset - Position; + } - Debug.Assert(writer.Position - startOffset == Size); + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; + foreach (var abbreviation in _abbreviations) + { + abbreviation.Write(writer); } + + Debug.Assert(writer.Position - startOffset == Size); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAccessibility.cs b/src/LibObjectFile/Dwarf/DwarfAccessibility.cs index 67f3271..d0ce3e7 100644 --- a/src/LibObjectFile/Dwarf/DwarfAccessibility.cs +++ b/src/LibObjectFile/Dwarf/DwarfAccessibility.cs @@ -2,14 +2,13 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public enum DwarfAccessibility : byte { - public enum DwarfAccessibility : byte - { - Public = DwarfNative.DW_ACCESS_public, + Public = DwarfNative.DW_ACCESS_public, - Private = DwarfNative.DW_ACCESS_private, + Private = DwarfNative.DW_ACCESS_private, - Protected = DwarfNative.DW_ACCESS_protected, - } + Protected = DwarfNative.DW_ACCESS_protected, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAddressRange.cs b/src/LibObjectFile/Dwarf/DwarfAddressRange.cs index 6a9e9d1..eccee75 100644 --- a/src/LibObjectFile/Dwarf/DwarfAddressRange.cs +++ b/src/LibObjectFile/Dwarf/DwarfAddressRange.cs @@ -2,26 +2,25 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public struct DwarfAddressRange { - public struct DwarfAddressRange + public DwarfAddressRange(ulong segment, ulong address, ulong length) { - public DwarfAddressRange(ulong segment, ulong address, ulong length) - { - Segment = segment; - Address = address; - Length = length; - } + Segment = segment; + Address = address; + Length = length; + } - public ulong Segment { get; set; } + public ulong Segment { get; set; } - public ulong Address { get; set; } + public ulong Address { get; set; } - public ulong Length { get; set; } + public ulong Length { get; set; } - public override string ToString() - { - return $"{nameof(Segment)}: 0x{Segment:x16}, {nameof(Address)}: 0x{Address:x16}, {nameof(Length)}: 0x{Length:x16}"; - } + public override string ToString() + { + return $"{nameof(Segment)}: 0x{Segment:x16}, {nameof(Address)}: 0x{Address:x16}, {nameof(Length)}: 0x{Length:x16}"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs index 2044bc6..b96ffff 100644 --- a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs @@ -8,270 +8,268 @@ using System.IO; using LibObjectFile.Utils; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[DebuggerDisplay("Count = {Ranges.Count,nq}")] +public class DwarfAddressRangeTable : DwarfRelocatableSection { - [DebuggerDisplay("Count = {Ranges.Count,nq}")] - public class DwarfAddressRangeTable : DwarfRelocatableSection + public DwarfAddressRangeTable() { - public DwarfAddressRangeTable() - { - Ranges = new List(); - Version = 2; - } + Ranges = new List(); + Version = 2; + } - public ushort Version { get; set; } + public ushort Version { get; set; } - public bool Is64BitEncoding { get; set; } + public bool Is64BitEncoding { get; set; } - public DwarfAddressSize AddressSize { get; set; } + public DwarfAddressSize AddressSize { get; set; } - public DwarfAddressSize SegmentSelectorSize { get; set; } + public DwarfAddressSize SegmentSelectorSize { get; set; } - public ulong DebugInfoOffset { get; private set; } + public ulong DebugInfoOffset { get; private set; } - public DwarfUnit? Unit { get; set; } + public DwarfUnit? Unit { get; set; } - public List Ranges { get; } + public List Ranges { get; } - public ulong HeaderLength => Size - DwarfHelper.SizeOfUnitLength(Is64BitEncoding); + public ulong HeaderLength => Size - DwarfHelper.SizeOfUnitLength(Is64BitEncoding); - protected override void Read(DwarfReader reader) - { - Position = reader.Position; - var unitLength = reader.ReadUnitLength(); - Is64BitEncoding = reader.Is64BitEncoding; - Version = reader.ReadU16(); + public override void Read(DwarfReader reader) + { + Position = reader.Position; + var unitLength = reader.ReadUnitLength(); + Is64BitEncoding = reader.Is64BitEncoding; + Version = reader.ReadU16(); - if (Version != 2) - { - reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_VersionNotSupported, $"Version {Version} for .debug_aranges not supported"); - return; - } + if (Version != 2) + { + reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_VersionNotSupported, $"Version {Version} for .debug_aranges not supported"); + return; + } - DebugInfoOffset = reader.ReadUIntFromEncoding(); + DebugInfoOffset = reader.ReadUIntFromEncoding(); - AddressSize = reader.ReadAddressSize(); + AddressSize = reader.ReadAddressSize(); - var segment_selector_size = (DwarfAddressSize)reader.ReadU8(); - SegmentSelectorSize = segment_selector_size; + var segment_selector_size = (DwarfAddressSize)reader.ReadU8(); + SegmentSelectorSize = segment_selector_size; - var align = (ulong)segment_selector_size + (ulong)AddressSize * 2; + var align = (ulong)segment_selector_size + (ulong)AddressSize * 2; - // SPECS 7.21: The first tuple following the header in each set begins at an offset that is a multiple of the size of a single tuple - reader.Position = AlignHelper.AlignToUpper(reader.Position, align); + // SPECS 7.21: The first tuple following the header in each set begins at an offset that is a multiple of the size of a single tuple + reader.Position = AlignHelper.AlignToUpper(reader.Position, align); - while (true) + while (true) + { + ulong segment = 0; + switch (segment_selector_size) { - ulong segment = 0; - switch (segment_selector_size) - { - case DwarfAddressSize.Bit8: - segment = reader.ReadU8(); - break; - - case DwarfAddressSize.Bit16: - segment = reader.ReadU16(); - break; + case DwarfAddressSize.Bit8: + segment = reader.ReadU8(); + break; - case DwarfAddressSize.Bit32: - segment = reader.ReadU32(); - break; + case DwarfAddressSize.Bit16: + segment = reader.ReadU16(); + break; - case DwarfAddressSize.Bit64: - segment = reader.ReadU64(); - break; + case DwarfAddressSize.Bit32: + segment = reader.ReadU32(); + break; - case DwarfAddressSize.None: - break; - } + case DwarfAddressSize.Bit64: + segment = reader.ReadU64(); + break; - ulong address = 0; - ulong length = 0; - switch (AddressSize) - { - case DwarfAddressSize.Bit8: - address = reader.ReadU8(); - length = reader.ReadU8(); - break; - case DwarfAddressSize.Bit16: - address = reader.ReadU16(); - length = reader.ReadU16(); - break; - case DwarfAddressSize.Bit32: - address = reader.ReadU32(); - length = reader.ReadU32(); - break; - case DwarfAddressSize.Bit64: - address = reader.ReadU64(); - length = reader.ReadU64(); - break; - } + case DwarfAddressSize.None: + break; + } - if (segment == 0 && address == 0 && length == 0) - { + ulong address = 0; + ulong length = 0; + switch (AddressSize) + { + case DwarfAddressSize.Bit8: + address = reader.ReadU8(); + length = reader.ReadU8(); break; - } + case DwarfAddressSize.Bit16: + address = reader.ReadU16(); + length = reader.ReadU16(); + break; + case DwarfAddressSize.Bit32: + address = reader.ReadU32(); + length = reader.ReadU32(); + break; + case DwarfAddressSize.Bit64: + address = reader.ReadU64(); + length = reader.ReadU64(); + break; + } - Ranges.Add(new DwarfAddressRange(segment, address, length)); + if (segment == 0 && address == 0 && length == 0) + { + break; } - Size = reader.Position - Position; + Ranges.Add(new DwarfAddressRange(segment, address, length)); } - public override void Verify(DiagnosticBag diagnostics) - { - base.Verify(diagnostics); + Size = reader.Position - Position; + } - if (Version != 2) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_VersionNotSupported, $"Non supported version {Version} for .debug_aranges"); - } + public override void Verify(DwarfVerifyContext context) + { + var diagnostics = context.Diagnostics; + if (Version != 2) + { + diagnostics.Error(DiagnosticId.DWARF_ERR_VersionNotSupported, $"Non supported version {Version} for .debug_aranges"); + } - if (Unit == null) + if (Unit == null) + { + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidNullUnitForAddressRangeTable, $"Invalid {nameof(Unit)} for .debug_aranges that cannot be null"); + } + else + { + var parentFile = Unit.GetParentFile(); + if (this.Parent != parentFile) { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidNullUnitForAddressRangeTable, $"Invalid {nameof(Unit)} for .debug_aranges that cannot be null"); - } - else - { - var parentFile = Unit.GetParentFile(); - if (this.Parent != parentFile) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidParentUnitForAddressRangeTable, $"Invalid parent {nameof(DwarfFile)} of {nameof(Unit)} for .debug_aranges that doesn't match the parent of instance"); - } + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidParentUnitForAddressRangeTable, $"Invalid parent {nameof(DwarfFile)} of {nameof(Unit)} for .debug_aranges that doesn't match the parent of instance"); } } + } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - ulong sizeOf = 0; - // unit_length - sizeOf += DwarfHelper.SizeOfUnitLength(Is64BitEncoding); + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + ulong sizeOf = 0; + // unit_length + sizeOf += DwarfHelper.SizeOfUnitLength(Is64BitEncoding); - // version - sizeOf += 2; + // version + sizeOf += 2; - // debug_info_offset - sizeOf += DwarfHelper.SizeOfUInt(Is64BitEncoding); + // debug_info_offset + sizeOf += DwarfHelper.SizeOfUInt(Is64BitEncoding); - // Address size - sizeOf += 1; + // Address size + sizeOf += 1; - // segment selector size - sizeOf += 1; + // segment selector size + sizeOf += 1; - var align = (ulong)SegmentSelectorSize + (ulong)AddressSize * 2; + var align = (ulong)SegmentSelectorSize + (ulong)AddressSize * 2; - // SPECS 7.21: The first tuple following the header in each set begins at an offset that is a multiple of the size of a single tuple - sizeOf = AlignHelper.AlignToUpper(sizeOf, align); + // SPECS 7.21: The first tuple following the header in each set begins at an offset that is a multiple of the size of a single tuple + sizeOf = AlignHelper.AlignToUpper(sizeOf, align); - // SizeOf ranges + 1 (for last 0 entry) - sizeOf += ((ulong)Ranges.Count + 1UL) * align; + // SizeOf ranges + 1 (for last 0 entry) + sizeOf += ((ulong)Ranges.Count + 1UL) * align; - Size = sizeOf; + Size = sizeOf; - if (Unit != null) - { - DebugInfoOffset = Unit.Position; - } + if (Unit != null) + { + DebugInfoOffset = Unit.Position; } + } - protected override void Write(DwarfWriter writer) - { - var startOffset = writer.Position; + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; - // unit_length - writer.WriteUnitLength(Size - DwarfHelper.SizeOfUnitLength(Is64BitEncoding)); + // unit_length + writer.WriteUnitLength(Size - DwarfHelper.SizeOfUnitLength(Is64BitEncoding)); - // version - writer.WriteU16(Version); + // version + writer.WriteU16(Version); - // debug_info_offset - var debugInfoOffset = DebugInfoOffset; - if (writer.EnableRelocation) - { - writer.RecordRelocation(DwarfRelocationTarget.DebugInfo, writer.SizeOfUIntEncoding(), debugInfoOffset); - debugInfoOffset = 0; - } - writer.WriteUIntFromEncoding(debugInfoOffset); + // debug_info_offset + var debugInfoOffset = DebugInfoOffset; + if (writer.EnableRelocation) + { + writer.RecordRelocation(DwarfRelocationTarget.DebugInfo, writer.SizeOfUIntEncoding(), debugInfoOffset); + debugInfoOffset = 0; + } + writer.WriteUIntFromEncoding(debugInfoOffset); - // address_size - writer.AddressSize = AddressSize; - writer.WriteU8((byte)AddressSize); + // address_size + writer.AddressSize = AddressSize; + writer.WriteU8((byte)AddressSize); - writer.WriteU8((byte)SegmentSelectorSize); + writer.WriteU8((byte)SegmentSelectorSize); - var align = (ulong)SegmentSelectorSize + (ulong)AddressSize * 2; + var align = (ulong)SegmentSelectorSize + (ulong)AddressSize * 2; - // SPECS 7.21: The first tuple following the header in each set begins at an offset that is a multiple of the size of a single tuple - var nextOffset = AlignHelper.AlignToUpper(writer.Position, align); - for (ulong offset = writer.Position; offset < nextOffset; offset++) - { - writer.WriteU8(0); - } - Debug.Assert(writer.Position == nextOffset); - - foreach (var range in Ranges) - { - if (SegmentSelectorSize != 0) - { - switch (SegmentSelectorSize) - { - case DwarfAddressSize.Bit8: - writer.WriteU8((byte)range.Segment); - break; - case DwarfAddressSize.Bit16: - writer.WriteU16((ushort)range.Segment); - break; - case DwarfAddressSize.Bit32: - writer.WriteU32((uint)range.Segment); - break; - case DwarfAddressSize.Bit64: - writer.WriteU64((ulong)range.Segment); - break; - } - } - - writer.WriteAddress(DwarfRelocationTarget.Code, range.Address); - writer.WriteUInt(range.Length); - } + // SPECS 7.21: The first tuple following the header in each set begins at an offset that is a multiple of the size of a single tuple + var nextOffset = AlignHelper.AlignToUpper(writer.Position, align); + for (ulong offset = writer.Position; offset < nextOffset; offset++) + { + writer.WriteU8(0); + } + Debug.Assert(writer.Position == nextOffset); + foreach (var range in Ranges) + { if (SegmentSelectorSize != 0) { switch (SegmentSelectorSize) { case DwarfAddressSize.Bit8: - writer.WriteU8(0); + writer.WriteU8((byte)range.Segment); break; case DwarfAddressSize.Bit16: - writer.WriteU16(0); + writer.WriteU16((ushort)range.Segment); break; case DwarfAddressSize.Bit32: - writer.WriteU32(0); + writer.WriteU32((uint)range.Segment); break; case DwarfAddressSize.Bit64: - writer.WriteU64(0); + writer.WriteU64((ulong)range.Segment); break; } } - switch (AddressSize) + writer.WriteAddress(DwarfRelocationTarget.Code, range.Address); + writer.WriteUInt(range.Length); + } + + if (SegmentSelectorSize != 0) + { + switch (SegmentSelectorSize) { case DwarfAddressSize.Bit8: - writer.WriteU16(0); + writer.WriteU8(0); break; case DwarfAddressSize.Bit16: - writer.WriteU32(0); + writer.WriteU16(0); break; case DwarfAddressSize.Bit32: - writer.WriteU64(0); + writer.WriteU32(0); break; case DwarfAddressSize.Bit64: - writer.WriteU64(0); writer.WriteU64(0); break; } + } - Debug.Assert(writer.Position - startOffset == Size); + switch (AddressSize) + { + case DwarfAddressSize.Bit8: + writer.WriteU16(0); + break; + case DwarfAddressSize.Bit16: + writer.WriteU32(0); + break; + case DwarfAddressSize.Bit32: + writer.WriteU64(0); + break; + case DwarfAddressSize.Bit64: + writer.WriteU64(0); + writer.WriteU64(0); + break; } + + Debug.Assert(writer.Position - startOffset == Size); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAddressSize.cs b/src/LibObjectFile/Dwarf/DwarfAddressSize.cs index c2f072e..3bb0404 100644 --- a/src/LibObjectFile/Dwarf/DwarfAddressSize.cs +++ b/src/LibObjectFile/Dwarf/DwarfAddressSize.cs @@ -2,18 +2,17 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public enum DwarfAddressSize : byte { - public enum DwarfAddressSize : byte - { - None = 0, + None = 0, - Bit8 = 1, + Bit8 = 1, - Bit16 = 2, + Bit16 = 2, - Bit32 = 4, + Bit32 = 4, - Bit64 = 8, - } + Bit64 = 8, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfArrayOrderingKind.cs b/src/LibObjectFile/Dwarf/DwarfArrayOrderingKind.cs index 8464d0c..b7e8f31 100644 --- a/src/LibObjectFile/Dwarf/DwarfArrayOrderingKind.cs +++ b/src/LibObjectFile/Dwarf/DwarfArrayOrderingKind.cs @@ -2,12 +2,11 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public enum DwarfArrayOrderingKind : byte { - public enum DwarfArrayOrderingKind : byte - { - RowMajor = DwarfNative.DW_ORD_row_major, + RowMajor = DwarfNative.DW_ORD_row_major, - ColumnMajor = DwarfNative.DW_ORD_col_major, - } + ColumnMajor = DwarfNative.DW_ORD_col_major, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAttribute.cs b/src/LibObjectFile/Dwarf/DwarfAttribute.cs index 1628d8e..8ef8f61 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttribute.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttribute.cs @@ -5,998 +5,1000 @@ using System; using System.Diagnostics; using System.IO; +using System.Text; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public sealed class DwarfAttribute : DwarfObject, IComparable { - public sealed class DwarfAttribute : DwarfObject, IComparable - { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private ulong _valueAsU64; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private ulong _valueAsU64; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private object? _valueAsObject; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private object? _valueAsObject; - public DwarfAttributeKindEx Kind { get; set; } + public DwarfAttributeKindEx Kind { get; set; } - public bool ValueAsBoolean - { - get => _valueAsU64 != 0; - set => _valueAsU64 = value ? 1U : 0; - } + public bool ValueAsBoolean + { + get => _valueAsU64 != 0; + set => _valueAsU64 = value ? 1U : 0; + } - public int ValueAsI32 - { - get => (int)_valueAsU64; - set => _valueAsU64 = (ulong)(long)value; - } + public int ValueAsI32 + { + get => (int)_valueAsU64; + set => _valueAsU64 = (ulong)(long)value; + } - public uint ValueAsU32 - { - get => (uint)_valueAsU64; - set => _valueAsU64 = value; - } + public uint ValueAsU32 + { + get => (uint)_valueAsU64; + set => _valueAsU64 = value; + } - public long ValueAsI64 - { - get => (long)_valueAsU64; - set => _valueAsU64 = (ulong)value; - } + public long ValueAsI64 + { + get => (long)_valueAsU64; + set => _valueAsU64 = (ulong)value; + } - public ulong ValueAsU64 - { - get => _valueAsU64; - set => _valueAsU64 = value; - } + public ulong ValueAsU64 + { + get => _valueAsU64; + set => _valueAsU64 = value; + } - /// - /// Gets or sets the encoding used for this attribute. Default is null meaning that the encoding - /// is detected automatically. Some attributes may require to explicitly set this encoding to disambiguate - /// between different encoding form (e.g boolean => instead of ) - /// - public DwarfAttributeEncoding? Encoding { get; set; } + /// + /// Gets or sets the encoding used for this attribute. Default is null meaning that the encoding + /// is detected automatically. Some attributes may require to explicitly set this encoding to disambiguate + /// between different encoding form (e.g boolean => instead of ) + /// + public DwarfAttributeEncoding? Encoding { get; set; } - public DwarfAttributeFormEx Form { get; internal set; } + public DwarfAttributeFormEx Form { get; internal set; } - public object? ValueAsObject + public object? ValueAsObject + { + get => _valueAsObject; + set { - get => _valueAsObject; - set + if (_valueAsObject is DwarfExpression oldExpression) { - if (_valueAsObject is DwarfExpression oldExpression) - { - oldExpression.Parent = null; - } - _valueAsObject = value; + oldExpression.Parent = null; + } + _valueAsObject = value; - if (value is DwarfExpression newExpression) - { - if (newExpression.Parent != null) throw new InvalidOperationException($"Cannot set the {newExpression.GetType()} as it already belongs to another {newExpression.Parent.GetType()} instance"); - newExpression.Parent = this; - } + if (value is DwarfExpression newExpression) + { + if (newExpression.Parent != null) throw new InvalidOperationException($"Cannot set the {newExpression.GetType()} as it already belongs to another {newExpression.Parent.GetType()} instance"); + newExpression.Parent = this; } } - - public override void Verify(DiagnosticBag diagnostics) - { - base.Verify(diagnostics); + } - // Check DwarfDIE reference - if (ValueAsObject is DwarfDIE attrDIE) - { - var thisSection = this.GetParentSection()!; - var attrSection = attrDIE.GetParentSection(); + public override void Verify(DwarfVerifyContext context) + { + var diagnostics = context.Diagnostics; - if (thisSection != attrSection) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidParentForDIE, $"Invalid parent for the DIE {attrDIE} referenced by the attribute {this}. It must be within the same parent {thisSection.GetType()}."); - } - } - else if (ValueAsObject is DwarfExpression expr) + // Check DwarfDIE reference + if (ValueAsObject is DwarfDIE attrDIE) + { + var thisSection = this.GetParentSection()!; + var attrSection = attrDIE.GetParentSection(); + + if (thisSection != attrSection) { - expr.Verify(diagnostics); + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidParentForDIE, $"Invalid parent for the DIE {attrDIE} referenced by the attribute {this}. It must be within the same parent {thisSection.GetType()}."); } - else if (ValueAsObject is DwarfLocationList locationList) - { - var thisSection = this.GetParentFile()!; - var locationListSection = locationList.GetParentFile(); + } + else if (ValueAsObject is DwarfExpression expr) + { + expr.Verify(context); + } + else if (ValueAsObject is DwarfLocationList locationList) + { + var thisSection = this.GetParentFile()!; + var locationListSection = locationList.GetParentFile(); - if (thisSection != locationListSection) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidParentForLocationList, $"Invalid parent for the LocationList {locationList} referenced by the attribute {this}. It must be within the same parent {thisSection.GetType()}."); - } + if (thisSection != locationListSection) + { + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidParentForLocationList, $"Invalid parent for the LocationList {locationList} referenced by the attribute {this}. It must be within the same parent {thisSection.GetType()}."); } } + } - public int CompareTo(DwarfAttribute? other) + public int CompareTo(DwarfAttribute? other) + { + if (other == null) return 1; + return ((uint)Kind).CompareTo((uint)other.Kind); + } + + protected override bool PrintMembers(StringBuilder builder) + { + if (ValueAsObject != null) { - if (other == null) return 1; - return ((uint)Kind).CompareTo((uint)other.Kind); + builder.Append(ValueAsU64 != 0 ? $"{nameof(Kind)}: {Kind}, Value: {ValueAsObject} Offset: {ValueAsU64}" : $"{nameof(Kind)}: {Kind}, Value: {ValueAsObject}"); } + else + { + builder.Append($"{nameof(Kind)}: {Kind}, Value: {ValueAsU64}"); + } + + return true; + } - public override string ToString() + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + var addressSize = layoutContext.CurrentUnit!.AddressSize; + var is64BitEncoding = layoutContext.CurrentUnit.Is64BitEncoding; + + var endOffset = Position; + switch (Form.Value) { - if (ValueAsObject != null) + case DwarfAttributeForm.Addr: + endOffset += DwarfHelper.SizeOfUInt(addressSize); // WriteUInt(ValueAsU64); + break; + case DwarfAttributeForm.Data1: + endOffset += 1; // WriteU8((byte)ValueAsU64); + break; + case DwarfAttributeForm.Data2: + endOffset += 2; // WriteU16((ushort)ValueAsU64); + break; + case DwarfAttributeForm.Data4: + endOffset += 4; // WriteU32((uint)ValueAsU64); + break; + case DwarfAttributeForm.Data8: + endOffset += 8; // WriteU64(ValueAsU64); + break; + case DwarfAttributeForm.String: + endOffset += DwarfHelper.SizeOfStringUTF8NullTerminated((string?)ValueAsObject); + break; + case DwarfAttributeForm.Block: { - return ValueAsU64 != 0 ? $"{nameof(Kind)}: {Kind}, Value: {ValueAsObject} Offset: {ValueAsU64}" : $"{nameof(Kind)}: {Kind}, Value: {ValueAsObject}"; + var stream = (Stream)ValueAsObject!; + endOffset += DwarfHelper.SizeOfULEB128((ulong)stream.Length); + endOffset += (ulong)stream.Length; + break; } - else + case DwarfAttributeForm.Block1: { - return $"{nameof(Kind)}: {Kind}, Value: {ValueAsU64}"; + var stream = (Stream)ValueAsObject!; + endOffset += 1; + endOffset += (ulong)stream.Length; + break; } - } - - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - var addressSize = layoutContext.CurrentUnit!.AddressSize; - var is64BitEncoding = layoutContext.CurrentUnit.Is64BitEncoding; - - var endOffset = Position; - switch (Form.Value) + case DwarfAttributeForm.Block2: { - case DwarfAttributeForm.Addr: - endOffset += DwarfHelper.SizeOfUInt(addressSize); // WriteUInt(ValueAsU64); - break; - case DwarfAttributeForm.Data1: - endOffset += 1; // WriteU8((byte)ValueAsU64); - break; - case DwarfAttributeForm.Data2: - endOffset += 2; // WriteU16((ushort)ValueAsU64); - break; - case DwarfAttributeForm.Data4: - endOffset += 4; // WriteU32((uint)ValueAsU64); - break; - case DwarfAttributeForm.Data8: - endOffset += 8; // WriteU64(ValueAsU64); - break; - case DwarfAttributeForm.String: - endOffset += DwarfHelper.SizeOfStringUTF8NullTerminated((string?)ValueAsObject); - break; - case DwarfAttributeForm.Block: - { - var stream = (Stream)ValueAsObject!; - endOffset += DwarfHelper.SizeOfULEB128((ulong)stream.Length); - endOffset += (ulong)stream.Length; - break; - } - case DwarfAttributeForm.Block1: - { - var stream = (Stream)ValueAsObject!; - endOffset += 1; - endOffset += (ulong)stream.Length; - break; - } - case DwarfAttributeForm.Block2: - { - var stream = (Stream)ValueAsObject!; - endOffset += 2; - endOffset += (ulong)stream.Length; - break; - } - case DwarfAttributeForm.Block4: - { - var stream = (Stream)ValueAsObject!; - endOffset += 4; - endOffset += (ulong)stream.Length; - break; - } - case DwarfAttributeForm.Flag: - endOffset += 1; // WriteU8((byte)(ValueAsU64 != 0 ? 1 : 0)); - break; - case DwarfAttributeForm.Sdata: - endOffset += DwarfHelper.SizeOfILEB128(ValueAsI64); // WriteILEB128(ValueAsI64); - break; - case DwarfAttributeForm.Strp: - endOffset += DwarfHelper.SizeOfUInt(is64BitEncoding); // WriteUIntFromEncoding(offset); - break; - case DwarfAttributeForm.Udata: - endOffset += DwarfHelper.SizeOfULEB128(ValueAsU64); // ReadULEB128(); - break; - case DwarfAttributeForm.RefAddr: - endOffset += DwarfHelper.SizeOfUInt(is64BitEncoding); // WriteUIntFromEncoding(dieRef.Offset); - break; - case DwarfAttributeForm.Ref1: - endOffset += 1; // WriteU8((byte)(dieRef.Offset - _currentUnit.Offset)); - break; - case DwarfAttributeForm.Ref2: - endOffset += 2; // WriteU16((ushort)(dieRef.Offset - _currentUnit.Offset)); - break; - case DwarfAttributeForm.Ref4: - endOffset += 4; // WriteU32((uint)(dieRef.Offset - _currentUnit.Offset)); - break; - case DwarfAttributeForm.Ref8: - endOffset += 8; // WriteU64((dieRef.Offset - _currentUnit.Offset)); - break; - case DwarfAttributeForm.RefUdata: - { - var dieRef = (DwarfDIE)ValueAsObject!; - endOffset += DwarfHelper.SizeOfULEB128(dieRef.Position - layoutContext.CurrentUnit.Position); // WriteULEB128((dieRef.Offset - _currentUnit.Offset)); - break; - } - - //case DwarfAttributeForm.indirect: - //{ - // attributeForm = ReadLEB128As(); - // goto indirect; - //} - - // addptr - // lineptr - // loclist - // loclistptr - // macptr - // rnglist - // rngrlistptr - // stroffsetsptr - case DwarfAttributeForm.SecOffset: - endOffset += DwarfHelper.SizeOfUInt(is64BitEncoding); - break; - - case DwarfAttributeForm.Exprloc: - var expr = (DwarfExpression)ValueAsObject!; - expr.Position = endOffset; - expr.UpdateLayoutInternal(layoutContext); - endOffset += expr.Size; - break; - - case DwarfAttributeForm.FlagPresent: - break; - - case DwarfAttributeForm.RefSig8: - endOffset += 8; // WriteU64(ValueAsU64); - break; - - case DwarfAttributeForm.Strx: throw new NotSupportedException("DW_FORM_strx - DWARF5"); - case DwarfAttributeForm.Addrx: throw new NotSupportedException("DW_FORM_addrx - DWARF5"); - case DwarfAttributeForm.RefSup4: throw new NotSupportedException("DW_FORM_ref_sup4 - DWARF5"); - case DwarfAttributeForm.StrpSup: throw new NotSupportedException("DW_FORM_strp_sup - DWARF5"); - case DwarfAttributeForm.Data16: throw new NotSupportedException("DW_FORM_data16 - DWARF5"); - case DwarfAttributeForm.LineStrp: throw new NotSupportedException("DW_FORM_line_strp - DWARF5"); - case DwarfAttributeForm.ImplicitConst: throw new NotSupportedException("DW_FORM_implicit_const - DWARF5"); - case DwarfAttributeForm.Loclistx: throw new NotSupportedException("DW_FORM_loclistx - DWARF5"); - case DwarfAttributeForm.Rnglistx: throw new NotSupportedException("DW_FORM_rnglistx - DWARF5"); - case DwarfAttributeForm.RefSup8: throw new NotSupportedException("DW_FORM_ref_sup8 - DWARF5"); - case DwarfAttributeForm.Strx1: throw new NotSupportedException("DW_FORM_strx1 - DWARF5"); - case DwarfAttributeForm.Strx2: throw new NotSupportedException("DW_FORM_strx2 - DWARF5"); - case DwarfAttributeForm.Strx3: throw new NotSupportedException("DW_FORM_strx3 - DWARF5"); - case DwarfAttributeForm.Strx4: throw new NotSupportedException("DW_FORM_strx4 - DWARF5"); - case DwarfAttributeForm.Addrx1: throw new NotSupportedException("DW_FORM_addrx1 - DWARF5"); - case DwarfAttributeForm.Addrx2: throw new NotSupportedException("DW_FORM_addrx2 - DWARF5"); - case DwarfAttributeForm.Addrx3: throw new NotSupportedException("DW_FORM_addrx3 - DWARF5"); - case DwarfAttributeForm.Addrx4: throw new NotSupportedException("DW_FORM_addrx4 - DWARF5"); - case DwarfAttributeForm.GNUAddrIndex: throw new NotSupportedException("DW_FORM_GNU_addr_index - GNU extension in debug_info.dwo."); - case DwarfAttributeForm.GNUStrIndex: throw new NotSupportedException("DW_FORM_GNU_str_index - GNU extension, somewhat like DW_FORM_strp"); - case DwarfAttributeForm.GNURefAlt: throw new NotSupportedException("DW_FORM_GNU_ref_alt - GNU extension. Offset in .debug_info."); - case DwarfAttributeForm.GNUStrpAlt: throw new NotSupportedException("DW_FORM_GNU_strp_alt - GNU extension. Offset in .debug_str of another object file."); - default: - throw new NotSupportedException($"Unknown {nameof(DwarfAttributeForm)}: {Form}"); + var stream = (Stream)ValueAsObject!; + endOffset += 2; + endOffset += (ulong)stream.Length; + break; + } + case DwarfAttributeForm.Block4: + { + var stream = (Stream)ValueAsObject!; + endOffset += 4; + endOffset += (ulong)stream.Length; + break; + } + case DwarfAttributeForm.Flag: + endOffset += 1; // WriteU8((byte)(ValueAsU64 != 0 ? 1 : 0)); + break; + case DwarfAttributeForm.Sdata: + endOffset += DwarfHelper.SizeOfILEB128(ValueAsI64); // WriteILEB128(ValueAsI64); + break; + case DwarfAttributeForm.Strp: + endOffset += DwarfHelper.SizeOfUInt(is64BitEncoding); // WriteUIntFromEncoding(offset); + break; + case DwarfAttributeForm.Udata: + endOffset += DwarfHelper.SizeOfULEB128(ValueAsU64); // ReadULEB128(); + break; + case DwarfAttributeForm.RefAddr: + endOffset += DwarfHelper.SizeOfUInt(is64BitEncoding); // WriteUIntFromEncoding(dieRef.Offset); + break; + case DwarfAttributeForm.Ref1: + endOffset += 1; // WriteU8((byte)(dieRef.Offset - _currentUnit.Offset)); + break; + case DwarfAttributeForm.Ref2: + endOffset += 2; // WriteU16((ushort)(dieRef.Offset - _currentUnit.Offset)); + break; + case DwarfAttributeForm.Ref4: + endOffset += 4; // WriteU32((uint)(dieRef.Offset - _currentUnit.Offset)); + break; + case DwarfAttributeForm.Ref8: + endOffset += 8; // WriteU64((dieRef.Offset - _currentUnit.Offset)); + break; + case DwarfAttributeForm.RefUdata: + { + var dieRef = (DwarfDIE)ValueAsObject!; + endOffset += DwarfHelper.SizeOfULEB128(dieRef.Position - layoutContext.CurrentUnit.Position); // WriteULEB128((dieRef.Offset - _currentUnit.Offset)); + break; } - Size = endOffset - Position; + //case DwarfAttributeForm.indirect: + //{ + // attributeForm = ReadLEB128As(); + // goto indirect; + //} + + // addptr + // lineptr + // loclist + // loclistptr + // macptr + // rnglist + // rngrlistptr + // stroffsetsptr + case DwarfAttributeForm.SecOffset: + endOffset += DwarfHelper.SizeOfUInt(is64BitEncoding); + break; + + case DwarfAttributeForm.Exprloc: + var expr = (DwarfExpression)ValueAsObject!; + expr.Position = endOffset; + expr.UpdateLayout(layoutContext); + endOffset += expr.Size; + break; + + case DwarfAttributeForm.FlagPresent: + break; + + case DwarfAttributeForm.RefSig8: + endOffset += 8; // WriteU64(ValueAsU64); + break; + + case DwarfAttributeForm.Strx: throw new NotSupportedException("DW_FORM_strx - DWARF5"); + case DwarfAttributeForm.Addrx: throw new NotSupportedException("DW_FORM_addrx - DWARF5"); + case DwarfAttributeForm.RefSup4: throw new NotSupportedException("DW_FORM_ref_sup4 - DWARF5"); + case DwarfAttributeForm.StrpSup: throw new NotSupportedException("DW_FORM_strp_sup - DWARF5"); + case DwarfAttributeForm.Data16: throw new NotSupportedException("DW_FORM_data16 - DWARF5"); + case DwarfAttributeForm.LineStrp: throw new NotSupportedException("DW_FORM_line_strp - DWARF5"); + case DwarfAttributeForm.ImplicitConst: throw new NotSupportedException("DW_FORM_implicit_const - DWARF5"); + case DwarfAttributeForm.Loclistx: throw new NotSupportedException("DW_FORM_loclistx - DWARF5"); + case DwarfAttributeForm.Rnglistx: throw new NotSupportedException("DW_FORM_rnglistx - DWARF5"); + case DwarfAttributeForm.RefSup8: throw new NotSupportedException("DW_FORM_ref_sup8 - DWARF5"); + case DwarfAttributeForm.Strx1: throw new NotSupportedException("DW_FORM_strx1 - DWARF5"); + case DwarfAttributeForm.Strx2: throw new NotSupportedException("DW_FORM_strx2 - DWARF5"); + case DwarfAttributeForm.Strx3: throw new NotSupportedException("DW_FORM_strx3 - DWARF5"); + case DwarfAttributeForm.Strx4: throw new NotSupportedException("DW_FORM_strx4 - DWARF5"); + case DwarfAttributeForm.Addrx1: throw new NotSupportedException("DW_FORM_addrx1 - DWARF5"); + case DwarfAttributeForm.Addrx2: throw new NotSupportedException("DW_FORM_addrx2 - DWARF5"); + case DwarfAttributeForm.Addrx3: throw new NotSupportedException("DW_FORM_addrx3 - DWARF5"); + case DwarfAttributeForm.Addrx4: throw new NotSupportedException("DW_FORM_addrx4 - DWARF5"); + case DwarfAttributeForm.GNUAddrIndex: throw new NotSupportedException("DW_FORM_GNU_addr_index - GNU extension in debug_info.dwo."); + case DwarfAttributeForm.GNUStrIndex: throw new NotSupportedException("DW_FORM_GNU_str_index - GNU extension, somewhat like DW_FORM_strp"); + case DwarfAttributeForm.GNURefAlt: throw new NotSupportedException("DW_FORM_GNU_ref_alt - GNU extension. Offset in .debug_info."); + case DwarfAttributeForm.GNUStrpAlt: throw new NotSupportedException("DW_FORM_GNU_strp_alt - GNU extension. Offset in .debug_str of another object file."); + default: + throw new NotSupportedException($"Unknown {nameof(DwarfAttributeForm)}: {Form}"); } - protected override void Read(DwarfReader reader) - { - var descriptor = reader.CurrentAttributeDescriptor; + Size = endOffset - Position; + } + + public override void Read(DwarfReader reader) + { + var descriptor = reader.CurrentAttributeDescriptor; - Kind = descriptor.Kind; - Form = descriptor.Form; + Kind = descriptor.Kind; + Form = descriptor.Form; - ReadAttributeFormRawValue(reader); + ReadAttributeFormRawValue(reader); - Size = reader.Position - Position; + Size = reader.Position - Position; - ResolveAttributeValue(reader); - } + ResolveAttributeValue(reader); + } - private void ResolveAttributeValue(DwarfReader reader) + private void ResolveAttributeValue(DwarfReader reader) + { + switch (Kind.Value) { - switch (Kind.Value) + case DwarfAttributeKind.DeclFile: { - case DwarfAttributeKind.DeclFile: + var currentLineProgramTable = reader.CurrentLineProgramTable; + if (currentLineProgramTable == null) { - var currentLineProgramTable = reader.CurrentLineProgramTable; - if (currentLineProgramTable == null) - { - // Log and error - } - else - { - var file = currentLineProgramTable.FileNames[ValueAsI32 - 1]; - ValueAsU64 = 0; - ValueAsObject = file; - } - break; + // Log and error } - - case DwarfAttributeKind.StmtList: + else { - if (ValueAsU64 == 0) return; + var file = currentLineProgramTable.FileNames[ValueAsI32 - 1]; + ValueAsU64 = 0; + ValueAsObject = file; + } + break; + } - if (reader.File.LineSection != null) + case DwarfAttributeKind.StmtList: + { + if (ValueAsU64 == 0) return; + + if (reader.File.LineSection != null) + { + if (reader.OffsetToLineProgramTable.TryGetValue(ValueAsU64, out var lineProgramTable)) { - if (reader.OffsetToLineProgramTable.TryGetValue(ValueAsU64, out var lineProgramTable)) - { - ValueAsU64 = 0; - ValueAsObject = lineProgramTable; - reader.PushLineProgramTable(lineProgramTable); - } - else - { - // Log and error - } + ValueAsU64 = 0; + ValueAsObject = lineProgramTable; + reader.PushLineProgramTable(lineProgramTable); } else { - - // Log an error + // Log and error } + } + else + { - break; - + // Log an error } - case DwarfAttributeKind.Location: + break; + + } + + case DwarfAttributeKind.Location: + { + if (Form == DwarfAttributeFormEx.SecOffset) { - if (Form == DwarfAttributeFormEx.SecOffset) + if (reader.OffsetToLocationList.TryGetValue(ValueAsU64, out var locationList)) + { + ValueAsU64 = 0; + ValueAsObject = locationList; + } + else { - if (reader.OffsetToLocationList.TryGetValue(ValueAsU64, out var locationList)) - { - ValueAsU64 = 0; - ValueAsObject = locationList; - } - else - { - // Log and error - } + // Log and error } - break; } + break; } } + } - private void ReadAttributeFormRawValue(DwarfReader reader) + private void ReadAttributeFormRawValue(DwarfReader reader) + { + var attributeForm = Form; + + indirect: + switch (attributeForm.Value) { - var attributeForm = Form; + case DwarfAttributeForm.Addr: + { + ValueAsU64 = reader.ReadUInt(); + break; + } - indirect: - switch (attributeForm.Value) + case DwarfAttributeForm.Data1: { - case DwarfAttributeForm.Addr: - { - ValueAsU64 = reader.ReadUInt(); - break; - } + ValueAsU64 = reader.ReadU8(); + break; + } + case DwarfAttributeForm.Data2: + { + ValueAsU64 = reader.ReadU16(); + break; + } + case DwarfAttributeForm.Data4: + { + ValueAsU64 = reader.ReadU32(); + break; + } + case DwarfAttributeForm.Data8: + { + ValueAsU64 = reader.ReadU64(); + break; + } - case DwarfAttributeForm.Data1: - { - ValueAsU64 = reader.ReadU8(); - break; - } - case DwarfAttributeForm.Data2: - { - ValueAsU64 = reader.ReadU16(); - break; - } - case DwarfAttributeForm.Data4: - { - ValueAsU64 = reader.ReadU32(); - break; - } - case DwarfAttributeForm.Data8: - { - ValueAsU64 = reader.ReadU64(); - break; - } + case DwarfAttributeForm.String: + { + ValueAsObject = reader.ReadStringUTF8NullTerminated(); + break; + } - case DwarfAttributeForm.String: - { - ValueAsObject = reader.ReadStringUTF8NullTerminated(); - break; - } + case DwarfAttributeForm.Block: + { + var length = reader.ReadULEB128(); + ValueAsObject = reader.ReadAsStream(length); + break; + } + case DwarfAttributeForm.Block1: + { + var length = reader.ReadU8(); + ValueAsObject = reader.ReadAsStream(length); + break; + } + case DwarfAttributeForm.Block2: + { + var length = reader.ReadU16(); + ValueAsObject = reader.ReadAsStream(length); + break; + } + case DwarfAttributeForm.Block4: + { + var length = reader.ReadU32(); + ValueAsObject = reader.ReadAsStream(length); + break; + } - case DwarfAttributeForm.Block: - { - var length = reader.ReadULEB128(); - ValueAsObject = reader.ReadAsStream(length); - break; - } - case DwarfAttributeForm.Block1: - { - var length = reader.ReadU8(); - ValueAsObject = reader.ReadAsStream(length); - break; - } - case DwarfAttributeForm.Block2: - { - var length = reader.ReadU16(); - ValueAsObject = reader.ReadAsStream(length); - break; - } - case DwarfAttributeForm.Block4: - { - var length = reader.ReadU32(); - ValueAsObject = reader.ReadAsStream(length); - break; - } + case DwarfAttributeForm.Flag: + { + ValueAsBoolean = reader.ReadU8() != 0; + break; + } + case DwarfAttributeForm.Sdata: + { + ValueAsI64 = reader.ReadILEB128(); + break; + } + case DwarfAttributeForm.Strp: + { + var offset = reader.ReadUIntFromEncoding(); + ValueAsObject = reader.File.StringTable.GetStringFromOffset(offset); + break; + } + case DwarfAttributeForm.Udata: + { + ValueAsU64 = reader.ReadULEB128(); + break; + } + case DwarfAttributeForm.RefAddr: + { + ValueAsU64 = reader.ReadUIntFromEncoding(); + reader.ResolveAttributeReferenceWithinSection(AttributeToDIERef(this), false); + break; + } + case DwarfAttributeForm.Ref1: + { + ValueAsU64 = reader.ReadU8(); + reader.ResolveAttributeReferenceWithinCompilationUnit(AttributeToDIERef(this), false); + break; + } + case DwarfAttributeForm.Ref2: + { + ValueAsU64 = reader.ReadU16(); + reader.ResolveAttributeReferenceWithinCompilationUnit(AttributeToDIERef(this), false); + break; + } + case DwarfAttributeForm.Ref4: + { + ValueAsU64 = reader.ReadU32(); + reader.ResolveAttributeReferenceWithinCompilationUnit(AttributeToDIERef(this), false); + break; + } + case DwarfAttributeForm.Ref8: + { + ValueAsU64 = reader.ReadU64(); + reader.ResolveAttributeReferenceWithinCompilationUnit(AttributeToDIERef(this), false); + break; + } + case DwarfAttributeForm.RefUdata: + { + ValueAsU64 = reader.ReadULEB128(); + reader.ResolveAttributeReferenceWithinCompilationUnit(AttributeToDIERef(this), false); + break; + } + case DwarfAttributeForm.Indirect: + { + attributeForm = new DwarfAttributeFormEx(reader.ReadULEB128AsU32()); + goto indirect; + } - case DwarfAttributeForm.Flag: - { - ValueAsBoolean = reader.ReadU8() != 0; - break; - } - case DwarfAttributeForm.Sdata: - { - ValueAsI64 = reader.ReadILEB128(); - break; - } - case DwarfAttributeForm.Strp: - { - var offset = reader.ReadUIntFromEncoding(); - ValueAsObject = reader.File.StringTable.GetStringFromOffset(offset); - break; - } - case DwarfAttributeForm.Udata: - { - ValueAsU64 = reader.ReadULEB128(); - break; - } - case DwarfAttributeForm.RefAddr: - { - ValueAsU64 = reader.ReadUIntFromEncoding(); - reader.ResolveAttributeReferenceWithinSection(AttributeToDIERef(this), false); - break; - } - case DwarfAttributeForm.Ref1: - { - ValueAsU64 = reader.ReadU8(); - reader.ResolveAttributeReferenceWithinCompilationUnit(AttributeToDIERef(this), false); - break; - } - case DwarfAttributeForm.Ref2: - { - ValueAsU64 = reader.ReadU16(); - reader.ResolveAttributeReferenceWithinCompilationUnit(AttributeToDIERef(this), false); - break; - } - case DwarfAttributeForm.Ref4: - { - ValueAsU64 = reader.ReadU32(); - reader.ResolveAttributeReferenceWithinCompilationUnit(AttributeToDIERef(this), false); - break; - } - case DwarfAttributeForm.Ref8: - { - ValueAsU64 = reader.ReadU64(); - reader.ResolveAttributeReferenceWithinCompilationUnit(AttributeToDIERef(this), false); - break; - } - case DwarfAttributeForm.RefUdata: - { - ValueAsU64 = reader.ReadULEB128(); - reader.ResolveAttributeReferenceWithinCompilationUnit(AttributeToDIERef(this), false); - break; - } - case DwarfAttributeForm.Indirect: - { - attributeForm = new DwarfAttributeFormEx(reader.ReadULEB128AsU32()); - goto indirect; - } + // addptr + // lineptr + // loclist + // loclistptr + // macptr + // rnglist + // rngrlistptr + // stroffsetsptr + case DwarfAttributeForm.SecOffset: + { + ValueAsU64 = reader.ReadUIntFromEncoding(); + //Console.WriteLine($"attribute {Key} offset: {ValueAsU64}"); + break; + } - // addptr - // lineptr - // loclist - // loclistptr - // macptr - // rnglist - // rngrlistptr - // stroffsetsptr - case DwarfAttributeForm.SecOffset: - { - ValueAsU64 = reader.ReadUIntFromEncoding(); - //Console.WriteLine($"attribute {Key} offset: {ValueAsU64}"); - break; - } + case DwarfAttributeForm.Exprloc: + { + var expression = new DwarfExpression(); + expression.ReadInternal(reader); + ValueAsObject = expression; + break; + } - case DwarfAttributeForm.Exprloc: - { - var expression = new DwarfExpression(); - expression.ReadInternal(reader); - ValueAsObject = expression; - break; - } + case DwarfAttributeForm.FlagPresent: + { + ValueAsBoolean = true; + break; + } - case DwarfAttributeForm.FlagPresent: - { - ValueAsBoolean = true; - break; - } + case DwarfAttributeForm.RefSig8: + { + var offset = reader.ReadU64(); + ValueAsU64 = offset; + break; + } - case DwarfAttributeForm.RefSig8: - { - var offset = reader.ReadU64(); - ValueAsU64 = offset; + case DwarfAttributeForm.Strx: throw new NotSupportedException("DW_FORM_strx - DWARF5"); + case DwarfAttributeForm.Addrx: throw new NotSupportedException("DW_FORM_addrx - DWARF5"); + case DwarfAttributeForm.RefSup4: throw new NotSupportedException("DW_FORM_ref_sup4 - DWARF5"); + case DwarfAttributeForm.StrpSup: throw new NotSupportedException("DW_FORM_strp_sup - DWARF5"); + case DwarfAttributeForm.Data16: throw new NotSupportedException("DW_FORM_data16 - DWARF5"); + case DwarfAttributeForm.LineStrp: throw new NotSupportedException("DW_FORM_line_strp - DWARF5"); + case DwarfAttributeForm.ImplicitConst: throw new NotSupportedException("DW_FORM_implicit_const - DWARF5"); + case DwarfAttributeForm.Loclistx: throw new NotSupportedException("DW_FORM_loclistx - DWARF5"); + case DwarfAttributeForm.Rnglistx: throw new NotSupportedException("DW_FORM_rnglistx - DWARF5"); + case DwarfAttributeForm.RefSup8: throw new NotSupportedException("DW_FORM_ref_sup8 - DWARF5"); + case DwarfAttributeForm.Strx1: throw new NotSupportedException("DW_FORM_strx1 - DWARF5"); + case DwarfAttributeForm.Strx2: throw new NotSupportedException("DW_FORM_strx2 - DWARF5"); + case DwarfAttributeForm.Strx3: throw new NotSupportedException("DW_FORM_strx3 - DWARF5"); + case DwarfAttributeForm.Strx4: throw new NotSupportedException("DW_FORM_strx4 - DWARF5"); + case DwarfAttributeForm.Addrx1: throw new NotSupportedException("DW_FORM_addrx1 - DWARF5"); + case DwarfAttributeForm.Addrx2: throw new NotSupportedException("DW_FORM_addrx2 - DWARF5"); + case DwarfAttributeForm.Addrx3: throw new NotSupportedException("DW_FORM_addrx3 - DWARF5"); + case DwarfAttributeForm.Addrx4: throw new NotSupportedException("DW_FORM_addrx4 - DWARF5"); + case DwarfAttributeForm.GNUAddrIndex: throw new NotSupportedException("DW_FORM_GNU_addr_index - GNU extension in debug_info.dwo."); + case DwarfAttributeForm.GNUStrIndex: throw new NotSupportedException("DW_FORM_GNU_str_index - GNU extension, somewhat like DW_FORM_strp"); + case DwarfAttributeForm.GNURefAlt: throw new NotSupportedException("DW_FORM_GNU_ref_alt - GNU extension. Offset in .debug_info."); + case DwarfAttributeForm.GNUStrpAlt: throw new NotSupportedException("DW_FORM_GNU_strp_alt - GNU extension. Offset in .debug_str of another object file."); + default: + throw new NotSupportedException($"Unknown {nameof(DwarfAttributeForm)}: {attributeForm.Value}"); + } + } + + internal void UpdateAttributeForm(DwarfLayoutContext context) + { + Form = ComputeAttributeForm(context); + } + + private DwarfAttributeForm ComputeAttributeForm(DwarfLayoutContext context) + { + var key = Kind; + var encoding = DwarfHelper.GetAttributeEncoding(key); + + if (encoding == DwarfAttributeEncoding.None) + { + switch (Kind.Value) + { + case DwarfAttributeKind.GNUAllCallSites: + case DwarfAttributeKind.GNUAllTailCallSites: + encoding = DwarfAttributeEncoding.Flag; + break; + case DwarfAttributeKind.GNUCallSiteTarget: + case DwarfAttributeKind.GNUCallSiteValue: + encoding = DwarfAttributeEncoding.ExpressionLocation | DwarfAttributeEncoding.LocationList; break; - } - case DwarfAttributeForm.Strx: throw new NotSupportedException("DW_FORM_strx - DWARF5"); - case DwarfAttributeForm.Addrx: throw new NotSupportedException("DW_FORM_addrx - DWARF5"); - case DwarfAttributeForm.RefSup4: throw new NotSupportedException("DW_FORM_ref_sup4 - DWARF5"); - case DwarfAttributeForm.StrpSup: throw new NotSupportedException("DW_FORM_strp_sup - DWARF5"); - case DwarfAttributeForm.Data16: throw new NotSupportedException("DW_FORM_data16 - DWARF5"); - case DwarfAttributeForm.LineStrp: throw new NotSupportedException("DW_FORM_line_strp - DWARF5"); - case DwarfAttributeForm.ImplicitConst: throw new NotSupportedException("DW_FORM_implicit_const - DWARF5"); - case DwarfAttributeForm.Loclistx: throw new NotSupportedException("DW_FORM_loclistx - DWARF5"); - case DwarfAttributeForm.Rnglistx: throw new NotSupportedException("DW_FORM_rnglistx - DWARF5"); - case DwarfAttributeForm.RefSup8: throw new NotSupportedException("DW_FORM_ref_sup8 - DWARF5"); - case DwarfAttributeForm.Strx1: throw new NotSupportedException("DW_FORM_strx1 - DWARF5"); - case DwarfAttributeForm.Strx2: throw new NotSupportedException("DW_FORM_strx2 - DWARF5"); - case DwarfAttributeForm.Strx3: throw new NotSupportedException("DW_FORM_strx3 - DWARF5"); - case DwarfAttributeForm.Strx4: throw new NotSupportedException("DW_FORM_strx4 - DWARF5"); - case DwarfAttributeForm.Addrx1: throw new NotSupportedException("DW_FORM_addrx1 - DWARF5"); - case DwarfAttributeForm.Addrx2: throw new NotSupportedException("DW_FORM_addrx2 - DWARF5"); - case DwarfAttributeForm.Addrx3: throw new NotSupportedException("DW_FORM_addrx3 - DWARF5"); - case DwarfAttributeForm.Addrx4: throw new NotSupportedException("DW_FORM_addrx4 - DWARF5"); - case DwarfAttributeForm.GNUAddrIndex: throw new NotSupportedException("DW_FORM_GNU_addr_index - GNU extension in debug_info.dwo."); - case DwarfAttributeForm.GNUStrIndex: throw new NotSupportedException("DW_FORM_GNU_str_index - GNU extension, somewhat like DW_FORM_strp"); - case DwarfAttributeForm.GNURefAlt: throw new NotSupportedException("DW_FORM_GNU_ref_alt - GNU extension. Offset in .debug_info."); - case DwarfAttributeForm.GNUStrpAlt: throw new NotSupportedException("DW_FORM_GNU_strp_alt - GNU extension. Offset in .debug_str of another object file."); default: - throw new NotSupportedException($"Unknown {nameof(DwarfAttributeForm)}: {attributeForm.Value}"); + // TODO: Add pluggable support for requesting attribute encoding here + throw new InvalidOperationException($"Unsupported attribute {this} with unknown encoding"); } } - internal void UpdateAttributeForm(DwarfLayoutContext context) + // If the attribute has a requested encoding + if (Encoding.HasValue) { - Form = ComputeAttributeForm(context); + var requestedEncoding = Encoding.Value; + if ((encoding & requestedEncoding) == 0) + { + throw new InvalidOperationException($"Requested encoding {requestedEncoding} for {this} doesn't match supported encoding {encoding} for this attribute"); + } + + // Replace encoding with requested encoding + encoding = requestedEncoding; } - - private DwarfAttributeForm ComputeAttributeForm(DwarfLayoutContext context) - { - var key = Kind; - var encoding = DwarfHelper.GetAttributeEncoding(key); - if (encoding == DwarfAttributeEncoding.None) + // Do we still have a complex encoding? + if ((((uint)encoding) & ((uint)encoding - 1)) != 0U) + { + // if we have, try to detect which one we should select + if (ValueAsObject is DwarfDIE) { - switch (Kind.Value) + if ((encoding & DwarfAttributeEncoding.Reference) == 0) { - case DwarfAttributeKind.GNUAllCallSites: - case DwarfAttributeKind.GNUAllTailCallSites: - encoding = DwarfAttributeEncoding.Flag; - break; - case DwarfAttributeKind.GNUCallSiteTarget: - case DwarfAttributeKind.GNUCallSiteValue: - encoding = DwarfAttributeEncoding.ExpressionLocation | DwarfAttributeEncoding.LocationList; - break; - - default: - // TODO: Add pluggable support for requesting attribute encoding here - throw new InvalidOperationException($"Unsupported attribute {this} with unknown encoding"); + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The DIE value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting Reference."); } - } - // If the attribute has a requested encoding - if (Encoding.HasValue) + encoding = DwarfAttributeEncoding.Reference; + } + else if (this.ValueAsObject is Stream) { - var requestedEncoding = Encoding.Value; - if ((encoding & requestedEncoding) == 0) + if ((encoding & DwarfAttributeEncoding.Block) == 0) { - throw new InvalidOperationException($"Requested encoding {requestedEncoding} for {this} doesn't match supported encoding {encoding} for this attribute"); + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The Stream value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting Block."); } - // Replace encoding with requested encoding - encoding = requestedEncoding; + encoding = DwarfAttributeEncoding.Block; } - - // Do we still have a complex encoding? - if ((((uint)encoding) & ((uint)encoding - 1)) != 0U) + else if (this.ValueAsObject is string) { - // if we have, try to detect which one we should select - if (ValueAsObject is DwarfDIE) + if ((encoding & DwarfAttributeEncoding.String) == 0) { - if ((encoding & DwarfAttributeEncoding.Reference) == 0) - { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The DIE value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting Reference."); - } - - encoding = DwarfAttributeEncoding.Reference; + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The string value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting String."); } - else if (this.ValueAsObject is Stream) - { - if ((encoding & DwarfAttributeEncoding.Block) == 0) - { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The Stream value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting Block."); - } - encoding = DwarfAttributeEncoding.Block; - } - else if (this.ValueAsObject is string) + encoding = DwarfAttributeEncoding.String; + } + else if (this.ValueAsObject is DwarfExpression) + { + if ((encoding & DwarfAttributeEncoding.ExpressionLocation) == 0) { - if ((encoding & DwarfAttributeEncoding.String) == 0) - { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The string value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting String."); - } - - encoding = DwarfAttributeEncoding.String; + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The expression value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting ExpressionLocation."); } - else if (this.ValueAsObject is DwarfExpression) - { - if ((encoding & DwarfAttributeEncoding.ExpressionLocation) == 0) - { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The expression value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting ExpressionLocation."); - } - encoding = DwarfAttributeEncoding.ExpressionLocation; + encoding = DwarfAttributeEncoding.ExpressionLocation; + } + else if (this.ValueAsObject is DwarfLocationList) + { + if ((encoding & DwarfAttributeEncoding.LocationList) == 0) + { + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The expression value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting LocationList."); } - else if (this.ValueAsObject is DwarfLocationList) + + encoding = DwarfAttributeEncoding.LocationList; + } + else if ((encoding & DwarfAttributeEncoding.Address) != 0) + { + if (this.ValueAsObject != null) { - if ((encoding & DwarfAttributeEncoding.LocationList) == 0) - { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The expression value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting LocationList."); - } + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The {this.ValueAsObject.GetType()} value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting Address."); + } - encoding = DwarfAttributeEncoding.LocationList; + // If not specified explicitly, We consider HighPC as a constant (delta from LowPC) + if (this.Kind == DwarfAttributeKindEx.HighPC) + { + encoding = DwarfAttributeEncoding.Constant; } - else if ((encoding & DwarfAttributeEncoding.Address) != 0) + else { - if (this.ValueAsObject != null) - { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The {this.ValueAsObject.GetType()} value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting Address."); - } - - // If not specified explicitly, We consider HighPC as a constant (delta from LowPC) - if (this.Kind == DwarfAttributeKindEx.HighPC) - { - encoding = DwarfAttributeEncoding.Constant; - } - else - { - encoding = DwarfAttributeEncoding.Address; - } + encoding = DwarfAttributeEncoding.Address; } - else if ((encoding & DwarfAttributeEncoding.Constant) != 0) + } + else if ((encoding & DwarfAttributeEncoding.Constant) != 0) + { + if (this.ValueAsObject != null) { - if (this.ValueAsObject != null) - { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The {this.ValueAsObject.GetType()} value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting Constant."); - } - - encoding = DwarfAttributeEncoding.Constant; + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The {this.ValueAsObject.GetType()} value of attribute {this} from DIE {this.Parent} is not valid for supported attribute encoding {encoding}. Expecting Constant."); } + + encoding = DwarfAttributeEncoding.Constant; } + } - switch (encoding) - { - case DwarfAttributeEncoding.Address: - return DwarfAttributeForm.Addr; + switch (encoding) + { + case DwarfAttributeEncoding.Address: + return DwarfAttributeForm.Addr; - case DwarfAttributeEncoding.Block: - VerifyAttributeValueNotNull(context); + case DwarfAttributeEncoding.Block: + VerifyAttributeValueNotNull(context); - if (!(this.ValueAsObject is Stream)) - { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The value of attribute {this} from DIE {this.Parent} must be a System.IO.Stream"); - } + if (!(this.ValueAsObject is Stream)) + { + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The value of attribute {this} from DIE {this.Parent} must be a System.IO.Stream"); + } - return DwarfAttributeForm.Block; + return DwarfAttributeForm.Block; - case DwarfAttributeEncoding.Constant: + case DwarfAttributeEncoding.Constant: - if (this.ValueAsU64 <= byte.MaxValue) - { - return DwarfAttributeForm.Data1; - } + if (this.ValueAsU64 <= byte.MaxValue) + { + return DwarfAttributeForm.Data1; + } - if (this.ValueAsU64 <= ushort.MaxValue) - { - return DwarfAttributeForm.Data2; - } + if (this.ValueAsU64 <= ushort.MaxValue) + { + return DwarfAttributeForm.Data2; + } - if (this.ValueAsU64 <= uint.MaxValue) - { - return DwarfAttributeForm.Data4; - } + if (this.ValueAsU64 <= uint.MaxValue) + { + return DwarfAttributeForm.Data4; + } - return DwarfAttributeForm.Data8; + return DwarfAttributeForm.Data8; - case DwarfAttributeEncoding.ExpressionLocation: - VerifyAttributeValueNotNull(context); + case DwarfAttributeEncoding.ExpressionLocation: + VerifyAttributeValueNotNull(context); - if (!(this.ValueAsObject is DwarfExpression)) - { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The value of attribute {this} from DIE {this.Parent} must be a {nameof(DwarfExpression)}"); - } + if (!(this.ValueAsObject is DwarfExpression)) + { + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The value of attribute {this} from DIE {this.Parent} must be a {nameof(DwarfExpression)}"); + } - return DwarfAttributeForm.Exprloc; + return DwarfAttributeForm.Exprloc; - case DwarfAttributeEncoding.Flag: - return this.ValueAsBoolean ? DwarfAttributeForm.FlagPresent : DwarfAttributeForm.Flag; + case DwarfAttributeEncoding.Flag: + return this.ValueAsBoolean ? DwarfAttributeForm.FlagPresent : DwarfAttributeForm.Flag; - case DwarfAttributeEncoding.LinePointer: - bool canHaveNull = this.Kind.Value == DwarfAttributeKind.StmtList; - if (!canHaveNull) - { - VerifyAttributeValueNotNull(context); + case DwarfAttributeEncoding.LinePointer: + bool canHaveNull = this.Kind.Value == DwarfAttributeKind.StmtList; + if (!canHaveNull) + { + VerifyAttributeValueNotNull(context); - if (!(this.ValueAsObject is DwarfLine)) - { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The value of attribute {this} from DIE {this.Parent} must be a {nameof(DwarfLine)}"); - } + if (!(this.ValueAsObject is DwarfLine)) + { + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The value of attribute {this} from DIE {this.Parent} must be a {nameof(DwarfLine)}"); } + } - return DwarfAttributeForm.SecOffset; + return DwarfAttributeForm.SecOffset; - case DwarfAttributeEncoding.Reference: - VerifyAttributeValueNotNull(context); + case DwarfAttributeEncoding.Reference: + VerifyAttributeValueNotNull(context); - if (this.ValueAsObject is DwarfDIE die) - { - var dieParentUnit = die.GetParentUnit(); - // If we are not from the same unit - if (dieParentUnit != context.CurrentUnit) - { - return DwarfAttributeForm.RefAddr; - } - } - else + if (this.ValueAsObject is DwarfDIE die) + { + var dieParentUnit = die.GetParentUnit(); + // If we are not from the same unit + if (dieParentUnit != context.CurrentUnit) { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The value of attribute {this} from DIE {this.Parent} must be a {nameof(DwarfDIE)}"); + return DwarfAttributeForm.RefAddr; } + } + else + { + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The value of attribute {this} from DIE {this.Parent} must be a {nameof(DwarfDIE)}"); + } - return context.Config.DefaultAttributeFormForReference; + return context.Config.DefaultAttributeFormForReference; - case DwarfAttributeEncoding.String: - VerifyAttributeValueNotNull(context); + case DwarfAttributeEncoding.String: + VerifyAttributeValueNotNull(context); - if (this.ValueAsObject is string str) - { - // Create string offset - if (context.File.StringTable.Contains(str)) - { - return DwarfAttributeForm.Strp; - } - } - else + if (this.ValueAsObject is string str) + { + // Create string offset + if (context.File.StringTable.Contains(str)) { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The value of attribute {this} from DIE {this.Parent} must be a string."); + return DwarfAttributeForm.Strp; } + } + else + { + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The value of attribute {this} from DIE {this.Parent} must be a string."); + } - return DwarfAttributeForm.String; + return DwarfAttributeForm.String; - case DwarfAttributeEncoding.RangeList: - case DwarfAttributeEncoding.LocationList: - case DwarfAttributeEncoding.Indirect: - case DwarfAttributeEncoding.AddressPointer: - case DwarfAttributeEncoding.LocationListsPointer: - case DwarfAttributeEncoding.RangeListsPointer: - case DwarfAttributeEncoding.StringOffsetPointer: - case DwarfAttributeEncoding.LocationListPointer: - case DwarfAttributeEncoding.MacroPointer: - case DwarfAttributeEncoding.RangeListPointer: - return DwarfAttributeForm.SecOffset; - } - - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The encoding {encoding} of attribute {this} from DIE {this.Parent} is not supported."); - return DwarfAttributeForm.Data8; + case DwarfAttributeEncoding.RangeList: + case DwarfAttributeEncoding.LocationList: + case DwarfAttributeEncoding.Indirect: + case DwarfAttributeEncoding.AddressPointer: + case DwarfAttributeEncoding.LocationListsPointer: + case DwarfAttributeEncoding.RangeListsPointer: + case DwarfAttributeEncoding.StringOffsetPointer: + case DwarfAttributeEncoding.LocationListPointer: + case DwarfAttributeEncoding.MacroPointer: + case DwarfAttributeEncoding.RangeListPointer: + return DwarfAttributeForm.SecOffset; } - private void VerifyAttributeValueNotNull(DwarfLayoutContext context) + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The encoding {encoding} of attribute {this} from DIE {this.Parent} is not supported."); + return DwarfAttributeForm.Data8; + } + + private void VerifyAttributeValueNotNull(DwarfLayoutContext context) + { + if (ValueAsObject == null) { - if (ValueAsObject == null) - { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object value of attribute {this} from DIE {this.Parent} cannot be null"); - } + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object value of attribute {this} from DIE {this.Parent} cannot be null"); } + } - protected override void Write(DwarfWriter writer) - { - var startAttributeOffset = Position; - Debug.Assert(Position == startAttributeOffset); + public override void Write(DwarfWriter writer) + { + var startAttributeOffset = Position; + Debug.Assert(Position == startAttributeOffset); - switch (Form.Value) + switch (Form.Value) + { + case DwarfAttributeForm.Addr: { - case DwarfAttributeForm.Addr: - { - writer.WriteAddress(DwarfRelocationTarget.Code, ValueAsU64); - break; - } - case DwarfAttributeForm.Data1: - { - writer.WriteU8((byte) ValueAsU64); - break; - } - case DwarfAttributeForm.Data2: - { - writer.WriteU16((ushort) ValueAsU64); - break; - } - case DwarfAttributeForm.Data4: - { - writer.WriteU32((uint) ValueAsU64); - break; - } - case DwarfAttributeForm.Data8: - { - writer.WriteU64(ValueAsU64); - break; - } - case DwarfAttributeForm.String: - { - writer.WriteStringUTF8NullTerminated((string)ValueAsObject!); - break; - } - case DwarfAttributeForm.Block: - { - var stream = (Stream) ValueAsObject!; - writer.WriteULEB128((ulong) stream.Length); - writer.Write(stream); - break; - } - case DwarfAttributeForm.Block1: - { - var stream = (Stream) ValueAsObject!; - writer.WriteU8((byte) stream.Length); - writer.Write(stream); - break; - } - case DwarfAttributeForm.Block2: - { - var stream = (Stream) ValueAsObject!; - writer.WriteU16((ushort) stream.Length); - writer.Write(stream); - break; - } - case DwarfAttributeForm.Block4: - { - var stream = (Stream) ValueAsObject!; - writer.WriteU32((uint) stream.Length); - writer.Write(stream); - break; - } - case DwarfAttributeForm.Flag: - { - writer.WriteU8((byte) (ValueAsU64 != 0 ? 1 : 0)); - break; - } - case DwarfAttributeForm.Sdata: - { - writer.WriteILEB128(ValueAsI64); - break; - } - case DwarfAttributeForm.Strp: - { - var offset = writer.File.StringTable.GetOrCreateString((string?) ValueAsObject); - if (writer.EnableRelocation) - { - writer.RecordRelocation(DwarfRelocationTarget.DebugString, writer.SizeOfUIntEncoding(), offset); - offset = 0; - } - writer.WriteUIntFromEncoding(offset); - break; - } - case DwarfAttributeForm.Udata: - { - writer.WriteULEB128(ValueAsU64); - break; - } - case DwarfAttributeForm.RefAddr: - { - var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteUIntFromEncoding(dieRef.Position); - break; - } - case DwarfAttributeForm.Ref1: - { - var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteU8((byte) (dieRef.Position - writer.CurrentUnit!.Position)); - break; - } - case DwarfAttributeForm.Ref2: - { - var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteU16((ushort) (dieRef.Position - writer.CurrentUnit!.Position)); - break; - } - case DwarfAttributeForm.Ref4: - { - var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteU32((uint) (dieRef.Position - writer.CurrentUnit!.Position)); - break; - } - case DwarfAttributeForm.Ref8: + writer.WriteAddress(DwarfRelocationTarget.Code, ValueAsU64); + break; + } + case DwarfAttributeForm.Data1: + { + writer.WriteU8((byte) ValueAsU64); + break; + } + case DwarfAttributeForm.Data2: + { + writer.WriteU16((ushort) ValueAsU64); + break; + } + case DwarfAttributeForm.Data4: + { + writer.WriteU32((uint) ValueAsU64); + break; + } + case DwarfAttributeForm.Data8: + { + writer.WriteU64(ValueAsU64); + break; + } + case DwarfAttributeForm.String: + { + writer.WriteStringUTF8NullTerminated((string)ValueAsObject!); + break; + } + case DwarfAttributeForm.Block: + { + var stream = (Stream) ValueAsObject!; + writer.WriteULEB128((ulong) stream.Length); + writer.Write(stream); + break; + } + case DwarfAttributeForm.Block1: + { + var stream = (Stream) ValueAsObject!; + writer.WriteU8((byte) stream.Length); + writer.Write(stream); + break; + } + case DwarfAttributeForm.Block2: + { + var stream = (Stream) ValueAsObject!; + writer.WriteU16((ushort) stream.Length); + writer.Write(stream); + break; + } + case DwarfAttributeForm.Block4: + { + var stream = (Stream) ValueAsObject!; + writer.WriteU32((uint) stream.Length); + writer.Write(stream); + break; + } + case DwarfAttributeForm.Flag: + { + writer.WriteU8((byte) (ValueAsU64 != 0 ? 1 : 0)); + break; + } + case DwarfAttributeForm.Sdata: + { + writer.WriteILEB128(ValueAsI64); + break; + } + case DwarfAttributeForm.Strp: + { + var offset = writer.File.StringTable.GetOrCreateString((string?) ValueAsObject); + if (writer.EnableRelocation) { - var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteU64((dieRef.Position - writer.CurrentUnit!.Position)); - break; + writer.RecordRelocation(DwarfRelocationTarget.DebugString, writer.SizeOfUIntEncoding(), offset); + offset = 0; } - case DwarfAttributeForm.RefUdata: + writer.WriteUIntFromEncoding(offset); + break; + } + case DwarfAttributeForm.Udata: + { + writer.WriteULEB128(ValueAsU64); + break; + } + case DwarfAttributeForm.RefAddr: + { + var dieRef = (DwarfDIE) ValueAsObject!; + writer.WriteUIntFromEncoding(dieRef.Position); + break; + } + case DwarfAttributeForm.Ref1: + { + var dieRef = (DwarfDIE) ValueAsObject!; + writer.WriteU8((byte) (dieRef.Position - writer.CurrentUnit!.Position)); + break; + } + case DwarfAttributeForm.Ref2: + { + var dieRef = (DwarfDIE) ValueAsObject!; + writer.WriteU16((ushort) (dieRef.Position - writer.CurrentUnit!.Position)); + break; + } + case DwarfAttributeForm.Ref4: + { + var dieRef = (DwarfDIE) ValueAsObject!; + writer.WriteU32((uint) (dieRef.Position - writer.CurrentUnit!.Position)); + break; + } + case DwarfAttributeForm.Ref8: + { + var dieRef = (DwarfDIE) ValueAsObject!; + writer.WriteU64((dieRef.Position - writer.CurrentUnit!.Position)); + break; + } + case DwarfAttributeForm.RefUdata: + { + var dieRef = (DwarfDIE) ValueAsObject!; + writer.WriteULEB128((dieRef.Position - writer.CurrentUnit!.Position)); + break; + } + + //case DwarfAttributeForm.indirect: + //{ + // attributeForm = reader.ReadLEB128As(); + // goto indirect; + //} + + // addptr + // lineptr + // loclist + // loclistptr + // macptr + // rnglist + // rngrlistptr + // stroffsetsptr + case DwarfAttributeForm.SecOffset: + { + if (ValueAsObject != null) { - var dieRef = (DwarfDIE) ValueAsObject!; - writer.WriteULEB128((dieRef.Position - writer.CurrentUnit!.Position)); - break; + writer.WriteUIntFromEncoding(((DwarfObject) ValueAsObject).Position); } - - //case DwarfAttributeForm.indirect: - //{ - // attributeForm = reader.ReadLEB128As(); - // goto indirect; - //} - - // addptr - // lineptr - // loclist - // loclistptr - // macptr - // rnglist - // rngrlistptr - // stroffsetsptr - case DwarfAttributeForm.SecOffset: + else { - if (ValueAsObject != null) - { - writer.WriteUIntFromEncoding(((DwarfObject) ValueAsObject).Position); - } - else - { - writer.WriteUIntFromEncoding(ValueAsU64); - } - break; + writer.WriteUIntFromEncoding(ValueAsU64); } - - case DwarfAttributeForm.Exprloc: - ((DwarfExpression) ValueAsObject!).WriteInternal(writer); - break; - - case DwarfAttributeForm.FlagPresent: - Debug.Assert(ValueAsBoolean); - break; - - case DwarfAttributeForm.RefSig8: - writer.WriteU64(ValueAsU64); - break; - - case DwarfAttributeForm.Strx: throw new NotSupportedException("DW_FORM_strx - DWARF5"); - case DwarfAttributeForm.Addrx: throw new NotSupportedException("DW_FORM_addrx - DWARF5"); - case DwarfAttributeForm.RefSup4: throw new NotSupportedException("DW_FORM_ref_sup4 - DWARF5"); - case DwarfAttributeForm.StrpSup: throw new NotSupportedException("DW_FORM_strp_sup - DWARF5"); - case DwarfAttributeForm.Data16: throw new NotSupportedException("DW_FORM_data16 - DWARF5"); - case DwarfAttributeForm.LineStrp: throw new NotSupportedException("DW_FORM_line_strp - DWARF5"); - case DwarfAttributeForm.ImplicitConst: throw new NotSupportedException("DW_FORM_implicit_const - DWARF5"); - case DwarfAttributeForm.Loclistx: throw new NotSupportedException("DW_FORM_loclistx - DWARF5"); - case DwarfAttributeForm.Rnglistx: throw new NotSupportedException("DW_FORM_rnglistx - DWARF5"); - case DwarfAttributeForm.RefSup8: throw new NotSupportedException("DW_FORM_ref_sup8 - DWARF5"); - case DwarfAttributeForm.Strx1: throw new NotSupportedException("DW_FORM_strx1 - DWARF5"); - case DwarfAttributeForm.Strx2: throw new NotSupportedException("DW_FORM_strx2 - DWARF5"); - case DwarfAttributeForm.Strx3: throw new NotSupportedException("DW_FORM_strx3 - DWARF5"); - case DwarfAttributeForm.Strx4: throw new NotSupportedException("DW_FORM_strx4 - DWARF5"); - case DwarfAttributeForm.Addrx1: throw new NotSupportedException("DW_FORM_addrx1 - DWARF5"); - case DwarfAttributeForm.Addrx2: throw new NotSupportedException("DW_FORM_addrx2 - DWARF5"); - case DwarfAttributeForm.Addrx3: throw new NotSupportedException("DW_FORM_addrx3 - DWARF5"); - case DwarfAttributeForm.Addrx4: throw new NotSupportedException("DW_FORM_addrx4 - DWARF5"); - case DwarfAttributeForm.GNUAddrIndex: throw new NotSupportedException("DW_FORM_GNU_addr_index - GNU extension in debug_info.dwo."); - case DwarfAttributeForm.GNUStrIndex: throw new NotSupportedException("DW_FORM_GNU_str_index - GNU extension, somewhat like DW_FORM_strp"); - case DwarfAttributeForm.GNURefAlt: throw new NotSupportedException("DW_FORM_GNU_ref_alt - GNU extension. Offset in .debug_info."); - case DwarfAttributeForm.GNUStrpAlt: throw new NotSupportedException("DW_FORM_GNU_strp_alt - GNU extension. Offset in .debug_str of another object file."); - default: - throw new NotSupportedException($"Unknown {nameof(DwarfAttributeForm)}: {Form}"); + break; } - Debug.Assert(writer.Position - startAttributeOffset == Size); + case DwarfAttributeForm.Exprloc: + ((DwarfExpression) ValueAsObject!).WriteInternal(writer); + break; + + case DwarfAttributeForm.FlagPresent: + Debug.Assert(ValueAsBoolean); + break; + + case DwarfAttributeForm.RefSig8: + writer.WriteU64(ValueAsU64); + break; + + case DwarfAttributeForm.Strx: throw new NotSupportedException("DW_FORM_strx - DWARF5"); + case DwarfAttributeForm.Addrx: throw new NotSupportedException("DW_FORM_addrx - DWARF5"); + case DwarfAttributeForm.RefSup4: throw new NotSupportedException("DW_FORM_ref_sup4 - DWARF5"); + case DwarfAttributeForm.StrpSup: throw new NotSupportedException("DW_FORM_strp_sup - DWARF5"); + case DwarfAttributeForm.Data16: throw new NotSupportedException("DW_FORM_data16 - DWARF5"); + case DwarfAttributeForm.LineStrp: throw new NotSupportedException("DW_FORM_line_strp - DWARF5"); + case DwarfAttributeForm.ImplicitConst: throw new NotSupportedException("DW_FORM_implicit_const - DWARF5"); + case DwarfAttributeForm.Loclistx: throw new NotSupportedException("DW_FORM_loclistx - DWARF5"); + case DwarfAttributeForm.Rnglistx: throw new NotSupportedException("DW_FORM_rnglistx - DWARF5"); + case DwarfAttributeForm.RefSup8: throw new NotSupportedException("DW_FORM_ref_sup8 - DWARF5"); + case DwarfAttributeForm.Strx1: throw new NotSupportedException("DW_FORM_strx1 - DWARF5"); + case DwarfAttributeForm.Strx2: throw new NotSupportedException("DW_FORM_strx2 - DWARF5"); + case DwarfAttributeForm.Strx3: throw new NotSupportedException("DW_FORM_strx3 - DWARF5"); + case DwarfAttributeForm.Strx4: throw new NotSupportedException("DW_FORM_strx4 - DWARF5"); + case DwarfAttributeForm.Addrx1: throw new NotSupportedException("DW_FORM_addrx1 - DWARF5"); + case DwarfAttributeForm.Addrx2: throw new NotSupportedException("DW_FORM_addrx2 - DWARF5"); + case DwarfAttributeForm.Addrx3: throw new NotSupportedException("DW_FORM_addrx3 - DWARF5"); + case DwarfAttributeForm.Addrx4: throw new NotSupportedException("DW_FORM_addrx4 - DWARF5"); + case DwarfAttributeForm.GNUAddrIndex: throw new NotSupportedException("DW_FORM_GNU_addr_index - GNU extension in debug_info.dwo."); + case DwarfAttributeForm.GNUStrIndex: throw new NotSupportedException("DW_FORM_GNU_str_index - GNU extension, somewhat like DW_FORM_strp"); + case DwarfAttributeForm.GNURefAlt: throw new NotSupportedException("DW_FORM_GNU_ref_alt - GNU extension. Offset in .debug_info."); + case DwarfAttributeForm.GNUStrpAlt: throw new NotSupportedException("DW_FORM_GNU_strp_alt - GNU extension. Offset in .debug_str of another object file."); + default: + throw new NotSupportedException($"Unknown {nameof(DwarfAttributeForm)}: {Form}"); } - private static readonly DwarfReader.DwarfDIEReferenceResolver DwarfAttributeDIEReferenceResolverInstance = DwarfAttributeDIEReferenceResolver; + Debug.Assert(writer.Position - startAttributeOffset == Size); + } - private static DwarfReader.DwarfDIEReference AttributeToDIERef(DwarfAttribute attr) - { - return new DwarfReader.DwarfDIEReference(attr.ValueAsU64, attr, DwarfAttributeDIEReferenceResolverInstance); - } + private static readonly DwarfReader.DwarfDIEReferenceResolver DwarfAttributeDIEReferenceResolverInstance = DwarfAttributeDIEReferenceResolver; - private static void DwarfAttributeDIEReferenceResolver(ref DwarfReader.DwarfDIEReference dieRef) - { - var attr = (DwarfAttribute)dieRef.DwarfObject; - attr.ValueAsU64 = 0; - attr.ValueAsObject = dieRef.Resolved; - } + private static DwarfReader.DwarfDIEReference AttributeToDIERef(DwarfAttribute attr) + { + return new DwarfReader.DwarfDIEReference(attr.ValueAsU64, attr, DwarfAttributeDIEReferenceResolverInstance); + } + + private static void DwarfAttributeDIEReferenceResolver(ref DwarfReader.DwarfDIEReference dieRef) + { + var attr = (DwarfAttribute)dieRef.DwarfObject; + attr.ValueAsU64 = 0; + attr.ValueAsObject = dieRef.Resolved; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAttributeDescriptor.cs b/src/LibObjectFile/Dwarf/DwarfAttributeDescriptor.cs index 4a62ecf..948fb8a 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeDescriptor.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeDescriptor.cs @@ -5,51 +5,50 @@ using System; using System.Diagnostics; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[DebuggerDisplay("{Kind} {Form}")] +public readonly struct DwarfAttributeDescriptor : IEquatable { - [DebuggerDisplay("{Kind} {Form}")] - public readonly struct DwarfAttributeDescriptor : IEquatable - { - public static readonly DwarfAttributeDescriptor Empty = new DwarfAttributeDescriptor(); + public static readonly DwarfAttributeDescriptor Empty = new DwarfAttributeDescriptor(); - public DwarfAttributeDescriptor(DwarfAttributeKindEx kind, DwarfAttributeFormEx form) - { - Kind = kind; - Form = form; - } + public DwarfAttributeDescriptor(DwarfAttributeKindEx kind, DwarfAttributeFormEx form) + { + Kind = kind; + Form = form; + } - public readonly DwarfAttributeKindEx Kind; + public readonly DwarfAttributeKindEx Kind; - public readonly DwarfAttributeFormEx Form; + public readonly DwarfAttributeFormEx Form; - public bool IsNull => Kind.Value == 0 && Form.Value == 0; + public bool IsNull => Kind.Value == 0 && Form.Value == 0; - public bool Equals(DwarfAttributeDescriptor other) - { - return Kind.Equals(other.Kind) && Form.Equals(other.Form); - } + public bool Equals(DwarfAttributeDescriptor other) + { + return Kind.Equals(other.Kind) && Form.Equals(other.Form); + } - public override bool Equals(object? obj) - { - return obj is DwarfAttributeDescriptor other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is DwarfAttributeDescriptor other && Equals(other); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - return (Kind.GetHashCode() * 397) ^ Form.GetHashCode(); - } + return (Kind.GetHashCode() * 397) ^ Form.GetHashCode(); } + } - public static bool operator ==(DwarfAttributeDescriptor left, DwarfAttributeDescriptor right) - { - return left.Equals(right); - } + public static bool operator ==(DwarfAttributeDescriptor left, DwarfAttributeDescriptor right) + { + return left.Equals(right); + } - public static bool operator !=(DwarfAttributeDescriptor left, DwarfAttributeDescriptor right) - { - return !left.Equals(right); - } + public static bool operator !=(DwarfAttributeDescriptor left, DwarfAttributeDescriptor right) + { + return !left.Equals(right); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs b/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs index be927bf..366ad84 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs @@ -6,77 +6,76 @@ using System.Collections; using System.Diagnostics; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] +public readonly struct DwarfAttributeDescriptors : IEquatable { - [DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] - public readonly struct DwarfAttributeDescriptors : IEquatable - { - private readonly DwarfAttributeDescriptor[] _descriptors; + private readonly DwarfAttributeDescriptor[] _descriptors; - public DwarfAttributeDescriptors(DwarfAttributeDescriptor[] descriptors) - { - _descriptors = descriptors ?? throw new ArgumentNullException(nameof(descriptors)); - } + public DwarfAttributeDescriptors(DwarfAttributeDescriptor[] descriptors) + { + _descriptors = descriptors ?? throw new ArgumentNullException(nameof(descriptors)); + } - public int Length => _descriptors?.Length ?? 0; + public int Length => _descriptors?.Length ?? 0; - public DwarfAttributeDescriptor this[int index] + public DwarfAttributeDescriptor this[int index] + { + get { - get - { - if (_descriptors == null) throw new ArgumentException("This descriptors instance is not initialized"); - return _descriptors[index]; - } + if (_descriptors == null) throw new ArgumentException("This descriptors instance is not initialized"); + return _descriptors[index]; } + } - public bool Equals(DwarfAttributeDescriptors other) - { - if (ReferenceEquals(_descriptors, other._descriptors)) return true; - if (_descriptors == null || other._descriptors == null) return false; - if (_descriptors.Length != other._descriptors.Length) return false; + public bool Equals(DwarfAttributeDescriptors other) + { + if (ReferenceEquals(_descriptors, other._descriptors)) return true; + if (_descriptors == null || other._descriptors == null) return false; + if (_descriptors.Length != other._descriptors.Length) return false; - for (int i = 0; i < _descriptors.Length; i++) + for (int i = 0; i < _descriptors.Length; i++) + { + if (_descriptors[i] != other._descriptors[i]) { - if (_descriptors[i] != other._descriptors[i]) - { - return false; - } + return false; } - return true; } + return true; + } - public override bool Equals(object? obj) - { - return obj is DwarfAttributeDescriptors other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is DwarfAttributeDescriptors other && Equals(other); + } - public override int GetHashCode() + public override int GetHashCode() + { + int hashCode = _descriptors == null ? 0 : _descriptors.Length; + if (hashCode == 0) return hashCode; + foreach (var descriptor in _descriptors!) { - int hashCode = _descriptors == null ? 0 : _descriptors.Length; - if (hashCode == 0) return hashCode; - foreach (var descriptor in _descriptors!) - { - hashCode = (hashCode * 397) ^ descriptor.GetHashCode(); - } - return hashCode; + hashCode = (hashCode * 397) ^ descriptor.GetHashCode(); } + return hashCode; + } - private string DebuggerDisplay => ToString(); + private string DebuggerDisplay => ToString(); - public static bool operator ==(DwarfAttributeDescriptors left, DwarfAttributeDescriptors right) - { - return left.Equals(right); - } + public static bool operator ==(DwarfAttributeDescriptors left, DwarfAttributeDescriptors right) + { + return left.Equals(right); + } - public static bool operator !=(DwarfAttributeDescriptors left, DwarfAttributeDescriptors right) - { - return !left.Equals(right); - } + public static bool operator !=(DwarfAttributeDescriptors left, DwarfAttributeDescriptors right) + { + return !left.Equals(right); + } - public override string ToString() - { - return $"Count = {_descriptors.Length}"; - } + public override string ToString() + { + return $"Count = {_descriptors.Length}"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAttributeEncoding.cs b/src/LibObjectFile/Dwarf/DwarfAttributeEncoding.cs index f722ab3..e1d8f4a 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeEncoding.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeEncoding.cs @@ -4,47 +4,46 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[Flags] +public enum DwarfAttributeEncoding { - [Flags] - public enum DwarfAttributeEncoding - { - None, + None, - Address = 1, + Address = 1, - Block = 1 << 1, + Block = 1 << 1, - Constant = 1 << 2, + Constant = 1 << 2, - ExpressionLocation = 1 << 3, + ExpressionLocation = 1 << 3, - Flag = 1 << 4, + Flag = 1 << 4, - LinePointer = 1 << 5, + LinePointer = 1 << 5, - LocationListPointer = 1 << 6, + LocationListPointer = 1 << 6, - MacroPointer = 1 << 7, + MacroPointer = 1 << 7, - RangeListPointer = 1 << 8, + RangeListPointer = 1 << 8, - Reference = 1 << 9, + Reference = 1 << 9, - String = 1 << 10, + String = 1 << 10, - RangeList = 1 << 11, + RangeList = 1 << 11, - Indirect = 1 << 12, + Indirect = 1 << 12, - LocationList = 1 << 13, + LocationList = 1 << 13, - AddressPointer = 1 << 14, + AddressPointer = 1 << 14, - LocationListsPointer = 1 << 15, + LocationListsPointer = 1 << 15, - RangeListsPointer = 1 << 16, + RangeListsPointer = 1 << 16, - StringOffsetPointer = 1 << 17, - } + StringOffsetPointer = 1 << 17, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAttributeFormEx.cs b/src/LibObjectFile/Dwarf/DwarfAttributeFormEx.cs index f9877b7..2465509 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeFormEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeFormEx.cs @@ -5,60 +5,59 @@ using System; using System.Diagnostics; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +/// +/// Defines the kind of an . +/// This is the value seen in +/// +[DebuggerDisplay("{ToString(),nq}")] +public readonly partial struct DwarfAttributeFormEx : IEquatable { - /// - /// Defines the kind of an . - /// This is the value seen in - /// - [DebuggerDisplay("{ToString(),nq}")] - public readonly partial struct DwarfAttributeFormEx : IEquatable + public DwarfAttributeFormEx(uint value) { - public DwarfAttributeFormEx(uint value) - { - Value = (DwarfAttributeForm)value; - } - public DwarfAttributeFormEx(DwarfAttributeForm value) - { - Value = value; - } + Value = (DwarfAttributeForm)value; + } + public DwarfAttributeFormEx(DwarfAttributeForm value) + { + Value = value; + } - public readonly DwarfAttributeForm Value; + public readonly DwarfAttributeForm Value; - public bool Equals(DwarfAttributeFormEx other) - { - return Value == other.Value; - } + public bool Equals(DwarfAttributeFormEx other) + { + return Value == other.Value; + } - public override bool Equals(object? obj) - { - return obj is DwarfAttributeFormEx other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is DwarfAttributeFormEx other && Equals(other); + } - public override int GetHashCode() - { - return (int) Value; - } + public override int GetHashCode() + { + return (int) Value; + } - public static bool operator ==(DwarfAttributeFormEx left, DwarfAttributeFormEx right) - { - return left.Equals(right); - } + public static bool operator ==(DwarfAttributeFormEx left, DwarfAttributeFormEx right) + { + return left.Equals(right); + } - public static bool operator !=(DwarfAttributeFormEx left, DwarfAttributeFormEx right) - { - return !left.Equals(right); - } + public static bool operator !=(DwarfAttributeFormEx left, DwarfAttributeFormEx right) + { + return !left.Equals(right); + } - public override string ToString() - { - return ToStringInternal() ?? $"Unknown {nameof(DwarfAttributeFormEx)} (0x{Value:X4})"; - } + public override string ToString() + { + return ToStringInternal() ?? $"Unknown {nameof(DwarfAttributeFormEx)} (0x{Value:X4})"; + } - public static explicit operator uint(DwarfAttributeFormEx form) => (uint)form.Value; + public static explicit operator uint(DwarfAttributeFormEx form) => (uint)form.Value; - public static implicit operator DwarfAttributeFormEx(DwarfAttributeForm kind) => new DwarfAttributeFormEx(kind); + public static implicit operator DwarfAttributeFormEx(DwarfAttributeForm kind) => new DwarfAttributeFormEx(kind); - public static implicit operator DwarfAttributeForm(DwarfAttributeFormEx kind) => kind.Value; - } + public static implicit operator DwarfAttributeForm(DwarfAttributeFormEx kind) => kind.Value; } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAttributeKindEx.cs b/src/LibObjectFile/Dwarf/DwarfAttributeKindEx.cs index 85ca704..376c88f 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeKindEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeKindEx.cs @@ -5,62 +5,61 @@ using System; using System.Diagnostics; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +/// +/// Defines the kind of an . +/// This is the value seen in +/// +[DebuggerDisplay("{ToString(),nq}")] +public readonly partial struct DwarfAttributeKindEx : IEquatable { - /// - /// Defines the kind of an . - /// This is the value seen in - /// - [DebuggerDisplay("{ToString(),nq}")] - public readonly partial struct DwarfAttributeKindEx : IEquatable + public DwarfAttributeKindEx(uint value) { - public DwarfAttributeKindEx(uint value) - { - Value = (DwarfAttributeKind)value; - } + Value = (DwarfAttributeKind)value; + } - public DwarfAttributeKindEx(DwarfAttributeKind value) - { - Value = value; - } + public DwarfAttributeKindEx(DwarfAttributeKind value) + { + Value = value; + } - public readonly DwarfAttributeKind Value; + public readonly DwarfAttributeKind Value; - public bool Equals(DwarfAttributeKindEx other) - { - return Value == other.Value; - } + public bool Equals(DwarfAttributeKindEx other) + { + return Value == other.Value; + } - public override bool Equals(object? obj) - { - return obj is DwarfAttributeKindEx other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is DwarfAttributeKindEx other && Equals(other); + } - public override int GetHashCode() - { - return (int) Value; - } + public override int GetHashCode() + { + return (int) Value; + } - public static bool operator ==(DwarfAttributeKindEx left, DwarfAttributeKindEx right) - { - return left.Equals(right); - } + public static bool operator ==(DwarfAttributeKindEx left, DwarfAttributeKindEx right) + { + return left.Equals(right); + } - public static bool operator !=(DwarfAttributeKindEx left, DwarfAttributeKindEx right) - { - return !left.Equals(right); - } + public static bool operator !=(DwarfAttributeKindEx left, DwarfAttributeKindEx right) + { + return !left.Equals(right); + } - public override string ToString() - { - return ToStringInternal() ?? $"Unknown {nameof(DwarfAttributeKindEx)} (0x{Value:X4})"; - } + public override string ToString() + { + return ToStringInternal() ?? $"Unknown {nameof(DwarfAttributeKindEx)} (0x{Value:X4})"; + } - public static explicit operator uint(DwarfAttributeKindEx kind) => (uint)kind.Value; + public static explicit operator uint(DwarfAttributeKindEx kind) => (uint)kind.Value; - public static implicit operator DwarfAttributeKindEx(DwarfAttributeKind kind) => new DwarfAttributeKindEx(kind); + public static implicit operator DwarfAttributeKindEx(DwarfAttributeKind kind) => new DwarfAttributeKindEx(kind); - public static implicit operator DwarfAttributeKind(DwarfAttributeKindEx kind) => kind.Value; - } + public static implicit operator DwarfAttributeKind(DwarfAttributeKindEx kind) => kind.Value; } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAttributeValue.cs b/src/LibObjectFile/Dwarf/DwarfAttributeValue.cs index b68406f..215f79c 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeValue.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeValue.cs @@ -2,20 +2,19 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfAttributeValue { - public class DwarfAttributeValue + public DwarfAttributeValue(object value) { - public DwarfAttributeValue(object value) - { - Value = value; - } + Value = value; + } - public object Value { get; set; } + public object Value { get; set; } - public override string ToString() - { - return $"{nameof(Value)}: {Value}"; - } + public override string ToString() + { + return $"{nameof(Value)}: {Value}"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfCallingConventionEx.cs b/src/LibObjectFile/Dwarf/DwarfCallingConventionEx.cs index 17746b6..7cbe258 100644 --- a/src/LibObjectFile/Dwarf/DwarfCallingConventionEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfCallingConventionEx.cs @@ -4,56 +4,55 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public readonly partial struct DwarfCallingConventionEx : IEquatable { - public readonly partial struct DwarfCallingConventionEx : IEquatable - { - public DwarfCallingConventionEx(byte value) - { - Value = (DwarfCallingConvention)value; - } - - public DwarfCallingConventionEx(DwarfCallingConvention value) - { - Value = value; - } - - public readonly DwarfCallingConvention Value; - - public override string ToString() - { - return ToStringInternal() ?? $"Unknown {nameof(DwarfCallingConvention)} (0x{Value:x2})"; - } - - public bool Equals(DwarfCallingConventionEx other) - { - return Value == other.Value; - } - - public override bool Equals(object? obj) - { - return obj is DwarfCallingConventionEx other && Equals(other); - } - - public override int GetHashCode() - { - return Value.GetHashCode(); - } - - public static bool operator ==(DwarfCallingConventionEx left, DwarfCallingConventionEx right) - { - return left.Equals(right); - } - - public static bool operator !=(DwarfCallingConventionEx left, DwarfCallingConventionEx right) - { - return !left.Equals(right); - } - - public static explicit operator uint(DwarfCallingConventionEx callConv) => (uint)callConv.Value; + public DwarfCallingConventionEx(byte value) + { + Value = (DwarfCallingConvention)value; + } + + public DwarfCallingConventionEx(DwarfCallingConvention value) + { + Value = value; + } + + public readonly DwarfCallingConvention Value; + + public override string ToString() + { + return ToStringInternal() ?? $"Unknown {nameof(DwarfCallingConvention)} (0x{Value:x2})"; + } + + public bool Equals(DwarfCallingConventionEx other) + { + return Value == other.Value; + } - public static implicit operator DwarfCallingConventionEx(DwarfCallingConvention callConv) => new DwarfCallingConventionEx(callConv); + public override bool Equals(object? obj) + { + return obj is DwarfCallingConventionEx other && Equals(other); + } - public static implicit operator DwarfCallingConvention(DwarfCallingConventionEx callConv) => callConv.Value; + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static bool operator ==(DwarfCallingConventionEx left, DwarfCallingConventionEx right) + { + return left.Equals(right); } + + public static bool operator !=(DwarfCallingConventionEx left, DwarfCallingConventionEx right) + { + return !left.Equals(right); + } + + public static explicit operator uint(DwarfCallingConventionEx callConv) => (uint)callConv.Value; + + public static implicit operator DwarfCallingConventionEx(DwarfCallingConvention callConv) => new DwarfCallingConventionEx(callConv); + + public static implicit operator DwarfCallingConvention(DwarfCallingConventionEx callConv) => callConv.Value; } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs b/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs index 3259857..a4b70ff 100644 --- a/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfCompilationUnit.cs @@ -2,80 +2,79 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfCompilationUnit : DwarfUnit { - public class DwarfCompilationUnit : DwarfUnit + public DwarfCompilationUnit() { - public DwarfCompilationUnit() - { - Kind = DwarfUnitKind.Compile; - // Default to version 4 - Version = 4; - } + Kind = DwarfUnitKind.Compile; + // Default to version 4 + Version = 4; + } - protected override void ReadHeader(DwarfReader reader) + protected override void ReadHeader(DwarfReader reader) + { + if (Version < 5) { - if (Version < 5) - { - // 3. debug_abbrev_offset (section offset) - DebugAbbreviationOffset = reader.ReadUIntFromEncoding(); + // 3. debug_abbrev_offset (section offset) + DebugAbbreviationOffset = reader.ReadUIntFromEncoding(); - // 4. address_size (ubyte) - AddressSize = reader.ReadAddressSize(); - reader.AddressSize = AddressSize; - } - else - { - // NOTE: order of address_size/debug_abbrev_offset are different from Dwarf 4 + // 4. address_size (ubyte) + AddressSize = reader.ReadAddressSize(); + reader.AddressSize = AddressSize; + } + else + { + // NOTE: order of address_size/debug_abbrev_offset are different from Dwarf 4 - // 4. address_size (ubyte) - AddressSize = reader.ReadAddressSize(); - reader.AddressSize = AddressSize; + // 4. address_size (ubyte) + AddressSize = reader.ReadAddressSize(); + reader.AddressSize = AddressSize; - // 5. debug_abbrev_offset (section offset) - DebugAbbreviationOffset = reader.ReadUIntFromEncoding(); - } + // 5. debug_abbrev_offset (section offset) + DebugAbbreviationOffset = reader.ReadUIntFromEncoding(); } + } - protected override void WriteHeader(DwarfWriter writer) + protected override void WriteHeader(DwarfWriter writer) + { + if (Version < 5) { - if (Version < 5) + // 3. debug_abbrev_offset (section offset) + var abbrevOffset = Abbreviation!.Position; + if (writer.EnableRelocation) { - // 3. debug_abbrev_offset (section offset) - var abbrevOffset = Abbreviation!.Position; - if (writer.EnableRelocation) - { - writer.RecordRelocation(DwarfRelocationTarget.DebugAbbrev, writer.SizeOfUIntEncoding(), abbrevOffset); - abbrevOffset = 0; - } - writer.WriteUIntFromEncoding(abbrevOffset); - - // 4. address_size (ubyte) - writer.WriteAddressSize(AddressSize); + writer.RecordRelocation(DwarfRelocationTarget.DebugAbbrev, writer.SizeOfUIntEncoding(), abbrevOffset); + abbrevOffset = 0; } - else - { - // NOTE: order of address_size/debug_abbrev_offset are different from Dwarf 4 - - // 4. address_size (ubyte) - writer.WriteAddressSize(AddressSize); + writer.WriteUIntFromEncoding(abbrevOffset); - // 5. debug_abbrev_offset (section offset) - var abbrevOffset = Abbreviation!.Position; - if (writer.EnableRelocation) - { - writer.RecordRelocation(DwarfRelocationTarget.DebugAbbrev, writer.SizeOfUIntEncoding(), abbrevOffset); - abbrevOffset = 0; - } - writer.WriteUIntFromEncoding(abbrevOffset); - } + // 4. address_size (ubyte) + writer.WriteAddressSize(AddressSize); } - - protected override ulong GetLayoutHeaderSize() + else { - // 3. debug_abbrev_offset (section offset) + // NOTE: order of address_size/debug_abbrev_offset are different from Dwarf 4 + // 4. address_size (ubyte) - return DwarfHelper.SizeOfUInt(Is64BitEncoding) + 1; + writer.WriteAddressSize(AddressSize); + + // 5. debug_abbrev_offset (section offset) + var abbrevOffset = Abbreviation!.Position; + if (writer.EnableRelocation) + { + writer.RecordRelocation(DwarfRelocationTarget.DebugAbbrev, writer.SizeOfUIntEncoding(), abbrevOffset); + abbrevOffset = 0; + } + writer.WriteUIntFromEncoding(abbrevOffset); } } + + protected override ulong GetLayoutHeaderSize() + { + // 3. debug_abbrev_offset (section offset) + // 4. address_size (ubyte) + return DwarfHelper.SizeOfUInt(Is64BitEncoding) + 1; + } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfConstant.cs b/src/LibObjectFile/Dwarf/DwarfConstant.cs index b2d304b..eaed5f9 100644 --- a/src/LibObjectFile/Dwarf/DwarfConstant.cs +++ b/src/LibObjectFile/Dwarf/DwarfConstant.cs @@ -4,68 +4,67 @@ using System.IO; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public struct DwarfConstant { - public struct DwarfConstant + public DwarfConstant(int value) { - public DwarfConstant(int value) - { - AsValue = new DwarfInteger() {I64 = value}; - AsObject = null; - } + AsValue = new DwarfInteger() {I64 = value}; + AsObject = null; + } - public DwarfConstant(DwarfExpression expression) - { - AsValue = default; - AsObject = expression; - } + public DwarfConstant(DwarfExpression expression) + { + AsValue = default; + AsObject = expression; + } - public DwarfConstant(DwarfDIE dieRef) - { - AsValue = default; - AsObject = dieRef; - } + public DwarfConstant(DwarfDIE dieRef) + { + AsValue = default; + AsObject = dieRef; + } - public DwarfConstant(Stream stream) - { - AsValue = default; - AsObject = stream; - } + public DwarfConstant(Stream stream) + { + AsValue = default; + AsObject = stream; + } - public DwarfInteger AsValue; + public DwarfInteger AsValue; - public object? AsObject; + public object? AsObject; - public DwarfExpression? AsExpression => AsObject as DwarfExpression; + public DwarfExpression? AsExpression => AsObject as DwarfExpression; - public DwarfDIE? AsReference => AsObject as DwarfDIE; + public DwarfDIE? AsReference => AsObject as DwarfDIE; - public Stream? AsStream => AsObject as Stream; + public Stream? AsStream => AsObject as Stream; - public string? AsString => AsObject as string; + public string? AsString => AsObject as string; - public override string ToString() - { - if (AsExpression != null) return $"Constant Expression: {AsExpression}"; - if (AsReference != null) return $"Constant Reference: {AsReference}"; - if (AsStream != null) return $"Constant Block: Length = {AsStream.Length}"; - if (AsString != null) return $"Constant String: {AsString}"; - return $"Constant Value: {AsValue}"; - } + public override string ToString() + { + if (AsExpression != null) return $"Constant Expression: {AsExpression}"; + if (AsReference != null) return $"Constant Reference: {AsReference}"; + if (AsStream != null) return $"Constant Block: Length = {AsStream.Length}"; + if (AsString != null) return $"Constant String: {AsString}"; + return $"Constant Value: {AsValue}"; + } - public static implicit operator DwarfConstant(int value) - { - return new DwarfConstant(value); - } + public static implicit operator DwarfConstant(int value) + { + return new DwarfConstant(value); + } - public static implicit operator DwarfConstant(DwarfExpression value) - { - return new DwarfConstant(value); - } + public static implicit operator DwarfConstant(DwarfExpression value) + { + return new DwarfConstant(value); + } - public static implicit operator DwarfConstant(DwarfDIE value) - { - return new DwarfConstant(value); - } + public static implicit operator DwarfConstant(DwarfDIE value) + { + return new DwarfConstant(value); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfContainer.cs b/src/LibObjectFile/Dwarf/DwarfContainer.cs index 83940ff..994c6c4 100644 --- a/src/LibObjectFile/Dwarf/DwarfContainer.cs +++ b/src/LibObjectFile/Dwarf/DwarfContainer.cs @@ -4,13 +4,8 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public abstract class DwarfContainer : DwarfObject { - public abstract class DwarfContainer : DwarfObject - { - public override void Verify(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - } - } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfDIE.cs b/src/LibObjectFile/Dwarf/DwarfDIE.cs index d1e4e42..5d8c1ef 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIE.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIE.cs @@ -1,526 +1,525 @@ // Copyright (c) Alexandre Mutel. All rights reserved. -// This attribute is licensed under the BSD-Clause 2 license. -// See the license.txt attribute in the project root for more information. +// 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; using System.Diagnostics; +using System.Text; using LibObjectFile.Utils; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfDIE : DwarfContainer { - public class DwarfDIE : DwarfContainer + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly List _attributes; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly List _children; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private DwarfTagEx _tag; + + /// + /// The current line program table when reading. + /// + internal DwarfLineProgramTable? CurrentLineProgramTable; + + public DwarfDIE() { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly List _attributes; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly List _children; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private DwarfTagEx _tag; - - /// - /// The current line program table when reading. - /// - internal DwarfLineProgramTable? CurrentLineProgramTable; - - public DwarfDIE() - { - _attributes = new List(); - _children = new List(); - } + _attributes = new List(); + _children = new List(); + } - public virtual DwarfTagEx Tag - { - get => _tag; - set => _tag = value; - } + public virtual DwarfTagEx Tag + { + get => _tag; + set => _tag = value; + } - public ReadOnlyList Attributes => _attributes; + public ReadOnlyList Attributes => _attributes; - public ReadOnlyList Children => _children; + public ReadOnlyList Children => _children; - public DwarfAbbreviationItem? Abbrev { get; internal set; } + public DwarfAbbreviationItem? Abbrev { get; internal set; } - /// - /// Adds a child to . - /// - /// A child - public void AddChild(DwarfDIE child) - { - _children.Add(this, child); - } + /// + /// Adds a child to . + /// + /// A child + public void AddChild(DwarfDIE child) + { + _children.Add(this, child); + } - /// - /// Inserts a child into at the specified index. - /// - /// Index into to insert the specified child - /// The child to insert - public void InsertChildAt(int index, DwarfDIE child) - { - _children.InsertAt(this, index, child); - } + /// + /// Inserts a child into at the specified index. + /// + /// Index into to insert the specified child + /// The child to insert + public void InsertChildAt(int index, DwarfDIE child) + { + _children.InsertAt(this, index, child); + } - /// - /// Removes a child from - /// - /// The child to remove - public void RemoveChild(DwarfDIE child) - { - _children.Remove(this, child); - } + /// + /// Removes a child from + /// + /// The child to remove + public void RemoveChild(DwarfDIE child) + { + _children.Remove(this, child); + } - /// - /// Removes a child from at the specified index. - /// - /// Index into to remove the specified child - public DwarfDIE RemoveChildAt(int index) - { - return _children.RemoveAt(this, index); - } + /// + /// Removes a child from at the specified index. + /// + /// Index into to remove the specified child + public DwarfDIE RemoveChildAt(int index) + { + return _children.RemoveAt(this, index); + } - /// - /// Adds an attribute to . - /// - /// A attribute - public void AddAttribute(DwarfAttribute attribute) - { - _attributes.AddSorted(this, attribute, true); - } + /// + /// Adds an attribute to . + /// + /// A attribute + public void AddAttribute(DwarfAttribute attribute) + { + _attributes.AddSorted(this, attribute, true); + } + + /// + /// Removes an attribute from + /// + /// The attribute to remove + public void RemoveAttribute(DwarfAttribute attribute) + { + _attributes.Remove(this, attribute); + } - /// - /// Removes an attribute from - /// - /// The attribute to remove - public void RemoveAttribute(DwarfAttribute attribute) + /// + /// Removes an attribute from at the specified index. + /// + /// Index into to remove the specified attribute + public DwarfAttribute RemoveAttributeAt(int index) + { + return _attributes.RemoveAt(this, index); + } + + + public override void Verify(DwarfVerifyContext context) + { + foreach (var attr in _attributes) { - _attributes.Remove(this, attribute); + attr.Verify(context); } - /// - /// Removes an attribute from at the specified index. - /// - /// Index into to remove the specified attribute - public DwarfAttribute RemoveAttributeAt(int index) + foreach (var child in _children) { - return _attributes.RemoveAt(this, index); + child.Verify(context); } + } + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"{nameof(Tag)}: {Tag}, {nameof(Attributes)}: {Attributes.Count}, {nameof(Children)}: {Children.Count}"); + return true; + } - public override void Verify(DiagnosticBag diagnostics) + protected TValue? GetAttributeValue(DwarfAttributeKind kind) + { + foreach (var attr in _attributes) { - base.Verify(diagnostics); - - foreach (var attr in _attributes) + if (attr.Kind == kind) { - attr.Verify(diagnostics); + return (TValue?)attr.ValueAsObject; } + } - foreach (var child in _children) + return default; + } + + protected unsafe TValue? GetAttributeValueOpt(DwarfAttributeKind kind) where TValue : unmanaged + { + Debug.Assert(sizeof(TValue) <= sizeof(ulong)); + + foreach (var attr in _attributes) + { + if (attr.Kind == kind) { - child.Verify(diagnostics); + ulong localU64 = attr.ValueAsU64; + return *(TValue*) &localU64; } } - public override string ToString() - { - return $"{nameof(Tag)}: {Tag}, {nameof(Attributes)}: {Attributes.Count}, {nameof(Children)}: {Children.Count}"; - } + return default; + } - protected TValue? GetAttributeValue(DwarfAttributeKind kind) + protected DwarfConstant? GetAttributeConstantOpt(DwarfAttributeKind kind) + { + foreach (var attr in _attributes) { - foreach (var attr in _attributes) + if (attr.Kind == kind) { - if (attr.Kind == kind) + return new DwarfConstant { - return (TValue?)attr.ValueAsObject; - } + AsValue = + { + U64 = attr.ValueAsU64 + }, + AsObject = attr.ValueAsObject + }; } - - return default; } - protected unsafe TValue? GetAttributeValueOpt(DwarfAttributeKind kind) where TValue : unmanaged + return null; + } + + protected void SetAttributeConstantOpt(DwarfAttributeKind kind, DwarfConstant? cst) + { + for (int i = 0; i < _attributes.Count; i++) { - Debug.Assert(sizeof(TValue) <= sizeof(ulong)); - - foreach (var attr in _attributes) + var attr = _attributes[i]; + if (attr.Kind == kind) { - if (attr.Kind == kind) + if (!cst.HasValue) { - ulong localU64 = attr.ValueAsU64; - return *(TValue*) &localU64; + RemoveAttributeAt(i); } + else + { + var value = cst.Value; + attr.ValueAsU64 = value.AsValue.U64; + attr.ValueAsObject = value.AsExpression; + } + return; } + } - return default; + if (cst.HasValue) + { + var value = cst.Value; + AddAttribute(new DwarfAttribute() + { + Kind = kind, + ValueAsU64 = value.AsValue.U64, + ValueAsObject = value.AsExpression + }); } + } - protected DwarfConstant? GetAttributeConstantOpt(DwarfAttributeKind kind) + protected DwarfLocation? GetAttributeLocationOpt(DwarfAttributeKind kind) + { + foreach (var attr in _attributes) { - foreach (var attr in _attributes) + if (attr.Kind == kind) { - if (attr.Kind == kind) + return new DwarfLocation { - return new DwarfConstant + AsValue = { - AsValue = - { - U64 = attr.ValueAsU64 - }, - AsObject = attr.ValueAsObject - }; - } + U64 = attr.ValueAsU64 + }, + AsObject = attr.ValueAsObject + }; } - - return null; } - protected void SetAttributeConstantOpt(DwarfAttributeKind kind, DwarfConstant? cst) + return null; + } + + protected void SetAttributeLocationOpt(DwarfAttributeKind kind, DwarfLocation? cst) + { + for (int i = 0; i < _attributes.Count; i++) { - for (int i = 0; i < _attributes.Count; i++) + var attr = _attributes[i]; + if (attr.Kind == kind) { - var attr = _attributes[i]; - if (attr.Kind == kind) + if (!cst.HasValue) { - if (!cst.HasValue) - { - RemoveAttributeAt(i); - } - else - { - var value = cst.Value; - attr.ValueAsU64 = value.AsValue.U64; - attr.ValueAsObject = value.AsExpression; - } - return; + RemoveAttributeAt(i); + } + else + { + var value = cst.Value; + attr.ValueAsU64 = value.AsValue.U64; + attr.ValueAsObject = value.AsObject; } + return; } + } - if (cst.HasValue) + if (cst.HasValue) + { + var value = cst.Value; + AddAttribute(new DwarfAttribute() { - var value = cst.Value; - AddAttribute(new DwarfAttribute() - { - Kind = kind, - ValueAsU64 = value.AsValue.U64, - ValueAsObject = value.AsExpression - }); - } + Kind = kind, + ValueAsU64 = value.AsValue.U64, + ValueAsObject = value.AsObject + }); } + } - protected DwarfLocation? GetAttributeLocationOpt(DwarfAttributeKind kind) + public DwarfAttribute? FindAttributeByKey(DwarfAttributeKind kind) + { + foreach (var attr in _attributes) { - foreach (var attr in _attributes) + if (attr.Kind == kind) { - if (attr.Kind == kind) - { - return new DwarfLocation - { - AsValue = - { - U64 = attr.ValueAsU64 - }, - AsObject = attr.ValueAsObject - }; - } + return attr; } - - return null; } - protected void SetAttributeLocationOpt(DwarfAttributeKind kind, DwarfLocation? cst) + return null; + } + + protected unsafe void SetAttributeValue(DwarfAttributeKind kind, TValue? value) + { + for (int i = 0; i < _attributes.Count; i++) { - for (int i = 0; i < _attributes.Count; i++) + var attr = _attributes[i]; + if (attr.Kind == kind) { - var attr = _attributes[i]; - if (attr.Kind == kind) + if (value == null) { - if (!cst.HasValue) - { - RemoveAttributeAt(i); - } - else - { - var value = cst.Value; - attr.ValueAsU64 = value.AsValue.U64; - attr.ValueAsObject = value.AsObject; - } - return; + RemoveAttributeAt(i); } - } - - if (cst.HasValue) - { - var value = cst.Value; - AddAttribute(new DwarfAttribute() + else { - Kind = kind, - ValueAsU64 = value.AsValue.U64, - ValueAsObject = value.AsObject - }); + attr.ValueAsObject = value; + } + return; } } - public DwarfAttribute? FindAttributeByKey(DwarfAttributeKind kind) + if (value == null) return; + AddAttribute(new DwarfAttribute() { Kind = kind, ValueAsObject = value}); + } + + protected void SetAttributeLinkValue(DwarfAttributeKind kind, TLink link) where TLink : IObjectFileNodeLink + { + for (int i = 0; i < _attributes.Count; i++) { - foreach (var attr in _attributes) + var attr = _attributes[i]; + if (attr.Kind == kind) { - if (attr.Kind == kind) + if (link == null) { - return attr; + RemoveAttributeAt(i); } + else + { + attr.ValueAsU64 = link.GetRelativeOffset(); + attr.ValueAsObject = link.GetObjectFileNode(); + } + return; } - - return null; } - protected unsafe void SetAttributeValue(DwarfAttributeKind kind, TValue? value) + AddAttribute(new DwarfAttribute() { - for (int i = 0; i < _attributes.Count; i++) - { - var attr = _attributes[i]; - if (attr.Kind == kind) - { - if (value == null) - { - RemoveAttributeAt(i); - } - else - { - attr.ValueAsObject = value; - } - return; - } - } + Kind = kind, + ValueAsU64 = link.GetRelativeOffset(), + ValueAsObject = link.GetObjectFileNode() + }); + } - if (value == null) return; - AddAttribute(new DwarfAttribute() { Kind = kind, ValueAsObject = value}); - } + protected unsafe void SetAttributeValueOpt(DwarfAttributeKind kind, TValue? value) where TValue : unmanaged + { + Debug.Assert(sizeof(TValue) <= sizeof(ulong)); - protected void SetAttributeLinkValue(DwarfAttributeKind kind, TLink link) where TLink : IObjectFileNodeLink + for (int i = 0; i < _attributes.Count; i++) { - for (int i = 0; i < _attributes.Count; i++) + var attr = _attributes[i]; + if (attr.Kind == kind) { - var attr = _attributes[i]; - if (attr.Kind == kind) + if (!value.HasValue) { - if (link == null) - { - RemoveAttributeAt(i); - } - else - { - attr.ValueAsU64 = link.GetRelativeOffset(); - attr.ValueAsObject = link.GetObjectFileNode(); - } - return; + RemoveAttributeAt(i); + } + else + { + ulong valueU64 = 0; + *((TValue*) &valueU64) = value.Value; + attr.ValueAsU64 = valueU64; + attr.ValueAsObject = null; } + return; } - - AddAttribute(new DwarfAttribute() - { - Kind = kind, - ValueAsU64 = link.GetRelativeOffset(), - ValueAsObject = link.GetObjectFileNode() - }); } - protected unsafe void SetAttributeValueOpt(DwarfAttributeKind kind, TValue? value) where TValue : unmanaged + if (value.HasValue) { - Debug.Assert(sizeof(TValue) <= sizeof(ulong)); + var attr = new DwarfAttribute() {Kind = kind}; + ulong valueU64 = 0; + *((TValue*)&valueU64) = value.Value; + attr.ValueAsU64 = valueU64; + AddAttribute(attr); + } + } - for (int i = 0; i < _attributes.Count; i++) - { - var attr = _attributes[i]; - if (attr.Kind == kind) - { - if (!value.HasValue) - { - RemoveAttributeAt(i); - } - else - { - ulong valueU64 = 0; - *((TValue*) &valueU64) = value.Value; - attr.ValueAsU64 = valueU64; - attr.ValueAsObject = null; - } - return; - } - } + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + var abbrev = Abbrev; - if (value.HasValue) - { - var attr = new DwarfAttribute() {Kind = kind}; - ulong valueU64 = 0; - *((TValue*)&valueU64) = value.Value; - attr.ValueAsU64 = valueU64; - AddAttribute(attr); - } + var endOffset = Position; + if (abbrev is null) + { + throw new InvalidOperationException("Abbreviation is not set"); } + endOffset += DwarfHelper.SizeOfULEB128(abbrev.Code); // WriteULEB128(abbreviationItem.Code); - protected override void UpdateLayout(DwarfLayoutContext layoutContext) + foreach (var attr in _attributes) { - var abbrev = Abbrev; + attr.Position = endOffset; + attr.UpdateLayout(layoutContext); + endOffset += attr.Size; + } - var endOffset = Position; - if (abbrev is null) + if (abbrev.HasChildren) + { + foreach (var child in _children) { - throw new InvalidOperationException("Abbreviation is not set"); + child.Position = endOffset; + child.UpdateLayout(layoutContext); + endOffset += child.Size; } - endOffset += DwarfHelper.SizeOfULEB128(abbrev.Code); // WriteULEB128(abbreviationItem.Code); - foreach (var attr in _attributes) - { - attr.Position = endOffset; - attr.UpdateLayoutInternal(layoutContext); - endOffset += attr.Size; - } + // Encode abbreviation 0 code + endOffset += DwarfHelper.SizeOfULEB128(0); + } - if (abbrev.HasChildren) - { - foreach (var child in _children) - { - child.Position = endOffset; - child.UpdateLayout(layoutContext); - endOffset += child.Size; - } + Size = endOffset - Position; + } - // Encode abbreviation 0 code - endOffset += DwarfHelper.SizeOfULEB128(0); - } + public override void Read(DwarfReader reader) + { + // Store map offset to DIE to resolve references + reader.PushDIE(this); - Size = endOffset - Position; - } + // Console.WriteLine($" <{level}><{die.Offset:x}> Abbrev Number: {abbreviationCode} ({die.Tag})"); - protected override void Read(DwarfReader reader) + if (Abbrev is null) { - // Store map offset to DIE to resolve references - reader.PushDIE(this); - - // Console.WriteLine($" <{level}><{die.Offset:x}> Abbrev Number: {abbreviationCode} ({die.Tag})"); - - if (Abbrev is null) - { - throw new InvalidOperationException("Abbreviation is not set"); - } + throw new InvalidOperationException("Abbreviation is not set"); + } - var descriptors = Abbrev.Descriptors; - if (descriptors.Length > 0) + var descriptors = Abbrev.Descriptors; + if (descriptors.Length > 0) + { + for (int i = 0; i < descriptors.Length; i++) { - for (int i = 0; i < descriptors.Length; i++) - { - reader.CurrentAttributeDescriptor = descriptors[i]; + reader.CurrentAttributeDescriptor = descriptors[i]; - var attribute = new DwarfAttribute() - { - Position = reader.Position, - }; + var attribute = new DwarfAttribute() + { + Position = reader.Position, + }; - attribute.ReadInternal(reader); + attribute.Read(reader); - AddAttribute(attribute); - } + AddAttribute(attribute); } + } - if (Abbrev.HasChildren) + if (Abbrev.HasChildren) + { + while (true) { - while (true) - { - reader.DIELevel++; - var child = ReadInstance(reader); - reader.DIELevel--; - if (child == null) break; + reader.DIELevel++; + var child = ReadInstance(reader); + reader.DIELevel--; + if (child == null) break; - AddChild(child); - } + AddChild(child); } + } - reader.PopDIE(); + reader.PopDIE(); - Size = reader.Position - Position; - } + Size = reader.Position - Position; + } + + internal static DwarfDIE? ReadInstance(DwarfReader reader) + { + var startDIEOffset = reader.Position; + var abbreviationCode = reader.ReadULEB128(); + DwarfDIE? die = null; - internal static DwarfDIE? ReadInstance(DwarfReader reader) + if (abbreviationCode != 0) { - var startDIEOffset = reader.Position; - var abbreviationCode = reader.ReadULEB128(); - DwarfDIE? die = null; - if (abbreviationCode != 0) + if (!reader.CurrentUnit!.Abbreviation!.TryFindByCode(abbreviationCode, out var abbreviationItem)) { - - if (!reader.CurrentUnit!.Abbreviation!.TryFindByCode(abbreviationCode, out var abbreviationItem)) - { - throw new InvalidOperationException($"Invalid abbreviation code {abbreviationCode}"); - } - - die = DIEHelper.ConvertTagToDwarfDIE((ushort) abbreviationItem.Tag); - die.Position = startDIEOffset; - die.Abbrev = abbreviationItem; - die.Tag = abbreviationItem.Tag; - die.ReadInternal(reader); + throw new InvalidOperationException($"Invalid abbreviation code {abbreviationCode}"); } - return die; + die = DIEHelper.ConvertTagToDwarfDIE((ushort) abbreviationItem.Tag); + die.Position = startDIEOffset; + die.Abbrev = abbreviationItem; + die.Tag = abbreviationItem.Tag; + die.Read(reader); } - internal void UpdateAbbreviationItem(DwarfLayoutContext context) - { - // Initialize the offset of DIE to ulong.MaxValue to make sure that when we have a reference - // to it, we can detect if it is a forward or backward reference. - // If it is a backward reference, we will be able to encode the offset - // otherwise we will have to pad the encoding with NOP (for DwarfOperation in expressions) - Position = ulong.MaxValue; + return die; + } - // TODO: pool if not used by GetOrCreate below - var descriptorArray = new DwarfAttributeDescriptor[Attributes.Count]; + internal void UpdateAbbreviationItem(DwarfLayoutContext context) + { + // Initialize the offset of DIE to ulong.MaxValue to make sure that when we have a reference + // to it, we can detect if it is a forward or backward reference. + // If it is a backward reference, we will be able to encode the offset + // otherwise we will have to pad the encoding with NOP (for DwarfOperation in expressions) + Position = ulong.MaxValue; - for (var i = 0; i < Attributes.Count; i++) - { - var attr = Attributes[i]; - attr.UpdateAttributeForm(context); - descriptorArray[i] = new DwarfAttributeDescriptor(attr.Kind, attr.Form); - } + // TODO: pool if not used by GetOrCreate below + var descriptorArray = new DwarfAttributeDescriptor[Attributes.Count]; - var key = new DwarfAbbreviationItemKey(Tag, Children.Count > 0, new DwarfAttributeDescriptors(descriptorArray)); - var item = context.CurrentUnit!.Abbreviation!.GetOrCreate(key); + for (var i = 0; i < Attributes.Count; i++) + { + var attr = Attributes[i]; + attr.UpdateAttributeForm(context); + descriptorArray[i] = new DwarfAttributeDescriptor(attr.Kind, attr.Form); + } - Abbrev = item; + var key = new DwarfAbbreviationItemKey(Tag, Children.Count > 0, new DwarfAttributeDescriptors(descriptorArray)); + var item = context.CurrentUnit!.Abbreviation!.GetOrCreate(key); - foreach (var children in Children) - { - children.UpdateAbbreviationItem(context); - } + Abbrev = item; + + foreach (var children in Children) + { + children.UpdateAbbreviationItem(context); } + } - protected override void Write(DwarfWriter writer) + public override void Write(DwarfWriter writer) + { + var startDIEOffset = Position; + Debug.Assert(Position == startDIEOffset); + var abbrev = Abbrev; + if (abbrev is null) { - var startDIEOffset = Position; - Debug.Assert(Position == startDIEOffset); - var abbrev = Abbrev; - if (abbrev is null) - { - throw new InvalidOperationException("Abbreviation is not set"); - } + throw new InvalidOperationException("Abbreviation is not set"); + } - writer.WriteULEB128(abbrev.Code); + writer.WriteULEB128(abbrev.Code); - foreach (var attr in _attributes) - { - attr.WriteInternal(writer); - } + foreach (var attr in _attributes) + { + attr.Write(writer); + } - if (abbrev.HasChildren) + if (abbrev.HasChildren) + { + foreach (var child in _children) { - foreach (var child in _children) - { - child.Write(writer); - } - writer.WriteULEB128(0); + child.Write(writer); } - - Debug.Assert(Size == writer.Position - startDIEOffset); + writer.WriteULEB128(0); } + + Debug.Assert(Size == writer.Position - startDIEOffset); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfDIEDeclaration.cs b/src/LibObjectFile/Dwarf/DwarfDIEDeclaration.cs index 0532dba..8b682be 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIEDeclaration.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIEDeclaration.cs @@ -2,27 +2,26 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public abstract class DwarfDIEDeclaration : DwarfDIE { - public abstract class DwarfDIEDeclaration : DwarfDIE + // DW_AT_decl_column, DW_AT_decl_file, and DW_AT_decl_line + public ulong? DeclColumn { - // DW_AT_decl_column, DW_AT_decl_file, and DW_AT_decl_line - public ulong? DeclColumn - { - get => GetAttributeValueOpt(DwarfAttributeKind.DeclColumn); - set => SetAttributeValueOpt(DwarfAttributeKind.DeclColumn, value); - } + get => GetAttributeValueOpt(DwarfAttributeKind.DeclColumn); + set => SetAttributeValueOpt(DwarfAttributeKind.DeclColumn, value); + } - public DwarfFileName? DeclFile - { - get => GetAttributeValue(DwarfAttributeKind.DeclFile); - set => SetAttributeValue(DwarfAttributeKind.DeclFile, value); - } + public DwarfFileName? DeclFile + { + get => GetAttributeValue(DwarfAttributeKind.DeclFile); + set => SetAttributeValue(DwarfAttributeKind.DeclFile, value); + } - public ulong? DeclLine - { - get => GetAttributeValueOpt(DwarfAttributeKind.DeclLine); - set => SetAttributeValueOpt(DwarfAttributeKind.DeclLine, value); - } + public ulong? DeclLine + { + get => GetAttributeValueOpt(DwarfAttributeKind.DeclLine); + set => SetAttributeValueOpt(DwarfAttributeKind.DeclLine, value); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfDiscriminantListKind.cs b/src/LibObjectFile/Dwarf/DwarfDiscriminantListKind.cs index b059ae4..5aa5841 100644 --- a/src/LibObjectFile/Dwarf/DwarfDiscriminantListKind.cs +++ b/src/LibObjectFile/Dwarf/DwarfDiscriminantListKind.cs @@ -2,12 +2,11 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public enum DwarfDiscriminantListKind : byte { - public enum DwarfDiscriminantListKind : byte - { - Label = DwarfNative.DW_DSC_label, + Label = DwarfNative.DW_DSC_label, - Range = DwarfNative.DW_DSC_range, - } + Range = DwarfNative.DW_DSC_range, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfElfContext.cs b/src/LibObjectFile/Dwarf/DwarfElfContext.cs index d862ac0..df17966 100644 --- a/src/LibObjectFile/Dwarf/DwarfElfContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfElfContext.cs @@ -8,334 +8,333 @@ using System.Linq; using LibObjectFile.Elf; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfElfContext : VisitorContextBase { - public class DwarfElfContext + private readonly int _codeSectionSymbolIndex; + private int _infoSectionSymbolIndex; + private int _abbreviationTableSymbolIndex; + private int _lineTableSymbolIndex; + private int _stringTableSymbolIndex; + private int _locationSectionSymbolIndex; + private readonly ElfSymbolTable? _symbolTable; + + public DwarfElfContext(ElfObjectFile elf) : base(elf, new DiagnosticBag()) { - private readonly int _codeSectionSymbolIndex; - private int _infoSectionSymbolIndex; - private int _abbreviationTableSymbolIndex; - private int _lineTableSymbolIndex; - private int _stringTableSymbolIndex; - private int _locationSectionSymbolIndex; - private readonly ElfSymbolTable? _symbolTable; - - public DwarfElfContext(ElfObjectFile elf) - { - Elf = elf ?? throw new ArgumentNullException(nameof(elf)); + Elf = elf ?? throw new ArgumentNullException(nameof(elf)); - var relocContext = new ElfRelocationContext(); + var relocContext = new ElfRelocationContext(); - var codeSection = elf.Sections.OfType().FirstOrDefault(s => s.Name == ".text"); + var codeSection = elf.Sections.OfType().FirstOrDefault(s => s.Name == ".text"); - _symbolTable = elf.Sections.OfType().FirstOrDefault(); - var mapSectionToSymbolIndex = new Dictionary(); - if (_symbolTable != null) + _symbolTable = elf.Sections.OfType().FirstOrDefault(); + var mapSectionToSymbolIndex = new Dictionary(); + if (_symbolTable != null) + { + for (var i = 0; i < _symbolTable.Entries.Count; i++) { - for (var i = 0; i < _symbolTable.Entries.Count; i++) - { - var entry = _symbolTable.Entries[i]; + var entry = _symbolTable.Entries[i]; - if (entry.Type == ElfSymbolType.Section && entry.Section.Section != null) - { - mapSectionToSymbolIndex[entry.Section.Section] = i; - } + if (entry.Type == ElfSymbolType.Section && entry.Section.Section != null) + { + mapSectionToSymbolIndex[entry.Section.Section] = i; } + } - if (codeSection != null) + if (codeSection != null) + { + if (!mapSectionToSymbolIndex.TryGetValue(codeSection, out _codeSectionSymbolIndex)) { - if (!mapSectionToSymbolIndex.TryGetValue(codeSection, out _codeSectionSymbolIndex)) + _codeSectionSymbolIndex = _symbolTable.Entries.Count; + _symbolTable.Entries.Add(new ElfSymbol() { - _codeSectionSymbolIndex = _symbolTable.Entries.Count; - _symbolTable.Entries.Add(new ElfSymbol() - { - Type = ElfSymbolType.Section, - Section = codeSection, - }); - } + Type = ElfSymbolType.Section, + Section = codeSection, + }); } } + } - foreach (var section in elf.Sections) + foreach (var section in elf.Sections) + { + switch (section.Name.Value) { - switch (section.Name.Value) - { - case ".debug_info": - InfoSection = ((ElfBinarySection)section); - mapSectionToSymbolIndex.TryGetValue(InfoSection, out _infoSectionSymbolIndex); - break; - case ".debug_abbrev": - AbbreviationTable = ((ElfBinarySection)section); - mapSectionToSymbolIndex.TryGetValue(AbbreviationTable, out _abbreviationTableSymbolIndex); - break; - case ".debug_aranges": - AddressRangeTable = ((ElfBinarySection)section); - break; - case ".debug_str": - StringTable = ((ElfBinarySection)section); - mapSectionToSymbolIndex.TryGetValue(StringTable, out _stringTableSymbolIndex); - break; - case ".debug_line": - LineTable = ((ElfBinarySection)section); - mapSectionToSymbolIndex.TryGetValue(LineTable, out _lineTableSymbolIndex); - break; - case ".debug_loc": - LocationSection = ((ElfBinarySection)section); - mapSectionToSymbolIndex.TryGetValue(LocationSection, out _locationSectionSymbolIndex); - break; - - case ".rela.debug_aranges": - case ".rel.debug_aranges": - RelocAddressRangeTable = (ElfRelocationTable)section; - RelocAddressRangeTable.Relocate(relocContext); - break; - - case ".rela.debug_line": - case ".rel.debug_line": - RelocLineTable = (ElfRelocationTable)section; - RelocLineTable.Relocate(relocContext); - break; - - case ".rela.debug_info": - case ".rel.debug_info": - RelocInfoSection = (ElfRelocationTable)section; - RelocInfoSection.Relocate(relocContext); - break; - - case ".rela.debug_loc": - case ".rel.debug_loc": - RelocLocationSection = (ElfRelocationTable)section; - RelocLocationSection.Relocate(relocContext); - break; - } + case ".debug_info": + InfoSection = ((ElfBinarySection)section); + mapSectionToSymbolIndex.TryGetValue(InfoSection, out _infoSectionSymbolIndex); + break; + case ".debug_abbrev": + AbbreviationTable = ((ElfBinarySection)section); + mapSectionToSymbolIndex.TryGetValue(AbbreviationTable, out _abbreviationTableSymbolIndex); + break; + case ".debug_aranges": + AddressRangeTable = ((ElfBinarySection)section); + break; + case ".debug_str": + StringTable = ((ElfBinarySection)section); + mapSectionToSymbolIndex.TryGetValue(StringTable, out _stringTableSymbolIndex); + break; + case ".debug_line": + LineTable = ((ElfBinarySection)section); + mapSectionToSymbolIndex.TryGetValue(LineTable, out _lineTableSymbolIndex); + break; + case ".debug_loc": + LocationSection = ((ElfBinarySection)section); + mapSectionToSymbolIndex.TryGetValue(LocationSection, out _locationSectionSymbolIndex); + break; + + case ".rela.debug_aranges": + case ".rel.debug_aranges": + RelocAddressRangeTable = (ElfRelocationTable)section; + RelocAddressRangeTable.Relocate(relocContext); + break; + + case ".rela.debug_line": + case ".rel.debug_line": + RelocLineTable = (ElfRelocationTable)section; + RelocLineTable.Relocate(relocContext); + break; + + case ".rela.debug_info": + case ".rel.debug_info": + RelocInfoSection = (ElfRelocationTable)section; + RelocInfoSection.Relocate(relocContext); + break; + + case ".rela.debug_loc": + case ".rel.debug_loc": + RelocLocationSection = (ElfRelocationTable)section; + RelocLocationSection.Relocate(relocContext); + break; } } + } - public ElfObjectFile Elf { get; } + public ElfObjectFile Elf { get; } - public bool IsLittleEndian => Elf.Encoding == ElfEncoding.Lsb; + public bool IsLittleEndian => Elf.Encoding == ElfEncoding.Lsb; - public DwarfAddressSize AddressSize => Elf.FileClass == ElfFileClass.Is64 ? DwarfAddressSize.Bit64 : DwarfAddressSize.Bit32; + public DwarfAddressSize AddressSize => Elf.FileClass == ElfFileClass.Is64 ? DwarfAddressSize.Bit64 : DwarfAddressSize.Bit32; - public ElfBinarySection? InfoSection { get; private set; } + public ElfBinarySection? InfoSection { get; private set; } - public ElfRelocationTable? RelocInfoSection { get; set; } + public ElfRelocationTable? RelocInfoSection { get; set; } - public ElfBinarySection? AbbreviationTable { get; set; } + public ElfBinarySection? AbbreviationTable { get; set; } - public ElfBinarySection? AddressRangeTable { get; private set; } + public ElfBinarySection? AddressRangeTable { get; private set; } - public ElfRelocationTable? RelocAddressRangeTable { get; set; } + public ElfRelocationTable? RelocAddressRangeTable { get; set; } - public ElfBinarySection? StringTable { get; set; } + public ElfBinarySection? StringTable { get; set; } - public ElfBinarySection? LineTable { get; set; } + public ElfBinarySection? LineTable { get; set; } - public ElfRelocationTable? RelocLineTable { get; set; } + public ElfRelocationTable? RelocLineTable { get; set; } - public ElfBinarySection? LocationSection { get; private set; } + public ElfBinarySection? LocationSection { get; private set; } - public ElfRelocationTable? RelocLocationSection { get; set; } + public ElfRelocationTable? RelocLocationSection { get; set; } - public int CodeSectionSymbolIndex => _codeSectionSymbolIndex; + public int CodeSectionSymbolIndex => _codeSectionSymbolIndex; - public int InfoSectionSymbolIndex => _infoSectionSymbolIndex; + public int InfoSectionSymbolIndex => _infoSectionSymbolIndex; - public int StringTableSymbolIndex => _stringTableSymbolIndex; + public int StringTableSymbolIndex => _stringTableSymbolIndex; - public int AbbreviationTableSymbolIndex => _abbreviationTableSymbolIndex; + public int AbbreviationTableSymbolIndex => _abbreviationTableSymbolIndex; - public int LineTableSymbolIndex => _lineTableSymbolIndex; + public int LineTableSymbolIndex => _lineTableSymbolIndex; - public int LocationSectionSymbolIndex => _locationSectionSymbolIndex; + public int LocationSectionSymbolIndex => _locationSectionSymbolIndex; - public ElfBinarySection GetOrCreateInfoSection() - { - return InfoSection ??= GetOrCreateDebugSection(".debug_info", true, out _infoSectionSymbolIndex); - } + public ElfBinarySection GetOrCreateInfoSection() + { + return InfoSection ??= GetOrCreateDebugSection(".debug_info", true, out _infoSectionSymbolIndex); + } - public ElfRelocationTable GetOrCreateRelocInfoSection() - { - return RelocInfoSection ??= GetOrCreateRelocationTable(InfoSection!); - } + public ElfRelocationTable GetOrCreateRelocInfoSection() + { + return RelocInfoSection ??= GetOrCreateRelocationTable(InfoSection!); + } - public ElfBinarySection GetOrCreateAbbreviationTable() - { - return AbbreviationTable ??= GetOrCreateDebugSection(".debug_abbrev", true, out _abbreviationTableSymbolIndex); - } + public ElfBinarySection GetOrCreateAbbreviationTable() + { + return AbbreviationTable ??= GetOrCreateDebugSection(".debug_abbrev", true, out _abbreviationTableSymbolIndex); + } - public ElfBinarySection GetOrCreateAddressRangeTable() - { - return AddressRangeTable ??= GetOrCreateDebugSection(".debug_aranges", false, out _); - } + public ElfBinarySection GetOrCreateAddressRangeTable() + { + return AddressRangeTable ??= GetOrCreateDebugSection(".debug_aranges", false, out _); + } - public ElfRelocationTable GetOrCreateRelocAddressRangeTable() - { - return RelocAddressRangeTable ??= GetOrCreateRelocationTable(AddressRangeTable!); - } + public ElfRelocationTable GetOrCreateRelocAddressRangeTable() + { + return RelocAddressRangeTable ??= GetOrCreateRelocationTable(AddressRangeTable!); + } - public ElfBinarySection GetOrCreateLineSection() - { - return LineTable ??= GetOrCreateDebugSection(".debug_line", true, out _lineTableSymbolIndex); - } + public ElfBinarySection GetOrCreateLineSection() + { + return LineTable ??= GetOrCreateDebugSection(".debug_line", true, out _lineTableSymbolIndex); + } - public ElfRelocationTable GetOrCreateRelocLineSection() - { - return RelocLineTable ??= GetOrCreateRelocationTable(LineTable!); - } + public ElfRelocationTable GetOrCreateRelocLineSection() + { + return RelocLineTable ??= GetOrCreateRelocationTable(LineTable!); + } - public ElfBinarySection GetOrCreateStringTable() - { - return StringTable ??= GetOrCreateDebugSection(".debug_str", true, out _stringTableSymbolIndex); - } + public ElfBinarySection GetOrCreateStringTable() + { + return StringTable ??= GetOrCreateDebugSection(".debug_str", true, out _stringTableSymbolIndex); + } - public ElfBinarySection GetOrCreateLocationSection() - { - return LocationSection ??= GetOrCreateDebugSection(".debug_loc", true, out _locationSectionSymbolIndex); - } + public ElfBinarySection GetOrCreateLocationSection() + { + return LocationSection ??= GetOrCreateDebugSection(".debug_loc", true, out _locationSectionSymbolIndex); + } - public ElfRelocationTable GetOrCreateRelocLocationSection() - { - return RelocLocationSection ??= GetOrCreateRelocationTable(LocationSection!); - } + public ElfRelocationTable GetOrCreateRelocLocationSection() + { + return RelocLocationSection ??= GetOrCreateRelocationTable(LocationSection!); + } - public void RemoveStringTable() + public void RemoveStringTable() + { + if (StringTable != null) { - if (StringTable != null) - { - Elf.RemoveSection(StringTable); - StringTable = null; - } + Elf.RemoveSection(StringTable); + StringTable = null; } + } - public void RemoveAbbreviationTable() + public void RemoveAbbreviationTable() + { + if (AbbreviationTable != null) { - if (AbbreviationTable != null) - { - Elf.RemoveSection(AbbreviationTable); - AbbreviationTable = null; - } + Elf.RemoveSection(AbbreviationTable); + AbbreviationTable = null; } + } - public void RemoveLineTable() + public void RemoveLineTable() + { + if (LineTable != null) { - if (LineTable != null) - { - Elf.RemoveSection(LineTable); - LineTable = null; - } - - RemoveRelocLineTable(); + Elf.RemoveSection(LineTable); + LineTable = null; } - public void RemoveRelocLineTable() + RemoveRelocLineTable(); + } + + public void RemoveRelocLineTable() + { + if (RelocLineTable != null) { - if (RelocLineTable != null) - { - Elf.RemoveSection(RelocLineTable); - RelocLineTable = null; - } + Elf.RemoveSection(RelocLineTable); + RelocLineTable = null; } + } - public void RemoveAddressRangeTable() + public void RemoveAddressRangeTable() + { + if (AddressRangeTable != null) { - if (AddressRangeTable != null) - { - Elf.RemoveSection(AddressRangeTable); - AddressRangeTable = null; - } - - RemoveRelocAddressRangeTable(); + Elf.RemoveSection(AddressRangeTable); + AddressRangeTable = null; } - public void RemoveRelocAddressRangeTable() + RemoveRelocAddressRangeTable(); + } + + public void RemoveRelocAddressRangeTable() + { + if (RelocAddressRangeTable != null) { - if (RelocAddressRangeTable != null) - { - Elf.RemoveSection(RelocAddressRangeTable); - RelocAddressRangeTable = null; - } + Elf.RemoveSection(RelocAddressRangeTable); + RelocAddressRangeTable = null; } + } - public void RemoveInfoSection() + public void RemoveInfoSection() + { + if (InfoSection != null) { - if (InfoSection != null) - { - Elf.RemoveSection(InfoSection); - InfoSection = null; - } - - RemoveRelocInfoSection(); + Elf.RemoveSection(InfoSection); + InfoSection = null; } - public void RemoveRelocInfoSection() + RemoveRelocInfoSection(); + } + + public void RemoveRelocInfoSection() + { + if (RelocInfoSection != null) { - if (RelocInfoSection != null) - { - Elf.RemoveSection(RelocInfoSection); - RelocInfoSection = null; - } + Elf.RemoveSection(RelocInfoSection); + RelocInfoSection = null; } + } - public void RemoveLocationSection() + public void RemoveLocationSection() + { + if (LocationSection != null) { - if (LocationSection != null) - { - Elf.RemoveSection(LocationSection); - LocationSection = null; - } - - RemoveRelocLocationSection(); + Elf.RemoveSection(LocationSection); + LocationSection = null; } - public void RemoveRelocLocationSection() + RemoveRelocLocationSection(); + } + + public void RemoveRelocLocationSection() + { + if (RelocLocationSection != null) { - if (RelocLocationSection != null) - { - Elf.RemoveSection(RelocLocationSection); - RelocLocationSection = null; - } + Elf.RemoveSection(RelocLocationSection); + RelocLocationSection = null; } + } - private ElfBinarySection GetOrCreateDebugSection(string name, bool createSymbol, out int symbolIndex) + private ElfBinarySection GetOrCreateDebugSection(string name, bool createSymbol, out int symbolIndex) + { + var newSection = new ElfBinarySection() { - var newSection = new ElfBinarySection() - { - Name = name, - Alignment = 1, - Type = ElfSectionType.ProgBits, - Stream = new MemoryStream(), - }; + Name = name, + Alignment = 1, + Type = ElfSectionType.ProgBits, + Stream = new MemoryStream(), + }; - Elf.AddSection(newSection); - symbolIndex = 0; + Elf.AddSection(newSection); + symbolIndex = 0; - if (createSymbol && _symbolTable != null) + if (createSymbol && _symbolTable != null) + { + symbolIndex = _symbolTable.Entries.Count; + _symbolTable.Entries.Add(new ElfSymbol() { - symbolIndex = _symbolTable.Entries.Count; - _symbolTable.Entries.Add(new ElfSymbol() - { - Type = ElfSymbolType.Section, - Section = newSection, - }); - } - - return newSection; + Type = ElfSymbolType.Section, + Section = newSection, + }); } - private ElfRelocationTable GetOrCreateRelocationTable(ElfBinarySection section) + return newSection; + } + + private ElfRelocationTable GetOrCreateRelocationTable(ElfBinarySection section) + { + var newSection = new ElfRelocationTable() { - var newSection = new ElfRelocationTable() - { - Name = $".rela{section.Name}", - Alignment = (ulong)AddressSize, - Flags = ElfSectionFlags.InfoLink, - Type = ElfSectionType.RelocationAddends, - Info = section, - Link = _symbolTable, - }; - Elf.AddSection(newSection); - return newSection; - } + Name = $".rela{section.Name}", + Alignment = (ulong)AddressSize, + Flags = ElfSectionFlags.InfoLink, + Type = ElfSectionType.RelocationAddends, + Info = section, + Link = _symbolTable, + }; + Elf.AddSection(newSection); + return newSection; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfExpression.cs b/src/LibObjectFile/Dwarf/DwarfExpression.cs index 6333875..81b1cf0 100644 --- a/src/LibObjectFile/Dwarf/DwarfExpression.cs +++ b/src/LibObjectFile/Dwarf/DwarfExpression.cs @@ -7,127 +7,124 @@ using System.Diagnostics; using LibObjectFile.Utils; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[DebuggerDisplay("Count = {Operations.Count,nq}")] +public class DwarfExpression : DwarfObject { - [DebuggerDisplay("Count = {Operations.Count,nq}")] - public class DwarfExpression : DwarfObject + private readonly List _operations; + + public DwarfExpression() { - private readonly List _operations; + _operations = new List(); + } - public DwarfExpression() - { - _operations = new List(); - } + public ReadOnlyList Operations => _operations; - public ReadOnlyList Operations => _operations; + internal List InternalOperations => _operations; - internal List InternalOperations => _operations; + public ulong OperationLengthInBytes { get; internal set; } - public ulong OperationLengthInBytes { get; internal set; } + public void AddOperation(DwarfOperation operation) + { + if (operation == null) throw new ArgumentNullException(nameof(operation)); + _operations.Add(this, operation); + } - public void AddOperation(DwarfOperation operation) - { - if (operation == null) throw new ArgumentNullException(nameof(operation)); - _operations.Add(this, operation); - } + public void RemoveOperation(DwarfOperation operation) + { + if (operation == null) throw new ArgumentNullException(nameof(operation)); + _operations.Remove(this, operation); + } - public void RemoveOperation(DwarfOperation operation) + public DwarfOperation RemoveOperationAt(int index) + { + return _operations.RemoveAt(this, index); + } + + public override void Verify(DwarfVerifyContext context) + { + foreach (var op in _operations) { - if (operation == null) throw new ArgumentNullException(nameof(operation)); - _operations.Remove(this, operation); + op.Verify(context); } + } + + internal void ReadInternal(DwarfReader reader, bool inLocationSection = false) + { + Position = reader.Position; + var size = inLocationSection ? reader.ReadU16() : reader.ReadULEB128(); + OperationLengthInBytes = size; + var endPosition = reader.Position + size; - public DwarfOperation RemoveOperationAt(int index) + while (reader.Position < endPosition) { - return _operations.RemoveAt(this, index); + var op = new DwarfOperation() {Position = reader.Position}; + op.Read(reader); + AddOperation(op); } - public override void Verify(DiagnosticBag diagnostics) - { - base.Verify(diagnostics); + Size = reader.Position - Position; + } - foreach (var op in _operations) - { - op.Verify(diagnostics); - } - } + internal void WriteInternal(DwarfWriter writer, bool inLocationSection = false) + { + Debug.Assert(Position == writer.Position); + Debug.Assert(!inLocationSection || OperationLengthInBytes <= ushort.MaxValue); - internal void ReadInternal(DwarfReader reader, bool inLocationSection = false) + var startExpressionOffset = writer.Position; + if (inLocationSection) { - Position = reader.Position; - var size = inLocationSection ? reader.ReadU16() : reader.ReadULEB128(); - OperationLengthInBytes = size; - var endPosition = reader.Position + size; - - while (reader.Position < endPosition) - { - var op = new DwarfOperation() {Position = reader.Position}; - op.ReadInternal(reader); - AddOperation(op); - } - - Size = reader.Position - Position; + writer.WriteU16((ushort)OperationLengthInBytes); } - - internal void WriteInternal(DwarfWriter writer, bool inLocationSection = false) + else { - Debug.Assert(Position == writer.Position); - Debug.Assert(!inLocationSection || OperationLengthInBytes <= ushort.MaxValue); - - var startExpressionOffset = writer.Position; - if (inLocationSection) - { - writer.WriteU16((ushort)OperationLengthInBytes); - } - else - { - writer.WriteULEB128(OperationLengthInBytes); - } - - foreach (var op in Operations) - { - op.WriteInternal(writer); - } - - Debug.Assert(writer.Position - startExpressionOffset == Size); + writer.WriteULEB128(OperationLengthInBytes); } - internal void UpdateLayoutInternal(DwarfLayoutContext layoutContext, bool inLocationSection = false) + foreach (var op in Operations) { - var endOffset = Position; - foreach (var op in _operations) - { - op.Position = endOffset; - op.UpdateLayoutInternal(layoutContext); - endOffset += op.Size; - } - - OperationLengthInBytes = endOffset - Position; - - // We need to shift the expression which is prefixed by its size encoded in LEB128, - // or fixed-size U2 in .debug_loc section - var deltaLength = inLocationSection ? sizeof(ushort) : DwarfHelper.SizeOfULEB128(Size); - foreach (var op in InternalOperations) - { - op.Position += deltaLength; - } - - Size = OperationLengthInBytes + deltaLength; + op.Write(writer); } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - UpdateLayoutInternal(layoutContext, inLocationSection: false); - } + Debug.Assert(writer.Position - startExpressionOffset == Size); + } - protected override void Read(DwarfReader reader) + internal void UpdateLayout(DwarfLayoutContext layoutContext, bool inLocationSection) + { + var endOffset = Position; + foreach (var op in _operations) { - ReadInternal(reader, inLocationSection: false); + op.Position = endOffset; + op.UpdateLayout(layoutContext); + endOffset += op.Size; } - protected override void Write(DwarfWriter writer) + OperationLengthInBytes = endOffset - Position; + + // We need to shift the expression which is prefixed by its size encoded in LEB128, + // or fixed-size U2 in .debug_loc section + var deltaLength = inLocationSection ? sizeof(ushort) : DwarfHelper.SizeOfULEB128(Size); + foreach (var op in InternalOperations) { - WriteInternal(writer, inLocationSection: false); + op.Position += deltaLength; } + + Size = OperationLengthInBytes + deltaLength; + } + + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + UpdateLayout(layoutContext, inLocationSection: false); + } + + public override void Read(DwarfReader reader) + { + ReadInternal(reader, inLocationSection: false); + } + + public override void Write(DwarfWriter writer) + { + WriteInternal(writer, inLocationSection: false); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfFile.cs b/src/LibObjectFile/Dwarf/DwarfFile.cs index 46f0c0d..5462e98 100644 --- a/src/LibObjectFile/Dwarf/DwarfFile.cs +++ b/src/LibObjectFile/Dwarf/DwarfFile.cs @@ -5,452 +5,453 @@ using System; using LibObjectFile.Elf; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfFile : DwarfContainer { - public class DwarfFile : DwarfContainer + private DwarfAbbreviationTable _abbreviationTable; + private DwarfStringTable _stringTable; + private DwarfLineSection _lineSection; + private DwarfInfoSection _infoSection; + private DwarfAddressRangeTable _addressRangeTable; + private DwarfLocationSection _locationSection; + + public DwarfFile() { - private DwarfAbbreviationTable _abbreviationTable; - private DwarfStringTable _stringTable; - private DwarfLineSection _lineSection; - private DwarfInfoSection _infoSection; - private DwarfAddressRangeTable _addressRangeTable; - private DwarfLocationSection _locationSection; - - public DwarfFile() - { - AssignChild(this, new DwarfAbbreviationTable(), out _abbreviationTable); - AssignChild(this, new DwarfStringTable(), out _stringTable); - AssignChild(this, new DwarfLineSection(), out _lineSection); - AssignChild(this, new DwarfInfoSection(), out _infoSection); - AssignChild(this, new DwarfAddressRangeTable(), out _addressRangeTable); - AssignChild(this, new DwarfLocationSection(), out _locationSection); - } + AssignChild(this, new DwarfAbbreviationTable(), out _abbreviationTable); + AssignChild(this, new DwarfStringTable(), out _stringTable); + AssignChild(this, new DwarfLineSection(), out _lineSection); + AssignChild(this, new DwarfInfoSection(), out _infoSection); + AssignChild(this, new DwarfAddressRangeTable(), out _addressRangeTable); + AssignChild(this, new DwarfLocationSection(), out _locationSection); + } - public DwarfAbbreviationTable AbbreviationTable - { - get => _abbreviationTable; - set => AttachChild(this, value, ref _abbreviationTable); - } + public DwarfAbbreviationTable AbbreviationTable + { + get => _abbreviationTable; + set => AttachChild(this, value, ref _abbreviationTable); + } - public DwarfStringTable StringTable - { - get => _stringTable; - set => AttachChild(this, value, ref _stringTable); - } + public DwarfStringTable StringTable + { + get => _stringTable; + set => AttachChild(this, value, ref _stringTable); + } - public DwarfLineSection LineSection - { - get => _lineSection; - set => AttachChild(this, value, ref _lineSection); - } + public DwarfLineSection LineSection + { + get => _lineSection; + set => AttachChild(this, value, ref _lineSection); + } - public DwarfAddressRangeTable AddressRangeTable - { - get => _addressRangeTable; - set => AttachChild(this, value, ref _addressRangeTable); - } + public DwarfAddressRangeTable AddressRangeTable + { + get => _addressRangeTable; + set => AttachChild(this, value, ref _addressRangeTable); + } - public DwarfInfoSection InfoSection + public DwarfInfoSection InfoSection + { + get => _infoSection; + set => AttachChild(this, value, ref _infoSection); + } + + public DwarfLocationSection LocationSection + { + get => _locationSection; + set => AttachChild(this, value, ref _locationSection); + } + + public override void Read(DwarfReader reader) + { + throw new NotImplementedException(); + } + + public override void Verify(DwarfVerifyContext context) + { + LineSection.Verify(context); + AbbreviationTable.Verify(context); + AddressRangeTable.Verify(context); + StringTable.Verify(context); + InfoSection.Verify(context); + } + + public void UpdateLayout(DwarfLayoutConfig config, DiagnosticBag diagnostics) + { + if (config == null) throw new ArgumentNullException(nameof(config)); + if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + + var layoutContext = new DwarfLayoutContext(this, config, diagnostics); + + LineSection.Position = 0; + LineSection.UpdateLayout(layoutContext); + if (layoutContext.HasErrors) { - get => _infoSection; - set => AttachChild(this, value, ref _infoSection); + return; } - public DwarfLocationSection LocationSection + // Reset the abbreviation table + // TODO: Make this configurable via the DwarfWriterContext + AbbreviationTable.Position = 0; + AbbreviationTable.Reset(); + + InfoSection.Position = 0; + InfoSection.UpdateLayout(layoutContext); + if (layoutContext.HasErrors) { - get => _locationSection; - set => AttachChild(this, value, ref _locationSection); + return; } - protected override void Read(DwarfReader reader) + // Update AddressRangeTable layout after Info + AddressRangeTable.Position = 0; + AddressRangeTable.UpdateLayout(layoutContext); + if (layoutContext.HasErrors) { - throw new NotImplementedException(); + return; } - public override void Verify(DiagnosticBag diagnostics) + // Update string table right after updating the layout of Info + StringTable.Position = 0; + StringTable.UpdateLayout(layoutContext); + if (layoutContext.HasErrors) { - base.Verify(diagnostics); - - LineSection.Verify(diagnostics); - AbbreviationTable.Verify(diagnostics); - AddressRangeTable.Verify(diagnostics); - StringTable.Verify(diagnostics); - InfoSection.Verify(diagnostics); + return; } - - public void UpdateLayout(DwarfLayoutConfig config, DiagnosticBag diagnostics) - { - if (config == null) throw new ArgumentNullException(nameof(config)); - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - var layoutContext = new DwarfLayoutContext(this, config, diagnostics); + // Update the abbrev table right after we have computed the entire layout of Info + AbbreviationTable.Position = 0; + AbbreviationTable.UpdateLayout(layoutContext); - LineSection.Position = 0; - LineSection.UpdateLayoutInternal(layoutContext); - if (layoutContext.HasErrors) - { - return; - } + LocationSection.Position = 0; + LocationSection.UpdateLayout(layoutContext); + } - // Reset the abbreviation table - // TODO: Make this configurable via the DwarfWriterContext - AbbreviationTable.Position = 0; - AbbreviationTable.Reset(); + public void Write(DwarfWriterContext writerContext) + { + if (writerContext == null) throw new ArgumentNullException(nameof(writerContext)); - InfoSection.Position = 0; - InfoSection.UpdateLayoutInternal(layoutContext); - if (layoutContext.HasErrors) - { - return; - } + var diagnostics = new DiagnosticBag(); - // Update AddressRangeTable layout after Info - AddressRangeTable.Position = 0; - AddressRangeTable.UpdateLayoutInternal(layoutContext); - if (layoutContext.HasErrors) - { - return; - } + var verifyContext = new DwarfVerifyContext(this, diagnostics); - // Update string table right after updating the layout of Info - StringTable.Position = 0; - StringTable.UpdateLayoutInternal(layoutContext); - if (layoutContext.HasErrors) - { - return; - } + // Verify correctness + Verify(verifyContext); + CheckErrors(diagnostics); - // Update the abbrev table right after we have computed the entire layout of Info - AbbreviationTable.Position = 0; - AbbreviationTable.UpdateLayoutInternal(layoutContext); + // Update the layout of all section and tables + UpdateLayout(writerContext.LayoutConfig, diagnostics); + CheckErrors(diagnostics); - LocationSection.Position = 0; - LocationSection.UpdateLayoutInternal(layoutContext); + // Write all section and stables + var writer = new DwarfWriter(this, writerContext.IsLittleEndian, diagnostics); + writer.AddressSize = writerContext.AddressSize; + writer.EnableRelocation = writerContext.EnableRelocation; + + writer.Log = writerContext.DebugLinePrinter; + if (writerContext.DebugLineStream != null) + { + writer.Stream = writerContext.DebugLineStream; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = LineSection; + LineSection.Relocations.Clear(); + LineSection.Write(writer); } - public void Write(DwarfWriterContext writerContext) + writer.Log = null; + if (writerContext.DebugAbbrevStream != null) { - if (writerContext == null) throw new ArgumentNullException(nameof(writerContext)); + writer.Stream = writerContext.DebugAbbrevStream; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = AbbreviationTable; + AbbreviationTable.Write(writer); + } - var diagnostics = new DiagnosticBag(); + if (writerContext.DebugAddressRangeStream != null) + { + writer.Stream = writerContext.DebugAddressRangeStream; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = AddressRangeTable; + AddressRangeTable.Relocations.Clear(); + AddressRangeTable.Write(writer); + } + + if (writerContext.DebugStringStream != null) + { + writer.Stream = writerContext.DebugStringStream; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = StringTable; + StringTable.Write(writer); + } - // Verify correctness - Verify(diagnostics); - CheckErrors(diagnostics); + if (writerContext.DebugInfoStream != null) + { + writer.Stream = writerContext.DebugInfoStream; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = InfoSection; + InfoSection.Relocations.Clear(); + InfoSection.Write(writer); + } - // Update the layout of all section and tables - UpdateLayout(writerContext.LayoutConfig, diagnostics); - CheckErrors(diagnostics); + if (writerContext.DebugLocationStream != null) + { + writer.Stream = writerContext.DebugLocationStream; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = LocationSection; + LocationSection.Relocations.Clear(); + LocationSection.Write(writer); + } - // Write all section and stables - var writer = new DwarfWriter(this, writerContext.IsLittleEndian, diagnostics); - writer.AddressSize = writerContext.AddressSize; - writer.EnableRelocation = writerContext.EnableRelocation; - - writer.Log = writerContext.DebugLinePrinter; - if (writerContext.DebugLineStream != null) - { - writer.Stream = writerContext.DebugLineStream; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = LineSection; - LineSection.Relocations.Clear(); - LineSection.WriteInternal(writer); - } + CheckErrors(diagnostics); + } + + public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig? layoutConfig = null) + { + if (elfContext == null) throw new ArgumentNullException(nameof(elfContext)); - writer.Log = null; - if (writerContext.DebugAbbrevStream != null) - { - writer.Stream = writerContext.DebugAbbrevStream; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = AbbreviationTable; - AbbreviationTable.WriteInternal(writer); - } + var diagnostics = new DiagnosticBag(); - if (writerContext.DebugAddressRangeStream != null) - { - writer.Stream = writerContext.DebugAddressRangeStream; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = AddressRangeTable; - AddressRangeTable.Relocations.Clear(); - AddressRangeTable.WriteInternal(writer); - } + layoutConfig ??= new DwarfLayoutConfig(); - if (writerContext.DebugStringStream != null) - { - writer.Stream = writerContext.DebugStringStream; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = StringTable; - StringTable.WriteInternal(writer); - } + var verifyContext = new DwarfVerifyContext(this, diagnostics); - if (writerContext.DebugInfoStream != null) - { - writer.Stream = writerContext.DebugInfoStream; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = InfoSection; - InfoSection.Relocations.Clear(); - InfoSection.WriteInternal(writer); - } + // Verify correctness + Verify(verifyContext); + CheckErrors(diagnostics); - if (writerContext.DebugLocationStream != null) - { - writer.Stream = writerContext.DebugLocationStream; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = LocationSection; - LocationSection.Relocations.Clear(); - LocationSection.WriteInternal(writer); - } + // Update the layout of all section and tables + UpdateLayout(layoutConfig, diagnostics); + CheckErrors(diagnostics); - CheckErrors(diagnostics); + // Setup the output based on actual content of Dwarf infos + var writer = new DwarfWriter(this, elfContext.IsLittleEndian, diagnostics) + { + AddressSize = elfContext.AddressSize, + EnableRelocation = elfContext.Elf.FileType == ElfFileType.Relocatable + }; + + // Pre-create table/sections to create symbols as well + if (StringTable.Size > 0) elfContext.GetOrCreateStringTable(); + if (AbbreviationTable.Size > 0) elfContext.GetOrCreateAbbreviationTable(); + if (LineSection.Size > 0) elfContext.GetOrCreateLineSection(); + if (AddressRangeTable.Size > 0) elfContext.GetOrCreateAddressRangeTable(); + if (InfoSection.Size > 0) elfContext.GetOrCreateInfoSection(); + + // String table + if (StringTable.Size > 0) + { + writer.Stream = elfContext.GetOrCreateStringTable().Stream!; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = StringTable; + StringTable.Write(writer); } - - public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig? layoutConfig = null) + else { - if (elfContext == null) throw new ArgumentNullException(nameof(elfContext)); - - var diagnostics = new DiagnosticBag(); - - layoutConfig ??= new DwarfLayoutConfig(); - - // Verify correctness - Verify(diagnostics); - CheckErrors(diagnostics); - - // Update the layout of all section and tables - UpdateLayout(layoutConfig, diagnostics); - CheckErrors(diagnostics); - - // Setup the output based on actual content of Dwarf infos - var writer = new DwarfWriter(this, elfContext.IsLittleEndian, diagnostics) - { - AddressSize = elfContext.AddressSize, - EnableRelocation = elfContext.Elf.FileType == ElfFileType.Relocatable - }; - - // Pre-create table/sections to create symbols as well - if (StringTable.Size > 0) elfContext.GetOrCreateStringTable(); - if (AbbreviationTable.Size > 0) elfContext.GetOrCreateAbbreviationTable(); - if (LineSection.Size > 0) elfContext.GetOrCreateLineSection(); - if (AddressRangeTable.Size > 0) elfContext.GetOrCreateAddressRangeTable(); - if (InfoSection.Size > 0) elfContext.GetOrCreateInfoSection(); - - // String table - if (StringTable.Size > 0) - { - writer.Stream = elfContext.GetOrCreateStringTable().Stream!; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = StringTable; - StringTable.WriteInternal(writer); - } - else - { - elfContext.RemoveStringTable(); - } + elfContext.RemoveStringTable(); + } - // Abbreviation table - if (AbbreviationTable.Size > 0) - { - writer.Stream = elfContext.GetOrCreateAbbreviationTable().Stream!; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = AbbreviationTable; - AbbreviationTable.WriteInternal(writer); - } - else - { - elfContext.RemoveAbbreviationTable(); - } + // Abbreviation table + if (AbbreviationTable.Size > 0) + { + writer.Stream = elfContext.GetOrCreateAbbreviationTable().Stream!; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = AbbreviationTable; + AbbreviationTable.Write(writer); + } + else + { + elfContext.RemoveAbbreviationTable(); + } - // Line table - if (LineSection.Size > 0) + // Line table + if (LineSection.Size > 0) + { + writer.Stream = elfContext.GetOrCreateLineSection().Stream!; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = LineSection; + LineSection.Relocations.Clear(); + LineSection.Write(writer); + if (writer.EnableRelocation && LineSection.Relocations.Count > 0) { - writer.Stream = elfContext.GetOrCreateLineSection().Stream!; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = LineSection; - LineSection.Relocations.Clear(); - LineSection.WriteInternal(writer); - if (writer.EnableRelocation && LineSection.Relocations.Count > 0) - { - LineSection.CopyRelocationsTo(elfContext, elfContext.GetOrCreateRelocLineSection()); - } - else - { - elfContext.RemoveRelocLineTable(); - } + LineSection.CopyRelocationsTo(elfContext, elfContext.GetOrCreateRelocLineSection()); } else { - elfContext.RemoveLineTable(); + elfContext.RemoveRelocLineTable(); } + } + else + { + elfContext.RemoveLineTable(); + } - // AddressRange table - if (AddressRangeTable.Size > 0) + // AddressRange table + if (AddressRangeTable.Size > 0) + { + writer.Stream = elfContext.GetOrCreateAddressRangeTable().Stream!; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = AddressRangeTable; + AddressRangeTable.Relocations.Clear(); + AddressRangeTable.Write(writer); + + if (writer.EnableRelocation && AddressRangeTable.Relocations.Count > 0) { - writer.Stream = elfContext.GetOrCreateAddressRangeTable().Stream!; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = AddressRangeTable; - AddressRangeTable.Relocations.Clear(); - AddressRangeTable.WriteInternal(writer); - - if (writer.EnableRelocation && AddressRangeTable.Relocations.Count > 0) - { - AddressRangeTable.CopyRelocationsTo(elfContext, elfContext.GetOrCreateRelocAddressRangeTable()); - } - else - { - elfContext.RemoveAddressRangeTable(); - } + AddressRangeTable.CopyRelocationsTo(elfContext, elfContext.GetOrCreateRelocAddressRangeTable()); } else { elfContext.RemoveAddressRangeTable(); } + } + else + { + elfContext.RemoveAddressRangeTable(); + } - // InfoSection - if (InfoSection.Size > 0) + // InfoSection + if (InfoSection.Size > 0) + { + writer.Stream = elfContext.GetOrCreateInfoSection().Stream!; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = InfoSection; + InfoSection.Relocations.Clear(); + InfoSection.Write(writer); + + if (writer.EnableRelocation && InfoSection.Relocations.Count > 0) { - writer.Stream = elfContext.GetOrCreateInfoSection().Stream!; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = InfoSection; - InfoSection.Relocations.Clear(); - InfoSection.WriteInternal(writer); - - if (writer.EnableRelocation && InfoSection.Relocations.Count > 0) - { - InfoSection.CopyRelocationsTo(elfContext, elfContext.GetOrCreateRelocInfoSection()); - } - else - { - elfContext.RemoveRelocInfoSection(); - } + InfoSection.CopyRelocationsTo(elfContext, elfContext.GetOrCreateRelocInfoSection()); } else { - elfContext.RemoveInfoSection(); + elfContext.RemoveRelocInfoSection(); } + } + else + { + elfContext.RemoveInfoSection(); + } - // LocationSection - if (LocationSection.Size > 0) + // LocationSection + if (LocationSection.Size > 0) + { + writer.Stream = elfContext.GetOrCreateLocationSection().Stream!; + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = LocationSection; + LocationSection.Relocations.Clear(); + LocationSection.Write(writer); + + if (writer.EnableRelocation && LocationSection.Relocations.Count > 0) { - writer.Stream = elfContext.GetOrCreateLocationSection().Stream!; - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = LocationSection; - LocationSection.Relocations.Clear(); - LocationSection.WriteInternal(writer); - - if (writer.EnableRelocation && LocationSection.Relocations.Count > 0) - { - LocationSection.CopyRelocationsTo(elfContext, elfContext.GetOrCreateRelocLocationSection()); - } - else - { - elfContext.RemoveRelocLocationSection(); - } + LocationSection.CopyRelocationsTo(elfContext, elfContext.GetOrCreateRelocLocationSection()); } else { - elfContext.RemoveLocationSection(); + elfContext.RemoveRelocLocationSection(); } - - CheckErrors(diagnostics); } - - public static DwarfFile Read(DwarfReaderContext readerContext) + else { - if (readerContext == null) throw new ArgumentNullException(nameof(readerContext)); - - var dwarf = new DwarfFile(); - var reader = new DwarfReader(readerContext, dwarf, new DiagnosticBag()); - - reader.Log = null; - if (readerContext.DebugAbbrevStream != null) - { - reader.Stream = readerContext.DebugAbbrevStream; - reader.CurrentSection = dwarf.AbbreviationTable; - dwarf.AbbreviationTable.ReadInternal(reader); - } - - if (readerContext.DebugStringStream != null) - { - reader.Stream = readerContext.DebugStringStream; - reader.CurrentSection = dwarf.StringTable; - dwarf.StringTable.ReadInternal(reader); - } - - reader.Log = readerContext.DebugLinePrinter; - if (readerContext.DebugLineStream != null) - { - reader.Stream = readerContext.DebugLineStream; - reader.CurrentSection = dwarf.LineSection; - dwarf.LineSection.ReadInternal(reader); - } - reader.Log = null; + elfContext.RemoveLocationSection(); + } - if (readerContext.DebugAddressRangeStream != null) - { - reader.Stream = readerContext.DebugAddressRangeStream; - reader.CurrentSection = dwarf.AddressRangeTable; - dwarf.AddressRangeTable.ReadInternal(reader); - } + CheckErrors(diagnostics); + } - reader.Log = null; - if (readerContext.DebugLocationStream != null) - { - reader.Stream = readerContext.DebugLocationStream; - reader.CurrentSection = dwarf.LocationSection; - dwarf.LocationSection.ReadInternal(reader); - } + public static DwarfFile Read(DwarfReaderContext readerContext) + { + if (readerContext == null) throw new ArgumentNullException(nameof(readerContext)); - reader.Log = null; - if (readerContext.DebugInfoStream != null) - { - reader.Stream = readerContext.DebugInfoStream; - reader.DefaultUnitKind = DwarfUnitKind.Compile; - reader.CurrentSection = dwarf.InfoSection; - dwarf.InfoSection.ReadInternal(reader); - } + var dwarf = new DwarfFile(); + var reader = new DwarfReader(readerContext, dwarf, new DiagnosticBag()); - CheckErrors(reader.Diagnostics); + reader.Log = null; + if (readerContext.DebugAbbrevStream != null) + { + reader.Stream = readerContext.DebugAbbrevStream; + reader.CurrentSection = dwarf.AbbreviationTable; + dwarf.AbbreviationTable.Read(reader); + } - return dwarf; + if (readerContext.DebugStringStream != null) + { + reader.Stream = readerContext.DebugStringStream; + reader.CurrentSection = dwarf.StringTable; + dwarf.StringTable.Read(reader); } - public static DwarfFile ReadFromElf(DwarfElfContext elfContext) + reader.Log = readerContext.DebugLinePrinter; + if (readerContext.DebugLineStream != null) { - if (elfContext == null) throw new ArgumentNullException(nameof(elfContext)); - return Read(new DwarfReaderContext(elfContext)); + reader.Stream = readerContext.DebugLineStream; + reader.CurrentSection = dwarf.LineSection; + dwarf.LineSection.Read(reader); } + reader.Log = null; - public static DwarfFile ReadFromElf(ElfObjectFile elf) + if (readerContext.DebugAddressRangeStream != null) { - return ReadFromElf(new DwarfElfContext(elf)); + reader.Stream = readerContext.DebugAddressRangeStream; + reader.CurrentSection = dwarf.AddressRangeTable; + dwarf.AddressRangeTable.Read(reader); } - private static void CheckErrors(DiagnosticBag diagnostics) + reader.Log = null; + if (readerContext.DebugLocationStream != null) { - if (diagnostics.HasErrors) - { - throw new ObjectFileException("Unexpected errors while verifying and updating the layout", diagnostics); - } + reader.Stream = readerContext.DebugLocationStream; + reader.CurrentSection = dwarf.LocationSection; + dwarf.LocationSection.Read(reader); } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) + reader.Log = null; + if (readerContext.DebugInfoStream != null) { + reader.Stream = readerContext.DebugInfoStream; + reader.DefaultUnitKind = DwarfUnitKind.Compile; + reader.CurrentSection = dwarf.InfoSection; + dwarf.InfoSection.Read(reader); } - protected override void Write(DwarfWriter writer) + CheckErrors(reader.Diagnostics); + + return dwarf; + } + + public static DwarfFile ReadFromElf(DwarfElfContext elfContext) + { + if (elfContext == null) throw new ArgumentNullException(nameof(elfContext)); + return Read(new DwarfReaderContext(elfContext)); + } + + public static DwarfFile ReadFromElf(ElfObjectFile elf) + { + return ReadFromElf(new DwarfElfContext(elf)); + } + + private static void CheckErrors(DiagnosticBag diagnostics) + { + if (diagnostics.HasErrors) { + throw new ObjectFileException("Unexpected errors while verifying and updating the layout", diagnostics); } } + + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + } + + public override void Write(DwarfWriter writer) + { + } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfFileName.cs b/src/LibObjectFile/Dwarf/DwarfFileName.cs index 10a93c2..e7558da 100644 --- a/src/LibObjectFile/Dwarf/DwarfFileName.cs +++ b/src/LibObjectFile/Dwarf/DwarfFileName.cs @@ -4,31 +4,30 @@ using System.IO; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public sealed class DwarfFileName { - public sealed class DwarfFileName + public DwarfFileName(string name) { - public DwarfFileName(string name) - { - Name = name; - } + Name = name; + } - public string Name { get; } + public string Name { get; } - public string? Directory { get; set; } + public string? Directory { get; set; } - public ulong Time { get; set; } + public ulong Time { get; set; } - public ulong Size { get; set; } + public ulong Size { get; set; } - public override string ToString() + public override string ToString() + { + if (Directory != null) { - if (Directory != null) - { - return Directory.Contains(Path.AltDirectorySeparatorChar) ? $"{Directory}{Path.AltDirectorySeparatorChar}{Name}" : $"{Directory}{Path.DirectorySeparatorChar}{Name}"; - } - - return Name; + return Directory.Contains(Path.AltDirectorySeparatorChar) ? $"{Directory}{Path.AltDirectorySeparatorChar}{Name}" : $"{Directory}{Path.DirectorySeparatorChar}{Name}"; } + + return Name; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfHelper.cs b/src/LibObjectFile/Dwarf/DwarfHelper.cs index 1ec6f08..d403144 100644 --- a/src/LibObjectFile/Dwarf/DwarfHelper.cs +++ b/src/LibObjectFile/Dwarf/DwarfHelper.cs @@ -5,110 +5,109 @@ using System.Text; using System.Numerics; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public static partial class DwarfHelper { - public static partial class DwarfHelper + public static ulong SizeOfStringUTF8NullTerminated(string? text) { - public static ulong SizeOfStringUTF8NullTerminated(string? text) - { - if (text == null) return 0; - return (ulong)Encoding.UTF8.GetByteCount(text) + 1; - } + if (text == null) return 0; + return (ulong)Encoding.UTF8.GetByteCount(text) + 1; + } - public static uint SizeOfUnitLength(bool is64Bit) - { - return is64Bit ? 12U : 4U; - } - - public static uint SizeOfUInt(bool is64Bit) - { - return is64Bit ? 8U : 4U; - } + public static uint SizeOfUnitLength(bool is64Bit) + { + return is64Bit ? 12U : 4U; + } - public static uint SizeOfUInt(DwarfAddressSize addressSize) - { - return (uint)(addressSize); - } + public static uint SizeOfUInt(bool is64Bit) + { + return is64Bit ? 8U : 4U; + } - public static uint SizeOfULEB128(ulong value) - { - // bits_to_encode = (data != 0) ? 64 - CLZ(x) : 1 = 64 - CLZ(data | 1) - // bytes = ceil(bits_to_encode / 7.0); = (6 + bits_to_encode) / 7 - uint x = 6 + 64 - (uint)BitOperations.LeadingZeroCount(value | 1UL); + public static uint SizeOfUInt(DwarfAddressSize addressSize) + { + return (uint)(addressSize); + } - // Division by 7 is done by (x * 37) >> 8 where 37 = ceil(256 / 7). - // This works for 0 <= x < 256 / (7 * 37 - 256), i.e. 0 <= x <= 85. - return (x * 37) >> 8; - } + public static uint SizeOfULEB128(ulong value) + { + // bits_to_encode = (data != 0) ? 64 - CLZ(x) : 1 = 64 - CLZ(data | 1) + // bytes = ceil(bits_to_encode / 7.0); = (6 + bits_to_encode) / 7 + uint x = 6 + 64 - (uint)BitOperations.LeadingZeroCount(value | 1UL); - public static uint SizeOfILEB128(long value) - { - // The same as SizeOfULEB128 calculation but we have to account for the sign bit. - value ^= value >> 63; - uint x = 1 + 6 + 64 - (uint)BitOperations.LeadingZeroCount((ulong)value | 1UL); - return (x * 37) >> 8; - } + // Division by 7 is done by (x * 37) >> 8 where 37 = ceil(256 / 7). + // This works for 0 <= x < 256 / (7 * 37 - 256), i.e. 0 <= x <= 85. + return (x * 37) >> 8; + } - public static DwarfAttributeEncoding GetAttributeEncoding(DwarfAttributeKindEx kind) - { - if ((uint)kind.Value >= AttributeToEncoding.Length) return DwarfAttributeEncoding.None; - return AttributeToEncoding[(int) kind.Value]; - } + public static uint SizeOfILEB128(long value) + { + // The same as SizeOfULEB128 calculation but we have to account for the sign bit. + value ^= value >> 63; + uint x = 1 + 6 + 64 - (uint)BitOperations.LeadingZeroCount((ulong)value | 1UL); + return (x * 37) >> 8; + } - private static readonly DwarfAttributeEncoding[] Encodings = new DwarfAttributeEncoding[] - { - DwarfAttributeEncoding.None , // 0 - DwarfAttributeEncoding.Address , // DW_FORM_addr 0x01 - DwarfAttributeEncoding.None , // Reserved 0x02 - DwarfAttributeEncoding.Block , // DW_FORM_block2 0x03 - DwarfAttributeEncoding.Block , // DW_FORM_block4 0x04 - DwarfAttributeEncoding.Constant , // DW_FORM_data2 0x05 - DwarfAttributeEncoding.Constant , // DW_FORM_data4 0x06 - DwarfAttributeEncoding.Constant , // DW_FORM_data8 0x07 - DwarfAttributeEncoding.String , // DW_FORM_string 0x08 - DwarfAttributeEncoding.Block , // DW_FORM_block 0x09 - DwarfAttributeEncoding.Block , // DW_FORM_block1 0x0a - DwarfAttributeEncoding.Constant , // DW_FORM_data1 0x0b - DwarfAttributeEncoding.Flag , // DW_FORM_flag 0x0c - DwarfAttributeEncoding.Constant , // DW_FORM_sdata 0x0d - DwarfAttributeEncoding.String , // DW_FORM_strp 0x0e - DwarfAttributeEncoding.Constant , // DW_FORM_udata 0x0f - DwarfAttributeEncoding.Reference , // DW_FORM_ref_addr 0x10 - DwarfAttributeEncoding.Reference , // DW_FORM_ref1 0x11 - DwarfAttributeEncoding.Reference , // DW_FORM_ref2 0x12 - DwarfAttributeEncoding.Reference , // DW_FORM_ref4 0x13 - DwarfAttributeEncoding.Reference , // DW_FORM_ref8 0x14 - DwarfAttributeEncoding.Reference , // DW_FORM_ref_udata 0x15 - DwarfAttributeEncoding.Indirect , // DW_FORM_indirect 0x16 - DwarfAttributeEncoding.AddressPointer | - DwarfAttributeEncoding.LinePointer | - DwarfAttributeEncoding.LocationList | - DwarfAttributeEncoding.LocationListsPointer | - DwarfAttributeEncoding.MacroPointer | - DwarfAttributeEncoding.RangeList | - DwarfAttributeEncoding.RangeListsPointer | - DwarfAttributeEncoding.StringOffsetPointer, // DW_FORM_sec_offset 0x17 - DwarfAttributeEncoding.ExpressionLocation , // DW_FORM_exprloc 0x18 - DwarfAttributeEncoding.Flag , // DW_FORM_flag_present 0x19 - DwarfAttributeEncoding.String , // DW_FORM_strx 0x1a - DwarfAttributeEncoding.Address , // DW_FORM_addrx 0x1b - DwarfAttributeEncoding.Reference , // DW_FORM_ref_sup4 0x1c - DwarfAttributeEncoding.String , // DW_FORM_strp_sup 0x1d - DwarfAttributeEncoding.Constant , // DW_FORM_data16 0x1e - DwarfAttributeEncoding.String , // DW_FORM_line_strp 0x1f - DwarfAttributeEncoding.Reference , // DW_FORM_ref_sig8 0x20 - DwarfAttributeEncoding.Constant , // DW_FORM_implicit_const 0x21 - DwarfAttributeEncoding.LocationList , // DW_FORM_loclistx 0x22 - DwarfAttributeEncoding.RangeList , // DW_FORM_rnglistx 0x23 - DwarfAttributeEncoding.Reference , // DW_FORM_ref_sup8 0x24 - DwarfAttributeEncoding.String , // DW_FORM_strx1 0x25 - DwarfAttributeEncoding.String , // DW_FORM_strx2 0x26 - DwarfAttributeEncoding.String , // DW_FORM_strx3 0x27 - DwarfAttributeEncoding.String , // DW_FORM_strx4 0x28 - DwarfAttributeEncoding.Address , // DW_FORM_addrx1 0x29 - DwarfAttributeEncoding.Address , // DW_FORM_addrx2 0x2a - DwarfAttributeEncoding.Address , // DW_FORM_addrx3 0x2b - DwarfAttributeEncoding.Address , // DW_FORM_addrx4 0x2c - }; + public static DwarfAttributeEncoding GetAttributeEncoding(DwarfAttributeKindEx kind) + { + if ((uint)kind.Value >= AttributeToEncoding.Length) return DwarfAttributeEncoding.None; + return AttributeToEncoding[(int) kind.Value]; } + + private static readonly DwarfAttributeEncoding[] Encodings = new DwarfAttributeEncoding[] + { + DwarfAttributeEncoding.None , // 0 + DwarfAttributeEncoding.Address , // DW_FORM_addr 0x01 + DwarfAttributeEncoding.None , // Reserved 0x02 + DwarfAttributeEncoding.Block , // DW_FORM_block2 0x03 + DwarfAttributeEncoding.Block , // DW_FORM_block4 0x04 + DwarfAttributeEncoding.Constant , // DW_FORM_data2 0x05 + DwarfAttributeEncoding.Constant , // DW_FORM_data4 0x06 + DwarfAttributeEncoding.Constant , // DW_FORM_data8 0x07 + DwarfAttributeEncoding.String , // DW_FORM_string 0x08 + DwarfAttributeEncoding.Block , // DW_FORM_block 0x09 + DwarfAttributeEncoding.Block , // DW_FORM_block1 0x0a + DwarfAttributeEncoding.Constant , // DW_FORM_data1 0x0b + DwarfAttributeEncoding.Flag , // DW_FORM_flag 0x0c + DwarfAttributeEncoding.Constant , // DW_FORM_sdata 0x0d + DwarfAttributeEncoding.String , // DW_FORM_strp 0x0e + DwarfAttributeEncoding.Constant , // DW_FORM_udata 0x0f + DwarfAttributeEncoding.Reference , // DW_FORM_ref_addr 0x10 + DwarfAttributeEncoding.Reference , // DW_FORM_ref1 0x11 + DwarfAttributeEncoding.Reference , // DW_FORM_ref2 0x12 + DwarfAttributeEncoding.Reference , // DW_FORM_ref4 0x13 + DwarfAttributeEncoding.Reference , // DW_FORM_ref8 0x14 + DwarfAttributeEncoding.Reference , // DW_FORM_ref_udata 0x15 + DwarfAttributeEncoding.Indirect , // DW_FORM_indirect 0x16 + DwarfAttributeEncoding.AddressPointer | + DwarfAttributeEncoding.LinePointer | + DwarfAttributeEncoding.LocationList | + DwarfAttributeEncoding.LocationListsPointer | + DwarfAttributeEncoding.MacroPointer | + DwarfAttributeEncoding.RangeList | + DwarfAttributeEncoding.RangeListsPointer | + DwarfAttributeEncoding.StringOffsetPointer, // DW_FORM_sec_offset 0x17 + DwarfAttributeEncoding.ExpressionLocation , // DW_FORM_exprloc 0x18 + DwarfAttributeEncoding.Flag , // DW_FORM_flag_present 0x19 + DwarfAttributeEncoding.String , // DW_FORM_strx 0x1a + DwarfAttributeEncoding.Address , // DW_FORM_addrx 0x1b + DwarfAttributeEncoding.Reference , // DW_FORM_ref_sup4 0x1c + DwarfAttributeEncoding.String , // DW_FORM_strp_sup 0x1d + DwarfAttributeEncoding.Constant , // DW_FORM_data16 0x1e + DwarfAttributeEncoding.String , // DW_FORM_line_strp 0x1f + DwarfAttributeEncoding.Reference , // DW_FORM_ref_sig8 0x20 + DwarfAttributeEncoding.Constant , // DW_FORM_implicit_const 0x21 + DwarfAttributeEncoding.LocationList , // DW_FORM_loclistx 0x22 + DwarfAttributeEncoding.RangeList , // DW_FORM_rnglistx 0x23 + DwarfAttributeEncoding.Reference , // DW_FORM_ref_sup8 0x24 + DwarfAttributeEncoding.String , // DW_FORM_strx1 0x25 + DwarfAttributeEncoding.String , // DW_FORM_strx2 0x26 + DwarfAttributeEncoding.String , // DW_FORM_strx3 0x27 + DwarfAttributeEncoding.String , // DW_FORM_strx4 0x28 + DwarfAttributeEncoding.Address , // DW_FORM_addrx1 0x29 + DwarfAttributeEncoding.Address , // DW_FORM_addrx2 0x2a + DwarfAttributeEncoding.Address , // DW_FORM_addrx3 0x2b + DwarfAttributeEncoding.Address , // DW_FORM_addrx4 0x2c + }; } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfIdentifierCaseKind.cs b/src/LibObjectFile/Dwarf/DwarfIdentifierCaseKind.cs index 5358d88..1c61d00 100644 --- a/src/LibObjectFile/Dwarf/DwarfIdentifierCaseKind.cs +++ b/src/LibObjectFile/Dwarf/DwarfIdentifierCaseKind.cs @@ -2,16 +2,15 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public enum DwarfIdentifierCaseKind : byte { - public enum DwarfIdentifierCaseKind : byte - { - Sensitive = DwarfNative.DW_ID_case_sensitive, + Sensitive = DwarfNative.DW_ID_case_sensitive, - UpCase = DwarfNative.DW_ID_up_case, + UpCase = DwarfNative.DW_ID_up_case, - DownCase = DwarfNative.DW_ID_down_case, + DownCase = DwarfNative.DW_ID_down_case, - Insensitive = DwarfNative.DW_ID_case_insensitive, - } + Insensitive = DwarfNative.DW_ID_case_insensitive, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs index 2d7a4f0..2cd746d 100644 --- a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs @@ -6,101 +6,98 @@ using System.Diagnostics; using LibObjectFile.Utils; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public sealed class DwarfInfoSection : DwarfRelocatableSection { - public sealed class DwarfInfoSection : DwarfRelocatableSection - { - private readonly List _units; + private readonly List _units; - public DwarfInfoSection() - { - _units = new List(); - } + public DwarfInfoSection() + { + _units = new List(); + } - public ReadOnlyList Units => _units; + public ReadOnlyList Units => _units; - public void AddUnit(DwarfUnit unit) - { - _units.Add(this, unit); - } + public void AddUnit(DwarfUnit unit) + { + _units.Add(this, unit); + } - public void RemoveUnit(DwarfUnit unit) - { - _units.Remove(this, unit); - } + public void RemoveUnit(DwarfUnit unit) + { + _units.Remove(this, unit); + } - public DwarfUnit RemoveUnitAt(int index) - { - return _units.RemoveAt(this, index); - } + public DwarfUnit RemoveUnitAt(int index) + { + return _units.RemoveAt(this, index); + } - protected override void Read(DwarfReader reader) - { - var addressRangeTable = reader.File.AddressRangeTable; + public override void Read(DwarfReader reader) + { + var addressRangeTable = reader.File.AddressRangeTable; - while (reader.Position < reader.Length) - { - // 7.5 Format of Debugging Information - // - Each such contribution consists of a compilation unit header + while (reader.Position < reader.Length) + { + // 7.5 Format of Debugging Information + // - Each such contribution consists of a compilation unit header - var startOffset = Position; + var startOffset = Position; - reader.ClearResolveAttributeReferenceWithinCompilationUnit(); + reader.ClearResolveAttributeReferenceWithinCompilationUnit(); - var cu = DwarfUnit.ReadInstance(reader, out var offsetEndOfUnit); - if (cu == null) - { - reader.Position = offsetEndOfUnit; - continue; - } + var cu = DwarfUnit.ReadInstance(reader, out var offsetEndOfUnit); + if (cu == null) + { + reader.Position = offsetEndOfUnit; + continue; + } - reader.CurrentUnit = cu; + reader.CurrentUnit = cu; - // Link AddressRangeTable to Unit - if (addressRangeTable.DebugInfoOffset == cu.Position) - { - addressRangeTable.Unit = cu; - } - - AddUnit(cu); + // Link AddressRangeTable to Unit + if (addressRangeTable.DebugInfoOffset == cu.Position) + { + addressRangeTable.Unit = cu; } - - reader.ResolveAttributeReferenceWithinSection(); + + AddUnit(cu); } - public override void Verify(DiagnosticBag diagnostics) - { - base.Verify(diagnostics); + reader.ResolveAttributeReferenceWithinSection(); + } - foreach (var unit in _units) - { - unit.Verify(diagnostics); - } + public override void Verify(DwarfVerifyContext context) + { + foreach (var unit in _units) + { + unit.Verify(context); } + } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + var offset = Position; + foreach (var unit in Units) { - var offset = Position; - foreach (var unit in Units) - { - layoutContext.CurrentUnit = unit; - unit.Position = offset; - unit.UpdateLayoutInternal(layoutContext); - offset += unit.Size; - } - Size = offset - Position; + layoutContext.CurrentUnit = unit; + unit.Position = offset; + unit.UpdateLayout(layoutContext); + offset += unit.Size; } + Size = offset - Position; + } - protected override void Write(DwarfWriter writer) + public override void Write(DwarfWriter writer) + { + Debug.Assert(Position == writer.Position); + foreach (var unit in _units) { - Debug.Assert(Position == writer.Position); - foreach (var unit in _units) - { - writer.CurrentUnit = unit; - unit.WriteInternal(writer); - } - writer.CurrentUnit = null; - Debug.Assert(Size == writer.Position - Position); + writer.CurrentUnit = unit; + unit.Write(writer); } + writer.CurrentUnit = null; + Debug.Assert(Size == writer.Position - Position); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfInlineKind.cs b/src/LibObjectFile/Dwarf/DwarfInlineKind.cs index a7b48d8..e6a38c4 100644 --- a/src/LibObjectFile/Dwarf/DwarfInlineKind.cs +++ b/src/LibObjectFile/Dwarf/DwarfInlineKind.cs @@ -2,16 +2,15 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public enum DwarfInlineKind : byte { - public enum DwarfInlineKind : byte - { - NotInlined = DwarfNative.DW_INL_not_inlined, + NotInlined = DwarfNative.DW_INL_not_inlined, - Inlined = DwarfNative.DW_INL_inlined, + Inlined = DwarfNative.DW_INL_inlined, - DeclaredNotInlined = DwarfNative.DW_INL_declared_not_inlined, + DeclaredNotInlined = DwarfNative.DW_INL_declared_not_inlined, - DeclaredInlined = DwarfNative.DW_INL_declared_inlined, - } + DeclaredInlined = DwarfNative.DW_INL_declared_inlined, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfInteger.cs b/src/LibObjectFile/Dwarf/DwarfInteger.cs index e00d5d2..0ac666d 100644 --- a/src/LibObjectFile/Dwarf/DwarfInteger.cs +++ b/src/LibObjectFile/Dwarf/DwarfInteger.cs @@ -4,38 +4,37 @@ using System.Runtime.InteropServices; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[StructLayout(LayoutKind.Explicit)] +public struct DwarfInteger { - [StructLayout(LayoutKind.Explicit)] - public struct DwarfInteger - { - [FieldOffset(0)] - public ulong U64; + [FieldOffset(0)] + public ulong U64; - [FieldOffset(0)] - public long I64; + [FieldOffset(0)] + public long I64; - [FieldOffset(0)] - public sbyte I8; + [FieldOffset(0)] + public sbyte I8; - [FieldOffset(0)] - public byte U8; + [FieldOffset(0)] + public byte U8; - [FieldOffset(0)] - public short I16; + [FieldOffset(0)] + public short I16; - [FieldOffset(0)] - public ushort U16; + [FieldOffset(0)] + public ushort U16; - [FieldOffset(0)] - public int I32; + [FieldOffset(0)] + public int I32; - [FieldOffset(0)] - public uint U32; + [FieldOffset(0)] + public uint U32; - public override string ToString() - { - return $"0x{U64:x16}"; - } + public override string ToString() + { + return $"0x{U64:x16}"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLanguageKindEx.cs b/src/LibObjectFile/Dwarf/DwarfLanguageKindEx.cs index 887cd6e..1083cef 100644 --- a/src/LibObjectFile/Dwarf/DwarfLanguageKindEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfLanguageKindEx.cs @@ -4,56 +4,55 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public readonly partial struct DwarfLanguageKindEx : IEquatable { - public readonly partial struct DwarfLanguageKindEx : IEquatable - { - public DwarfLanguageKindEx(ushort value) - { - Value = (DwarfLanguageKind)value; - } - - public DwarfLanguageKindEx(DwarfLanguageKind value) - { - Value = value; - } - - public readonly DwarfLanguageKind Value; - - public override string ToString() - { - return ToStringInternal() ?? $"Unknown {nameof(DwarfLanguageKind)} (0x{Value:x4})"; - } - - public bool Equals(DwarfLanguageKindEx other) - { - return Value == other.Value; - } - - public override bool Equals(object? obj) - { - return obj is DwarfLanguageKindEx other && Equals(other); - } - - public override int GetHashCode() - { - return Value.GetHashCode(); - } - - public static bool operator ==(DwarfLanguageKindEx left, DwarfLanguageKindEx right) - { - return left.Equals(right); - } - - public static bool operator !=(DwarfLanguageKindEx left, DwarfLanguageKindEx right) - { - return !left.Equals(right); - } - - public static explicit operator uint(DwarfLanguageKindEx kind) => (uint)kind.Value; + public DwarfLanguageKindEx(ushort value) + { + Value = (DwarfLanguageKind)value; + } + + public DwarfLanguageKindEx(DwarfLanguageKind value) + { + Value = value; + } + + public readonly DwarfLanguageKind Value; + + public override string ToString() + { + return ToStringInternal() ?? $"Unknown {nameof(DwarfLanguageKind)} (0x{Value:x4})"; + } + + public bool Equals(DwarfLanguageKindEx other) + { + return Value == other.Value; + } - public static implicit operator DwarfLanguageKindEx(DwarfLanguageKind kind) => new DwarfLanguageKindEx(kind); + public override bool Equals(object? obj) + { + return obj is DwarfLanguageKindEx other && Equals(other); + } - public static implicit operator DwarfLanguageKind(DwarfLanguageKindEx kind) => kind.Value; + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static bool operator ==(DwarfLanguageKindEx left, DwarfLanguageKindEx right) + { + return left.Equals(right); } + + public static bool operator !=(DwarfLanguageKindEx left, DwarfLanguageKindEx right) + { + return !left.Equals(right); + } + + public static explicit operator uint(DwarfLanguageKindEx kind) => (uint)kind.Value; + + public static implicit operator DwarfLanguageKindEx(DwarfLanguageKind kind) => new DwarfLanguageKindEx(kind); + + public static implicit operator DwarfLanguageKind(DwarfLanguageKindEx kind) => kind.Value; } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLayoutConfig.cs b/src/LibObjectFile/Dwarf/DwarfLayoutConfig.cs index 728d7f6..b5e9502 100644 --- a/src/LibObjectFile/Dwarf/DwarfLayoutConfig.cs +++ b/src/LibObjectFile/Dwarf/DwarfLayoutConfig.cs @@ -4,34 +4,33 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfLayoutConfig { - public class DwarfLayoutConfig - { - private DwarfAttributeForm _defaultAttributeFormForReference; + private DwarfAttributeForm _defaultAttributeFormForReference; - public DwarfLayoutConfig() - { - DefaultAttributeFormForReference = DwarfAttributeForm.Ref4; - } + public DwarfLayoutConfig() + { + DefaultAttributeFormForReference = DwarfAttributeForm.Ref4; + } - public DwarfAttributeFormEx DefaultAttributeFormForReference + public DwarfAttributeFormEx DefaultAttributeFormForReference + { + get => _defaultAttributeFormForReference; + set { - get => _defaultAttributeFormForReference; - set + switch (value.Value) { - switch (value.Value) - { - case DwarfAttributeForm.Ref1: - case DwarfAttributeForm.Ref2: - case DwarfAttributeForm.Ref4: - break; - default: - throw new ArgumentOutOfRangeException(nameof(value)); - } - - _defaultAttributeFormForReference = value; + case DwarfAttributeForm.Ref1: + case DwarfAttributeForm.Ref2: + case DwarfAttributeForm.Ref4: + break; + default: + throw new ArgumentOutOfRangeException(nameof(value)); } + + _defaultAttributeFormForReference = value; } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs b/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs index e55b2a6..da6cc10 100644 --- a/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs @@ -2,25 +2,33 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + + + +public abstract class DwarfVisitorContext : VisitorContextBase { - public sealed class DwarfLayoutContext + internal DwarfVisitorContext(DwarfFile file, DiagnosticBag diagnostics) : base(file, diagnostics) { - internal DwarfLayoutContext(DwarfFile file, DwarfLayoutConfig config, DiagnosticBag diagnostics) - { - File = file; - Config = config; - Diagnostics = diagnostics; - } + } +} - public DwarfFile File { get; } - public DiagnosticBag Diagnostics { get; } +public sealed class DwarfLayoutContext : DwarfVisitorContext +{ + internal DwarfLayoutContext(DwarfFile file, DwarfLayoutConfig config, DiagnosticBag diagnostics) : base(file, diagnostics) + { + Config = config; + } - public DwarfLayoutConfig Config { get; } + public DwarfLayoutConfig Config { get; } + + public DwarfUnit? CurrentUnit { get; internal set; } +} - public bool HasErrors => Diagnostics.HasErrors; - - public DwarfUnit? CurrentUnit { get; internal set; } +public sealed class DwarfVerifyContext : DwarfVisitorContext +{ + internal DwarfVerifyContext(DwarfFile file, DiagnosticBag diagnostics) : base(file, diagnostics) + { } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLine.cs b/src/LibObjectFile/Dwarf/DwarfLine.cs index 4f4f7e2..e2528d8 100644 --- a/src/LibObjectFile/Dwarf/DwarfLine.cs +++ b/src/LibObjectFile/Dwarf/DwarfLine.cs @@ -3,182 +3,185 @@ // See the license.txt file in the project root for more information. using System.Globalization; +using System.Text; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfLine : DwarfObject { - public class DwarfLine : DwarfObject + public DwarfLine() { - public DwarfLine() - { - IsStatement = true; - } + IsStatement = true; + } - // ----------------------- - // DWARF 2 - // ----------------------- + // ----------------------- + // DWARF 2 + // ----------------------- - /// - /// The program-counter value corresponding to a machine instruction generated by the compiler. - /// - public ulong Address { get; set; } + /// + /// The program-counter value corresponding to a machine instruction generated by the compiler. + /// + public ulong Address { get; set; } - /// - /// An unsigned integer representing the index of an operation within a VLIW instruction. - /// The index of the first operation is 0. For non-VLIW architectures, this register will always be 0. - /// - public byte OperationIndex { get; set; } - - /// - /// The identity of the source file corresponding to a machine instruction. - /// - public DwarfFileName? File { get; set; } - - /// - /// An unsigned integer indicating a source line number. - /// Lines are numbered beginning at 1. - /// The compiler may emit the value 0 in cases where an instruction cannot be attributed to any source line. - /// - public uint Line { get; set; } - - /// - /// An unsigned integer indicating a column number within a source line. - /// Columns are numbered beginning at 1. The value 0 is reserved to indicate that a statement begins at the “left edge” of the line. - /// - public uint Column { get; set; } - - /// - /// A boolean indicating that the current instruction is a recommended breakpoint location. - /// A recommended breakpoint location is intended to “represent” a line, a statement and/or a semantically distinct subpart of a statement. - /// - public bool IsStatement { get; set; } - - /// - /// A boolean indicating that the current instruction is the beginning of a basic block. - /// - public bool IsBasicBlock { get; set; } - - // ----------------------- + /// + /// An unsigned integer representing the index of an operation within a VLIW instruction. + /// The index of the first operation is 0. For non-VLIW architectures, this register will always be 0. + /// + public byte OperationIndex { get; set; } + + /// + /// The identity of the source file corresponding to a machine instruction. + /// + public DwarfFileName? File { get; set; } + + /// + /// An unsigned integer indicating a source line number. + /// Lines are numbered beginning at 1. + /// The compiler may emit the value 0 in cases where an instruction cannot be attributed to any source line. + /// + public uint Line { get; set; } + + /// + /// An unsigned integer indicating a column number within a source line. + /// Columns are numbered beginning at 1. The value 0 is reserved to indicate that a statement begins at the “left edge” of the line. + /// + public uint Column { get; set; } + + /// + /// A boolean indicating that the current instruction is a recommended breakpoint location. + /// A recommended breakpoint location is intended to “represent” a line, a statement and/or a semantically distinct subpart of a statement. + /// + public bool IsStatement { get; set; } + + /// + /// A boolean indicating that the current instruction is the beginning of a basic block. + /// + public bool IsBasicBlock { get; set; } + + // ----------------------- + // DWARF 3 + // ----------------------- + + /// + /// A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an entry breakpoint of a function. + /// + public bool IsPrologueEnd { get; set; } + + /// + /// A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an exit breakpoint of a function. + /// + public bool IsEpilogueBegin { get; set; } + + /// + /// An unsigned integer whose value encodes the applicable instruction set architecture for the current instruction. + /// + public ulong Isa { get; set; } + + // ----------------------- + // DWARF 4 + // ----------------------- + + /// + /// An unsigned integer identifying the block to which the current instruction belongs. + /// Discriminator values are assigned arbitrarily by the DWARF producer and serve to distinguish among multiple blocks that may all be + /// associated with the same source file, line, and column. + /// Where only one block exists for a given source position, the discriminator value should be zero. + /// + public ulong Discriminator { get; set; } + + public DwarfLine Clone() + { + return (DwarfLine) MemberwiseClone(); + } + + internal void Delta(DwarfLine against, out ulong deltaAddress, + out byte deltaOperationIndex, + out bool fileNameChanged, + out int deltaLine, + out int deltaColumn, + out bool isStatementChanged, + out bool isBasicBlockChanged, + out bool isPrologueEndChanged, + out bool isEpilogueBeginChanged, + out bool isaChanged, + out bool isDiscriminatorChanged) + { + deltaAddress = against.Address - this.Address; + deltaOperationIndex = (byte)(against.OperationIndex - this.OperationIndex); + fileNameChanged = !ReferenceEquals(this.File, against.File); + deltaLine = (int) ((long) against.Line - (long) this.Line); + deltaColumn = (int) ((long) against.Column - (long) this.Column); + isStatementChanged = against.IsStatement != this.IsStatement; + isBasicBlockChanged = against.IsBasicBlock != this.IsBasicBlock; + isPrologueEndChanged = against.IsPrologueEnd != this.IsPrologueEnd; + isEpilogueBeginChanged = against.IsEpilogueBegin != this.IsEpilogueBegin; + isaChanged = against.Isa != this.Isa; + isDiscriminatorChanged = against.Discriminator != this.Discriminator; + } + + internal void Reset(DwarfFileName? firstFile, bool isStatement) + { + Address = 0; + File = firstFile; + Line = 1; + Column = 0; + this.IsStatement = isStatement; + IsBasicBlock = false; + // DWARF 3 - // ----------------------- - - /// - /// A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an entry breakpoint of a function. - /// - public bool IsPrologueEnd { get; set; } - - /// - /// A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an exit breakpoint of a function. - /// - public bool IsEpilogueBegin { get; set; } - - /// - /// An unsigned integer whose value encodes the applicable instruction set architecture for the current instruction. - /// - public ulong Isa { get; set; } - - // ----------------------- - // DWARF 4 - // ----------------------- - - /// - /// An unsigned integer identifying the block to which the current instruction belongs. - /// Discriminator values are assigned arbitrarily by the DWARF producer and serve to distinguish among multiple blocks that may all be - /// associated with the same source file, line, and column. - /// Where only one block exists for a given source position, the discriminator value should be zero. - /// - public ulong Discriminator { get; set; } - - public DwarfLine Clone() - { - return (DwarfLine) MemberwiseClone(); - } - - internal void Delta(DwarfLine against, out ulong deltaAddress, - out byte deltaOperationIndex, - out bool fileNameChanged, - out int deltaLine, - out int deltaColumn, - out bool isStatementChanged, - out bool isBasicBlockChanged, - out bool isPrologueEndChanged, - out bool isEpilogueBeginChanged, - out bool isaChanged, - out bool isDiscriminatorChanged) - { - deltaAddress = against.Address - this.Address; - deltaOperationIndex = (byte)(against.OperationIndex - this.OperationIndex); - fileNameChanged = !ReferenceEquals(this.File, against.File); - deltaLine = (int) ((long) against.Line - (long) this.Line); - deltaColumn = (int) ((long) against.Column - (long) this.Column); - isStatementChanged = against.IsStatement != this.IsStatement; - isBasicBlockChanged = against.IsBasicBlock != this.IsBasicBlock; - isPrologueEndChanged = against.IsPrologueEnd != this.IsPrologueEnd; - isEpilogueBeginChanged = against.IsEpilogueBegin != this.IsEpilogueBegin; - isaChanged = against.Isa != this.Isa; - isDiscriminatorChanged = against.Discriminator != this.Discriminator; - } - - internal void Reset(DwarfFileName? firstFile, bool isStatement) - { - Address = 0; - File = firstFile; - Line = 1; - Column = 0; - this.IsStatement = isStatement; - IsBasicBlock = false; - - // DWARF 3 - IsPrologueEnd = false; - IsEpilogueBegin = false; - Isa = 0; - - // DWARF 5 - Discriminator = 0; - } - - internal void SpecialReset() - { - IsBasicBlock = false; - IsPrologueEnd = false; - IsEpilogueBegin = false; - Discriminator = 0; - } + IsPrologueEnd = false; + IsEpilogueBegin = false; + Isa = 0; + + // DWARF 5 + Discriminator = 0; + } + + internal void SpecialReset() + { + IsBasicBlock = false; + IsPrologueEnd = false; + IsEpilogueBegin = false; + Discriminator = 0; + } - internal DwarfLineState ToState() + internal DwarfLineState ToState() + { + return new DwarfLineState() { - return new DwarfLineState() - { - Address = Address, - Column = Column, - Discriminator = Discriminator, - File = File, - Isa = Isa, - IsBasicBlock = IsBasicBlock, - IsEpilogueBegin = IsEpilogueBegin, - IsPrologueEnd = IsPrologueEnd, - IsStatement = IsStatement, - OperationIndex = OperationIndex, - Line = Line, - }; - } + Address = Address, + Column = Column, + Discriminator = Discriminator, + File = File, + Isa = Isa, + IsBasicBlock = IsBasicBlock, + IsEpilogueBegin = IsEpilogueBegin, + IsPrologueEnd = IsPrologueEnd, + IsStatement = IsStatement, + OperationIndex = OperationIndex, + Line = Line, + }; + } - public override string ToString() - { - return $"{nameof(Position)}: 0x{Position:x8}, {nameof(Address)}: 0x{Address:x16}, {nameof(File)}: {File}, {nameof(Line)}: {Line,4}, {nameof(Column)}: {Column,2}, {nameof(IsStatement)}: {Bool2Str(IsStatement),5}, {nameof(IsBasicBlock)}: {Bool2Str(IsBasicBlock),5}, {nameof(IsPrologueEnd)}: {Bool2Str(IsPrologueEnd),5}, {nameof(IsEpilogueBegin)}: {Bool2Str(IsEpilogueBegin),5}, {nameof(Isa)}: {Isa,3}, {nameof(Discriminator)}: {Discriminator,3}"; - } - private static string Bool2Str(bool value) => value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(); + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append( + $"{nameof(Position)}: 0x{Position:x8}, {nameof(Address)}: 0x{Address:x16}, {nameof(File)}: {File}, {nameof(Line)}: {Line,4}, {nameof(Column)}: {Column,2}, {nameof(IsStatement)}: {Bool2Str(IsStatement),5}, {nameof(IsBasicBlock)}: {Bool2Str(IsBasicBlock),5}, {nameof(IsPrologueEnd)}: {Bool2Str(IsPrologueEnd),5}, {nameof(IsEpilogueBegin)}: {Bool2Str(IsEpilogueBegin),5}, {nameof(Isa)}: {Isa,3}, {nameof(Discriminator)}: {Discriminator,3}"); + return true; + } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - } + private static string Bool2Str(bool value) => value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(); - protected override void Read(DwarfReader reader) - { - } - - protected override void Write(DwarfWriter writer) - { - } + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + } + + public override void Read(DwarfReader reader) + { + } + + public override void Write(DwarfWriter writer) + { } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs index f78d8d9..4826b47 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs @@ -1,5 +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. @@ -10,1379 +9,1379 @@ using System.Text; using LibObjectFile.Utils; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[DebuggerDisplay("Count = {LineSequences.Count,nq}")] +public sealed class DwarfLineProgramTable : DwarfObject { - [DebuggerDisplay("Count = {LineSequences.Count,nq}")] - public sealed class DwarfLineProgramTable : DwarfObject + private readonly Dictionary _directoryNameToIndex; + private readonly Dictionary _fileNameToIndex; + private readonly List _directoryNames; + private readonly List _lineSequences; + private readonly DwarfLine _stateLine; + private byte _minimumInstructionLength; + private byte _maximumOperationsPerInstruction; + private readonly List _standardOpCodeLengths; + + public DwarfLineProgramTable() { - private readonly Dictionary _directoryNameToIndex; - private readonly Dictionary _fileNameToIndex; - private readonly List _directoryNames; - private readonly List _lineSequences; - private readonly DwarfLine _stateLine; - private byte _minimumInstructionLength; - private byte _maximumOperationsPerInstruction; - private readonly List _standardOpCodeLengths; - - public DwarfLineProgramTable() + FileNames = new List(); + _lineSequences = new List(); + _directoryNameToIndex = new Dictionary(); + _fileNameToIndex = new Dictionary(); + _directoryNames = new List(); + _stateLine = new DwarfLine(); + Version = 2; + LineBase = -5; + LineRange = 14; + _minimumInstructionLength = 1; + _maximumOperationsPerInstruction = 1; + _standardOpCodeLengths = new List(); + foreach (var stdOpCode in DefaultStandardOpCodeLengths) { - FileNames = new List(); - _lineSequences = new List(); - _directoryNameToIndex = new Dictionary(); - _fileNameToIndex = new Dictionary(); - _directoryNames = new List(); - _stateLine = new DwarfLine(); - Version = 2; - LineBase = -5; - LineRange = 14; - _minimumInstructionLength = 1; - _maximumOperationsPerInstruction = 1; - _standardOpCodeLengths = new List(); - foreach (var stdOpCode in DefaultStandardOpCodeLengths) - { - _standardOpCodeLengths.Add(stdOpCode); - } + _standardOpCodeLengths.Add(stdOpCode); } + } - public bool Is64BitEncoding { get; set; } + public bool Is64BitEncoding { get; set; } - public DwarfAddressSize AddressSize { get; set; } + public DwarfAddressSize AddressSize { get; set; } - public ushort Version { get; set; } + public ushort Version { get; set; } - public sbyte LineBase { get; set; } + public sbyte LineBase { get; set; } - public byte LineRange { get; set; } + public byte LineRange { get; set; } - public ulong HeaderLength { get; private set; } + public ulong HeaderLength { get; private set; } - public List StandardOpCodeLengths => _standardOpCodeLengths; + public List StandardOpCodeLengths => _standardOpCodeLengths; - public byte OpCodeBase + public byte OpCodeBase + { + get => (byte)(StandardOpCodeLengths.Count + 1); + } + + public byte MinimumInstructionLength + { + get => _minimumInstructionLength; + set { - get => (byte)(StandardOpCodeLengths.Count + 1); + if (value == 0) throw new ArgumentOutOfRangeException(nameof(value), "Must be > 0"); + _minimumInstructionLength = value; } + } - public byte MinimumInstructionLength + public byte MaximumOperationsPerInstruction + { + get => _maximumOperationsPerInstruction; + set { - get => _minimumInstructionLength; - set - { - if (value == 0) throw new ArgumentOutOfRangeException(nameof(value), "Must be > 0"); - _minimumInstructionLength = value; - } + if (value == 0) throw new ArgumentOutOfRangeException(nameof(value), "Must be > 0"); + _maximumOperationsPerInstruction = value; } + } - public byte MaximumOperationsPerInstruction - { - get => _maximumOperationsPerInstruction; - set - { - if (value == 0) throw new ArgumentOutOfRangeException(nameof(value), "Must be > 0"); - _maximumOperationsPerInstruction = value; - } - } + public List FileNames { get; } - public List FileNames { get; } + public ReadOnlyList LineSequences => _lineSequences; - public ReadOnlyList LineSequences => _lineSequences; + public void AddLineSequence(DwarfLineSequence line) + { + _lineSequences.Add(this, line); + } - public void AddLineSequence(DwarfLineSequence line) + public void RemoveLineSequence(DwarfLineSequence line) + { + _lineSequences.Remove(this, line); + } + + public DwarfLineSequence RemoveLineSequenceAt(int index) + { + return _lineSequences.RemoveAt(this, index); + } + + public override void Read(DwarfReader reader) + { + var log = reader.Log; + var startOfSection = reader.Position; + + reader.OffsetToLineProgramTable.Add(startOfSection, this); + + var unitLength = reader.ReadUnitLength(); + Is64BitEncoding = reader.Is64BitEncoding; + AddressSize = reader.AddressSize; + var startPosition = reader.Position; + var version = reader.ReadU16(); + + if (version < 2 || version >= 5) { - _lineSequences.Add(this, line); + throw new NotSupportedException($"Version .debug_line {version} not supported"); } - public void RemoveLineSequence(DwarfLineSequence line) + Version = version; + + var header_length = reader.ReadUIntFromEncoding(); + HeaderLength = header_length; + var minimum_instruction_length = reader.ReadU8(); + MinimumInstructionLength = minimum_instruction_length; + + byte maximum_operations_per_instruction = 1; + if (version >= 4) { - _lineSequences.Remove(this, line); + maximum_operations_per_instruction = reader.ReadU8(); } - - public DwarfLineSequence RemoveLineSequenceAt(int index) + MaximumOperationsPerInstruction = maximum_operations_per_instruction; + + var default_is_stmt = reader.ReadU8(); + var line_base = reader.ReadI8(); + LineBase = line_base; + var line_range = reader.ReadU8(); + LineRange = line_range; + var opcode_base = reader.ReadU8(); + + if (log != null) { - return _lineSequences.RemoveAt(this, index); + log.WriteLine(); + log.WriteLine($" Offset: 0x{startOfSection:x}"); + log.WriteLine($" Length: {unitLength}"); + log.WriteLine($" DWARF Version: {Version}"); + log.WriteLine($" Prologue Length: {header_length}"); + log.WriteLine($" Minimum Instruction Length: {minimum_instruction_length}"); + if (Version >= 4) + { + log.WriteLine($" Maximum Operations Per Instruction: {maximum_operations_per_instruction}"); + } + log.WriteLine($" Initial value of 'is_stmt': {default_is_stmt}"); + log.WriteLine($" Line Base: {line_base}"); + log.WriteLine($" Line Range: {line_range}"); + log.WriteLine($" Opcode Base: {opcode_base}"); } - - protected override void Read(DwarfReader reader) + + _standardOpCodeLengths.Clear(); + for (int i = 1; i < opcode_base; i++) { - var log = reader.Log; - var startOfSection = reader.Position; - - reader.OffsetToLineProgramTable.Add(startOfSection, this); - - var unitLength = reader.ReadUnitLength(); - Is64BitEncoding = reader.Is64BitEncoding; - AddressSize = reader.AddressSize; - var startPosition = reader.Position; - var version = reader.ReadU16(); - - if (version < 2 || version >= 5) + var opcode_length = reader.ReadULEB128AsU32(); + _standardOpCodeLengths.Add((uint)opcode_length); + if (i - 1 <= DefaultStandardOpCodeLengths.Length && opcode_length != DefaultStandardOpCodeLengths[i - 1]) { - throw new NotSupportedException($"Version .debug_line {version} not supported"); + reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The standard opcode length at [{i}] = {opcode_length} is different from the expected length {DefaultStandardOpCodeLengths[i - 1]}"); } + } - Version = version; - - var header_length = reader.ReadUIntFromEncoding(); - HeaderLength = header_length; - var minimum_instruction_length = reader.ReadU8(); - MinimumInstructionLength = minimum_instruction_length; + if (log != null && opcode_base > 0) + { + log.WriteLine(); + log.WriteLine(" Opcodes:"); + for (int i = 0; i < _standardOpCodeLengths.Count; i++) + { + var argCount = _standardOpCodeLengths[i]; + log.WriteLine($" Opcode {i + 1} has {argCount} {((argCount == 0 || argCount > 1) ? "args" : "arg")}"); + } + } - byte maximum_operations_per_instruction = 1; - if (version >= 4) + var directoriesOffset = reader.Position; + var directories = new List(); + while (true) + { + var dir = reader.ReadStringUTF8NullTerminated(); + if (string.IsNullOrEmpty(dir)) { - maximum_operations_per_instruction = reader.ReadU8(); + break; } - MaximumOperationsPerInstruction = maximum_operations_per_instruction; - - var default_is_stmt = reader.ReadU8(); - var line_base = reader.ReadI8(); - LineBase = line_base; - var line_range = reader.ReadU8(); - LineRange = line_range; - var opcode_base = reader.ReadU8(); - - if (log != null) + + directories.Add(dir); + } + + if (log != null) + { + log.WriteLine(); + if (directories.Count > 0) { - log.WriteLine(); - log.WriteLine($" Offset: 0x{startOfSection:x}"); - log.WriteLine($" Length: {unitLength}"); - log.WriteLine($" DWARF Version: {Version}"); - log.WriteLine($" Prologue Length: {header_length}"); - log.WriteLine($" Minimum Instruction Length: {minimum_instruction_length}"); - if (Version >= 4) + log.WriteLine($" The Directory Table (offset 0x{directoriesOffset:x}):"); + for (int i = 0; i < directories.Count; i++) { - log.WriteLine($" Maximum Operations Per Instruction: {maximum_operations_per_instruction}"); + log.WriteLine($" {i + 1}\t{directories[i]}"); } - log.WriteLine($" Initial value of 'is_stmt': {default_is_stmt}"); - log.WriteLine($" Line Base: {line_base}"); - log.WriteLine($" Line Range: {line_range}"); - log.WriteLine($" Opcode Base: {opcode_base}"); } - - _standardOpCodeLengths.Clear(); - for (int i = 1; i < opcode_base; i++) + else { - var opcode_length = reader.ReadULEB128AsU32(); - _standardOpCodeLengths.Add((uint)opcode_length); - if (i - 1 <= DefaultStandardOpCodeLengths.Length && opcode_length != DefaultStandardOpCodeLengths[i - 1]) - { - reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The standard opcode length at [{i}] = {opcode_length} is different from the expected length {DefaultStandardOpCodeLengths[i - 1]}"); - } + log.WriteLine(" The Directory Table is empty."); } + } - if (log != null && opcode_base > 0) + var fileNamesOffset = reader.Position; + bool printDumpHeader = true; + while (true) + { + var name = reader.ReadStringUTF8NullTerminated(); + + if (string.IsNullOrEmpty(name)) { - log.WriteLine(); - log.WriteLine(" Opcodes:"); - for (int i = 0; i < _standardOpCodeLengths.Count; i++) - { - var argCount = _standardOpCodeLengths[i]; - log.WriteLine($" Opcode {i + 1} has {argCount} {((argCount == 0 || argCount > 1) ? "args" : "arg")}"); - } + break; } - var directoriesOffset = reader.Position; - var directories = new List(); - while (true) - { - var dir = reader.ReadStringUTF8NullTerminated(); - if (string.IsNullOrEmpty(dir)) - { - break; - } + var fileName = new DwarfFileName(name); - directories.Add(dir); + var directoryIndex = reader.ReadULEB128(); + if (!name.Contains('/') && !name.Contains('\\') && directoryIndex > 0 && (directoryIndex - 1) < (ulong) directories.Count) + { + fileName.Directory = directories[(int) directoryIndex - 1]; } - - if (log != null) + else { - log.WriteLine(); - if (directories.Count > 0) - { - log.WriteLine($" The Directory Table (offset 0x{directoriesOffset:x}):"); - for (int i = 0; i < directories.Count; i++) - { - log.WriteLine($" {i + 1}\t{directories[i]}"); - } - } - else - { - log.WriteLine(" The Directory Table is empty."); - } + // log error } - var fileNamesOffset = reader.Position; - bool printDumpHeader = true; - while (true) - { - var name = reader.ReadStringUTF8NullTerminated(); + fileName.Time = reader.ReadULEB128(); + fileName.Size = reader.ReadULEB128(); - if (string.IsNullOrEmpty(name)) + if (log != null) + { + if (printDumpHeader) { - break; + log.WriteLine(); + log.WriteLine($" The File Name Table (offset 0x{fileNamesOffset:x}):"); + log.WriteLine($" Entry\tDir\tTime\tSize\tName"); + printDumpHeader = false; } + log.WriteLine($" {FileNames.Count + 1}\t{directoryIndex}\t{fileName.Time}\t{fileName.Size}\t{name}"); + } - var fileName = new DwarfFileName(name); + FileNames.Add(fileName); + } - var directoryIndex = reader.ReadULEB128(); - if (!name.Contains('/') && !name.Contains('\\') && directoryIndex > 0 && (directoryIndex - 1) < (ulong) directories.Count) - { - fileName.Directory = directories[(int) directoryIndex - 1]; - } - else - { - // log error - } + if (log != null && printDumpHeader) + { + log.WriteLine(); + log.WriteLine(" The File Name Table is empty."); + } - fileName.Time = reader.ReadULEB128(); - fileName.Size = reader.ReadULEB128(); + var state = _stateLine; + state.Position = reader.Position; + var firstFileName = FileNames.Count > 0 ? FileNames[0] : null; + state.Reset(firstFileName, default_is_stmt != 0); - if (log != null) - { - if (printDumpHeader) - { - log.WriteLine(); - log.WriteLine($" The File Name Table (offset 0x{fileNamesOffset:x}):"); - log.WriteLine($" Entry\tDir\tTime\tSize\tName"); - printDumpHeader = false; - } - log.WriteLine($" {FileNames.Count + 1}\t{directoryIndex}\t{fileName.Time}\t{fileName.Size}\t{name}"); - } + var intFileNameCount = FileNames.Count; - FileNames.Add(fileName); - } + printDumpHeader = true; + var currentSequence = new DwarfLineSequence {Position = state.Position}; - if (log != null && printDumpHeader) + while (true) + { + var currentLength = reader.Position - startPosition; + if (currentLength >= unitLength) { - log.WriteLine(); - log.WriteLine(" The File Name Table is empty."); + break; } - var state = _stateLine; - state.Position = reader.Position; - var firstFileName = FileNames.Count > 0 ? FileNames[0] : null; - state.Reset(firstFileName, default_is_stmt != 0); - - var intFileNameCount = FileNames.Count; - - printDumpHeader = true; - var currentSequence = new DwarfLineSequence {Position = state.Position}; - - while (true) + if (log != null) { - var currentLength = reader.Position - startPosition; - if (currentLength >= unitLength) + if (printDumpHeader) { - break; + log.WriteLine(); + log.WriteLine(" Line Number Statements:"); + printDumpHeader = false; } - if (log != null) - { - if (printDumpHeader) + log.Write($" [0x{reader.Position:x8}]"); + } + + var opcode = reader.ReadU8(); + switch (opcode) + { + case DwarfNative.DW_LNS_copy: + currentSequence.Add(state.Clone()); + state.Position = reader.Position; + state.SpecialReset(); + if (log != null) { - log.WriteLine(); - log.WriteLine(" Line Number Statements:"); - printDumpHeader = false; + log.WriteLine(" Copy"); } - - log.Write($" [0x{reader.Position:x8}]"); - } - - var opcode = reader.ReadU8(); - switch (opcode) + break; + case DwarfNative.DW_LNS_advance_pc: { - case DwarfNative.DW_LNS_copy: - currentSequence.Add(state.Clone()); - state.Position = reader.Position; - state.SpecialReset(); - if (log != null) - { - log.WriteLine(" Copy"); - } - break; - case DwarfNative.DW_LNS_advance_pc: + var operation_advance = reader.ReadULEB128() * minimum_instruction_length; + + ulong deltaAddress = operation_advance; + if (version >= 4) { - var operation_advance = reader.ReadULEB128() * minimum_instruction_length; + deltaAddress = minimum_instruction_length * ((state.OperationIndex + operation_advance) / maximum_operations_per_instruction); + state.OperationIndex = (byte)((state.OperationIndex + operation_advance) % maximum_operations_per_instruction); + } + state.Address += deltaAddress; - ulong deltaAddress = operation_advance; - if (version >= 4) + if (log != null) + { + if (minimum_instruction_length == 1) { - deltaAddress = minimum_instruction_length * ((state.OperationIndex + operation_advance) / maximum_operations_per_instruction); - state.OperationIndex = (byte)((state.OperationIndex + operation_advance) % maximum_operations_per_instruction); + log.WriteLine($" Advance PC by {deltaAddress} to 0x{state.Address:x}"); } - state.Address += deltaAddress; - - if (log != null) + else { - if (minimum_instruction_length == 1) - { - log.WriteLine($" Advance PC by {deltaAddress} to 0x{state.Address:x}"); - } - else - { - log.WriteLine($" Advance PC by {deltaAddress} to 0x{state.Address:x}[{state.OperationIndex}]"); - } + log.WriteLine($" Advance PC by {deltaAddress} to 0x{state.Address:x}[{state.OperationIndex}]"); } - break; } - case DwarfNative.DW_LNS_advance_line: - var deltaLine = reader.ReadILEB128(); - state.Line = (uint) (state.Line + deltaLine); - if (log != null) - { - log.WriteLine($" Advance Line by {deltaLine} to {state.Line}"); - } - break; - case DwarfNative.DW_LNS_set_file: - var fileIndex = reader.ReadLEB128AsI32(); - if (fileIndex == 0 || (fileIndex - 1) >= FileNames.Count ) + break; + } + case DwarfNative.DW_LNS_advance_line: + var deltaLine = reader.ReadILEB128(); + state.Line = (uint) (state.Line + deltaLine); + if (log != null) + { + log.WriteLine($" Advance Line by {deltaLine} to {state.Line}"); + } + break; + case DwarfNative.DW_LNS_set_file: + var fileIndex = reader.ReadLEB128AsI32(); + if (fileIndex == 0 || (fileIndex - 1) >= FileNames.Count ) + { + state.File = null; + } + else + { + state.File = FileNames[fileIndex - 1]; + } + if (log != null) + { + log.WriteLine($" Set File Name to entry {fileIndex} in the File Name Table"); + } + break; + case DwarfNative.DW_LNS_set_column: + state.Column = reader.ReadULEB128AsU32(); + if (log != null) + { + log.WriteLine($" Set column to {state.Column}"); + } + break; + case DwarfNative.DW_LNS_negate_stmt: + state.IsStatement = !state.IsStatement; + if (log != null) + { + log.WriteLine($" Set is_stmt to {(state.IsStatement ? 1 : 0)}"); + } + break; + case DwarfNative.DW_LNS_set_basic_block: + state.IsBasicBlock = true; + if (log != null) + { + log.WriteLine($" Set basic block"); + } + break; + case DwarfNative.DW_LNS_const_add_pc: + { + // Advance by opcode 255 + var adjusted_opcode = 255 - opcode_base; + var operation_advance = (ulong) adjusted_opcode / line_range; + + ulong deltaAddress = operation_advance; + if (version >= 4) + { + deltaAddress = minimum_instruction_length * ((state.OperationIndex + operation_advance) / maximum_operations_per_instruction); + state.OperationIndex = (byte)((state.OperationIndex + operation_advance) % maximum_operations_per_instruction); + } + else + { + deltaAddress *= minimum_instruction_length; + } + state.Address += deltaAddress; + + if (log != null) + { + if (minimum_instruction_length == 1) { - state.File = null; + log.WriteLine($" Advance PC by constant {deltaAddress} to 0x{state.Address:x}"); } else { - state.File = FileNames[fileIndex - 1]; + log.WriteLine($" Advance PC by constant {deltaAddress} to 0x{state.Address:x}[{state.OperationIndex}]"); } + } + break; + } + case DwarfNative.DW_LNS_fixed_advance_pc: + var fixedDelta = reader.ReadU16(); + state.Address += fixedDelta; + state.OperationIndex = 0; + if (log != null) + { + log.WriteLine($" Advance PC by fixed size amount {fixedDelta} to 0x{state.Address:x}"); + } + break; + case DwarfNative.DW_LNS_set_prologue_end: // DWARF 3 + state.IsPrologueEnd = true; + if (log != null) + { + log.WriteLine($" Set prologue_end to true"); + } + break; + case DwarfNative.DW_LNS_set_epilogue_begin: // DWARF 3 + state.IsEpilogueBegin = true; + if (log != null) + { + log.WriteLine($" Set epilogue_begin to true"); + } + break; + case DwarfNative.DW_LNS_set_isa: // DWARF 3 + state.Isa = reader.ReadULEB128(); + if (log != null) + { + log.WriteLine($" Set ISA to {state.Isa}"); + } + break; + case 0: + var sizeOfExtended = reader.ReadULEB128(); + var lengthOffset = reader.Position; + var endOffset = reader.Position + sizeOfExtended; + bool hasValidOpCode = true; + if (reader.Position < endOffset) + { + var sub_opcode = reader.ReadU8(); + + // extended opcode if (log != null) { - log.WriteLine($" Set File Name to entry {fileIndex} in the File Name Table"); + log.Write($" Extended opcode {sub_opcode}: "); } - break; - case DwarfNative.DW_LNS_set_column: - state.Column = reader.ReadULEB128AsU32(); - if (log != null) + + switch (sub_opcode) { - log.WriteLine($" Set column to {state.Column}"); + case DwarfNative.DW_LNE_end_sequence: + currentSequence.Add(state.Clone()); + currentSequence.Size = reader.Position - currentSequence.Position; + AddLineSequence(currentSequence); + + currentSequence = new DwarfLineSequence() {Position = reader.Position}; + + state.Position = reader.Position; + state.Reset(firstFileName, default_is_stmt != 0); + if (log != null) + { + log.WriteLine("End of Sequence"); + log.WriteLine(); + } + break; + case DwarfNative.DW_LNE_set_address: + state.Address = reader.ReadUInt(); + state.OperationIndex = 0; + if (log != null) + { + log.WriteLine($"set Address to 0x{state.Address:x}"); + } + break; + case DwarfNative.DW_LNE_define_file: + var fileName = reader.ReadStringUTF8NullTerminated(); + var fileDirectoryIndex = reader.ReadLEB128AsI32(); + var fileTime = reader.ReadULEB128(); + var fileSize = reader.ReadULEB128(); + + var debugFileName = new DwarfFileName(fileName) + { + Directory = fileDirectoryIndex == 0 || fileDirectoryIndex >= directories.Count ? null : directories[fileDirectoryIndex - 1], + Time = fileTime, + Size = fileSize + }; + + state.File = debugFileName; + + if (log != null) + { + log.WriteLine("define new File Table entry"); + log.WriteLine($" Entry\tDir\tTime\tSize\tName"); + intFileNameCount++; + log.WriteLine($" {intFileNameCount + 1}\t{fileDirectoryIndex}\t{debugFileName.Time}\t{debugFileName.Size,-7}\t{fileName}"); + log.WriteLine(); + } + break; + case DwarfNative.DW_LNE_set_discriminator: // DWARF 4 + state.Discriminator = reader.ReadULEB128(); + if (log != null) + { + log.WriteLine($"set Discriminator to {state.Discriminator}"); + } + break; + default: + if (log != null) + { + log.WriteLine($"Unknown opcode"); + } + + hasValidOpCode = false; + // TODO: Add support for pluggable handling of extensions + reader.Diagnostics.Warning(DiagnosticId.DWARF_WRN_UnsupportedLineExtendedCode, $"Unsupported line extended opcode 0x{sub_opcode:x}"); + break; } - break; - case DwarfNative.DW_LNS_negate_stmt: - state.IsStatement = !state.IsStatement; - if (log != null) + + } + + // Log a warning if the end offset doesn't match what we are expecting + if (hasValidOpCode && reader.Position != endOffset) + { + reader.Diagnostics.Warning(DiagnosticId.DWARF_WRN_InvalidExtendedOpCodeLength, $"Invalid length {sizeOfExtended} at offset 0x{lengthOffset:x}"); + } + + reader.Position = endOffset; + break; + default: + if (opcode < opcode_base) + { + // If this is a standard opcode but not part of DWARF ("extension") + // we still want to be able to continue debugging + Debug.Assert(opcode > 0); + var numberOfLEB128Args = _standardOpCodeLengths[opcode - 1]; + for (ulong i = 0; i < numberOfLEB128Args; i++) { - log.WriteLine($" Set is_stmt to {(state.IsStatement ? 1 : 0)}"); + reader.ReadULEB128(); } - break; - case DwarfNative.DW_LNS_set_basic_block: - state.IsBasicBlock = true; + if (log != null) { - log.WriteLine($" Set basic block"); + log.WriteLine("Unsupported standard opcode with {numberOfLEB128Args} LEB128 args skipped"); } - break; - case DwarfNative.DW_LNS_const_add_pc: + } + else { - // Advance by opcode 255 - var adjusted_opcode = 255 - opcode_base; - var operation_advance = (ulong) adjusted_opcode / line_range; + // Special opcode + var adjusted_opcode = opcode - opcode_base; + var operation_advance = (ulong)adjusted_opcode / line_range; + var line_inc = line_base + (adjusted_opcode % line_range); + state.Line = (uint)(state.Line + line_inc); + + ulong deltaAddress; - ulong deltaAddress = operation_advance; if (version >= 4) { deltaAddress = minimum_instruction_length * ((state.OperationIndex + operation_advance) / maximum_operations_per_instruction); + state.Address += deltaAddress; state.OperationIndex = (byte)((state.OperationIndex + operation_advance) % maximum_operations_per_instruction); } else { - deltaAddress *= minimum_instruction_length; + deltaAddress = operation_advance; + state.Address = state.Address + operation_advance; } - state.Address += deltaAddress; if (log != null) { if (minimum_instruction_length == 1) { - log.WriteLine($" Advance PC by constant {deltaAddress} to 0x{state.Address:x}"); + log.Write($" Special opcode {adjusted_opcode}: advance Address by {deltaAddress} to 0x{state.Address:x}"); } else { - log.WriteLine($" Advance PC by constant {deltaAddress} to 0x{state.Address:x}[{state.OperationIndex}]"); - } - } - break; - } - case DwarfNative.DW_LNS_fixed_advance_pc: - var fixedDelta = reader.ReadU16(); - state.Address += fixedDelta; - state.OperationIndex = 0; - if (log != null) - { - log.WriteLine($" Advance PC by fixed size amount {fixedDelta} to 0x{state.Address:x}"); - } - break; - case DwarfNative.DW_LNS_set_prologue_end: // DWARF 3 - state.IsPrologueEnd = true; - if (log != null) - { - log.WriteLine($" Set prologue_end to true"); - } - break; - case DwarfNative.DW_LNS_set_epilogue_begin: // DWARF 3 - state.IsEpilogueBegin = true; - if (log != null) - { - log.WriteLine($" Set epilogue_begin to true"); - } - break; - case DwarfNative.DW_LNS_set_isa: // DWARF 3 - state.Isa = reader.ReadULEB128(); - if (log != null) - { - log.WriteLine($" Set ISA to {state.Isa}"); - } - break; - case 0: - var sizeOfExtended = reader.ReadULEB128(); - var lengthOffset = reader.Position; - var endOffset = reader.Position + sizeOfExtended; - bool hasValidOpCode = true; - if (reader.Position < endOffset) - { - var sub_opcode = reader.ReadU8(); - - // extended opcode - if (log != null) - { - log.Write($" Extended opcode {sub_opcode}: "); - } - - switch (sub_opcode) - { - case DwarfNative.DW_LNE_end_sequence: - currentSequence.Add(state.Clone()); - currentSequence.Size = reader.Position - currentSequence.Position; - AddLineSequence(currentSequence); - - currentSequence = new DwarfLineSequence() {Position = reader.Position}; - - state.Position = reader.Position; - state.Reset(firstFileName, default_is_stmt != 0); - if (log != null) - { - log.WriteLine("End of Sequence"); - log.WriteLine(); - } - break; - case DwarfNative.DW_LNE_set_address: - state.Address = reader.ReadUInt(); - state.OperationIndex = 0; - if (log != null) - { - log.WriteLine($"set Address to 0x{state.Address:x}"); - } - break; - case DwarfNative.DW_LNE_define_file: - var fileName = reader.ReadStringUTF8NullTerminated(); - var fileDirectoryIndex = reader.ReadLEB128AsI32(); - var fileTime = reader.ReadULEB128(); - var fileSize = reader.ReadULEB128(); - - var debugFileName = new DwarfFileName(fileName) - { - Directory = fileDirectoryIndex == 0 || fileDirectoryIndex >= directories.Count ? null : directories[fileDirectoryIndex - 1], - Time = fileTime, - Size = fileSize - }; - - state.File = debugFileName; - - if (log != null) - { - log.WriteLine("define new File Table entry"); - log.WriteLine($" Entry\tDir\tTime\tSize\tName"); - intFileNameCount++; - log.WriteLine($" {intFileNameCount + 1}\t{fileDirectoryIndex}\t{debugFileName.Time}\t{debugFileName.Size,-7}\t{fileName}"); - log.WriteLine(); - } - break; - case DwarfNative.DW_LNE_set_discriminator: // DWARF 4 - state.Discriminator = reader.ReadULEB128(); - if (log != null) - { - log.WriteLine($"set Discriminator to {state.Discriminator}"); - } - break; - default: - if (log != null) - { - log.WriteLine($"Unknown opcode"); - } - - hasValidOpCode = false; - // TODO: Add support for pluggable handling of extensions - reader.Diagnostics.Warning(DiagnosticId.DWARF_WRN_UnsupportedLineExtendedCode, $"Unsupported line extended opcode 0x{sub_opcode:x}"); - break; - } - - } - - // Log a warning if the end offset doesn't match what we are expecting - if (hasValidOpCode && reader.Position != endOffset) - { - reader.Diagnostics.Warning(DiagnosticId.DWARF_WRN_InvalidExtendedOpCodeLength, $"Invalid length {sizeOfExtended} at offset 0x{lengthOffset:x}"); - } - - reader.Position = endOffset; - break; - default: - if (opcode < opcode_base) - { - // If this is a standard opcode but not part of DWARF ("extension") - // we still want to be able to continue debugging - Debug.Assert(opcode > 0); - var numberOfLEB128Args = _standardOpCodeLengths[opcode - 1]; - for (ulong i = 0; i < numberOfLEB128Args; i++) - { - reader.ReadULEB128(); + log.Write($" Special opcode {adjusted_opcode}: advance Address by {deltaAddress} to 0x{state.Address:x}[{state.OperationIndex}]"); } - if (log != null) - { - log.WriteLine("Unsupported standard opcode with {numberOfLEB128Args} LEB128 args skipped"); - } + // TODO: Make verbose version + log.WriteLine($" and Line by {line_inc} to {state.Line}"); } - else - { - // Special opcode - var adjusted_opcode = opcode - opcode_base; - var operation_advance = (ulong)adjusted_opcode / line_range; - var line_inc = line_base + (adjusted_opcode % line_range); - state.Line = (uint)(state.Line + line_inc); - - ulong deltaAddress; - if (version >= 4) - { - deltaAddress = minimum_instruction_length * ((state.OperationIndex + operation_advance) / maximum_operations_per_instruction); - state.Address += deltaAddress; - state.OperationIndex = (byte)((state.OperationIndex + operation_advance) % maximum_operations_per_instruction); - } - else - { - deltaAddress = operation_advance; - state.Address = state.Address + operation_advance; - } - - if (log != null) - { - if (minimum_instruction_length == 1) - { - log.Write($" Special opcode {adjusted_opcode}: advance Address by {deltaAddress} to 0x{state.Address:x}"); - } - else - { - log.Write($" Special opcode {adjusted_opcode}: advance Address by {deltaAddress} to 0x{state.Address:x}[{state.OperationIndex}]"); - } + currentSequence.Add(state.Clone()); + state.Position = reader.Position; + state.SpecialReset(); + } - // TODO: Make verbose version - log.WriteLine($" and Line by {line_inc} to {state.Line}"); - } + break; + } + } + } - currentSequence.Add(state.Clone()); - state.Position = reader.Position; - state.SpecialReset(); - } + public override void Verify(DwarfVerifyContext context) + { + var diagnostics = context.Diagnostics; - break; - } - } + if (Version < 2 || Version >= 5) + { + diagnostics.Error(DiagnosticId.DWARF_ERR_VersionNotSupported, $"Version .debug_line {Version} not supported"); } - public override void Verify(DiagnosticBag diagnostics) + if (AddressSize == DwarfAddressSize.None) { - base.Verify(diagnostics); + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidAddressSize, $"Address size for .debug_line cannot be None/0"); + } - if (Version < 2 || Version >= 5) + if (StandardOpCodeLengths.Count < DefaultStandardOpCodeLengths.Length) + { + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidNumberOfStandardOpCodeLengths, $"Invalid length {StandardOpCodeLengths.Count} of {nameof(StandardOpCodeLengths)}. Expecting standard opcode length >= {DefaultStandardOpCodeLengths.Length} for {this}."); + } + else + { + for (int i = 0; i < DefaultStandardOpCodeLengths.Length; i++) { - diagnostics.Error(DiagnosticId.DWARF_ERR_VersionNotSupported, $"Version .debug_line {Version} not supported"); - } + var opCodeLength = StandardOpCodeLengths[i]; + var expectedOpCodeLength = DefaultStandardOpCodeLengths[i]; - if (AddressSize == DwarfAddressSize.None) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidAddressSize, $"Address size for .debug_line cannot be None/0"); + if (opCodeLength != expectedOpCodeLength) + { + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidStandardOpCodeLength, $"Invalid Standard OpCode Length {opCodeLength} for OpCode {i+1}. Expecting {expectedOpCodeLength} for {this}."); + } } + } - if (StandardOpCodeLengths.Count < DefaultStandardOpCodeLengths.Length) + var startLine = LineBase; + var endLine = LineBase + LineRange; + if (startLine > 0 || endLine <= 0) + { + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidStandardOpCodeLength, $"Invalid value for {nameof(LineBase)} = {LineBase} and/or {nameof(LineRange)} = {LineRange}. Expecting the range to cover the Line 0 for {this}"); + } + else + { + // TODO: take into account MaximumOperationsPerInstruction + var maxAdjustedOpCode = 255 - OpCodeBase; + int maxAddressIncrement = maxAdjustedOpCode / LineRange; + if (maxAdjustedOpCode <= 0 || maxAddressIncrement < MinimumInstructionLength) { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidNumberOfStandardOpCodeLengths, $"Invalid length {StandardOpCodeLengths.Count} of {nameof(StandardOpCodeLengths)}. Expecting standard opcode length >= {DefaultStandardOpCodeLengths.Length} for {this}."); + diagnostics.Error(DiagnosticId.DWARF_WRN_CannotEncodeAddressIncrement, $"Cannot encode properly address increment with {nameof(LineBase)} = {LineBase}, {nameof(LineRange)} = {LineRange} and {nameof(StandardOpCodeLengths)}. The combination of {nameof(LineRange)} and {nameof(OpCodeBase)} are not making enough room for encoding address increment for {this}"); } - else - { - for (int i = 0; i < DefaultStandardOpCodeLengths.Length; i++) - { - var opCodeLength = StandardOpCodeLengths[i]; - var expectedOpCodeLength = DefaultStandardOpCodeLengths[i]; + } - if (opCodeLength != expectedOpCodeLength) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidStandardOpCodeLength, $"Invalid Standard OpCode Length {opCodeLength} for OpCode {i+1}. Expecting {expectedOpCodeLength} for {this}."); - } - } - } + if (MaximumOperationsPerInstruction > 1 && Version < 4) + { + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidMaximumOperationsPerInstruction, $"Invalid {nameof(MaximumOperationsPerInstruction)} = {MaximumOperationsPerInstruction}. Must be == 1 for {this}"); + } - var startLine = LineBase; - var endLine = LineBase + LineRange; - if (startLine > 0 || endLine <= 0) + for (var i = 0; i < FileNames.Count; i++) + { + var fileName = FileNames[i]; + if (fileName == null) { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidStandardOpCodeLength, $"Invalid value for {nameof(LineBase)} = {LineBase} and/or {nameof(LineRange)} = {LineRange}. Expecting the range to cover the Line 0 for {this}"); + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidNullFileNameEntry, $"Invalid null {nameof(FileNames)} entry at [{i}] for {this}"); } else { - // TODO: take into account MaximumOperationsPerInstruction - var maxAdjustedOpCode = 255 - OpCodeBase; - int maxAddressIncrement = maxAdjustedOpCode / LineRange; - if (maxAdjustedOpCode <= 0 || maxAddressIncrement < MinimumInstructionLength) + if (fileName.Name == null) { - diagnostics.Error(DiagnosticId.DWARF_WRN_CannotEncodeAddressIncrement, $"Cannot encode properly address increment with {nameof(LineBase)} = {LineBase}, {nameof(LineRange)} = {LineRange} and {nameof(StandardOpCodeLengths)}. The combination of {nameof(LineRange)} and {nameof(OpCodeBase)} are not making enough room for encoding address increment for {this}"); + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidFileName, $"Invalid null filename for {nameof(FileNames)} entry at [{i}] for {this}"); } } + } - if (MaximumOperationsPerInstruction > 1 && Version < 4) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidMaximumOperationsPerInstruction, $"Invalid {nameof(MaximumOperationsPerInstruction)} = {MaximumOperationsPerInstruction}. Must be == 1 for {this}"); - } - - for (var i = 0; i < FileNames.Count; i++) + // Check that address increment is positive + foreach (var lineSequence in _lineSequences) + { + var lines = lineSequence.Lines; + ulong previousAddress = 0; + for (var i = 0; i < lines.Count; i++) { - var fileName = FileNames[i]; - if (fileName == null) + var line = lines[i]; + var deltaAddress = (long)line.Address - (long)previousAddress; + if (deltaAddress < 0) { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidNullFileNameEntry, $"Invalid null {nameof(FileNames)} entry at [{i}] for {this}"); + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidNegativeAddressDelta, $"Invalid address 0x{line.Address:x} after previous 0x{previousAddress:x} for debug line entry at [{i}]. The increment must be positive. for {this}"); } - else - { - if (fileName.Name == null) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidFileName, $"Invalid null filename for {nameof(FileNames)} entry at [{i}] for {this}"); - } - } - } + previousAddress = line.Address; - // Check that address increment is positive - foreach (var lineSequence in _lineSequences) - { - var lines = lineSequence.Lines; - ulong previousAddress = 0; - for (var i = 0; i < lines.Count; i++) + if (line.OperationIndex >= MaximumOperationsPerInstruction) { - var line = lines[i]; - var deltaAddress = (long)line.Address - (long)previousAddress; - if (deltaAddress < 0) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidNegativeAddressDelta, $"Invalid address 0x{line.Address:x} after previous 0x{previousAddress:x} for debug line entry at [{i}]. The increment must be positive. for {this}"); - } - previousAddress = line.Address; - - if (line.OperationIndex >= MaximumOperationsPerInstruction) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidOperationIndex, $"Invalid operation index {line.OperationIndex} must be < {MaximumOperationsPerInstruction} for debug line entry at [{i}] for {this}"); - } + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidOperationIndex, $"Invalid operation index {line.OperationIndex} must be < {MaximumOperationsPerInstruction} for debug line entry at [{i}] for {this}"); } } } + } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - ulong sizeOf = 0; + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + ulong sizeOf = 0; - // unit_length - sizeOf += DwarfHelper.SizeOfUnitLength(Is64BitEncoding); + // unit_length + sizeOf += DwarfHelper.SizeOfUnitLength(Is64BitEncoding); - sizeOf += 2; // version (uhalf) + sizeOf += 2; // version (uhalf) - // header length (calculated just after file names and size added as well) - sizeOf += DwarfHelper.SizeOfUInt(Is64BitEncoding); - ulong headerLengthStart = sizeOf; + // header length (calculated just after file names and size added as well) + sizeOf += DwarfHelper.SizeOfUInt(Is64BitEncoding); + ulong headerLengthStart = sizeOf; - // minimum_instruction_length + // minimum_instruction_length + sizeOf++; + + if (Version >= 4) + { + // maximum_operations_per_instruction sizeOf++; + } - if (Version >= 4) - { - // maximum_operations_per_instruction - sizeOf++; - } + // default_is_stmt + // line_base + // line_range + // opcode_base + sizeOf += 4; - // default_is_stmt - // line_base - // line_range - // opcode_base - sizeOf += 4; + // StandardOpCodeLengths + foreach (var opcodeLength in _standardOpCodeLengths) + { + sizeOf += DwarfHelper.SizeOfULEB128(opcodeLength); + } + + // Write directory names + _directoryNameToIndex.Clear(); + _directoryNames.Clear(); + _fileNameToIndex.Clear(); - // StandardOpCodeLengths - foreach (var opcodeLength in _standardOpCodeLengths) + foreach (var fileName in FileNames) + { + uint dirIndex = 0; + if (fileName.Directory != null) { - sizeOf += DwarfHelper.SizeOfULEB128(opcodeLength); + var directoryName = fileName.Directory; + RecordDirectory(directoryName, ref sizeOf, out dirIndex); } - // Write directory names - _directoryNameToIndex.Clear(); - _directoryNames.Clear(); - _fileNameToIndex.Clear(); + _fileNameToIndex.Add(fileName, (uint)_fileNameToIndex.Count + 1); + + sizeOf += (ulong)Encoding.UTF8.GetByteCount(fileName.Name) + 1; + sizeOf += DwarfHelper.SizeOfULEB128(dirIndex); + sizeOf += DwarfHelper.SizeOfULEB128(fileName.Time); + sizeOf += DwarfHelper.SizeOfULEB128(fileName.Size); + } + // byte 0 => end of directory names + end of file names + sizeOf += 2; + + HeaderLength = sizeOf - headerLengthStart; + + LayoutDebugLineOpCodes(ref sizeOf, OpCodeBase); + + Size = sizeOf; + } + + private void RecordDirectory(string directoryName, ref ulong sizeOf, out uint dirIndex) + { + if (!_directoryNameToIndex.TryGetValue(directoryName, out dirIndex)) + { + dirIndex = (uint) _directoryNames.Count + 1; + _directoryNameToIndex.Add(directoryName, dirIndex); + sizeOf += (ulong) Encoding.UTF8.GetByteCount(directoryName) + 1; + _directoryNames.Add(directoryName); + } + } + + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; + + writer.Is64BitEncoding = Is64BitEncoding; + writer.WriteUnitLength(Size - DwarfHelper.SizeOfUnitLength(Is64BitEncoding)); + + writer.WriteU16(Version); + writer.WriteUIntFromEncoding(HeaderLength); + + var startOfHeader = writer.Position; + + writer.WriteU8(MinimumInstructionLength); - foreach (var fileName in FileNames) - { - uint dirIndex = 0; - if (fileName.Directory != null) - { - var directoryName = fileName.Directory; - RecordDirectory(directoryName, ref sizeOf, out dirIndex); - } + if (Version >= 4) + { + writer.WriteU8(MaximumOperationsPerInstruction); + } - _fileNameToIndex.Add(fileName, (uint)_fileNameToIndex.Count + 1); + // default_is_stmt + writer.WriteU8(1); - sizeOf += (ulong)Encoding.UTF8.GetByteCount(fileName.Name) + 1; - sizeOf += DwarfHelper.SizeOfULEB128(dirIndex); - sizeOf += DwarfHelper.SizeOfULEB128(fileName.Time); - sizeOf += DwarfHelper.SizeOfULEB128(fileName.Size); - } - // byte 0 => end of directory names + end of file names - sizeOf += 2; + // line_base + writer.WriteI8(LineBase); - HeaderLength = sizeOf - headerLengthStart; + // line_range + writer.WriteU8(LineRange); - LayoutDebugLineOpCodes(ref sizeOf, OpCodeBase); + // opcode_base + writer.WriteU8(OpCodeBase); - Size = sizeOf; + // standard_opcode_lengths + foreach (var opcodeLength in StandardOpCodeLengths) + { + writer.WriteULEB128(opcodeLength); } - private void RecordDirectory(string directoryName, ref ulong sizeOf, out uint dirIndex) + // Write directory names + foreach (var directoryName in _directoryNames) { - if (!_directoryNameToIndex.TryGetValue(directoryName, out dirIndex)) - { - dirIndex = (uint) _directoryNames.Count + 1; - _directoryNameToIndex.Add(directoryName, dirIndex); - sizeOf += (ulong) Encoding.UTF8.GetByteCount(directoryName) + 1; - _directoryNames.Add(directoryName); - } + writer.WriteStringUTF8NullTerminated(directoryName); } + // empty string + writer.WriteU8(0); - protected override void Write(DwarfWriter writer) + // Write filenames + foreach (var fileName in FileNames) { - var startOffset = writer.Position; - - writer.Is64BitEncoding = Is64BitEncoding; - writer.WriteUnitLength(Size - DwarfHelper.SizeOfUnitLength(Is64BitEncoding)); - - writer.WriteU16(Version); - writer.WriteUIntFromEncoding(HeaderLength); - - var startOfHeader = writer.Position; + writer.WriteStringUTF8NullTerminated(fileName.Name); - writer.WriteU8(MinimumInstructionLength); - - if (Version >= 4) + uint directoryIndex = 0; + if (fileName.Directory != null) { - writer.WriteU8(MaximumOperationsPerInstruction); + directoryIndex = _directoryNameToIndex[fileName.Directory]; } - // default_is_stmt - writer.WriteU8(1); + writer.WriteULEB128(directoryIndex); + writer.WriteULEB128(fileName.Time); + writer.WriteULEB128(fileName.Size); + } + // empty string + writer.WriteU8(0); + + var headSizeWritten = writer.Position - startOfHeader; + Debug.Assert(HeaderLength == headSizeWritten, $"Expected Header Length: {HeaderLength} != Written Header Length: {headSizeWritten}"); + + WriteDebugLineOpCodes(writer, OpCodeBase); - // line_base - writer.WriteI8(LineBase); + Debug.Assert(Size == writer.Position - startOffset, $"Expected Size: {Size} != Written Size: {writer.Position - startOffset}"); + } - // line_range - writer.WriteU8(LineRange); + private void WriteDebugLineOpCodes(DwarfWriter writer, uint opCodeBase) + { + var previousLineState = new DwarfLineState(); + var firstFile = FileNames.Count > 0 ? FileNames[0] : null; + previousLineState.Reset(firstFile, true); + var initialState = previousLineState; + + uint maxDeltaAddressPerSpecialCode; + byte maxOperationAdvance = (byte) ((255 - OpCodeBase) / LineRange); + if (Version >= 4) + { + maxDeltaAddressPerSpecialCode = (uint)maxOperationAdvance / MaximumOperationsPerInstruction; + } + else + { + maxDeltaAddressPerSpecialCode = maxOperationAdvance; + } + maxDeltaAddressPerSpecialCode *= MinimumInstructionLength; - // opcode_base - writer.WriteU8(OpCodeBase); + bool hasSetAddress; - // standard_opcode_lengths - foreach (var opcodeLength in StandardOpCodeLengths) - { - writer.WriteULEB128(opcodeLength); - } + foreach (var lineSequence in _lineSequences) + { + var lines = lineSequence.Lines; - // Write directory names - foreach (var directoryName in _directoryNames) - { - writer.WriteStringUTF8NullTerminated(directoryName); - } - // empty string - writer.WriteU8(0); + hasSetAddress = false; - // Write filenames - foreach (var fileName in FileNames) + for (var lineIndex = 0; lineIndex < lines.Count; lineIndex++) { - writer.WriteStringUTF8NullTerminated(fileName.Name); - - uint directoryIndex = 0; - if (fileName.Directory != null) + var debugLine = lines[lineIndex]; + ulong deltaAddress; + int deltaOperationIndex; + bool fileNameChanged; + int deltaLine; + int deltaColumn; + bool isStatementChanged; + bool isBasicBlockChanged; + bool isEndSequenceChanged; + bool isPrologueEndChanged; + bool isEpilogueBeginChanged; + bool isaChanged; + bool isDiscriminatorChanged; + + bool hasGeneratedRow = false; + + var debugLineState = debugLine.ToState(); + + previousLineState.Delta(debugLineState, out deltaAddress, + out deltaOperationIndex, + out fileNameChanged, + out deltaLine, + out deltaColumn, + out isStatementChanged, + out isBasicBlockChanged, + out isEndSequenceChanged, + out isPrologueEndChanged, + out isEpilogueBeginChanged, + out isaChanged, + out isDiscriminatorChanged); + + Debug.Assert(debugLine.Position == writer.Position, $"Expected Debug Line Offset: {debugLine.Position} != Written Offset: {writer.Position}"); + + // DW_LNS_set_column + if (deltaColumn != 0) { - directoryIndex = _directoryNameToIndex[fileName.Directory]; + writer.WriteU8(DwarfNative.DW_LNS_set_column); + writer.WriteULEB128(debugLine.Column); } - writer.WriteULEB128(directoryIndex); - writer.WriteULEB128(fileName.Time); - writer.WriteULEB128(fileName.Size); - } - // empty string - writer.WriteU8(0); - - var headSizeWritten = writer.Position - startOfHeader; - Debug.Assert(HeaderLength == headSizeWritten, $"Expected Header Length: {HeaderLength} != Written Header Length: {headSizeWritten}"); - - WriteDebugLineOpCodes(writer, OpCodeBase); - - Debug.Assert(Size == writer.Position - startOffset, $"Expected Size: {Size} != Written Size: {writer.Position - startOffset}"); - } + // DW_LNS_set_file or DW_LNE_define_file + if (fileNameChanged) + { + var fileName = debugLine.File; + if (fileName is null) + { + throw new InvalidOperationException("File name cannot be null"); + } - private void WriteDebugLineOpCodes(DwarfWriter writer, uint opCodeBase) - { - var previousLineState = new DwarfLineState(); - var firstFile = FileNames.Count > 0 ? FileNames[0] : null; - previousLineState.Reset(firstFile, true); - var initialState = previousLineState; + // DW_LNS_set_file + if (_fileNameToIndex.TryGetValue(fileName, out var fileIndex)) + { + writer.WriteU8(DwarfNative.DW_LNS_set_file); + writer.WriteULEB128(fileIndex); + } + else + { + // DW_LNE_define_file + writer.WriteU8(0); + uint dirIndex = fileName.Directory != null && _directoryNameToIndex.ContainsKey(fileName.Directory) ? _directoryNameToIndex[fileName.Directory] : 0; - uint maxDeltaAddressPerSpecialCode; - byte maxOperationAdvance = (byte) ((255 - OpCodeBase) / LineRange); - if (Version >= 4) - { - maxDeltaAddressPerSpecialCode = (uint)maxOperationAdvance / MaximumOperationsPerInstruction; - } - else - { - maxDeltaAddressPerSpecialCode = maxOperationAdvance; - } - maxDeltaAddressPerSpecialCode *= MinimumInstructionLength; + ulong sizeOfInlineFileName = 1; + sizeOfInlineFileName += (ulong) Encoding.UTF8.GetByteCount(fileName.Name) + 1; + sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(dirIndex); + sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(fileName.Time); + sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(fileName.Size); - bool hasSetAddress; + writer.WriteULEB128(sizeOfInlineFileName); - foreach (var lineSequence in _lineSequences) - { - var lines = lineSequence.Lines; + writer.WriteU8(DwarfNative.DW_LNE_define_file); + writer.WriteStringUTF8NullTerminated(fileName.Name); + writer.WriteULEB128(dirIndex); + writer.WriteULEB128(fileName.Time); + writer.WriteULEB128(fileName.Size); + } + } - hasSetAddress = false; + // DW_LNS_copy + if (isBasicBlockChanged && !debugLine.IsBasicBlock || + isPrologueEndChanged && !debugLine.IsPrologueEnd || + isEpilogueBeginChanged && !debugLine.IsEpilogueBegin) + { + writer.WriteU8(DwarfNative.DW_LNS_copy); + isDiscriminatorChanged = debugLine.Discriminator != 0; + hasGeneratedRow = true; + } - for (var lineIndex = 0; lineIndex < lines.Count; lineIndex++) + // DW_LNS_set_basic_block + if (isBasicBlockChanged && debugLine.IsBasicBlock) { - var debugLine = lines[lineIndex]; - ulong deltaAddress; - int deltaOperationIndex; - bool fileNameChanged; - int deltaLine; - int deltaColumn; - bool isStatementChanged; - bool isBasicBlockChanged; - bool isEndSequenceChanged; - bool isPrologueEndChanged; - bool isEpilogueBeginChanged; - bool isaChanged; - bool isDiscriminatorChanged; - - bool hasGeneratedRow = false; - - var debugLineState = debugLine.ToState(); - - previousLineState.Delta(debugLineState, out deltaAddress, - out deltaOperationIndex, - out fileNameChanged, - out deltaLine, - out deltaColumn, - out isStatementChanged, - out isBasicBlockChanged, - out isEndSequenceChanged, - out isPrologueEndChanged, - out isEpilogueBeginChanged, - out isaChanged, - out isDiscriminatorChanged); - - Debug.Assert(debugLine.Position == writer.Position, $"Expected Debug Line Offset: {debugLine.Position} != Written Offset: {writer.Position}"); - - // DW_LNS_set_column - if (deltaColumn != 0) - { - writer.WriteU8(DwarfNative.DW_LNS_set_column); - writer.WriteULEB128(debugLine.Column); - } + writer.WriteU8(DwarfNative.DW_LNS_set_basic_block); + } - // DW_LNS_set_file or DW_LNE_define_file - if (fileNameChanged) - { - var fileName = debugLine.File; - if (fileName is null) - { - throw new InvalidOperationException("File name cannot be null"); - } + // DW_LNS_set_prologue_end + if (isPrologueEndChanged && debugLine.IsPrologueEnd) + { + writer.WriteU8(DwarfNative.DW_LNS_set_prologue_end); + } - // DW_LNS_set_file - if (_fileNameToIndex.TryGetValue(fileName, out var fileIndex)) - { - writer.WriteU8(DwarfNative.DW_LNS_set_file); - writer.WriteULEB128(fileIndex); - } - else - { - // DW_LNE_define_file - writer.WriteU8(0); - uint dirIndex = fileName.Directory != null && _directoryNameToIndex.ContainsKey(fileName.Directory) ? _directoryNameToIndex[fileName.Directory] : 0; - - ulong sizeOfInlineFileName = 1; - sizeOfInlineFileName += (ulong) Encoding.UTF8.GetByteCount(fileName.Name) + 1; - sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(dirIndex); - sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(fileName.Time); - sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(fileName.Size); - - writer.WriteULEB128(sizeOfInlineFileName); - - writer.WriteU8(DwarfNative.DW_LNE_define_file); - writer.WriteStringUTF8NullTerminated(fileName.Name); - writer.WriteULEB128(dirIndex); - writer.WriteULEB128(fileName.Time); - writer.WriteULEB128(fileName.Size); - } - } + // DW_LNS_set_epilogue_begin + if (isEpilogueBeginChanged && debugLine.IsEpilogueBegin) + { + writer.WriteU8(DwarfNative.DW_LNS_set_epilogue_begin); + } - // DW_LNS_copy - if (isBasicBlockChanged && !debugLine.IsBasicBlock || - isPrologueEndChanged && !debugLine.IsPrologueEnd || - isEpilogueBeginChanged && !debugLine.IsEpilogueBegin) - { - writer.WriteU8(DwarfNative.DW_LNS_copy); - isDiscriminatorChanged = debugLine.Discriminator != 0; - hasGeneratedRow = true; - } + // DW_LNS_set_isa + if (isaChanged) + { + writer.WriteU8(DwarfNative.DW_LNS_set_isa); + writer.WriteULEB128(debugLine.Isa); + } - // DW_LNS_set_basic_block - if (isBasicBlockChanged && debugLine.IsBasicBlock) - { - writer.WriteU8(DwarfNative.DW_LNS_set_basic_block); - } + // DW_LNE_set_discriminator + if (isDiscriminatorChanged) + { + writer.WriteU8(0); + writer.WriteULEB128(1 + DwarfHelper.SizeOfULEB128(debugLine.Discriminator)); + writer.WriteU8(DwarfNative.DW_LNE_set_discriminator); + writer.WriteULEB128(debugLine.Discriminator); + } - // DW_LNS_set_prologue_end - if (isPrologueEndChanged && debugLine.IsPrologueEnd) - { - writer.WriteU8(DwarfNative.DW_LNS_set_prologue_end); - } + // DW_LNS_negate_stmt + if (isStatementChanged) + { + writer.WriteU8(DwarfNative.DW_LNS_negate_stmt); + } - // DW_LNS_set_epilogue_begin - if (isEpilogueBeginChanged && debugLine.IsEpilogueBegin) - { - writer.WriteU8(DwarfNative.DW_LNS_set_epilogue_begin); - } + bool isEndOfSequence = lineIndex + 1 == lines.Count; + bool canEncodeSpecial = !isEndOfSequence; + bool canEncodeLineInSpecialCode = canEncodeSpecial && deltaLine >= LineBase && deltaLine < LineBase + LineRange; - // DW_LNS_set_isa - if (isaChanged) - { - writer.WriteU8(DwarfNative.DW_LNS_set_isa); - writer.WriteULEB128(debugLine.Isa); - } + bool operationAdvancedEncoded = false; - // DW_LNE_set_discriminator - if (isDiscriminatorChanged) - { - writer.WriteU8(0); - writer.WriteULEB128(1 + DwarfHelper.SizeOfULEB128(debugLine.Discriminator)); - writer.WriteU8(DwarfNative.DW_LNE_set_discriminator); - writer.WriteULEB128(debugLine.Discriminator); - } + // Pre-encode address if necessary + if (!hasSetAddress) + { + writer.WriteU8(0); + writer.WriteULEB128(1 + DwarfHelper.SizeOfUInt(writer.AddressSize)); + writer.WriteU8(DwarfNative.DW_LNE_set_address); + writer.WriteAddress(DwarfRelocationTarget.Code, debugLine.Address); + operationAdvancedEncoded = true; + deltaAddress = 0; + hasSetAddress = true; + } - // DW_LNS_negate_stmt - if (isStatementChanged) + // DW_LNS_advance_line + // In case we can't encode the line advance via special code + if (!canEncodeLineInSpecialCode) + { + if (deltaLine != 0) { - writer.WriteU8(DwarfNative.DW_LNS_negate_stmt); + writer.WriteU8(DwarfNative.DW_LNS_advance_line); + writer.WriteILEB128(deltaLine); + deltaLine = 0; } + } - bool isEndOfSequence = lineIndex + 1 == lines.Count; - bool canEncodeSpecial = !isEndOfSequence; - bool canEncodeLineInSpecialCode = canEncodeSpecial && deltaLine >= LineBase && deltaLine < LineBase + LineRange; - bool operationAdvancedEncoded = false; + if (deltaAddress > maxDeltaAddressPerSpecialCode && deltaAddress <= (2U * maxDeltaAddressPerSpecialCode)) + { + ulong deltaAddressSpecialOpCode255; - // Pre-encode address if necessary - if (!hasSetAddress) + if (Version >= 4) { - writer.WriteU8(0); - writer.WriteULEB128(1 + DwarfHelper.SizeOfUInt(writer.AddressSize)); - writer.WriteU8(DwarfNative.DW_LNE_set_address); - writer.WriteAddress(DwarfRelocationTarget.Code, debugLine.Address); - operationAdvancedEncoded = true; - deltaAddress = 0; - hasSetAddress = true; + deltaAddressSpecialOpCode255 = (((ulong) previousLineState.OperationIndex + maxOperationAdvance) / MaximumOperationsPerInstruction); + deltaOperationIndex = debugLine.OperationIndex - (byte) ((previousLineState.OperationIndex + maxOperationAdvance) % MaximumOperationsPerInstruction); } - - // DW_LNS_advance_line - // In case we can't encode the line advance via special code - if (!canEncodeLineInSpecialCode) + else { - if (deltaLine != 0) - { - writer.WriteU8(DwarfNative.DW_LNS_advance_line); - writer.WriteILEB128(deltaLine); - deltaLine = 0; - } + deltaAddressSpecialOpCode255 = maxOperationAdvance; + deltaOperationIndex = 0; } + Debug.Assert(deltaAddressSpecialOpCode255 * MinimumInstructionLength < deltaAddress); + deltaAddress -= deltaAddressSpecialOpCode255 * MinimumInstructionLength; - if (deltaAddress > maxDeltaAddressPerSpecialCode && deltaAddress <= (2U * maxDeltaAddressPerSpecialCode)) - { - ulong deltaAddressSpecialOpCode255; - - if (Version >= 4) - { - deltaAddressSpecialOpCode255 = (((ulong) previousLineState.OperationIndex + maxOperationAdvance) / MaximumOperationsPerInstruction); - deltaOperationIndex = debugLine.OperationIndex - (byte) ((previousLineState.OperationIndex + maxOperationAdvance) % MaximumOperationsPerInstruction); - } - else - { - deltaAddressSpecialOpCode255 = maxOperationAdvance; - deltaOperationIndex = 0; - } - - Debug.Assert(deltaAddressSpecialOpCode255 * MinimumInstructionLength < deltaAddress); - deltaAddress -= deltaAddressSpecialOpCode255 * MinimumInstructionLength; - - writer.WriteU8(DwarfNative.DW_LNS_const_add_pc); - } + writer.WriteU8(DwarfNative.DW_LNS_const_add_pc); + } - var operation_advance = deltaAddress * MaximumOperationsPerInstruction / MinimumInstructionLength + debugLine.OperationIndex; + var operation_advance = deltaAddress * MaximumOperationsPerInstruction / MinimumInstructionLength + debugLine.OperationIndex; - bool canEncodeAddressInSpecialCode = false; - ulong opcode = 256; - if (canEncodeSpecial && (operation_advance > 0 || deltaOperationIndex != 0 || deltaLine != 0)) + bool canEncodeAddressInSpecialCode = false; + ulong opcode = 256; + if (canEncodeSpecial && (operation_advance > 0 || deltaOperationIndex != 0 || deltaLine != 0)) + { + opcode = operation_advance * LineRange + opCodeBase + (ulong) (deltaLine - LineBase); + if (opcode > 255) { - opcode = operation_advance * LineRange + opCodeBase + (ulong) (deltaLine - LineBase); - if (opcode > 255) - { - if (deltaLine != 0) - { - opcode = opCodeBase + (ulong) (deltaLine - LineBase); - } - } - else + if (deltaLine != 0) { - canEncodeAddressInSpecialCode = true; + opcode = opCodeBase + (ulong) (deltaLine - LineBase); } } - - if (!operationAdvancedEncoded && !canEncodeAddressInSpecialCode) + else { - if (deltaAddress > 0 || deltaOperationIndex != 0) - { - writer.WriteU8(DwarfNative.DW_LNS_advance_pc); - writer.WriteULEB128(operation_advance); - } + canEncodeAddressInSpecialCode = true; } + } - // Special opcode - if (opcode <= 255) + if (!operationAdvancedEncoded && !canEncodeAddressInSpecialCode) + { + if (deltaAddress > 0 || deltaOperationIndex != 0) { - writer.WriteU8((byte) opcode); - debugLineState.SpecialReset(); - hasGeneratedRow = true; + writer.WriteU8(DwarfNative.DW_LNS_advance_pc); + writer.WriteULEB128(operation_advance); } + } - if (isEndOfSequence) - { - writer.WriteU8(0); - writer.WriteULEB128(1); - writer.WriteU8(DwarfNative.DW_LNE_end_sequence); + // Special opcode + if (opcode <= 255) + { + writer.WriteU8((byte) opcode); + debugLineState.SpecialReset(); + hasGeneratedRow = true; + } - hasGeneratedRow = true; + if (isEndOfSequence) + { + writer.WriteU8(0); + writer.WriteULEB128(1); + writer.WriteU8(DwarfNative.DW_LNE_end_sequence); - hasSetAddress = false; - previousLineState = initialState; - previousLineState.Reset(firstFile, true); - } - else - { - previousLineState = debugLineState; - } + hasGeneratedRow = true; - if (!hasGeneratedRow) - { - writer.WriteU8(DwarfNative.DW_LNS_copy); - } + hasSetAddress = false; + previousLineState = initialState; + previousLineState.Reset(firstFile, true); + } + else + { + previousLineState = debugLineState; + } - Debug.Assert(debugLine.Size == writer.Position - debugLine.Position, $"Expected Debug Line Size: {debugLine.Size} != Written Size: {writer.Position - debugLine.Position}"); + if (!hasGeneratedRow) + { + writer.WriteU8(DwarfNative.DW_LNS_copy); } + + Debug.Assert(debugLine.Size == writer.Position - debugLine.Position, $"Expected Debug Line Size: {debugLine.Size} != Written Size: {writer.Position - debugLine.Position}"); } } + } - private void LayoutDebugLineOpCodes(ref ulong sizeOf, uint opCodeBase) + private void LayoutDebugLineOpCodes(ref ulong sizeOf, uint opCodeBase) + { + var previousLineState = new DwarfLineState(); + var firstFile = FileNames.Count > 0 ? FileNames[0] : null; + previousLineState.Reset(firstFile, true); + var initialState = previousLineState; + + uint maxDeltaAddressPerSpecialCode; + byte maxOperationAdvance = (byte)((255 - OpCodeBase) / LineRange); + if (Version >= 4) { - var previousLineState = new DwarfLineState(); - var firstFile = FileNames.Count > 0 ? FileNames[0] : null; - previousLineState.Reset(firstFile, true); - var initialState = previousLineState; + maxDeltaAddressPerSpecialCode = (uint)maxOperationAdvance / MaximumOperationsPerInstruction; + } + else + { + maxDeltaAddressPerSpecialCode = maxOperationAdvance; + } + maxDeltaAddressPerSpecialCode *= MinimumInstructionLength; - uint maxDeltaAddressPerSpecialCode; - byte maxOperationAdvance = (byte)((255 - OpCodeBase) / LineRange); - if (Version >= 4) - { - maxDeltaAddressPerSpecialCode = (uint)maxOperationAdvance / MaximumOperationsPerInstruction; - } - else - { - maxDeltaAddressPerSpecialCode = maxOperationAdvance; - } - maxDeltaAddressPerSpecialCode *= MinimumInstructionLength; + bool hasSetAddress; + + foreach (var lineSequence in _lineSequences) + { + var lines = lineSequence.Lines; - bool hasSetAddress; + lineSequence.Position = Position + sizeOf; + hasSetAddress = false; - foreach (var lineSequence in _lineSequences) + for (var lineIndex = 0; lineIndex < lines.Count; lineIndex++) { - var lines = lineSequence.Lines; - - lineSequence.Position = Position + sizeOf; - hasSetAddress = false; + var debugLine = lines[lineIndex]; + ulong deltaAddress; + int deltaOperationIndex; + bool fileNameChanged; + int deltaLine; + int deltaColumn; + bool isStatementChanged; + bool isBasicBlockChanged; + bool isEndSequenceChanged; + bool isPrologueEndChanged; + bool isEpilogueBeginChanged; + bool isaChanged; + bool isDiscriminatorChanged; + + bool hasGeneratedRow = false; + + var debugLineState = debugLine.ToState(); + + previousLineState.Delta(debugLineState, out deltaAddress, + out deltaOperationIndex, + out fileNameChanged, + out deltaLine, + out deltaColumn, + out isStatementChanged, + out isBasicBlockChanged, + out isEndSequenceChanged, + out isPrologueEndChanged, + out isEpilogueBeginChanged, + out isaChanged, + out isDiscriminatorChanged); + + debugLine.Position = Position + sizeOf; + + // DW_LNS_set_column + if (deltaColumn != 0) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_column); + sizeOf += DwarfHelper.SizeOfULEB128(debugLine.Column); //writer.WriteLEB128(debugLine.Column)); + } - for (var lineIndex = 0; lineIndex < lines.Count; lineIndex++) + // DW_LNS_set_file or DW_LNE_define_file + if (fileNameChanged) { - var debugLine = lines[lineIndex]; - ulong deltaAddress; - int deltaOperationIndex; - bool fileNameChanged; - int deltaLine; - int deltaColumn; - bool isStatementChanged; - bool isBasicBlockChanged; - bool isEndSequenceChanged; - bool isPrologueEndChanged; - bool isEpilogueBeginChanged; - bool isaChanged; - bool isDiscriminatorChanged; - - bool hasGeneratedRow = false; - - var debugLineState = debugLine.ToState(); - - previousLineState.Delta(debugLineState, out deltaAddress, - out deltaOperationIndex, - out fileNameChanged, - out deltaLine, - out deltaColumn, - out isStatementChanged, - out isBasicBlockChanged, - out isEndSequenceChanged, - out isPrologueEndChanged, - out isEpilogueBeginChanged, - out isaChanged, - out isDiscriminatorChanged); - - debugLine.Position = Position + sizeOf; - - // DW_LNS_set_column - if (deltaColumn != 0) + var fileName = debugLine.File; + + if (fileName is null) { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_column); - sizeOf += DwarfHelper.SizeOfULEB128(debugLine.Column); //writer.WriteLEB128(debugLine.Column)); + throw new InvalidOperationException("File name cannot be null"); } - // DW_LNS_set_file or DW_LNE_define_file - if (fileNameChanged) + // DW_LNS_set_file + if (_fileNameToIndex.TryGetValue(fileName, out var fileIndex)) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_file); + sizeOf += DwarfHelper.SizeOfULEB128(fileIndex); // writer.WriteLEB128(fileIndex); + } + else { - var fileName = debugLine.File; + // DW_LNE_define_file + sizeOf += 1; // writer.WriteU8(0); + uint dirIndex = fileName.Directory != null && _directoryNameToIndex.ContainsKey(fileName.Directory) ? _directoryNameToIndex[fileName.Directory] : 0; - if (fileName is null) - { - throw new InvalidOperationException("File name cannot be null"); - } + ulong sizeOfInlineFileName = 1; + sizeOfInlineFileName += (ulong) Encoding.UTF8.GetByteCount(fileName.Name) + 1; + sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(dirIndex); + sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(fileName.Time); + sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(fileName.Size); - // DW_LNS_set_file - if (_fileNameToIndex.TryGetValue(fileName, out var fileIndex)) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_file); - sizeOf += DwarfHelper.SizeOfULEB128(fileIndex); // writer.WriteLEB128(fileIndex); - } - else - { - // DW_LNE_define_file - sizeOf += 1; // writer.WriteU8(0); - uint dirIndex = fileName.Directory != null && _directoryNameToIndex.ContainsKey(fileName.Directory) ? _directoryNameToIndex[fileName.Directory] : 0; - - ulong sizeOfInlineFileName = 1; - sizeOfInlineFileName += (ulong) Encoding.UTF8.GetByteCount(fileName.Name) + 1; - sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(dirIndex); - sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(fileName.Time); - sizeOfInlineFileName += DwarfHelper.SizeOfULEB128(fileName.Size); - - sizeOf += DwarfHelper.SizeOfULEB128(sizeOfInlineFileName); - sizeOf += sizeOfInlineFileName; - } + sizeOf += DwarfHelper.SizeOfULEB128(sizeOfInlineFileName); + sizeOf += sizeOfInlineFileName; } + } - // DW_LNS_copy - if (isBasicBlockChanged && !debugLine.IsBasicBlock || - isPrologueEndChanged && !debugLine.IsPrologueEnd || - isEpilogueBeginChanged && !debugLine.IsEpilogueBegin) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_copy); - isDiscriminatorChanged = debugLine.Discriminator != 0; - hasGeneratedRow = true; - } + // DW_LNS_copy + if (isBasicBlockChanged && !debugLine.IsBasicBlock || + isPrologueEndChanged && !debugLine.IsPrologueEnd || + isEpilogueBeginChanged && !debugLine.IsEpilogueBegin) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_copy); + isDiscriminatorChanged = debugLine.Discriminator != 0; + hasGeneratedRow = true; + } - // DW_LNS_set_basic_block - if (isBasicBlockChanged && debugLine.IsBasicBlock) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_basic_block); - } + // DW_LNS_set_basic_block + if (isBasicBlockChanged && debugLine.IsBasicBlock) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_basic_block); + } - // DW_LNS_set_prologue_end - if (isPrologueEndChanged && debugLine.IsPrologueEnd) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_prologue_end); - } + // DW_LNS_set_prologue_end + if (isPrologueEndChanged && debugLine.IsPrologueEnd) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_prologue_end); + } - // DW_LNS_set_epilogue_begin - if (isEpilogueBeginChanged && debugLine.IsEpilogueBegin) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_epilogue_begin); - } + // DW_LNS_set_epilogue_begin + if (isEpilogueBeginChanged && debugLine.IsEpilogueBegin) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_epilogue_begin); + } - // DW_LNS_set_isa - if (isaChanged) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_isa); - sizeOf += DwarfHelper.SizeOfULEB128(debugLine.Isa); // writer.WriteLEB128(debugLine.Isa); - } + // DW_LNS_set_isa + if (isaChanged) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_isa); + sizeOf += DwarfHelper.SizeOfULEB128(debugLine.Isa); // writer.WriteLEB128(debugLine.Isa); + } - // DW_LNE_set_discriminator - if (isDiscriminatorChanged) - { - sizeOf += 1; // writer.WriteU8(0); - var sizeOfDiscriminator = DwarfHelper.SizeOfULEB128(debugLine.Discriminator); - sizeOf += DwarfHelper.SizeOfULEB128(1 + sizeOfDiscriminator); // writer.WriteLEB128(1 + DwarfHelper.SizeOfLEB128(debugLine.Discriminator)); - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNE_set_discriminator); - sizeOf += sizeOfDiscriminator; // writer.WriteLEB128(debugLine.Discriminator); - } + // DW_LNE_set_discriminator + if (isDiscriminatorChanged) + { + sizeOf += 1; // writer.WriteU8(0); + var sizeOfDiscriminator = DwarfHelper.SizeOfULEB128(debugLine.Discriminator); + sizeOf += DwarfHelper.SizeOfULEB128(1 + sizeOfDiscriminator); // writer.WriteLEB128(1 + DwarfHelper.SizeOfLEB128(debugLine.Discriminator)); + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNE_set_discriminator); + sizeOf += sizeOfDiscriminator; // writer.WriteLEB128(debugLine.Discriminator); + } - // DW_LNS_negate_stmt - if (isStatementChanged) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_negate_stmt); - } + // DW_LNS_negate_stmt + if (isStatementChanged) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_negate_stmt); + } - bool isEndOfSequence = lineIndex + 1 == lines.Count; - bool canEncodeSpecial = !isEndOfSequence; - bool canEncodeLineInSpecialCode = canEncodeSpecial && deltaLine >= LineBase && deltaLine < LineBase + LineRange; - bool operationAdvancedEncoded = false; + bool isEndOfSequence = lineIndex + 1 == lines.Count; + bool canEncodeSpecial = !isEndOfSequence; + bool canEncodeLineInSpecialCode = canEncodeSpecial && deltaLine >= LineBase && deltaLine < LineBase + LineRange; + bool operationAdvancedEncoded = false; - if (!hasSetAddress) + if (!hasSetAddress) + { + sizeOf += 1; // writer.WriteU8(0); + var sizeOfAddress = DwarfHelper.SizeOfUInt(AddressSize); + sizeOf += DwarfHelper.SizeOfULEB128(1 + sizeOfAddress); // writer.WriteLEB128(DwarfHelper.SizeOfNativeInt(writer.IsTargetAddress64Bit)); + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNE_set_address); + sizeOf += sizeOfAddress; // writer.WriteLEB128(debugLine.Address); + operationAdvancedEncoded = true; + deltaAddress = 0; + hasSetAddress = true; + } + else if (deltaAddress > maxDeltaAddressPerSpecialCode && deltaAddress <= (2U * maxDeltaAddressPerSpecialCode)) + { + ulong deltaAddressSpecialOpCode255; + + if (Version >= 4) { - sizeOf += 1; // writer.WriteU8(0); - var sizeOfAddress = DwarfHelper.SizeOfUInt(AddressSize); - sizeOf += DwarfHelper.SizeOfULEB128(1 + sizeOfAddress); // writer.WriteLEB128(DwarfHelper.SizeOfNativeInt(writer.IsTargetAddress64Bit)); - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNE_set_address); - sizeOf += sizeOfAddress; // writer.WriteLEB128(debugLine.Address); - operationAdvancedEncoded = true; - deltaAddress = 0; - hasSetAddress = true; + deltaAddressSpecialOpCode255 = (((ulong) previousLineState.OperationIndex + maxOperationAdvance) / MaximumOperationsPerInstruction); + deltaOperationIndex = debugLine.OperationIndex - (byte) ((previousLineState.OperationIndex + maxOperationAdvance) % MaximumOperationsPerInstruction); } - else if (deltaAddress > maxDeltaAddressPerSpecialCode && deltaAddress <= (2U * maxDeltaAddressPerSpecialCode)) + else { - ulong deltaAddressSpecialOpCode255; - - if (Version >= 4) - { - deltaAddressSpecialOpCode255 = (((ulong) previousLineState.OperationIndex + maxOperationAdvance) / MaximumOperationsPerInstruction); - deltaOperationIndex = debugLine.OperationIndex - (byte) ((previousLineState.OperationIndex + maxOperationAdvance) % MaximumOperationsPerInstruction); - } - else - { - deltaAddressSpecialOpCode255 = maxOperationAdvance; - deltaOperationIndex = 0; - } + deltaAddressSpecialOpCode255 = maxOperationAdvance; + deltaOperationIndex = 0; + } - Debug.Assert(deltaAddressSpecialOpCode255 * MinimumInstructionLength < deltaAddress); - deltaAddress -= deltaAddressSpecialOpCode255 * MinimumInstructionLength; + Debug.Assert(deltaAddressSpecialOpCode255 * MinimumInstructionLength < deltaAddress); + deltaAddress -= deltaAddressSpecialOpCode255 * MinimumInstructionLength; - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_const_add_pc); - } + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_const_add_pc); + } - // DW_LNS_advance_line - if (!canEncodeLineInSpecialCode) + // DW_LNS_advance_line + if (!canEncodeLineInSpecialCode) + { + if (deltaLine != 0) { - if (deltaLine != 0) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_advance_line); - sizeOf += DwarfHelper.SizeOfILEB128(deltaLine); // writer.WriteSignedLEB128(deltaLine); - deltaLine = 0; - } + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_advance_line); + sizeOf += DwarfHelper.SizeOfILEB128(deltaLine); // writer.WriteSignedLEB128(deltaLine); + deltaLine = 0; } + } - var operation_advance = deltaAddress * MaximumOperationsPerInstruction / MinimumInstructionLength + debugLine.OperationIndex; + var operation_advance = deltaAddress * MaximumOperationsPerInstruction / MinimumInstructionLength + debugLine.OperationIndex; - bool canEncodeAddress = false; - ulong opcode = 256; - if (canEncodeSpecial && (operation_advance > 0 || deltaOperationIndex > 0 || deltaLine != 0)) + bool canEncodeAddress = false; + ulong opcode = 256; + if (canEncodeSpecial && (operation_advance > 0 || deltaOperationIndex > 0 || deltaLine != 0)) + { + opcode = operation_advance * LineRange + opCodeBase + (ulong) (deltaLine - LineBase); + if (opcode > 255) { - opcode = operation_advance * LineRange + opCodeBase + (ulong) (deltaLine - LineBase); - if (opcode > 255) - { - if (deltaLine != 0) - { - opcode = opCodeBase + (ulong) (deltaLine - LineBase); - } - } - else + if (deltaLine != 0) { - canEncodeAddress = true; + opcode = opCodeBase + (ulong) (deltaLine - LineBase); } } - - if (!operationAdvancedEncoded && !canEncodeAddress) + else { - if (deltaAddress > 0 || deltaOperationIndex > 0) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_advance_pc); - sizeOf += DwarfHelper.SizeOfULEB128(operation_advance); // writer.WriteLEB128(operation_advance); - } + canEncodeAddress = true; } + } - // Special opcode - if (opcode <= 255) + if (!operationAdvancedEncoded && !canEncodeAddress) + { + if (deltaAddress > 0 || deltaOperationIndex > 0) { - sizeOf += 1; // writer.WriteU8((byte)opcode); - debugLineState.SpecialReset(); - hasGeneratedRow = true; + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_advance_pc); + sizeOf += DwarfHelper.SizeOfULEB128(operation_advance); // writer.WriteLEB128(operation_advance); } + } - if (isEndOfSequence) - { - sizeOf += 3; // writer.WriteU8(0); - // writer.WriteLEB128(1); - // writer.WriteU8(DwarfNative.DW_LNE_end_sequence); - previousLineState = initialState; - previousLineState.Reset(firstFile, true); - hasGeneratedRow = true; - hasSetAddress = false; - } - else - { - previousLineState = debugLineState; - } + // Special opcode + if (opcode <= 255) + { + sizeOf += 1; // writer.WriteU8((byte)opcode); + debugLineState.SpecialReset(); + hasGeneratedRow = true; + } - if (!hasGeneratedRow) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_copy); - } + if (isEndOfSequence) + { + sizeOf += 3; // writer.WriteU8(0); + // writer.WriteLEB128(1); + // writer.WriteU8(DwarfNative.DW_LNE_end_sequence); + previousLineState = initialState; + previousLineState.Reset(firstFile, true); + hasGeneratedRow = true; + hasSetAddress = false; + } + else + { + previousLineState = debugLineState; + } - debugLine.Size = Position + sizeOf - debugLine.Position; + if (!hasGeneratedRow) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_copy); } - lineSequence.Size = Position + sizeOf - lineSequence.Position; + + debugLine.Size = Position + sizeOf - debugLine.Position; } + lineSequence.Size = Position + sizeOf - lineSequence.Position; } + } - private static ReadOnlySpan DefaultStandardOpCodeLengths => new ReadOnlySpan(new byte[12] - { - 0, // DwarfNative.DW_LNS_copy - 1, // DwarfNative.DW_LNS_advance_pc - 1, // DwarfNative.DW_LNS_advance_line - 1, // DwarfNative.DW_LNS_set_file - 1, // DwarfNative.DW_LNS_set_column - 0, // DwarfNative.DW_LNS_negate_stmt - 0, // DwarfNative.DW_LNS_set_basic_block - 0, // DwarfNative.DW_LNS_const_add_pc - 1, // DwarfNative.DW_LNS_fixed_advance_pc - 0, // DwarfNative.DW_LNS_set_prologue_end - 0, // DwarfNative.DW_LNS_set_epilogue_begin - 1, // DwarfNative.DW_LNS_set_isa - }); - - public override string ToString() - { - return $"Section .debug_line, {nameof(Version)}: {Version}, {nameof(Is64BitEncoding)}: {Is64BitEncoding}, {nameof(FileNames)}: {FileNames.Count}, {nameof(LineSequences)}: {LineSequences.Count}"; - } + private static ReadOnlySpan DefaultStandardOpCodeLengths => new ReadOnlySpan(new byte[12] + { + 0, // DwarfNative.DW_LNS_copy + 1, // DwarfNative.DW_LNS_advance_pc + 1, // DwarfNative.DW_LNS_advance_line + 1, // DwarfNative.DW_LNS_set_file + 1, // DwarfNative.DW_LNS_set_column + 0, // DwarfNative.DW_LNS_negate_stmt + 0, // DwarfNative.DW_LNS_set_basic_block + 0, // DwarfNative.DW_LNS_const_add_pc + 1, // DwarfNative.DW_LNS_fixed_advance_pc + 0, // DwarfNative.DW_LNS_set_prologue_end + 0, // DwarfNative.DW_LNS_set_epilogue_begin + 1, // DwarfNative.DW_LNS_set_isa + }); + + protected override bool PrintMembers(StringBuilder builder) + { + builder.AppendLine($"Section .debug_line, {nameof(Version)}: {Version}, {nameof(Is64BitEncoding)}: {Is64BitEncoding}, {nameof(FileNames)}: {FileNames.Count}, {nameof(LineSequences)}: {LineSequences.Count}"); + return true; } -} +} \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLineSection.cs b/src/LibObjectFile/Dwarf/DwarfLineSection.cs index 19fba82..d426f62 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSection.cs @@ -1,91 +1,89 @@ - -// 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.Collections.Generic; using System.Diagnostics; +using System.Text; using LibObjectFile.Utils; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[DebuggerDisplay("Count = {LineTables.Count,nq}")] +public sealed class DwarfLineSection : DwarfRelocatableSection { - [DebuggerDisplay("Count = {LineTables.Count,nq}")] - public sealed class DwarfLineSection : DwarfRelocatableSection + private readonly List _tables; + + + public DwarfLineSection() { - private readonly List _tables; + _tables = new List(); + } + public ReadOnlyList LineTables => _tables; - public DwarfLineSection() - { - _tables = new List(); - } + public void AddLineProgramTable(DwarfLineProgramTable line) + { + _tables.Add(this, line); + } - public ReadOnlyList LineTables => _tables; + public void RemoveLineProgramTable(DwarfLineProgramTable line) + { + _tables.Remove(this, line); + } - public void AddLineProgramTable(DwarfLineProgramTable line) - { - _tables.Add(this, line); - } + public DwarfLineProgramTable RemoveLineProgramTableAt(int index) + { + return _tables.RemoveAt(this, index); + } - public void RemoveLineProgramTable(DwarfLineProgramTable line) + public override void Read(DwarfReader reader) + { + while (reader.Position < reader.Length) { - _tables.Remove(this, line); + var programTable = new DwarfLineProgramTable(); + programTable.Position = reader.Position; + programTable.Read(reader); + AddLineProgramTable(programTable); } + } - public DwarfLineProgramTable RemoveLineProgramTableAt(int index) + public override void Verify(DwarfVerifyContext context) + { + foreach (var dwarfLineProgramTable in _tables) { - return _tables.RemoveAt(this, index); + dwarfLineProgramTable.Verify(context); } + } - protected override void Read(DwarfReader reader) - { - while (reader.Position < reader.Length) - { - var programTable = new DwarfLineProgramTable(); - programTable.Position = reader.Position; - programTable.ReadInternal(reader); - AddLineProgramTable(programTable); - } - } + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + ulong sizeOf = 0; - public override void Verify(DiagnosticBag diagnostics) + foreach (var dwarfLineProgramTable in _tables) { - base.Verify(diagnostics); - - foreach (var dwarfLineProgramTable in _tables) - { - dwarfLineProgramTable.Verify(diagnostics); - } + dwarfLineProgramTable.Position = Position + sizeOf; + dwarfLineProgramTable.UpdateLayout(layoutContext); + sizeOf += dwarfLineProgramTable.Size; } + Size = sizeOf; + } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - ulong sizeOf = 0; - - foreach (var dwarfLineProgramTable in _tables) - { - dwarfLineProgramTable.Position = Position + sizeOf; - dwarfLineProgramTable.UpdateLayoutInternal(layoutContext); - sizeOf += dwarfLineProgramTable.Size; - } - Size = sizeOf; - } + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; - protected override void Write(DwarfWriter writer) + foreach (var dwarfLineProgramTable in _tables) { - var startOffset = writer.Position; - - foreach (var dwarfLineProgramTable in _tables) - { - dwarfLineProgramTable.WriteInternal(writer); - } - - Debug.Assert(Size == writer.Position - startOffset, $"Expected Size: {Size} != Written Size: {writer.Position - startOffset}"); + dwarfLineProgramTable.Write(writer); } - public override string ToString() - { - return $"Section .debug_line, Entries: {_tables.Count}"; - } + Debug.Assert(Size == writer.Position - startOffset, $"Expected Size: {Size} != Written Size: {writer.Position - startOffset}"); + } + + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"Section .debug_line, Entries: {_tables.Count}"); + return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs index 23583e0..9fd1884 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs @@ -7,66 +7,65 @@ using System.Diagnostics; using LibObjectFile.Utils; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +/// +/// A sequence of +/// +[DebuggerDisplay("Count = {Lines.Count,nq}")] +public class DwarfLineSequence : DwarfObject, IEnumerable { - /// - /// A sequence of - /// - [DebuggerDisplay("Count = {Lines.Count,nq}")] - public class DwarfLineSequence : DwarfObject, IEnumerable + private readonly List _lines; + + public DwarfLineSequence() { - private readonly List _lines; + _lines = new List(); + } - public DwarfLineSequence() - { - _lines = new List(); - } + public ReadOnlyList Lines => _lines; - public ReadOnlyList Lines => _lines; + public void Add(DwarfLine line) + { + _lines.Add(this, line); + } - public void Add(DwarfLine line) - { - _lines.Add(this, line); - } + public void Remove(DwarfLine line) + { + _lines.Remove(this, line); + } - public void Remove(DwarfLine line) - { - _lines.Remove(this, line); - } + public DwarfLine RemoveAt(int index) + { + return _lines.RemoveAt(this, index); + } - public DwarfLine RemoveAt(int index) - { - return _lines.RemoveAt(this, index); - } - - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - // This is implemented in DwarfLineSection - } + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + // This is implemented in DwarfLineSection + } - protected override void Read(DwarfReader reader) - { - // This is implemented in DwarfLineSection - } + public override void Read(DwarfReader reader) + { + // This is implemented in DwarfLineSection + } - protected override void Write(DwarfWriter writer) - { - // This is implemented in DwarfLineSection - } + public override void Write(DwarfWriter writer) + { + // This is implemented in DwarfLineSection + } - public List.Enumerator GetEnumerator() - { - return _lines.GetEnumerator(); - } + public List.Enumerator GetEnumerator() + { + return _lines.GetEnumerator(); + } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable) _lines).GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable) _lines).GetEnumerator(); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLineState.cs b/src/LibObjectFile/Dwarf/DwarfLineState.cs index 9bb37e4..dd3d15d 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineState.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineState.cs @@ -4,154 +4,153 @@ using System.Globalization; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +/// +/// Internal struct used to track line state via a struct, created via +/// +internal struct DwarfLineState { + // ----------------------- + // DWARF 2 + // ----------------------- + + /// + /// The program-counter value corresponding to a machine instruction generated by the compiler. + /// + public ulong Address { get; set; } + + /// + /// An unsigned integer representing the index of an operation within a VLIW instruction. + /// The index of the first operation is 0. For non-VLIW architectures, this register will always be 0. + /// + public byte OperationIndex { get; set; } + + /// + /// The identity of the source file corresponding to a machine instruction. + /// + public DwarfFileName? File { get; set; } + + /// + /// An unsigned integer indicating a source line number. + /// Lines are numbered beginning at 1. + /// The compiler may emit the value 0 in cases where an instruction cannot be attributed to any source line. + /// + public uint Line { get; set; } + + /// + /// An unsigned integer indicating a column number within a source line. + /// Columns are numbered beginning at 1. The value 0 is reserved to indicate that a statement begins at the “left edge” of the line. + /// + public uint Column { get; set; } + + /// + /// A boolean indicating that the current instruction is a recommended breakpoint location. + /// A recommended breakpoint location is intended to “represent” a line, a statement and/or a semantically distinct subpart of a statement. + /// + public bool IsStatement { get; set; } + + /// + /// A boolean indicating that the current instruction is the beginning of a basic block. + /// + public bool IsBasicBlock { get; set; } + + /// + /// A boolean indicating that the current address is that of the first byte after the end of a sequence of target machine instructions. + /// IsEndSequence terminates a sequence of lines; therefore other information in the same row is not meaningful. + /// + public bool IsEndSequence { get; set; } + + // ----------------------- + // DWARF 3 + // ----------------------- + /// - /// Internal struct used to track line state via a struct, created via + /// A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an entry breakpoint of a function. /// - internal struct DwarfLineState + public bool IsPrologueEnd { get; set; } + + /// + /// A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an exit breakpoint of a function. + /// + public bool IsEpilogueBegin { get; set; } + + /// + /// An unsigned integer whose value encodes the applicable instruction set architecture for the current instruction. + /// + public ulong Isa { get; set; } + + // ----------------------- + // DWARF 4 + // ----------------------- + + /// + /// An unsigned integer identifying the block to which the current instruction belongs. + /// Discriminator values are assigned arbitrarily by the DWARF producer and serve to distinguish among multiple blocks that may all be + /// associated with the same source file, line, and column. + /// Where only one block exists for a given source position, the discriminator value should be zero. + /// + public ulong Discriminator { get; set; } + + internal void Delta(in DwarfLineState against, + out ulong deltaAddress, + out int deltaOperationIndex, + out bool fileNameChanged, + out int deltaLine, + out int deltaColumn, + out bool isStatementChanged, + out bool isBasicBlockChanged, + out bool isEndSequenceChanged, + out bool isPrologueEndChanged, + out bool isEpilogueBeginChanged, + out bool isaChanged, + out bool isDiscriminatorChanged) { - // ----------------------- - // DWARF 2 - // ----------------------- - - /// - /// The program-counter value corresponding to a machine instruction generated by the compiler. - /// - public ulong Address { get; set; } - - /// - /// An unsigned integer representing the index of an operation within a VLIW instruction. - /// The index of the first operation is 0. For non-VLIW architectures, this register will always be 0. - /// - public byte OperationIndex { get; set; } - - /// - /// The identity of the source file corresponding to a machine instruction. - /// - public DwarfFileName? File { get; set; } - - /// - /// An unsigned integer indicating a source line number. - /// Lines are numbered beginning at 1. - /// The compiler may emit the value 0 in cases where an instruction cannot be attributed to any source line. - /// - public uint Line { get; set; } - - /// - /// An unsigned integer indicating a column number within a source line. - /// Columns are numbered beginning at 1. The value 0 is reserved to indicate that a statement begins at the “left edge” of the line. - /// - public uint Column { get; set; } - - /// - /// A boolean indicating that the current instruction is a recommended breakpoint location. - /// A recommended breakpoint location is intended to “represent” a line, a statement and/or a semantically distinct subpart of a statement. - /// - public bool IsStatement { get; set; } - - /// - /// A boolean indicating that the current instruction is the beginning of a basic block. - /// - public bool IsBasicBlock { get; set; } - - /// - /// A boolean indicating that the current address is that of the first byte after the end of a sequence of target machine instructions. - /// IsEndSequence terminates a sequence of lines; therefore other information in the same row is not meaningful. - /// - public bool IsEndSequence { get; set; } - - // ----------------------- + deltaAddress = against.Address - this.Address; + deltaOperationIndex = against.OperationIndex - this.OperationIndex; + fileNameChanged = !ReferenceEquals(this.File, against.File); + deltaLine = (int)((long)against.Line - (long)this.Line); + deltaColumn = (int)((long)against.Column - (long)this.Column); + isStatementChanged = against.IsStatement != this.IsStatement; + isBasicBlockChanged = against.IsBasicBlock != this.IsBasicBlock; + isEndSequenceChanged = against.IsEndSequence != this.IsEndSequence; + isPrologueEndChanged = against.IsPrologueEnd != this.IsPrologueEnd; + isEpilogueBeginChanged = against.IsEpilogueBegin != this.IsEpilogueBegin; + isaChanged = against.Isa != this.Isa; + isDiscriminatorChanged = against.Discriminator != this.Discriminator; + } + + internal void Reset(DwarfFileName? firstFile, bool isStatement) + { + Address = 0; + File = firstFile; + Line = 1; + Column = 0; + this.IsStatement = isStatement; + IsBasicBlock = false; + IsEndSequence = false; + // DWARF 3 - // ----------------------- - - /// - /// A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an entry breakpoint of a function. - /// - public bool IsPrologueEnd { get; set; } - - /// - /// A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an exit breakpoint of a function. - /// - public bool IsEpilogueBegin { get; set; } - - /// - /// An unsigned integer whose value encodes the applicable instruction set architecture for the current instruction. - /// - public ulong Isa { get; set; } - - // ----------------------- - // DWARF 4 - // ----------------------- - - /// - /// An unsigned integer identifying the block to which the current instruction belongs. - /// Discriminator values are assigned arbitrarily by the DWARF producer and serve to distinguish among multiple blocks that may all be - /// associated with the same source file, line, and column. - /// Where only one block exists for a given source position, the discriminator value should be zero. - /// - public ulong Discriminator { get; set; } - - internal void Delta(in DwarfLineState against, - out ulong deltaAddress, - out int deltaOperationIndex, - out bool fileNameChanged, - out int deltaLine, - out int deltaColumn, - out bool isStatementChanged, - out bool isBasicBlockChanged, - out bool isEndSequenceChanged, - out bool isPrologueEndChanged, - out bool isEpilogueBeginChanged, - out bool isaChanged, - out bool isDiscriminatorChanged) - { - deltaAddress = against.Address - this.Address; - deltaOperationIndex = against.OperationIndex - this.OperationIndex; - fileNameChanged = !ReferenceEquals(this.File, against.File); - deltaLine = (int)((long)against.Line - (long)this.Line); - deltaColumn = (int)((long)against.Column - (long)this.Column); - isStatementChanged = against.IsStatement != this.IsStatement; - isBasicBlockChanged = against.IsBasicBlock != this.IsBasicBlock; - isEndSequenceChanged = against.IsEndSequence != this.IsEndSequence; - isPrologueEndChanged = against.IsPrologueEnd != this.IsPrologueEnd; - isEpilogueBeginChanged = against.IsEpilogueBegin != this.IsEpilogueBegin; - isaChanged = against.Isa != this.Isa; - isDiscriminatorChanged = against.Discriminator != this.Discriminator; - } - - internal void Reset(DwarfFileName? firstFile, bool isStatement) - { - Address = 0; - File = firstFile; - Line = 1; - Column = 0; - this.IsStatement = isStatement; - IsBasicBlock = false; - IsEndSequence = false; - - // DWARF 3 - IsPrologueEnd = false; - IsEpilogueBegin = false; - Isa = 0; - - // DWARF 5 - Discriminator = 0; - } - - internal void SpecialReset() - { - IsBasicBlock = false; - IsPrologueEnd = false; - IsEpilogueBegin = false; - Discriminator = 0; - } - - public override string ToString() - { - return $"{nameof(Address)}: 0x{Address:x16}, {nameof(File)}: {File}, {nameof(Line)}: {Line,4}, {nameof(Column)}: {Column,2}, {nameof(IsStatement)}: {Bool2Str(IsStatement),5}, {nameof(IsBasicBlock)}: {Bool2Str(IsBasicBlock),5}, {nameof(IsEndSequence)}: {Bool2Str(IsEndSequence),5}, {nameof(IsPrologueEnd)}: {Bool2Str(IsPrologueEnd),5}, {nameof(IsEpilogueBegin)}: {Bool2Str(IsEpilogueBegin),5}, {nameof(Isa)}: {Isa,3}, {nameof(Discriminator)}: {Discriminator,3}"; - } - - private static string Bool2Str(bool value) => value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(); + IsPrologueEnd = false; + IsEpilogueBegin = false; + Isa = 0; + + // DWARF 5 + Discriminator = 0; + } + + internal void SpecialReset() + { + IsBasicBlock = false; + IsPrologueEnd = false; + IsEpilogueBegin = false; + Discriminator = 0; + } + + public override string ToString() + { + return $"{nameof(Address)}: 0x{Address:x16}, {nameof(File)}: {File}, {nameof(Line)}: {Line,4}, {nameof(Column)}: {Column,2}, {nameof(IsStatement)}: {Bool2Str(IsStatement),5}, {nameof(IsBasicBlock)}: {Bool2Str(IsBasicBlock),5}, {nameof(IsEndSequence)}: {Bool2Str(IsEndSequence),5}, {nameof(IsPrologueEnd)}: {Bool2Str(IsPrologueEnd),5}, {nameof(IsEpilogueBegin)}: {Bool2Str(IsEpilogueBegin),5}, {nameof(Isa)}: {Isa,3}, {nameof(Discriminator)}: {Discriminator,3}"; } + + private static string Bool2Str(bool value) => value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(); } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLocation.cs b/src/LibObjectFile/Dwarf/DwarfLocation.cs index 1fccf4a..59bffbb 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocation.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocation.cs @@ -2,54 +2,53 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public struct DwarfLocation { - public struct DwarfLocation + public DwarfLocation(int value) + { + AsValue = new DwarfInteger() { I64 = value }; + AsObject = null; + } + + public DwarfLocation(DwarfExpression expression) + { + AsValue = default; + AsObject = expression; + } + + public DwarfLocation(DwarfLocationList locationList) + { + AsValue = default; + AsObject = locationList; + } + + public DwarfInteger AsValue; + + public object? AsObject; + + public DwarfExpression? AsExpression => AsObject as DwarfExpression; + + public DwarfLocationList? AsLocationList => AsObject as DwarfLocationList; + + public DwarfDIE? AsReference => AsObject as DwarfDIE; + + public override string ToString() + { + if (AsExpression != null) return $"Location Expression: {AsExpression}"; + if (AsLocationList != null) return $"Location List: {AsLocationList}"; + if (AsReference != null) return $"Location Reference: {AsReference}"; + return $"Location Constant: {AsValue}"; + } + + public static implicit operator DwarfLocation(DwarfExpression value) + { + return new DwarfLocation(value); + } + + public static implicit operator DwarfLocation(DwarfLocationList value) { - public DwarfLocation(int value) - { - AsValue = new DwarfInteger() { I64 = value }; - AsObject = null; - } - - public DwarfLocation(DwarfExpression expression) - { - AsValue = default; - AsObject = expression; - } - - public DwarfLocation(DwarfLocationList locationList) - { - AsValue = default; - AsObject = locationList; - } - - public DwarfInteger AsValue; - - public object? AsObject; - - public DwarfExpression? AsExpression => AsObject as DwarfExpression; - - public DwarfLocationList? AsLocationList => AsObject as DwarfLocationList; - - public DwarfDIE? AsReference => AsObject as DwarfDIE; - - public override string ToString() - { - if (AsExpression != null) return $"Location Expression: {AsExpression}"; - if (AsLocationList != null) return $"Location List: {AsLocationList}"; - if (AsReference != null) return $"Location Reference: {AsReference}"; - return $"Location Constant: {AsValue}"; - } - - public static implicit operator DwarfLocation(DwarfExpression value) - { - return new DwarfLocation(value); - } - - public static implicit operator DwarfLocation(DwarfLocationList value) - { - return new DwarfLocation(value); - } + return new DwarfLocation(value); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLocationList.cs b/src/LibObjectFile/Dwarf/DwarfLocationList.cs index 2b4e3ec..f22dfdd 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationList.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationList.cs @@ -6,103 +6,99 @@ using System.Collections.Generic; using LibObjectFile.Utils; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfLocationList : DwarfContainer { + private readonly List _locationListEntries; - public class DwarfLocationList : DwarfContainer + public DwarfLocationList() { - private readonly List _locationListEntries; + _locationListEntries = new List(); + } - public DwarfLocationList() - { - _locationListEntries = new List(); - } + public ReadOnlyList LocationListEntries => _locationListEntries; - public ReadOnlyList LocationListEntries => _locationListEntries; + public void AddLocationListEntry(DwarfLocationListEntry locationListEntry) + { + _locationListEntries.Add(this, locationListEntry); + } - public void AddLocationListEntry(DwarfLocationListEntry locationListEntry) - { - _locationListEntries.Add(this, locationListEntry); - } + public void RemoveLocationList(DwarfLocationListEntry locationListEntry) + { + _locationListEntries.Remove(this, locationListEntry); + } - public void RemoveLocationList(DwarfLocationListEntry locationListEntry) - { - _locationListEntries.Remove(this, locationListEntry); - } + public DwarfLocationListEntry RemoveLocationListEntryAt(int index) + { + return _locationListEntries.RemoveAt(this, index); + } - public DwarfLocationListEntry RemoveLocationListEntryAt(int index) - { - return _locationListEntries.RemoveAt(this, index); - } + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + var endOffset = Position; - protected override void UpdateLayout(DwarfLayoutContext layoutContext) + foreach (var locationListEntry in _locationListEntries) { - var endOffset = Position; + locationListEntry.Position = endOffset; + locationListEntry.UpdateLayout(layoutContext); + endOffset += locationListEntry.Size; + } - foreach (var locationListEntry in _locationListEntries) - { - locationListEntry.Position = endOffset; - locationListEntry.UpdateLayoutInternal(layoutContext); - endOffset += locationListEntry.Size; - } + // End of list + endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); - // End of list - endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); + Size = endOffset - Position; + } - Size = endOffset - Position; - } + public override void Read(DwarfReader reader) + { + reader.OffsetToLocationList.Add(reader.Position, this); - protected override void Read(DwarfReader reader) + while (reader.Position < reader.Length) { - reader.OffsetToLocationList.Add(reader.Position, this); + var locationListEntry = new DwarfLocationListEntry(); + locationListEntry.Read(reader); - while (reader.Position < reader.Length) + if (locationListEntry.Start == 0 && locationListEntry.End == 0) { - var locationListEntry = new DwarfLocationListEntry(); - locationListEntry.ReadInternal(reader); - - if (locationListEntry.Start == 0 && locationListEntry.End == 0) - { - // End of list - return; - } - - _locationListEntries.Add(locationListEntry); + // End of list + return; } + + _locationListEntries.Add(locationListEntry); } + } - protected override void Write(DwarfWriter writer) + public override void Write(DwarfWriter writer) + { + foreach (var locationListEntry in _locationListEntries) { - foreach (var locationListEntry in _locationListEntries) - { - locationListEntry.WriteInternal(writer); - } - - // End of list - writer.WriteUInt(0); - writer.WriteUInt(0); + locationListEntry.Write(writer); } - public override string ToString() - { - var builder = new StringBuilder(); + // End of list + writer.WriteUInt(0); + writer.WriteUInt(0); + } - for (int i = 0; i < _locationListEntries.Count; i++) + protected override bool PrintMembers(StringBuilder builder) + { + for (int i = 0; i < _locationListEntries.Count; i++) + { + if (i == 3) + { + builder.Append(", ..."); + break; + } + else if (i != 0) { - if (i == 3) - { - builder.Append(", ..."); - return builder.ToString(); - } - else if (i != 0) - { - builder.Append(", "); - } - - builder.Append(_locationListEntries[i].ToString()); + builder.Append(", "); } - return builder.ToString(); + builder.Append(_locationListEntries[i].ToString()); } + + return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs b/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs index c127e02..841b1f4 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs @@ -2,84 +2,86 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +using System.Text; + +namespace LibObjectFile.Dwarf; + +public class DwarfLocationListEntry : DwarfObject { - public class DwarfLocationListEntry : DwarfObject - { - public ulong Start; + public ulong Start; + + public ulong End; + + public DwarfExpression? Expression; - public ulong End; + public DwarfLocationListEntry() + { + } - public DwarfExpression? Expression; + public override void Read(DwarfReader reader) + { + Start = reader.ReadUInt(); + End = reader.ReadUInt(); - public DwarfLocationListEntry() + if (Start == 0 && End == 0) { + // End of list + return; } - protected override void Read(DwarfReader reader) + bool isBaseAddress = + (reader.AddressSize == DwarfAddressSize.Bit64 && Start == ulong.MaxValue) || + (reader.AddressSize == DwarfAddressSize.Bit32 && Start == uint.MaxValue); + if (isBaseAddress) { - Start = reader.ReadUInt(); - End = reader.ReadUInt(); - - if (Start == 0 && End == 0) - { - // End of list - return; - } - - bool isBaseAddress = - (reader.AddressSize == DwarfAddressSize.Bit64 && Start == ulong.MaxValue) || - (reader.AddressSize == DwarfAddressSize.Bit32 && Start == uint.MaxValue); - if (isBaseAddress) - { - // Sets new base address for following entries - return; - } - - Expression = new DwarfExpression(); - Expression.ReadInternal(reader, inLocationSection: true); + // Sets new base address for following entries + return; } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - var endOffset = Position; + Expression = new DwarfExpression(); + Expression.ReadInternal(reader, inLocationSection: true); + } - endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); - if (Expression != null) - { - Expression.Position = endOffset; - Expression.UpdateLayoutInternal(layoutContext, inLocationSection: true); - endOffset += Expression.Size; - } + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + var endOffset = Position; - Size = endOffset - Position; + endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); + if (Expression != null) + { + Expression.Position = endOffset; + Expression.UpdateLayout(layoutContext, inLocationSection: true); + endOffset += Expression.Size; } - protected override void Write(DwarfWriter writer) + Size = endOffset - Position; + } + + public override void Write(DwarfWriter writer) + { + bool isBaseAddress = + (writer.AddressSize == DwarfAddressSize.Bit64 && Start == ulong.MaxValue) || + (writer.AddressSize == DwarfAddressSize.Bit32 && Start == uint.MaxValue); + if (isBaseAddress) { - bool isBaseAddress = - (writer.AddressSize == DwarfAddressSize.Bit64 && Start == ulong.MaxValue) || - (writer.AddressSize == DwarfAddressSize.Bit32 && Start == uint.MaxValue); - if (isBaseAddress) - { - writer.WriteUInt(Start); - writer.WriteAddress(DwarfRelocationTarget.Code, End); - } - else - { - writer.WriteAddress(DwarfRelocationTarget.Code, Start); - writer.WriteAddress(DwarfRelocationTarget.Code, End); - } - - if (Expression != null) - { - Expression.WriteInternal(writer, inLocationSection: true); - } + writer.WriteUInt(Start); + writer.WriteAddress(DwarfRelocationTarget.Code, End); + } + else + { + writer.WriteAddress(DwarfRelocationTarget.Code, Start); + writer.WriteAddress(DwarfRelocationTarget.Code, End); } - public override string ToString() + if (Expression != null) { - return $"Location: {Start:x} - {End:x} {Expression}"; + Expression.WriteInternal(writer, inLocationSection: true); } } -} + + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"Location: {Start:x} - {End:x} {Expression}"); + return true; + } +} \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs index 95fe731..eb3fd4b 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs @@ -1,90 +1,88 @@ - -// 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.Collections.Generic; using System.Diagnostics; +using System.Text; using LibObjectFile.Utils; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[DebuggerDisplay("Count = {LineTables.Count,nq}")] +public sealed class DwarfLocationSection : DwarfRelocatableSection { - [DebuggerDisplay("Count = {LineTables.Count,nq}")] - public sealed class DwarfLocationSection : DwarfRelocatableSection + private readonly List _locationLists; + + public DwarfLocationSection() { - private readonly List _locationLists; + _locationLists = new List(); + } - public DwarfLocationSection() - { - _locationLists = new List(); - } + public ReadOnlyList LocationLists => _locationLists; - public ReadOnlyList LocationLists => _locationLists; + public void AddLocationList(DwarfLocationList locationList) + { + _locationLists.Add(this, locationList); + } - public void AddLocationList(DwarfLocationList locationList) - { - _locationLists.Add(this, locationList); - } + public void RemoveLocationList(DwarfLocationList locationList) + { + _locationLists.Remove(this, locationList); + } - public void RemoveLocationList(DwarfLocationList locationList) - { - _locationLists.Remove(this, locationList); - } + public DwarfLocationList RemoveLineProgramTableAt(int index) + { + return _locationLists.RemoveAt(this, index); + } - public DwarfLocationList RemoveLineProgramTableAt(int index) + public override void Read(DwarfReader reader) + { + while (reader.Position < reader.Length) { - return _locationLists.RemoveAt(this, index); + var locationList = new DwarfLocationList(); + locationList.Position = reader.Position; + locationList.Read(reader); + AddLocationList(locationList); } + } - protected override void Read(DwarfReader reader) + public override void Verify(DwarfVerifyContext context) + { + foreach (var locationList in _locationLists) { - while (reader.Position < reader.Length) - { - var locationList = new DwarfLocationList(); - locationList.Position = reader.Position; - locationList.ReadInternal(reader); - AddLocationList(locationList); - } + locationList.Verify(context); } + } - public override void Verify(DiagnosticBag diagnostics) - { - base.Verify(diagnostics); - - foreach (var locationList in _locationLists) - { - locationList.Verify(diagnostics); - } - } + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + ulong sizeOf = 0; - protected override void UpdateLayout(DwarfLayoutContext layoutContext) + foreach (var locationList in _locationLists) { - ulong sizeOf = 0; - - foreach (var locationList in _locationLists) - { - locationList.Position = Position + sizeOf; - locationList.UpdateLayoutInternal(layoutContext); - sizeOf += locationList.Size; - } - Size = sizeOf; + locationList.Position = Position + sizeOf; + locationList.UpdateLayout(layoutContext); + sizeOf += locationList.Size; } + Size = sizeOf; + } - protected override void Write(DwarfWriter writer) - { - var startOffset = writer.Position; - - foreach (var locationList in _locationLists) - { - locationList.WriteInternal(writer); - } - - Debug.Assert(Size == writer.Position - startOffset, $"Expected Size: {Size} != Written Size: {writer.Position - startOffset}"); - } + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; - public override string ToString() + foreach (var locationList in _locationLists) { - return $"Section .debug_loc, Entries: {_locationLists.Count}"; + locationList.Write(writer); } + + Debug.Assert(Size == writer.Position - startOffset, $"Expected Size: {Size} != Written Size: {writer.Position - startOffset}"); + } + + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"Section .debug_loc, Entries: {_locationLists.Count}"); + return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfObject.cs b/src/LibObjectFile/Dwarf/DwarfObject.cs index 27a4c76..b34a3a8 100644 --- a/src/LibObjectFile/Dwarf/DwarfObject.cs +++ b/src/LibObjectFile/Dwarf/DwarfObject.cs @@ -7,11 +7,11 @@ namespace LibObjectFile.Dwarf; -public abstract class DwarfObject : ObjectFileNodeBase +public abstract class DwarfObject : ObjectFileElement { public DwarfFile? GetParentFile() { - var check = (ObjectFileNodeBase)this; + var check = (ObjectFileElement?)this; while (check != null) { if (check is DwarfFile dwarfFile) return dwarfFile; @@ -22,7 +22,7 @@ public abstract class DwarfObject : ObjectFileNodeBase public DwarfUnit? GetParentUnit() { - var check = (ObjectFileNodeBase)this; + var check = (ObjectFileElement?)this; while (check != null) { if (check is DwarfUnit dwarfUnit) return dwarfUnit; @@ -33,7 +33,7 @@ public abstract class DwarfObject : ObjectFileNodeBase public DwarfSection? GetParentSection() { - var check = (ObjectFileNodeBase)this; + var check = (ObjectFileElement?)this; while (check != null) { if (check is DwarfSection dwarfSection) return dwarfSection; @@ -43,9 +43,9 @@ public abstract class DwarfObject : ObjectFileNodeBase } } -public abstract class DwarfObject : DwarfObject where TContainer : ObjectFileNodeBase +public abstract class DwarfObject : DwarfObject where TContainer : ObjectFileElement { - protected override void ValidateParent(ObjectFileNodeBase parent) + protected override void ValidateParent(ObjectFileElement parent) { if (!(parent is TContainer)) { @@ -53,7 +53,6 @@ protected override void ValidateParent(ObjectFileNodeBase parent) } } - /// /// Gets the containing . Might be null if this section or segment /// does not belong to an existing . @@ -64,27 +63,4 @@ protected override void ValidateParent(ObjectFileNodeBase parent) get => (TContainer?)base.Parent; internal set => base.Parent = value; } - - internal void UpdateLayoutInternal(DwarfLayoutContext layoutContext) - { - UpdateLayout(layoutContext); - } - - protected abstract void UpdateLayout(DwarfLayoutContext layoutContext); - - - internal void ReadInternal(DwarfReader reader) - { - Read(reader); - } - - protected abstract void Read(DwarfReader reader); - - - internal void WriteInternal(DwarfWriter writer) - { - Write(writer); - } - - protected abstract void Write(DwarfWriter writer); } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfOperation.cs b/src/LibObjectFile/Dwarf/DwarfOperation.cs index 84ee680..5eac45c 100644 --- a/src/LibObjectFile/Dwarf/DwarfOperation.cs +++ b/src/LibObjectFile/Dwarf/DwarfOperation.cs @@ -6,1167 +6,1166 @@ using System.Diagnostics; using System.IO; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[DebuggerDisplay("{DebuggerDisplay,nq}")] +public class DwarfOperation : DwarfObject { - [DebuggerDisplay("{DebuggerDisplay,nq}")] - public class DwarfOperation : DwarfObject - { - public DwarfOperationKindEx Kind { get; set; } + public DwarfOperationKindEx Kind { get; set; } - public object? Operand0 { get; set; } + public object? Operand0 { get; set; } - public DwarfInteger Operand1; + public DwarfInteger Operand1; - public DwarfInteger Operand2; + public DwarfInteger Operand2; - private string DebuggerDisplay => $"{Kind} {Operand1} {Operand2} {Operand0}"; + private string DebuggerDisplay => $"{Kind} {Operand1} {Operand2} {Operand0}"; - protected override void Read(DwarfReader reader) + public override void Read(DwarfReader reader) + { + Position = reader.Position; + var kind = new DwarfOperationKindEx(reader.ReadU8()); + Kind = kind; + + switch (kind.Value) { - Position = reader.Position; - var kind = new DwarfOperationKindEx(reader.ReadU8()); - Kind = kind; + case DwarfOperationKind.Addr: + Operand1.U64 = reader.ReadUInt(); + break; + case DwarfOperationKind.Const1u: + Operand1.U64 = reader.ReadU8(); + break; + case DwarfOperationKind.Const1s: + Operand1.I64 = reader.ReadI8(); + break; + case DwarfOperationKind.Const2u: + Operand1.U64 = reader.ReadU16(); + break; + case DwarfOperationKind.Const2s: + Operand1.I64 = reader.ReadI16(); + break; + + case DwarfOperationKind.Const4u: + Operand1.U64 = reader.ReadU32(); + break; + case DwarfOperationKind.Const4s: + Operand1.I64 = reader.ReadU32(); + break; + + case DwarfOperationKind.Const8u: + Operand1.U64 = reader.ReadU64(); + break; + + case DwarfOperationKind.Const8s: + Operand1.I64 = reader.ReadI64(); + break; + + case DwarfOperationKind.Constu: + Operand1.U64 = reader.ReadULEB128(); + break; + + case DwarfOperationKind.Consts: + Operand1.I64 = reader.ReadILEB128(); + break; + + case DwarfOperationKind.Deref: + case DwarfOperationKind.Dup: + case DwarfOperationKind.Drop: + case DwarfOperationKind.Over: + case DwarfOperationKind.Swap: + case DwarfOperationKind.Rot: + case DwarfOperationKind.Xderef: + case DwarfOperationKind.Abs: + case DwarfOperationKind.And: + case DwarfOperationKind.Div: + case DwarfOperationKind.Minus: + case DwarfOperationKind.Mod: + case DwarfOperationKind.Mul: + case DwarfOperationKind.Neg: + case DwarfOperationKind.Not: + case DwarfOperationKind.Or: + case DwarfOperationKind.Plus: + case DwarfOperationKind.Shl: + case DwarfOperationKind.Shr: + case DwarfOperationKind.Shra: + case DwarfOperationKind.Xor: + case DwarfOperationKind.Eq: + case DwarfOperationKind.Ge: + case DwarfOperationKind.Gt: + case DwarfOperationKind.Le: + case DwarfOperationKind.Lt: + case DwarfOperationKind.Ne: + case DwarfOperationKind.Nop: + case DwarfOperationKind.PushObjectAddress: + case DwarfOperationKind.FormTlsAddress: + case DwarfOperationKind.CallFrameCfa: + break; + + case DwarfOperationKind.Pick: + Operand1.U64 = reader.ReadU8(); + break; + + case DwarfOperationKind.PlusUconst: + Operand1.U64 = reader.ReadULEB128(); + break; + + case DwarfOperationKind.Bra: + case DwarfOperationKind.Skip: + // TODO: resolve branches to DwarfOperation + Operand1.I64 = reader.ReadI16(); + break; + + case DwarfOperationKind.Lit0: + case DwarfOperationKind.Lit1: + case DwarfOperationKind.Lit2: + case DwarfOperationKind.Lit3: + case DwarfOperationKind.Lit4: + case DwarfOperationKind.Lit5: + case DwarfOperationKind.Lit6: + case DwarfOperationKind.Lit7: + case DwarfOperationKind.Lit8: + case DwarfOperationKind.Lit9: + case DwarfOperationKind.Lit10: + case DwarfOperationKind.Lit11: + case DwarfOperationKind.Lit12: + case DwarfOperationKind.Lit13: + case DwarfOperationKind.Lit14: + case DwarfOperationKind.Lit15: + case DwarfOperationKind.Lit16: + case DwarfOperationKind.Lit17: + case DwarfOperationKind.Lit18: + case DwarfOperationKind.Lit19: + case DwarfOperationKind.Lit20: + case DwarfOperationKind.Lit21: + case DwarfOperationKind.Lit22: + case DwarfOperationKind.Lit23: + case DwarfOperationKind.Lit24: + case DwarfOperationKind.Lit25: + case DwarfOperationKind.Lit26: + case DwarfOperationKind.Lit27: + case DwarfOperationKind.Lit28: + case DwarfOperationKind.Lit29: + case DwarfOperationKind.Lit30: + case DwarfOperationKind.Lit31: + Operand1.U64 = (ulong)((byte)kind.Value - (byte)DwarfOperationKind.Lit0); + break; + + case DwarfOperationKind.Reg0: + case DwarfOperationKind.Reg1: + case DwarfOperationKind.Reg2: + case DwarfOperationKind.Reg3: + case DwarfOperationKind.Reg4: + case DwarfOperationKind.Reg5: + case DwarfOperationKind.Reg6: + case DwarfOperationKind.Reg7: + case DwarfOperationKind.Reg8: + case DwarfOperationKind.Reg9: + case DwarfOperationKind.Reg10: + case DwarfOperationKind.Reg11: + case DwarfOperationKind.Reg12: + case DwarfOperationKind.Reg13: + case DwarfOperationKind.Reg14: + case DwarfOperationKind.Reg15: + case DwarfOperationKind.Reg16: + case DwarfOperationKind.Reg17: + case DwarfOperationKind.Reg18: + case DwarfOperationKind.Reg19: + case DwarfOperationKind.Reg20: + case DwarfOperationKind.Reg21: + case DwarfOperationKind.Reg22: + case DwarfOperationKind.Reg23: + case DwarfOperationKind.Reg24: + case DwarfOperationKind.Reg25: + case DwarfOperationKind.Reg26: + case DwarfOperationKind.Reg27: + case DwarfOperationKind.Reg28: + case DwarfOperationKind.Reg29: + case DwarfOperationKind.Reg30: + case DwarfOperationKind.Reg31: + Operand1.U64 = (ulong)kind.Value - (ulong)DwarfOperationKind.Reg0; + break; + + case DwarfOperationKind.Breg0: + case DwarfOperationKind.Breg1: + case DwarfOperationKind.Breg2: + case DwarfOperationKind.Breg3: + case DwarfOperationKind.Breg4: + case DwarfOperationKind.Breg5: + case DwarfOperationKind.Breg6: + case DwarfOperationKind.Breg7: + case DwarfOperationKind.Breg8: + case DwarfOperationKind.Breg9: + case DwarfOperationKind.Breg10: + case DwarfOperationKind.Breg11: + case DwarfOperationKind.Breg12: + case DwarfOperationKind.Breg13: + case DwarfOperationKind.Breg14: + case DwarfOperationKind.Breg15: + case DwarfOperationKind.Breg16: + case DwarfOperationKind.Breg17: + case DwarfOperationKind.Breg18: + case DwarfOperationKind.Breg19: + case DwarfOperationKind.Breg20: + case DwarfOperationKind.Breg21: + case DwarfOperationKind.Breg22: + case DwarfOperationKind.Breg23: + case DwarfOperationKind.Breg24: + case DwarfOperationKind.Breg25: + case DwarfOperationKind.Breg26: + case DwarfOperationKind.Breg27: + case DwarfOperationKind.Breg28: + case DwarfOperationKind.Breg29: + case DwarfOperationKind.Breg30: + case DwarfOperationKind.Breg31: + Operand1.U64 = (ulong)kind.Value - (ulong)DwarfOperationKind.Breg0; + Operand2.I64 = reader.ReadILEB128(); + break; + + case DwarfOperationKind.Regx: + Operand1.U64 = reader.ReadULEB128(); + break; + + case DwarfOperationKind.Fbreg: + Operand1.I64 = reader.ReadILEB128(); + break; + + case DwarfOperationKind.Bregx: + Operand1.U64 = reader.ReadULEB128(); + Operand2.I64 = reader.ReadILEB128(); + break; + + case DwarfOperationKind.Piece: + Operand1.U64 = reader.ReadULEB128(); + break; + + case DwarfOperationKind.DerefSize: + Operand1.U64 = reader.ReadU8(); + break; + + case DwarfOperationKind.XderefSize: + Operand1.U64 = reader.ReadU8(); + break; + + case DwarfOperationKind.Call2: + { + var offset = reader.ReadU16(); + var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); + reader.ResolveAttributeReferenceWithinSection(dieRef, false); + break; + } + + case DwarfOperationKind.Call4: + { + var offset = reader.ReadU32(); + var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); + reader.ResolveAttributeReferenceWithinSection(dieRef, false); + break; + } + + case DwarfOperationKind.CallRef: + { + var offset = reader.ReadUIntFromEncoding(); + var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); + reader.ResolveAttributeReferenceWithinSection(dieRef, false); + break; + } + + case DwarfOperationKind.BitPiece: + Operand1.U64 = reader.ReadULEB128(); + Operand2.U64 = reader.ReadULEB128(); + break; + + case DwarfOperationKind.ImplicitValue: + { + var length = reader.ReadULEB128(); + Operand0 = reader.ReadAsStream(length); + break; + } + + case DwarfOperationKind.StackValue: + break; - switch (kind.Value) + case DwarfOperationKind.ImplicitPointer: + case DwarfOperationKind.GNUImplicitPointer: { - case DwarfOperationKind.Addr: - Operand1.U64 = reader.ReadUInt(); - break; - case DwarfOperationKind.Const1u: - Operand1.U64 = reader.ReadU8(); - break; - case DwarfOperationKind.Const1s: - Operand1.I64 = reader.ReadI8(); - break; - case DwarfOperationKind.Const2u: - Operand1.U64 = reader.ReadU16(); - break; - case DwarfOperationKind.Const2s: - Operand1.I64 = reader.ReadI16(); - break; - - case DwarfOperationKind.Const4u: - Operand1.U64 = reader.ReadU32(); - break; - case DwarfOperationKind.Const4s: - Operand1.I64 = reader.ReadU32(); - break; - - case DwarfOperationKind.Const8u: - Operand1.U64 = reader.ReadU64(); - break; - - case DwarfOperationKind.Const8s: - Operand1.I64 = reader.ReadI64(); - break; - - case DwarfOperationKind.Constu: - Operand1.U64 = reader.ReadULEB128(); - break; - - case DwarfOperationKind.Consts: - Operand1.I64 = reader.ReadILEB128(); - break; - - case DwarfOperationKind.Deref: - case DwarfOperationKind.Dup: - case DwarfOperationKind.Drop: - case DwarfOperationKind.Over: - case DwarfOperationKind.Swap: - case DwarfOperationKind.Rot: - case DwarfOperationKind.Xderef: - case DwarfOperationKind.Abs: - case DwarfOperationKind.And: - case DwarfOperationKind.Div: - case DwarfOperationKind.Minus: - case DwarfOperationKind.Mod: - case DwarfOperationKind.Mul: - case DwarfOperationKind.Neg: - case DwarfOperationKind.Not: - case DwarfOperationKind.Or: - case DwarfOperationKind.Plus: - case DwarfOperationKind.Shl: - case DwarfOperationKind.Shr: - case DwarfOperationKind.Shra: - case DwarfOperationKind.Xor: - case DwarfOperationKind.Eq: - case DwarfOperationKind.Ge: - case DwarfOperationKind.Gt: - case DwarfOperationKind.Le: - case DwarfOperationKind.Lt: - case DwarfOperationKind.Ne: - case DwarfOperationKind.Nop: - case DwarfOperationKind.PushObjectAddress: - case DwarfOperationKind.FormTlsAddress: - case DwarfOperationKind.CallFrameCfa: - break; - - case DwarfOperationKind.Pick: - Operand1.U64 = reader.ReadU8(); - break; - - case DwarfOperationKind.PlusUconst: - Operand1.U64 = reader.ReadULEB128(); - break; - - case DwarfOperationKind.Bra: - case DwarfOperationKind.Skip: - // TODO: resolve branches to DwarfOperation - Operand1.I64 = reader.ReadI16(); - break; - - case DwarfOperationKind.Lit0: - case DwarfOperationKind.Lit1: - case DwarfOperationKind.Lit2: - case DwarfOperationKind.Lit3: - case DwarfOperationKind.Lit4: - case DwarfOperationKind.Lit5: - case DwarfOperationKind.Lit6: - case DwarfOperationKind.Lit7: - case DwarfOperationKind.Lit8: - case DwarfOperationKind.Lit9: - case DwarfOperationKind.Lit10: - case DwarfOperationKind.Lit11: - case DwarfOperationKind.Lit12: - case DwarfOperationKind.Lit13: - case DwarfOperationKind.Lit14: - case DwarfOperationKind.Lit15: - case DwarfOperationKind.Lit16: - case DwarfOperationKind.Lit17: - case DwarfOperationKind.Lit18: - case DwarfOperationKind.Lit19: - case DwarfOperationKind.Lit20: - case DwarfOperationKind.Lit21: - case DwarfOperationKind.Lit22: - case DwarfOperationKind.Lit23: - case DwarfOperationKind.Lit24: - case DwarfOperationKind.Lit25: - case DwarfOperationKind.Lit26: - case DwarfOperationKind.Lit27: - case DwarfOperationKind.Lit28: - case DwarfOperationKind.Lit29: - case DwarfOperationKind.Lit30: - case DwarfOperationKind.Lit31: - Operand1.U64 = (ulong)((byte)kind.Value - (byte)DwarfOperationKind.Lit0); - break; - - case DwarfOperationKind.Reg0: - case DwarfOperationKind.Reg1: - case DwarfOperationKind.Reg2: - case DwarfOperationKind.Reg3: - case DwarfOperationKind.Reg4: - case DwarfOperationKind.Reg5: - case DwarfOperationKind.Reg6: - case DwarfOperationKind.Reg7: - case DwarfOperationKind.Reg8: - case DwarfOperationKind.Reg9: - case DwarfOperationKind.Reg10: - case DwarfOperationKind.Reg11: - case DwarfOperationKind.Reg12: - case DwarfOperationKind.Reg13: - case DwarfOperationKind.Reg14: - case DwarfOperationKind.Reg15: - case DwarfOperationKind.Reg16: - case DwarfOperationKind.Reg17: - case DwarfOperationKind.Reg18: - case DwarfOperationKind.Reg19: - case DwarfOperationKind.Reg20: - case DwarfOperationKind.Reg21: - case DwarfOperationKind.Reg22: - case DwarfOperationKind.Reg23: - case DwarfOperationKind.Reg24: - case DwarfOperationKind.Reg25: - case DwarfOperationKind.Reg26: - case DwarfOperationKind.Reg27: - case DwarfOperationKind.Reg28: - case DwarfOperationKind.Reg29: - case DwarfOperationKind.Reg30: - case DwarfOperationKind.Reg31: - Operand1.U64 = (ulong)kind.Value - (ulong)DwarfOperationKind.Reg0; - break; - - case DwarfOperationKind.Breg0: - case DwarfOperationKind.Breg1: - case DwarfOperationKind.Breg2: - case DwarfOperationKind.Breg3: - case DwarfOperationKind.Breg4: - case DwarfOperationKind.Breg5: - case DwarfOperationKind.Breg6: - case DwarfOperationKind.Breg7: - case DwarfOperationKind.Breg8: - case DwarfOperationKind.Breg9: - case DwarfOperationKind.Breg10: - case DwarfOperationKind.Breg11: - case DwarfOperationKind.Breg12: - case DwarfOperationKind.Breg13: - case DwarfOperationKind.Breg14: - case DwarfOperationKind.Breg15: - case DwarfOperationKind.Breg16: - case DwarfOperationKind.Breg17: - case DwarfOperationKind.Breg18: - case DwarfOperationKind.Breg19: - case DwarfOperationKind.Breg20: - case DwarfOperationKind.Breg21: - case DwarfOperationKind.Breg22: - case DwarfOperationKind.Breg23: - case DwarfOperationKind.Breg24: - case DwarfOperationKind.Breg25: - case DwarfOperationKind.Breg26: - case DwarfOperationKind.Breg27: - case DwarfOperationKind.Breg28: - case DwarfOperationKind.Breg29: - case DwarfOperationKind.Breg30: - case DwarfOperationKind.Breg31: - Operand1.U64 = (ulong)kind.Value - (ulong)DwarfOperationKind.Breg0; - Operand2.I64 = reader.ReadILEB128(); - break; - - case DwarfOperationKind.Regx: - Operand1.U64 = reader.ReadULEB128(); - break; - - case DwarfOperationKind.Fbreg: - Operand1.I64 = reader.ReadILEB128(); - break; - - case DwarfOperationKind.Bregx: - Operand1.U64 = reader.ReadULEB128(); - Operand2.I64 = reader.ReadILEB128(); - break; - - case DwarfOperationKind.Piece: - Operand1.U64 = reader.ReadULEB128(); - break; - - case DwarfOperationKind.DerefSize: - Operand1.U64 = reader.ReadU8(); - break; - - case DwarfOperationKind.XderefSize: - Operand1.U64 = reader.ReadU8(); - break; - - case DwarfOperationKind.Call2: + ulong offset; + // a reference to a debugging information entry that describes the dereferenced object’s value + if (reader.CurrentUnit!.Version == 2) { - var offset = reader.ReadU16(); - var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); - reader.ResolveAttributeReferenceWithinSection(dieRef, false); - break; + offset = reader.ReadUInt(); } - - case DwarfOperationKind.Call4: + else { - var offset = reader.ReadU32(); - var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); - reader.ResolveAttributeReferenceWithinSection(dieRef, false); - break; + offset = reader.ReadUIntFromEncoding(); } + // a signed number that is treated as a byte offset from the start of that value + Operand1.I64 = reader.ReadILEB128(); - case DwarfOperationKind.CallRef: + if (offset != 0) { - var offset = reader.ReadUIntFromEncoding(); var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); reader.ResolveAttributeReferenceWithinSection(dieRef, false); - break; } + break; + } - case DwarfOperationKind.BitPiece: - Operand1.U64 = reader.ReadULEB128(); - Operand2.U64 = reader.ReadULEB128(); - break; + case DwarfOperationKind.Addrx: + case DwarfOperationKind.GNUAddrIndex: + case DwarfOperationKind.Constx: + case DwarfOperationKind.GNUConstIndex: + Operand1.U64 = reader.ReadULEB128(); + break; - case DwarfOperationKind.ImplicitValue: + case DwarfOperationKind.EntryValue: + case DwarfOperationKind.GNUEntryValue: + { + var subExpression = new DwarfExpression(); + subExpression.ReadInternal(reader); + Operand0 = subExpression; + break; + } + + case DwarfOperationKind.ConstType: + case DwarfOperationKind.GNUConstType: + { + // The DW_OP_const_type operation takes three operands + + // The first operand is an unsigned LEB128 integer that represents the offset + // of a debugging information entry in the current compilation unit, which + // must be a DW_TAG_base_type entry that provides the type of the constant provided + var offset = reader.ReadULEB128(); + if (offset != 0) { - var length = reader.ReadULEB128(); - Operand0 = reader.ReadAsStream(length); - break; + var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); + reader.ResolveAttributeReferenceWithinCompilationUnit(dieRef, false); } + Operand1.U64 = ReadEncodedValue(reader, kind, out var sizeOfEncodedValue); + // Encode size of encoded value in Operand1 + Operand2.U64 = sizeOfEncodedValue; + break; + } + + case DwarfOperationKind.RegvalType: + case DwarfOperationKind.GNURegvalType: + { + // The DW_OP_regval_type operation provides the contents of a given register + // interpreted as a value of a given type - case DwarfOperationKind.StackValue: - break; + // The first operand is an unsigned LEB128 number, which identifies a register + // whose contents is to be pushed onto the stack + Operand1.U64 = reader.ReadULEB128(); - case DwarfOperationKind.ImplicitPointer: - case DwarfOperationKind.GNUImplicitPointer: + // The second operand is an unsigned LEB128 number that represents the offset + // of a debugging information entry in the current compilation unit + var offset = reader.ReadULEB128(); + if (offset != 0) { - ulong offset; - // a reference to a debugging information entry that describes the dereferenced object’s value - if (reader.CurrentUnit!.Version == 2) - { - offset = reader.ReadUInt(); - } - else - { - offset = reader.ReadUIntFromEncoding(); - } - // a signed number that is treated as a byte offset from the start of that value - Operand1.I64 = reader.ReadILEB128(); - - if (offset != 0) - { - var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); - reader.ResolveAttributeReferenceWithinSection(dieRef, false); - } - break; + var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); + reader.ResolveAttributeReferenceWithinCompilationUnit(dieRef, false); } + break; + } + + case DwarfOperationKind.DerefType: + case DwarfOperationKind.GNUDerefType: + case DwarfOperationKind.XderefType: + { + // The DW_OP_deref_type operation behaves like the DW_OP_deref_size operation: + // it pops the top stack entry and treats it as an address. - case DwarfOperationKind.Addrx: - case DwarfOperationKind.GNUAddrIndex: - case DwarfOperationKind.Constx: - case DwarfOperationKind.GNUConstIndex: - Operand1.U64 = reader.ReadULEB128(); - break; + // This operand is a 1-byte unsigned integral constant whose value which is the + // same as the size of the base type referenced by the second operand + Operand1.U64 = reader.ReadU8(); - case DwarfOperationKind.EntryValue: - case DwarfOperationKind.GNUEntryValue: + // The second operand is an unsigned LEB128 number that represents the offset + // of a debugging information entry in the current compilation unit + var offset = reader.ReadULEB128(); + if (offset != 0) { - var subExpression = new DwarfExpression(); - subExpression.ReadInternal(reader); - Operand0 = subExpression; - break; + var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); + reader.ResolveAttributeReferenceWithinCompilationUnit(dieRef, false); } + break; + } - case DwarfOperationKind.ConstType: - case DwarfOperationKind.GNUConstType: + case DwarfOperationKind.Convert: + case DwarfOperationKind.GNUConvert: + case DwarfOperationKind.Reinterpret: + case DwarfOperationKind.GNUReinterpret: + { + ulong offset = reader.ReadULEB128(); + if (offset != 0) { - // The DW_OP_const_type operation takes three operands - - // The first operand is an unsigned LEB128 integer that represents the offset - // of a debugging information entry in the current compilation unit, which - // must be a DW_TAG_base_type entry that provides the type of the constant provided - var offset = reader.ReadULEB128(); - if (offset != 0) - { - var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); - reader.ResolveAttributeReferenceWithinCompilationUnit(dieRef, false); - } - Operand1.U64 = ReadEncodedValue(reader, kind, out var sizeOfEncodedValue); - // Encode size of encoded value in Operand1 - Operand2.U64 = sizeOfEncodedValue; - break; + var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); + reader.ResolveAttributeReferenceWithinCompilationUnit(dieRef, false); } + break; + } + + case DwarfOperationKind.GNUPushTlsAddress: + case DwarfOperationKind.GNUUninit: + break; + + case DwarfOperationKind.GNUEncodedAddr: + { + Operand1.U64 = ReadEncodedValue(reader, kind, out var sizeOfEncodedValue); + Operand2.U64 = sizeOfEncodedValue; + break; + } + + case DwarfOperationKind.GNUParameterRef: + Operand1.U64 = reader.ReadU32(); + break; + + default: + throw new NotSupportedException($"The {nameof(DwarfOperationKind)} {kind} is not supported"); + } + + // Store the size of the current op + Size = reader.Position - Position; + } - case DwarfOperationKind.RegvalType: - case DwarfOperationKind.GNURegvalType: + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + var endOffset = Position; + // 1 byte per opcode + endOffset += 1; + + switch (Kind.Value) + { + case DwarfOperationKind.Addr: + endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); + break; + case DwarfOperationKind.Const1u: + case DwarfOperationKind.Const1s: + case DwarfOperationKind.Pick: + case DwarfOperationKind.DerefSize: + case DwarfOperationKind.XderefSize: + endOffset += 1; + break; + case DwarfOperationKind.Const2u: + case DwarfOperationKind.Const2s: + case DwarfOperationKind.Bra: + case DwarfOperationKind.Skip: + case DwarfOperationKind.Call2: + endOffset += 2; + break; + case DwarfOperationKind.Const4u: + case DwarfOperationKind.Const4s: + case DwarfOperationKind.Call4: + endOffset += 4; + break; + case DwarfOperationKind.Const8u: + case DwarfOperationKind.Const8s: + endOffset += 8; + break; + + case DwarfOperationKind.Constu: + case DwarfOperationKind.PlusUconst: + case DwarfOperationKind.Regx: + case DwarfOperationKind.Piece: + case DwarfOperationKind.Addrx: + case DwarfOperationKind.GNUAddrIndex: + case DwarfOperationKind.Constx: + case DwarfOperationKind.GNUConstIndex: + endOffset += DwarfHelper.SizeOfULEB128(Operand1.U64); + break; + + case DwarfOperationKind.Consts: + case DwarfOperationKind.Fbreg: + endOffset += DwarfHelper.SizeOfILEB128(Operand1.I64); + break; + + case DwarfOperationKind.Deref: + case DwarfOperationKind.Dup: + case DwarfOperationKind.Drop: + case DwarfOperationKind.Over: + case DwarfOperationKind.Swap: + case DwarfOperationKind.Rot: + case DwarfOperationKind.Xderef: + case DwarfOperationKind.Abs: + case DwarfOperationKind.And: + case DwarfOperationKind.Div: + case DwarfOperationKind.Minus: + case DwarfOperationKind.Mod: + case DwarfOperationKind.Mul: + case DwarfOperationKind.Neg: + case DwarfOperationKind.Not: + case DwarfOperationKind.Or: + case DwarfOperationKind.Plus: + case DwarfOperationKind.Shl: + case DwarfOperationKind.Shr: + case DwarfOperationKind.Shra: + case DwarfOperationKind.Xor: + case DwarfOperationKind.Eq: + case DwarfOperationKind.Ge: + case DwarfOperationKind.Gt: + case DwarfOperationKind.Le: + case DwarfOperationKind.Lt: + case DwarfOperationKind.Ne: + case DwarfOperationKind.Nop: + case DwarfOperationKind.PushObjectAddress: + case DwarfOperationKind.FormTlsAddress: + case DwarfOperationKind.CallFrameCfa: + case DwarfOperationKind.Lit0: + case DwarfOperationKind.Lit1: + case DwarfOperationKind.Lit2: + case DwarfOperationKind.Lit3: + case DwarfOperationKind.Lit4: + case DwarfOperationKind.Lit5: + case DwarfOperationKind.Lit6: + case DwarfOperationKind.Lit7: + case DwarfOperationKind.Lit8: + case DwarfOperationKind.Lit9: + case DwarfOperationKind.Lit10: + case DwarfOperationKind.Lit11: + case DwarfOperationKind.Lit12: + case DwarfOperationKind.Lit13: + case DwarfOperationKind.Lit14: + case DwarfOperationKind.Lit15: + case DwarfOperationKind.Lit16: + case DwarfOperationKind.Lit17: + case DwarfOperationKind.Lit18: + case DwarfOperationKind.Lit19: + case DwarfOperationKind.Lit20: + case DwarfOperationKind.Lit21: + case DwarfOperationKind.Lit22: + case DwarfOperationKind.Lit23: + case DwarfOperationKind.Lit24: + case DwarfOperationKind.Lit25: + case DwarfOperationKind.Lit26: + case DwarfOperationKind.Lit27: + case DwarfOperationKind.Lit28: + case DwarfOperationKind.Lit29: + case DwarfOperationKind.Lit30: + case DwarfOperationKind.Lit31: + case DwarfOperationKind.Reg0: + case DwarfOperationKind.Reg1: + case DwarfOperationKind.Reg2: + case DwarfOperationKind.Reg3: + case DwarfOperationKind.Reg4: + case DwarfOperationKind.Reg5: + case DwarfOperationKind.Reg6: + case DwarfOperationKind.Reg7: + case DwarfOperationKind.Reg8: + case DwarfOperationKind.Reg9: + case DwarfOperationKind.Reg10: + case DwarfOperationKind.Reg11: + case DwarfOperationKind.Reg12: + case DwarfOperationKind.Reg13: + case DwarfOperationKind.Reg14: + case DwarfOperationKind.Reg15: + case DwarfOperationKind.Reg16: + case DwarfOperationKind.Reg17: + case DwarfOperationKind.Reg18: + case DwarfOperationKind.Reg19: + case DwarfOperationKind.Reg20: + case DwarfOperationKind.Reg21: + case DwarfOperationKind.Reg22: + case DwarfOperationKind.Reg23: + case DwarfOperationKind.Reg24: + case DwarfOperationKind.Reg25: + case DwarfOperationKind.Reg26: + case DwarfOperationKind.Reg27: + case DwarfOperationKind.Reg28: + case DwarfOperationKind.Reg29: + case DwarfOperationKind.Reg30: + case DwarfOperationKind.Reg31: + case DwarfOperationKind.StackValue: + case DwarfOperationKind.GNUPushTlsAddress: + case DwarfOperationKind.GNUUninit: + break; + + case DwarfOperationKind.Breg0: + case DwarfOperationKind.Breg1: + case DwarfOperationKind.Breg2: + case DwarfOperationKind.Breg3: + case DwarfOperationKind.Breg4: + case DwarfOperationKind.Breg5: + case DwarfOperationKind.Breg6: + case DwarfOperationKind.Breg7: + case DwarfOperationKind.Breg8: + case DwarfOperationKind.Breg9: + case DwarfOperationKind.Breg10: + case DwarfOperationKind.Breg11: + case DwarfOperationKind.Breg12: + case DwarfOperationKind.Breg13: + case DwarfOperationKind.Breg14: + case DwarfOperationKind.Breg15: + case DwarfOperationKind.Breg16: + case DwarfOperationKind.Breg17: + case DwarfOperationKind.Breg18: + case DwarfOperationKind.Breg19: + case DwarfOperationKind.Breg20: + case DwarfOperationKind.Breg21: + case DwarfOperationKind.Breg22: + case DwarfOperationKind.Breg23: + case DwarfOperationKind.Breg24: + case DwarfOperationKind.Breg25: + case DwarfOperationKind.Breg26: + case DwarfOperationKind.Breg27: + case DwarfOperationKind.Breg28: + case DwarfOperationKind.Breg29: + case DwarfOperationKind.Breg30: + case DwarfOperationKind.Breg31: + endOffset += DwarfHelper.SizeOfILEB128(Operand2.I64); + break; + + case DwarfOperationKind.Bregx: + endOffset += DwarfHelper.SizeOfULEB128(Operand1.U64); + endOffset += DwarfHelper.SizeOfILEB128(Operand2.I64); + break; + + case DwarfOperationKind.CallRef: + endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); + break; + + case DwarfOperationKind.BitPiece: + endOffset += DwarfHelper.SizeOfULEB128(Operand1.U64); + endOffset += DwarfHelper.SizeOfULEB128(Operand2.U64); + break; + + case DwarfOperationKind.ImplicitValue: + if (Operand0 == null) { - // The DW_OP_regval_type operation provides the contents of a given register - // interpreted as a value of a given type - - // The first operand is an unsigned LEB128 number, which identifies a register - // whose contents is to be pushed onto the stack - Operand1.U64 = reader.ReadULEB128(); - - // The second operand is an unsigned LEB128 number that represents the offset - // of a debugging information entry in the current compilation unit - var offset = reader.ReadULEB128(); - if (offset != 0) - { - var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); - reader.ResolveAttributeReferenceWithinCompilationUnit(dieRef, false); - } - break; + layoutContext.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of implicit value operation {this} from DIE cannot be null."); } - - case DwarfOperationKind.DerefType: - case DwarfOperationKind.GNUDerefType: - case DwarfOperationKind.XderefType: + else if (Operand0 is Stream stream) { - // The DW_OP_deref_type operation behaves like the DW_OP_deref_size operation: - // it pops the top stack entry and treats it as an address. - - // This operand is a 1-byte unsigned integral constant whose value which is the - // same as the size of the base type referenced by the second operand - Operand1.U64 = reader.ReadU8(); - - // The second operand is an unsigned LEB128 number that represents the offset - // of a debugging information entry in the current compilation unit - var offset = reader.ReadULEB128(); - if (offset != 0) - { - var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); - reader.ResolveAttributeReferenceWithinCompilationUnit(dieRef, false); - } - break; + var streamSize = (ulong)stream.Length; + endOffset += DwarfHelper.SizeOfULEB128(streamSize); + endOffset += streamSize; } + else + { + layoutContext.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of implicit value operation {this} must be a System.IO.Stream."); + } + + break; - case DwarfOperationKind.Convert: - case DwarfOperationKind.GNUConvert: - case DwarfOperationKind.Reinterpret: - case DwarfOperationKind.GNUReinterpret: + case DwarfOperationKind.ImplicitPointer: + case DwarfOperationKind.GNUImplicitPointer: + // a reference to a debugging information entry that describes the dereferenced object’s value + if (layoutContext.CurrentUnit!.Version == 2) { - ulong offset = reader.ReadULEB128(); - if (offset != 0) - { - var dieRef = new DwarfReader.DwarfDIEReference(offset, this, DwarfExpressionLocationDIEReferenceResolverInstance); - reader.ResolveAttributeReferenceWithinCompilationUnit(dieRef, false); - } - break; + endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.AddressSize); + } + else + { + endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.Is64BitEncoding); } - case DwarfOperationKind.GNUPushTlsAddress: - case DwarfOperationKind.GNUUninit: - break; + // a signed number that is treated as a byte offset from the start of that value + endOffset += DwarfHelper.SizeOfILEB128(Operand1.I64); + break; - case DwarfOperationKind.GNUEncodedAddr: + case DwarfOperationKind.EntryValue: + case DwarfOperationKind.GNUEntryValue: + if (Operand0 == null) + { + endOffset += DwarfHelper.SizeOfULEB128(0); + } + else if (Operand0 is DwarfExpression expr) + { + expr.Position = endOffset; + expr.UpdateLayout(layoutContext); + endOffset += DwarfHelper.SizeOfULEB128(expr.Size); + } + else { - Operand1.U64 = ReadEncodedValue(reader, kind, out var sizeOfEncodedValue); - Operand2.U64 = sizeOfEncodedValue; - break; + layoutContext.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of EntryValue operation {this} must be a {nameof(DwarfExpression)} instead of {Operand0.GetType()}."); } - case DwarfOperationKind.GNUParameterRef: - Operand1.U64 = reader.ReadU32(); - break; + break; - default: - throw new NotSupportedException($"The {nameof(DwarfOperationKind)} {kind} is not supported"); + case DwarfOperationKind.ConstType: + case DwarfOperationKind.GNUConstType: + { + // The DW_OP_const_type operation takes three operands + + // The first operand is an unsigned LEB128 integer that represents the offset + // of a debugging information entry in the current compilation unit, which + // must be a DW_TAG_base_type entry that provides the type of the constant provided + + endOffset += SizeOfDIEReference(layoutContext); + endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, layoutContext.CurrentUnit!.AddressSize); + break; } - // Store the size of the current op - Size = reader.Position - Position; + case DwarfOperationKind.RegvalType: + case DwarfOperationKind.GNURegvalType: + { + // The DW_OP_regval_type operation provides the contents of a given register + // interpreted as a value of a given type + + // The first operand is an unsigned LEB128 number, which identifies a register + // whose contents is to be pushed onto the stack + endOffset += DwarfHelper.SizeOfULEB128(Operand1.U64); + + // The second operand is an unsigned LEB128 number that represents the offset + // of a debugging information entry in the current compilation unit + endOffset += SizeOfDIEReference(layoutContext); + break; + } + + case DwarfOperationKind.DerefType: + case DwarfOperationKind.GNUDerefType: + case DwarfOperationKind.XderefType: + { + // The DW_OP_deref_type operation behaves like the DW_OP_deref_size operation: + // it pops the top stack entry and treats it as an address. + + // This operand is a 1-byte unsigned integral constant whose value which is the + // same as the size of the base type referenced by the second operand + endOffset += 1; + + // The second operand is an unsigned LEB128 number that represents the offset + // of a debugging information entry in the current compilation unit + endOffset += SizeOfDIEReference(layoutContext); + break; + } + + case DwarfOperationKind.Convert: + case DwarfOperationKind.GNUConvert: + case DwarfOperationKind.Reinterpret: + case DwarfOperationKind.GNUReinterpret: + endOffset += SizeOfDIEReference(layoutContext); + break; + + case DwarfOperationKind.GNUEncodedAddr: + endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, layoutContext.CurrentUnit!.AddressSize); + break; + + case DwarfOperationKind.GNUParameterRef: + endOffset += 4; + break; + + default: + throw new NotSupportedException($"The {nameof(DwarfOperationKind)} {Kind} is not supported"); } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) + Size = endOffset - Position; + } + + private ulong SizeOfDIEReference(DwarfLayoutContext context) + { + if (Operand0 == null) + { + return DwarfHelper.SizeOfULEB128(0); + } + else if (Operand0 is DwarfDIE die) { - var endOffset = Position; - // 1 byte per opcode - endOffset += 1; + // TODO: check that die reference is within this section - switch (Kind.Value) + if (die.Position < Position) { - case DwarfOperationKind.Addr: - endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); - break; - case DwarfOperationKind.Const1u: - case DwarfOperationKind.Const1s: - case DwarfOperationKind.Pick: - case DwarfOperationKind.DerefSize: - case DwarfOperationKind.XderefSize: - endOffset += 1; - break; - case DwarfOperationKind.Const2u: - case DwarfOperationKind.Const2s: - case DwarfOperationKind.Bra: - case DwarfOperationKind.Skip: - case DwarfOperationKind.Call2: - endOffset += 2; - break; - case DwarfOperationKind.Const4u: - case DwarfOperationKind.Const4s: - case DwarfOperationKind.Call4: - endOffset += 4; - break; - case DwarfOperationKind.Const8u: - case DwarfOperationKind.Const8s: - endOffset += 8; - break; - - case DwarfOperationKind.Constu: - case DwarfOperationKind.PlusUconst: - case DwarfOperationKind.Regx: - case DwarfOperationKind.Piece: - case DwarfOperationKind.Addrx: - case DwarfOperationKind.GNUAddrIndex: - case DwarfOperationKind.Constx: - case DwarfOperationKind.GNUConstIndex: - endOffset += DwarfHelper.SizeOfULEB128(Operand1.U64); - break; - - case DwarfOperationKind.Consts: - case DwarfOperationKind.Fbreg: - endOffset += DwarfHelper.SizeOfILEB128(Operand1.I64); - break; - - case DwarfOperationKind.Deref: - case DwarfOperationKind.Dup: - case DwarfOperationKind.Drop: - case DwarfOperationKind.Over: - case DwarfOperationKind.Swap: - case DwarfOperationKind.Rot: - case DwarfOperationKind.Xderef: - case DwarfOperationKind.Abs: - case DwarfOperationKind.And: - case DwarfOperationKind.Div: - case DwarfOperationKind.Minus: - case DwarfOperationKind.Mod: - case DwarfOperationKind.Mul: - case DwarfOperationKind.Neg: - case DwarfOperationKind.Not: - case DwarfOperationKind.Or: - case DwarfOperationKind.Plus: - case DwarfOperationKind.Shl: - case DwarfOperationKind.Shr: - case DwarfOperationKind.Shra: - case DwarfOperationKind.Xor: - case DwarfOperationKind.Eq: - case DwarfOperationKind.Ge: - case DwarfOperationKind.Gt: - case DwarfOperationKind.Le: - case DwarfOperationKind.Lt: - case DwarfOperationKind.Ne: - case DwarfOperationKind.Nop: - case DwarfOperationKind.PushObjectAddress: - case DwarfOperationKind.FormTlsAddress: - case DwarfOperationKind.CallFrameCfa: - case DwarfOperationKind.Lit0: - case DwarfOperationKind.Lit1: - case DwarfOperationKind.Lit2: - case DwarfOperationKind.Lit3: - case DwarfOperationKind.Lit4: - case DwarfOperationKind.Lit5: - case DwarfOperationKind.Lit6: - case DwarfOperationKind.Lit7: - case DwarfOperationKind.Lit8: - case DwarfOperationKind.Lit9: - case DwarfOperationKind.Lit10: - case DwarfOperationKind.Lit11: - case DwarfOperationKind.Lit12: - case DwarfOperationKind.Lit13: - case DwarfOperationKind.Lit14: - case DwarfOperationKind.Lit15: - case DwarfOperationKind.Lit16: - case DwarfOperationKind.Lit17: - case DwarfOperationKind.Lit18: - case DwarfOperationKind.Lit19: - case DwarfOperationKind.Lit20: - case DwarfOperationKind.Lit21: - case DwarfOperationKind.Lit22: - case DwarfOperationKind.Lit23: - case DwarfOperationKind.Lit24: - case DwarfOperationKind.Lit25: - case DwarfOperationKind.Lit26: - case DwarfOperationKind.Lit27: - case DwarfOperationKind.Lit28: - case DwarfOperationKind.Lit29: - case DwarfOperationKind.Lit30: - case DwarfOperationKind.Lit31: - case DwarfOperationKind.Reg0: - case DwarfOperationKind.Reg1: - case DwarfOperationKind.Reg2: - case DwarfOperationKind.Reg3: - case DwarfOperationKind.Reg4: - case DwarfOperationKind.Reg5: - case DwarfOperationKind.Reg6: - case DwarfOperationKind.Reg7: - case DwarfOperationKind.Reg8: - case DwarfOperationKind.Reg9: - case DwarfOperationKind.Reg10: - case DwarfOperationKind.Reg11: - case DwarfOperationKind.Reg12: - case DwarfOperationKind.Reg13: - case DwarfOperationKind.Reg14: - case DwarfOperationKind.Reg15: - case DwarfOperationKind.Reg16: - case DwarfOperationKind.Reg17: - case DwarfOperationKind.Reg18: - case DwarfOperationKind.Reg19: - case DwarfOperationKind.Reg20: - case DwarfOperationKind.Reg21: - case DwarfOperationKind.Reg22: - case DwarfOperationKind.Reg23: - case DwarfOperationKind.Reg24: - case DwarfOperationKind.Reg25: - case DwarfOperationKind.Reg26: - case DwarfOperationKind.Reg27: - case DwarfOperationKind.Reg28: - case DwarfOperationKind.Reg29: - case DwarfOperationKind.Reg30: - case DwarfOperationKind.Reg31: - case DwarfOperationKind.StackValue: - case DwarfOperationKind.GNUPushTlsAddress: - case DwarfOperationKind.GNUUninit: - break; - - case DwarfOperationKind.Breg0: - case DwarfOperationKind.Breg1: - case DwarfOperationKind.Breg2: - case DwarfOperationKind.Breg3: - case DwarfOperationKind.Breg4: - case DwarfOperationKind.Breg5: - case DwarfOperationKind.Breg6: - case DwarfOperationKind.Breg7: - case DwarfOperationKind.Breg8: - case DwarfOperationKind.Breg9: - case DwarfOperationKind.Breg10: - case DwarfOperationKind.Breg11: - case DwarfOperationKind.Breg12: - case DwarfOperationKind.Breg13: - case DwarfOperationKind.Breg14: - case DwarfOperationKind.Breg15: - case DwarfOperationKind.Breg16: - case DwarfOperationKind.Breg17: - case DwarfOperationKind.Breg18: - case DwarfOperationKind.Breg19: - case DwarfOperationKind.Breg20: - case DwarfOperationKind.Breg21: - case DwarfOperationKind.Breg22: - case DwarfOperationKind.Breg23: - case DwarfOperationKind.Breg24: - case DwarfOperationKind.Breg25: - case DwarfOperationKind.Breg26: - case DwarfOperationKind.Breg27: - case DwarfOperationKind.Breg28: - case DwarfOperationKind.Breg29: - case DwarfOperationKind.Breg30: - case DwarfOperationKind.Breg31: - endOffset += DwarfHelper.SizeOfILEB128(Operand2.I64); - break; - - case DwarfOperationKind.Bregx: - endOffset += DwarfHelper.SizeOfULEB128(Operand1.U64); - endOffset += DwarfHelper.SizeOfILEB128(Operand2.I64); - break; - - case DwarfOperationKind.CallRef: - endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); - break; - - case DwarfOperationKind.BitPiece: - endOffset += DwarfHelper.SizeOfULEB128(Operand1.U64); - endOffset += DwarfHelper.SizeOfULEB128(Operand2.U64); - break; - - case DwarfOperationKind.ImplicitValue: - if (Operand0 == null) - { - layoutContext.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of implicit value operation {this} from DIE cannot be null."); - } - else if (Operand0 is Stream stream) - { - var streamSize = (ulong)stream.Length; - endOffset += DwarfHelper.SizeOfULEB128(streamSize); - endOffset += streamSize; - } - else - { - layoutContext.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of implicit value operation {this} must be a System.IO.Stream."); - } - - break; - - case DwarfOperationKind.ImplicitPointer: - case DwarfOperationKind.GNUImplicitPointer: - // a reference to a debugging information entry that describes the dereferenced object’s value - if (layoutContext.CurrentUnit!.Version == 2) - { - endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.AddressSize); - } - else - { - endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.Is64BitEncoding); - } - - // a signed number that is treated as a byte offset from the start of that value - endOffset += DwarfHelper.SizeOfILEB128(Operand1.I64); - break; - - case DwarfOperationKind.EntryValue: - case DwarfOperationKind.GNUEntryValue: - if (Operand0 == null) - { - endOffset += DwarfHelper.SizeOfULEB128(0); - } - else if (Operand0 is DwarfExpression expr) - { - expr.Position = endOffset; - expr.UpdateLayoutInternal(layoutContext); - endOffset += DwarfHelper.SizeOfULEB128(expr.Size); - } - else - { - layoutContext.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of EntryValue operation {this} must be a {nameof(DwarfExpression)} instead of {Operand0.GetType()}."); - } - - break; - - case DwarfOperationKind.ConstType: - case DwarfOperationKind.GNUConstType: - { - // The DW_OP_const_type operation takes three operands - - // The first operand is an unsigned LEB128 integer that represents the offset - // of a debugging information entry in the current compilation unit, which - // must be a DW_TAG_base_type entry that provides the type of the constant provided - - endOffset += SizeOfDIEReference(layoutContext); - endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, layoutContext.CurrentUnit!.AddressSize); - break; - } - - case DwarfOperationKind.RegvalType: - case DwarfOperationKind.GNURegvalType: - { - // The DW_OP_regval_type operation provides the contents of a given register - // interpreted as a value of a given type - - // The first operand is an unsigned LEB128 number, which identifies a register - // whose contents is to be pushed onto the stack - endOffset += DwarfHelper.SizeOfULEB128(Operand1.U64); - - // The second operand is an unsigned LEB128 number that represents the offset - // of a debugging information entry in the current compilation unit - endOffset += SizeOfDIEReference(layoutContext); - break; - } - - case DwarfOperationKind.DerefType: - case DwarfOperationKind.GNUDerefType: - case DwarfOperationKind.XderefType: - { - // The DW_OP_deref_type operation behaves like the DW_OP_deref_size operation: - // it pops the top stack entry and treats it as an address. - - // This operand is a 1-byte unsigned integral constant whose value which is the - // same as the size of the base type referenced by the second operand - endOffset += 1; - - // The second operand is an unsigned LEB128 number that represents the offset - // of a debugging information entry in the current compilation unit - endOffset += SizeOfDIEReference(layoutContext); - break; - } - - case DwarfOperationKind.Convert: - case DwarfOperationKind.GNUConvert: - case DwarfOperationKind.Reinterpret: - case DwarfOperationKind.GNUReinterpret: - endOffset += SizeOfDIEReference(layoutContext); - break; - - case DwarfOperationKind.GNUEncodedAddr: - endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, layoutContext.CurrentUnit!.AddressSize); - break; - - case DwarfOperationKind.GNUParameterRef: - endOffset += 4; - break; - - default: - throw new NotSupportedException($"The {nameof(DwarfOperationKind)} {Kind} is not supported"); + return DwarfHelper.SizeOfULEB128(die.Position); } + else + { + // TODO: encode depending on Context.DefaultAttributeFormForReference + return DwarfHelper.SizeOfILEB128(uint.MaxValue); + } + } + else + { + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of {Kind} operation {this} must be a {nameof(DwarfDIE)} instead of {Operand0.GetType()}."); + } + + return 0U; + } - Size = endOffset - Position; + private ulong SizeOfEncodedValue(DwarfOperationKindEx kind, ulong value, byte size, DwarfAddressSize addressSize) + { + switch (size) + { + case 0: + return 1 + DwarfHelper.SizeOfUInt(addressSize); + case 1: + return 1 + 1; + case 2: + return 1 + 2; + case 4: + return 1 + 4; + case 8: + return 1 + 8; + default: + // TODO: report via diagnostics in Verify + throw new InvalidOperationException($"Invalid Encoded address size {size} for {kind}"); } + } + + public override void Write(DwarfWriter writer) + { + var startOpOffset = Position; + Debug.Assert(startOpOffset == Position); + + writer.WriteU8((byte)Kind); - private ulong SizeOfDIEReference(DwarfLayoutContext context) + switch (Kind.Value) { - if (Operand0 == null) + case DwarfOperationKind.Addr: + writer.WriteAddress(DwarfRelocationTarget.Code, Operand1.U64); + break; + case DwarfOperationKind.Const1u: + case DwarfOperationKind.Const1s: + case DwarfOperationKind.Pick: + case DwarfOperationKind.DerefSize: + case DwarfOperationKind.XderefSize: + writer.WriteU8((byte)Operand1.U64); + break; + + case DwarfOperationKind.Const2u: + case DwarfOperationKind.Const2s: + writer.WriteU16((ushort)Operand1.U64); + break; + + case DwarfOperationKind.Const4u: + case DwarfOperationKind.Const4s: + writer.WriteU32((uint)Operand1.U64); + break; + + case DwarfOperationKind.Const8u: + case DwarfOperationKind.Const8s: + writer.WriteU64(Operand1.U64); + break; + + case DwarfOperationKind.Constu: + case DwarfOperationKind.PlusUconst: + case DwarfOperationKind.Regx: + case DwarfOperationKind.Piece: + case DwarfOperationKind.Addrx: + case DwarfOperationKind.GNUAddrIndex: + case DwarfOperationKind.Constx: + case DwarfOperationKind.GNUConstIndex: + writer.WriteULEB128(Operand1.U64); + break; + + case DwarfOperationKind.Consts: + case DwarfOperationKind.Fbreg: + writer.WriteILEB128(Operand1.I64); + break; + + case DwarfOperationKind.Deref: + case DwarfOperationKind.Dup: + case DwarfOperationKind.Drop: + case DwarfOperationKind.Over: + case DwarfOperationKind.Swap: + case DwarfOperationKind.Rot: + case DwarfOperationKind.Xderef: + case DwarfOperationKind.Abs: + case DwarfOperationKind.And: + case DwarfOperationKind.Div: + case DwarfOperationKind.Minus: + case DwarfOperationKind.Mod: + case DwarfOperationKind.Mul: + case DwarfOperationKind.Neg: + case DwarfOperationKind.Not: + case DwarfOperationKind.Or: + case DwarfOperationKind.Plus: + case DwarfOperationKind.Shl: + case DwarfOperationKind.Shr: + case DwarfOperationKind.Shra: + case DwarfOperationKind.Xor: + case DwarfOperationKind.Eq: + case DwarfOperationKind.Ge: + case DwarfOperationKind.Gt: + case DwarfOperationKind.Le: + case DwarfOperationKind.Lt: + case DwarfOperationKind.Ne: + case DwarfOperationKind.Nop: + case DwarfOperationKind.PushObjectAddress: + case DwarfOperationKind.FormTlsAddress: + case DwarfOperationKind.CallFrameCfa: + break; + + case DwarfOperationKind.Bra: + case DwarfOperationKind.Skip: + writer.WriteU16((ushort)((long)Position + 2 - (long)((DwarfOperation)Operand0!).Position)); + break; + + case DwarfOperationKind.Lit0: + case DwarfOperationKind.Lit1: + case DwarfOperationKind.Lit2: + case DwarfOperationKind.Lit3: + case DwarfOperationKind.Lit4: + case DwarfOperationKind.Lit5: + case DwarfOperationKind.Lit6: + case DwarfOperationKind.Lit7: + case DwarfOperationKind.Lit8: + case DwarfOperationKind.Lit9: + case DwarfOperationKind.Lit10: + case DwarfOperationKind.Lit11: + case DwarfOperationKind.Lit12: + case DwarfOperationKind.Lit13: + case DwarfOperationKind.Lit14: + case DwarfOperationKind.Lit15: + case DwarfOperationKind.Lit16: + case DwarfOperationKind.Lit17: + case DwarfOperationKind.Lit18: + case DwarfOperationKind.Lit19: + case DwarfOperationKind.Lit20: + case DwarfOperationKind.Lit21: + case DwarfOperationKind.Lit22: + case DwarfOperationKind.Lit23: + case DwarfOperationKind.Lit24: + case DwarfOperationKind.Lit25: + case DwarfOperationKind.Lit26: + case DwarfOperationKind.Lit27: + case DwarfOperationKind.Lit28: + case DwarfOperationKind.Lit29: + case DwarfOperationKind.Lit30: + case DwarfOperationKind.Lit31: + case DwarfOperationKind.Reg0: + case DwarfOperationKind.Reg1: + case DwarfOperationKind.Reg2: + case DwarfOperationKind.Reg3: + case DwarfOperationKind.Reg4: + case DwarfOperationKind.Reg5: + case DwarfOperationKind.Reg6: + case DwarfOperationKind.Reg7: + case DwarfOperationKind.Reg8: + case DwarfOperationKind.Reg9: + case DwarfOperationKind.Reg10: + case DwarfOperationKind.Reg11: + case DwarfOperationKind.Reg12: + case DwarfOperationKind.Reg13: + case DwarfOperationKind.Reg14: + case DwarfOperationKind.Reg15: + case DwarfOperationKind.Reg16: + case DwarfOperationKind.Reg17: + case DwarfOperationKind.Reg18: + case DwarfOperationKind.Reg19: + case DwarfOperationKind.Reg20: + case DwarfOperationKind.Reg21: + case DwarfOperationKind.Reg22: + case DwarfOperationKind.Reg23: + case DwarfOperationKind.Reg24: + case DwarfOperationKind.Reg25: + case DwarfOperationKind.Reg26: + case DwarfOperationKind.Reg27: + case DwarfOperationKind.Reg28: + case DwarfOperationKind.Reg29: + case DwarfOperationKind.Reg30: + case DwarfOperationKind.Reg31: + case DwarfOperationKind.StackValue: + break; + + case DwarfOperationKind.Breg0: + case DwarfOperationKind.Breg1: + case DwarfOperationKind.Breg2: + case DwarfOperationKind.Breg3: + case DwarfOperationKind.Breg4: + case DwarfOperationKind.Breg5: + case DwarfOperationKind.Breg6: + case DwarfOperationKind.Breg7: + case DwarfOperationKind.Breg8: + case DwarfOperationKind.Breg9: + case DwarfOperationKind.Breg10: + case DwarfOperationKind.Breg11: + case DwarfOperationKind.Breg12: + case DwarfOperationKind.Breg13: + case DwarfOperationKind.Breg14: + case DwarfOperationKind.Breg15: + case DwarfOperationKind.Breg16: + case DwarfOperationKind.Breg17: + case DwarfOperationKind.Breg18: + case DwarfOperationKind.Breg19: + case DwarfOperationKind.Breg20: + case DwarfOperationKind.Breg21: + case DwarfOperationKind.Breg22: + case DwarfOperationKind.Breg23: + case DwarfOperationKind.Breg24: + case DwarfOperationKind.Breg25: + case DwarfOperationKind.Breg26: + case DwarfOperationKind.Breg27: + case DwarfOperationKind.Breg28: + case DwarfOperationKind.Breg29: + case DwarfOperationKind.Breg30: + case DwarfOperationKind.Breg31: + writer.WriteILEB128(Operand2.I64); + break; + + case DwarfOperationKind.Bregx: + writer.WriteULEB128(Operand1.U64); + writer.WriteILEB128(Operand2.I64); + break; + + case DwarfOperationKind.Call2: + writer.WriteU16((ushort)((DwarfDIE)Operand0!).Position); + break; + + case DwarfOperationKind.Call4: + writer.WriteU32((uint)((DwarfDIE)Operand0!).Position); + break; + + case DwarfOperationKind.CallRef: + writer.WriteUInt(((DwarfDIE)Operand0!).Position); + break; + + case DwarfOperationKind.BitPiece: + writer.WriteULEB128(Operand1.U64); + writer.WriteULEB128(Operand2.U64); + break; + + case DwarfOperationKind.ImplicitValue: { - return DwarfHelper.SizeOfULEB128(0); + var stream = (Stream)Operand0!; + writer.WriteULEB128((ulong)stream.Position); + writer.Write(stream); + break; } - else if (Operand0 is DwarfDIE die) - { - // TODO: check that die reference is within this section - if (die.Position < Position) + case DwarfOperationKind.ImplicitPointer: + case DwarfOperationKind.GNUImplicitPointer: + { + ulong offset = ((DwarfDIE)Operand0!).Position; + // a reference to a debugging information entry that describes the dereferenced object’s value + if (writer.CurrentUnit!.Version == 2) { - return DwarfHelper.SizeOfULEB128(die.Position); + writer.WriteUInt(offset); } else { - // TODO: encode depending on Context.DefaultAttributeFormForReference - return DwarfHelper.SizeOfILEB128(uint.MaxValue); + writer.WriteUIntFromEncoding(offset); } + // a signed number that is treated as a byte offset from the start of that value + writer.WriteILEB128(Operand1.I64); + break; } - else + + case DwarfOperationKind.EntryValue: + case DwarfOperationKind.GNUEntryValue: { - context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of {Kind} operation {this} must be a {nameof(DwarfDIE)} instead of {Operand0.GetType()}."); + var expression = (DwarfExpression)Operand0!; + writer.WriteULEB128(expression.Size); + expression.WriteInternal(writer); + break; } - return 0U; - } - - private ulong SizeOfEncodedValue(DwarfOperationKindEx kind, ulong value, byte size, DwarfAddressSize addressSize) - { - switch (size) + case DwarfOperationKind.ConstType: + case DwarfOperationKind.GNUConstType: { - case 0: - return 1 + DwarfHelper.SizeOfUInt(addressSize); - case 1: - return 1 + 1; - case 2: - return 1 + 2; - case 4: - return 1 + 4; - case 8: - return 1 + 8; - default: - // TODO: report via diagnostics in Verify - throw new InvalidOperationException($"Invalid Encoded address size {size} for {kind}"); + // The DW_OP_const_type operation takes three operands + + // The first operand is an unsigned LEB128 integer that represents the offset + // of a debugging information entry in the current compilation unit, which + // must be a DW_TAG_base_type entry that provides the type of the constant provided + writer.WriteULEB128(((DwarfDIE)Operand0!).Position); + WriteEncodedValue(writer, Kind, Operand1.U64, (byte)Operand2.U64); + break; } - } - protected override void Write(DwarfWriter writer) - { - var startOpOffset = Position; - Debug.Assert(startOpOffset == Position); + case DwarfOperationKind.RegvalType: + case DwarfOperationKind.GNURegvalType: + { + // The DW_OP_regval_type operation provides the contents of a given register + // interpreted as a value of a given type + + // The first operand is an unsigned LEB128 number, which identifies a register + // whose contents is to be pushed onto the stack + writer.WriteULEB128(Operand1.U64); - writer.WriteU8((byte)Kind); + // The second operand is an unsigned LEB128 number that represents the offset + // of a debugging information entry in the current compilation unit + writer.WriteULEB128(((DwarfDIE)Operand0!).Position); + break; + } - switch (Kind.Value) + case DwarfOperationKind.DerefType: + case DwarfOperationKind.GNUDerefType: + case DwarfOperationKind.XderefType: { - case DwarfOperationKind.Addr: - writer.WriteAddress(DwarfRelocationTarget.Code, Operand1.U64); - break; - case DwarfOperationKind.Const1u: - case DwarfOperationKind.Const1s: - case DwarfOperationKind.Pick: - case DwarfOperationKind.DerefSize: - case DwarfOperationKind.XderefSize: - writer.WriteU8((byte)Operand1.U64); - break; - - case DwarfOperationKind.Const2u: - case DwarfOperationKind.Const2s: - writer.WriteU16((ushort)Operand1.U64); - break; - - case DwarfOperationKind.Const4u: - case DwarfOperationKind.Const4s: - writer.WriteU32((uint)Operand1.U64); - break; - - case DwarfOperationKind.Const8u: - case DwarfOperationKind.Const8s: - writer.WriteU64(Operand1.U64); - break; - - case DwarfOperationKind.Constu: - case DwarfOperationKind.PlusUconst: - case DwarfOperationKind.Regx: - case DwarfOperationKind.Piece: - case DwarfOperationKind.Addrx: - case DwarfOperationKind.GNUAddrIndex: - case DwarfOperationKind.Constx: - case DwarfOperationKind.GNUConstIndex: - writer.WriteULEB128(Operand1.U64); - break; - - case DwarfOperationKind.Consts: - case DwarfOperationKind.Fbreg: - writer.WriteILEB128(Operand1.I64); - break; - - case DwarfOperationKind.Deref: - case DwarfOperationKind.Dup: - case DwarfOperationKind.Drop: - case DwarfOperationKind.Over: - case DwarfOperationKind.Swap: - case DwarfOperationKind.Rot: - case DwarfOperationKind.Xderef: - case DwarfOperationKind.Abs: - case DwarfOperationKind.And: - case DwarfOperationKind.Div: - case DwarfOperationKind.Minus: - case DwarfOperationKind.Mod: - case DwarfOperationKind.Mul: - case DwarfOperationKind.Neg: - case DwarfOperationKind.Not: - case DwarfOperationKind.Or: - case DwarfOperationKind.Plus: - case DwarfOperationKind.Shl: - case DwarfOperationKind.Shr: - case DwarfOperationKind.Shra: - case DwarfOperationKind.Xor: - case DwarfOperationKind.Eq: - case DwarfOperationKind.Ge: - case DwarfOperationKind.Gt: - case DwarfOperationKind.Le: - case DwarfOperationKind.Lt: - case DwarfOperationKind.Ne: - case DwarfOperationKind.Nop: - case DwarfOperationKind.PushObjectAddress: - case DwarfOperationKind.FormTlsAddress: - case DwarfOperationKind.CallFrameCfa: - break; - - case DwarfOperationKind.Bra: - case DwarfOperationKind.Skip: - writer.WriteU16((ushort)((long)Position + 2 - (long)((DwarfOperation)Operand0!).Position)); - break; - - case DwarfOperationKind.Lit0: - case DwarfOperationKind.Lit1: - case DwarfOperationKind.Lit2: - case DwarfOperationKind.Lit3: - case DwarfOperationKind.Lit4: - case DwarfOperationKind.Lit5: - case DwarfOperationKind.Lit6: - case DwarfOperationKind.Lit7: - case DwarfOperationKind.Lit8: - case DwarfOperationKind.Lit9: - case DwarfOperationKind.Lit10: - case DwarfOperationKind.Lit11: - case DwarfOperationKind.Lit12: - case DwarfOperationKind.Lit13: - case DwarfOperationKind.Lit14: - case DwarfOperationKind.Lit15: - case DwarfOperationKind.Lit16: - case DwarfOperationKind.Lit17: - case DwarfOperationKind.Lit18: - case DwarfOperationKind.Lit19: - case DwarfOperationKind.Lit20: - case DwarfOperationKind.Lit21: - case DwarfOperationKind.Lit22: - case DwarfOperationKind.Lit23: - case DwarfOperationKind.Lit24: - case DwarfOperationKind.Lit25: - case DwarfOperationKind.Lit26: - case DwarfOperationKind.Lit27: - case DwarfOperationKind.Lit28: - case DwarfOperationKind.Lit29: - case DwarfOperationKind.Lit30: - case DwarfOperationKind.Lit31: - case DwarfOperationKind.Reg0: - case DwarfOperationKind.Reg1: - case DwarfOperationKind.Reg2: - case DwarfOperationKind.Reg3: - case DwarfOperationKind.Reg4: - case DwarfOperationKind.Reg5: - case DwarfOperationKind.Reg6: - case DwarfOperationKind.Reg7: - case DwarfOperationKind.Reg8: - case DwarfOperationKind.Reg9: - case DwarfOperationKind.Reg10: - case DwarfOperationKind.Reg11: - case DwarfOperationKind.Reg12: - case DwarfOperationKind.Reg13: - case DwarfOperationKind.Reg14: - case DwarfOperationKind.Reg15: - case DwarfOperationKind.Reg16: - case DwarfOperationKind.Reg17: - case DwarfOperationKind.Reg18: - case DwarfOperationKind.Reg19: - case DwarfOperationKind.Reg20: - case DwarfOperationKind.Reg21: - case DwarfOperationKind.Reg22: - case DwarfOperationKind.Reg23: - case DwarfOperationKind.Reg24: - case DwarfOperationKind.Reg25: - case DwarfOperationKind.Reg26: - case DwarfOperationKind.Reg27: - case DwarfOperationKind.Reg28: - case DwarfOperationKind.Reg29: - case DwarfOperationKind.Reg30: - case DwarfOperationKind.Reg31: - case DwarfOperationKind.StackValue: - break; - - case DwarfOperationKind.Breg0: - case DwarfOperationKind.Breg1: - case DwarfOperationKind.Breg2: - case DwarfOperationKind.Breg3: - case DwarfOperationKind.Breg4: - case DwarfOperationKind.Breg5: - case DwarfOperationKind.Breg6: - case DwarfOperationKind.Breg7: - case DwarfOperationKind.Breg8: - case DwarfOperationKind.Breg9: - case DwarfOperationKind.Breg10: - case DwarfOperationKind.Breg11: - case DwarfOperationKind.Breg12: - case DwarfOperationKind.Breg13: - case DwarfOperationKind.Breg14: - case DwarfOperationKind.Breg15: - case DwarfOperationKind.Breg16: - case DwarfOperationKind.Breg17: - case DwarfOperationKind.Breg18: - case DwarfOperationKind.Breg19: - case DwarfOperationKind.Breg20: - case DwarfOperationKind.Breg21: - case DwarfOperationKind.Breg22: - case DwarfOperationKind.Breg23: - case DwarfOperationKind.Breg24: - case DwarfOperationKind.Breg25: - case DwarfOperationKind.Breg26: - case DwarfOperationKind.Breg27: - case DwarfOperationKind.Breg28: - case DwarfOperationKind.Breg29: - case DwarfOperationKind.Breg30: - case DwarfOperationKind.Breg31: - writer.WriteILEB128(Operand2.I64); - break; - - case DwarfOperationKind.Bregx: - writer.WriteULEB128(Operand1.U64); - writer.WriteILEB128(Operand2.I64); - break; - - case DwarfOperationKind.Call2: - writer.WriteU16((ushort)((DwarfDIE)Operand0!).Position); - break; - - case DwarfOperationKind.Call4: - writer.WriteU32((uint)((DwarfDIE)Operand0!).Position); - break; - - case DwarfOperationKind.CallRef: - writer.WriteUInt(((DwarfDIE)Operand0!).Position); - break; - - case DwarfOperationKind.BitPiece: - writer.WriteULEB128(Operand1.U64); - writer.WriteULEB128(Operand2.U64); - break; - - case DwarfOperationKind.ImplicitValue: - { - var stream = (Stream)Operand0!; - writer.WriteULEB128((ulong)stream.Position); - writer.Write(stream); - break; - } - - case DwarfOperationKind.ImplicitPointer: - case DwarfOperationKind.GNUImplicitPointer: - { - ulong offset = ((DwarfDIE)Operand0!).Position; - // a reference to a debugging information entry that describes the dereferenced object’s value - if (writer.CurrentUnit!.Version == 2) - { - writer.WriteUInt(offset); - } - else - { - writer.WriteUIntFromEncoding(offset); - } - // a signed number that is treated as a byte offset from the start of that value - writer.WriteILEB128(Operand1.I64); - break; - } - - case DwarfOperationKind.EntryValue: - case DwarfOperationKind.GNUEntryValue: - { - var expression = (DwarfExpression)Operand0!; - writer.WriteULEB128(expression.Size); - expression.WriteInternal(writer); - break; - } - - case DwarfOperationKind.ConstType: - case DwarfOperationKind.GNUConstType: - { - // The DW_OP_const_type operation takes three operands - - // The first operand is an unsigned LEB128 integer that represents the offset - // of a debugging information entry in the current compilation unit, which - // must be a DW_TAG_base_type entry that provides the type of the constant provided - writer.WriteULEB128(((DwarfDIE)Operand0!).Position); - WriteEncodedValue(writer, Kind, Operand1.U64, (byte)Operand2.U64); - break; - } - - case DwarfOperationKind.RegvalType: - case DwarfOperationKind.GNURegvalType: - { - // The DW_OP_regval_type operation provides the contents of a given register - // interpreted as a value of a given type - - // The first operand is an unsigned LEB128 number, which identifies a register - // whose contents is to be pushed onto the stack - writer.WriteULEB128(Operand1.U64); - - // The second operand is an unsigned LEB128 number that represents the offset - // of a debugging information entry in the current compilation unit - writer.WriteULEB128(((DwarfDIE)Operand0!).Position); - break; - } - - case DwarfOperationKind.DerefType: - case DwarfOperationKind.GNUDerefType: - case DwarfOperationKind.XderefType: - { - // The DW_OP_deref_type operation behaves like the DW_OP_deref_size operation: - // it pops the top stack entry and treats it as an address. - - // This operand is a 1-byte unsigned integral constant whose value which is the - // same as the size of the base type referenced by the second operand - writer.WriteU8((byte)Operand1.U64); - - // The second operand is an unsigned LEB128 number that represents the offset - // of a debugging information entry in the current compilation unit - writer.WriteULEB128(((DwarfDIE)Operand0!).Position); - break; - } - - case DwarfOperationKind.Convert: - case DwarfOperationKind.GNUConvert: - case DwarfOperationKind.Reinterpret: - case DwarfOperationKind.GNUReinterpret: - writer.WriteULEB128(((DwarfDIE)Operand0!).Position); - break; - - case DwarfOperationKind.GNUPushTlsAddress: - case DwarfOperationKind.GNUUninit: - break; - - case DwarfOperationKind.GNUEncodedAddr: - WriteEncodedValue(writer, Kind, Operand1.U64, (byte)Operand2.U64); - break; - - case DwarfOperationKind.GNUParameterRef: - writer.WriteU32((uint)Operand1.U64); - break; - - default: - throw new NotSupportedException($"The {nameof(DwarfOperationKind)} {Kind} is not supported"); + // The DW_OP_deref_type operation behaves like the DW_OP_deref_size operation: + // it pops the top stack entry and treats it as an address. + + // This operand is a 1-byte unsigned integral constant whose value which is the + // same as the size of the base type referenced by the second operand + writer.WriteU8((byte)Operand1.U64); + + // The second operand is an unsigned LEB128 number that represents the offset + // of a debugging information entry in the current compilation unit + writer.WriteULEB128(((DwarfDIE)Operand0!).Position); + break; } - Debug.Assert(writer.Position - startOpOffset == Size); + case DwarfOperationKind.Convert: + case DwarfOperationKind.GNUConvert: + case DwarfOperationKind.Reinterpret: + case DwarfOperationKind.GNUReinterpret: + writer.WriteULEB128(((DwarfDIE)Operand0!).Position); + break; + + case DwarfOperationKind.GNUPushTlsAddress: + case DwarfOperationKind.GNUUninit: + break; + + case DwarfOperationKind.GNUEncodedAddr: + WriteEncodedValue(writer, Kind, Operand1.U64, (byte)Operand2.U64); + break; + + case DwarfOperationKind.GNUParameterRef: + writer.WriteU32((uint)Operand1.U64); + break; + + default: + throw new NotSupportedException($"The {nameof(DwarfOperationKind)} {Kind} is not supported"); } - private static ulong ReadEncodedValue(DwarfReader reader, DwarfOperationKind kind, out byte size) + Debug.Assert(writer.Position - startOpOffset == Size); + } + + private static ulong ReadEncodedValue(DwarfReader reader, DwarfOperationKind kind, out byte size) + { + size = reader.ReadU8(); + switch (size) { - size = reader.ReadU8(); - switch (size) - { - case 0: - return reader.ReadUInt(); - case 1: - return reader.ReadU8(); - case 2: - return reader.ReadU16(); - case 4: - return reader.ReadU32(); - case 8: - return reader.ReadU64(); - default: - throw new InvalidOperationException($"Invalid Encoded address size {size} for {kind}"); - } + case 0: + return reader.ReadUInt(); + case 1: + return reader.ReadU8(); + case 2: + return reader.ReadU16(); + case 4: + return reader.ReadU32(); + case 8: + return reader.ReadU64(); + default: + throw new InvalidOperationException($"Invalid Encoded address size {size} for {kind}"); } + } - private static void WriteEncodedValue(DwarfWriter writer, DwarfOperationKindEx kind, ulong value, byte size) + private static void WriteEncodedValue(DwarfWriter writer, DwarfOperationKindEx kind, ulong value, byte size) + { + writer.WriteU8(size); + switch (size) { - writer.WriteU8(size); - switch (size) - { - case 0: - writer.WriteUInt(value); - break; - case 1: - writer.WriteU8((byte)value); - break; - case 2: - writer.WriteU16((ushort)value); - break; - case 4: - writer.WriteU32((uint)value); - break; - case 8: - writer.WriteU64(value); - break; - default: - // TODO: report via diagnostics in Verify - throw new InvalidOperationException($"Invalid Encoded address size {size} for {kind}"); - } + case 0: + writer.WriteUInt(value); + break; + case 1: + writer.WriteU8((byte)value); + break; + case 2: + writer.WriteU16((ushort)value); + break; + case 4: + writer.WriteU32((uint)value); + break; + case 8: + writer.WriteU64(value); + break; + default: + // TODO: report via diagnostics in Verify + throw new InvalidOperationException($"Invalid Encoded address size {size} for {kind}"); } + } - private static readonly DwarfReader.DwarfDIEReferenceResolver DwarfExpressionLocationDIEReferenceResolverInstance = DwarfExpressionLocationDIEReferenceResolver; + private static readonly DwarfReader.DwarfDIEReferenceResolver DwarfExpressionLocationDIEReferenceResolverInstance = DwarfExpressionLocationDIEReferenceResolver; - private static void DwarfExpressionLocationDIEReferenceResolver(ref DwarfReader.DwarfDIEReference dieRef) - { - var op = (DwarfOperation)dieRef.DwarfObject; - op.Operand0 = dieRef.Resolved!; - } + private static void DwarfExpressionLocationDIEReferenceResolver(ref DwarfReader.DwarfDIEReference dieRef) + { + var op = (DwarfOperation)dieRef.DwarfObject; + op.Operand0 = dieRef.Resolved!; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfOperationKindEx.cs b/src/LibObjectFile/Dwarf/DwarfOperationKindEx.cs index f932e6f..20f5790 100644 --- a/src/LibObjectFile/Dwarf/DwarfOperationKindEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfOperationKindEx.cs @@ -4,56 +4,55 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public readonly partial struct DwarfOperationKindEx : IEquatable { - public readonly partial struct DwarfOperationKindEx : IEquatable + public DwarfOperationKindEx(byte value) { - public DwarfOperationKindEx(byte value) - { - Value = (DwarfOperationKind)value; - } + Value = (DwarfOperationKind)value; + } - public DwarfOperationKindEx(DwarfOperationKind value) - { - Value = value; - } + public DwarfOperationKindEx(DwarfOperationKind value) + { + Value = value; + } - public readonly DwarfOperationKind Value; + public readonly DwarfOperationKind Value; - public override string ToString() - { - return ToStringInternal() ?? $"Unknown {nameof(DwarfOperationKindEx)} ({(uint)Value:x2})"; - } + public override string ToString() + { + return ToStringInternal() ?? $"Unknown {nameof(DwarfOperationKindEx)} ({(uint)Value:x2})"; + } - public bool Equals(DwarfOperationKindEx other) - { - return Value == other.Value; - } + public bool Equals(DwarfOperationKindEx other) + { + return Value == other.Value; + } - public override bool Equals(object? obj) - { - return obj is DwarfOperationKindEx other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is DwarfOperationKindEx other && Equals(other); + } - public override int GetHashCode() - { - return Value.GetHashCode(); - } + public override int GetHashCode() + { + return Value.GetHashCode(); + } - public static bool operator ==(DwarfOperationKindEx left, DwarfOperationKindEx right) - { - return left.Equals(right); - } + public static bool operator ==(DwarfOperationKindEx left, DwarfOperationKindEx right) + { + return left.Equals(right); + } - public static bool operator !=(DwarfOperationKindEx left, DwarfOperationKindEx right) - { - return !left.Equals(right); - } + public static bool operator !=(DwarfOperationKindEx left, DwarfOperationKindEx right) + { + return !left.Equals(right); + } - public static explicit operator uint(DwarfOperationKindEx kind) => (uint)kind.Value; + public static explicit operator uint(DwarfOperationKindEx kind) => (uint)kind.Value; - public static implicit operator DwarfOperationKindEx(DwarfOperationKind kind) => new DwarfOperationKindEx(kind); + public static implicit operator DwarfOperationKindEx(DwarfOperationKind kind) => new DwarfOperationKindEx(kind); - public static implicit operator DwarfOperationKind(DwarfOperationKindEx kind) => kind.Value; - } + public static implicit operator DwarfOperationKind(DwarfOperationKindEx kind) => kind.Value; } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfPrinter.cs b/src/LibObjectFile/Dwarf/DwarfPrinter.cs index 604ed93..120585d 100644 --- a/src/LibObjectFile/Dwarf/DwarfPrinter.cs +++ b/src/LibObjectFile/Dwarf/DwarfPrinter.cs @@ -6,229 +6,228 @@ using System.IO; using System.Linq; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public static class DwarfPrinter { - public static class DwarfPrinter + public static void Print(this DwarfAbbreviationTable abbrevTable, TextWriter writer) { - public static void Print(this DwarfAbbreviationTable abbrevTable, TextWriter writer) - { - if (writer == null) throw new ArgumentNullException(nameof(writer)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); - writer.WriteLine("Contents of the .debug_abbrev section:"); + writer.WriteLine("Contents of the .debug_abbrev section:"); - foreach (var abbreviation in abbrevTable.Abbreviations) - { - Print(abbreviation, writer); - } + foreach (var abbreviation in abbrevTable.Abbreviations) + { + Print(abbreviation, writer); } + } - public static void Print(this DwarfAbbreviation abbreviation, TextWriter writer) - { - if (writer == null) throw new ArgumentNullException(nameof(writer)); + public static void Print(this DwarfAbbreviation abbreviation, TextWriter writer) + { + if (writer == null) throw new ArgumentNullException(nameof(writer)); - writer.WriteLine(); + writer.WriteLine(); - writer.WriteLine($" Number TAG (0x{abbreviation.Position})"); + writer.WriteLine($" Number TAG (0x{abbreviation.Position})"); - foreach (var item in abbreviation.Items) + foreach (var item in abbreviation.Items) + { + writer.WriteLine($" {item.Code} {item.Tag} [{(item.HasChildren ? "has children" : "no children")}]"); + var descriptors = item.Descriptors; + for (int i = 0; i < descriptors.Length; i++) { - writer.WriteLine($" {item.Code} {item.Tag} [{(item.HasChildren ? "has children" : "no children")}]"); - var descriptors = item.Descriptors; - for (int i = 0; i < descriptors.Length; i++) - { - var descriptor = descriptors[i]; - writer.WriteLine($" {descriptor.Kind.ToString(),-18} {descriptor.Form}"); - } - writer.WriteLine(" DW_AT value: 0 DW_FORM value: 0"); + var descriptor = descriptors[i]; + writer.WriteLine($" {descriptor.Kind.ToString(),-18} {descriptor.Form}"); } + writer.WriteLine(" DW_AT value: 0 DW_FORM value: 0"); } + } - public static void PrintRelocations(this DwarfRelocatableSection relocSection, TextWriter writer) - { - if (writer == null) throw new ArgumentNullException(nameof(writer)); + public static void PrintRelocations(this DwarfRelocatableSection relocSection, TextWriter writer) + { + if (writer == null) throw new ArgumentNullException(nameof(writer)); - writer.WriteLine(); - if (relocSection.Relocations.Count == 0) - { - writer.WriteLine(" There are no relocations in this section."); - return; - } + writer.WriteLine(); + if (relocSection.Relocations.Count == 0) + { + writer.WriteLine(" There are no relocations in this section."); + return; + } - writer.WriteLine($" Relocations of this section contains {(relocSection.Relocations.Count > 1 ? $"{relocSection.Relocations.Count} entries" : "1 entry")}:"); - writer.WriteLine(); - writer.WriteLine(" Offset Target Size Addend"); - foreach (var reloc in relocSection.Relocations) - { - writer.WriteLine($"{reloc.Offset:x16} {reloc.Target,-24} {(uint)reloc.Size,-6} {reloc.Addend:x}"); - } + writer.WriteLine($" Relocations of this section contains {(relocSection.Relocations.Count > 1 ? $"{relocSection.Relocations.Count} entries" : "1 entry")}:"); + writer.WriteLine(); + writer.WriteLine(" Offset Target Size Addend"); + foreach (var reloc in relocSection.Relocations) + { + writer.WriteLine($"{reloc.Offset:x16} {reloc.Target,-24} {(uint)reloc.Size,-6} {reloc.Addend:x}"); } + } - public static void Print(this DwarfInfoSection debugInfo, TextWriter writer) + public static void Print(this DwarfInfoSection debugInfo, TextWriter writer) + { + if (writer == null) throw new ArgumentNullException(nameof(writer)); + + foreach (var unit in debugInfo.Units) { - if (writer == null) throw new ArgumentNullException(nameof(writer)); + Print(unit, writer); + } + } - foreach (var unit in debugInfo.Units) - { - Print(unit, writer); - } + public static void Print(this DwarfUnit unit, TextWriter writer) + { + if (writer == null) throw new ArgumentNullException(nameof(writer)); + writer.WriteLine("Contents of the .debug_info section:"); + writer.WriteLine(); + writer.WriteLine($" Compilation Unit @ offset 0x{unit.Position:x}:"); + writer.WriteLine($" Length: 0x{unit.UnitLength:x}"); + writer.WriteLine($" Version: {unit.Version}"); + writer.WriteLine($" Abbrev Offset: 0x{unit.Abbreviation?.Position ?? 0:x}"); + writer.WriteLine($" Pointer Size: {(uint)unit.AddressSize}"); + if (unit.Root != null) + { + Print(unit.Root, writer); } + } - public static void Print(this DwarfUnit unit, TextWriter writer) + public static void Print(this DwarfDIE die, TextWriter writer, int level = 0) + { + if (writer == null) throw new ArgumentNullException(nameof(writer)); + + writer.WriteLine($" <{level}><{die.Position:x}>: Abbrev Number: {die.Abbrev!.Code} ({die.Tag})"); + + foreach (var attr in die.Attributes) { - if (writer == null) throw new ArgumentNullException(nameof(writer)); - writer.WriteLine("Contents of the .debug_info section:"); - writer.WriteLine(); - writer.WriteLine($" Compilation Unit @ offset 0x{unit.Position:x}:"); - writer.WriteLine($" Length: 0x{unit.UnitLength:x}"); - writer.WriteLine($" Version: {unit.Version}"); - writer.WriteLine($" Abbrev Offset: 0x{unit.Abbreviation?.Position ?? 0:x}"); - writer.WriteLine($" Pointer Size: {(uint)unit.AddressSize}"); - if (unit.Root != null) + string? attrValue = null; + switch (attr.ValueAsObject) { - Print(unit.Root, writer); + case DwarfDIE dieRef: + attrValue = $"<0x{dieRef.Position:x}>"; + break; + case string str: + attrValue = str; + break; + case DwarfExpression expr: + attrValue = $"{expr.Operations.Count} OpCodes ({string.Join(", ", expr.Operations.Select(x => x.Kind))})"; + break; } - } - public static void Print(this DwarfDIE die, TextWriter writer, int level = 0) - { - if (writer == null) throw new ArgumentNullException(nameof(writer)); + switch (attr.Kind.Value) + { + case DwarfAttributeKind.Language: - writer.WriteLine($" <{level}><{die.Position:x}>: Abbrev Number: {die.Abbrev!.Code} ({die.Tag})"); + attrValue = $"{attr.ValueAsU64} {GetLanguageKind((DwarfLanguageKind)attr.ValueAsU64)}"; + break; + } - foreach (var attr in die.Attributes) + if (attrValue == null) { - string? attrValue = null; - switch (attr.ValueAsObject) - { - case DwarfDIE dieRef: - attrValue = $"<0x{dieRef.Position:x}>"; - break; - case string str: - attrValue = str; - break; - case DwarfExpression expr: - attrValue = $"{expr.Operations.Count} OpCodes ({string.Join(", ", expr.Operations.Select(x => x.Kind))})"; - break; - } - switch (attr.Kind.Value) + var encoding = DwarfHelper.GetAttributeEncoding(attr.Kind); + if ((encoding & DwarfAttributeEncoding.Address) != 0) { - case DwarfAttributeKind.Language: - - attrValue = $"{attr.ValueAsU64} {GetLanguageKind((DwarfLanguageKind)attr.ValueAsU64)}"; - break; + attrValue = $"0x{attr.ValueAsU64:x}"; } - - if (attrValue == null) + else { - - var encoding = DwarfHelper.GetAttributeEncoding(attr.Kind); - if ((encoding & DwarfAttributeEncoding.Address) != 0) - { - attrValue = $"0x{attr.ValueAsU64:x}"; - } - else - { - attrValue = $"{attr.ValueAsU64}"; - } + attrValue = $"{attr.ValueAsU64}"; } - - writer.WriteLine($" <{attr.Position:x}> {attr.Kind,-18} : {attrValue}"); } - foreach (var child in die.Children) - { - Print(child, writer, level + 1); - } + writer.WriteLine($" <{attr.Position:x}> {attr.Kind,-18} : {attrValue}"); } - public static void Print(this DwarfAddressRangeTable addressRangeTable, TextWriter writer) + foreach (var child in die.Children) { - if (writer == null) throw new ArgumentNullException(nameof(writer)); - - writer.WriteLine("Contents of the .debug_aranges section:"); - writer.WriteLine(); - writer.WriteLine($" Length: {addressRangeTable.HeaderLength}"); - writer.WriteLine($" Version: {addressRangeTable.Version}"); - writer.WriteLine($" Offset into .debug_info: 0x{addressRangeTable.DebugInfoOffset:x}"); - writer.WriteLine($" Pointer Size: {(byte)addressRangeTable.AddressSize}"); - writer.WriteLine($" Segment Size: {(byte)addressRangeTable.SegmentSelectorSize}"); - writer.WriteLine(); - var addressSize = (uint)addressRangeTable.AddressSize; - if (addressSize > 4) - { - writer.WriteLine(" Address Length"); - } - else - { - writer.WriteLine(" Address Length"); - } + Print(child, writer, level + 1); + } + } - var formatStyle = "x" + (addressSize * 2); - foreach (var range in addressRangeTable.Ranges) - { - writer.WriteLine($" {range.Address.ToString(formatStyle)} {range.Length.ToString(formatStyle)}"); - } - writer.WriteLine($" {((ulong)0).ToString(formatStyle)} {((ulong)0).ToString(formatStyle)}"); + public static void Print(this DwarfAddressRangeTable addressRangeTable, TextWriter writer) + { + if (writer == null) throw new ArgumentNullException(nameof(writer)); + + writer.WriteLine("Contents of the .debug_aranges section:"); + writer.WriteLine(); + writer.WriteLine($" Length: {addressRangeTable.HeaderLength}"); + writer.WriteLine($" Version: {addressRangeTable.Version}"); + writer.WriteLine($" Offset into .debug_info: 0x{addressRangeTable.DebugInfoOffset:x}"); + writer.WriteLine($" Pointer Size: {(byte)addressRangeTable.AddressSize}"); + writer.WriteLine($" Segment Size: {(byte)addressRangeTable.SegmentSelectorSize}"); + writer.WriteLine(); + var addressSize = (uint)addressRangeTable.AddressSize; + if (addressSize > 4) + { + writer.WriteLine(" Address Length"); + } + else + { + writer.WriteLine(" Address Length"); } - private static string GetLanguageKind(DwarfLanguageKind kind) + var formatStyle = "x" + (addressSize * 2); + foreach (var range in addressRangeTable.Ranges) { - var rawKind = (uint) kind; - switch (rawKind) - { - case DwarfNative.DW_LANG_C89: return "(ANSI C)"; - case DwarfNative.DW_LANG_C: return "(non-ANSI C)"; - case DwarfNative.DW_LANG_Ada83: return "(Ada)"; - case DwarfNative.DW_LANG_C_plus_plus: return "(C++)"; - case DwarfNative.DW_LANG_Cobol74: return "(Cobol 74)"; - case DwarfNative.DW_LANG_Cobol85: return "(Cobol 85)"; - case DwarfNative.DW_LANG_Fortran77: return "(FORTRAN 77)"; - case DwarfNative.DW_LANG_Fortran90: return "(Fortran 90)"; - case DwarfNative.DW_LANG_Pascal83: return "(ANSI Pascal)"; - case DwarfNative.DW_LANG_Modula2: return "(Modula 2)"; - // DWARF 2.1 - case DwarfNative.DW_LANG_Java: return "(Java)"; - case DwarfNative.DW_LANG_C99: return "(ANSI C99)"; - case DwarfNative.DW_LANG_Ada95: return "(ADA 95)"; - case DwarfNative.DW_LANG_Fortran95: return "(Fortran 95)"; - // DWARF 3 - case DwarfNative.DW_LANG_PLI: return "(PLI)"; - case DwarfNative.DW_LANG_ObjC: return "(Objective C)"; - case DwarfNative.DW_LANG_ObjC_plus_plus: return "(Objective C++)"; - case DwarfNative.DW_LANG_UPC: return "(Unified Parallel C)"; - case DwarfNative.DW_LANG_D: return "(D)"; - // DWARF 4 - case DwarfNative.DW_LANG_Python: return "(Python)"; - // DWARF 5 - case DwarfNative.DW_LANG_OpenCL: return "(OpenCL)"; - case DwarfNative.DW_LANG_Go: return "(Go)"; - case DwarfNative.DW_LANG_Modula3: return "(Modula 3)"; - case DwarfNative.DW_LANG_Haskel: return "(Haskell)"; - case DwarfNative.DW_LANG_C_plus_plus_03: return "(C++03)"; - case DwarfNative.DW_LANG_C_plus_plus_11: return "(C++11)"; - case DwarfNative.DW_LANG_OCaml: return "(OCaml)"; - case DwarfNative.DW_LANG_Rust: return "(Rust)"; - case DwarfNative.DW_LANG_C11: return "(C11)"; - case DwarfNative.DW_LANG_Swift: return "(Swift)"; - case DwarfNative.DW_LANG_Julia: return "(Julia)"; - case DwarfNative.DW_LANG_Dylan: return "(Dylan)"; - case DwarfNative.DW_LANG_C_plus_plus_14: return "(C++14)"; - case DwarfNative.DW_LANG_Fortran03: return "(Fortran 03)"; - case DwarfNative.DW_LANG_Fortran08: return "(Fortran 08)"; - case DwarfNative.DW_LANG_RenderScript: return "(RenderScript)"; - - case DwarfNative.DW_LANG_Mips_Assembler: return "(MIPS assembler)"; + writer.WriteLine($" {range.Address.ToString(formatStyle)} {range.Length.ToString(formatStyle)}"); + } + writer.WriteLine($" {((ulong)0).ToString(formatStyle)} {((ulong)0).ToString(formatStyle)}"); + } + + private static string GetLanguageKind(DwarfLanguageKind kind) + { + var rawKind = (uint) kind; + switch (rawKind) + { + case DwarfNative.DW_LANG_C89: return "(ANSI C)"; + case DwarfNative.DW_LANG_C: return "(non-ANSI C)"; + case DwarfNative.DW_LANG_Ada83: return "(Ada)"; + case DwarfNative.DW_LANG_C_plus_plus: return "(C++)"; + case DwarfNative.DW_LANG_Cobol74: return "(Cobol 74)"; + case DwarfNative.DW_LANG_Cobol85: return "(Cobol 85)"; + case DwarfNative.DW_LANG_Fortran77: return "(FORTRAN 77)"; + case DwarfNative.DW_LANG_Fortran90: return "(Fortran 90)"; + case DwarfNative.DW_LANG_Pascal83: return "(ANSI Pascal)"; + case DwarfNative.DW_LANG_Modula2: return "(Modula 2)"; + // DWARF 2.1 + case DwarfNative.DW_LANG_Java: return "(Java)"; + case DwarfNative.DW_LANG_C99: return "(ANSI C99)"; + case DwarfNative.DW_LANG_Ada95: return "(ADA 95)"; + case DwarfNative.DW_LANG_Fortran95: return "(Fortran 95)"; + // DWARF 3 + case DwarfNative.DW_LANG_PLI: return "(PLI)"; + case DwarfNative.DW_LANG_ObjC: return "(Objective C)"; + case DwarfNative.DW_LANG_ObjC_plus_plus: return "(Objective C++)"; + case DwarfNative.DW_LANG_UPC: return "(Unified Parallel C)"; + case DwarfNative.DW_LANG_D: return "(D)"; + // DWARF 4 + case DwarfNative.DW_LANG_Python: return "(Python)"; + // DWARF 5 + case DwarfNative.DW_LANG_OpenCL: return "(OpenCL)"; + case DwarfNative.DW_LANG_Go: return "(Go)"; + case DwarfNative.DW_LANG_Modula3: return "(Modula 3)"; + case DwarfNative.DW_LANG_Haskel: return "(Haskell)"; + case DwarfNative.DW_LANG_C_plus_plus_03: return "(C++03)"; + case DwarfNative.DW_LANG_C_plus_plus_11: return "(C++11)"; + case DwarfNative.DW_LANG_OCaml: return "(OCaml)"; + case DwarfNative.DW_LANG_Rust: return "(Rust)"; + case DwarfNative.DW_LANG_C11: return "(C11)"; + case DwarfNative.DW_LANG_Swift: return "(Swift)"; + case DwarfNative.DW_LANG_Julia: return "(Julia)"; + case DwarfNative.DW_LANG_Dylan: return "(Dylan)"; + case DwarfNative.DW_LANG_C_plus_plus_14: return "(C++14)"; + case DwarfNative.DW_LANG_Fortran03: return "(Fortran 03)"; + case DwarfNative.DW_LANG_Fortran08: return "(Fortran 08)"; + case DwarfNative.DW_LANG_RenderScript: return "(RenderScript)"; + + case DwarfNative.DW_LANG_Mips_Assembler: return "(MIPS assembler)"; - case DwarfNative.DW_LANG_Upc: return "(Unified Parallel C)"; + case DwarfNative.DW_LANG_Upc: return "(Unified Parallel C)"; - default: - if (rawKind >= DwarfNative.DW_LANG_lo_user && rawKind <= DwarfNative.DW_LANG_hi_user) - return $"(implementation defined: {rawKind:x})"; - break; - } - - return $"(Unknown: {rawKind:x})"; + default: + if (rawKind >= DwarfNative.DW_LANG_lo_user && rawKind <= DwarfNative.DW_LANG_hi_user) + return $"(implementation defined: {rawKind:x})"; + break; } + + return $"(Unknown: {rawKind:x})"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfReader.cs b/src/LibObjectFile/Dwarf/DwarfReader.cs index 922c86a..19f56b5 100644 --- a/src/LibObjectFile/Dwarf/DwarfReader.cs +++ b/src/LibObjectFile/Dwarf/DwarfReader.cs @@ -5,169 +5,168 @@ using System.Collections.Generic; using System.Diagnostics; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public sealed class DwarfReader : DwarfReaderWriter { - public sealed class DwarfReader : DwarfReaderWriter + private readonly Dictionary _registeredDIEPerCompilationUnit; + private readonly Dictionary _registeredDIEPerSection; + private readonly List _unresolvedDIECompilationUnitReference; + private readonly List _attributesWithUnresolvedDIESectionReference; + private readonly Stack _stack; + private readonly Stack _stackWithLineProgramTable; + + internal DwarfReader(DwarfReaderContext context, DwarfFile file, DiagnosticBag diagnostics) : base(file, diagnostics) { - private readonly Dictionary _registeredDIEPerCompilationUnit; - private readonly Dictionary _registeredDIEPerSection; - private readonly List _unresolvedDIECompilationUnitReference; - private readonly List _attributesWithUnresolvedDIESectionReference; - private readonly Stack _stack; - private readonly Stack _stackWithLineProgramTable; - - internal DwarfReader(DwarfReaderContext context, DwarfFile file, DiagnosticBag diagnostics) : base(file, diagnostics) - { - IsReadOnly = context.IsInputReadOnly; - AddressSize = context.AddressSize; - IsLittleEndian = context.IsLittleEndian; - _registeredDIEPerCompilationUnit = new Dictionary(); - _registeredDIEPerSection = new Dictionary(); - _unresolvedDIECompilationUnitReference = new List(); - _attributesWithUnresolvedDIESectionReference = new List(); - OffsetToLineProgramTable = new Dictionary(); - OffsetToLocationList = new Dictionary(); - _stack = new Stack(); - _stackWithLineProgramTable = new Stack(); - } + IsReadOnly = context.IsInputReadOnly; + AddressSize = context.AddressSize; + IsLittleEndian = context.IsLittleEndian; + _registeredDIEPerCompilationUnit = new Dictionary(); + _registeredDIEPerSection = new Dictionary(); + _unresolvedDIECompilationUnitReference = new List(); + _attributesWithUnresolvedDIESectionReference = new List(); + OffsetToLineProgramTable = new Dictionary(); + OffsetToLocationList = new Dictionary(); + _stack = new Stack(); + _stackWithLineProgramTable = new Stack(); + } - public override bool IsReadOnly { get; } + public override bool IsReadOnly { get; } - public DwarfUnitKind DefaultUnitKind { get; internal set; } + public DwarfUnitKind DefaultUnitKind { get; internal set; } - internal int DIELevel { get; set; } + internal int DIELevel { get; set; } - internal DwarfDIE? CurrentDIE => _stack.Count > 0 ? _stack.Peek() : null; + internal DwarfDIE? CurrentDIE => _stack.Count > 0 ? _stack.Peek() : null; - internal DwarfLineProgramTable? CurrentLineProgramTable => _stackWithLineProgramTable.Count > 0 ? _stackWithLineProgramTable.Peek().CurrentLineProgramTable : null; + internal DwarfLineProgramTable? CurrentLineProgramTable => _stackWithLineProgramTable.Count > 0 ? _stackWithLineProgramTable.Peek().CurrentLineProgramTable : null; - internal DwarfAttributeDescriptor CurrentAttributeDescriptor { get; set; } + internal DwarfAttributeDescriptor CurrentAttributeDescriptor { get; set; } - internal Dictionary OffsetToLineProgramTable { get; } + internal Dictionary OffsetToLineProgramTable { get; } - internal Dictionary OffsetToLocationList { get; } + internal Dictionary OffsetToLocationList { get; } - internal void PushDIE(DwarfDIE die) - { - _registeredDIEPerCompilationUnit.Add(die.Position - CurrentUnit!.Position, die); - _registeredDIEPerSection.Add(die.Position, die); - _stack.Push(die); - } + internal void PushDIE(DwarfDIE die) + { + _registeredDIEPerCompilationUnit.Add(die.Position - CurrentUnit!.Position, die); + _registeredDIEPerSection.Add(die.Position, die); + _stack.Push(die); + } - internal void PushLineProgramTable(DwarfLineProgramTable lineTable) + internal void PushLineProgramTable(DwarfLineProgramTable lineTable) + { + var dieWithLineProgramTable = CurrentDIE; + if (_stackWithLineProgramTable.Count > 0 && ReferenceEquals(_stackWithLineProgramTable.Peek(), dieWithLineProgramTable)) { - var dieWithLineProgramTable = CurrentDIE; - if (_stackWithLineProgramTable.Count > 0 && ReferenceEquals(_stackWithLineProgramTable.Peek(), dieWithLineProgramTable)) - { - return; - } - - if (dieWithLineProgramTable != null) - { - _stackWithLineProgramTable.Push(dieWithLineProgramTable); - dieWithLineProgramTable.CurrentLineProgramTable = lineTable; - } + return; } - internal void PopDIE() + if (dieWithLineProgramTable != null) { - var die = _stack.Pop(); - if (die.CurrentLineProgramTable != null) - { - var dieWithProgramLineTable = _stackWithLineProgramTable.Pop(); - Debug.Assert(ReferenceEquals(die, dieWithProgramLineTable)); - dieWithProgramLineTable.CurrentLineProgramTable = null; - } + _stackWithLineProgramTable.Push(dieWithLineProgramTable); + dieWithLineProgramTable.CurrentLineProgramTable = lineTable; } + } - internal void ClearResolveAttributeReferenceWithinCompilationUnit() + internal void PopDIE() + { + var die = _stack.Pop(); + if (die.CurrentLineProgramTable != null) { - _registeredDIEPerCompilationUnit.Clear(); - _unresolvedDIECompilationUnitReference.Clear(); + var dieWithProgramLineTable = _stackWithLineProgramTable.Pop(); + Debug.Assert(ReferenceEquals(die, dieWithProgramLineTable)); + dieWithProgramLineTable.CurrentLineProgramTable = null; } + } + + internal void ClearResolveAttributeReferenceWithinCompilationUnit() + { + _registeredDIEPerCompilationUnit.Clear(); + _unresolvedDIECompilationUnitReference.Clear(); + } - internal void ResolveAttributeReferenceWithinCompilationUnit() + internal void ResolveAttributeReferenceWithinCompilationUnit() + { + // Resolve attribute reference within the CU + foreach (var unresolvedAttrRef in _unresolvedDIECompilationUnitReference) { - // Resolve attribute reference within the CU - foreach (var unresolvedAttrRef in _unresolvedDIECompilationUnitReference) - { - ResolveAttributeReferenceWithinCompilationUnit(unresolvedAttrRef, true); - } + ResolveAttributeReferenceWithinCompilationUnit(unresolvedAttrRef, true); } + } - internal void ResolveAttributeReferenceWithinSection() + internal void ResolveAttributeReferenceWithinSection() + { + // Resolve attribute reference within the section + foreach (var unresolvedAttrRef in _attributesWithUnresolvedDIESectionReference) { - // Resolve attribute reference within the section - foreach (var unresolvedAttrRef in _attributesWithUnresolvedDIESectionReference) - { - ResolveAttributeReferenceWithinSection(unresolvedAttrRef, true); - } + ResolveAttributeReferenceWithinSection(unresolvedAttrRef, true); } + } - internal void ResolveAttributeReferenceWithinCompilationUnit(DwarfDIEReference dieRef, bool errorIfNotFound) + internal void ResolveAttributeReferenceWithinCompilationUnit(DwarfDIEReference dieRef, bool errorIfNotFound) + { + if (_registeredDIEPerCompilationUnit.TryGetValue(dieRef.Offset, out var die)) + { + dieRef.Resolved = die; + dieRef.Resolver(ref dieRef); + } + else { - if (_registeredDIEPerCompilationUnit.TryGetValue(dieRef.Offset, out var die)) + if (errorIfNotFound) { - dieRef.Resolved = die; - dieRef.Resolver(ref dieRef); + if (dieRef.Offset != 0) + { + Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidReference, $"Unable to resolve DIE reference (0x{dieRef.Offset:x}, section 0x{(dieRef.Offset):x}) for {dieRef.DwarfObject} at offset 0x{dieRef.Offset:x}"); + } } else { - if (errorIfNotFound) - { - if (dieRef.Offset != 0) - { - Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidReference, $"Unable to resolve DIE reference (0x{dieRef.Offset:x}, section 0x{(dieRef.Offset):x}) for {dieRef.DwarfObject} at offset 0x{dieRef.Offset:x}"); - } - } - else - { - _unresolvedDIECompilationUnitReference.Add(dieRef); - } + _unresolvedDIECompilationUnitReference.Add(dieRef); } } + } - internal void ResolveAttributeReferenceWithinSection(DwarfDIEReference dieRef, bool errorIfNotFound) + internal void ResolveAttributeReferenceWithinSection(DwarfDIEReference dieRef, bool errorIfNotFound) + { + if (_registeredDIEPerSection.TryGetValue(dieRef.Offset, out var die)) + { + dieRef.Resolved = die; + dieRef.Resolver(ref dieRef); + } + else { - if (_registeredDIEPerSection.TryGetValue(dieRef.Offset, out var die)) + if (errorIfNotFound) { - dieRef.Resolved = die; - dieRef.Resolver(ref dieRef); + if (dieRef.Offset != 0) + { + Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidReference, $"Unable to resolve DIE reference (0x{dieRef.Offset:x}) for {dieRef.DwarfObject} at offset 0x{dieRef.Offset:x}"); + } } else { - if (errorIfNotFound) - { - if (dieRef.Offset != 0) - { - Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidReference, $"Unable to resolve DIE reference (0x{dieRef.Offset:x}) for {dieRef.DwarfObject} at offset 0x{dieRef.Offset:x}"); - } - } - else - { - _attributesWithUnresolvedDIESectionReference.Add(dieRef); - } + _attributesWithUnresolvedDIESectionReference.Add(dieRef); } } + } - internal struct DwarfDIEReference + internal struct DwarfDIEReference + { + public DwarfDIEReference(ulong offset, object dwarfObject, DwarfDIEReferenceResolver resolver) : this() { - public DwarfDIEReference(ulong offset, object dwarfObject, DwarfDIEReferenceResolver resolver) : this() - { - Offset = offset; - DwarfObject = dwarfObject; - Resolver = resolver; - } - - public readonly ulong Offset; + Offset = offset; + DwarfObject = dwarfObject; + Resolver = resolver; + } - public readonly object DwarfObject; + public readonly ulong Offset; - public readonly DwarfDIEReferenceResolver Resolver; + public readonly object DwarfObject; - public DwarfDIE? Resolved; - } + public readonly DwarfDIEReferenceResolver Resolver; - internal delegate void DwarfDIEReferenceResolver(ref DwarfDIEReference reference); + public DwarfDIE? Resolved; } + + internal delegate void DwarfDIEReferenceResolver(ref DwarfDIEReference reference); } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfReaderContext.cs b/src/LibObjectFile/Dwarf/DwarfReaderContext.cs index 6d4fa71..1ffb02b 100644 --- a/src/LibObjectFile/Dwarf/DwarfReaderContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfReaderContext.cs @@ -4,27 +4,26 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfReaderContext : DwarfReaderWriterContext { - public class DwarfReaderContext : DwarfReaderWriterContext + public DwarfReaderContext() { - public DwarfReaderContext() - { - } - - public DwarfReaderContext(DwarfElfContext elfContext) - { - if (elfContext == null) throw new ArgumentNullException(nameof(elfContext)); - IsLittleEndian = elfContext.IsLittleEndian; - AddressSize = elfContext.AddressSize; - DebugLineStream = elfContext.LineTable?.Stream; - DebugStringStream = elfContext.StringTable?.Stream; - DebugAbbrevStream = elfContext.AbbreviationTable?.Stream; - DebugInfoStream = elfContext.InfoSection?.Stream; - DebugAddressRangeStream = elfContext.AddressRangeTable?.Stream; - DebugLocationStream = elfContext.LocationSection?.Stream; - } + } - public bool IsInputReadOnly { get; set; } + public DwarfReaderContext(DwarfElfContext elfContext) + { + if (elfContext == null) throw new ArgumentNullException(nameof(elfContext)); + IsLittleEndian = elfContext.IsLittleEndian; + AddressSize = elfContext.AddressSize; + DebugLineStream = elfContext.LineTable?.Stream; + DebugStringStream = elfContext.StringTable?.Stream; + DebugAbbrevStream = elfContext.AbbreviationTable?.Stream; + DebugInfoStream = elfContext.InfoSection?.Stream; + DebugAddressRangeStream = elfContext.AddressRangeTable?.Stream; + DebugLocationStream = elfContext.LocationSection?.Stream; } + + public bool IsInputReadOnly { get; set; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs b/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs index f2ce923..7566d9f 100644 --- a/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs +++ b/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs @@ -5,169 +5,167 @@ using System; using System.IO; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public abstract class DwarfReaderWriter : ObjectFileReaderWriter { - public abstract class DwarfReaderWriter : ObjectFileReaderWriter + internal DwarfReaderWriter(DwarfFile file, DiagnosticBag diagnostics) : base(file, System.IO.Stream.Null, diagnostics) { - internal DwarfReaderWriter(DwarfFile file, DiagnosticBag diagnostics) : base(System.IO.Stream.Null, diagnostics) - { - File = file; - } + } - public DwarfFile File { get; } + public new DwarfFile File => (DwarfFile)base.File; - public bool Is64BitEncoding { get; set; } + public bool Is64BitEncoding { get; set; } - public DwarfAddressSize AddressSize { get; internal set; } + public DwarfAddressSize AddressSize { get; internal set; } - public DwarfSection? CurrentSection { get; internal set; } + public DwarfSection? CurrentSection { get; internal set; } - public DwarfUnit? CurrentUnit { get; internal set; } + public DwarfUnit? CurrentUnit { get; internal set; } - public DwarfAddressSize SizeOfUIntEncoding() + public DwarfAddressSize SizeOfUIntEncoding() + { + return Is64BitEncoding ? DwarfAddressSize.Bit64 : DwarfAddressSize.Bit32; + } + + public DwarfAddressSize ReadAddressSize() + { + var address_size = (DwarfAddressSize)ReadU8(); + switch (address_size) { - return Is64BitEncoding ? DwarfAddressSize.Bit64 : DwarfAddressSize.Bit32; + case DwarfAddressSize.Bit8: + case DwarfAddressSize.Bit16: + case DwarfAddressSize.Bit32: + case DwarfAddressSize.Bit64: + break; + default: + Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidAddressSize, $"Unsupported address size {(uint)address_size}."); + break; } - public DwarfAddressSize ReadAddressSize() + return address_size; + } + + public void WriteAddressSize(DwarfAddressSize addressSize) + { + WriteU8((byte)addressSize); + } + + public ulong ReadUnitLength() + { + Is64BitEncoding = false; + uint length = ReadU32(); + if (length >= 0xFFFFFFF0) { - var address_size = (DwarfAddressSize)ReadU8(); - switch (address_size) + if (length != 0xFFFFFFFF) { - case DwarfAddressSize.Bit8: - case DwarfAddressSize.Bit16: - case DwarfAddressSize.Bit32: - case DwarfAddressSize.Bit64: - break; - default: - Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidAddressSize, $"Unsupported address size {(uint)address_size}."); - break; + throw new InvalidOperationException($"Unsupported unit length prefix 0x{length:x8}"); } - return address_size; + Is64BitEncoding = true; + return ReadU64(); } + return length; + } - public void WriteAddressSize(DwarfAddressSize addressSize) + public void WriteUnitLength(ulong length) + { + if (Is64BitEncoding) { - WriteU8((byte)addressSize); + WriteU32(0xFFFFFFFF); + WriteU64(length); } - - public ulong ReadUnitLength() + else { - Is64BitEncoding = false; - uint length = ReadU32(); if (length >= 0xFFFFFFF0) { - if (length != 0xFFFFFFFF) - { - throw new InvalidOperationException($"Unsupported unit length prefix 0x{length:x8}"); - } - - Is64BitEncoding = true; - return ReadU64(); + throw new ArgumentOutOfRangeException(nameof(length), $"Must be < 0xFFFFFFF0 but is 0x{length:X}"); } - return length; + WriteU32((uint)length); } + } - public void WriteUnitLength(ulong length) - { - if (Is64BitEncoding) - { - WriteU32(0xFFFFFFFF); - WriteU64(length); - } - else - { - if (length >= 0xFFFFFFF0) - { - throw new ArgumentOutOfRangeException(nameof(length), $"Must be < 0xFFFFFFF0 but is 0x{length:X}"); - } - WriteU32((uint)length); - } - } + public ulong ReadUIntFromEncoding() + { + return Is64BitEncoding ? ReadU64() : ReadU32(); + } - public ulong ReadUIntFromEncoding() + public void WriteUIntFromEncoding(ulong value) + { + if (Is64BitEncoding) { - return Is64BitEncoding ? ReadU64() : ReadU32(); + WriteU64(value); } - - public void WriteUIntFromEncoding(ulong value) + else { - if (Is64BitEncoding) - { - WriteU64(value); - } - else - { - WriteU32((uint)value); - } + WriteU32((uint)value); } + } - public ulong ReadUInt() - { - switch (AddressSize) - { - case DwarfAddressSize.Bit8: - return ReadU8(); - case DwarfAddressSize.Bit16: - return ReadU16(); - case DwarfAddressSize.Bit32: - return ReadU32(); - case DwarfAddressSize.Bit64: - return ReadU64(); - default: - throw new ArgumentOutOfRangeException($"Invalid AddressSize {AddressSize}"); - } + public ulong ReadUInt() + { + switch (AddressSize) + { + case DwarfAddressSize.Bit8: + return ReadU8(); + case DwarfAddressSize.Bit16: + return ReadU16(); + case DwarfAddressSize.Bit32: + return ReadU32(); + case DwarfAddressSize.Bit64: + return ReadU64(); + default: + throw new ArgumentOutOfRangeException($"Invalid AddressSize {AddressSize}"); } + } - public void WriteUInt(ulong target) - { - switch (AddressSize) - { - case DwarfAddressSize.Bit8: - WriteU8((byte)target); - break; - case DwarfAddressSize.Bit16: - WriteU16((ushort)target); - break; - case DwarfAddressSize.Bit32: - WriteU32((uint)target); - break; - case DwarfAddressSize.Bit64: - WriteU64(target); - break; - default: - throw new ArgumentOutOfRangeException($"Invalid AddressSize {AddressSize}"); - } + public void WriteUInt(ulong target) + { + switch (AddressSize) + { + case DwarfAddressSize.Bit8: + WriteU8((byte)target); + break; + case DwarfAddressSize.Bit16: + WriteU16((ushort)target); + break; + case DwarfAddressSize.Bit32: + WriteU32((uint)target); + break; + case DwarfAddressSize.Bit64: + WriteU64(target); + break; + default: + throw new ArgumentOutOfRangeException($"Invalid AddressSize {AddressSize}"); } + } - public ulong ReadULEB128() - { - return Stream.ReadULEB128(); - } + public ulong ReadULEB128() + { + return Stream.ReadULEB128(); + } - public uint ReadULEB128AsU32() - { - return Stream.ReadULEB128AsU32(); - } + public uint ReadULEB128AsU32() + { + return Stream.ReadULEB128AsU32(); + } - public int ReadLEB128AsI32() - { - return Stream.ReadLEB128AsI32(); - } + public int ReadLEB128AsI32() + { + return Stream.ReadLEB128AsI32(); + } - public long ReadILEB128() - { - return Stream.ReadSignedLEB128(); - } + public long ReadILEB128() + { + return Stream.ReadSignedLEB128(); + } - public void WriteULEB128(ulong value) - { - Stream.WriteULEB128(value); - } - public void WriteILEB128(long value) - { - Stream.WriteILEB128(value); - } + public void WriteULEB128(ulong value) + { + Stream.WriteULEB128(value); + } + public void WriteILEB128(long value) + { + Stream.WriteILEB128(value); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfReaderWriterContext.cs b/src/LibObjectFile/Dwarf/DwarfReaderWriterContext.cs index 4d7a9bf..6ef68cc 100644 --- a/src/LibObjectFile/Dwarf/DwarfReaderWriterContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfReaderWriterContext.cs @@ -4,26 +4,25 @@ using System.IO; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public abstract class DwarfReaderWriterContext { - public abstract class DwarfReaderWriterContext - { - public bool IsLittleEndian { get; set; } + public bool IsLittleEndian { get; set; } - public DwarfAddressSize AddressSize { get; set; } + public DwarfAddressSize AddressSize { get; set; } - public Stream? DebugAbbrevStream { get; set; } + public Stream? DebugAbbrevStream { get; set; } - public Stream? DebugStringStream { get; set; } + public Stream? DebugStringStream { get; set; } - public Stream? DebugAddressRangeStream { get; set; } + public Stream? DebugAddressRangeStream { get; set; } - public Stream? DebugLineStream { get; set; } + public Stream? DebugLineStream { get; set; } - public TextWriter? DebugLinePrinter { get; set; } + public TextWriter? DebugLinePrinter { get; set; } - public Stream? DebugInfoStream { get; set; } + public Stream? DebugInfoStream { get; set; } - public Stream? DebugLocationStream { get; set; } - } + public Stream? DebugLocationStream { get; set; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs b/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs index d5ddd3b..a2023be 100644 --- a/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs @@ -7,60 +7,59 @@ using System.Linq; using LibObjectFile.Elf; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public abstract class DwarfRelocatableSection : DwarfSection +{ + protected DwarfRelocatableSection() + { + Relocations = new List(); + } + + + public List Relocations { get; } +} + +public static class DwarfRelocationSectionExtensions { - public abstract class DwarfRelocatableSection : DwarfSection + public static void CopyRelocationsTo(this DwarfRelocatableSection dwarfRelocSection, DwarfElfContext elfContext, ElfRelocationTable relocTable) { - protected DwarfRelocatableSection() + if (elfContext == null) throw new ArgumentNullException(nameof(elfContext)); + if (relocTable == null) throw new ArgumentNullException(nameof(relocTable)); + + switch (elfContext.Elf.Arch.Value) { - Relocations = new List(); + case ElfArch.X86_64: + CopyRelocationsX86_64(dwarfRelocSection, elfContext, relocTable); + break; + default: + throw new NotImplementedException($"The relocation for architecture {relocTable.Parent!.Arch} is not supported/implemented."); } - - public List Relocations { get; } } - public static class DwarfRelocationSectionExtensions + private static void CopyRelocationsX86_64(DwarfRelocatableSection dwarfRelocSection, DwarfElfContext elfContext, ElfRelocationTable relocTable) { - public static void CopyRelocationsTo(this DwarfRelocatableSection dwarfRelocSection, DwarfElfContext elfContext, ElfRelocationTable relocTable) + relocTable.Entries.Clear(); + foreach (var reloc in dwarfRelocSection.Relocations) { - if (elfContext == null) throw new ArgumentNullException(nameof(elfContext)); - if (relocTable == null) throw new ArgumentNullException(nameof(relocTable)); - - switch (elfContext.Elf.Arch.Value) + var relocType = reloc.Size == DwarfAddressSize.Bit64 ? ElfRelocationType.R_X86_64_64 : ElfRelocationType.R_X86_64_32; + switch (reloc.Target) { - case ElfArch.X86_64: - CopyRelocationsX86_64(dwarfRelocSection, elfContext, relocTable); + case DwarfRelocationTarget.Code: + relocTable.Entries.Add(new ElfRelocation(reloc.Offset, relocType, (uint)elfContext.CodeSectionSymbolIndex, (long) reloc.Addend)); + break; + case DwarfRelocationTarget.DebugString: + relocTable.Entries.Add(new ElfRelocation(reloc.Offset, relocType, (uint)elfContext.StringTableSymbolIndex, (long)reloc.Addend)); + break; + case DwarfRelocationTarget.DebugAbbrev: + relocTable.Entries.Add(new ElfRelocation(reloc.Offset, relocType, (uint)elfContext.AbbreviationTableSymbolIndex, (long)reloc.Addend)); + break; + case DwarfRelocationTarget.DebugInfo: + relocTable.Entries.Add(new ElfRelocation(reloc.Offset, relocType, (uint)elfContext.InfoSectionSymbolIndex, (long)reloc.Addend)); break; default: - throw new NotImplementedException($"The relocation for architecture {relocTable.Parent!.Arch} is not supported/implemented."); - } - - } - - private static void CopyRelocationsX86_64(DwarfRelocatableSection dwarfRelocSection, DwarfElfContext elfContext, ElfRelocationTable relocTable) - { - relocTable.Entries.Clear(); - foreach (var reloc in dwarfRelocSection.Relocations) - { - var relocType = reloc.Size == DwarfAddressSize.Bit64 ? ElfRelocationType.R_X86_64_64 : ElfRelocationType.R_X86_64_32; - switch (reloc.Target) - { - case DwarfRelocationTarget.Code: - relocTable.Entries.Add(new ElfRelocation(reloc.Offset, relocType, (uint)elfContext.CodeSectionSymbolIndex, (long) reloc.Addend)); - break; - case DwarfRelocationTarget.DebugString: - relocTable.Entries.Add(new ElfRelocation(reloc.Offset, relocType, (uint)elfContext.StringTableSymbolIndex, (long)reloc.Addend)); - break; - case DwarfRelocationTarget.DebugAbbrev: - relocTable.Entries.Add(new ElfRelocation(reloc.Offset, relocType, (uint)elfContext.AbbreviationTableSymbolIndex, (long)reloc.Addend)); - break; - case DwarfRelocationTarget.DebugInfo: - relocTable.Entries.Add(new ElfRelocation(reloc.Offset, relocType, (uint)elfContext.InfoSectionSymbolIndex, (long)reloc.Addend)); - break; - default: - throw new ArgumentOutOfRangeException(); - } + throw new ArgumentOutOfRangeException(); } } } diff --git a/src/LibObjectFile/Dwarf/DwarfRelocation.cs b/src/LibObjectFile/Dwarf/DwarfRelocation.cs index 237d81c..32a2e89 100644 --- a/src/LibObjectFile/Dwarf/DwarfRelocation.cs +++ b/src/LibObjectFile/Dwarf/DwarfRelocation.cs @@ -4,30 +4,29 @@ using System.Diagnostics; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +[DebuggerDisplay("{" + nameof(ToString) + "(),nq}")] +public struct DwarfRelocation { - [DebuggerDisplay("{" + nameof(ToString) + "(),nq}")] - public struct DwarfRelocation + public DwarfRelocation(ulong offset, DwarfRelocationTarget target, DwarfAddressSize size, ulong addend) { - public DwarfRelocation(ulong offset, DwarfRelocationTarget target, DwarfAddressSize size, ulong addend) - { - Offset = offset; - Target = target; - Size = size; - Addend = addend; - } + Offset = offset; + Target = target; + Size = size; + Addend = addend; + } - public ulong Offset { get; set; } + public ulong Offset { get; set; } - public DwarfRelocationTarget Target { get; set; } + public DwarfRelocationTarget Target { get; set; } - public DwarfAddressSize Size { get; set; } + public DwarfAddressSize Size { get; set; } - public ulong Addend { get; set; } + public ulong Addend { get; set; } - public override string ToString() - { - return $"{nameof(Offset)}: {Offset}, {nameof(Target)}: {Target}, {nameof(Size)}: {Size}, {nameof(Addend)}: {Addend}"; - } + public override string ToString() + { + return $"{nameof(Offset)}: {Offset}, {nameof(Target)}: {Target}, {nameof(Size)}: {Size}, {nameof(Addend)}: {Addend}"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfRelocationTarget.cs b/src/LibObjectFile/Dwarf/DwarfRelocationTarget.cs index bcec19f..5119d78 100644 --- a/src/LibObjectFile/Dwarf/DwarfRelocationTarget.cs +++ b/src/LibObjectFile/Dwarf/DwarfRelocationTarget.cs @@ -2,16 +2,15 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public enum DwarfRelocationTarget { - public enum DwarfRelocationTarget - { - Code, + Code, - DebugString, + DebugString, - DebugAbbrev, + DebugAbbrev, - DebugInfo, - } + DebugInfo, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfSection.cs b/src/LibObjectFile/Dwarf/DwarfSection.cs index 68a9e43..526a453 100644 --- a/src/LibObjectFile/Dwarf/DwarfSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfSection.cs @@ -5,27 +5,26 @@ using System; using System.Diagnostics; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public abstract class DwarfSection : DwarfContainer { - public abstract class DwarfSection : DwarfContainer + protected override void ValidateParent(ObjectFileElement parent) { - protected override void ValidateParent(ObjectFileNodeBase parent) + if (!(parent is DwarfFile)) { - if (!(parent is DwarfFile)) - { - throw new ArgumentException($"Parent must inherit from type {nameof(DwarfFile)}"); - } + throw new ArgumentException($"Parent must inherit from type {nameof(DwarfFile)}"); } + } - /// - /// Gets the containing . Might be null if this section or segment - /// does not belong to an existing . - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public new DwarfFile? Parent - { - get => (DwarfFile?)base.Parent; - internal set => base.Parent = value; - } + /// + /// Gets the containing . Might be null if this section or segment + /// does not belong to an existing . + /// + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public new DwarfFile? Parent + { + get => (DwarfFile?)base.Parent; + internal set => base.Parent = value; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfSectionLink.cs b/src/LibObjectFile/Dwarf/DwarfSectionLink.cs index 2bcfa17..1213751 100644 --- a/src/LibObjectFile/Dwarf/DwarfSectionLink.cs +++ b/src/LibObjectFile/Dwarf/DwarfSectionLink.cs @@ -4,45 +4,44 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public struct DwarfSectionLink : IEquatable { - public struct DwarfSectionLink : IEquatable + public DwarfSectionLink(ulong offset) { - public DwarfSectionLink(ulong offset) - { - Offset = offset; - } + Offset = offset; + } - public readonly ulong Offset; - - public override string ToString() - { - return $"SectionLink {nameof(Offset)}: 0x{Offset:x}"; - } - - public bool Equals(DwarfSectionLink other) - { - return Offset == other.Offset; - } - - public override bool Equals(object? obj) - { - return obj is DwarfSectionLink other && Equals(other); - } - - public override int GetHashCode() - { - return Offset.GetHashCode(); - } - - public static bool operator ==(DwarfSectionLink left, DwarfSectionLink right) - { - return left.Equals(right); - } - - public static bool operator !=(DwarfSectionLink left, DwarfSectionLink right) - { - return !left.Equals(right); - } + public readonly ulong Offset; + + public override string ToString() + { + return $"SectionLink {nameof(Offset)}: 0x{Offset:x}"; + } + + public bool Equals(DwarfSectionLink other) + { + return Offset == other.Offset; + } + + public override bool Equals(object? obj) + { + return obj is DwarfSectionLink other && Equals(other); + } + + public override int GetHashCode() + { + return Offset.GetHashCode(); + } + + public static bool operator ==(DwarfSectionLink left, DwarfSectionLink right) + { + return left.Equals(right); + } + + public static bool operator !=(DwarfSectionLink left, DwarfSectionLink right) + { + return !left.Equals(right); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfStreamExtensions.cs b/src/LibObjectFile/Dwarf/DwarfStreamExtensions.cs index 5bbde35..e08b357 100644 --- a/src/LibObjectFile/Dwarf/DwarfStreamExtensions.cs +++ b/src/LibObjectFile/Dwarf/DwarfStreamExtensions.cs @@ -5,93 +5,92 @@ using System; using System.IO; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public static class DwarfStreamExtensions { - public static class DwarfStreamExtensions + public static ulong ReadULEB128(this Stream stream) { - public static ulong ReadULEB128(this Stream stream) + ulong value = 0; + int shift = 0; + while (true) { - ulong value = 0; - int shift = 0; - while (true) + var b = stream.ReadU8(); + value = ((ulong)(b & 0x7f) << shift) | value; + if ((b & 0x80) == 0) { - var b = stream.ReadU8(); - value = ((ulong)(b & 0x7f) << shift) | value; - if ((b & 0x80) == 0) - { - break; - } - shift += 7; + break; } - return value; + shift += 7; } + return value; + } - public static void WriteULEB128(this Stream stream, ulong value) + public static void WriteULEB128(this Stream stream, ulong value) + { + do { - do - { - var b = (byte)(value & 0x7f); - value >>= 7; - if (value != 0) - b |= 0x80; - stream.WriteU8(b); - } while (value != 0); - } + var b = (byte)(value & 0x7f); + value >>= 7; + if (value != 0) + b |= 0x80; + stream.WriteU8(b); + } while (value != 0); + } - public static void WriteILEB128(this Stream stream, long value) + public static void WriteILEB128(this Stream stream, long value) + { + bool cont = true; + while (cont) { - bool cont = true; - while (cont) + var b = (byte)((byte)value & 0x7f); + value >>= 7; + bool isSignBitSet = (b & 0x40) != 0; + if ((value == 0 && !isSignBitSet) || (value == -1 && isSignBitSet)) { - var b = (byte)((byte)value & 0x7f); - value >>= 7; - bool isSignBitSet = (b & 0x40) != 0; - if ((value == 0 && !isSignBitSet) || (value == -1 && isSignBitSet)) - { - cont = false; - } - else - { - b |= 0x80; - } - stream.WriteU8(b); + cont = false; } + else + { + b |= 0x80; + } + stream.WriteU8(b); } + } - public static uint ReadULEB128AsU32(this Stream stream) - { - var offset = stream.Position; - var value = stream.ReadULEB128(); - if (value >= uint.MaxValue) throw new InvalidOperationException($"The LEB128 0x{value:x16} read from stream at offset {offset} is out of range of uint >= {uint.MaxValue}"); - return (uint)value; - } + public static uint ReadULEB128AsU32(this Stream stream) + { + var offset = stream.Position; + var value = stream.ReadULEB128(); + if (value >= uint.MaxValue) throw new InvalidOperationException($"The LEB128 0x{value:x16} read from stream at offset {offset} is out of range of uint >= {uint.MaxValue}"); + return (uint)value; + } - public static int ReadLEB128AsI32(this Stream stream) - { - var offset = stream.Position; - var value = stream.ReadULEB128(); - if (value >= int.MaxValue) throw new InvalidOperationException($"The LEB128 0x{value:x16} read from stream at offset {offset} is out of range of int >= {int.MaxValue}"); - return (int)value; - } + public static int ReadLEB128AsI32(this Stream stream) + { + var offset = stream.Position; + var value = stream.ReadULEB128(); + if (value >= int.MaxValue) throw new InvalidOperationException($"The LEB128 0x{value:x16} read from stream at offset {offset} is out of range of int >= {int.MaxValue}"); + return (int)value; + } - public static long ReadSignedLEB128(this Stream stream) + public static long ReadSignedLEB128(this Stream stream) + { + long value = 0; + int shift = 0; + byte b = 0; + do { - long value = 0; - int shift = 0; - byte b = 0; - do - { - b = stream.ReadU8(); - value |= ((long) (b & 0x7f) << shift); - shift += 7; - } while ((b & 0x80) != 0); - - if (shift < 64 && (b & 0x40) != 0) - { - value |= (long)(~0UL << shift); - } + b = stream.ReadU8(); + value |= ((long) (b & 0x7f) << shift); + shift += 7; + } while ((b & 0x80) != 0); - return value; + if (shift < 64 && (b & 0x40) != 0) + { + value |= (long)(~0UL << shift); } + + return value; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfStringTable.cs b/src/LibObjectFile/Dwarf/DwarfStringTable.cs index 2ff4a1d..023ab21 100644 --- a/src/LibObjectFile/Dwarf/DwarfStringTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfStringTable.cs @@ -7,84 +7,83 @@ using System.Diagnostics; using System.IO; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfStringTable : DwarfSection { - public class DwarfStringTable : DwarfSection - { - private readonly Dictionary _stringToOffset; - private readonly Dictionary _offsetToString; - private Stream _stream; + private readonly Dictionary _stringToOffset; + private readonly Dictionary _offsetToString; + private Stream _stream; - public DwarfStringTable() : this(new MemoryStream()) - { - } + public DwarfStringTable() : this(new MemoryStream()) + { + } - public DwarfStringTable(Stream stream) - { - ArgumentNullException.ThrowIfNull(stream); - _stream = stream; - _stringToOffset = new Dictionary(); - _offsetToString = new Dictionary(); - } + public DwarfStringTable(Stream stream) + { + ArgumentNullException.ThrowIfNull(stream); + _stream = stream; + _stringToOffset = new Dictionary(); + _offsetToString = new Dictionary(); + } - public Stream Stream - { - get => _stream; - set => _stream = value ?? throw new ArgumentNullException(nameof(value)); - } + public Stream Stream + { + get => _stream; + set => _stream = value ?? throw new ArgumentNullException(nameof(value)); + } - public string GetStringFromOffset(ulong offset) + public string GetStringFromOffset(ulong offset) + { + if (_offsetToString.TryGetValue(offset, out var text)) { - if (_offsetToString.TryGetValue(offset, out var text)) - { - return text; - } - - Stream.Position = (long) offset; - text = Stream.ReadStringUTF8NullTerminated(); - _offsetToString[offset] = text; - _stringToOffset[text] = offset; return text; } - public bool Contains(string? text) - { - if (text == null) return false; - return _stringToOffset.ContainsKey(text); - } + Stream.Position = (long) offset; + text = Stream.ReadStringUTF8NullTerminated(); + _offsetToString[offset] = text; + _stringToOffset[text] = offset; + return text; + } - public ulong GetOrCreateString(string? text) - { - if (text == null) return 0; + public bool Contains(string? text) + { + if (text == null) return false; + return _stringToOffset.ContainsKey(text); + } - ulong offset; - if (_stringToOffset.TryGetValue(text, out offset)) - { - return offset; - } + public ulong GetOrCreateString(string? text) + { + if (text == null) return 0; - Stream.Position = Stream.Length; - offset = (ulong)Stream.Position; - Stream.WriteStringUTF8NullTerminated(text); - _offsetToString[offset] = text; - _stringToOffset[text] = offset; + ulong offset; + if (_stringToOffset.TryGetValue(text, out offset)) + { return offset; } - protected override void Read(DwarfReader reader) - { - Stream = reader.ReadAsStream((ulong) reader.Stream.Length); - Size = reader.Position - Position; - } + Stream.Position = Stream.Length; + offset = (ulong)Stream.Position; + Stream.WriteStringUTF8NullTerminated(text); + _offsetToString[offset] = text; + _stringToOffset[text] = offset; + return offset; + } - protected override void Write(DwarfWriter writer) - { - writer.Write(Stream); - } + public override void Read(DwarfReader reader) + { + Stream = reader.ReadAsStream((ulong) reader.Stream.Length); + Size = reader.Position - Position; + } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - Size = (ulong?)(Stream?.Length) ?? 0UL; - } + public override void Write(DwarfWriter writer) + { + writer.Write(Stream); + } + + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + Size = (ulong?)(Stream?.Length) ?? 0UL; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfTagEx.cs b/src/LibObjectFile/Dwarf/DwarfTagEx.cs index 69a6a83..6597074 100644 --- a/src/LibObjectFile/Dwarf/DwarfTagEx.cs +++ b/src/LibObjectFile/Dwarf/DwarfTagEx.cs @@ -4,59 +4,58 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +/// +/// Defines the tag of an . +/// +public readonly partial struct DwarfTagEx : IEquatable { - /// - /// Defines the tag of an . - /// - public readonly partial struct DwarfTagEx : IEquatable - { - public DwarfTagEx(uint value) - { - Value = (DwarfTag)value; - } - - public DwarfTagEx(DwarfTag value) - { - Value = value; - } - - public readonly DwarfTag Value; + public DwarfTagEx(uint value) + { + Value = (DwarfTag)value; + } + + public DwarfTagEx(DwarfTag value) + { + Value = value; + } + + public readonly DwarfTag Value; - public bool Equals(DwarfTagEx other) - { - return Value == other.Value; - } + public bool Equals(DwarfTagEx other) + { + return Value == other.Value; + } - public override bool Equals(object? obj) - { - return obj is DwarfTagEx other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is DwarfTagEx other && Equals(other); + } - public override int GetHashCode() - { - return (int) Value; - } + public override int GetHashCode() + { + return (int) Value; + } - public static bool operator ==(DwarfTagEx left, DwarfTagEx right) - { - return left.Equals(right); - } + public static bool operator ==(DwarfTagEx left, DwarfTagEx right) + { + return left.Equals(right); + } - public static bool operator !=(DwarfTagEx left, DwarfTagEx right) - { - return !left.Equals(right); - } + public static bool operator !=(DwarfTagEx left, DwarfTagEx right) + { + return !left.Equals(right); + } - public override string ToString() - { - return ToStringInternal() ?? $"Unknown {nameof(DwarfTagEx)} (0x{Value:X4})"; - } + public override string ToString() + { + return ToStringInternal() ?? $"Unknown {nameof(DwarfTagEx)} (0x{Value:X4})"; + } - public static explicit operator uint(DwarfTagEx tag) => (uint)tag.Value; + public static explicit operator uint(DwarfTagEx tag) => (uint)tag.Value; - public static implicit operator DwarfTagEx(DwarfTag tag) => new DwarfTagEx(tag); + public static implicit operator DwarfTagEx(DwarfTag tag) => new DwarfTagEx(tag); - public static implicit operator DwarfTag(DwarfTagEx tag) => tag.Value; - } + public static implicit operator DwarfTag(DwarfTagEx tag) => tag.Value; } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfUnit.cs b/src/LibObjectFile/Dwarf/DwarfUnit.cs index 31f9331..42fafac 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnit.cs @@ -5,237 +5,235 @@ using System; using System.Diagnostics; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +/// +/// Base class for a Dwarf Unit. +/// +public abstract class DwarfUnit : DwarfContainer { + private DwarfDIE? _root; + /// - /// Base class for a Dwarf Unit. + /// Gets or sets the encoding of this unit. /// - public abstract class DwarfUnit : DwarfContainer - { - private DwarfDIE? _root; - - /// - /// Gets or sets the encoding of this unit. - /// - public bool Is64BitEncoding { get; set; } - - /// - /// Gets or sets the address size used by this unit. - /// - public DwarfAddressSize AddressSize { get; set; } - - /// - /// Gets or sets the version of this unit. - /// - public ushort Version { get; set; } - - /// - /// Gets or sets the kind of this unit. - /// - public DwarfUnitKindEx Kind { get; set; } - - /// - /// Gets the abbreviation offset, only valid once the layout has been calculated through . - /// - public ulong DebugAbbreviationOffset { get; internal set; } - - /// - /// Gets the unit length, only valid once the layout has been calculated through . - /// - public ulong UnitLength { get; internal set; } - - /// - /// Gets or sets the root of this compilation unit. - /// - public DwarfDIE? Root - { - get => _root; - set => AttachNullableChild(this, value, ref _root); - } + public bool Is64BitEncoding { get; set; } - /// - /// Gets the abbreviation associated with the . - /// - /// - /// This abbreviation is automatically setup after reading or after updating the layout through . - /// - public DwarfAbbreviation? Abbreviation { get; internal set; } + /// + /// Gets or sets the address size used by this unit. + /// + public DwarfAddressSize AddressSize { get; set; } - public override void Verify(DiagnosticBag diagnostics) - { - base.Verify(diagnostics); + /// + /// Gets or sets the version of this unit. + /// + public ushort Version { get; set; } - if (Version < 2 || Version > 5) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_VersionNotSupported, $"Version .debug_info {Version} not supported"); - } + /// + /// Gets or sets the kind of this unit. + /// + public DwarfUnitKindEx Kind { get; set; } - if (AddressSize == DwarfAddressSize.None) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidAddressSize, $"Address size for .debug_info cannot be None/0"); - } + /// + /// Gets the abbreviation offset, only valid once the layout has been calculated through . + /// + public ulong DebugAbbreviationOffset { get; internal set; } + + /// + /// Gets the unit length, only valid once the layout has been calculated through . + /// + public ulong UnitLength { get; internal set; } - Root?.Verify(diagnostics); + /// + /// Gets or sets the root of this compilation unit. + /// + public DwarfDIE? Root + { + get => _root; + set => AttachNullableChild(this, value, ref _root); + } + + /// + /// Gets the abbreviation associated with the . + /// + /// + /// This abbreviation is automatically setup after reading or after updating the layout through . + /// + public DwarfAbbreviation? Abbreviation { get; internal set; } + + public override void Verify(DwarfVerifyContext context) + { + var diagnostics = context.Diagnostics; + if (Version < 2 || Version > 5) + { + diagnostics.Error(DiagnosticId.DWARF_ERR_VersionNotSupported, $"Version .debug_info {Version} not supported"); } - protected override void ValidateParent(ObjectFileNodeBase parent) + if (AddressSize == DwarfAddressSize.None) { - if (!(parent is DwarfSection)) - { - throw new ArgumentException($"Parent must inherit from type {nameof(DwarfSection)}"); - } + diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidAddressSize, $"Address size for .debug_info cannot be None/0"); } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) + Root?.Verify(context); + } + + protected override void ValidateParent(ObjectFileElement parent) + { + if (!(parent is DwarfSection)) { - var offset = this.Position; + throw new ArgumentException($"Parent must inherit from type {nameof(DwarfSection)}"); + } + } - // 1. unit_length - offset += DwarfHelper.SizeOfUnitLength(Is64BitEncoding); + public override void UpdateLayout(DwarfLayoutContext layoutContext) + { + var offset = this.Position; - var offsetAfterUnitLength = offset; + // 1. unit_length + offset += DwarfHelper.SizeOfUnitLength(Is64BitEncoding); - // 2. version (uhalf) - offset += sizeof(ushort); // WriteU16(unit.Version); + var offsetAfterUnitLength = offset; - if (Version >= 5) - { - // 3. unit_type (ubyte) - offset += 1; // WriteU8(unit.Kind.Value); - } + // 2. version (uhalf) + offset += sizeof(ushort); // WriteU16(unit.Version); - // Update the layout specific to the Unit instance - offset += GetLayoutHeaderSize(); + if (Version >= 5) + { + // 3. unit_type (ubyte) + offset += 1; // WriteU8(unit.Kind.Value); + } - Abbreviation = null; + // Update the layout specific to the Unit instance + offset += GetLayoutHeaderSize(); - // Compute the full layout of all DIE and attributes (once abbreviation are calculated) - if (Root != null) - { - // Before updating the layout, we need to compute the abbreviation - Abbreviation = new DwarfAbbreviation(); - layoutContext.File.AbbreviationTable.AddAbbreviation(Abbreviation); + Abbreviation = null; + + // Compute the full layout of all DIE and attributes (once abbreviation are calculated) + if (Root != null) + { + // Before updating the layout, we need to compute the abbreviation + Abbreviation = new DwarfAbbreviation(); + layoutContext.File.AbbreviationTable.AddAbbreviation(Abbreviation); - Root.UpdateAbbreviationItem(layoutContext); + Root.UpdateAbbreviationItem(layoutContext); - DebugAbbreviationOffset = Abbreviation.Position; + DebugAbbreviationOffset = Abbreviation.Position; - Root.Position = offset; - Root.UpdateLayoutInternal(layoutContext); - offset += Root.Size; - } - - Size = offset - Position; - UnitLength = offset - offsetAfterUnitLength; + Root.Position = offset; + Root.UpdateLayout(layoutContext); + offset += Root.Size; } - protected abstract ulong GetLayoutHeaderSize(); + Size = offset - Position; + UnitLength = offset - offsetAfterUnitLength; + } - protected abstract void ReadHeader(DwarfReader reader); + protected abstract ulong GetLayoutHeaderSize(); - protected abstract void WriteHeader(DwarfWriter writer); + protected abstract void ReadHeader(DwarfReader reader); - protected override void Read(DwarfReader reader) - { - reader.CurrentUnit = this; + protected abstract void WriteHeader(DwarfWriter writer); + + public override void Read(DwarfReader reader) + { + reader.CurrentUnit = this; - foreach (var abbreviation in reader.File.AbbreviationTable.Abbreviations) + foreach (var abbreviation in reader.File.AbbreviationTable.Abbreviations) + { + if (abbreviation.Position == DebugAbbreviationOffset) { - if (abbreviation.Position == DebugAbbreviationOffset) - { - Abbreviation = abbreviation; - break; - } + Abbreviation = abbreviation; + break; } - - Root = DwarfDIE.ReadInstance(reader); - - reader.ResolveAttributeReferenceWithinCompilationUnit(); - - Size = reader.Position - Position; } - internal static DwarfUnit? ReadInstance(DwarfReader reader, out ulong offsetEndOfUnit) - { - var startOffset = reader.Position; + Root = DwarfDIE.ReadInstance(reader); - DwarfUnit? unit = null; + reader.ResolveAttributeReferenceWithinCompilationUnit(); - // 1. unit_length - var unit_length = reader.ReadUnitLength(); + Size = reader.Position - Position; + } - offsetEndOfUnit = (ulong)reader.Position + unit_length; + internal static DwarfUnit? ReadInstance(DwarfReader reader, out ulong offsetEndOfUnit) + { + var startOffset = reader.Position; - // 2. version (uhalf) - var version = reader.ReadU16(); + DwarfUnit? unit = null; - DwarfUnitKindEx unitKind = reader.DefaultUnitKind; + // 1. unit_length + var unit_length = reader.ReadUnitLength(); - if (version <= 2 || version > 5) - { - reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_VersionNotSupported, $"Version {version} is not supported"); - return null; - } + offsetEndOfUnit = (ulong)reader.Position + unit_length; - if (version >= 5) - { - // 3. unit_type (ubyte) - unitKind = new DwarfUnitKindEx(reader.ReadU8()); - } + // 2. version (uhalf) + var version = reader.ReadU16(); - switch (unitKind.Value) - { - case DwarfUnitKind.Compile: - case DwarfUnitKind.Partial: - unit = new DwarfCompilationUnit(); - break; - - default: - reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_UnsupportedUnitType, $"Unit Type {unitKind} is not supported"); - return null; - } + DwarfUnitKindEx unitKind = reader.DefaultUnitKind; - unit.UnitLength = unit_length; - unit.Kind = unitKind; - unit.Is64BitEncoding = reader.Is64BitEncoding; - unit.Position = startOffset; - unit.Version = version; + if (version <= 2 || version > 5) + { + reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_VersionNotSupported, $"Version {version} is not supported"); + return null; + } - unit.ReadHeader(reader); + if (version >= 5) + { + // 3. unit_type (ubyte) + unitKind = new DwarfUnitKindEx(reader.ReadU8()); + } - unit.ReadInternal(reader); + switch (unitKind.Value) + { + case DwarfUnitKind.Compile: + case DwarfUnitKind.Partial: + unit = new DwarfCompilationUnit(); + break; - return unit; + default: + reader.Diagnostics.Error(DiagnosticId.DWARF_ERR_UnsupportedUnitType, $"Unit Type {unitKind} is not supported"); + return null; } - protected override void Write(DwarfWriter writer) - { - var startOffset = writer.Position; - Debug.Assert(Position == writer.Position); + unit.UnitLength = unit_length; + unit.Kind = unitKind; + unit.Is64BitEncoding = reader.Is64BitEncoding; + unit.Position = startOffset; + unit.Version = version; - // 1. unit_length - Is64BitEncoding = Is64BitEncoding; - writer.WriteUnitLength(UnitLength); + unit.ReadHeader(reader); - var offsetAfterUnitLength = writer.Position; + unit.Read(reader); - // 2. version (uhalf) - writer.WriteU16(Version); + return unit; + } - if (Version >= 5) - { - // 3. unit_type (ubyte) - writer.WriteU8((byte)Kind.Value); - } + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; + Debug.Assert(Position == writer.Position); - WriteHeader(writer); - writer.AddressSize = AddressSize; + // 1. unit_length + Is64BitEncoding = Is64BitEncoding; + writer.WriteUnitLength(UnitLength); - Root?.WriteInternal(writer); - // TODO: check size of unit length + var offsetAfterUnitLength = writer.Position; - Debug.Assert(Size == writer.Position - startOffset); - Debug.Assert(UnitLength == writer.Position - offsetAfterUnitLength); + // 2. version (uhalf) + writer.WriteU16(Version); + + if (Version >= 5) + { + // 3. unit_type (ubyte) + writer.WriteU8((byte)Kind.Value); } + + WriteHeader(writer); + writer.AddressSize = AddressSize; + + Root?.Write(writer); + // TODO: check size of unit length + + Debug.Assert(Size == writer.Position - startOffset); + Debug.Assert(UnitLength == writer.Position - offsetAfterUnitLength); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfUnitKind.cs b/src/LibObjectFile/Dwarf/DwarfUnitKind.cs index 57cddc6..47b9281 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnitKind.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnitKind.cs @@ -4,60 +4,59 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public readonly partial struct DwarfUnitKindEx : IEquatable { - public readonly partial struct DwarfUnitKindEx : IEquatable + public DwarfUnitKindEx(byte value) { - public DwarfUnitKindEx(byte value) - { - Value = (DwarfUnitKind)value; - } + Value = (DwarfUnitKind)value; + } - public DwarfUnitKindEx(DwarfUnitKind value) - { - Value = value; - } + public DwarfUnitKindEx(DwarfUnitKind value) + { + Value = value; + } - public readonly DwarfUnitKind Value; + public readonly DwarfUnitKind Value; - public override string ToString() + public override string ToString() + { + if ((byte)Value >= DwarfNative.DW_UT_lo_user) { - if ((byte)Value >= DwarfNative.DW_UT_lo_user) - { - return $"User {nameof(DwarfUnitKindEx)} (0x{Value:x2})"; - } - return ToStringInternal() ?? $"Unknown {nameof(DwarfUnitKindEx)} (0x{Value:x2})"; + return $"User {nameof(DwarfUnitKindEx)} (0x{Value:x2})"; } + return ToStringInternal() ?? $"Unknown {nameof(DwarfUnitKindEx)} (0x{Value:x2})"; + } - public bool Equals(DwarfUnitKindEx other) - { - return Value == other.Value; - } + public bool Equals(DwarfUnitKindEx other) + { + return Value == other.Value; + } - public override bool Equals(object? obj) - { - return obj is DwarfUnitKindEx other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is DwarfUnitKindEx other && Equals(other); + } - public override int GetHashCode() - { - return Value.GetHashCode(); - } + public override int GetHashCode() + { + return Value.GetHashCode(); + } - public static bool operator ==(DwarfUnitKindEx left, DwarfUnitKindEx right) - { - return left.Equals(right); - } + public static bool operator ==(DwarfUnitKindEx left, DwarfUnitKindEx right) + { + return left.Equals(right); + } - public static bool operator !=(DwarfUnitKindEx left, DwarfUnitKindEx right) - { - return !left.Equals(right); - } + public static bool operator !=(DwarfUnitKindEx left, DwarfUnitKindEx right) + { + return !left.Equals(right); + } - public static explicit operator uint(DwarfUnitKindEx kind) => (uint)kind.Value; + public static explicit operator uint(DwarfUnitKindEx kind) => (uint)kind.Value; - public static implicit operator DwarfUnitKindEx(DwarfUnitKind kind) => new DwarfUnitKindEx(kind); + public static implicit operator DwarfUnitKindEx(DwarfUnitKind kind) => new DwarfUnitKindEx(kind); - public static implicit operator DwarfUnitKind(DwarfUnitKindEx kind) => kind.Value; - } + public static implicit operator DwarfUnitKind(DwarfUnitKindEx kind) => kind.Value; } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfVirtuality.cs b/src/LibObjectFile/Dwarf/DwarfVirtuality.cs index 167b04a..b83e7d2 100644 --- a/src/LibObjectFile/Dwarf/DwarfVirtuality.cs +++ b/src/LibObjectFile/Dwarf/DwarfVirtuality.cs @@ -2,14 +2,13 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public enum DwarfVirtuality : byte { - public enum DwarfVirtuality : byte - { - None = DwarfNative.DW_VIRTUALITY_none, + None = DwarfNative.DW_VIRTUALITY_none, - Virtual = DwarfNative.DW_VIRTUALITY_virtual, + Virtual = DwarfNative.DW_VIRTUALITY_virtual, - PureVirtual = DwarfNative.DW_VIRTUALITY_pure_virtual, - } + PureVirtual = DwarfNative.DW_VIRTUALITY_pure_virtual, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfVisibility.cs b/src/LibObjectFile/Dwarf/DwarfVisibility.cs index ea7e430..5d69d40 100644 --- a/src/LibObjectFile/Dwarf/DwarfVisibility.cs +++ b/src/LibObjectFile/Dwarf/DwarfVisibility.cs @@ -2,14 +2,13 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public enum DwarfVisibility : byte { - public enum DwarfVisibility : byte - { - Local = DwarfNative.DW_VIS_local, + Local = DwarfNative.DW_VIS_local, - Exported = DwarfNative.DW_VIS_exported, + Exported = DwarfNative.DW_VIS_exported, - Qualified = DwarfNative.DW_VIS_qualified, - } + Qualified = DwarfNative.DW_VIS_qualified, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfWriter.cs b/src/LibObjectFile/Dwarf/DwarfWriter.cs index ee8d8fa..ba5889f 100644 --- a/src/LibObjectFile/Dwarf/DwarfWriter.cs +++ b/src/LibObjectFile/Dwarf/DwarfWriter.cs @@ -4,42 +4,41 @@ using System; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public sealed class DwarfWriter : DwarfReaderWriter { - public sealed class DwarfWriter : DwarfReaderWriter + internal DwarfWriter(DwarfFile file, bool isLittleEndian, DiagnosticBag diagnostics) : base(file, diagnostics) { - internal DwarfWriter(DwarfFile file, bool isLittleEndian, DiagnosticBag diagnostics) : base(file, diagnostics) - { - IsLittleEndian = isLittleEndian; - } + IsLittleEndian = isLittleEndian; + } - public override bool IsReadOnly => false; + public override bool IsReadOnly => false; - public bool EnableRelocation { get; internal set; } + public bool EnableRelocation { get; internal set; } - public void RecordRelocation(DwarfRelocationTarget target, DwarfAddressSize addressSize, ulong address) + public void RecordRelocation(DwarfRelocationTarget target, DwarfAddressSize addressSize, ulong address) + { + if (CurrentSection is DwarfRelocatableSection relocSection) { - if (CurrentSection is DwarfRelocatableSection relocSection) - { - relocSection.Relocations.Add(new DwarfRelocation(Position, target, addressSize, address)); + relocSection.Relocations.Add(new DwarfRelocation(Position, target, addressSize, address)); - } - else - { - throw new InvalidOperationException($"Invalid {nameof(CurrentSection)} in {nameof(DwarfWriter)}. It must be a {nameof(DwarfRelocatableSection)}."); - } } + else + { + throw new InvalidOperationException($"Invalid {nameof(CurrentSection)} in {nameof(DwarfWriter)}. It must be a {nameof(DwarfRelocatableSection)}."); + } + } - public void WriteAddress(DwarfRelocationTarget target, ulong address) + public void WriteAddress(DwarfRelocationTarget target, ulong address) + { + if (EnableRelocation) { - if (EnableRelocation) - { - RecordRelocation(target, AddressSize, address); - // If the relocation is recorded, we write 0 as an address - address = 0; - } - WriteUInt(address); + RecordRelocation(target, AddressSize, address); + // If the relocation is recorded, we write 0 as an address + address = 0; } + WriteUInt(address); } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfWriterContext.cs b/src/LibObjectFile/Dwarf/DwarfWriterContext.cs index 577293a..c28730f 100644 --- a/src/LibObjectFile/Dwarf/DwarfWriterContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfWriterContext.cs @@ -5,22 +5,21 @@ using System; using LibObjectFile.Elf; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfWriterContext : DwarfReaderWriterContext { - public class DwarfWriterContext : DwarfReaderWriterContext + public DwarfWriterContext() : this(new DwarfLayoutConfig()) { - public DwarfWriterContext() : this(new DwarfLayoutConfig()) - { - } + } - public DwarfWriterContext(DwarfLayoutConfig layoutConfig) - { - LayoutConfig = layoutConfig ?? throw new ArgumentNullException(nameof(layoutConfig)); - EnableRelocation = true; - } + public DwarfWriterContext(DwarfLayoutConfig layoutConfig) + { + LayoutConfig = layoutConfig ?? throw new ArgumentNullException(nameof(layoutConfig)); + EnableRelocation = true; + } - public DwarfLayoutConfig LayoutConfig { get; } + public DwarfLayoutConfig LayoutConfig { get; } - public bool EnableRelocation { get; set; } - } + public bool EnableRelocation { get; set; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfArch.cs b/src/LibObjectFile/Elf/ElfArch.cs index 5478ed7..6231687 100644 --- a/src/LibObjectFile/Elf/ElfArch.cs +++ b/src/LibObjectFile/Elf/ElfArch.cs @@ -4,64 +4,63 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines a machine architecture. +/// This is the value seen in or +/// as well as the various machine defines (e.g ). +/// +public readonly partial struct ElfArchEx : IEquatable { - /// - /// Defines a machine architecture. - /// This is the value seen in or - /// as well as the various machine defines (e.g ). - /// - public readonly partial struct ElfArchEx : IEquatable + public ElfArchEx(ushort value) { - public ElfArchEx(ushort value) - { - Value = (ElfArch)value; - } + Value = (ElfArch)value; + } - public ElfArchEx(ElfArch value) - { - Value = value; - } + public ElfArchEx(ElfArch value) + { + Value = value; + } - /// - /// Raw value. - /// - public readonly ElfArch Value; + /// + /// Raw value. + /// + public readonly ElfArch Value; - public override string ToString() - { - return ToStringInternal() ?? $"Unknown {nameof(ElfArchEx)} (0x{Value:X4})"; - } + public override string ToString() + { + return ToStringInternal() ?? $"Unknown {nameof(ElfArchEx)} (0x{Value:X4})"; + } - public bool Equals(ElfArchEx other) - { - return Value == other.Value; - } + public bool Equals(ElfArchEx other) + { + return Value == other.Value; + } - public override bool Equals(object? obj) - { - return obj is ElfArchEx other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is ElfArchEx other && Equals(other); + } - public override int GetHashCode() - { - return Value.GetHashCode(); - } + public override int GetHashCode() + { + return Value.GetHashCode(); + } - public static bool operator ==(ElfArchEx left, ElfArchEx right) - { - return left.Equals(right); - } + public static bool operator ==(ElfArchEx left, ElfArchEx right) + { + return left.Equals(right); + } - public static bool operator !=(ElfArchEx left, ElfArchEx right) - { - return !left.Equals(right); - } + public static bool operator !=(ElfArchEx left, ElfArchEx right) + { + return !left.Equals(right); + } - public static explicit operator uint(ElfArchEx arch) => (uint)arch.Value; + public static explicit operator uint(ElfArchEx arch) => (uint)arch.Value; - public static implicit operator ElfArchEx(ElfArch arch) => new ElfArchEx(arch); + public static implicit operator ElfArchEx(ElfArch arch) => new ElfArchEx(arch); - public static implicit operator ElfArch(ElfArchEx arch) => arch.Value; - } + public static implicit operator ElfArch(ElfArchEx arch) => arch.Value; } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfDecoderDirect.cs b/src/LibObjectFile/Elf/ElfDecoderDirect.cs index e5d25d9..32a7e83 100644 --- a/src/LibObjectFile/Elf/ElfDecoderDirect.cs +++ b/src/LibObjectFile/Elf/ElfDecoderDirect.cs @@ -2,101 +2,100 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A decoder for the various Elf types that doesn't change LSB/MSB ordering from the current machine. +/// +public struct ElfDecoderDirect : IElfDecoder { - /// - /// A decoder for the various Elf types that doesn't change LSB/MSB ordering from the current machine. - /// - public struct ElfDecoderDirect : IElfDecoder - { - public ushort Decode(ElfNative.Elf32_Half src) - { - return src.Value; - } - - public ushort Decode(ElfNative.Elf64_Half src) - { - return src.Value; - } - - public uint Decode(ElfNative.Elf32_Word src) - { - return src.Value; - } - - public uint Decode(ElfNative.Elf64_Word src) - { - return src.Value; - } - - public int Decode(ElfNative.Elf32_Sword src) - { - return src.Value; - } - - public int Decode(ElfNative.Elf64_Sword src) - { - return src.Value; - } - - public ulong Decode(ElfNative.Elf32_Xword src) - { - return src.Value; - } - - public long Decode(ElfNative.Elf32_Sxword src) - { - return src.Value; - } - - public ulong Decode(ElfNative.Elf64_Xword src) - { - return src.Value; - } - - public long Decode(ElfNative.Elf64_Sxword src) - { - return src.Value; - } - - public uint Decode(ElfNative.Elf32_Addr src) - { - return src.Value; - } - - public ulong Decode(ElfNative.Elf64_Addr src) - { - return src.Value; - } - - public uint Decode(ElfNative.Elf32_Off src) - { - return src.Value; - } - - public ulong Decode(ElfNative.Elf64_Off src) - { - return src.Value; - } - - public ushort Decode(ElfNative.Elf32_Section src) - { - return src.Value; - } - - public ushort Decode(ElfNative.Elf64_Section src) - { - return src.Value; - } - - public ushort Decode(ElfNative.Elf32_Versym src) - { - return src.Value; - } - - public ushort Decode(ElfNative.Elf64_Versym src) - { - return src.Value; - } + public ushort Decode(ElfNative.Elf32_Half src) + { + return src.Value; + } + + public ushort Decode(ElfNative.Elf64_Half src) + { + return src.Value; + } + + public uint Decode(ElfNative.Elf32_Word src) + { + return src.Value; + } + + public uint Decode(ElfNative.Elf64_Word src) + { + return src.Value; + } + + public int Decode(ElfNative.Elf32_Sword src) + { + return src.Value; + } + + public int Decode(ElfNative.Elf64_Sword src) + { + return src.Value; + } + + public ulong Decode(ElfNative.Elf32_Xword src) + { + return src.Value; + } + + public long Decode(ElfNative.Elf32_Sxword src) + { + return src.Value; + } + + public ulong Decode(ElfNative.Elf64_Xword src) + { + return src.Value; + } + + public long Decode(ElfNative.Elf64_Sxword src) + { + return src.Value; + } + + public uint Decode(ElfNative.Elf32_Addr src) + { + return src.Value; + } + + public ulong Decode(ElfNative.Elf64_Addr src) + { + return src.Value; + } + + public uint Decode(ElfNative.Elf32_Off src) + { + return src.Value; + } + + public ulong Decode(ElfNative.Elf64_Off src) + { + return src.Value; + } + + public ushort Decode(ElfNative.Elf32_Section src) + { + return src.Value; + } + + public ushort Decode(ElfNative.Elf64_Section src) + { + return src.Value; + } + + public ushort Decode(ElfNative.Elf32_Versym src) + { + return src.Value; + } + + public ushort Decode(ElfNative.Elf64_Versym src) + { + return src.Value; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfDecoderSwap.cs b/src/LibObjectFile/Elf/ElfDecoderSwap.cs index bdae152..30f74ba 100644 --- a/src/LibObjectFile/Elf/ElfDecoderSwap.cs +++ b/src/LibObjectFile/Elf/ElfDecoderSwap.cs @@ -4,101 +4,100 @@ using System.Buffers.Binary; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A decoder for the various Elf types that swap LSB/MSB ordering based on a mismatch between the current machine and file ordering. +/// +public readonly struct ElfDecoderSwap : IElfDecoder { - /// - /// A decoder for the various Elf types that swap LSB/MSB ordering based on a mismatch between the current machine and file ordering. - /// - public readonly struct ElfDecoderSwap : IElfDecoder - { - public ushort Decode(ElfNative.Elf32_Half src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public ushort Decode(ElfNative.Elf64_Half src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public uint Decode(ElfNative.Elf32_Word src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public uint Decode(ElfNative.Elf64_Word src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public int Decode(ElfNative.Elf32_Sword src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public int Decode(ElfNative.Elf64_Sword src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public ulong Decode(ElfNative.Elf32_Xword src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public long Decode(ElfNative.Elf32_Sxword src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public ulong Decode(ElfNative.Elf64_Xword src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public long Decode(ElfNative.Elf64_Sxword src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public uint Decode(ElfNative.Elf32_Addr src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public ulong Decode(ElfNative.Elf64_Addr src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public uint Decode(ElfNative.Elf32_Off src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public ulong Decode(ElfNative.Elf64_Off src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public ushort Decode(ElfNative.Elf32_Section src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public ushort Decode(ElfNative.Elf64_Section src) - { - return BinaryPrimitives.ReverseEndianness(src); - } - - public ushort Decode(ElfNative.Elf32_Versym src) - { - return BinaryPrimitives.ReverseEndianness((ElfNative.Elf32_Half)src); - } - - public ushort Decode(ElfNative.Elf64_Versym src) - { - return BinaryPrimitives.ReverseEndianness((ElfNative.Elf64_Half)src); - } + public ushort Decode(ElfNative.Elf32_Half src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public ushort Decode(ElfNative.Elf64_Half src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public uint Decode(ElfNative.Elf32_Word src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public uint Decode(ElfNative.Elf64_Word src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public int Decode(ElfNative.Elf32_Sword src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public int Decode(ElfNative.Elf64_Sword src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public ulong Decode(ElfNative.Elf32_Xword src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public long Decode(ElfNative.Elf32_Sxword src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public ulong Decode(ElfNative.Elf64_Xword src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public long Decode(ElfNative.Elf64_Sxword src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public uint Decode(ElfNative.Elf32_Addr src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public ulong Decode(ElfNative.Elf64_Addr src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public uint Decode(ElfNative.Elf32_Off src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public ulong Decode(ElfNative.Elf64_Off src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public ushort Decode(ElfNative.Elf32_Section src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public ushort Decode(ElfNative.Elf64_Section src) + { + return BinaryPrimitives.ReverseEndianness(src); + } + + public ushort Decode(ElfNative.Elf32_Versym src) + { + return BinaryPrimitives.ReverseEndianness((ElfNative.Elf32_Half)src); + } + + public ushort Decode(ElfNative.Elf64_Versym src) + { + return BinaryPrimitives.ReverseEndianness((ElfNative.Elf64_Half)src); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfEncoderDirect.cs b/src/LibObjectFile/Elf/ElfEncoderDirect.cs index 35df406..01a8307 100644 --- a/src/LibObjectFile/Elf/ElfEncoderDirect.cs +++ b/src/LibObjectFile/Elf/ElfEncoderDirect.cs @@ -2,101 +2,100 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// An encoder for the various Elf types that doesn't change LSB/MSB ordering from the current machine. +/// +internal readonly struct ElfEncoderDirect : IElfEncoder { - /// - /// An encoder for the various Elf types that doesn't change LSB/MSB ordering from the current machine. - /// - internal readonly struct ElfEncoderDirect : IElfEncoder - { - public void Encode(out ElfNative.Elf32_Half dest, ushort value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf64_Half dest, ushort value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf32_Word dest, uint value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf64_Word dest, uint value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf32_Sword dest, int value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf64_Sword dest, int value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf32_Xword dest, ulong value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf32_Sxword dest, long value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf64_Xword dest, ulong value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf64_Sxword dest, long value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf32_Addr dest, uint value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf64_Addr dest, ulong value) - { - dest = value; - } - - public void Encode(out ElfNative.Elf32_Off dest, uint offset) - { - dest = offset; - } - - public void Encode(out ElfNative.Elf64_Off dest, ulong offset) - { - dest = offset; - } - - public void Encode(out ElfNative.Elf32_Section dest, ushort index) - { - dest = index; - } - - public void Encode(out ElfNative.Elf64_Section dest, ushort index) - { - dest = index; - } - - public void Encode(out ElfNative.Elf32_Versym dest, ushort value) - { - dest = (ElfNative.Elf32_Half)value; - } - - public void Encode(out ElfNative.Elf64_Versym dest, ushort value) - { - dest = (ElfNative.Elf64_Half)value; - } + public void Encode(out ElfNative.Elf32_Half dest, ushort value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf64_Half dest, ushort value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf32_Word dest, uint value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf64_Word dest, uint value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf32_Sword dest, int value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf64_Sword dest, int value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf32_Xword dest, ulong value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf32_Sxword dest, long value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf64_Xword dest, ulong value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf64_Sxword dest, long value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf32_Addr dest, uint value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf64_Addr dest, ulong value) + { + dest = value; + } + + public void Encode(out ElfNative.Elf32_Off dest, uint offset) + { + dest = offset; + } + + public void Encode(out ElfNative.Elf64_Off dest, ulong offset) + { + dest = offset; + } + + public void Encode(out ElfNative.Elf32_Section dest, ushort index) + { + dest = index; + } + + public void Encode(out ElfNative.Elf64_Section dest, ushort index) + { + dest = index; + } + + public void Encode(out ElfNative.Elf32_Versym dest, ushort value) + { + dest = (ElfNative.Elf32_Half)value; + } + + public void Encode(out ElfNative.Elf64_Versym dest, ushort value) + { + dest = (ElfNative.Elf64_Half)value; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfEncoderSwap.cs b/src/LibObjectFile/Elf/ElfEncoderSwap.cs index 7d6f4f4..8496b8a 100644 --- a/src/LibObjectFile/Elf/ElfEncoderSwap.cs +++ b/src/LibObjectFile/Elf/ElfEncoderSwap.cs @@ -4,101 +4,100 @@ using System.Buffers.Binary; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// An encoder for the various Elf types that swap LSB/MSB ordering based on a mismatch between the current machine and file ordering. +/// +internal readonly struct ElfEncoderSwap : IElfEncoder { - /// - /// An encoder for the various Elf types that swap LSB/MSB ordering based on a mismatch between the current machine and file ordering. - /// - internal readonly struct ElfEncoderSwap : IElfEncoder - { - public void Encode(out ElfNative.Elf32_Half dest, ushort value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf64_Half dest, ushort value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf32_Word dest, uint value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf64_Word dest, uint value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf32_Sword dest, int value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf64_Sword dest, int value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf32_Xword dest, ulong value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf32_Sxword dest, long value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf64_Xword dest, ulong value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf64_Sxword dest, long value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf32_Addr dest, uint value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf64_Addr dest, ulong value) - { - dest = BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf32_Off dest, uint offset) - { - dest = BinaryPrimitives.ReverseEndianness(offset); - } - - public void Encode(out ElfNative.Elf64_Off dest, ulong offset) - { - dest = BinaryPrimitives.ReverseEndianness(offset); - } - - public void Encode(out ElfNative.Elf32_Section dest, ushort index) - { - dest = BinaryPrimitives.ReverseEndianness(index); - } - - public void Encode(out ElfNative.Elf64_Section dest, ushort index) - { - dest = BinaryPrimitives.ReverseEndianness(index); - } - - public void Encode(out ElfNative.Elf32_Versym dest, ushort value) - { - dest = (ElfNative.Elf32_Half)BinaryPrimitives.ReverseEndianness(value); - } - - public void Encode(out ElfNative.Elf64_Versym dest, ushort value) - { - dest = (ElfNative.Elf64_Half)BinaryPrimitives.ReverseEndianness(value); - } + public void Encode(out ElfNative.Elf32_Half dest, ushort value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf64_Half dest, ushort value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf32_Word dest, uint value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf64_Word dest, uint value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf32_Sword dest, int value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf64_Sword dest, int value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf32_Xword dest, ulong value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf32_Sxword dest, long value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf64_Xword dest, ulong value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf64_Sxword dest, long value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf32_Addr dest, uint value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf64_Addr dest, ulong value) + { + dest = BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf32_Off dest, uint offset) + { + dest = BinaryPrimitives.ReverseEndianness(offset); + } + + public void Encode(out ElfNative.Elf64_Off dest, ulong offset) + { + dest = BinaryPrimitives.ReverseEndianness(offset); + } + + public void Encode(out ElfNative.Elf32_Section dest, ushort index) + { + dest = BinaryPrimitives.ReverseEndianness(index); + } + + public void Encode(out ElfNative.Elf64_Section dest, ushort index) + { + dest = BinaryPrimitives.ReverseEndianness(index); + } + + public void Encode(out ElfNative.Elf32_Versym dest, ushort value) + { + dest = (ElfNative.Elf32_Half)BinaryPrimitives.ReverseEndianness(value); + } + + public void Encode(out ElfNative.Elf64_Versym dest, ushort value) + { + dest = (ElfNative.Elf64_Half)BinaryPrimitives.ReverseEndianness(value); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfEncoding.cs b/src/LibObjectFile/Elf/ElfEncoding.cs index f7997e6..34252e6 100644 --- a/src/LibObjectFile/Elf/ElfEncoding.cs +++ b/src/LibObjectFile/Elf/ElfEncoding.cs @@ -2,28 +2,27 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Encoding of an . +/// This is the value seen in the ident part of an Elf header at index +/// It is associated with , and +/// +public enum ElfEncoding : byte { /// - /// Encoding of an . - /// This is the value seen in the ident part of an Elf header at index - /// It is associated with , and + /// Invalid data encoding. Equivalent of /// - public enum ElfEncoding : byte - { - /// - /// Invalid data encoding. Equivalent of - /// - None = ElfNative.ELFDATANONE, + None = ElfNative.ELFDATANONE, - /// - /// 2's complement, little endian. Equivalent of - /// - Lsb = ElfNative.ELFDATA2LSB, + /// + /// 2's complement, little endian. Equivalent of + /// + Lsb = ElfNative.ELFDATA2LSB, - /// - /// 2's complement, big endian. Equivalent of - /// - Msb = ElfNative.ELFDATA2MSB, - } + /// + /// 2's complement, big endian. Equivalent of + /// + Msb = ElfNative.ELFDATA2MSB, } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfFileClass.cs b/src/LibObjectFile/Elf/ElfFileClass.cs index 6824a53..c6be17d 100644 --- a/src/LibObjectFile/Elf/ElfFileClass.cs +++ b/src/LibObjectFile/Elf/ElfFileClass.cs @@ -2,29 +2,27 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf -{ +namespace LibObjectFile.Elf; +/// +/// Defines the File class byte index (32bit or 64bits) of an . +/// This is the value seen in the ident part of an Elf header at index +/// It is associated with , and +/// +public enum ElfFileClass : byte +{ /// - /// Defines the File class byte index (32bit or 64bits) of an . - /// This is the value seen in the ident part of an Elf header at index - /// It is associated with , and + /// Invalid class. Equivalent of . /// - public enum ElfFileClass : byte - { - /// - /// Invalid class. Equivalent of . - /// - None = ElfNative.ELFCLASSNONE, + None = ElfNative.ELFCLASSNONE, - /// - /// 32-bit objects. Equivalent of . - /// - Is32 = ElfNative.ELFCLASS32, + /// + /// 32-bit objects. Equivalent of . + /// + Is32 = ElfNative.ELFCLASS32, - /// - /// 64-bit objects. Equivalent of . - /// - Is64 = ElfNative.ELFCLASS64, - } + /// + /// 64-bit objects. Equivalent of . + /// + Is64 = ElfNative.ELFCLASS64, } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfFilePart.cs b/src/LibObjectFile/Elf/ElfFilePart.cs index b879196..1059648 100644 --- a/src/LibObjectFile/Elf/ElfFilePart.cs +++ b/src/LibObjectFile/Elf/ElfFilePart.cs @@ -5,89 +5,88 @@ using System; using System.Diagnostics; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Internal struct used to identify which part of the file is attached to a section or not. +/// It is used while reading back an ELF file from the disk to create +/// +[DebuggerDisplay("{StartOffset,nq} - {EndOffset,nq} : {Section,nq}")] +internal readonly struct ElfFilePart : IComparable, IEquatable { /// - /// Internal struct used to identify which part of the file is attached to a section or not. - /// It is used while reading back an ELF file from the disk to create + /// Creates an instance that is not yet bound to a section for which an + /// will be created /// - [DebuggerDisplay("{StartOffset,nq} - {EndOffset,nq} : {Section,nq}")] - internal readonly struct ElfFilePart : IComparable, IEquatable + /// Start of the offset in the file + /// End of the offset in the file (inclusive) + public ElfFilePart(ulong startOffset, ulong endOffset) { - /// - /// Creates an instance that is not yet bound to a section for which an - /// will be created - /// - /// Start of the offset in the file - /// End of the offset in the file (inclusive) - public ElfFilePart(ulong startOffset, ulong endOffset) - { - StartOffset = startOffset; - EndOffset = endOffset; - Section = null; - } + StartOffset = startOffset; + EndOffset = endOffset; + Section = null; + } - /// - /// Creates an instance that is bound to a section - /// - /// A section of the file - public ElfFilePart(ElfSection section) - { - Section = section ?? throw new ArgumentNullException(nameof(section)); - Debug.Assert(section.Size > 0); - StartOffset = section.Position; - EndOffset = StartOffset + Section.Size - 1; - } + /// + /// Creates an instance that is bound to a section + /// + /// A section of the file + public ElfFilePart(ElfSection section) + { + Section = section ?? throw new ArgumentNullException(nameof(section)); + Debug.Assert(section.Size > 0); + StartOffset = section.Position; + EndOffset = StartOffset + Section.Size - 1; + } - public readonly ulong StartOffset; + public readonly ulong StartOffset; - public readonly ulong EndOffset; + public readonly ulong EndOffset; - public readonly ElfSection? Section; + public readonly ElfSection? Section; - public int CompareTo(ElfFilePart other) + public int CompareTo(ElfFilePart other) + { + if (EndOffset < other.StartOffset) { - if (EndOffset < other.StartOffset) - { - return -1; - } - - if (StartOffset > other.EndOffset) - { - return 1; - } - - // May overlap or not - return 0; + return -1; } - - public bool Equals(ElfFilePart other) + if (StartOffset > other.EndOffset) { - return StartOffset == other.StartOffset && EndOffset == other.EndOffset; + return 1; } - public override bool Equals(object? obj) - { - return obj is ElfFilePart other && Equals(other); - } + // May overlap or not + return 0; + } - public override int GetHashCode() - { - unchecked - { - return (StartOffset.GetHashCode() * 397) ^ EndOffset.GetHashCode(); - } - } - public static bool operator ==(ElfFilePart left, ElfFilePart right) - { - return left.Equals(right); - } + public bool Equals(ElfFilePart other) + { + return StartOffset == other.StartOffset && EndOffset == other.EndOffset; + } + + public override bool Equals(object? obj) + { + return obj is ElfFilePart other && Equals(other); + } - public static bool operator !=(ElfFilePart left, ElfFilePart right) + public override int GetHashCode() + { + unchecked { - return !left.Equals(right); + return (StartOffset.GetHashCode() * 397) ^ EndOffset.GetHashCode(); } } + + public static bool operator ==(ElfFilePart left, ElfFilePart right) + { + return left.Equals(right); + } + + public static bool operator !=(ElfFilePart left, ElfFilePart right) + { + return !left.Equals(right); + } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfFilePartList.cs b/src/LibObjectFile/Elf/ElfFilePartList.cs index 8625bb1..25dc1ff 100644 --- a/src/LibObjectFile/Elf/ElfFilePartList.cs +++ b/src/LibObjectFile/Elf/ElfFilePartList.cs @@ -4,79 +4,78 @@ using System.Collections.Generic; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Internal list used to keep an ordered list of based on offsets. +/// It is used to track region of the file that are actually referenced by a +/// but are not declared as a +/// +internal struct ElfFilePartList { - /// - /// Internal list used to keep an ordered list of based on offsets. - /// It is used to track region of the file that are actually referenced by a - /// but are not declared as a - /// - internal struct ElfFilePartList - { - private readonly List _parts; + private readonly List _parts; - public ElfFilePartList(int capacity) - { - _parts = new List(capacity); - } + public ElfFilePartList(int capacity) + { + _parts = new List(capacity); + } - public int Count => _parts.Count; + public int Count => _parts.Count; - public ElfFilePart this[int index] - { - get => _parts[index]; - set => _parts[index] = value; - } + public ElfFilePart this[int index] + { + get => _parts[index]; + set => _parts[index] = value; + } - public void Insert(ElfFilePart part) + public void Insert(ElfFilePart part) + { + for (int i = 0; i < _parts.Count; i++) { - for (int i = 0; i < _parts.Count; i++) + var against = _parts[i]; + var delta = part.CompareTo(against); + if (delta < 0) { - var against = _parts[i]; - var delta = part.CompareTo(against); - if (delta < 0) - { - _parts.Insert(i, part); - return; - } + _parts.Insert(i, part); + return; + } - // Don't add an overlap - if (delta == 0) - { - // do nothing - return; - } + // Don't add an overlap + if (delta == 0) + { + // do nothing + return; } - _parts.Add(part); } + _parts.Add(part); + } - public void CreateParts(ulong startOffset, ulong endOffset) + public void CreateParts(ulong startOffset, ulong endOffset) + { + var offset = startOffset; + for (int i = 0; i < _parts.Count && offset < endOffset; i++) { - var offset = startOffset; - for (int i = 0; i < _parts.Count && offset < endOffset; i++) + var part = _parts[i]; + if (offset < part.StartOffset) { - var part = _parts[i]; - if (offset < part.StartOffset) + if (endOffset < part.StartOffset) { - if (endOffset < part.StartOffset) - { - var newPart = new ElfFilePart(offset, endOffset); - _parts.Insert(i, newPart); - offset = endOffset + 1; - break; - } - - // Don't merge parts, so that we will create a single ElfInlineShadowSection per parts - _parts.Insert(i, new ElfFilePart(offset, part.StartOffset - 1)); + var newPart = new ElfFilePart(offset, endOffset); + _parts.Insert(i, newPart); + offset = endOffset + 1; + break; } - offset = part.EndOffset + 1; + // Don't merge parts, so that we will create a single ElfInlineShadowSection per parts + _parts.Insert(i, new ElfFilePart(offset, part.StartOffset - 1)); } - if (offset < endOffset) - { - _parts.Add(new ElfFilePart(offset, endOffset)); - } + offset = part.EndOffset + 1; + } + + if (offset < endOffset) + { + _parts.Add(new ElfFilePart(offset, endOffset)); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfFileType.cs b/src/LibObjectFile/Elf/ElfFileType.cs index 57e2f2b..512115f 100644 --- a/src/LibObjectFile/Elf/ElfFileType.cs +++ b/src/LibObjectFile/Elf/ElfFileType.cs @@ -2,38 +2,37 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines the file type of an . +/// This is the value seen in or +/// as well as the various machine defines (e.g ). +/// +public enum ElfFileType : ushort { /// - /// Defines the file type of an . - /// This is the value seen in or - /// as well as the various machine defines (e.g ). + /// No file type /// - public enum ElfFileType : ushort - { - /// - /// No file type - /// - None = ElfNative.ET_NONE, + None = ElfNative.ET_NONE, - /// - /// Relocatable file - /// - Relocatable = ElfNative.ET_REL, + /// + /// Relocatable file + /// + Relocatable = ElfNative.ET_REL, - /// - /// Executable file - /// - Executable = ElfNative.ET_EXEC, + /// + /// Executable file + /// + Executable = ElfNative.ET_EXEC, - /// - /// Shared object file - /// - Dynamic = ElfNative.ET_DYN, + /// + /// Shared object file + /// + Dynamic = ElfNative.ET_DYN, - /// - /// Core file - /// - Core = ElfNative.ET_CORE, - } + /// + /// Core file + /// + Core = ElfNative.ET_CORE, } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfHeaderFlags.cs b/src/LibObjectFile/Elf/ElfHeaderFlags.cs index d8ddd7a..25ce449 100644 --- a/src/LibObjectFile/Elf/ElfHeaderFlags.cs +++ b/src/LibObjectFile/Elf/ElfHeaderFlags.cs @@ -4,55 +4,54 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines the flags of an . +/// This is the value seen in or . +/// This is currently not used. +/// +public readonly struct ElfHeaderFlags : IEquatable { - /// - /// Defines the flags of an . - /// This is the value seen in or . - /// This is currently not used. - /// - public readonly struct ElfHeaderFlags : IEquatable + public ElfHeaderFlags(uint value) { - public ElfHeaderFlags(uint value) - { - Value = value; - } + Value = value; + } - public readonly uint Value; + public readonly uint Value; - public bool Equals(ElfHeaderFlags other) - { - return Value == other.Value; - } + public bool Equals(ElfHeaderFlags other) + { + return Value == other.Value; + } - public override bool Equals(object? obj) - { - return obj is ElfHeaderFlags other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is ElfHeaderFlags other && Equals(other); + } - public override int GetHashCode() - { - return (int) Value; - } + public override int GetHashCode() + { + return (int) Value; + } - public static bool operator ==(ElfHeaderFlags left, ElfHeaderFlags right) - { - return left.Equals(right); - } + public static bool operator ==(ElfHeaderFlags left, ElfHeaderFlags right) + { + return left.Equals(right); + } - public static bool operator !=(ElfHeaderFlags left, ElfHeaderFlags right) - { - return !left.Equals(right); - } + public static bool operator !=(ElfHeaderFlags left, ElfHeaderFlags right) + { + return !left.Equals(right); + } - public override string ToString() - { - return $"0x{Value:x}"; - } + public override string ToString() + { + return $"0x{Value:x}"; + } - public static explicit operator uint(ElfHeaderFlags flags) => flags.Value; + public static explicit operator uint(ElfHeaderFlags flags) => flags.Value; - public static implicit operator ElfHeaderFlags(uint flags) => new ElfHeaderFlags(flags); - } + public static implicit operator ElfHeaderFlags(uint flags) => new ElfHeaderFlags(flags); } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfNativeExtensions.cs b/src/LibObjectFile/Elf/ElfNativeExtensions.cs index 6f521cf..40bbbe5 100644 --- a/src/LibObjectFile/Elf/ElfNativeExtensions.cs +++ b/src/LibObjectFile/Elf/ElfNativeExtensions.cs @@ -4,101 +4,100 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Contains all the low-level structures used for reading/writing ELF data, automatically generated from C headers. +/// +public static partial class ElfNative { - /// - /// Contains all the low-level structures used for reading/writing ELF data, automatically generated from C headers. - /// - public static partial class ElfNative + public partial struct Elf32_Shdr: IEquatable { - public partial struct Elf32_Shdr: IEquatable - { - public static readonly Elf32_Shdr Null = new Elf32_Shdr(); - - public bool IsNull => this == Null; + public static readonly Elf32_Shdr Null = new Elf32_Shdr(); - public bool Equals(Elf32_Shdr other) - { - return sh_name.Equals(other.sh_name) && sh_type.Equals(other.sh_type) && sh_flags.Equals(other.sh_flags) && sh_addr.Equals(other.sh_addr) && sh_offset.Equals(other.sh_offset) && sh_size.Equals(other.sh_size) && sh_link.Equals(other.sh_link) && sh_info.Equals(other.sh_info) && sh_addralign.Equals(other.sh_addralign) && sh_entsize.Equals(other.sh_entsize); - } + public bool IsNull => this == Null; - public override bool Equals(object? obj) - { - return obj is Elf32_Shdr other && Equals(other); - } + public bool Equals(Elf32_Shdr other) + { + return sh_name.Equals(other.sh_name) && sh_type.Equals(other.sh_type) && sh_flags.Equals(other.sh_flags) && sh_addr.Equals(other.sh_addr) && sh_offset.Equals(other.sh_offset) && sh_size.Equals(other.sh_size) && sh_link.Equals(other.sh_link) && sh_info.Equals(other.sh_info) && sh_addralign.Equals(other.sh_addralign) && sh_entsize.Equals(other.sh_entsize); + } - public override int GetHashCode() - { - unchecked - { - var hashCode = sh_name.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_type.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_flags.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_addr.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_offset.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_size.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_link.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_info.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_addralign.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_entsize.GetHashCode(); - return hashCode; - } - } + public override bool Equals(object? obj) + { + return obj is Elf32_Shdr other && Equals(other); + } - public static bool operator ==(Elf32_Shdr left, Elf32_Shdr right) + public override int GetHashCode() + { + unchecked { - return left.Equals(right); + var hashCode = sh_name.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_type.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_flags.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_addr.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_offset.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_size.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_link.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_info.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_addralign.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_entsize.GetHashCode(); + return hashCode; } + } - public static bool operator !=(Elf32_Shdr left, Elf32_Shdr right) - { - return !left.Equals(right); - } + public static bool operator ==(Elf32_Shdr left, Elf32_Shdr right) + { + return left.Equals(right); } - public partial struct Elf64_Shdr : IEquatable + public static bool operator !=(Elf32_Shdr left, Elf32_Shdr right) { - public static readonly Elf64_Shdr Null = new Elf64_Shdr(); + return !left.Equals(right); + } + } + + public partial struct Elf64_Shdr : IEquatable + { + public static readonly Elf64_Shdr Null = new Elf64_Shdr(); - public bool IsNull => this == Null; + public bool IsNull => this == Null; - public bool Equals(Elf64_Shdr other) - { - return sh_name.Equals(other.sh_name) && sh_type.Equals(other.sh_type) && sh_flags.Equals(other.sh_flags) && sh_addr.Equals(other.sh_addr) && sh_offset.Equals(other.sh_offset) && sh_size.Equals(other.sh_size) && sh_link.Equals(other.sh_link) && sh_info.Equals(other.sh_info) && sh_addralign.Equals(other.sh_addralign) && sh_entsize.Equals(other.sh_entsize); - } + public bool Equals(Elf64_Shdr other) + { + return sh_name.Equals(other.sh_name) && sh_type.Equals(other.sh_type) && sh_flags.Equals(other.sh_flags) && sh_addr.Equals(other.sh_addr) && sh_offset.Equals(other.sh_offset) && sh_size.Equals(other.sh_size) && sh_link.Equals(other.sh_link) && sh_info.Equals(other.sh_info) && sh_addralign.Equals(other.sh_addralign) && sh_entsize.Equals(other.sh_entsize); + } - public override bool Equals(object? obj) - { - return obj is Elf64_Shdr other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is Elf64_Shdr other && Equals(other); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = sh_name.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_type.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_flags.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_addr.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_offset.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_size.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_link.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_info.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_addralign.GetHashCode(); - hashCode = (hashCode * 397) ^ sh_entsize.GetHashCode(); - return hashCode; - } + var hashCode = sh_name.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_type.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_flags.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_addr.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_offset.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_size.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_link.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_info.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_addralign.GetHashCode(); + hashCode = (hashCode * 397) ^ sh_entsize.GetHashCode(); + return hashCode; } + } - public static bool operator ==(Elf64_Shdr left, Elf64_Shdr right) - { - return left.Equals(right); - } + public static bool operator ==(Elf64_Shdr left, Elf64_Shdr right) + { + return left.Equals(right); + } - public static bool operator !=(Elf64_Shdr left, Elf64_Shdr right) - { - return !left.Equals(right); - } + public static bool operator !=(Elf64_Shdr left, Elf64_Shdr right) + { + return !left.Equals(right); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfOSAbi2.cs b/src/LibObjectFile/Elf/ElfOSAbi2.cs index 8090671..fb2ce26 100644 --- a/src/LibObjectFile/Elf/ElfOSAbi2.cs +++ b/src/LibObjectFile/Elf/ElfOSAbi2.cs @@ -4,61 +4,60 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines an OS ABI. +/// This is the value seen in the ident part of an Elf header at index +/// as well as the various machine defines (e.g ). +/// +public readonly partial struct ElfOSABIEx : IEquatable { - /// - /// Defines an OS ABI. - /// This is the value seen in the ident part of an Elf header at index - /// as well as the various machine defines (e.g ). - /// - public readonly partial struct ElfOSABIEx : IEquatable + public ElfOSABIEx(byte value) { - public ElfOSABIEx(byte value) - { - Value = (ElfOSABI)value; - } + Value = (ElfOSABI)value; + } - public ElfOSABIEx(ElfOSABI value) - { - Value = value; - } + public ElfOSABIEx(ElfOSABI value) + { + Value = value; + } - public readonly ElfOSABI Value; + public readonly ElfOSABI Value; - public override string ToString() - { - return ToStringInternal() ?? $"Unknown {nameof(ElfOSABIEx)} (0x{Value:X4})"; - } + public override string ToString() + { + return ToStringInternal() ?? $"Unknown {nameof(ElfOSABIEx)} (0x{Value:X4})"; + } - public bool Equals(ElfOSABIEx other) - { - return Value == other.Value; - } + public bool Equals(ElfOSABIEx other) + { + return Value == other.Value; + } - public override bool Equals(object? obj) - { - return obj is ElfOSABIEx other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is ElfOSABIEx other && Equals(other); + } - public override int GetHashCode() - { - return Value.GetHashCode(); - } + public override int GetHashCode() + { + return Value.GetHashCode(); + } - public static bool operator ==(ElfOSABIEx left, ElfOSABIEx right) - { - return left.Equals(right); - } + public static bool operator ==(ElfOSABIEx left, ElfOSABIEx right) + { + return left.Equals(right); + } - public static bool operator !=(ElfOSABIEx left, ElfOSABIEx right) - { - return !left.Equals(right); - } + public static bool operator !=(ElfOSABIEx left, ElfOSABIEx right) + { + return !left.Equals(right); + } - public static explicit operator byte(ElfOSABIEx osABI) => (byte)osABI.Value; + public static explicit operator byte(ElfOSABIEx osABI) => (byte)osABI.Value; - public static implicit operator ElfOSABIEx(ElfOSABI osABI) => new ElfOSABIEx(osABI); + public static implicit operator ElfOSABIEx(ElfOSABI osABI) => new ElfOSABIEx(osABI); - public static implicit operator ElfOSABI(ElfOSABIEx osABI) => osABI.Value; - } + public static implicit operator ElfOSABI(ElfOSABIEx osABI) => osABI.Value; } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfObject.cs b/src/LibObjectFile/Elf/ElfObject.cs index 1ad4fb5..a2dd3e0 100644 --- a/src/LibObjectFile/Elf/ElfObject.cs +++ b/src/LibObjectFile/Elf/ElfObject.cs @@ -7,20 +7,23 @@ namespace LibObjectFile.Elf; +public abstract class ElfObjectBase : ObjectFileElement +{ +} + /// /// Base class for an and . /// -public abstract class ElfObject : ObjectFileNode +public abstract class ElfObject : ElfObjectBase { - protected override void ValidateParent(ObjectFileNodeBase parent) + protected override void ValidateParent(ObjectFileElement parent) { if (!(parent is ElfObjectFile)) { throw new ArgumentException($"Parent must inherit from type {nameof(ElfObjectFile)}"); } } - - + /// /// Gets the containing . Might be null if this section or segment /// does not belong to an existing . diff --git a/src/LibObjectFile/Elf/ElfObjectFile.cs b/src/LibObjectFile/Elf/ElfObjectFile.cs index 98c5dfc..b56a3d2 100644 --- a/src/LibObjectFile/Elf/ElfObjectFile.cs +++ b/src/LibObjectFile/Elf/ElfObjectFile.cs @@ -17,7 +17,7 @@ namespace LibObjectFile.Elf; /// /// Defines an ELF object file that can be manipulated in memory. /// -public sealed class ElfObjectFile : ObjectFileNode +public sealed class ElfObjectFile : ElfObjectBase { private readonly List _sections; private ElfSectionHeaderStringTable? _sectionHeaderStringTable; @@ -166,14 +166,27 @@ public ElfSectionHeaderStringTable? SectionHeaderStringTable /// Gets the current calculated layout of this instance (e.g offset of the program header table) /// public ElfObjectLayout Layout { get; } + + public DiagnosticBag Verify() + { + var diagnostics = new DiagnosticBag(); + Verify(diagnostics); + return diagnostics; + } /// /// Verifies the integrity of this ELF object file. /// /// A DiagnosticBag instance to receive the diagnostics. - public override void Verify(DiagnosticBag diagnostics) + public void Verify(DiagnosticBag diagnostics) { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + var context = new ElfVisitorContext(this, diagnostics); + Verify(context); + } + + public override void Verify(ElfVisitorContext context) + { + var diagnostics = context.Diagnostics; if (FileClass == ElfFileClass.None) { @@ -188,13 +201,13 @@ public override void Verify(DiagnosticBag diagnostics) foreach (var segment in Segments) { - segment.Verify(diagnostics); + segment.Verify(context); } // Verify all sections before doing anything else foreach (var section in Sections) { - section.Verify(diagnostics); + section.Verify(context); } } @@ -211,11 +224,13 @@ public List GetSectionsOrderedByStreamIndex() /// /// A DiagnosticBag instance to receive the diagnostics. /// true if the calculation of the layout is successful. otherwise false - public override unsafe void UpdateLayout(DiagnosticBag diagnostics) + public unsafe void UpdateLayout(DiagnosticBag diagnostics) { if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); Size = 0; + + var context = new ElfVisitorContext(this, diagnostics); ulong offset = FileClass == ElfFileClass.Is32 ? (uint)sizeof(ElfNative.Elf32_Ehdr) : (uint)sizeof(ElfNative.Elf64_Ehdr); Layout.SizeOfElfHeader = (ushort)offset; @@ -280,7 +295,7 @@ public override unsafe void UpdateLayout(DiagnosticBag diagnostics) } } - section.UpdateLayout(diagnostics); + section.UpdateLayout(context); // Console.WriteLine($"{section.ToString(),-50} Offset: {section.Offset:x4} Size: {section.Size:x4}"); @@ -311,7 +326,7 @@ public override unsafe void UpdateLayout(DiagnosticBag diagnostics) for (int i = 0; i < Segments.Count; i++) { var programHeader = Segments[i]; - programHeader.UpdateLayout(diagnostics); + programHeader.UpdateLayout(context); } } @@ -775,4 +790,8 @@ private static int CompareStreamIndexAndIndex(ElfSection left, ElfSection right) if (delta != 0) return delta; return left.Index.CompareTo(right.Index); } + + public override void UpdateLayout(ElfVisitorContext layoutContext) + { + } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs b/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs index 34eb6ce..fb60352 100644 --- a/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs +++ b/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs @@ -4,80 +4,79 @@ using System; -namespace LibObjectFile.Elf -{ - using static ElfNative; +namespace LibObjectFile.Elf; + +using static ElfNative; +/// +/// Extensions for +/// +public static class ElfObjectFileExtensions +{ /// - /// Extensions for + /// Copy to an array buffer the ident array as found in ELF header + /// or . /// - public static class ElfObjectFileExtensions + /// The object file to copy the ident from. + /// A span receiving the ident. Must be >= 16 bytes length + public static void CopyIdentTo(this ElfObjectFile objectFile, Span ident) { - /// - /// Copy to an array buffer the ident array as found in ELF header - /// or . - /// - /// The object file to copy the ident from. - /// A span receiving the ident. Must be >= 16 bytes length - public static void CopyIdentTo(this ElfObjectFile objectFile, Span ident) + if (objectFile == null) throw new ArgumentNullException(nameof(objectFile)); + if (ident.Length < EI_NIDENT) { - if (objectFile == null) throw new ArgumentNullException(nameof(objectFile)); - if (ident.Length < EI_NIDENT) - { - throw new ArgumentException($"Expecting span length to be >= {EI_NIDENT}"); - } - - // Clear ident - for (int i = 0; i < EI_NIDENT; i++) - { - ident[i] = 0; - } - - ident[EI_MAG0] = ELFMAG0; - ident[EI_MAG1] = ELFMAG1; - ident[EI_MAG2] = ELFMAG2; - ident[EI_MAG3] = ELFMAG3; - ident[EI_CLASS] = (byte) objectFile.FileClass; - ident[EI_DATA] = (byte) objectFile.Encoding; - ident[EI_VERSION] = (byte)objectFile.Version; - ident[EI_OSABI] = (byte)objectFile.OSABI.Value; - ident[EI_ABIVERSION] = objectFile.AbiVersion; + throw new ArgumentException($"Expecting span length to be >= {EI_NIDENT}"); } - - /// - /// Tries to copy from an ident array as found in ELF header to this ELF object file instance. - /// or . - /// - /// The object file to receive the ident from. - /// A span to read from. Must be >= 16 bytes length - /// The diagnostics - /// true if copying the ident was successful. false otherwise - public static bool TryCopyIdentFrom(this ElfObjectFile objectFile, ReadOnlySpan ident, DiagnosticBag diagnostics) + + // Clear ident + for (int i = 0; i < EI_NIDENT; i++) { - if (objectFile == null) throw new ArgumentNullException(nameof(objectFile)); - if (ident.Length < EI_NIDENT) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidHeaderIdentLength, $"Invalid ELF Ident length found. Must be >= {EI_NIDENT}"); - return false; - } + ident[i] = 0; + } - if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidHeaderMagic, "Invalid ELF Magic found"); - return false; - } + ident[EI_MAG0] = ELFMAG0; + ident[EI_MAG1] = ELFMAG1; + ident[EI_MAG2] = ELFMAG2; + ident[EI_MAG3] = ELFMAG3; + ident[EI_CLASS] = (byte) objectFile.FileClass; + ident[EI_DATA] = (byte) objectFile.Encoding; + ident[EI_VERSION] = (byte)objectFile.Version; + ident[EI_OSABI] = (byte)objectFile.OSABI.Value; + ident[EI_ABIVERSION] = objectFile.AbiVersion; + } - CopyIndentFrom(objectFile, ident); - return true; + /// + /// Tries to copy from an ident array as found in ELF header to this ELF object file instance. + /// or . + /// + /// The object file to receive the ident from. + /// A span to read from. Must be >= 16 bytes length + /// The diagnostics + /// true if copying the ident was successful. false otherwise + public static bool TryCopyIdentFrom(this ElfObjectFile objectFile, ReadOnlySpan ident, DiagnosticBag diagnostics) + { + if (objectFile == null) throw new ArgumentNullException(nameof(objectFile)); + if (ident.Length < EI_NIDENT) + { + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidHeaderIdentLength, $"Invalid ELF Ident length found. Must be >= {EI_NIDENT}"); + return false; } - internal static void CopyIndentFrom(this ElfObjectFile objectFile, ReadOnlySpan ident) + if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) { - objectFile.FileClass = (ElfFileClass)ident[EI_CLASS]; - objectFile.Encoding = (ElfEncoding)ident[EI_DATA]; - objectFile.Version = ident[EI_VERSION]; - objectFile.OSABI = new ElfOSABIEx(ident[EI_OSABI]); - objectFile.AbiVersion = ident[EI_ABIVERSION]; + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidHeaderMagic, "Invalid ELF Magic found"); + return false; } + + CopyIndentFrom(objectFile, ident); + return true; + } + + internal static void CopyIndentFrom(this ElfObjectFile objectFile, ReadOnlySpan ident) + { + objectFile.FileClass = (ElfFileClass)ident[EI_CLASS]; + objectFile.Encoding = (ElfEncoding)ident[EI_DATA]; + objectFile.Version = ident[EI_VERSION]; + objectFile.OSABI = new ElfOSABIEx(ident[EI_OSABI]); + objectFile.AbiVersion = ident[EI_ABIVERSION]; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfPrinter.cs b/src/LibObjectFile/Elf/ElfPrinter.cs index 288228f..0eb4f71 100644 --- a/src/LibObjectFile/Elf/ElfPrinter.cs +++ b/src/LibObjectFile/Elf/ElfPrinter.cs @@ -8,810 +8,809 @@ using System.Text; using System.Xml; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Extensions methods for to print their layout in text forms, similar to readelf. +/// +public static class ElfPrinter { /// - /// Extensions methods for to print their layout in text forms, similar to readelf. + /// Prints an to the specified writer. /// - public static class ElfPrinter + /// The object file to print. + /// The destination text writer. + public static void Print(this ElfObjectFile elf, TextWriter writer) { - /// - /// Prints an to the specified writer. - /// - /// The object file to print. - /// The destination text writer. - public static void Print(this ElfObjectFile elf, TextWriter writer) - { - if (elf == null) throw new ArgumentNullException(nameof(elf)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); - PrintElfHeader(elf, writer); - PrintSectionHeaders(elf, writer); - PrintSectionGroups(elf, writer); - PrintProgramHeaders(elf, writer); - PrintDynamicSections(elf, writer); - PrintRelocations(elf, writer); - PrintUnwind(elf, writer); - PrintSymbolTables(elf, writer); - PrintVersionInformation(elf, writer); - PrintNotes(elf, writer); - } + if (elf == null) throw new ArgumentNullException(nameof(elf)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); + PrintElfHeader(elf, writer); + PrintSectionHeaders(elf, writer); + PrintSectionGroups(elf, writer); + PrintProgramHeaders(elf, writer); + PrintDynamicSections(elf, writer); + PrintRelocations(elf, writer); + PrintUnwind(elf, writer); + PrintSymbolTables(elf, writer); + PrintVersionInformation(elf, writer); + PrintNotes(elf, writer); + } - public static void PrintElfHeader(ElfObjectFile elf, TextWriter writer) - { - if (elf == null) throw new ArgumentNullException(nameof(elf)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); + public static void PrintElfHeader(ElfObjectFile elf, TextWriter writer) + { + if (elf == null) throw new ArgumentNullException(nameof(elf)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); - Span ident = stackalloc byte[ElfObjectFile.IdentSizeInBytes]; - elf.CopyIdentTo(ident); + Span ident = stackalloc byte[ElfObjectFile.IdentSizeInBytes]; + elf.CopyIdentTo(ident); - writer.WriteLine("ELF Header:"); + writer.WriteLine("ELF Header:"); - writer.Write(" Magic: "); - foreach (var b in ident) - { - writer.Write($"{b:x2} "); - } - writer.WriteLine(); - writer.WriteLine($" Class: {GetElfFileClass(elf.FileClass)}"); - writer.WriteLine($" Data: {GetElfEncoding(elf.Encoding)}"); - writer.WriteLine($" Version: {GetElfVersion((byte)elf.Version)}"); - writer.WriteLine($" OS/ABI: {GetElfOsAbi(elf.OSABI)}"); - writer.WriteLine($" ABI Version: {elf.AbiVersion}"); - writer.WriteLine($" Type: {GetElfFileType(elf.FileType)}"); - writer.WriteLine($" Machine: {GetElfArch(elf.Arch)}"); - writer.WriteLine($" Version: 0x{elf.Version:x}"); - writer.WriteLine($" Entry point address: 0x{elf.EntryPointAddress:x}"); - writer.WriteLine($" Start of program headers: {elf.Layout.OffsetOfProgramHeaderTable} (bytes into file)"); - writer.WriteLine($" Start of section headers: {elf.Layout.OffsetOfSectionHeaderTable} (bytes into file)"); - writer.WriteLine($" Flags: {elf.Flags}"); - writer.WriteLine($" Size of this header: {elf.Layout.SizeOfElfHeader} (bytes)"); - writer.WriteLine($" Size of program headers: {elf.Layout.SizeOfProgramHeaderEntry} (bytes)"); - writer.WriteLine($" Number of program headers: {elf.Segments.Count}"); - writer.WriteLine($" Size of section headers: {elf.Layout.SizeOfSectionHeaderEntry} (bytes)"); - if (elf.VisibleSectionCount >= ElfNative.SHN_LORESERVE || elf.VisibleSectionCount == 0) - { - writer.WriteLine($" Number of section headers: 0 ({elf.VisibleSectionCount})"); - } - else - { - writer.WriteLine($" Number of section headers: {elf.VisibleSectionCount}"); - } - writer.WriteLine($" Section header string table index: {elf.SectionHeaderStringTable?.SectionIndex ?? 0}"); + writer.Write(" Magic: "); + foreach (var b in ident) + { + writer.Write($"{b:x2} "); } - - public static void PrintSectionHeaders(ElfObjectFile elf, TextWriter writer) + writer.WriteLine(); + writer.WriteLine($" Class: {GetElfFileClass(elf.FileClass)}"); + writer.WriteLine($" Data: {GetElfEncoding(elf.Encoding)}"); + writer.WriteLine($" Version: {GetElfVersion((byte)elf.Version)}"); + writer.WriteLine($" OS/ABI: {GetElfOsAbi(elf.OSABI)}"); + writer.WriteLine($" ABI Version: {elf.AbiVersion}"); + writer.WriteLine($" Type: {GetElfFileType(elf.FileType)}"); + writer.WriteLine($" Machine: {GetElfArch(elf.Arch)}"); + writer.WriteLine($" Version: 0x{elf.Version:x}"); + writer.WriteLine($" Entry point address: 0x{elf.EntryPointAddress:x}"); + writer.WriteLine($" Start of program headers: {elf.Layout.OffsetOfProgramHeaderTable} (bytes into file)"); + writer.WriteLine($" Start of section headers: {elf.Layout.OffsetOfSectionHeaderTable} (bytes into file)"); + writer.WriteLine($" Flags: {elf.Flags}"); + writer.WriteLine($" Size of this header: {elf.Layout.SizeOfElfHeader} (bytes)"); + writer.WriteLine($" Size of program headers: {elf.Layout.SizeOfProgramHeaderEntry} (bytes)"); + writer.WriteLine($" Number of program headers: {elf.Segments.Count}"); + writer.WriteLine($" Size of section headers: {elf.Layout.SizeOfSectionHeaderEntry} (bytes)"); + if (elf.VisibleSectionCount >= ElfNative.SHN_LORESERVE || elf.VisibleSectionCount == 0) + { + writer.WriteLine($" Number of section headers: 0 ({elf.VisibleSectionCount})"); + } + else { - if (elf == null) throw new ArgumentNullException(nameof(elf)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); + writer.WriteLine($" Number of section headers: {elf.VisibleSectionCount}"); + } + writer.WriteLine($" Section header string table index: {elf.SectionHeaderStringTable?.SectionIndex ?? 0}"); + } - writer.WriteLine(); - if (elf.VisibleSectionCount == 0) - { - writer.WriteLine("There are no sections in this file."); - return; - } + public static void PrintSectionHeaders(ElfObjectFile elf, TextWriter writer) + { + if (elf == null) throw new ArgumentNullException(nameof(elf)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); - writer.WriteLine(elf.VisibleSectionCount > 1 ? "Section Headers:" : "Section Header:"); + writer.WriteLine(); + if (elf.VisibleSectionCount == 0) + { + writer.WriteLine("There are no sections in this file."); + return; + } - writer.WriteLine(" [Nr] Name Type Address Off Size ES Flg Lk Inf Al"); - for (int i = 0; i < elf.Sections.Count; i++) - { - var section = elf.Sections[i]; - if (section.IsShadow) continue; - writer.WriteLine($" [{section.SectionIndex,2:#0}] {GetElfSectionName(section),-17} {GetElfSectionType(section.Type),-15} {section.VirtualAddress:x16} {section.Position:x6} {section.Size:x6} {section.TableEntrySize:x2} {GetElfSectionFlags(section.Flags),3} {section.Link.GetIndex(),2} {section.Info.GetIndex(),3} {section.Alignment,2}"); - } - writer.WriteLine(@"Key to Flags: + writer.WriteLine(elf.VisibleSectionCount > 1 ? "Section Headers:" : "Section Header:"); + + writer.WriteLine(" [Nr] Name Type Address Off Size ES Flg Lk Inf Al"); + for (int i = 0; i < elf.Sections.Count; i++) + { + var section = elf.Sections[i]; + if (section.IsShadow) continue; + writer.WriteLine($" [{section.SectionIndex,2:#0}] {GetElfSectionName(section),-17} {GetElfSectionType(section.Type),-15} {section.VirtualAddress:x16} {section.Position:x6} {section.Size:x6} {section.TableEntrySize:x2} {GetElfSectionFlags(section.Flags),3} {section.Link.GetIndex(),2} {section.Info.GetIndex(),3} {section.Alignment,2}"); + } + writer.WriteLine(@"Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), D (mbind), l (large), p (processor specific)"); - } + } - public static void PrintSectionGroups(ElfObjectFile elf, TextWriter writer) - { - if (elf == null) throw new ArgumentNullException(nameof(elf)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); + public static void PrintSectionGroups(ElfObjectFile elf, TextWriter writer) + { + if (elf == null) throw new ArgumentNullException(nameof(elf)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); - writer.WriteLine(); - writer.WriteLine("There are no section groups in this file."); - // TODO - } + writer.WriteLine(); + writer.WriteLine("There are no section groups in this file."); + // TODO + } + + private static string GetElfSectionName(ElfSection section) + { + return section.Parent?.SectionHeaderStringTable == null ? "" : section.Name.Value!; + } + + public static void PrintProgramHeaders(ElfObjectFile elf, TextWriter writer) + { + if (elf == null) throw new ArgumentNullException(nameof(elf)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); + + writer.WriteLine(); - private static string GetElfSectionName(ElfSection section) + if (elf.Segments.Count == 0) { - return section.Parent?.SectionHeaderStringTable == null ? "" : section.Name.Value!; + writer.WriteLine("There are no program headers in this file."); + return; } + + writer.WriteLine(elf.Segments.Count > 1 ? "Program Headers:" : "Program Header:"); - public static void PrintProgramHeaders(ElfObjectFile elf, TextWriter writer) + writer.WriteLine(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align"); + for (int i = 0; i < elf.Segments.Count; i++) { - if (elf == null) throw new ArgumentNullException(nameof(elf)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); + var phdr = elf.Segments[i]; + writer.WriteLine($" {GetElfSegmentType(phdr.Type),-14} 0x{phdr.Position:x6} 0x{phdr.VirtualAddress:x16} 0x{phdr.PhysicalAddress:x16} 0x{phdr.Size:x6} 0x{phdr.SizeInMemory:x6} {GetElfSegmentFlags(phdr.Flags),3} 0x{phdr.Alignment:x}"); + } + if (elf.Segments.Count > 0 && elf.VisibleSectionCount > 0 && elf.SectionHeaderStringTable != null) + { writer.WriteLine(); + writer.WriteLine(" Section to Segment mapping:"); + writer.WriteLine(" Segment Sections..."); - if (elf.Segments.Count == 0) - { - writer.WriteLine("There are no program headers in this file."); - return; - } - - writer.WriteLine(elf.Segments.Count > 1 ? "Program Headers:" : "Program Header:"); - - writer.WriteLine(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align"); for (int i = 0; i < elf.Segments.Count; i++) { - var phdr = elf.Segments[i]; - writer.WriteLine($" {GetElfSegmentType(phdr.Type),-14} 0x{phdr.Position:x6} 0x{phdr.VirtualAddress:x16} 0x{phdr.PhysicalAddress:x16} 0x{phdr.Size:x6} 0x{phdr.SizeInMemory:x6} {GetElfSegmentFlags(phdr.Flags),3} 0x{phdr.Alignment:x}"); - } - - if (elf.Segments.Count > 0 && elf.VisibleSectionCount > 0 && elf.SectionHeaderStringTable != null) - { - writer.WriteLine(); - writer.WriteLine(" Section to Segment mapping:"); - writer.WriteLine(" Segment Sections..."); + var segment = elf.Segments[i]; + writer.Write($" {i:00} "); - for (int i = 0; i < elf.Segments.Count; i++) + foreach (var section in elf.Sections) { - var segment = elf.Segments[i]; - writer.Write($" {i:00} "); - - foreach (var section in elf.Sections) + if (IsSectionInSegment(section, segment, true, true)) { - if (IsSectionInSegment(section, segment, true, true)) - { - writer.Write($"{GetElfSectionName(section)} "); - } + writer.Write($"{GetElfSectionName(section)} "); } - - writer.WriteLine(); } + + writer.WriteLine(); } } + } - public static void PrintRelocations(ElfObjectFile elf, TextWriter writer) - { - if (elf == null) throw new ArgumentNullException(nameof(elf)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); + public static void PrintRelocations(ElfObjectFile elf, TextWriter writer) + { + if (elf == null) throw new ArgumentNullException(nameof(elf)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); - bool hasRelocations = false; + bool hasRelocations = false; - foreach (var section in elf.Sections) + foreach (var section in elf.Sections) + { + if (section.Type == ElfSectionType.Relocation || section.Type == ElfSectionType.RelocationAddends) { - if (section.Type == ElfSectionType.Relocation || section.Type == ElfSectionType.RelocationAddends) - { - hasRelocations = true; - var relocTable = (ElfRelocationTable) section; + hasRelocations = true; + var relocTable = (ElfRelocationTable) section; - writer.WriteLine(); - writer.WriteLine($"Relocation section {(elf.SectionHeaderStringTable == null ? "0" : $"'{section.Name}'")} at offset 0x{section.Position:x} contains {relocTable.Entries.Count} {(relocTable.Entries.Count > 1 ? "entries" : "entry")}:"); + writer.WriteLine(); + writer.WriteLine($"Relocation section {(elf.SectionHeaderStringTable == null ? "0" : $"'{section.Name}'")} at offset 0x{section.Position:x} contains {relocTable.Entries.Count} {(relocTable.Entries.Count > 1 ? "entries" : "entry")}:"); - if (elf.FileClass == ElfFileClass.Is32) - { - // TODO - writer.WriteLine(" Offset Info Type Symbol's Value Symbol's Name + Addend"); - } - else - { - writer.WriteLine(" Offset Info Type Symbol's Value Symbol's Name + Addend"); - } - foreach (var entry in relocTable.Entries) + if (elf.FileClass == ElfFileClass.Is32) + { + // TODO + writer.WriteLine(" Offset Info Type Symbol's Value Symbol's Name + Addend"); + } + else + { + writer.WriteLine(" Offset Info Type Symbol's Value Symbol's Name + Addend"); + } + foreach (var entry in relocTable.Entries) + { + var symbolTable = relocTable.Link.Section as ElfSymbolTable; + if (symbolTable == null) continue; + string symbolName = string.Empty; + ulong symbolValue = 0; + if (entry.SymbolIndex < symbolTable.Entries.Count) { - var symbolTable = relocTable.Link.Section as ElfSymbolTable; - if (symbolTable == null) continue; - string symbolName = string.Empty; - ulong symbolValue = 0; - if (entry.SymbolIndex < symbolTable.Entries.Count) - { - var symbolEntry = symbolTable.Entries[(int) entry.SymbolIndex]; - symbolName = symbolEntry.Name!; - symbolValue = symbolEntry.Value; + var symbolEntry = symbolTable.Entries[(int) entry.SymbolIndex]; + symbolName = symbolEntry.Name!; + symbolValue = symbolEntry.Value; - if (string.IsNullOrEmpty(symbolName)) + if (string.IsNullOrEmpty(symbolName)) + { + switch (symbolEntry.Type) { - switch (symbolEntry.Type) - { - case ElfSymbolType.Section: - if (symbolEntry.Section.Section != null) - { - symbolName = symbolEntry.Section.Section.Name!; - } - break; - } + case ElfSymbolType.Section: + if (symbolEntry.Section.Section != null) + { + symbolName = symbolEntry.Section.Section.Name!; + } + break; } } + } - if (elf.FileClass == ElfFileClass.Is32) + if (elf.FileClass == ElfFileClass.Is32) + { + writer.WriteLine($"{entry.Offset:x8} {entry.Info32:x8} {entry.Type.Name,-22} {symbolValue:x8} {symbolName} + {entry.Addend:x}"); + } + else + { + if (string.IsNullOrEmpty(symbolName)) { - writer.WriteLine($"{entry.Offset:x8} {entry.Info32:x8} {entry.Type.Name,-22} {symbolValue:x8} {symbolName} + {entry.Addend:x}"); + writer.WriteLine($"{entry.Offset:x16} {entry.Info64:x16} {entry.Type.Name,-22} {"",16} {entry.Addend:x}"); } else { - if (string.IsNullOrEmpty(symbolName)) - { - writer.WriteLine($"{entry.Offset:x16} {entry.Info64:x16} {entry.Type.Name,-22} {"",16} {entry.Addend:x}"); - } - else - { - writer.WriteLine($"{entry.Offset:x16} {entry.Info64:x16} {entry.Type.Name,-22} {symbolValue:x16} {symbolName} + {entry.Addend:x}"); - } + writer.WriteLine($"{entry.Offset:x16} {entry.Info64:x16} {entry.Type.Name,-22} {symbolValue:x16} {symbolName} + {entry.Addend:x}"); } } - } - } - if (!hasRelocations) - { - writer.WriteLine(); - writer.WriteLine("There are no relocations in this file."); } } - public static void PrintUnwind(ElfObjectFile elf, TextWriter writer) + if (!hasRelocations) { - if (elf == null) throw new ArgumentNullException(nameof(elf)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); - - if (elf.Arch == ElfArchEx.I386 || elf.Arch == ElfArchEx.X86_64) - { - writer.WriteLine("No processor specific unwind information to decode"); - } - else - { - writer.WriteLine(); - writer.WriteLine($"The decoding of unwind sections for machine type {GetElfArch(elf.Arch)} is not currently supported."); - } + writer.WriteLine(); + writer.WriteLine("There are no relocations in this file."); } + } + public static void PrintUnwind(ElfObjectFile elf, TextWriter writer) + { + if (elf == null) throw new ArgumentNullException(nameof(elf)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); - public static void PrintSymbolTables(ElfObjectFile elf, TextWriter writer) + if (elf.Arch == ElfArchEx.I386 || elf.Arch == ElfArchEx.X86_64) { - if (elf == null) throw new ArgumentNullException(nameof(elf)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); - - if (elf.VisibleSectionCount == 0) - { - writer.WriteLine(); - writer.WriteLine("Dynamic symbol information is not available for displaying symbols."); - return; - } - - foreach (var section in elf.Sections) - { - if (!(section is ElfSymbolTable symbolTable)) continue; - - writer.WriteLine(); - writer.WriteLine(symbolTable.Entries.Count <= 1 - ? $"Symbol table '{GetElfSectionName(symbolTable)}' contains {symbolTable.Entries.Count} entry:" - : $"Symbol table '{GetElfSectionName(symbolTable)}' contains {symbolTable.Entries.Count} entries:" - ); - - if (elf.FileClass == ElfFileClass.Is32) - { - writer.WriteLine(" Num: Value Size Type Bind Vis Ndx Name"); - } - else - { - writer.WriteLine(" Num: Value Size Type Bind Vis Ndx Name"); - } - - for (var i = 0; i < symbolTable.Entries.Count; i++) - { - var symbol = symbolTable.Entries[i]; - writer.WriteLine($"{i,6}: {symbol.Value:x16} {symbol.Size,5} {GetElfSymbolType(symbol.Type),-7} {GetElfSymbolBind(symbol.Bind),-6} {GetElfSymbolVisibility(symbol.Visibility),-7} {GetElfSymbolLink(symbol.Section),4} {symbol.Name.Value}"); - } - } + writer.WriteLine("No processor specific unwind information to decode"); } - - private static string GetElfSymbolLink(ElfSectionLink link) + else { - var index = link.GetIndex(); - switch (index) - { - case 0: - return "UND"; - case ElfNative.SHN_ABS: - return "ABS"; - case ElfNative.SHN_COMMON: - return "COMMON"; - } - return index.ToString(); + writer.WriteLine(); + writer.WriteLine($"The decoding of unwind sections for machine type {GetElfArch(elf.Arch)} is not currently supported."); } + } - public static void PrintNotes(ElfObjectFile elf, TextWriter writer) - { - if (elf == null) throw new ArgumentNullException(nameof(elf)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); - - foreach (var section in elf.Sections) - { - if (!(section is ElfNoteTable noteTable)) continue; - writer.WriteLine(); - writer.WriteLine($"Displaying notes found in: {noteTable.Name}"); + public static void PrintSymbolTables(ElfObjectFile elf, TextWriter writer) + { + if (elf == null) throw new ArgumentNullException(nameof(elf)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); - writer.WriteLine(" Owner\t\tData size\tDescription"); - foreach (var note in noteTable.Entries) - { - writer.WriteLine($" {note.GetName()}\t\t0x{(note.GetDescriptorSize()):x8}\t{GetElfNoteDescription(note)}"); - } - } + if (elf.VisibleSectionCount == 0) + { + writer.WriteLine(); + writer.WriteLine("Dynamic symbol information is not available for displaying symbols."); + return; } - private static string GetElfNoteDescription(ElfNote note) + foreach (var section in elf.Sections) { - var builder = new StringBuilder(); + if (!(section is ElfSymbolTable symbolTable)) continue; + + writer.WriteLine(); + writer.WriteLine(symbolTable.Entries.Count <= 1 + ? $"Symbol table '{GetElfSectionName(symbolTable)}' contains {symbolTable.Entries.Count} entry:" + : $"Symbol table '{GetElfSectionName(symbolTable)}' contains {symbolTable.Entries.Count} entries:" + ); - if (note.GetName() == "GNU") + if (elf.FileClass == ElfFileClass.Is32) { - switch (note.GetNoteType().Value) - { - case ElfNoteType.GNU_ABI_TAG: - builder.Append("NT_GNU_ABI_TAG (ABI version tag)"); - break; - case ElfNoteType.GNU_HWCAP: - builder.Append("NT_GNU_HWCAP (DSO-supplied software HWCAP info)"); - break; - case ElfNoteType.GNU_BUILD_ID: - builder.Append("NT_GNU_BUILD_ID (unique build ID bitstring)"); - break; - case ElfNoteType.GNU_GOLD_VERSION: - builder.Append("NT_GNU_GOLD_VERSION (gold version)"); - break; - } + writer.WriteLine(" Num: Value Size Type Bind Vis Ndx Name"); } else { - switch (note.GetNoteType().Value) - { - case ElfNoteType.VERSION: - builder.Append("NT_VERSION (version)"); - break; - } + writer.WriteLine(" Num: Value Size Type Bind Vis Ndx Name"); } - if (builder.Length == 0) + for (var i = 0; i < symbolTable.Entries.Count; i++) { - builder.Append($"Unknown note type: (0x{(uint)note.GetNoteType().Value:x8})"); + var symbol = symbolTable.Entries[i]; + writer.WriteLine($"{i,6}: {symbol.Value:x16} {symbol.Size,5} {GetElfSymbolType(symbol.Type),-7} {GetElfSymbolBind(symbol.Bind),-6} {GetElfSymbolVisibility(symbol.Visibility),-7} {GetElfSymbolLink(symbol.Section),4} {symbol.Name.Value}"); } + } + } - builder.Append("\t\t"); - - builder.Append(note.GetDescriptorAsText()); - return builder.ToString(); + private static string GetElfSymbolLink(ElfSectionLink link) + { + var index = link.GetIndex(); + switch (index) + { + case 0: + return "UND"; + case ElfNative.SHN_ABS: + return "ABS"; + case ElfNative.SHN_COMMON: + return "COMMON"; } + return index.ToString(); + } - public static void PrintVersionInformation(ElfObjectFile elf, TextWriter writer) + public static void PrintNotes(ElfObjectFile elf, TextWriter writer) + { + if (elf == null) throw new ArgumentNullException(nameof(elf)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); + + foreach (var section in elf.Sections) { - if (elf == null) throw new ArgumentNullException(nameof(elf)); - if (writer == null) throw new ArgumentNullException(nameof(writer)); + if (!(section is ElfNoteTable noteTable)) continue; + writer.WriteLine(); - writer.WriteLine("No version information found in this file."); + writer.WriteLine($"Displaying notes found in: {noteTable.Name}"); + + writer.WriteLine(" Owner\t\tData size\tDescription"); + foreach (var note in noteTable.Entries) + { + writer.WriteLine($" {note.GetName()}\t\t0x{(note.GetDescriptorSize()):x8}\t{GetElfNoteDescription(note)}"); + } } + } - private static string GetElfSymbolType(ElfSymbolType symbolType) + private static string GetElfNoteDescription(ElfNote note) + { + var builder = new StringBuilder(); + + if (note.GetName() == "GNU") { - switch (symbolType) + switch (note.GetNoteType().Value) { - case ElfSymbolType.NoType: return "NOTYPE"; - case ElfSymbolType.Object: return "OBJECT"; - case ElfSymbolType.Function: return "FUNC"; - case ElfSymbolType.Section: return "SECTION"; - case ElfSymbolType.File: return "FILE"; - case ElfSymbolType.Common: return "COMMON"; - case ElfSymbolType.Tls: return "TLS"; - case ElfSymbolType.GnuIndirectFunction: - case ElfSymbolType.SpecificOS1: - case ElfSymbolType.SpecificOS2: - return $": {(uint)symbolType}"; - case ElfSymbolType.SpecificProcessor0: - case ElfSymbolType.SpecificProcessor1: - case ElfSymbolType.SpecificProcessor2: - return $": {(uint)symbolType}"; - default: - return $": {(uint)symbolType}"; + case ElfNoteType.GNU_ABI_TAG: + builder.Append("NT_GNU_ABI_TAG (ABI version tag)"); + break; + case ElfNoteType.GNU_HWCAP: + builder.Append("NT_GNU_HWCAP (DSO-supplied software HWCAP info)"); + break; + case ElfNoteType.GNU_BUILD_ID: + builder.Append("NT_GNU_BUILD_ID (unique build ID bitstring)"); + break; + case ElfNoteType.GNU_GOLD_VERSION: + builder.Append("NT_GNU_GOLD_VERSION (gold version)"); + break; } } - - private static string GetElfSymbolBind(ElfSymbolBind symbolBind) + else { - switch (symbolBind) + switch (note.GetNoteType().Value) { - case ElfSymbolBind.Local: - return "LOCAL"; - case ElfSymbolBind.Global: - return "GLOBAL"; - case ElfSymbolBind.Weak: - return "WEAK"; - case ElfSymbolBind.SpecificOS1: - case ElfSymbolBind.SpecificOS2: - return $": {(uint)symbolBind}"; - case ElfSymbolBind.SpecificProcessor0: - case ElfSymbolBind.SpecificProcessor1: - case ElfSymbolBind.SpecificProcessor2: - return $": {(uint)symbolBind}"; - default: - return $": {(uint)symbolBind}"; + case ElfNoteType.VERSION: + builder.Append("NT_VERSION (version)"); + break; } } - private static string GetElfSymbolVisibility(ElfSymbolVisibility symbolVisibility) + if (builder.Length == 0) { - return symbolVisibility switch - { - ElfSymbolVisibility.Default => "DEFAULT", - ElfSymbolVisibility.Internal => "INTERNAL", - ElfSymbolVisibility.Hidden => "HIDDEN", - ElfSymbolVisibility.Protected => "PROTECTED", - _ => $"Unrecognized visibility value: {(uint) symbolVisibility}" - }; + builder.Append($"Unknown note type: (0x{(uint)note.GetNoteType().Value:x8})"); } - private static bool IsTlsSpecial(ElfSection section, ElfSegment segment) + builder.Append("\t\t"); + + builder.Append(note.GetDescriptorAsText()); + return builder.ToString(); + } + + public static void PrintVersionInformation(ElfObjectFile elf, TextWriter writer) + { + if (elf == null) throw new ArgumentNullException(nameof(elf)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); + writer.WriteLine(); + writer.WriteLine("No version information found in this file."); + } + + private static string GetElfSymbolType(ElfSymbolType symbolType) + { + switch (symbolType) { - return (((section).Flags & ElfSectionFlags.Tls) != 0 - && (section).Type == ElfSectionType.NoBits - && (segment).Type != ElfSegmentTypeCore.Tls); + case ElfSymbolType.NoType: return "NOTYPE"; + case ElfSymbolType.Object: return "OBJECT"; + case ElfSymbolType.Function: return "FUNC"; + case ElfSymbolType.Section: return "SECTION"; + case ElfSymbolType.File: return "FILE"; + case ElfSymbolType.Common: return "COMMON"; + case ElfSymbolType.Tls: return "TLS"; + case ElfSymbolType.GnuIndirectFunction: + case ElfSymbolType.SpecificOS1: + case ElfSymbolType.SpecificOS2: + return $": {(uint)symbolType}"; + case ElfSymbolType.SpecificProcessor0: + case ElfSymbolType.SpecificProcessor1: + case ElfSymbolType.SpecificProcessor2: + return $": {(uint)symbolType}"; + default: + return $": {(uint)symbolType}"; } + } - private static ulong GetSectionSize(ElfSection section, ElfSegment segment) + private static string GetElfSymbolBind(ElfSymbolBind symbolBind) + { + switch (symbolBind) { - return IsTlsSpecial(section, segment) ? 0 : section.Size; + case ElfSymbolBind.Local: + return "LOCAL"; + case ElfSymbolBind.Global: + return "GLOBAL"; + case ElfSymbolBind.Weak: + return "WEAK"; + case ElfSymbolBind.SpecificOS1: + case ElfSymbolBind.SpecificOS2: + return $": {(uint)symbolBind}"; + case ElfSymbolBind.SpecificProcessor0: + case ElfSymbolBind.SpecificProcessor1: + case ElfSymbolBind.SpecificProcessor2: + return $": {(uint)symbolBind}"; + default: + return $": {(uint)symbolBind}"; } + } - private static bool IsSectionInSegment(ElfSection section, ElfSegment segment, bool checkVirtualAddress, bool isStrict) + private static string GetElfSymbolVisibility(ElfSymbolVisibility symbolVisibility) + { + return symbolVisibility switch { - return (( /* Only PT_LOAD, PT_GNU_RELRO and PT_TLS segments can contain SHF_TLS sections. */ - ((((section).Flags & ElfSectionFlags.Tls) != 0) - && (segment.Type == ElfSegmentTypeCore.Tls - //|| segment.Type == ElfSegmentTypeCore.GnuRelPro - || segment.Type == ElfSegmentTypeCore.Load)) - /* PT_TLS segment contains only SHF_TLS sections, PT_PHDR no - sections at all. */ - || (((section).Flags & ElfSectionFlags.Tls) == 0 + ElfSymbolVisibility.Default => "DEFAULT", + ElfSymbolVisibility.Internal => "INTERNAL", + ElfSymbolVisibility.Hidden => "HIDDEN", + ElfSymbolVisibility.Protected => "PROTECTED", + _ => $"Unrecognized visibility value: {(uint) symbolVisibility}" + }; + } - && segment.Type != ElfSegmentTypeCore.Tls + private static bool IsTlsSpecial(ElfSection section, ElfSegment segment) + { + return (((section).Flags & ElfSectionFlags.Tls) != 0 + && (section).Type == ElfSectionType.NoBits + && (segment).Type != ElfSegmentTypeCore.Tls); + } - && segment.Type != ElfSegmentTypeCore.ProgramHeader)) - /* PT_LOAD and similar segments only have SHF_ALLOC sections. */ - && !((section.Flags & ElfSectionFlags.Alloc) == 0 + private static ulong GetSectionSize(ElfSection section, ElfSegment segment) + { + return IsTlsSpecial(section, segment) ? 0 : section.Size; + } - && (segment.Type == ElfSegmentTypeCore.Load + private static bool IsSectionInSegment(ElfSection section, ElfSegment segment, bool checkVirtualAddress, bool isStrict) + { + return (( /* Only PT_LOAD, PT_GNU_RELRO and PT_TLS segments can contain SHF_TLS sections. */ + ((((section).Flags & ElfSectionFlags.Tls) != 0) + && (segment.Type == ElfSegmentTypeCore.Tls + //|| segment.Type == ElfSegmentTypeCore.GnuRelPro + || segment.Type == ElfSegmentTypeCore.Load)) + /* PT_TLS segment contains only SHF_TLS sections, PT_PHDR no + sections at all. */ + || (((section).Flags & ElfSectionFlags.Tls) == 0 - || segment.Type == ElfSegmentTypeCore.Dynamic - //|| segment.Type == PT_GNU_EH_FRAME - //|| segment.Type == PT_GNU_STACK - //|| segment.Type == PT_GNU_RELRO - //|| (segment.Type >= PT_GNU_MBIND_LO - //&& segment.Type <= PT_GNU_MBIND_HI - ))) - /* Any section besides one of type SHT_NOBITS must have file - offsets within the segment. */ - && (section.Type == ElfSectionType.NoBits - || ((section).Position >= segment.Position + && segment.Type != ElfSegmentTypeCore.Tls - && (!(isStrict) + && segment.Type != ElfSegmentTypeCore.ProgramHeader)) + /* PT_LOAD and similar segments only have SHF_ALLOC sections. */ + && !((section.Flags & ElfSectionFlags.Alloc) == 0 - || (section.Position - segment.Position + && (segment.Type == ElfSegmentTypeCore.Load - <= segment.Size - 1)) + || segment.Type == ElfSegmentTypeCore.Dynamic + //|| segment.Type == PT_GNU_EH_FRAME + //|| segment.Type == PT_GNU_STACK + //|| segment.Type == PT_GNU_RELRO + //|| (segment.Type >= PT_GNU_MBIND_LO + //&& segment.Type <= PT_GNU_MBIND_HI + ))) + /* Any section besides one of type SHT_NOBITS must have file + offsets within the segment. */ + && (section.Type == ElfSectionType.NoBits + || ((section).Position >= segment.Position - && ((section.Position - segment.Position - + GetSectionSize(section, segment)) + && (!(isStrict) - <= segment.Size))) - /* SHF_ALLOC sections must have VMAs within the segment. */ - && (!(checkVirtualAddress) - || (section.Flags & ElfSectionFlags.Alloc) == 0 - || (section.VirtualAddress >= segment.VirtualAddress + || (section.Position - segment.Position - && (!(isStrict) + <= segment.Size - 1)) - || (section.VirtualAddress - segment.VirtualAddress + && ((section.Position - segment.Position + + GetSectionSize(section, segment)) - <= segment.SizeInMemory - 1)) + <= segment.Size))) + /* SHF_ALLOC sections must have VMAs within the segment. */ + && (!(checkVirtualAddress) + || (section.Flags & ElfSectionFlags.Alloc) == 0 + || (section.VirtualAddress >= segment.VirtualAddress - && ((section.VirtualAddress - segment.VirtualAddress - + GetSectionSize(section, segment)) + && (!(isStrict) - <= segment.SizeInMemory))) - /* No zero size sections at start or end of PT_DYNAMIC nor - PT_NOTE. */ - && ((segment.Type != ElfSegmentTypeCore.Dynamic + || (section.VirtualAddress - segment.VirtualAddress - && segment.Type != ElfSegmentTypeCore.Note) - || section.Size != 0 - || segment.SizeInMemory == 0 - || ((section.Type == ElfSectionType.NoBits + <= segment.SizeInMemory - 1)) - || (section.Position > segment.Position + && ((section.VirtualAddress - segment.VirtualAddress + + GetSectionSize(section, segment)) - && (section.Position - segment.Position + <= segment.SizeInMemory))) + /* No zero size sections at start or end of PT_DYNAMIC nor + PT_NOTE. */ + && ((segment.Type != ElfSegmentTypeCore.Dynamic - < segment.Size))) + && segment.Type != ElfSegmentTypeCore.Note) + || section.Size != 0 + || segment.SizeInMemory == 0 + || ((section.Type == ElfSectionType.NoBits - && ((section.Flags & ElfSectionFlags.Alloc) == 0 + || (section.Position > segment.Position - || (section.VirtualAddress > segment.VirtualAddress + && (section.Position - segment.Position - && (section.VirtualAddress - segment.VirtualAddress + < segment.Size))) - < segment.SizeInMemory))))); - } + && ((section.Flags & ElfSectionFlags.Alloc) == 0 - public static void PrintDynamicSections(ElfObjectFile elf, TextWriter writer) - { - writer.WriteLine(); - writer.WriteLine("There is no dynamic section in this file."); - // TODO - } + || (section.VirtualAddress > segment.VirtualAddress - private static string GetElfSegmentFlags(ElfSegmentFlags flags) - { - if (flags.Value == 0) return string.Empty; - - var builder = new StringBuilder(); - builder.Append((flags.Value & ElfNative.PF_R) != 0 ? 'R' : ' '); - builder.Append((flags.Value & ElfNative.PF_W) != 0 ? 'W' : ' '); - builder.Append((flags.Value & ElfNative.PF_X) != 0 ? 'E' : ' '); - // TODO: other flags - return builder.ToString(); - } + && (section.VirtualAddress - segment.VirtualAddress - public static string GetElfSegmentType(ElfSegmentType segmentType) - { - return segmentType.Value switch - { - ElfNative.PT_NULL => "NULL", - ElfNative.PT_LOAD => "LOAD", - ElfNative.PT_DYNAMIC => "DYNAMIC", - ElfNative.PT_INTERP => "INTERP", - ElfNative.PT_NOTE => "NOTE", - ElfNative.PT_SHLIB => "SHLIB", - ElfNative.PT_PHDR => "PHDR", - ElfNative.PT_TLS => "TLS", - ElfNative.PT_GNU_EH_FRAME => "GNU_EH_FRAME", - ElfNative.PT_GNU_STACK => "GNU_STACK", - ElfNative.PT_GNU_RELRO => "GNU_RELRO", - _ => $": {segmentType.Value:x}" - }; - } + < segment.SizeInMemory))))); + } + + public static void PrintDynamicSections(ElfObjectFile elf, TextWriter writer) + { + writer.WriteLine(); + writer.WriteLine("There is no dynamic section in this file."); + // TODO + } + + private static string GetElfSegmentFlags(ElfSegmentFlags flags) + { + if (flags.Value == 0) return string.Empty; + + var builder = new StringBuilder(); + builder.Append((flags.Value & ElfNative.PF_R) != 0 ? 'R' : ' '); + builder.Append((flags.Value & ElfNative.PF_W) != 0 ? 'W' : ' '); + builder.Append((flags.Value & ElfNative.PF_X) != 0 ? 'E' : ' '); + // TODO: other flags + return builder.ToString(); + } - private static string GetElfSectionFlags(ElfSectionFlags flags) + public static string GetElfSegmentType(ElfSegmentType segmentType) + { + return segmentType.Value switch { - if (flags == ElfSectionFlags.None) return string.Empty; - - var builder = new StringBuilder(); - if ((flags & ElfSectionFlags.Write) != 0) builder.Append('W'); - if ((flags & ElfSectionFlags.Alloc) != 0) builder.Append('A'); - if ((flags & ElfSectionFlags.Executable) != 0) builder.Append('X'); - if ((flags & ElfSectionFlags.Merge) != 0) builder.Append('M'); - if ((flags & ElfSectionFlags.Strings) != 0) builder.Append('S'); - if ((flags & ElfSectionFlags.InfoLink) != 0) builder.Append('I'); - if ((flags & ElfSectionFlags.LinkOrder) != 0) builder.Append('L'); - if ((flags & ElfSectionFlags.OsNonConforming) != 0) builder.Append('O'); - if ((flags & ElfSectionFlags.Group) != 0) builder.Append('G'); - if ((flags & ElfSectionFlags.Tls) != 0) builder.Append('T'); - if ((flags & ElfSectionFlags.Compressed) != 0) builder.Append('C'); - - // TODO: unknown, OS specific, Exclude...etc. - return builder.ToString(); - } + ElfNative.PT_NULL => "NULL", + ElfNative.PT_LOAD => "LOAD", + ElfNative.PT_DYNAMIC => "DYNAMIC", + ElfNative.PT_INTERP => "INTERP", + ElfNative.PT_NOTE => "NOTE", + ElfNative.PT_SHLIB => "SHLIB", + ElfNative.PT_PHDR => "PHDR", + ElfNative.PT_TLS => "TLS", + ElfNative.PT_GNU_EH_FRAME => "GNU_EH_FRAME", + ElfNative.PT_GNU_STACK => "GNU_STACK", + ElfNative.PT_GNU_RELRO => "GNU_RELRO", + _ => $": {segmentType.Value:x}" + }; + } - private static string GetElfSectionType(ElfSectionType sectionType) + private static string GetElfSectionFlags(ElfSectionFlags flags) + { + if (flags == ElfSectionFlags.None) return string.Empty; + + var builder = new StringBuilder(); + if ((flags & ElfSectionFlags.Write) != 0) builder.Append('W'); + if ((flags & ElfSectionFlags.Alloc) != 0) builder.Append('A'); + if ((flags & ElfSectionFlags.Executable) != 0) builder.Append('X'); + if ((flags & ElfSectionFlags.Merge) != 0) builder.Append('M'); + if ((flags & ElfSectionFlags.Strings) != 0) builder.Append('S'); + if ((flags & ElfSectionFlags.InfoLink) != 0) builder.Append('I'); + if ((flags & ElfSectionFlags.LinkOrder) != 0) builder.Append('L'); + if ((flags & ElfSectionFlags.OsNonConforming) != 0) builder.Append('O'); + if ((flags & ElfSectionFlags.Group) != 0) builder.Append('G'); + if ((flags & ElfSectionFlags.Tls) != 0) builder.Append('T'); + if ((flags & ElfSectionFlags.Compressed) != 0) builder.Append('C'); + + // TODO: unknown, OS specific, Exclude...etc. + return builder.ToString(); + } + + private static string GetElfSectionType(ElfSectionType sectionType) + { + switch (sectionType) { - switch (sectionType) - { - case ElfSectionType.Null: - return "NULL"; - case ElfSectionType.ProgBits: - return "PROGBITS"; - case ElfSectionType.SymbolTable: - return "SYMTAB"; - case ElfSectionType.StringTable: - return "STRTAB"; - case ElfSectionType.RelocationAddends: - return "RELA"; - case ElfSectionType.SymbolHashTable: - return "HASH"; - case ElfSectionType.DynamicLinking: - return "DYNAMIC"; - case ElfSectionType.Note: - return "NOTE"; - case ElfSectionType.NoBits: - return "NOBITS"; - case ElfSectionType.Relocation: - return "REL"; - case ElfSectionType.Shlib: - return "SHLIB"; - case ElfSectionType.DynamicLinkerSymbolTable: - return "DYNSYM"; - case ElfSectionType.InitArray: - return "INIT_ARRAY"; - case ElfSectionType.FiniArray: - return "FINI_ARRAY"; - case ElfSectionType.PreInitArray: - return "PREINIT_ARRAY"; - case ElfSectionType.Group: - return "GROUP"; - case ElfSectionType.SymbolTableSectionHeaderIndices: - return "SYMTAB SECTION INDICIES"; - case ElfSectionType.GnuHash: - return "GNU_HASH"; - case ElfSectionType.GnuLibList: - return "GNU_LIBLIST"; - case ElfSectionType.GnuAttributes: - return "GNU_ATTRIBUTES"; - case ElfSectionType.Checksum: - return "CHECKSUM"; - case ElfSectionType.GnuVersionDefinition: - case (ElfSectionType)0x6ffffffc: - return "VERDEF"; - case ElfSectionType.GnuVersionNeedsSection: - return "VERNEED"; - case ElfSectionType.GnuVersionSymbolTable: - case (ElfSectionType)0x6ffffff0: - return "VERSYM"; - case (ElfSectionType)0x7ffffffd: - return "AUXILIARY"; - case (ElfSectionType)0x7fffffff: - return "FILTER"; - default: - return $"{(uint)sectionType:x8}: "; - } + case ElfSectionType.Null: + return "NULL"; + case ElfSectionType.ProgBits: + return "PROGBITS"; + case ElfSectionType.SymbolTable: + return "SYMTAB"; + case ElfSectionType.StringTable: + return "STRTAB"; + case ElfSectionType.RelocationAddends: + return "RELA"; + case ElfSectionType.SymbolHashTable: + return "HASH"; + case ElfSectionType.DynamicLinking: + return "DYNAMIC"; + case ElfSectionType.Note: + return "NOTE"; + case ElfSectionType.NoBits: + return "NOBITS"; + case ElfSectionType.Relocation: + return "REL"; + case ElfSectionType.Shlib: + return "SHLIB"; + case ElfSectionType.DynamicLinkerSymbolTable: + return "DYNSYM"; + case ElfSectionType.InitArray: + return "INIT_ARRAY"; + case ElfSectionType.FiniArray: + return "FINI_ARRAY"; + case ElfSectionType.PreInitArray: + return "PREINIT_ARRAY"; + case ElfSectionType.Group: + return "GROUP"; + case ElfSectionType.SymbolTableSectionHeaderIndices: + return "SYMTAB SECTION INDICIES"; + case ElfSectionType.GnuHash: + return "GNU_HASH"; + case ElfSectionType.GnuLibList: + return "GNU_LIBLIST"; + case ElfSectionType.GnuAttributes: + return "GNU_ATTRIBUTES"; + case ElfSectionType.Checksum: + return "CHECKSUM"; + case ElfSectionType.GnuVersionDefinition: + case (ElfSectionType)0x6ffffffc: + return "VERDEF"; + case ElfSectionType.GnuVersionNeedsSection: + return "VERNEED"; + case ElfSectionType.GnuVersionSymbolTable: + case (ElfSectionType)0x6ffffff0: + return "VERSYM"; + case (ElfSectionType)0x7ffffffd: + return "AUXILIARY"; + case (ElfSectionType)0x7fffffff: + return "FILTER"; + default: + return $"{(uint)sectionType:x8}: "; } + } - private static string GetElfFileClass(ElfFileClass fileClass) + private static string GetElfFileClass(ElfFileClass fileClass) + { + switch (fileClass) { - switch (fileClass) - { - case ElfFileClass.None: - return "none"; - case ElfFileClass.Is32: - return "ELF32"; - case ElfFileClass.Is64: - return "ELF64"; - default: - return $""; - } + case ElfFileClass.None: + return "none"; + case ElfFileClass.Is32: + return "ELF32"; + case ElfFileClass.Is64: + return "ELF64"; + default: + return $""; } + } - private static string GetElfEncoding(ElfEncoding encoding) + private static string GetElfEncoding(ElfEncoding encoding) + { + return encoding switch { - return encoding switch - { - ElfEncoding.None => "none", - ElfEncoding.Lsb => "2's complement, little endian", - ElfEncoding.Msb => "2's complement, big endian", - _ => $"" - }; - } + ElfEncoding.None => "none", + ElfEncoding.Lsb => "2's complement, little endian", + ElfEncoding.Msb => "2's complement, big endian", + _ => $"" + }; + } - private static string GetElfVersion(byte version) + private static string GetElfVersion(byte version) + { + return version switch { - return version switch - { - ElfNative.EV_CURRENT => $"{version} (current)", - ElfNative.EV_NONE => "", - _ => $"{version} ", - }; - } + ElfNative.EV_CURRENT => $"{version} (current)", + ElfNative.EV_NONE => "", + _ => $"{version} ", + }; + } - private static string GetElfOsAbi(ElfOSABIEx osabi) + private static string GetElfOsAbi(ElfOSABIEx osabi) + { + return osabi.Value switch { - return osabi.Value switch - { - ElfOSABI.NONE => "UNIX - System V", - ElfOSABI.HPUX => "UNIX - HP-UX", - ElfOSABI.NETBSD => "UNIX - NetBSD", - ElfOSABI.GNU => "UNIX - GNU", - ElfOSABI.SOLARIS => "UNIX - Solaris", - ElfOSABI.AIX => "UNIX - AIX", - ElfOSABI.IRIX => "UNIX - IRIX", - ElfOSABI.FREEBSD => "UNIX - FreeBSD", - ElfOSABI.TRU64 => "UNIX - TRU64", - ElfOSABI.MODESTO => "Novell - Modesto", - ElfOSABI.OPENBSD => "UNIX - OpenBSD", - _ => $" "UNIX - System V", + ElfOSABI.HPUX => "UNIX - HP-UX", + ElfOSABI.NETBSD => "UNIX - NetBSD", + ElfOSABI.GNU => "UNIX - GNU", + ElfOSABI.SOLARIS => "UNIX - Solaris", + ElfOSABI.AIX => "UNIX - AIX", + ElfOSABI.IRIX => "UNIX - IRIX", + ElfOSABI.FREEBSD => "UNIX - FreeBSD", + ElfOSABI.TRU64 => "UNIX - TRU64", + ElfOSABI.MODESTO => "Novell - Modesto", + ElfOSABI.OPENBSD => "UNIX - OpenBSD", + _ => $"= ElfNative.ET_LOPROC && e_type <= ElfNative.ET_HIPROC) return $"Processor Specific: ({e_type:x})"; - else if (e_type >= ElfNative.ET_LOOS && e_type <= ElfNative.ET_HIOS) - return $"OS Specific: ({e_type:x})"; - else - return $": {e_type:x}"; - } + case ElfFileType.None: return "NONE (None)"; + case ElfFileType.Relocatable: return "REL (Relocatable file)"; + case ElfFileType.Executable: return "EXEC (Executable file)"; + case ElfFileType.Dynamic: return "DYN (Shared object file)"; + case ElfFileType.Core: return "CORE (Core file)"; + default: + var e_type = (ushort) fileType; + if (e_type >= ElfNative.ET_LOPROC && e_type <= ElfNative.ET_HIPROC) return $"Processor Specific: ({e_type:x})"; + else if (e_type >= ElfNative.ET_LOOS && e_type <= ElfNative.ET_HIOS) + return $"OS Specific: ({e_type:x})"; + else + return $": {e_type:x}"; } + } - static string GetElfArch(ElfArchEx arch) + static string GetElfArch(ElfArchEx arch) + { + switch ((ushort)arch.Value) { - switch ((ushort)arch.Value) - { - case ElfNative.EM_NONE: return "None"; - case ElfNative.EM_M32: return "WE32100"; - case ElfNative.EM_SPARC: return "Sparc"; - case ElfNative.EM_386: return "Intel 80386"; - case ElfNative.EM_68K: return "MC68000"; - case ElfNative.EM_88K: return "MC88000"; - case ElfNative.EM_860: return "Intel 80860"; - case ElfNative.EM_MIPS: return "MIPS R3000"; - case ElfNative.EM_S370: return "IBM System/370"; - case ElfNative.EM_MIPS_RS3_LE: return "MIPS R4000 big-endian"; - case ElfNative.EM_PARISC: return "HPPA"; - case ElfNative.EM_SPARC32PLUS: return "Sparc v8+"; - case ElfNative.EM_960: return "Intel 80960"; - case ElfNative.EM_PPC: return "PowerPC"; - case ElfNative.EM_PPC64: return "PowerPC64"; - case ElfNative.EM_S390: return "IBM S/390"; - case ElfNative.EM_V800: return "Renesas V850 (using RH850 ABI)"; - case ElfNative.EM_FR20: return "Fujitsu FR20"; - case ElfNative.EM_RH32: return "TRW RH32"; - case ElfNative.EM_ARM: return "ARM"; - case ElfNative.EM_SH: return "Renesas / SuperH SH"; - case ElfNative.EM_SPARCV9: return "Sparc v9"; - case ElfNative.EM_TRICORE: return "Siemens Tricore"; - case ElfNative.EM_ARC: return "ARC"; - case ElfNative.EM_H8_300: return "Renesas H8/300"; - case ElfNative.EM_H8_300H: return "Renesas H8/300H"; - case ElfNative.EM_H8S: return "Renesas H8S"; - case ElfNative.EM_H8_500: return "Renesas H8/500"; - case ElfNative.EM_IA_64: return "Intel IA-64"; - case ElfNative.EM_MIPS_X: return "Stanford MIPS-X"; - case ElfNative.EM_COLDFIRE: return "Motorola Coldfire"; - case ElfNative.EM_68HC12: return "Motorola MC68HC12 Microcontroller"; - case ElfNative.EM_MMA: return "Fujitsu Multimedia Accelerator"; - case ElfNative.EM_PCP: return "Siemens PCP"; - case ElfNative.EM_NCPU: return "Sony nCPU embedded RISC processor"; - case ElfNative.EM_NDR1: return "Denso NDR1 microprocesspr"; - case ElfNative.EM_STARCORE: return "Motorola Star*Core processor"; - case ElfNative.EM_ME16: return "Toyota ME16 processor"; - case ElfNative.EM_ST100: return "STMicroelectronics ST100 processor"; - case ElfNative.EM_TINYJ: return "Advanced Logic Corp. TinyJ embedded processor"; - case ElfNative.EM_X86_64: return "Advanced Micro Devices X86-64"; - case ElfNative.EM_PDSP: return "Sony DSP processor"; - case ElfNative.EM_FX66: return "Siemens FX66 microcontroller"; - case ElfNative.EM_ST9PLUS: return "STMicroelectronics ST9+ 8/16 bit microcontroller"; - case ElfNative.EM_ST7: return "STMicroelectronics ST7 8-bit microcontroller"; - case ElfNative.EM_68HC16: return "Motorola MC68HC16 Microcontroller"; - case ElfNative.EM_68HC11: return "Motorola MC68HC11 Microcontroller"; - case ElfNative.EM_68HC08: return "Motorola MC68HC08 Microcontroller"; - case ElfNative.EM_68HC05: return "Motorola MC68HC05 Microcontroller"; - case ElfNative.EM_SVX: return "Silicon Graphics SVx"; - case ElfNative.EM_ST19: return "STMicroelectronics ST19 8-bit microcontroller"; - case ElfNative.EM_VAX: return "Digital VAX"; - case ElfNative.EM_CRIS: return "Axis Communications 32-bit embedded processor"; - case ElfNative.EM_JAVELIN: return "Infineon Technologies 32-bit embedded cpu"; - case ElfNative.EM_FIREPATH: return "Element 14 64-bit DSP processor"; - case ElfNative.EM_ZSP: return "LSI Logic's 16-bit DSP processor"; - case ElfNative.EM_MMIX: return "Donald Knuth's educational 64-bit processor"; - case ElfNative.EM_HUANY: return "Harvard Universitys's machine-independent object format"; - case ElfNative.EM_PRISM: return "Vitesse Prism"; - case ElfNative.EM_AVR: return "Atmel AVR 8-bit microcontroller"; - case ElfNative.EM_FR30: return "Fujitsu FR30"; - case ElfNative.EM_D10V: return "d10v"; - case ElfNative.EM_D30V: return "d30v"; - case ElfNative.EM_V850: return "Renesas V850"; - case ElfNative.EM_M32R: return "Renesas M32R (formerly Mitsubishi M32r)"; - case ElfNative.EM_MN10300: return "mn10300"; - case ElfNative.EM_MN10200: return "mn10200"; - case ElfNative.EM_PJ: return "picoJava"; - case ElfNative.EM_XTENSA: return "Tensilica Xtensa Processor"; - default: - return $": 0x{arch.Value:x}"; - } + case ElfNative.EM_NONE: return "None"; + case ElfNative.EM_M32: return "WE32100"; + case ElfNative.EM_SPARC: return "Sparc"; + case ElfNative.EM_386: return "Intel 80386"; + case ElfNative.EM_68K: return "MC68000"; + case ElfNative.EM_88K: return "MC88000"; + case ElfNative.EM_860: return "Intel 80860"; + case ElfNative.EM_MIPS: return "MIPS R3000"; + case ElfNative.EM_S370: return "IBM System/370"; + case ElfNative.EM_MIPS_RS3_LE: return "MIPS R4000 big-endian"; + case ElfNative.EM_PARISC: return "HPPA"; + case ElfNative.EM_SPARC32PLUS: return "Sparc v8+"; + case ElfNative.EM_960: return "Intel 80960"; + case ElfNative.EM_PPC: return "PowerPC"; + case ElfNative.EM_PPC64: return "PowerPC64"; + case ElfNative.EM_S390: return "IBM S/390"; + case ElfNative.EM_V800: return "Renesas V850 (using RH850 ABI)"; + case ElfNative.EM_FR20: return "Fujitsu FR20"; + case ElfNative.EM_RH32: return "TRW RH32"; + case ElfNative.EM_ARM: return "ARM"; + case ElfNative.EM_SH: return "Renesas / SuperH SH"; + case ElfNative.EM_SPARCV9: return "Sparc v9"; + case ElfNative.EM_TRICORE: return "Siemens Tricore"; + case ElfNative.EM_ARC: return "ARC"; + case ElfNative.EM_H8_300: return "Renesas H8/300"; + case ElfNative.EM_H8_300H: return "Renesas H8/300H"; + case ElfNative.EM_H8S: return "Renesas H8S"; + case ElfNative.EM_H8_500: return "Renesas H8/500"; + case ElfNative.EM_IA_64: return "Intel IA-64"; + case ElfNative.EM_MIPS_X: return "Stanford MIPS-X"; + case ElfNative.EM_COLDFIRE: return "Motorola Coldfire"; + case ElfNative.EM_68HC12: return "Motorola MC68HC12 Microcontroller"; + case ElfNative.EM_MMA: return "Fujitsu Multimedia Accelerator"; + case ElfNative.EM_PCP: return "Siemens PCP"; + case ElfNative.EM_NCPU: return "Sony nCPU embedded RISC processor"; + case ElfNative.EM_NDR1: return "Denso NDR1 microprocesspr"; + case ElfNative.EM_STARCORE: return "Motorola Star*Core processor"; + case ElfNative.EM_ME16: return "Toyota ME16 processor"; + case ElfNative.EM_ST100: return "STMicroelectronics ST100 processor"; + case ElfNative.EM_TINYJ: return "Advanced Logic Corp. TinyJ embedded processor"; + case ElfNative.EM_X86_64: return "Advanced Micro Devices X86-64"; + case ElfNative.EM_PDSP: return "Sony DSP processor"; + case ElfNative.EM_FX66: return "Siemens FX66 microcontroller"; + case ElfNative.EM_ST9PLUS: return "STMicroelectronics ST9+ 8/16 bit microcontroller"; + case ElfNative.EM_ST7: return "STMicroelectronics ST7 8-bit microcontroller"; + case ElfNative.EM_68HC16: return "Motorola MC68HC16 Microcontroller"; + case ElfNative.EM_68HC11: return "Motorola MC68HC11 Microcontroller"; + case ElfNative.EM_68HC08: return "Motorola MC68HC08 Microcontroller"; + case ElfNative.EM_68HC05: return "Motorola MC68HC05 Microcontroller"; + case ElfNative.EM_SVX: return "Silicon Graphics SVx"; + case ElfNative.EM_ST19: return "STMicroelectronics ST19 8-bit microcontroller"; + case ElfNative.EM_VAX: return "Digital VAX"; + case ElfNative.EM_CRIS: return "Axis Communications 32-bit embedded processor"; + case ElfNative.EM_JAVELIN: return "Infineon Technologies 32-bit embedded cpu"; + case ElfNative.EM_FIREPATH: return "Element 14 64-bit DSP processor"; + case ElfNative.EM_ZSP: return "LSI Logic's 16-bit DSP processor"; + case ElfNative.EM_MMIX: return "Donald Knuth's educational 64-bit processor"; + case ElfNative.EM_HUANY: return "Harvard Universitys's machine-independent object format"; + case ElfNative.EM_PRISM: return "Vitesse Prism"; + case ElfNative.EM_AVR: return "Atmel AVR 8-bit microcontroller"; + case ElfNative.EM_FR30: return "Fujitsu FR30"; + case ElfNative.EM_D10V: return "d10v"; + case ElfNative.EM_D30V: return "d30v"; + case ElfNative.EM_V850: return "Renesas V850"; + case ElfNative.EM_M32R: return "Renesas M32R (formerly Mitsubishi M32r)"; + case ElfNative.EM_MN10300: return "mn10300"; + case ElfNative.EM_MN10200: return "mn10200"; + case ElfNative.EM_PJ: return "picoJava"; + case ElfNative.EM_XTENSA: return "Tensilica Xtensa Processor"; + default: + return $": 0x{arch.Value:x}"; } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfReader.cs b/src/LibObjectFile/Elf/ElfReader.cs index a548def..191d701 100644 --- a/src/LibObjectFile/Elf/ElfReader.cs +++ b/src/LibObjectFile/Elf/ElfReader.cs @@ -6,55 +6,53 @@ using System.Buffers; using System.IO; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Base class for reading and building an from a . +/// +public abstract class ElfReader : ObjectFileReaderWriter, IElfDecoder { + private protected ElfReader(ElfObjectFile objectFile, Stream stream, ElfReaderOptions readerOptions) : base(objectFile, stream) + { + Options = readerOptions; + } + + public ElfObjectFile ObjectFile => (ElfObjectFile)base.File; + /// - /// Base class for reading and building an from a . + /// Gets the used for reading the /// - public abstract class ElfReader : ObjectFileReaderWriter, IElfDecoder + public ElfReaderOptions Options { get; } + + public override bool IsReadOnly => Options.ReadOnly; + + internal abstract void Read(); + + public abstract ElfSectionLink ResolveLink(ElfSectionLink link, string errorMessageFormat); + + internal static ElfReader Create(ElfObjectFile objectFile, Stream stream, ElfReaderOptions options) { - private protected ElfReader(ElfObjectFile objectFile, Stream stream, ElfReaderOptions readerOptions) : base(stream) - { - ObjectFile = objectFile ?? throw new ArgumentNullException(nameof(objectFile)); - Options = readerOptions; - } - - private protected ElfObjectFile ObjectFile { get; } - - /// - /// Gets the used for reading the - /// - public ElfReaderOptions Options { get; } - - public override bool IsReadOnly => Options.ReadOnly; - - internal abstract void Read(); - - public abstract ElfSectionLink ResolveLink(ElfSectionLink link, string errorMessageFormat); - - internal static ElfReader Create(ElfObjectFile objectFile, Stream stream, ElfReaderOptions options) - { - var thisComputerEncoding = BitConverter.IsLittleEndian ? ElfEncoding.Lsb : ElfEncoding.Msb; - return objectFile.Encoding == thisComputerEncoding ? (ElfReader) new ElfReaderDirect(objectFile, stream, options) : new ElfReaderSwap(objectFile, stream, options); - } - - public abstract ushort Decode(ElfNative.Elf32_Half src); - public abstract ushort Decode(ElfNative.Elf64_Half src); - public abstract uint Decode(ElfNative.Elf32_Word src); - public abstract uint Decode(ElfNative.Elf64_Word src); - public abstract int Decode(ElfNative.Elf32_Sword src); - public abstract int Decode(ElfNative.Elf64_Sword src); - public abstract ulong Decode(ElfNative.Elf32_Xword src); - public abstract long Decode(ElfNative.Elf32_Sxword src); - public abstract ulong Decode(ElfNative.Elf64_Xword src); - public abstract long Decode(ElfNative.Elf64_Sxword src); - public abstract uint Decode(ElfNative.Elf32_Addr src); - public abstract ulong Decode(ElfNative.Elf64_Addr src); - public abstract uint Decode(ElfNative.Elf32_Off src); - public abstract ulong Decode(ElfNative.Elf64_Off src); - public abstract ushort Decode(ElfNative.Elf32_Section src); - public abstract ushort Decode(ElfNative.Elf64_Section src); - public abstract ushort Decode(ElfNative.Elf32_Versym src); - public abstract ushort Decode(ElfNative.Elf64_Versym src); + var thisComputerEncoding = BitConverter.IsLittleEndian ? ElfEncoding.Lsb : ElfEncoding.Msb; + return objectFile.Encoding == thisComputerEncoding ? (ElfReader) new ElfReaderDirect(objectFile, stream, options) : new ElfReaderSwap(objectFile, stream, options); } + + public abstract ushort Decode(ElfNative.Elf32_Half src); + public abstract ushort Decode(ElfNative.Elf64_Half src); + public abstract uint Decode(ElfNative.Elf32_Word src); + public abstract uint Decode(ElfNative.Elf64_Word src); + public abstract int Decode(ElfNative.Elf32_Sword src); + public abstract int Decode(ElfNative.Elf64_Sword src); + public abstract ulong Decode(ElfNative.Elf32_Xword src); + public abstract long Decode(ElfNative.Elf32_Sxword src); + public abstract ulong Decode(ElfNative.Elf64_Xword src); + public abstract long Decode(ElfNative.Elf64_Sxword src); + public abstract uint Decode(ElfNative.Elf32_Addr src); + public abstract ulong Decode(ElfNative.Elf64_Addr src); + public abstract uint Decode(ElfNative.Elf32_Off src); + public abstract ulong Decode(ElfNative.Elf64_Off src); + public abstract ushort Decode(ElfNative.Elf32_Section src); + public abstract ushort Decode(ElfNative.Elf64_Section src); + public abstract ushort Decode(ElfNative.Elf32_Versym src); + public abstract ushort Decode(ElfNative.Elf64_Versym src); } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfReaderDirect.cs b/src/LibObjectFile/Elf/ElfReaderDirect.cs index a66822f..768542b 100644 --- a/src/LibObjectFile/Elf/ElfReaderDirect.cs +++ b/src/LibObjectFile/Elf/ElfReaderDirect.cs @@ -4,15 +4,14 @@ using System.IO; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Internal implementation of with a . +/// +internal sealed class ElfReaderDirect : ElfReader { - /// - /// Internal implementation of with a . - /// - internal sealed class ElfReaderDirect : ElfReader + public ElfReaderDirect(ElfObjectFile elfObjectFile, Stream stream, ElfReaderOptions options) : base(elfObjectFile, stream, options) { - public ElfReaderDirect(ElfObjectFile elfObjectFile, Stream stream, ElfReaderOptions options) : base(elfObjectFile, stream, options) - { - } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfReaderOptions.cs b/src/LibObjectFile/Elf/ElfReaderOptions.cs index a8276c4..0bcf4f4 100644 --- a/src/LibObjectFile/Elf/ElfReaderOptions.cs +++ b/src/LibObjectFile/Elf/ElfReaderOptions.cs @@ -2,46 +2,45 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Options used by and +/// +public class ElfReaderOptions { /// - /// Options used by and + /// Gets or sets a boolean indicating if the stream can be used in read-only mode, or false the resulting + /// will be modified. /// - public class ElfReaderOptions - { - /// - /// Gets or sets a boolean indicating if the stream can be used in read-only mode, or false the resulting - /// will be modified. - /// - public bool ReadOnly { get; set; } + public bool ReadOnly { get; set; } - /// - /// Gets or sets a delegate that can be used to replace the creation of when - /// reading from a Stream. - /// - public TryCreateNoteDelegate? TryCreateNote { get; set; } + /// + /// Gets or sets a delegate that can be used to replace the creation of when + /// reading from a Stream. + /// + public TryCreateNoteDelegate? TryCreateNote { get; set; } - /// - /// Gets or sets a delegate that can be used to replace the creation of when - /// reading from a Stream. - /// - public TryCreateSectionDelegate? TryCreateSection { get; set; } + /// + /// Gets or sets a delegate that can be used to replace the creation of when + /// reading from a Stream. + /// + public TryCreateSectionDelegate? TryCreateSection { get; set; } - /// - /// Tries to create a section instance from the specified type. Might return null. - /// - /// Type of the section to create. - /// The diagnostics - /// null if the section is not supported or an instance of for the specified type. - public delegate ElfSection TryCreateSectionDelegate(ElfSectionType sectionType, DiagnosticBag diagnostics); + /// + /// Tries to create a section instance from the specified type. Might return null. + /// + /// Type of the section to create. + /// The diagnostics + /// null if the section is not supported or an instance of for the specified type. + public delegate ElfSection TryCreateSectionDelegate(ElfSectionType sectionType, DiagnosticBag diagnostics); - /// - /// Tries to create a note instance from the specified name and type. Might return null. - /// - /// Name of the note. - /// Type of the note - /// null if the note is not supported or an instance of for the specified name and type. - public delegate ElfNote TryCreateNoteDelegate(string noteName, ElfNoteType noteType); + /// + /// Tries to create a note instance from the specified name and type. Might return null. + /// + /// Name of the note. + /// Type of the note + /// null if the note is not supported or an instance of for the specified name and type. + public delegate ElfNote TryCreateNoteDelegate(string noteName, ElfNoteType noteType); - } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfReaderSwap.cs b/src/LibObjectFile/Elf/ElfReaderSwap.cs index 71cb8a3..0136cb0 100644 --- a/src/LibObjectFile/Elf/ElfReaderSwap.cs +++ b/src/LibObjectFile/Elf/ElfReaderSwap.cs @@ -4,15 +4,14 @@ using System.IO; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Internal implementation of with a . +/// +internal sealed class ElfReaderSwap : ElfReader { - /// - /// Internal implementation of with a . - /// - internal sealed class ElfReaderSwap : ElfReader + public ElfReaderSwap(ElfObjectFile elfObjectFile, Stream stream, ElfReaderOptions options) : base(elfObjectFile, stream, options) { - public ElfReaderSwap(ElfObjectFile elfObjectFile, Stream stream, ElfReaderOptions options) : base(elfObjectFile, stream, options) - { - } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs index b7160f6..94608b9 100644 --- a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs +++ b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs @@ -7,853 +7,854 @@ using System.IO; using static System.Collections.Specialized.BitVector32; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Internal implementation of to read from a stream to an instance. +/// +/// The decoder used for LSB/MSB conversion +internal abstract class ElfReader : ElfReader where TDecoder : struct, IElfDecoder { - /// - /// Internal implementation of to read from a stream to an instance. - /// - /// The decoder used for LSB/MSB conversion - internal abstract class ElfReader : ElfReader where TDecoder : struct, IElfDecoder + private TDecoder _decoder; + private ulong _startOfFile; + private ushort _programHeaderCount; + private uint _sectionHeaderCount; + private uint _sectionStringTableIndex; + private bool _isFirstSectionValidNull; + private bool _hasValidSectionStringTable; + + protected ElfReader(ElfObjectFile objectFile, Stream stream, ElfReaderOptions options) : base(objectFile, stream, options) { - private TDecoder _decoder; - private ulong _startOfFile; - private ushort _programHeaderCount; - private uint _sectionHeaderCount; - private uint _sectionStringTableIndex; - private bool _isFirstSectionValidNull; - private bool _hasValidSectionStringTable; + _decoder = new TDecoder(); + } - protected ElfReader(ElfObjectFile objectFile, Stream stream, ElfReaderOptions options) : base(objectFile, stream, options) + private ElfObjectFile.ElfObjectLayout Layout => ObjectFile.Layout; + + internal override void Read() + { + if (ObjectFile.FileClass == ElfFileClass.None) { - _decoder = new TDecoder(); + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidHeaderFileClassNone, "Cannot read an ELF Class = None"); + throw new ObjectFileException($"Invalid {nameof(ElfObjectFile)}", Diagnostics); } - private ElfObjectFile.ElfObjectLayout Layout => ObjectFile.Layout; + _startOfFile = (ulong)Stream.Position; + ReadElfHeader(); + ReadProgramHeaders(); + ReadSections(); - internal override void Read() + VerifyAndFixProgramHeadersAndSections(); + } + + private void ReadElfHeader() + { + if (ObjectFile.FileClass == ElfFileClass.Is32) { - if (ObjectFile.FileClass == ElfFileClass.None) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidHeaderFileClassNone, "Cannot read an ELF Class = None"); - throw new ObjectFileException($"Invalid {nameof(ElfObjectFile)}", Diagnostics); - } - - _startOfFile = (ulong)Stream.Position; - ReadElfHeader(); - ReadProgramHeaders(); - ReadSections(); - - VerifyAndFixProgramHeadersAndSections(); + ReadElfHeader32(); } - - private void ReadElfHeader() + else { - if (ObjectFile.FileClass == ElfFileClass.Is32) - { - ReadElfHeader32(); - } - else - { - ReadElfHeader64(); - } + ReadElfHeader64(); + } - if (_sectionHeaderCount >= ElfNative.SHN_LORESERVE) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionHeaderCount, $"Invalid number `{_sectionHeaderCount}` of section headers found from Elf Header. Must be < {ElfNative.SHN_LORESERVE}"); - } + if (_sectionHeaderCount >= ElfNative.SHN_LORESERVE) + { + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionHeaderCount, $"Invalid number `{_sectionHeaderCount}` of section headers found from Elf Header. Must be < {ElfNative.SHN_LORESERVE}"); } + } - private unsafe void ReadElfHeader32() + private unsafe void ReadElfHeader32() + { + ElfNative.Elf32_Ehdr hdr; + ulong streamOffset = (ulong)Stream.Position; + if (!TryReadData(sizeof(ElfNative.Elf32_Ehdr), out hdr)) { - ElfNative.Elf32_Ehdr hdr; - ulong streamOffset = (ulong)Stream.Position; - if (!TryReadData(sizeof(ElfNative.Elf32_Ehdr), out hdr)) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteHeader32Size, $"Unable to read entirely Elf header. Not enough data (size: {sizeof(ElfNative.Elf32_Ehdr)}) read at offset {streamOffset} from the stream"); - } + Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteHeader32Size, $"Unable to read entirely Elf header. Not enough data (size: {sizeof(ElfNative.Elf32_Ehdr)}) read at offset {streamOffset} from the stream"); + } - ObjectFile.FileType = (ElfFileType)_decoder.Decode(hdr.e_type); - ObjectFile.Arch = new ElfArchEx(_decoder.Decode(hdr.e_machine)); - ObjectFile.Version = _decoder.Decode(hdr.e_version); + ObjectFile.FileType = (ElfFileType)_decoder.Decode(hdr.e_type); + ObjectFile.Arch = new ElfArchEx(_decoder.Decode(hdr.e_machine)); + ObjectFile.Version = _decoder.Decode(hdr.e_version); - ObjectFile.EntryPointAddress = _decoder.Decode(hdr.e_entry); - Layout.SizeOfElfHeader = _decoder.Decode(hdr.e_ehsize); - ObjectFile.Flags = _decoder.Decode(hdr.e_flags); + ObjectFile.EntryPointAddress = _decoder.Decode(hdr.e_entry); + Layout.SizeOfElfHeader = _decoder.Decode(hdr.e_ehsize); + ObjectFile.Flags = _decoder.Decode(hdr.e_flags); - // program headers - Layout.OffsetOfProgramHeaderTable = _decoder.Decode(hdr.e_phoff); - Layout.SizeOfProgramHeaderEntry = _decoder.Decode(hdr.e_phentsize); - _programHeaderCount = _decoder.Decode(hdr.e_phnum); + // program headers + Layout.OffsetOfProgramHeaderTable = _decoder.Decode(hdr.e_phoff); + Layout.SizeOfProgramHeaderEntry = _decoder.Decode(hdr.e_phentsize); + _programHeaderCount = _decoder.Decode(hdr.e_phnum); - // entries for sections - Layout.OffsetOfSectionHeaderTable = _decoder.Decode(hdr.e_shoff); - Layout.SizeOfSectionHeaderEntry = _decoder.Decode(hdr.e_shentsize); - _sectionHeaderCount = _decoder.Decode(hdr.e_shnum); - _sectionStringTableIndex = _decoder.Decode(hdr.e_shstrndx); - } + // entries for sections + Layout.OffsetOfSectionHeaderTable = _decoder.Decode(hdr.e_shoff); + Layout.SizeOfSectionHeaderEntry = _decoder.Decode(hdr.e_shentsize); + _sectionHeaderCount = _decoder.Decode(hdr.e_shnum); + _sectionStringTableIndex = _decoder.Decode(hdr.e_shstrndx); + } - private unsafe void ReadElfHeader64() + private unsafe void ReadElfHeader64() + { + ElfNative.Elf64_Ehdr hdr; + ulong streamOffset = (ulong)Stream.Position; + if (!TryReadData(sizeof(ElfNative.Elf64_Ehdr), out hdr)) { - ElfNative.Elf64_Ehdr hdr; - ulong streamOffset = (ulong)Stream.Position; - if (!TryReadData(sizeof(ElfNative.Elf64_Ehdr), out hdr)) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteHeader64Size, $"Unable to read entirely Elf header. Not enough data (size: {sizeof(ElfNative.Elf64_Ehdr)}) read at offset {streamOffset} from the stream"); - } + Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteHeader64Size, $"Unable to read entirely Elf header. Not enough data (size: {sizeof(ElfNative.Elf64_Ehdr)}) read at offset {streamOffset} from the stream"); + } - ObjectFile.FileType = (ElfFileType)_decoder.Decode(hdr.e_type); - ObjectFile.Arch = new ElfArchEx(_decoder.Decode(hdr.e_machine)); - ObjectFile.Version = _decoder.Decode(hdr.e_version); + ObjectFile.FileType = (ElfFileType)_decoder.Decode(hdr.e_type); + ObjectFile.Arch = new ElfArchEx(_decoder.Decode(hdr.e_machine)); + ObjectFile.Version = _decoder.Decode(hdr.e_version); - ObjectFile.EntryPointAddress = _decoder.Decode(hdr.e_entry); - Layout.SizeOfElfHeader = _decoder.Decode(hdr.e_ehsize); - ObjectFile.Flags = _decoder.Decode(hdr.e_flags); + ObjectFile.EntryPointAddress = _decoder.Decode(hdr.e_entry); + Layout.SizeOfElfHeader = _decoder.Decode(hdr.e_ehsize); + ObjectFile.Flags = _decoder.Decode(hdr.e_flags); - // program headers - Layout.OffsetOfProgramHeaderTable = _decoder.Decode(hdr.e_phoff); - Layout.SizeOfProgramHeaderEntry = _decoder.Decode(hdr.e_phentsize); - _programHeaderCount = _decoder.Decode(hdr.e_phnum); + // program headers + Layout.OffsetOfProgramHeaderTable = _decoder.Decode(hdr.e_phoff); + Layout.SizeOfProgramHeaderEntry = _decoder.Decode(hdr.e_phentsize); + _programHeaderCount = _decoder.Decode(hdr.e_phnum); - // entries for sections - Layout.OffsetOfSectionHeaderTable = _decoder.Decode(hdr.e_shoff); - Layout.SizeOfSectionHeaderEntry = _decoder.Decode(hdr.e_shentsize); - _sectionHeaderCount = _decoder.Decode(hdr.e_shnum); - _sectionStringTableIndex = _decoder.Decode(hdr.e_shstrndx); - } + // entries for sections + Layout.OffsetOfSectionHeaderTable = _decoder.Decode(hdr.e_shoff); + Layout.SizeOfSectionHeaderEntry = _decoder.Decode(hdr.e_shentsize); + _sectionHeaderCount = _decoder.Decode(hdr.e_shnum); + _sectionStringTableIndex = _decoder.Decode(hdr.e_shstrndx); + } - private void ReadProgramHeaders() + private void ReadProgramHeaders() + { + if (Layout.SizeOfProgramHeaderEntry == 0) { - if (Layout.SizeOfProgramHeaderEntry == 0) + if (_programHeaderCount > 0) { - if (_programHeaderCount > 0) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidZeroProgramHeaderTableEntrySize, $"Unable to read program header table as the size of program header entry ({nameof(ElfNative.Elf32_Ehdr.e_phentsize)}) == 0 in the Elf Header"); - } - return; + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidZeroProgramHeaderTableEntrySize, $"Unable to read program header table as the size of program header entry ({nameof(ElfNative.Elf32_Ehdr.e_phentsize)}) == 0 in the Elf Header"); } + return; + } - for (int i = 0; i < _programHeaderCount; i++) - { - var offset = Layout.OffsetOfProgramHeaderTable + (ulong)i * Layout.SizeOfProgramHeaderEntry; + for (int i = 0; i < _programHeaderCount; i++) + { + var offset = Layout.OffsetOfProgramHeaderTable + (ulong)i * Layout.SizeOfProgramHeaderEntry; - if (offset >= (ulong)Stream.Length) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidProgramHeaderStreamOffset, $"Unable to read program header [{i}] as its offset {offset} is out of bounds"); - break; - } + if (offset >= (ulong)Stream.Length) + { + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidProgramHeaderStreamOffset, $"Unable to read program header [{i}] as its offset {offset} is out of bounds"); + break; + } - // Seek to the header position - Stream.Position = (long)offset; + // Seek to the header position + Stream.Position = (long)offset; - var segment = (ObjectFile.FileClass == ElfFileClass.Is32) ? ReadProgramHeader32(i) : ReadProgramHeader64(i); - ObjectFile.AddSegment(segment); - } + var segment = (ObjectFile.FileClass == ElfFileClass.Is32) ? ReadProgramHeader32(i) : ReadProgramHeader64(i); + ObjectFile.AddSegment(segment); } + } - private ElfSegment ReadProgramHeader32(int phdrIndex) + private ElfSegment ReadProgramHeader32(int phdrIndex) + { + var streamOffset = Stream.Position; + if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf32_Phdr hdr)) { - var streamOffset = Stream.Position; - if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf32_Phdr hdr)) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteProgramHeader32Size, $"Unable to read entirely program header [{phdrIndex}]. Not enough data (size: {Layout.SizeOfProgramHeaderEntry}) read at offset {streamOffset} from the stream"); - } - - return new ElfSegment - { - Type = new ElfSegmentType(_decoder.Decode(hdr.p_type)), - Position =_decoder.Decode(hdr.p_offset), - VirtualAddress = _decoder.Decode(hdr.p_vaddr), - PhysicalAddress = _decoder.Decode(hdr.p_paddr), - Size = _decoder.Decode(hdr.p_filesz), - SizeInMemory = _decoder.Decode(hdr.p_memsz), - Flags = new ElfSegmentFlags(_decoder.Decode(hdr.p_flags)), - Alignment = _decoder.Decode(hdr.p_align) - }; + Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteProgramHeader32Size, $"Unable to read entirely program header [{phdrIndex}]. Not enough data (size: {Layout.SizeOfProgramHeaderEntry}) read at offset {streamOffset} from the stream"); } - private ElfSegment ReadProgramHeader64(int phdrIndex) + return new ElfSegment { - var streamOffset = Stream.Position; - if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf64_Phdr hdr)) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteProgramHeader64Size, $"Unable to read entirely program header [{phdrIndex}]. Not enough data (size: {Layout.SizeOfProgramHeaderEntry}) read at offset {streamOffset} from the stream"); - } + Type = new ElfSegmentType(_decoder.Decode(hdr.p_type)), + Position =_decoder.Decode(hdr.p_offset), + VirtualAddress = _decoder.Decode(hdr.p_vaddr), + PhysicalAddress = _decoder.Decode(hdr.p_paddr), + Size = _decoder.Decode(hdr.p_filesz), + SizeInMemory = _decoder.Decode(hdr.p_memsz), + Flags = new ElfSegmentFlags(_decoder.Decode(hdr.p_flags)), + Alignment = _decoder.Decode(hdr.p_align) + }; + } - return new ElfSegment - { - Type = new ElfSegmentType(_decoder.Decode(hdr.p_type)), - Position = _decoder.Decode(hdr.p_offset), - VirtualAddress = _decoder.Decode(hdr.p_vaddr), - PhysicalAddress = _decoder.Decode(hdr.p_paddr), - Size = _decoder.Decode(hdr.p_filesz), - SizeInMemory = _decoder.Decode(hdr.p_memsz), - Flags = new ElfSegmentFlags(_decoder.Decode(hdr.p_flags)), - Alignment = _decoder.Decode(hdr.p_align) - }; - } - - private void ReadSections() + private ElfSegment ReadProgramHeader64(int phdrIndex) + { + var streamOffset = Stream.Position; + if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf64_Phdr hdr)) { - if (Layout.OffsetOfSectionHeaderTable == 0) return; - - // Write section header table - ReadSectionHeaderTable(); + Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteProgramHeader64Size, $"Unable to read entirely program header [{phdrIndex}]. Not enough data (size: {Layout.SizeOfProgramHeaderEntry}) read at offset {streamOffset} from the stream"); } - private void ReadSectionHeaderTable() + return new ElfSegment { - if (Layout.SizeOfSectionHeaderEntry == 0) - { - if (_sectionHeaderCount > 0) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidZeroSectionHeaderTableEntrySize, $"Unable to read section header table as the size of section header entry ({nameof(ElfNative.Elf32_Ehdr.e_ehsize)}) == 0 in the Elf Header"); - } - return; - } - - uint i = 0; + Type = new ElfSegmentType(_decoder.Decode(hdr.p_type)), + Position = _decoder.Decode(hdr.p_offset), + VirtualAddress = _decoder.Decode(hdr.p_vaddr), + PhysicalAddress = _decoder.Decode(hdr.p_paddr), + Size = _decoder.Decode(hdr.p_filesz), + SizeInMemory = _decoder.Decode(hdr.p_memsz), + Flags = new ElfSegmentFlags(_decoder.Decode(hdr.p_flags)), + Alignment = _decoder.Decode(hdr.p_align) + }; + } + + private void ReadSections() + { + if (Layout.OffsetOfSectionHeaderTable == 0) return; - if (_sectionHeaderCount == 0) - { - // We are dealing with an object file that has more than SHN_LORESERVE - // (0xff00) sections. It has to begin with a NULL section header where - // its Size contains the real number of sections, and Link optionally - // points to string table section if it's section index is too high. - if (ReadExtendedNullSectionTableEntry()) - { - i = 1; - ObjectFile.AddSection(new ElfNullSection()); - _isFirstSectionValidNull = true; - } - } + // Write section header table + ReadSectionHeaderTable(); + } - for (; i < _sectionHeaderCount; i++) + private void ReadSectionHeaderTable() + { + if (Layout.SizeOfSectionHeaderEntry == 0) + { + if (_sectionHeaderCount > 0) { - var offset = Layout.OffsetOfSectionHeaderTable + i * Layout.SizeOfSectionHeaderEntry; - - if (offset >= (ulong)Stream.Length) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionHeaderStreamOffset, $"Unable to read section [{i}] as its offset {offset} is out of bounds"); - break; - } - - // Seek to the header position - Stream.Position = (long)offset; - - var section = ReadSectionTableEntry(i); - ObjectFile.AddSection(section); + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidZeroSectionHeaderTableEntrySize, $"Unable to read section header table as the size of section header entry ({nameof(ElfNative.Elf32_Ehdr.e_ehsize)}) == 0 in the Elf Header"); } + return; } - private ElfSection ReadSectionTableEntry(uint sectionIndex) - { - return ObjectFile.FileClass == ElfFileClass.Is32 ? ReadSectionTableEntry32(sectionIndex) : ReadSectionTableEntry64(sectionIndex); - } + uint i = 0; - private ElfSection ReadSectionTableEntry32(uint sectionIndex) + if (_sectionHeaderCount == 0) { - var streamOffset = Stream.Position; - if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf32_Shdr rawSection)) + // We are dealing with an object file that has more than SHN_LORESERVE + // (0xff00) sections. It has to begin with a NULL section header where + // its Size contains the real number of sections, and Link optionally + // points to string table section if it's section index is too high. + if (ReadExtendedNullSectionTableEntry()) { - Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSectionHeader32Size, $"Unable to read entirely section header [{sectionIndex}]. Not enough data (size: {Layout.SizeOfSectionHeaderEntry}) read at offset {streamOffset} from the stream"); + i = 1; + ObjectFile.AddSection(new ElfNullSection()); + _isFirstSectionValidNull = true; } + } - if (sectionIndex == 0) - { - _isFirstSectionValidNull = rawSection.IsNull; - } - - var sectionType = (ElfSectionType)_decoder.Decode(rawSection.sh_type); - bool isValidNullSection = sectionIndex == 0 && rawSection.IsNull; - var section = CreateElfSection(sectionIndex, sectionType, isValidNullSection); + for (; i < _sectionHeaderCount; i++) + { + var offset = Layout.OffsetOfSectionHeaderTable + i * Layout.SizeOfSectionHeaderEntry; - if (!isValidNullSection) + if (offset >= (ulong)Stream.Length) { - section.Name = new ElfString(_decoder.Decode(rawSection.sh_name)); - section.Type = (ElfSectionType)_decoder.Decode(rawSection.sh_type); - section.Flags = (ElfSectionFlags)_decoder.Decode(rawSection.sh_flags); - section.VirtualAddress = _decoder.Decode(rawSection.sh_addr); - section.Position = _decoder.Decode(rawSection.sh_offset); - section.Alignment = _decoder.Decode(rawSection.sh_addralign); - section.Link = new ElfSectionLink(_decoder.Decode(rawSection.sh_link)); - section.Info = new ElfSectionLink(_decoder.Decode(rawSection.sh_info)); - section.Size = _decoder.Decode(rawSection.sh_size); - section.OriginalTableEntrySize = _decoder.Decode(rawSection.sh_entsize); + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionHeaderStreamOffset, $"Unable to read section [{i}] as its offset {offset} is out of bounds"); + break; } - return section; + // Seek to the header position + Stream.Position = (long)offset; + + var section = ReadSectionTableEntry(i); + ObjectFile.AddSection(section); } + } + + private ElfSection ReadSectionTableEntry(uint sectionIndex) + { + return ObjectFile.FileClass == ElfFileClass.Is32 ? ReadSectionTableEntry32(sectionIndex) : ReadSectionTableEntry64(sectionIndex); + } - private ElfSection ReadSectionTableEntry64(uint sectionIndex) + private ElfSection ReadSectionTableEntry32(uint sectionIndex) + { + var streamOffset = Stream.Position; + if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf32_Shdr rawSection)) { - var streamOffset = Stream.Position; - if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf64_Shdr rawSection)) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSectionHeader64Size, $"Unable to read entirely section header [{sectionIndex}]. Not enough data (size: {Layout.SizeOfSectionHeaderEntry}) read at offset {streamOffset} from the stream"); - } + Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSectionHeader32Size, $"Unable to read entirely section header [{sectionIndex}]. Not enough data (size: {Layout.SizeOfSectionHeaderEntry}) read at offset {streamOffset} from the stream"); + } - if (sectionIndex == 0) - { - _isFirstSectionValidNull = rawSection.IsNull; - } + if (sectionIndex == 0) + { + _isFirstSectionValidNull = rawSection.IsNull; + } + + var sectionType = (ElfSectionType)_decoder.Decode(rawSection.sh_type); + bool isValidNullSection = sectionIndex == 0 && rawSection.IsNull; + var section = CreateElfSection(sectionIndex, sectionType, isValidNullSection); - var sectionType = (ElfSectionType)_decoder.Decode(rawSection.sh_type); - bool isValidNullSection = sectionIndex == 0 && rawSection.IsNull; - var section = CreateElfSection(sectionIndex, sectionType, sectionIndex == 0 && rawSection.IsNull); + if (!isValidNullSection) + { + section.Name = new ElfString(_decoder.Decode(rawSection.sh_name)); + section.Type = (ElfSectionType)_decoder.Decode(rawSection.sh_type); + section.Flags = (ElfSectionFlags)_decoder.Decode(rawSection.sh_flags); + section.VirtualAddress = _decoder.Decode(rawSection.sh_addr); + section.Position = _decoder.Decode(rawSection.sh_offset); + section.Alignment = _decoder.Decode(rawSection.sh_addralign); + section.Link = new ElfSectionLink(_decoder.Decode(rawSection.sh_link)); + section.Info = new ElfSectionLink(_decoder.Decode(rawSection.sh_info)); + section.Size = _decoder.Decode(rawSection.sh_size); + section.OriginalTableEntrySize = _decoder.Decode(rawSection.sh_entsize); + } - if (!isValidNullSection) - { - section.Name = new ElfString(_decoder.Decode(rawSection.sh_name)); - section.Type = (ElfSectionType)_decoder.Decode(rawSection.sh_type); - section.Flags = (ElfSectionFlags)_decoder.Decode(rawSection.sh_flags); - section.VirtualAddress = _decoder.Decode(rawSection.sh_addr); - section.Position = _decoder.Decode(rawSection.sh_offset); - section.Alignment = _decoder.Decode(rawSection.sh_addralign); - section.Link = new ElfSectionLink(_decoder.Decode(rawSection.sh_link)); - section.Info = new ElfSectionLink(_decoder.Decode(rawSection.sh_info)); - section.Size = _decoder.Decode(rawSection.sh_size); - section.OriginalTableEntrySize = _decoder.Decode(rawSection.sh_entsize); - } + return section; + } - return section; + private ElfSection ReadSectionTableEntry64(uint sectionIndex) + { + var streamOffset = Stream.Position; + if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf64_Shdr rawSection)) + { + Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSectionHeader64Size, $"Unable to read entirely section header [{sectionIndex}]. Not enough data (size: {Layout.SizeOfSectionHeaderEntry}) read at offset {streamOffset} from the stream"); } - private bool ReadExtendedNullSectionTableEntry() + if (sectionIndex == 0) { - uint sh_type; - ulong sh_size; - uint sh_link; - bool isNull; + _isFirstSectionValidNull = rawSection.IsNull; + } - Stream.Position = (long)Layout.OffsetOfSectionHeaderTable; + var sectionType = (ElfSectionType)_decoder.Decode(rawSection.sh_type); + bool isValidNullSection = sectionIndex == 0 && rawSection.IsNull; + var section = CreateElfSection(sectionIndex, sectionType, sectionIndex == 0 && rawSection.IsNull); - if (ObjectFile.FileClass == ElfFileClass.Is32) - { - - if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf32_Shdr rawSection32)) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSectionHeader32Size, $"Unable to read entirely NULL section header. Not enough data (size: {Layout.SizeOfSectionHeaderEntry}) read at offset {Layout.OffsetOfSectionHeaderTable} from the stream"); - return false; - } + if (!isValidNullSection) + { + section.Name = new ElfString(_decoder.Decode(rawSection.sh_name)); + section.Type = (ElfSectionType)_decoder.Decode(rawSection.sh_type); + section.Flags = (ElfSectionFlags)_decoder.Decode(rawSection.sh_flags); + section.VirtualAddress = _decoder.Decode(rawSection.sh_addr); + section.Position = _decoder.Decode(rawSection.sh_offset); + section.Alignment = _decoder.Decode(rawSection.sh_addralign); + section.Link = new ElfSectionLink(_decoder.Decode(rawSection.sh_link)); + section.Info = new ElfSectionLink(_decoder.Decode(rawSection.sh_info)); + section.Size = _decoder.Decode(rawSection.sh_size); + section.OriginalTableEntrySize = _decoder.Decode(rawSection.sh_entsize); + } - sh_type = _decoder.Decode(rawSection32.sh_type); - sh_size = _decoder.Decode(rawSection32.sh_size); - sh_link = _decoder.Decode(rawSection32.sh_link); - rawSection32.sh_size = 0; - rawSection32.sh_link = 0; - isNull = rawSection32.IsNull; - } - else - { - if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf64_Shdr rawSection64)) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSectionHeader64Size, $"Unable to read entirely NULL section header. Not enough data (size: {Layout.SizeOfSectionHeaderEntry}) read at offset {Layout.OffsetOfSectionHeaderTable} from the stream"); - return false; - } + return section; + } - sh_type = _decoder.Decode(rawSection64.sh_type); - sh_size = _decoder.Decode(rawSection64.sh_size); - sh_link = _decoder.Decode(rawSection64.sh_link); - rawSection64.sh_size = 0; - rawSection64.sh_link = 0; - isNull = rawSection64.IsNull; - } + private bool ReadExtendedNullSectionTableEntry() + { + uint sh_type; + ulong sh_size; + uint sh_link; + bool isNull; + + Stream.Position = (long)Layout.OffsetOfSectionHeaderTable; - if (!isNull) + if (ObjectFile.FileClass == ElfFileClass.Is32) + { + + if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf32_Shdr rawSection32)) { - Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidFirstSectionExpectingUndefined, $"Invalid Section [0] {(ElfSectionType)sh_type}. Expecting {ElfNative.SHN_UNDEF}"); + Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSectionHeader32Size, $"Unable to read entirely NULL section header. Not enough data (size: {Layout.SizeOfSectionHeaderEntry}) read at offset {Layout.OffsetOfSectionHeaderTable} from the stream"); return false; } - if (sh_size >= uint.MaxValue) + sh_type = _decoder.Decode(rawSection32.sh_type); + sh_size = _decoder.Decode(rawSection32.sh_size); + sh_link = _decoder.Decode(rawSection32.sh_link); + rawSection32.sh_size = 0; + rawSection32.sh_link = 0; + isNull = rawSection32.IsNull; + } + else + { + if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf64_Shdr rawSection64)) { - Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionHeaderCount, $"Extended section count [{sh_size}] exceeds {uint.MaxValue}"); + Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSectionHeader64Size, $"Unable to read entirely NULL section header. Not enough data (size: {Layout.SizeOfSectionHeaderEntry}) read at offset {Layout.OffsetOfSectionHeaderTable} from the stream"); return false; } - _sectionHeaderCount = (uint)sh_size; - if (_sectionStringTableIndex == ElfNative.SHN_XINDEX) - { - _sectionStringTableIndex = sh_link; - } + sh_type = _decoder.Decode(rawSection64.sh_type); + sh_size = _decoder.Decode(rawSection64.sh_size); + sh_link = _decoder.Decode(rawSection64.sh_link); + rawSection64.sh_size = 0; + rawSection64.sh_link = 0; + isNull = rawSection64.IsNull; + } - return true; + if (!isNull) + { + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidFirstSectionExpectingUndefined, $"Invalid Section [0] {(ElfSectionType)sh_type}. Expecting {ElfNative.SHN_UNDEF}"); + return false; } - - public override ElfSectionLink ResolveLink(ElfSectionLink link, string errorMessageFormat) + + if (sh_size >= uint.MaxValue) { - if (errorMessageFormat == null) throw new ArgumentNullException(nameof(errorMessageFormat)); + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionHeaderCount, $"Extended section count [{sh_size}] exceeds {uint.MaxValue}"); + return false; + } - // Connect section Link instance - if (!link.IsEmpty) + _sectionHeaderCount = (uint)sh_size; + if (_sectionStringTableIndex == ElfNative.SHN_XINDEX) + { + _sectionStringTableIndex = sh_link; + } + + return true; + } + + public override ElfSectionLink ResolveLink(ElfSectionLink link, string errorMessageFormat) + { + if (errorMessageFormat == null) throw new ArgumentNullException(nameof(errorMessageFormat)); + + // Connect section Link instance + if (!link.IsEmpty) + { + if (link.SpecialIndex == _sectionStringTableIndex) + { + link = new ElfSectionLink(ObjectFile.SectionHeaderStringTable); + } + else { - if (link.SpecialIndex == _sectionStringTableIndex) + var sectionIndex = link.SpecialIndex; + + bool sectionFound = false; + if (sectionIndex < ObjectFile.Sections.Count && ObjectFile.Sections[(int)sectionIndex].SectionIndex == sectionIndex) { - link = new ElfSectionLink(ObjectFile.SectionHeaderStringTable); + link = new ElfSectionLink(ObjectFile.Sections[(int)sectionIndex]); + sectionFound = true; } else { - var sectionIndex = link.SpecialIndex; - - bool sectionFound = false; - if (sectionIndex < ObjectFile.Sections.Count && ObjectFile.Sections[(int)sectionIndex].SectionIndex == sectionIndex) + foreach (var section in ObjectFile.Sections) { - link = new ElfSectionLink(ObjectFile.Sections[(int)sectionIndex]); - sectionFound = true; - } - else - { - foreach (var section in ObjectFile.Sections) + if (section.SectionIndex == sectionIndex) { - if (section.SectionIndex == sectionIndex) - { - link = new ElfSectionLink(section); - sectionFound = true; - break; - } + link = new ElfSectionLink(section); + sectionFound = true; + break; } } + } - if (!sectionFound) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidResolvedLink, string.Format(errorMessageFormat, link.SpecialIndex)); - } + if (!sectionFound) + { + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidResolvedLink, string.Format(errorMessageFormat, link.SpecialIndex)); } } - - return link; } + + return link; + } - private void VerifyAndFixProgramHeadersAndSections() + private void VerifyAndFixProgramHeadersAndSections() + { + var context = new ElfVisitorContext(ObjectFile, Diagnostics); + + if (!_isFirstSectionValidNull && ObjectFile.Sections.Count > 0) { - if (!_isFirstSectionValidNull && ObjectFile.Sections.Count > 0) - { - Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidFirstSectionExpectingUndefined, $"Invalid Section [0] {ObjectFile.Sections[0].Type}. Expecting {ElfNative.SHN_UNDEF}"); - } + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidFirstSectionExpectingUndefined, $"Invalid Section [0] {ObjectFile.Sections[0].Type}. Expecting {ElfNative.SHN_UNDEF}"); + } + + if (_hasValidSectionStringTable) + { + Stream.Position = (long)ObjectFile.SectionHeaderStringTable!.Position; + ObjectFile.SectionHeaderStringTable.Read(this); + } - if (_hasValidSectionStringTable) + for (var i = 0; i < ObjectFile.Sections.Count; i++) + { + var section = ObjectFile.Sections[i]; + if (section is ElfNullSection || section is ElfProgramHeaderTable) continue; + + // Resolve the name of the section + if (ObjectFile.SectionHeaderStringTable != null && ObjectFile.SectionHeaderStringTable.TryFind(section.Name.Index, out var sectionName)) { - Stream.Position = (long)ObjectFile.SectionHeaderStringTable!.Position; - ObjectFile.SectionHeaderStringTable.ReadInternal(this); + section.Name = section.Name.WithName(sectionName); } - - for (var i = 0; i < ObjectFile.Sections.Count; i++) + else { - var section = ObjectFile.Sections[i]; - if (section is ElfNullSection || section is ElfProgramHeaderTable) continue; - - // Resolve the name of the section - if (ObjectFile.SectionHeaderStringTable != null && ObjectFile.SectionHeaderStringTable.TryFind(section.Name.Index, out var sectionName)) + if (ObjectFile.SectionHeaderStringTable == null) { - section.Name = section.Name.WithName(sectionName); + Diagnostics.Warning(DiagnosticId.ELF_ERR_InvalidStringIndexMissingStringHeaderTable, $"Unable to resolve string index [{section.Name.Index}] for section [{section.Index}] as section header string table does not exist"); } else { - if (ObjectFile.SectionHeaderStringTable == null) - { - Diagnostics.Warning(DiagnosticId.ELF_ERR_InvalidStringIndexMissingStringHeaderTable, $"Unable to resolve string index [{section.Name.Index}] for section [{section.Index}] as section header string table does not exist"); - } - else - { - Diagnostics.Warning(DiagnosticId.ELF_ERR_InvalidStringIndex, $"Unable to resolve string index [{section.Name.Index}] for section [{section.Index}] from section header string table"); - } + Diagnostics.Warning(DiagnosticId.ELF_ERR_InvalidStringIndex, $"Unable to resolve string index [{section.Name.Index}] for section [{section.Index}] from section header string table"); } + } - // Connect section Link instance - section.Link = ResolveLink(section.Link, $"Invalid section Link [{{0}}] for section [{i}]"); - - // Connect section Info instance - if (section.Type != ElfSectionType.DynamicLinkerSymbolTable && section.Type != ElfSectionType.SymbolTable && (section.Flags & ElfSectionFlags.InfoLink) != 0) - { - section.Info = ResolveLink(section.Info, $"Invalid section Info [{{0}}] for section [{i}]"); - } + // Connect section Link instance + section.Link = ResolveLink(section.Link, $"Invalid section Link [{{0}}] for section [{i}]"); - if (i == 0 && _isFirstSectionValidNull) - { - continue; - } + // Connect section Info instance + if (section.Type != ElfSectionType.DynamicLinkerSymbolTable && section.Type != ElfSectionType.SymbolTable && (section.Flags & ElfSectionFlags.InfoLink) != 0) + { + section.Info = ResolveLink(section.Info, $"Invalid section Info [{{0}}] for section [{i}]"); + } - if (i == _sectionStringTableIndex && _hasValidSectionStringTable) - { - continue; - } + if (i == 0 && _isFirstSectionValidNull) + { + continue; + } - if (section.HasContent) - { - Stream.Position = (long)section.Position; - section.ReadInternal(this); - } + if (i == _sectionStringTableIndex && _hasValidSectionStringTable) + { + continue; } - foreach (var section in ObjectFile.Sections) + if (section.HasContent) { - section.AfterReadInternal(this); + Stream.Position = (long)section.Position; + section.Read(this); } + } - var fileParts = new ElfFilePartList(ObjectFile.Sections.Count + ObjectFile.Segments.Count); + foreach (var section in ObjectFile.Sections) + { + section.AfterReadInternal(this); + } - if (_isFirstSectionValidNull) + var fileParts = new ElfFilePartList(ObjectFile.Sections.Count + ObjectFile.Segments.Count); + + if (_isFirstSectionValidNull) + { + var programHeaderTable = new ElfProgramHeaderTable() { - var programHeaderTable = new ElfProgramHeaderTable() - { - Position = Layout.OffsetOfProgramHeaderTable, - }; + Position = Layout.OffsetOfProgramHeaderTable, + }; + + // Add the shadow section ElfProgramHeaderTable + ObjectFile.InsertSectionAt(1, programHeaderTable); + programHeaderTable.UpdateLayout(context); - // Add the shadow section ElfProgramHeaderTable - ObjectFile.InsertSectionAt(1, programHeaderTable); - programHeaderTable.UpdateLayout(Diagnostics); + if (programHeaderTable.Size > 0) + { + fileParts.Insert(new ElfFilePart(programHeaderTable)); + } + } - if (programHeaderTable.Size > 0) + // Make sure to pre-sort all sections by offset + var orderedSections = new List(ObjectFile.Sections.Count); + orderedSections.AddRange(ObjectFile.Sections); + orderedSections.Sort(CompareSectionOffsetsAndSizesDelegate); + // Store the stream index to recover the same order when saving back. + for(int i = 0; i < orderedSections.Count; i++) + { + orderedSections[i].StreamIndex = (uint)i; + } + + // Lastly verify integrity of all sections + bool hasShadowSections = false; + + var lastOffset = fileParts.Count > 0 ? fileParts[fileParts.Count - 1].EndOffset : 0; + for (var i = 0; i < orderedSections.Count; i++) + { + var section = orderedSections[i]; + section.Verify(context); + + if (lastOffset > 0 && section.Position > lastOffset) + { + if (section.Position > lastOffset) { - fileParts.Insert(new ElfFilePart(programHeaderTable)); + // Create parts for the segment + fileParts.CreateParts(lastOffset + 1, section.Position - 1); + hasShadowSections = true; } } - // Make sure to pre-sort all sections by offset - var orderedSections = new List(ObjectFile.Sections.Count); - orderedSections.AddRange(ObjectFile.Sections); - orderedSections.Sort(CompareSectionOffsetsAndSizesDelegate); - // Store the stream index to recover the same order when saving back. - for(int i = 0; i < orderedSections.Count; i++) + if (section.Size == 0 || !section.HasContent) { - orderedSections[i].StreamIndex = (uint)i; + continue; } - // Lastly verify integrity of all sections - bool hasShadowSections = false; + // Collect sections parts + fileParts.Insert(new ElfFilePart(section)); + lastOffset = section.Position + section.Size - 1; - var lastOffset = fileParts.Count > 0 ? fileParts[fileParts.Count - 1].EndOffset : 0; - for (var i = 0; i < orderedSections.Count; i++) + // Verify overlapping sections and generate and error + if (i + 1 < orderedSections.Count) { - var section = orderedSections[i]; - section.Verify(this.Diagnostics); - - if (lastOffset > 0 && section.Position > lastOffset) + var otherSection = orderedSections[i + 1]; + if (otherSection.Position < section.Position + section.Size) { - if (section.Position > lastOffset) - { - // Create parts for the segment - fileParts.CreateParts(lastOffset + 1, section.Position - 1); - hasShadowSections = true; - } + Diagnostics.Warning(DiagnosticId.ELF_ERR_InvalidOverlappingSections, $"The section {section} [{section.Position} : {section.Position + section.Size - 1}] is overlapping with the section {otherSection} [{otherSection.Position} : {otherSection.Position + otherSection.Size - 1}]"); } + } + } + + // Link segments to sections if we have an exact match. + // otherwise record any segments that are not bound to a section. - if (section.Size == 0 || !section.HasContent) - { - continue; - } + foreach (var segment in ObjectFile.Segments) + { + if (segment.Size == 0) continue; - // Collect sections parts - fileParts.Insert(new ElfFilePart(section)); - lastOffset = section.Position + section.Size - 1; + var segmentEndOffset = segment.Position + segment.Size - 1; + foreach (var section in orderedSections) + { + if (section.Size == 0 || !section.HasContent) continue; - // Verify overlapping sections and generate and error - if (i + 1 < orderedSections.Count) + var sectionEndOffset = section.Position + section.Size - 1; + if (segment.Position == section.Position && segmentEndOffset == sectionEndOffset) { - var otherSection = orderedSections[i + 1]; - if (otherSection.Position < section.Position + section.Size) - { - Diagnostics.Warning(DiagnosticId.ELF_ERR_InvalidOverlappingSections, $"The section {section} [{section.Position} : {section.Position + section.Size - 1}] is overlapping with the section {otherSection} [{otherSection.Position} : {otherSection.Position + otherSection.Size - 1}]"); - } + // Single case: segment == section + // If we found a section, we will bind the program header to this section + // and switch the offset calculation to auto + segment.Range = section; + segment.OffsetKind = ValueKind.Auto; + break; } } - - // Link segments to sections if we have an exact match. - // otherwise record any segments that are not bound to a section. - foreach (var segment in ObjectFile.Segments) + if (segment.Range.IsEmpty) { - if (segment.Size == 0) continue; + var offset = segment.Position; - var segmentEndOffset = segment.Position + segment.Size - 1; - foreach (var section in orderedSections) + // If a segment offset is set to 0, we need to take into + // account the fact that the Elf header is already being handled + // so we should not try to create a shadow section for it + if (offset < Layout.SizeOfElfHeader) { - if (section.Size == 0 || !section.HasContent) continue; - - var sectionEndOffset = section.Position + section.Size - 1; - if (segment.Position == section.Position && segmentEndOffset == sectionEndOffset) - { - // Single case: segment == section - // If we found a section, we will bind the program header to this section - // and switch the offset calculation to auto - segment.Range = section; - segment.OffsetKind = ValueKind.Auto; - break; - } + offset = Layout.SizeOfElfHeader; } - - if (segment.Range.IsEmpty) - { - var offset = segment.Position; - - // If a segment offset is set to 0, we need to take into - // account the fact that the Elf header is already being handled - // so we should not try to create a shadow section for it - if (offset < Layout.SizeOfElfHeader) - { - offset = Layout.SizeOfElfHeader; - } - // Create parts for the segment - fileParts.CreateParts(offset, segmentEndOffset); - hasShadowSections = true; - } + // Create parts for the segment + fileParts.CreateParts(offset, segmentEndOffset); + hasShadowSections = true; } + } + + // If the previous loop has created ElfFilePart, we have to + // create ElfCustomShadowSection and update the ElfSegment.Range + if (hasShadowSections) + { + int shadowCount = 0; + // If we have sections and the first section is NULL valid, we can start inserting + // shadow sections at index 1 (after null section), otherwise we can insert shadow + // sections before. + uint previousSectionIndex = _isFirstSectionValidNull ? 1U : 0U; - // If the previous loop has created ElfFilePart, we have to - // create ElfCustomShadowSection and update the ElfSegment.Range - if (hasShadowSections) + // Create ElfCustomShadowSection for any parts in the file + // that are referenced by a segment but doesn't have a section + for (var i = 0; i < fileParts.Count; i++) { - int shadowCount = 0; - // If we have sections and the first section is NULL valid, we can start inserting - // shadow sections at index 1 (after null section), otherwise we can insert shadow - // sections before. - uint previousSectionIndex = _isFirstSectionValidNull ? 1U : 0U; - - // Create ElfCustomShadowSection for any parts in the file - // that are referenced by a segment but doesn't have a section - for (var i = 0; i < fileParts.Count; i++) + var part = fileParts[i]; + if (part.Section == null) { - var part = fileParts[i]; - if (part.Section == null) + var shadowSection = new ElfBinaryShadowSection() { - var shadowSection = new ElfBinaryShadowSection() - { - Name = ".shadow." + shadowCount, - Position = part.StartOffset, - Size = part.EndOffset - part.StartOffset + 1 - }; - shadowCount++; - - Stream.Position = (long)shadowSection.Position; - shadowSection.ReadInternal(this); - - // Insert the shadow section with this order - shadowSection.StreamIndex = previousSectionIndex; - for (int j = (int)previousSectionIndex; j < orderedSections.Count; j++) - { - var otherSection = orderedSections[j]; - otherSection.StreamIndex++; - } - // Update ordered sections - orderedSections.Insert((int)previousSectionIndex, shadowSection); - ObjectFile.AddSection(shadowSection); - - fileParts[i] = new ElfFilePart(shadowSection); - } - else + Name = ".shadow." + shadowCount, + Position = part.StartOffset, + Size = part.EndOffset - part.StartOffset + 1 + }; + shadowCount++; + + Stream.Position = (long)shadowSection.Position; + shadowSection.Read(this); + + // Insert the shadow section with this order + shadowSection.StreamIndex = previousSectionIndex; + for (int j = (int)previousSectionIndex; j < orderedSections.Count; j++) { - previousSectionIndex = part.Section.StreamIndex + 1; + var otherSection = orderedSections[j]; + otherSection.StreamIndex++; } + // Update ordered sections + orderedSections.Insert((int)previousSectionIndex, shadowSection); + ObjectFile.AddSection(shadowSection); + + fileParts[i] = new ElfFilePart(shadowSection); } + else + { + previousSectionIndex = part.Section.StreamIndex + 1; + } + } + + // Update all segment Ranges + foreach (var segment in ObjectFile.Segments) + { + if (segment.Size == 0) continue; + if (!segment.Range.IsEmpty) continue; - // Update all segment Ranges - foreach (var segment in ObjectFile.Segments) + var segmentEndOffset = segment.Position + segment.Size - 1; + for (var i = 0; i < orderedSections.Count; i++) { - if (segment.Size == 0) continue; - if (!segment.Range.IsEmpty) continue; + var section = orderedSections[i]; + if (section.Size == 0 || !section.HasContent) continue; - var segmentEndOffset = segment.Position + segment.Size - 1; - for (var i = 0; i < orderedSections.Count; i++) + var sectionEndOffset = section.Position + section.Size - 1; + if (segment.Position >= section.Position && segment.Position <= sectionEndOffset) { - var section = orderedSections[i]; - if (section.Size == 0 || !section.HasContent) continue; - - var sectionEndOffset = section.Position + section.Size - 1; - if (segment.Position >= section.Position && segment.Position <= sectionEndOffset) + ElfSection beginSection = section; + ElfSection? endSection = null; + for (int j = i; j < orderedSections.Count; j++) { - ElfSection beginSection = section; - ElfSection? endSection = null; - for (int j = i; j < orderedSections.Count; j++) - { - var nextSection = orderedSections[j]; - if (nextSection.Size == 0 || !nextSection.HasContent) continue; - - sectionEndOffset = nextSection.Position + nextSection.Size - 1; + var nextSection = orderedSections[j]; + if (nextSection.Size == 0 || !nextSection.HasContent) continue; - if (segmentEndOffset >= nextSection.Position && segmentEndOffset <= sectionEndOffset) - { - endSection = nextSection; - break; - } - } + sectionEndOffset = nextSection.Position + nextSection.Size - 1; - if (endSection == null) + if (segmentEndOffset >= nextSection.Position && segmentEndOffset <= sectionEndOffset) { - // TODO: is it a throw/assert or a log? - Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRange, $"Invalid range for {segment}. The range is set to empty"); - } - else - { - segment.Range = new ElfSegmentRange(beginSection, segment.Position - beginSection.Position, endSection, (long)(segmentEndOffset - endSection.Position)); + endSection = nextSection; + break; } + } - segment.OffsetKind = ValueKind.Auto; - break; + if (endSection == null) + { + // TODO: is it a throw/assert or a log? + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRange, $"Invalid range for {segment}. The range is set to empty"); } + else + { + segment.Range = new ElfSegmentRange(beginSection, segment.Position - beginSection.Position, endSection, (long)(segmentEndOffset - endSection.Position)); + } + + segment.OffsetKind = ValueKind.Auto; + break; } } } } + } - private ElfSection CreateElfSection(uint sectionIndex, ElfSectionType sectionType, bool isNullSection) + private ElfSection CreateElfSection(uint sectionIndex, ElfSectionType sectionType, bool isNullSection) + { + ElfSection? section = null; + + switch (sectionType) { - ElfSection? section = null; + case ElfSectionType.Null: + if (isNullSection) + { + section = new ElfNullSection(); + } + break; + case ElfSectionType.DynamicLinkerSymbolTable: + case ElfSectionType.SymbolTable: + section = new ElfSymbolTable(); + break; + case ElfSectionType.StringTable: + + if (sectionIndex == _sectionStringTableIndex) + { + _hasValidSectionStringTable = true; + section = new ElfSectionHeaderStringTable(); + } + else + { + section = new ElfStringTable(); + } + break; + case ElfSectionType.Relocation: + case ElfSectionType.RelocationAddends: + section = new ElfRelocationTable(); + break; + case ElfSectionType.Note: + section = new ElfNoteTable(); + break; + case ElfSectionType.SymbolTableSectionHeaderIndices: + section = new ElfSymbolTableSectionHeaderIndices(); + break; + } - switch (sectionType) + // If the section is not a builtin section, try to offload to a delegate + // or use the default ElfCustomSection. + if (section == null) + { + if (Options.TryCreateSection != null) { - case ElfSectionType.Null: - if (isNullSection) - { - section = new ElfNullSection(); - } - break; - case ElfSectionType.DynamicLinkerSymbolTable: - case ElfSectionType.SymbolTable: - section = new ElfSymbolTable(); - break; - case ElfSectionType.StringTable: - - if (sectionIndex == _sectionStringTableIndex) - { - _hasValidSectionStringTable = true; - section = new ElfSectionHeaderStringTable(); - } - else - { - section = new ElfStringTable(); - } - break; - case ElfSectionType.Relocation: - case ElfSectionType.RelocationAddends: - section = new ElfRelocationTable(); - break; - case ElfSectionType.Note: - section = new ElfNoteTable(); - break; - case ElfSectionType.SymbolTableSectionHeaderIndices: - section = new ElfSymbolTableSectionHeaderIndices(); - break; + section = Options.TryCreateSection(sectionType, Diagnostics); } - // If the section is not a builtin section, try to offload to a delegate - // or use the default ElfCustomSection. if (section == null) { - if (Options.TryCreateSection != null) - { - section = Options.TryCreateSection(sectionType, Diagnostics); - } - - if (section == null) - { - section = new ElfBinarySection(); - } + section = new ElfBinarySection(); } - - return section; } + + return section; + } - public override ushort Decode(ElfNative.Elf32_Half src) - { - return _decoder.Decode(src); - } + public override ushort Decode(ElfNative.Elf32_Half src) + { + return _decoder.Decode(src); + } - public override ushort Decode(ElfNative.Elf64_Half src) - { - return _decoder.Decode(src); - } + public override ushort Decode(ElfNative.Elf64_Half src) + { + return _decoder.Decode(src); + } - public override uint Decode(ElfNative.Elf32_Word src) - { - return _decoder.Decode(src); - } + public override uint Decode(ElfNative.Elf32_Word src) + { + return _decoder.Decode(src); + } - public override uint Decode(ElfNative.Elf64_Word src) - { - return _decoder.Decode(src); - } + public override uint Decode(ElfNative.Elf64_Word src) + { + return _decoder.Decode(src); + } - public override int Decode(ElfNative.Elf32_Sword src) - { - return _decoder.Decode(src); - } + public override int Decode(ElfNative.Elf32_Sword src) + { + return _decoder.Decode(src); + } - public override int Decode(ElfNative.Elf64_Sword src) - { - return _decoder.Decode(src); - } + public override int Decode(ElfNative.Elf64_Sword src) + { + return _decoder.Decode(src); + } - public override ulong Decode(ElfNative.Elf32_Xword src) - { - return _decoder.Decode(src); - } + public override ulong Decode(ElfNative.Elf32_Xword src) + { + return _decoder.Decode(src); + } - public override long Decode(ElfNative.Elf32_Sxword src) - { - return _decoder.Decode(src); - } + public override long Decode(ElfNative.Elf32_Sxword src) + { + return _decoder.Decode(src); + } - public override ulong Decode(ElfNative.Elf64_Xword src) - { - return _decoder.Decode(src); - } + public override ulong Decode(ElfNative.Elf64_Xword src) + { + return _decoder.Decode(src); + } - public override long Decode(ElfNative.Elf64_Sxword src) - { - return _decoder.Decode(src); - } + public override long Decode(ElfNative.Elf64_Sxword src) + { + return _decoder.Decode(src); + } - public override uint Decode(ElfNative.Elf32_Addr src) - { - return _decoder.Decode(src); - } + public override uint Decode(ElfNative.Elf32_Addr src) + { + return _decoder.Decode(src); + } - public override ulong Decode(ElfNative.Elf64_Addr src) - { - return _decoder.Decode(src); - } + public override ulong Decode(ElfNative.Elf64_Addr src) + { + return _decoder.Decode(src); + } - public override uint Decode(ElfNative.Elf32_Off src) - { - return _decoder.Decode(src); - } + public override uint Decode(ElfNative.Elf32_Off src) + { + return _decoder.Decode(src); + } - public override ulong Decode(ElfNative.Elf64_Off src) - { - return _decoder.Decode(src); - } + public override ulong Decode(ElfNative.Elf64_Off src) + { + return _decoder.Decode(src); + } - public override ushort Decode(ElfNative.Elf32_Section src) - { - return _decoder.Decode(src); - } + public override ushort Decode(ElfNative.Elf32_Section src) + { + return _decoder.Decode(src); + } - public override ushort Decode(ElfNative.Elf64_Section src) - { - return _decoder.Decode(src); - } + public override ushort Decode(ElfNative.Elf64_Section src) + { + return _decoder.Decode(src); + } - public override ushort Decode(ElfNative.Elf32_Versym src) - { - return _decoder.Decode(src); - } + public override ushort Decode(ElfNative.Elf32_Versym src) + { + return _decoder.Decode(src); + } - public override ushort Decode(ElfNative.Elf64_Versym src) - { - return _decoder.Decode(src); - } + public override ushort Decode(ElfNative.Elf64_Versym src) + { + return _decoder.Decode(src); + } - private static readonly Comparison CompareSectionOffsetsAndSizesDelegate = new Comparison(CompareSectionOffsetsAndSizes); + private static readonly Comparison CompareSectionOffsetsAndSizesDelegate = new Comparison(CompareSectionOffsetsAndSizes); - private static int CompareSectionOffsetsAndSizes(ElfSection left, ElfSection right) + private static int CompareSectionOffsetsAndSizes(ElfSection left, ElfSection right) + { + int result = left.Position.CompareTo(right.Position); + if (result == 0) { - int result = left.Position.CompareTo(right.Position); - if (result == 0) - { - result = left.Size.CompareTo(right.Size); - } - return result; + result = left.Size.CompareTo(right.Size); } + return result; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSection.cs b/src/LibObjectFile/Elf/ElfSection.cs index 841a687..fb1f9b1 100644 --- a/src/LibObjectFile/Elf/ElfSection.cs +++ b/src/LibObjectFile/Elf/ElfSection.cs @@ -4,195 +4,176 @@ using System; using System.Diagnostics; +using System.Text; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines the base class for a section in an . +/// +[DebuggerDisplay("{ToString(),nq}")] +public abstract class ElfSection : ElfObject { - /// - /// Defines the base class for a section in an . - /// - [DebuggerDisplay("{ToString(),nq}")] - public abstract class ElfSection : ElfObject + private ElfSectionType _type; + + protected ElfSection() : this(ElfSectionType.Null) { - private ElfSectionType _type; + } - protected ElfSection() : this(ElfSectionType.Null) - { - } + protected ElfSection(ElfSectionType sectionType) + { + _type = sectionType; + } - protected ElfSection(ElfSectionType sectionType) + public virtual ElfSectionType Type + { + get => _type; + set { - _type = sectionType; + _type = value; } + } - public virtual ElfSectionType Type + protected override void ValidateParent(ObjectFileElement parent) + { + if (!(parent is ElfObjectFile)) { - get => _type; - set - { - _type = value; - } + throw new ArgumentException($"Parent must inherit from type {nameof(ElfObjectFile)}"); } + } - protected override void ValidateParent(ObjectFileNodeBase parent) - { - if (!(parent is ElfObjectFile)) - { - throw new ArgumentException($"Parent must inherit from type {nameof(ElfObjectFile)}"); - } - } + /// + /// Gets the containing . Might be null if this section or segment + /// does not belong to an existing . + /// + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public new ElfObjectFile? Parent + { + get => (ElfObjectFile?)base.Parent; + internal set => base.Parent = value; + } - /// - /// Gets the containing . Might be null if this section or segment - /// does not belong to an existing . - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public new ElfObjectFile? Parent - { - get => (ElfObjectFile?)base.Parent; - internal set => base.Parent = value; - } + /// + /// Gets or sets the of this section. + /// + public ElfSectionFlags Flags { get; set; } - /// - /// Gets or sets the of this section. - /// - public ElfSectionFlags Flags { get; set; } - - /// - /// Gets or sets the name of this section. - /// - public ElfString Name { get; set; } - - /// - /// Gets or sets the virtual address of this section. - /// - public ulong VirtualAddress { get; set; } - - /// - /// Gets or sets the alignment requirement of this section. - /// - public ulong Alignment { get; set; } - - /// - /// Gets or sets the link element of this section. - /// - public ElfSectionLink Link { get; set; } - - /// - /// Gets or sets the info element of this section. - /// - public ElfSectionLink Info { get; set; } - - /// - /// Gets the table entry size of this section. - /// - public virtual ulong TableEntrySize => 0; - - /// - /// Gets the index of the visible sections in (visible == not ) - /// - public uint SectionIndex { get; internal set; } - - /// - /// Gets or sets the ordering index used when writing back this section. - /// - public uint StreamIndex { get; set; } - - /// - /// Gets the size of the original table entry size of this section. - /// - public ulong OriginalTableEntrySize { get; internal set; } - - /// - /// Gets a boolean indicating if this section is a . - /// - public bool IsShadow => this is ElfShadowSection; - - /// - /// Gets a boolean indicating if this section has some content (Size should be taken into account). - /// - public bool HasContent => Type != ElfSectionType.NoBits && (Type != ElfSectionType.Null || this is ElfShadowSection); - - /// - /// Read data from the specified reader to this instance. - /// - /// The reader to read data from. - protected abstract void Read(ElfReader reader); - - /// - /// Writes data to the specified writer from this instance. - /// - /// The writer to write data to. - protected abstract void Write(ElfWriter writer); - - internal void WriteInternal(ElfWriter writer) - { - Write(writer); - } + /// + /// Gets or sets the name of this section. + /// + public ElfString Name { get; set; } - internal void ReadInternal(ElfReader reader) - { - Read(reader); - } - - public override void Verify(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + /// + /// Gets or sets the virtual address of this section. + /// + public ulong VirtualAddress { get; set; } - // Check parent for link section - if (Link.Section != null) - { - if (Link.Section.Parent != this.Parent) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionLinkParent, $"Invalid parent for {nameof(Link)}: `{Link}` used by section `{this}`. The {nameof(Link)}.{nameof(ElfSectionLink.Section)} must have the same parent {nameof(ElfObjectFile)} than this section"); - } - } + /// + /// Gets or sets the alignment requirement of this section. + /// + public ulong Alignment { get; set; } - if (Info.Section != null) - { - if (Info.Section.Parent != this.Parent) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionInfoParent, $"Invalid parent for {nameof(Info)}: `{Info}` used by section `{this}`. The {nameof(Info)}.{nameof(ElfSectionLink.Section)} must have the same parent {nameof(ElfObjectFile)} than this section"); - } - } + /// + /// Gets or sets the link element of this section. + /// + public ElfSectionLink Link { get; set; } + + /// + /// Gets or sets the info element of this section. + /// + public ElfSectionLink Info { get; set; } + + /// + /// Gets the table entry size of this section. + /// + public virtual ulong TableEntrySize => 0; + + /// + /// Gets the index of the visible sections in (visible == not ) + /// + public uint SectionIndex { get; internal set; } + + /// + /// Gets or sets the ordering index used when writing back this section. + /// + public uint StreamIndex { get; set; } + + /// + /// Gets the size of the original table entry size of this section. + /// + public ulong OriginalTableEntrySize { get; internal set; } + + /// + /// Gets a boolean indicating if this section is a . + /// + public bool IsShadow => this is ElfShadowSection; + + /// + /// Gets a boolean indicating if this section has some content (Size should be taken into account). + /// + public bool HasContent => Type != ElfSectionType.NoBits && (Type != ElfSectionType.Null || this is ElfShadowSection); + + + public override void Verify(ElfVisitorContext context) + { + var diagnostics = context.Diagnostics; - // Verify that Link is correctly setup for this section - switch (Type) + // Check parent for link section + if (Link.Section != null) + { + if (Link.Section.Parent != this.Parent) { - case ElfSectionType.DynamicLinking: - case ElfSectionType.DynamicLinkerSymbolTable: - case ElfSectionType.SymbolTable: - Link.TryGetSectionSafe(this.GetType().Name, nameof(Link), this, diagnostics, out _, ElfSectionType.StringTable); - break; - case ElfSectionType.SymbolHashTable: - case ElfSectionType.Relocation: - case ElfSectionType.RelocationAddends: - Link.TryGetSectionSafe(this.GetType().Name, nameof(Link), this, diagnostics, out _, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable); - break; + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionLinkParent, $"Invalid parent for {nameof(Link)}: `{Link}` used by section `{this}`. The {nameof(Link)}.{nameof(ElfSectionLink.Section)} must have the same parent {nameof(ElfObjectFile)} than this section"); } } - protected virtual void AfterRead(ElfReader reader) + if (Info.Section != null) { + if (Info.Section.Parent != this.Parent) + { + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionInfoParent, $"Invalid parent for {nameof(Info)}: `{Info}` used by section `{this}`. The {nameof(Info)}.{nameof(ElfSectionLink.Section)} must have the same parent {nameof(ElfObjectFile)} than this section"); + } } - protected virtual void BeforeWrite(ElfWriter writer) + // Verify that Link is correctly setup for this section + switch (Type) { + case ElfSectionType.DynamicLinking: + case ElfSectionType.DynamicLinkerSymbolTable: + case ElfSectionType.SymbolTable: + Link.TryGetSectionSafe(this.GetType().Name, nameof(Link), this, diagnostics, out _, ElfSectionType.StringTable); + break; + case ElfSectionType.SymbolHashTable: + case ElfSectionType.Relocation: + case ElfSectionType.RelocationAddends: + Link.TryGetSectionSafe(this.GetType().Name, nameof(Link), this, diagnostics, out _, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable); + break; } + } - public override string ToString() - { - return $"Section [{SectionIndex}](Internal: {Index}) `{Name}` "; - } + protected virtual void AfterRead(ElfReader reader) + { + } - internal void BeforeWriteInternal(ElfWriter writer) - { - BeforeWrite(writer); - } + protected virtual void BeforeWrite(ElfWriter writer) + { + } - internal void AfterReadInternal(ElfReader reader) - { - AfterRead(reader); - } + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"Section [{SectionIndex}](Internal: {Index}) `{Name}` "); + return true; + } + + + internal void BeforeWriteInternal(ElfWriter writer) + { + BeforeWrite(writer); + } + + internal void AfterReadInternal(ElfReader reader) + { + AfterRead(reader); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSectionExtension.cs b/src/LibObjectFile/Elf/ElfSectionExtension.cs index 86cae8d..81cccf1 100644 --- a/src/LibObjectFile/Elf/ElfSectionExtension.cs +++ b/src/LibObjectFile/Elf/ElfSectionExtension.cs @@ -5,150 +5,149 @@ using System; using LibObjectFile.Utils; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Extensions for +/// +public static class ElfSectionExtension { /// - /// Extensions for + /// Configure a section default name, type and flags with /// - public static class ElfSectionExtension + /// The type of the section to configure + /// The section to configure + /// The special type + /// The section configured + public static TElfSection ConfigureAs(this TElfSection section, ElfSectionSpecialType sectionSpecialType) where TElfSection : ElfSection { - /// - /// Configure a section default name, type and flags with - /// - /// The type of the section to configure - /// The section to configure - /// The special type - /// The section configured - public static TElfSection ConfigureAs(this TElfSection section, ElfSectionSpecialType sectionSpecialType) where TElfSection : ElfSection + switch (sectionSpecialType) { - switch (sectionSpecialType) - { - case ElfSectionSpecialType.None: - break; - case ElfSectionSpecialType.Bss: - section.Name = ".bss"; - section.Type = ElfSectionType.NoBits; - section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Write; - break; - case ElfSectionSpecialType.Comment: - section.Name = ".comment"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.Data: - section.Name = ".data"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Write; - break; - case ElfSectionSpecialType.Data1: - section.Name = ".data1"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Write; - break; - case ElfSectionSpecialType.Debug: - section.Name = ".debug"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.Dynamic: - section.Name = ".dynamic"; - section.Type = ElfSectionType.DynamicLinking; - section.Flags = ElfSectionFlags.Alloc; - break; - case ElfSectionSpecialType.DynamicStringTable: - section.Name = ".dynstr"; - section.Type = ElfSectionType.StringTable; - section.Flags = ElfSectionFlags.Alloc; - break; - case ElfSectionSpecialType.DynamicSymbolTable: - section.Name = ".dynsym"; - section.Type = ElfSectionType.DynamicLinkerSymbolTable; - section.Flags = ElfSectionFlags.Alloc; - break; - case ElfSectionSpecialType.Fini: - section.Name = ".fini"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Executable; - break; - case ElfSectionSpecialType.Got: - section.Name = ".got"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.Hash: - section.Name = ".hash"; - section.Type = ElfSectionType.SymbolHashTable; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.Init: - section.Name = ".init"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Executable; - break; - case ElfSectionSpecialType.Interp: - section.Name = ".interp"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.Alloc; - break; - case ElfSectionSpecialType.Line: - section.Name = ".line"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.Note: - section.Name = ".note"; - section.Type = ElfSectionType.Note; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.Plt: - section.Name = ".plt"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.Relocation: - section.Name = ElfRelocationTable.DefaultName; - section.Type = ElfSectionType.Relocation; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.RelocationAddends: - section.Name = ElfRelocationTable.DefaultNameWithAddends; - section.Type = ElfSectionType.RelocationAddends; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.ReadOnlyData: - section.Name = ".rodata"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.Alloc; - break; - case ElfSectionSpecialType.ReadOnlyData1: - section.Name = ".rodata1"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.Alloc; - break; - case ElfSectionSpecialType.SectionHeaderStringTable: - section.Name = ".shstrtab"; - section.Type = ElfSectionType.StringTable; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.StringTable: - section.Name = ElfStringTable.DefaultName; - section.Type = ElfSectionType.StringTable; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.SymbolTable: - section.Name = ElfSymbolTable.DefaultName; - section.Type = ElfSectionType.SymbolTable; - section.Flags = ElfSectionFlags.None; - break; - case ElfSectionSpecialType.Text: - section.Name = ".text"; - section.Type = ElfSectionType.ProgBits; - section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Executable; - break; - default: - throw ThrowHelper.InvalidEnum(sectionSpecialType); - } - return section; + case ElfSectionSpecialType.None: + break; + case ElfSectionSpecialType.Bss: + section.Name = ".bss"; + section.Type = ElfSectionType.NoBits; + section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Write; + break; + case ElfSectionSpecialType.Comment: + section.Name = ".comment"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.Data: + section.Name = ".data"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Write; + break; + case ElfSectionSpecialType.Data1: + section.Name = ".data1"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Write; + break; + case ElfSectionSpecialType.Debug: + section.Name = ".debug"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.Dynamic: + section.Name = ".dynamic"; + section.Type = ElfSectionType.DynamicLinking; + section.Flags = ElfSectionFlags.Alloc; + break; + case ElfSectionSpecialType.DynamicStringTable: + section.Name = ".dynstr"; + section.Type = ElfSectionType.StringTable; + section.Flags = ElfSectionFlags.Alloc; + break; + case ElfSectionSpecialType.DynamicSymbolTable: + section.Name = ".dynsym"; + section.Type = ElfSectionType.DynamicLinkerSymbolTable; + section.Flags = ElfSectionFlags.Alloc; + break; + case ElfSectionSpecialType.Fini: + section.Name = ".fini"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Executable; + break; + case ElfSectionSpecialType.Got: + section.Name = ".got"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.Hash: + section.Name = ".hash"; + section.Type = ElfSectionType.SymbolHashTable; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.Init: + section.Name = ".init"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Executable; + break; + case ElfSectionSpecialType.Interp: + section.Name = ".interp"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.Alloc; + break; + case ElfSectionSpecialType.Line: + section.Name = ".line"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.Note: + section.Name = ".note"; + section.Type = ElfSectionType.Note; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.Plt: + section.Name = ".plt"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.Relocation: + section.Name = ElfRelocationTable.DefaultName; + section.Type = ElfSectionType.Relocation; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.RelocationAddends: + section.Name = ElfRelocationTable.DefaultNameWithAddends; + section.Type = ElfSectionType.RelocationAddends; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.ReadOnlyData: + section.Name = ".rodata"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.Alloc; + break; + case ElfSectionSpecialType.ReadOnlyData1: + section.Name = ".rodata1"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.Alloc; + break; + case ElfSectionSpecialType.SectionHeaderStringTable: + section.Name = ".shstrtab"; + section.Type = ElfSectionType.StringTable; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.StringTable: + section.Name = ElfStringTable.DefaultName; + section.Type = ElfSectionType.StringTable; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.SymbolTable: + section.Name = ElfSymbolTable.DefaultName; + section.Type = ElfSectionType.SymbolTable; + section.Flags = ElfSectionFlags.None; + break; + case ElfSectionSpecialType.Text: + section.Name = ".text"; + section.Type = ElfSectionType.ProgBits; + section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Executable; + break; + default: + throw ThrowHelper.InvalidEnum(sectionSpecialType); } + return section; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSectionFlags.cs b/src/LibObjectFile/Elf/ElfSectionFlags.cs index d52c4ea..3eb8a0a 100644 --- a/src/LibObjectFile/Elf/ElfSectionFlags.cs +++ b/src/LibObjectFile/Elf/ElfSectionFlags.cs @@ -4,69 +4,68 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines the flag of a section. +/// +[Flags] +public enum ElfSectionFlags : uint { + None = 0, + /// - /// Defines the flag of a section. + /// Writable /// - [Flags] - public enum ElfSectionFlags : uint - { - None = 0, - - /// - /// Writable - /// - Write = ElfNative.SHF_WRITE, + Write = ElfNative.SHF_WRITE, - /// - /// Occupies memory during execution - /// - Alloc = ElfNative.SHF_ALLOC, + /// + /// Occupies memory during execution + /// + Alloc = ElfNative.SHF_ALLOC, - /// - /// Executable - /// - Executable = ElfNative.SHF_EXECINSTR, + /// + /// Executable + /// + Executable = ElfNative.SHF_EXECINSTR, - /// - /// Might be merged - /// - Merge = ElfNative.SHF_MERGE, + /// + /// Might be merged + /// + Merge = ElfNative.SHF_MERGE, - /// - /// Contains nul-terminated strings - /// - Strings = ElfNative.SHF_STRINGS, + /// + /// Contains nul-terminated strings + /// + Strings = ElfNative.SHF_STRINGS, - /// - /// `sh_info' contains SHT index - /// - InfoLink = ElfNative.SHF_INFO_LINK, + /// + /// `sh_info' contains SHT index + /// + InfoLink = ElfNative.SHF_INFO_LINK, - /// - /// Preserve order after combining - /// - LinkOrder = ElfNative.SHF_LINK_ORDER, + /// + /// Preserve order after combining + /// + LinkOrder = ElfNative.SHF_LINK_ORDER, - /// - /// Non-standard OS specific handling required - /// - OsNonConforming = ElfNative.SHF_OS_NONCONFORMING, + /// + /// Non-standard OS specific handling required + /// + OsNonConforming = ElfNative.SHF_OS_NONCONFORMING, - /// - /// Section is member of a group. - /// - Group = ElfNative.SHF_GROUP, + /// + /// Section is member of a group. + /// + Group = ElfNative.SHF_GROUP, - /// - /// Section hold thread-local data. - /// - Tls = ElfNative.SHF_TLS, + /// + /// Section hold thread-local data. + /// + Tls = ElfNative.SHF_TLS, - /// - /// Section with compressed data. - /// - Compressed = ElfNative.SHF_COMPRESSED, - } + /// + /// Section with compressed data. + /// + Compressed = ElfNative.SHF_COMPRESSED, } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSectionLink.cs b/src/LibObjectFile/Elf/ElfSectionLink.cs index 852e44d..deea99b 100644 --- a/src/LibObjectFile/Elf/ElfSectionLink.cs +++ b/src/LibObjectFile/Elf/ElfSectionLink.cs @@ -5,144 +5,143 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines a link to a section, special section or an index (used by and ) +/// +public readonly struct ElfSectionLink : IEquatable { - /// - /// Defines a link to a section, special section or an index (used by and ) - /// - public readonly struct ElfSectionLink : IEquatable - { - public static readonly ElfSectionLink Empty = new ElfSectionLink(ElfNative.SHN_UNDEF); + public static readonly ElfSectionLink Empty = new ElfSectionLink(ElfNative.SHN_UNDEF); - public static readonly ElfSectionLink SectionAbsolute = new ElfSectionLink(ElfNative.SHN_ABS); + public static readonly ElfSectionLink SectionAbsolute = new ElfSectionLink(ElfNative.SHN_ABS); - public static readonly ElfSectionLink SectionCommon = new ElfSectionLink(ElfNative.SHN_COMMON); + public static readonly ElfSectionLink SectionCommon = new ElfSectionLink(ElfNative.SHN_COMMON); - public ElfSectionLink(uint index) - { - Section = null; - SpecialIndex = index; - } + public ElfSectionLink(uint index) + { + Section = null; + SpecialIndex = index; + } - public ElfSectionLink(ElfSection? section) - { - Section = section; - SpecialIndex = 0; - } + public ElfSectionLink(ElfSection? section) + { + Section = section; + SpecialIndex = 0; + } - public readonly ElfSection? Section; + public readonly ElfSection? Section; - public readonly uint SpecialIndex; + public readonly uint SpecialIndex; - public bool IsEmpty => Section == null && SpecialIndex == 0; + public bool IsEmpty => Section == null && SpecialIndex == 0; - /// - /// true if this link to a section is a special section. - /// - public bool IsSpecial => Section == null && (SpecialIndex == ElfNative.SHN_UNDEF || SpecialIndex >= ElfNative.SHN_LORESERVE); + /// + /// true if this link to a section is a special section. + /// + public bool IsSpecial => Section == null && (SpecialIndex == ElfNative.SHN_UNDEF || SpecialIndex >= ElfNative.SHN_LORESERVE); - public uint GetIndex() - { - return Section?.SectionIndex ?? SpecialIndex; - } + public uint GetIndex() + { + return Section?.SectionIndex ?? SpecialIndex; + } - public bool Equals(ElfSectionLink other) - { - return Equals(Section, other.Section) && SpecialIndex == other.SpecialIndex; - } + public bool Equals(ElfSectionLink other) + { + return Equals(Section, other.Section) && SpecialIndex == other.SpecialIndex; + } - public override bool Equals(object? obj) - { - return obj is ElfSectionLink other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is ElfSectionLink other && Equals(other); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - return ((Section != null ? Section.GetHashCode() : 0) * 397) ^ SpecialIndex.GetHashCode(); - } + return ((Section != null ? Section.GetHashCode() : 0) * 397) ^ SpecialIndex.GetHashCode(); } + } - public static bool operator ==(ElfSectionLink left, ElfSectionLink right) - { - return left.Equals(right); - } + public static bool operator ==(ElfSectionLink left, ElfSectionLink right) + { + return left.Equals(right); + } - public static bool operator !=(ElfSectionLink left, ElfSectionLink right) + public static bool operator !=(ElfSectionLink left, ElfSectionLink right) + { + return !left.Equals(right); + } + + public override string ToString() + { + if (Section != null) { - return !left.Equals(right); + return Section.ToString(); } - public override string ToString() + if (SpecialIndex == 0) return "Special Section Undefined"; + + if (SpecialIndex > ElfNative.SHN_BEFORE) { - if (Section != null) + if (SpecialIndex == ElfNative.SHN_ABS) { - return Section.ToString(); + return "Special Section Absolute"; } - - if (SpecialIndex == 0) return "Special Section Undefined"; - - if (SpecialIndex > ElfNative.SHN_BEFORE) - { - if (SpecialIndex == ElfNative.SHN_ABS) - { - return "Special Section Absolute"; - } - if (SpecialIndex == ElfNative.SHN_COMMON) - { - return "Special Section Common"; - } - - if (SpecialIndex == ElfNative.SHN_XINDEX) - { - return "Special Section XIndex"; - } + if (SpecialIndex == ElfNative.SHN_COMMON) + { + return "Special Section Common"; } - return $"Unknown Section Value 0x{SpecialIndex:X8}"; + if (SpecialIndex == ElfNative.SHN_XINDEX) + { + return "Special Section XIndex"; + } } - public static implicit operator ElfSectionLink(ElfSection? section) - { - return new ElfSectionLink(section); - } + return $"Unknown Section Value 0x{SpecialIndex:X8}"; + } + + public static implicit operator ElfSectionLink(ElfSection? section) + { + return new ElfSectionLink(section); + } - public bool TryGetSectionSafe(string className, string propertyName, object context, DiagnosticBag diagnostics, [NotNullWhen(true)] out TSection? section, params ElfSectionType[] sectionTypes) where TSection : ElfSection - { - section = null; + public bool TryGetSectionSafe(string className, string propertyName, object context, DiagnosticBag diagnostics, [NotNullWhen(true)] out TSection? section, params ElfSectionType[] sectionTypes) where TSection : ElfSection + { + section = null; - if (Section == null) - { - diagnostics.Error(DiagnosticId.ELF_ERR_LinkOrInfoSectionNull, $"`{className}.{propertyName}` cannot be null for this instance", context); - return false; - } + if (Section == null) + { + diagnostics.Error(DiagnosticId.ELF_ERR_LinkOrInfoSectionNull, $"`{className}.{propertyName}` cannot be null for this instance", context); + return false; + } - bool foundValid = false; - foreach (var elfSectionType in sectionTypes) + bool foundValid = false; + foreach (var elfSectionType in sectionTypes) + { + if (Section.Type == elfSectionType) { - if (Section.Type == elfSectionType) - { - foundValid = true; - break; - } + foundValid = true; + break; } + } - if (!foundValid) - { - diagnostics.Error(DiagnosticId.ELF_ERR_LinkOrInfoInvalidSectionType, $"The type `{Section.Type}` of `{className}.{propertyName}` must be a {string.Join(" or ", sectionTypes)}", context); - return false; - } - section = Section as TSection; + if (!foundValid) + { + diagnostics.Error(DiagnosticId.ELF_ERR_LinkOrInfoInvalidSectionType, $"The type `{Section.Type}` of `{className}.{propertyName}` must be a {string.Join(" or ", sectionTypes)}", context); + return false; + } + section = Section as TSection; - if (section == null) - { - diagnostics.Error(DiagnosticId.ELF_ERR_LinkOrInfoInvalidSectionInstance, $"The `{className}.{propertyName}` must be an instance of {typeof(TSection).Name}"); - return false; - } - return true; + if (section == null) + { + diagnostics.Error(DiagnosticId.ELF_ERR_LinkOrInfoInvalidSectionInstance, $"The `{className}.{propertyName}` must be an instance of {typeof(TSection).Name}"); + return false; } + return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSectionSpecialType.cs b/src/LibObjectFile/Elf/ElfSectionSpecialType.cs index 7a4aaba..e679e3f 100644 --- a/src/LibObjectFile/Elf/ElfSectionSpecialType.cs +++ b/src/LibObjectFile/Elf/ElfSectionSpecialType.cs @@ -2,37 +2,36 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines special sections that can be configured via +/// +public enum ElfSectionSpecialType { - /// - /// Defines special sections that can be configured via - /// - public enum ElfSectionSpecialType - { - None, - Bss, - Comment, - Data, - Data1, - Debug, - Dynamic, - DynamicStringTable, - DynamicSymbolTable, - Fini, - Got, - Hash, - Init, - Interp, - Line, - Note, - Plt, - Relocation, - RelocationAddends, - ReadOnlyData, - ReadOnlyData1, - SectionHeaderStringTable, - StringTable, - SymbolTable, - Text, - } + None, + Bss, + Comment, + Data, + Data1, + Debug, + Dynamic, + DynamicStringTable, + DynamicSymbolTable, + Fini, + Got, + Hash, + Init, + Interp, + Line, + Note, + Plt, + Relocation, + RelocationAddends, + ReadOnlyData, + ReadOnlyData1, + SectionHeaderStringTable, + StringTable, + SymbolTable, + Text, } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSectionType.cs b/src/LibObjectFile/Elf/ElfSectionType.cs index ccc1c7e..724dbf0 100644 --- a/src/LibObjectFile/Elf/ElfSectionType.cs +++ b/src/LibObjectFile/Elf/ElfSectionType.cs @@ -2,131 +2,130 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines the type of a section. +/// +public enum ElfSectionType : uint { /// - /// Defines the type of a section. - /// - public enum ElfSectionType : uint - { - /// - /// Section header table entry unused - /// - Null = ElfNative.SHT_NULL, - - /// - /// Program data - /// - ProgBits = ElfNative.SHT_PROGBITS, - - /// - /// Symbol table - /// - SymbolTable = ElfNative.SHT_SYMTAB, - - /// - /// String table - /// - StringTable = ElfNative.SHT_STRTAB, - - /// - /// Relocation entries with addends - /// - RelocationAddends = ElfNative.SHT_RELA, - - /// - /// Symbol hash table - /// - SymbolHashTable = ElfNative.SHT_HASH, - - /// - /// Dynamic linking information - /// - DynamicLinking = ElfNative.SHT_DYNAMIC, - - /// - /// Notes - /// - Note = ElfNative.SHT_NOTE, - - /// - /// Program space with no data (bss) - /// - NoBits = ElfNative.SHT_NOBITS, - - /// - /// Relocation entries, no addends - /// - Relocation = ElfNative.SHT_REL, - - /// - /// Reserved - /// - Shlib = ElfNative.SHT_SHLIB, - - /// - /// Dynamic linker symbol table - /// - DynamicLinkerSymbolTable = ElfNative.SHT_DYNSYM, - - /// - /// Array of constructors - /// - InitArray = ElfNative.SHT_INIT_ARRAY, - - /// - /// Array of destructors - /// - FiniArray = ElfNative.SHT_FINI_ARRAY, - - /// - /// Array of pre-constructors - /// - PreInitArray = ElfNative.SHT_PREINIT_ARRAY, - - /// - /// Section group - /// - Group = ElfNative.SHT_GROUP, - - /// - /// Extended section indices - /// - SymbolTableSectionHeaderIndices = ElfNative.SHT_SYMTAB_SHNDX, - - /// - /// Object attributes. - /// - GnuAttributes = ElfNative.SHT_GNU_ATTRIBUTES, - - /// - /// GNU-style hash table. - /// - GnuHash = ElfNative.SHT_GNU_HASH, - - /// - /// Prelink library list - /// - GnuLibList = ElfNative.SHT_GNU_LIBLIST, - - /// - /// Checksum for DSO content. - /// - Checksum = ElfNative.SHT_CHECKSUM, - - /// - /// Version definition section. - /// - GnuVersionDefinition = ElfNative.SHT_GNU_verdef, - - /// - /// Version needs section. - /// - GnuVersionNeedsSection = ElfNative.SHT_GNU_verneed, - - /// - /// Version symbol table. - /// - GnuVersionSymbolTable = ElfNative.SHT_GNU_versym, - } + /// Section header table entry unused + /// + Null = ElfNative.SHT_NULL, + + /// + /// Program data + /// + ProgBits = ElfNative.SHT_PROGBITS, + + /// + /// Symbol table + /// + SymbolTable = ElfNative.SHT_SYMTAB, + + /// + /// String table + /// + StringTable = ElfNative.SHT_STRTAB, + + /// + /// Relocation entries with addends + /// + RelocationAddends = ElfNative.SHT_RELA, + + /// + /// Symbol hash table + /// + SymbolHashTable = ElfNative.SHT_HASH, + + /// + /// Dynamic linking information + /// + DynamicLinking = ElfNative.SHT_DYNAMIC, + + /// + /// Notes + /// + Note = ElfNative.SHT_NOTE, + + /// + /// Program space with no data (bss) + /// + NoBits = ElfNative.SHT_NOBITS, + + /// + /// Relocation entries, no addends + /// + Relocation = ElfNative.SHT_REL, + + /// + /// Reserved + /// + Shlib = ElfNative.SHT_SHLIB, + + /// + /// Dynamic linker symbol table + /// + DynamicLinkerSymbolTable = ElfNative.SHT_DYNSYM, + + /// + /// Array of constructors + /// + InitArray = ElfNative.SHT_INIT_ARRAY, + + /// + /// Array of destructors + /// + FiniArray = ElfNative.SHT_FINI_ARRAY, + + /// + /// Array of pre-constructors + /// + PreInitArray = ElfNative.SHT_PREINIT_ARRAY, + + /// + /// Section group + /// + Group = ElfNative.SHT_GROUP, + + /// + /// Extended section indices + /// + SymbolTableSectionHeaderIndices = ElfNative.SHT_SYMTAB_SHNDX, + + /// + /// Object attributes. + /// + GnuAttributes = ElfNative.SHT_GNU_ATTRIBUTES, + + /// + /// GNU-style hash table. + /// + GnuHash = ElfNative.SHT_GNU_HASH, + + /// + /// Prelink library list + /// + GnuLibList = ElfNative.SHT_GNU_LIBLIST, + + /// + /// Checksum for DSO content. + /// + Checksum = ElfNative.SHT_CHECKSUM, + + /// + /// Version definition section. + /// + GnuVersionDefinition = ElfNative.SHT_GNU_verdef, + + /// + /// Version needs section. + /// + GnuVersionNeedsSection = ElfNative.SHT_GNU_verneed, + + /// + /// Version symbol table. + /// + GnuVersionSymbolTable = ElfNative.SHT_GNU_versym, } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSegment.cs b/src/LibObjectFile/Elf/ElfSegment.cs index 7d52bc0..99edbeb 100644 --- a/src/LibObjectFile/Elf/ElfSegment.cs +++ b/src/LibObjectFile/Elf/ElfSegment.cs @@ -3,148 +3,148 @@ // See the license.txt file in the project root for more information. using System; +using System.Text; using LibObjectFile.Utils; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines a segment or program header. +/// +public sealed class ElfSegment : ElfObject { + public ValueKind OffsetKind { get; set; } + /// - /// Defines a segment or program header. + /// Gets or sets the type of this segment. /// - public sealed class ElfSegment : ElfObject - { - public ValueKind OffsetKind { get; set; } - - /// - /// Gets or sets the type of this segment. - /// - public ElfSegmentType Type { get; set; } - - /// - /// Gets or sets the range of section this segment applies to. - /// It can applies to . - /// - public ElfSegmentRange Range { get; set; } - - /// - /// Gets or sets the virtual address. - /// - public ulong VirtualAddress { get; set; } - - /// - /// Gets or sets the physical address. - /// - public ulong PhysicalAddress { get; set; } + public ElfSegmentType Type { get; set; } + + /// + /// Gets or sets the range of section this segment applies to. + /// It can applies to . + /// + public ElfSegmentRange Range { get; set; } + + /// + /// Gets or sets the virtual address. + /// + public ulong VirtualAddress { get; set; } + + /// + /// Gets or sets the physical address. + /// + public ulong PhysicalAddress { get; set; } - /// - /// Gets or sets the size in bytes occupied in memory by this segment. - /// - public ulong SizeInMemory { get; set; } - - /// - /// Gets or sets the flags of this segment. - /// - public ElfSegmentFlags Flags { get; set; } - - /// - /// Gets the alignment requirement of this section. - /// - public ulong Alignment { get; set; } - - public override void UpdateLayout(DiagnosticBag diagnostics) + /// + /// Gets or sets the size in bytes occupied in memory by this segment. + /// + public ulong SizeInMemory { get; set; } + + /// + /// Gets or sets the flags of this segment. + /// + public ElfSegmentFlags Flags { get; set; } + + /// + /// Gets the alignment requirement of this section. + /// + public ulong Alignment { get; set; } + + public override void UpdateLayout(ElfVisitorContext context) + { + var diagnostics = context.Diagnostics; + + if (OffsetKind == ValueKind.Auto) { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + Position = Range.Offset; + } + + if (Range.IsEmpty) + { + //diagnostics.Error($"Invalid empty {nameof(Range)} in {this}. An {nameof(ElfSegment)} requires to be attached to a section or a range of section or a {nameof(ElfShadowSection)}"); + } + else + { + Size = Range.Size; - if (OffsetKind == ValueKind.Auto) + // TODO: Add checks that Alignment is Power Of 2 + var alignment = Alignment == 0 ? Alignment = 1 : Alignment; + if (!AlignHelper.IsPowerOfTwo(alignment)) { - Position = Range.Offset; + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentAlignmentForLoad, $"Invalid segment alignment requirements: Alignment = {alignment} must be a power of 2"); } - - if (Range.IsEmpty) + + if (Range.BeginSection?.Parent == null) { - //diagnostics.Error($"Invalid empty {nameof(Range)} in {this}. An {nameof(ElfSegment)} requires to be attached to a section or a range of section or a {nameof(ElfShadowSection)}"); + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeBeginSectionParent, $"Invalid null parent {nameof(Range)}.{nameof(Range.BeginSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); } - else + + if (Range.EndSection?.Parent == null) { - Size = Range.Size; + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndSectionParent, $"Invalid null parent {nameof(Range)}.{nameof(Range.EndSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); + } - // TODO: Add checks that Alignment is Power Of 2 - var alignment = Alignment == 0 ? Alignment = 1 : Alignment; - if (!AlignHelper.IsPowerOfTwo(alignment)) + if (Type == ElfSegmentTypeCore.Load) + { + // Specs: + // As ‘‘Program Loading’’ later in this part describes, loadable process segments must have congruent values for p_vaddr and p_offset, modulo the page size. + // TODO: how to make this configurable? + if ((alignment % 4096) != 0) { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentAlignmentForLoad, $"Invalid segment alignment requirements: Alignment = {alignment} must be a power of 2"); + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentAlignmentForLoad, $"Invalid {nameof(ElfNative.PT_LOAD)} segment alignment requirements: {alignment} must be multiple of the Page Size {4096}"); } - if (Range.BeginSection?.Parent == null) + var mod = (VirtualAddress - Range.Offset) & (alignment - 1); + if (mod != 0) { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeBeginSectionParent, $"Invalid null parent {nameof(Range)}.{nameof(Range.BeginSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentVirtualAddressOrOffset, $"Invalid {nameof(ElfNative.PT_LOAD)} segment alignment requirements: (VirtualAddress - Range.Offset) & (Alignment - 1) == {mod} while it must be == 0"); } + } - if (Range.EndSection?.Parent == null) + if (Size > 0) + { + var range = Range; + if (range.BeginSection is null) { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndSectionParent, $"Invalid null parent {nameof(Range)}.{nameof(Range.EndSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeBeginSectionParent, $"Invalid null {nameof(Range)}.{nameof(Range.BeginSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); } - - if (Type == ElfSegmentTypeCore.Load) + else if (range.BeginOffset >= range.BeginSection.Size) { - // Specs: - // As ‘‘Program Loading’’ later in this part describes, loadable process segments must have congruent values for p_vaddr and p_offset, modulo the page size. - // TODO: how to make this configurable? - if ((alignment % 4096) != 0) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentAlignmentForLoad, $"Invalid {nameof(ElfNative.PT_LOAD)} segment alignment requirements: {alignment} must be multiple of the Page Size {4096}"); - } - - var mod = (VirtualAddress - Range.Offset) & (alignment - 1); - if (mod != 0) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentVirtualAddressOrOffset, $"Invalid {nameof(ElfNative.PT_LOAD)} segment alignment requirements: (VirtualAddress - Range.Offset) & (Alignment - 1) == {mod} while it must be == 0"); - } + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeBeginOffset, $"Invalid {nameof(Range)}.{nameof(Range.BeginOffset)}: {Range.BeginOffset} cannot be >= {nameof(Range.BeginSection)}.{nameof(ElfSection.Size)}: {range.BeginSection.Size} in {this}. The offset must be within the section"); } - if (Size > 0) + if (range.EndSection is null) { - var range = Range; - if (range.BeginSection is null) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeBeginSectionParent, $"Invalid null {nameof(Range)}.{nameof(Range.BeginSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); - } - else if (range.BeginOffset >= range.BeginSection.Size) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeBeginOffset, $"Invalid {nameof(Range)}.{nameof(Range.BeginOffset)}: {Range.BeginOffset} cannot be >= {nameof(Range.BeginSection)}.{nameof(ElfSection.Size)}: {range.BeginSection.Size} in {this}. The offset must be within the section"); - } - - if (range.EndSection is null) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndSectionParent, $"Invalid null {nameof(Range)}.{nameof(Range.EndSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); - } - else if ((Range.EndOffset >= 0 && (ulong)Range.EndOffset >= range.EndSection.Size)) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndOffset, $"Invalid {nameof(Range)}.{nameof(Range.EndOffset)}: {Range.EndOffset} cannot be >= {nameof(Range)}.{nameof(ElfSegmentRange.EndSection)}.{nameof(ElfSection.Size)}: {range.EndSection.Size} in {this}. The offset must be within the section"); - } - else if (Range.EndOffset < 0) + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndSectionParent, $"Invalid null {nameof(Range)}.{nameof(Range.EndSection)} in {this}. The section must be attached to the same {nameof(ElfObjectFile)} than this instance"); + } + else if ((Range.EndOffset >= 0 && (ulong)Range.EndOffset >= range.EndSection.Size)) + { + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndOffset, $"Invalid {nameof(Range)}.{nameof(Range.EndOffset)}: {Range.EndOffset} cannot be >= {nameof(Range)}.{nameof(ElfSegmentRange.EndSection)}.{nameof(ElfSection.Size)}: {range.EndSection.Size} in {this}. The offset must be within the section"); + } + else if (Range.EndOffset < 0) + { + var endOffset = (long)range.EndSection.Size + Range.EndOffset; + if (endOffset < 0) { - var endOffset = (long)range.EndSection.Size + Range.EndOffset; - if (endOffset < 0) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndOffset, $"Invalid relative {nameof(Range)}.{nameof(Range.EndOffset)}: {Range.EndOffset}. The resulting end offset {endOffset} with {nameof(Range)}.{nameof(ElfSegmentRange.EndSection)}.{nameof(ElfSection.Size)}: {range.EndSection.Size} cannot be < 0 in {this}. The offset must be within the section"); - } + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeEndOffset, $"Invalid relative {nameof(Range)}.{nameof(Range.EndOffset)}: {Range.EndOffset}. The resulting end offset {endOffset} with {nameof(Range)}.{nameof(ElfSegmentRange.EndSection)}.{nameof(ElfSection.Size)}: {range.EndSection.Size} cannot be < 0 in {this}. The offset must be within the section"); } } + } - if (Range.BeginSection?.Parent != null && Range.EndSection?.Parent != null) + if (Range.BeginSection?.Parent != null && Range.EndSection?.Parent != null) + { + if (Range.BeginSection.Index > Range.EndSection.Index) { - if (Range.BeginSection.Index > Range.EndSection.Index) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeIndices, $"Invalid index order between {nameof(Range)}.{nameof(ElfSegmentRange.BeginSection)}.{nameof(ElfSegment.Index)}: {Range.BeginSection.Index} and {nameof(Range)}.{nameof(ElfSegmentRange.EndSection)}.{nameof(ElfSegment.Index)}: {Range.EndSection.Index} in {this}. The from index must be <= to the end index."); - } + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentRangeIndices, $"Invalid index order between {nameof(Range)}.{nameof(ElfSegmentRange.BeginSection)}.{nameof(ElfSegment.Index)}: {Range.BeginSection.Index} and {nameof(Range)}.{nameof(ElfSegmentRange.EndSection)}.{nameof(ElfSegment.Index)}: {Range.EndSection.Index} in {this}. The from index must be <= to the end index."); } } } + } - - public override string ToString() - { - return $"Segment [{Index}]"; - } + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"[{Index}]"); + return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSegmentFlags.cs b/src/LibObjectFile/Elf/ElfSegmentFlags.cs index dd82cd9..b076e90 100644 --- a/src/LibObjectFile/Elf/ElfSegmentFlags.cs +++ b/src/LibObjectFile/Elf/ElfSegmentFlags.cs @@ -4,58 +4,57 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines a segment flags +/// +public readonly struct ElfSegmentFlags : IEquatable { - /// - /// Defines a segment flags - /// - public readonly struct ElfSegmentFlags : IEquatable - { - public ElfSegmentFlags(uint value) - { - Value = value; - } - - public ElfSegmentFlags(ElfSegmentFlagsCore value) - { - Value = (uint)value; - } + public ElfSegmentFlags(uint value) + { + Value = value; + } + + public ElfSegmentFlags(ElfSegmentFlagsCore value) + { + Value = (uint)value; + } - public readonly uint Value; - - public bool Equals(ElfSegmentFlags other) - { - return Value == other.Value; - } - - public override bool Equals(object? obj) - { - return obj is ElfSegmentFlags other && Equals(other); - } - - public override int GetHashCode() - { - return (int) Value; - } - - public static bool operator ==(ElfSegmentFlags left, ElfSegmentFlags right) - { - return left.Equals(right); - } - - public static bool operator !=(ElfSegmentFlags left, ElfSegmentFlags right) - { - return !left.Equals(right); - } - - public override string ToString() - { - return $"SegmentFlags {((ElfSegmentFlagsCore)(Value&3))} 0x{Value:X8}"; - } - - public static implicit operator ElfSegmentFlags(ElfSegmentFlagsCore segmentFlagsCore) - { - return new ElfSegmentFlags(segmentFlagsCore); - } + public readonly uint Value; + + public bool Equals(ElfSegmentFlags other) + { + return Value == other.Value; + } + + public override bool Equals(object? obj) + { + return obj is ElfSegmentFlags other && Equals(other); + } + + public override int GetHashCode() + { + return (int) Value; + } + + public static bool operator ==(ElfSegmentFlags left, ElfSegmentFlags right) + { + return left.Equals(right); + } + + public static bool operator !=(ElfSegmentFlags left, ElfSegmentFlags right) + { + return !left.Equals(right); + } + + public override string ToString() + { + return $"SegmentFlags {((ElfSegmentFlagsCore)(Value&3))} 0x{Value:X8}"; + } + + public static implicit operator ElfSegmentFlags(ElfSegmentFlagsCore segmentFlagsCore) + { + return new ElfSegmentFlags(segmentFlagsCore); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSegmentFlagsCore.cs b/src/LibObjectFile/Elf/ElfSegmentFlagsCore.cs index 8a67b00..2b1bf65 100644 --- a/src/LibObjectFile/Elf/ElfSegmentFlagsCore.cs +++ b/src/LibObjectFile/Elf/ElfSegmentFlagsCore.cs @@ -4,32 +4,31 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines the core part of +/// +[Flags] +public enum ElfSegmentFlagsCore : uint { /// - /// Defines the core part of + /// Segment flags is undefined /// - [Flags] - public enum ElfSegmentFlagsCore : uint - { - /// - /// Segment flags is undefined - /// - None = 0, + None = 0, - /// - /// Segment is executable - /// - Executable = ElfNative.PF_X, + /// + /// Segment is executable + /// + Executable = ElfNative.PF_X, - /// - /// Segment is writable - /// - Writable = ElfNative.PF_W, + /// + /// Segment is writable + /// + Writable = ElfNative.PF_W, - /// - /// Segment is readable - /// - Readable = ElfNative.PF_R, - } + /// + /// Segment is readable + /// + Readable = ElfNative.PF_R, } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSegmentRange.cs b/src/LibObjectFile/Elf/ElfSegmentRange.cs index 88035b3..bfb1753 100644 --- a/src/LibObjectFile/Elf/ElfSegmentRange.cs +++ b/src/LibObjectFile/Elf/ElfSegmentRange.cs @@ -4,143 +4,142 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines the range of section a segment is bound to. +/// +public readonly struct ElfSegmentRange : IEquatable { + public static readonly ElfSegmentRange Empty = new ElfSegmentRange(); + /// - /// Defines the range of section a segment is bound to. + /// Creates a new instance that is bound to an entire section/ /// - public readonly struct ElfSegmentRange : IEquatable + /// The section to be bound to + public ElfSegmentRange(ElfSection section) { - public static readonly ElfSegmentRange Empty = new ElfSegmentRange(); + BeginSection = section ?? throw new ArgumentNullException(nameof(section)); + BeginOffset = 0; + EndSection = section; + EndOffset = -1; + } - /// - /// Creates a new instance that is bound to an entire section/ - /// - /// The section to be bound to - public ElfSegmentRange(ElfSection section) + /// + /// Creates a new instance that is bound to a range of section. + /// + /// The first section. + /// The offset inside the first section. + /// The last section. + /// The offset in the last section + public ElfSegmentRange(ElfSection beginSection, ulong beginOffset, ElfSection endSection, long endOffset) + { + BeginSection = beginSection ?? throw new ArgumentNullException(nameof(beginSection)); + BeginOffset = beginOffset; + EndSection = endSection ?? throw new ArgumentNullException(nameof(endSection)); + EndOffset = endOffset; + if (BeginSection.Index > EndSection.Index) { - BeginSection = section ?? throw new ArgumentNullException(nameof(section)); - BeginOffset = 0; - EndSection = section; - EndOffset = -1; + throw new ArgumentOutOfRangeException(nameof(beginSection), $"The {nameof(beginSection)}.{nameof(ElfSection.Index)} = {BeginSection.Index} is > {nameof(endSection)}.{nameof(ElfSection.Index)} = {EndSection.Index}, while it must be <="); } + } + + /// + /// The first section. + /// + public readonly ElfSection? BeginSection; + + /// + /// The relative offset in . + /// + public readonly ulong BeginOffset; + + /// + /// The last section. + /// + public readonly ElfSection? EndSection; + + /// + /// The offset in the last section. If the offset is < 0, then the actual offset starts from end of the section where finalEndOffset = section.Size + EndOffset. + /// + public readonly long EndOffset; - /// - /// Creates a new instance that is bound to a range of section. - /// - /// The first section. - /// The offset inside the first section. - /// The last section. - /// The offset in the last section - public ElfSegmentRange(ElfSection beginSection, ulong beginOffset, ElfSection endSection, long endOffset) + /// + /// Gets a boolean indicating if this section is empty. + /// + public bool IsEmpty => this == Empty; + + /// + /// Returns the absolute offset of this range taking into account the .. + /// + public ulong Offset + { + get { - BeginSection = beginSection ?? throw new ArgumentNullException(nameof(beginSection)); - BeginOffset = beginOffset; - EndSection = endSection ?? throw new ArgumentNullException(nameof(endSection)); - EndOffset = endOffset; - if (BeginSection.Index > EndSection.Index) + // If this Begin/End section are not attached we can't calculate any meaningful size + if (BeginSection?.Parent == null || EndSection?.Parent == null || BeginSection?.Parent != EndSection?.Parent) { - throw new ArgumentOutOfRangeException(nameof(beginSection), $"The {nameof(beginSection)}.{nameof(ElfSection.Index)} = {BeginSection.Index} is > {nameof(endSection)}.{nameof(ElfSection.Index)} = {EndSection.Index}, while it must be <="); + return 0; } - } - - /// - /// The first section. - /// - public readonly ElfSection? BeginSection; - - /// - /// The relative offset in . - /// - public readonly ulong BeginOffset; - - /// - /// The last section. - /// - public readonly ElfSection? EndSection; - - /// - /// The offset in the last section. If the offset is < 0, then the actual offset starts from end of the section where finalEndOffset = section.Size + EndOffset. - /// - public readonly long EndOffset; - - /// - /// Gets a boolean indicating if this section is empty. - /// - public bool IsEmpty => this == Empty; - - /// - /// Returns the absolute offset of this range taking into account the .. - /// - public ulong Offset - { - get - { - // If this Begin/End section are not attached we can't calculate any meaningful size - if (BeginSection?.Parent == null || EndSection?.Parent == null || BeginSection?.Parent != EndSection?.Parent) - { - return 0; - } - return BeginSection!.Position + BeginOffset; - } + return BeginSection!.Position + BeginOffset; } + } - /// - /// Returns the size of this range taking into account the size of each section involved in this range. - /// - public ulong Size + /// + /// Returns the size of this range taking into account the size of each section involved in this range. + /// + public ulong Size + { + get { - get + // If this Begin/End section are not attached we can't calculate any meaningful size + if (BeginSection?.Parent == null || EndSection?.Parent == null || BeginSection.Parent != EndSection.Parent) { - // If this Begin/End section are not attached we can't calculate any meaningful size - if (BeginSection?.Parent == null || EndSection?.Parent == null || BeginSection.Parent != EndSection.Parent) - { - return 0; - } - - ulong size = EndSection.Position - BeginSection.Position; - size -= BeginOffset; - size += EndOffset < 0 ? (ulong)((long)EndSection.Size + EndOffset + 1) : (ulong)(EndOffset + 1); - return size; + return 0; } + + ulong size = EndSection.Position - BeginSection.Position; + size -= BeginOffset; + size += EndOffset < 0 ? (ulong)((long)EndSection.Size + EndOffset + 1) : (ulong)(EndOffset + 1); + return size; } + } - public bool Equals(ElfSegmentRange other) - { - return Equals(BeginSection, other.BeginSection) && BeginOffset == other.BeginOffset && Equals(EndSection, other.EndSection) && EndOffset == other.EndOffset; - } + public bool Equals(ElfSegmentRange other) + { + return Equals(BeginSection, other.BeginSection) && BeginOffset == other.BeginOffset && Equals(EndSection, other.EndSection) && EndOffset == other.EndOffset; + } - public override bool Equals(object? obj) - { - return obj is ElfSegmentRange other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is ElfSegmentRange other && Equals(other); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = (BeginSection != null ? BeginSection.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ BeginOffset.GetHashCode(); - hashCode = (hashCode * 397) ^ (EndSection != null ? EndSection.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ EndOffset.GetHashCode(); - return hashCode; - } + var hashCode = (BeginSection != null ? BeginSection.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ BeginOffset.GetHashCode(); + hashCode = (hashCode * 397) ^ (EndSection != null ? EndSection.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ EndOffset.GetHashCode(); + return hashCode; } + } - public static bool operator ==(ElfSegmentRange left, ElfSegmentRange right) - { - return left.Equals(right); - } + public static bool operator ==(ElfSegmentRange left, ElfSegmentRange right) + { + return left.Equals(right); + } - public static bool operator !=(ElfSegmentRange left, ElfSegmentRange right) - { - return !left.Equals(right); - } + public static bool operator !=(ElfSegmentRange left, ElfSegmentRange right) + { + return !left.Equals(right); + } - public static implicit operator ElfSegmentRange(ElfSection? section) - { - return section is null ? Empty : new ElfSegmentRange(section); - } + public static implicit operator ElfSegmentRange(ElfSection? section) + { + return section is null ? Empty : new ElfSegmentRange(section); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSegmentType.cs b/src/LibObjectFile/Elf/ElfSegmentType.cs index 7ede7b6..4e8d628 100644 --- a/src/LibObjectFile/Elf/ElfSegmentType.cs +++ b/src/LibObjectFile/Elf/ElfSegmentType.cs @@ -4,58 +4,57 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines a segment type +/// +public readonly struct ElfSegmentType : IEquatable { - /// - /// Defines a segment type - /// - public readonly struct ElfSegmentType : IEquatable - { - public ElfSegmentType(uint value) - { - Value = value; - } - - public ElfSegmentType(ElfSegmentTypeCore value) - { - Value = (uint)value; - } + public ElfSegmentType(uint value) + { + Value = value; + } + + public ElfSegmentType(ElfSegmentTypeCore value) + { + Value = (uint)value; + } - public readonly uint Value; - - public bool Equals(ElfSegmentType other) - { - return Value == other.Value; - } - - public override bool Equals(object? obj) - { - return obj is ElfSegmentType other && Equals(other); - } - - public override int GetHashCode() - { - return (int) Value; - } - - public static bool operator ==(ElfSegmentType left, ElfSegmentType right) - { - return left.Equals(right); - } - - public static bool operator !=(ElfSegmentType left, ElfSegmentType right) - { - return !left.Equals(right); - } - - public override string ToString() - { - return Value < ElfNative.PT_NUM ? $"SegmentType {((ElfSegmentTypeCore) Value)}" : $"SegmentType 0x{Value:X8}"; - } + public readonly uint Value; + + public bool Equals(ElfSegmentType other) + { + return Value == other.Value; + } + + public override bool Equals(object? obj) + { + return obj is ElfSegmentType other && Equals(other); + } + + public override int GetHashCode() + { + return (int) Value; + } + + public static bool operator ==(ElfSegmentType left, ElfSegmentType right) + { + return left.Equals(right); + } + + public static bool operator !=(ElfSegmentType left, ElfSegmentType right) + { + return !left.Equals(right); + } + + public override string ToString() + { + return Value < ElfNative.PT_NUM ? $"SegmentType {((ElfSegmentTypeCore) Value)}" : $"SegmentType 0x{Value:X8}"; + } - public static implicit operator ElfSegmentType(ElfSegmentTypeCore segmentTypeCore) - { - return new ElfSegmentType(segmentTypeCore); - } + public static implicit operator ElfSegmentType(ElfSegmentTypeCore segmentTypeCore) + { + return new ElfSegmentType(segmentTypeCore); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSegmentTypeCore.cs b/src/LibObjectFile/Elf/ElfSegmentTypeCore.cs index 469dc41..17f27e0 100644 --- a/src/LibObjectFile/Elf/ElfSegmentTypeCore.cs +++ b/src/LibObjectFile/Elf/ElfSegmentTypeCore.cs @@ -2,51 +2,50 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines a core +/// +public enum ElfSegmentTypeCore : uint { /// - /// Defines a core - /// - public enum ElfSegmentTypeCore : uint - { - /// - /// Program header table entry unused - /// - Null = ElfNative.PT_NULL, - - /// - /// Loadable program segment - /// - Load = ElfNative.PT_LOAD, - - /// - /// Dynamic linking information - /// - Dynamic = ElfNative.PT_DYNAMIC, - - /// - /// Program interpreter - /// - Interpreter = ElfNative.PT_INTERP, - - /// - /// Auxiliary information - /// - Note = ElfNative.PT_NOTE, - - /// - /// Reserved - /// - SectionHeaderLib = ElfNative.PT_SHLIB, - - /// - /// Entry for header table itself - /// - ProgramHeader = ElfNative.PT_PHDR, - - /// - /// Thread-local storage segment - /// - Tls = ElfNative.PT_TLS, - } + /// Program header table entry unused + /// + Null = ElfNative.PT_NULL, + + /// + /// Loadable program segment + /// + Load = ElfNative.PT_LOAD, + + /// + /// Dynamic linking information + /// + Dynamic = ElfNative.PT_DYNAMIC, + + /// + /// Program interpreter + /// + Interpreter = ElfNative.PT_INTERP, + + /// + /// Auxiliary information + /// + Note = ElfNative.PT_NOTE, + + /// + /// Reserved + /// + SectionHeaderLib = ElfNative.PT_SHLIB, + + /// + /// Entry for header table itself + /// + ProgramHeader = ElfNative.PT_PHDR, + + /// + /// Thread-local storage segment + /// + Tls = ElfNative.PT_TLS, } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfString.cs b/src/LibObjectFile/Elf/ElfString.cs index 7547d0c..f694387 100644 --- a/src/LibObjectFile/Elf/ElfString.cs +++ b/src/LibObjectFile/Elf/ElfString.cs @@ -4,108 +4,107 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines a string with the associated index in the string table. +/// +public readonly struct ElfString : IEquatable { - /// - /// Defines a string with the associated index in the string table. - /// - public readonly struct ElfString : IEquatable + private ElfString(string? value, uint index) { - private ElfString(string? value, uint index) - { - Value = value; - Index = index; - } + Value = value; + Index = index; + } - public ElfString(string? value) - { - Value = value; - Index = 0; - } + public ElfString(string? value) + { + Value = value; + Index = 0; + } - public ElfString(uint index) - { - Value = null; - Index = index; - } + public ElfString(uint index) + { + Value = null; + Index = index; + } - public readonly string? Value; + public readonly string? Value; - public readonly uint Index; + public readonly uint Index; - public bool IsEmpty => Value == null && Index == 0; + public bool IsEmpty => Value == null && Index == 0; - public bool Equals(ElfString other) - { - return (Value ?? string.Empty) == (other.Value ?? string.Empty) && Index == other.Index; - } + public bool Equals(ElfString other) + { + return (Value ?? string.Empty) == (other.Value ?? string.Empty) && Index == other.Index; + } - public override bool Equals(object? obj) - { - return obj is ElfString other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is ElfString other && Equals(other); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - return ((Value ?? string.Empty).GetHashCode() * 397) ^ (int) Index; - } + return ((Value ?? string.Empty).GetHashCode() * 397) ^ (int) Index; } + } - public static bool operator ==(ElfString left, ElfString right) - { - return left.Equals(right); - } + public static bool operator ==(ElfString left, ElfString right) + { + return left.Equals(right); + } - public static bool operator !=(ElfString left, ElfString right) - { - return !left.Equals(right); - } + public static bool operator !=(ElfString left, ElfString right) + { + return !left.Equals(right); + } - public static bool operator ==(string left, ElfString right) - { - return string.Equals(left, right.Value); - } + public static bool operator ==(string left, ElfString right) + { + return string.Equals(left, right.Value); + } - public static bool operator !=(ElfString left, string right) - { - return !string.Equals(left.Value, right); - } + public static bool operator !=(ElfString left, string right) + { + return !string.Equals(left.Value, right); + } - public static bool operator ==(ElfString right, string left) - { - return string.Equals(left, right.Value); - } + public static bool operator ==(ElfString right, string left) + { + return string.Equals(left, right.Value); + } - public static bool operator !=(string right, ElfString left) - { - return !string.Equals(left.Value, right); - } + public static bool operator !=(string right, ElfString left) + { + return !string.Equals(left.Value, right); + } - public static implicit operator string?(ElfString elfString) - { - return elfString.Value; - } + public static implicit operator string?(ElfString elfString) + { + return elfString.Value; + } - public static implicit operator ElfString(string text) - { - return new ElfString(text); - } + public static implicit operator ElfString(string text) + { + return new ElfString(text); + } - public ElfString WithName(string name) - { - return new ElfString(name, Index); - } + public ElfString WithName(string name) + { + return new ElfString(name, Index); + } - public ElfString WithIndex(uint index) - { - return new ElfString(Value, index); - } + public ElfString WithIndex(uint index) + { + return new ElfString(Value, index); + } - public override string ToString() - { - return Value ?? $"0x{Index:x8}"; - } + public override string ToString() + { + return Value ?? $"0x{Index:x8}"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfVisitorContext.cs b/src/LibObjectFile/Elf/ElfVisitorContext.cs new file mode 100644 index 0000000..86b4059 --- /dev/null +++ b/src/LibObjectFile/Elf/ElfVisitorContext.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.Elf; + +public class ElfVisitorContext : VisitorContextBase +{ + internal ElfVisitorContext(ElfObjectFile elfObjectFile, DiagnosticBag diagnostics) : base(elfObjectFile, diagnostics) + { + } +} \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfWriter.cs b/src/LibObjectFile/Elf/ElfWriter.cs index 46740c1..312bbb0 100644 --- a/src/LibObjectFile/Elf/ElfWriter.cs +++ b/src/LibObjectFile/Elf/ElfWriter.cs @@ -6,47 +6,45 @@ using System.Buffers; using System.IO; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Base class for writing an to a . +/// +public abstract class ElfWriter : ObjectFileReaderWriter, IElfEncoder { - /// - /// Base class for writing an to a . - /// - public abstract class ElfWriter : ObjectFileReaderWriter, IElfEncoder + private protected ElfWriter(ElfObjectFile objectFile, Stream stream) : base(objectFile, stream) { - private protected ElfWriter(ElfObjectFile objectFile, Stream stream) : base(stream) - { - ObjectFile = objectFile ?? throw new ArgumentNullException(nameof(objectFile)); - } + } - private protected ElfObjectFile ObjectFile { get; } + public ElfObjectFile ObjectFile => (ElfObjectFile)base.File; - internal abstract void Write(); + internal abstract void Write(); - public override bool IsReadOnly => false; + public override bool IsReadOnly => false; - internal static ElfWriter Create(ElfObjectFile objectFile, Stream stream) - { - var thisComputerEncoding = BitConverter.IsLittleEndian ? ElfEncoding.Lsb : ElfEncoding.Msb; - return objectFile.Encoding == thisComputerEncoding ? (ElfWriter) new ElfWriterDirect(objectFile, stream) : new ElfWriterSwap(objectFile, stream); - } - - public abstract void Encode(out ElfNative.Elf32_Half dest, ushort value); - public abstract void Encode(out ElfNative.Elf64_Half dest, ushort value); - public abstract void Encode(out ElfNative.Elf32_Word dest, uint value); - public abstract void Encode(out ElfNative.Elf64_Word dest, uint value); - public abstract void Encode(out ElfNative.Elf32_Sword dest, int value); - public abstract void Encode(out ElfNative.Elf64_Sword dest, int value); - public abstract void Encode(out ElfNative.Elf32_Xword dest, ulong value); - public abstract void Encode(out ElfNative.Elf32_Sxword dest, long value); - public abstract void Encode(out ElfNative.Elf64_Xword dest, ulong value); - public abstract void Encode(out ElfNative.Elf64_Sxword dest, long value); - public abstract void Encode(out ElfNative.Elf32_Addr dest, uint value); - public abstract void Encode(out ElfNative.Elf64_Addr dest, ulong value); - public abstract void Encode(out ElfNative.Elf32_Off dest, uint offset); - public abstract void Encode(out ElfNative.Elf64_Off dest, ulong offset); - public abstract void Encode(out ElfNative.Elf32_Section dest, ushort index); - public abstract void Encode(out ElfNative.Elf64_Section dest, ushort index); - public abstract void Encode(out ElfNative.Elf32_Versym dest, ushort value); - public abstract void Encode(out ElfNative.Elf64_Versym dest, ushort value); + internal static ElfWriter Create(ElfObjectFile objectFile, Stream stream) + { + var thisComputerEncoding = BitConverter.IsLittleEndian ? ElfEncoding.Lsb : ElfEncoding.Msb; + return objectFile.Encoding == thisComputerEncoding ? (ElfWriter) new ElfWriterDirect(objectFile, stream) : new ElfWriterSwap(objectFile, stream); } + + public abstract void Encode(out ElfNative.Elf32_Half dest, ushort value); + public abstract void Encode(out ElfNative.Elf64_Half dest, ushort value); + public abstract void Encode(out ElfNative.Elf32_Word dest, uint value); + public abstract void Encode(out ElfNative.Elf64_Word dest, uint value); + public abstract void Encode(out ElfNative.Elf32_Sword dest, int value); + public abstract void Encode(out ElfNative.Elf64_Sword dest, int value); + public abstract void Encode(out ElfNative.Elf32_Xword dest, ulong value); + public abstract void Encode(out ElfNative.Elf32_Sxword dest, long value); + public abstract void Encode(out ElfNative.Elf64_Xword dest, ulong value); + public abstract void Encode(out ElfNative.Elf64_Sxword dest, long value); + public abstract void Encode(out ElfNative.Elf32_Addr dest, uint value); + public abstract void Encode(out ElfNative.Elf64_Addr dest, ulong value); + public abstract void Encode(out ElfNative.Elf32_Off dest, uint offset); + public abstract void Encode(out ElfNative.Elf64_Off dest, ulong offset); + public abstract void Encode(out ElfNative.Elf32_Section dest, ushort index); + public abstract void Encode(out ElfNative.Elf64_Section dest, ushort index); + public abstract void Encode(out ElfNative.Elf32_Versym dest, ushort value); + public abstract void Encode(out ElfNative.Elf64_Versym dest, ushort value); } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfWriterDirect.cs b/src/LibObjectFile/Elf/ElfWriterDirect.cs index 329195f..b6f27bf 100644 --- a/src/LibObjectFile/Elf/ElfWriterDirect.cs +++ b/src/LibObjectFile/Elf/ElfWriterDirect.cs @@ -4,15 +4,14 @@ using System.IO; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Internal implementation of with a . +/// +internal sealed class ElfWriterDirect : ElfWriter { - /// - /// Internal implementation of with a . - /// - internal sealed class ElfWriterDirect : ElfWriter + public ElfWriterDirect(ElfObjectFile elfObjectFile, Stream stream) : base(elfObjectFile, stream) { - public ElfWriterDirect(ElfObjectFile elfObjectFile, Stream stream) : base(elfObjectFile, stream) - { - } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfWriterSwap.cs b/src/LibObjectFile/Elf/ElfWriterSwap.cs index 3bd6fc2..7af0131 100644 --- a/src/LibObjectFile/Elf/ElfWriterSwap.cs +++ b/src/LibObjectFile/Elf/ElfWriterSwap.cs @@ -4,15 +4,14 @@ using System.IO; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Internal implementation of with a . +/// +internal sealed class ElfWriterSwap : ElfWriter { - /// - /// Internal implementation of with a . - /// - internal sealed class ElfWriterSwap : ElfWriter + public ElfWriterSwap(ElfObjectFile elfObjectFile, Stream stream) : base(elfObjectFile, stream) { - public ElfWriterSwap(ElfObjectFile elfObjectFile, Stream stream) : base(elfObjectFile, stream) - { - } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs b/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs index 1f5d416..f831011 100644 --- a/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs +++ b/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs @@ -5,312 +5,311 @@ using System; using System.IO; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +using static ElfNative; + +/// +/// Internal implementation of to write to a stream an instance. +/// +/// The encoder used for LSB/MSB conversion +internal abstract class ElfWriter : ElfWriter where TEncoder : struct, IElfEncoder { - using static ElfNative; + private TEncoder _encoder; + private ulong _startOfFile; - /// - /// Internal implementation of to write to a stream an instance. - /// - /// The encoder used for LSB/MSB conversion - internal abstract class ElfWriter : ElfWriter where TEncoder : struct, IElfEncoder + protected ElfWriter(ElfObjectFile objectFile, Stream stream) : base(objectFile, stream) { - private TEncoder _encoder; - private ulong _startOfFile; + _encoder = new TEncoder(); + } - protected ElfWriter(ElfObjectFile objectFile, Stream stream) : base(objectFile, stream) + internal override void Write() + { + _startOfFile = (ulong)Stream.Position; + WriteHeader(); + CheckProgramHeaders(); + WriteSections(); + } + + private ElfObjectFile.ElfObjectLayout Layout => ObjectFile.Layout; + + private void WriteHeader() + { + if (ObjectFile.FileClass == ElfFileClass.Is32) { - _encoder = new TEncoder(); + WriteSectionHeader32(); } - - internal override void Write() + else { - _startOfFile = (ulong)Stream.Position; - WriteHeader(); - CheckProgramHeaders(); - WriteSections(); + WriteSectionHeader64(); } + } - private ElfObjectFile.ElfObjectLayout Layout => ObjectFile.Layout; + public override void Encode(out Elf32_Half dest, ushort value) + { + _encoder.Encode(out dest, value); + } - private void WriteHeader() - { - if (ObjectFile.FileClass == ElfFileClass.Is32) - { - WriteSectionHeader32(); - } - else - { - WriteSectionHeader64(); - } - } + public override void Encode(out Elf64_Half dest, ushort value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf32_Half dest, ushort value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf32_Word dest, uint value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf64_Half dest, ushort value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf64_Word dest, uint value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf32_Word dest, uint value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf32_Sword dest, int value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf64_Word dest, uint value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf64_Sword dest, int value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf32_Sword dest, int value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf32_Xword dest, ulong value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf64_Sword dest, int value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf32_Sxword dest, long value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf32_Xword dest, ulong value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf64_Xword dest, ulong value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf32_Sxword dest, long value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf64_Sxword dest, long value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf64_Xword dest, ulong value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf32_Addr dest, uint value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf64_Sxword dest, long value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf64_Addr dest, ulong value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf32_Addr dest, uint value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf32_Off dest, uint offset) + { + _encoder.Encode(out dest, offset); + } - public override void Encode(out Elf64_Addr dest, ulong value) - { - _encoder.Encode(out dest, value); - } + public override void Encode(out Elf64_Off dest, ulong offset) + { + _encoder.Encode(out dest, offset); + } - public override void Encode(out Elf32_Off dest, uint offset) - { - _encoder.Encode(out dest, offset); - } + public override void Encode(out Elf32_Section dest, ushort index) + { + _encoder.Encode(out dest, index); + } - public override void Encode(out Elf64_Off dest, ulong offset) - { - _encoder.Encode(out dest, offset); - } + public override void Encode(out Elf64_Section dest, ushort index) + { + _encoder.Encode(out dest, index); + } - public override void Encode(out Elf32_Section dest, ushort index) - { - _encoder.Encode(out dest, index); - } + public override void Encode(out Elf32_Versym dest, ushort value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf64_Section dest, ushort index) - { - _encoder.Encode(out dest, index); - } + public override void Encode(out Elf64_Versym dest, ushort value) + { + _encoder.Encode(out dest, value); + } - public override void Encode(out Elf32_Versym dest, ushort value) - { - _encoder.Encode(out dest, value); - } + private unsafe void WriteSectionHeader32() + { + var hdr = new Elf32_Ehdr(); + ObjectFile.CopyIdentTo(new Span(hdr.e_ident, EI_NIDENT)); + + _encoder.Encode(out hdr.e_type, (ushort)ObjectFile.FileType); + _encoder.Encode(out hdr.e_machine, (ushort)ObjectFile.Arch.Value); + _encoder.Encode(out hdr.e_version, EV_CURRENT); + _encoder.Encode(out hdr.e_entry, (uint)ObjectFile.EntryPointAddress); + _encoder.Encode(out hdr.e_ehsize, Layout.SizeOfElfHeader); + _encoder.Encode(out hdr.e_flags, (uint)ObjectFile.Flags); + + // program headers + _encoder.Encode(out hdr.e_phoff, (uint)Layout.OffsetOfProgramHeaderTable); + _encoder.Encode(out hdr.e_phentsize, Layout.SizeOfProgramHeaderEntry); + _encoder.Encode(out hdr.e_phnum, (ushort) ObjectFile.Segments.Count); + + // entries for sections + _encoder.Encode(out hdr.e_shoff, (uint)Layout.OffsetOfSectionHeaderTable); + _encoder.Encode(out hdr.e_shentsize, Layout.SizeOfSectionHeaderEntry); + _encoder.Encode(out hdr.e_shnum, ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE ? (ushort)0 : (ushort)ObjectFile.VisibleSectionCount); + uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; + _encoder.Encode(out hdr.e_shstrndx, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? (ushort)ElfNative.SHN_XINDEX : (ushort)shstrSectionIndex); + + Write(hdr); + } + + private unsafe void WriteSectionHeader64() + { + var hdr = new Elf64_Ehdr(); + ObjectFile.CopyIdentTo(new Span(hdr.e_ident, EI_NIDENT)); + + _encoder.Encode(out hdr.e_type, (ushort)ObjectFile.FileType); + _encoder.Encode(out hdr.e_machine, (ushort)ObjectFile.Arch.Value); + _encoder.Encode(out hdr.e_version, EV_CURRENT); + _encoder.Encode(out hdr.e_entry, ObjectFile.EntryPointAddress); + _encoder.Encode(out hdr.e_ehsize, Layout.SizeOfElfHeader); + _encoder.Encode(out hdr.e_flags, (uint)ObjectFile.Flags); + + // program headers + _encoder.Encode(out hdr.e_phoff, Layout.OffsetOfProgramHeaderTable); + _encoder.Encode(out hdr.e_phentsize, Layout.SizeOfProgramHeaderEntry); + _encoder.Encode(out hdr.e_phnum, (ushort)ObjectFile.Segments.Count); + + // entries for sections + _encoder.Encode(out hdr.e_shoff, Layout.OffsetOfSectionHeaderTable); + _encoder.Encode(out hdr.e_shentsize, (ushort)sizeof(Elf64_Shdr)); + _encoder.Encode(out hdr.e_shnum, ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE ? (ushort)0 : (ushort)ObjectFile.VisibleSectionCount); + uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; + _encoder.Encode(out hdr.e_shstrndx, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? (ushort)ElfNative.SHN_XINDEX : (ushort)shstrSectionIndex); + + Write(hdr); + } - public override void Encode(out Elf64_Versym dest, ushort value) + private void CheckProgramHeaders() + { + if (ObjectFile.Segments.Count == 0) { - _encoder.Encode(out dest, value); + return; } - private unsafe void WriteSectionHeader32() + var offset = (ulong)Stream.Position - _startOfFile; + if (offset != Layout.OffsetOfProgramHeaderTable) { - var hdr = new Elf32_Ehdr(); - ObjectFile.CopyIdentTo(new Span(hdr.e_ident, EI_NIDENT)); - - _encoder.Encode(out hdr.e_type, (ushort)ObjectFile.FileType); - _encoder.Encode(out hdr.e_machine, (ushort)ObjectFile.Arch.Value); - _encoder.Encode(out hdr.e_version, EV_CURRENT); - _encoder.Encode(out hdr.e_entry, (uint)ObjectFile.EntryPointAddress); - _encoder.Encode(out hdr.e_ehsize, Layout.SizeOfElfHeader); - _encoder.Encode(out hdr.e_flags, (uint)ObjectFile.Flags); - - // program headers - _encoder.Encode(out hdr.e_phoff, (uint)Layout.OffsetOfProgramHeaderTable); - _encoder.Encode(out hdr.e_phentsize, Layout.SizeOfProgramHeaderEntry); - _encoder.Encode(out hdr.e_phnum, (ushort) ObjectFile.Segments.Count); - - // entries for sections - _encoder.Encode(out hdr.e_shoff, (uint)Layout.OffsetOfSectionHeaderTable); - _encoder.Encode(out hdr.e_shentsize, Layout.SizeOfSectionHeaderEntry); - _encoder.Encode(out hdr.e_shnum, ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE ? (ushort)0 : (ushort)ObjectFile.VisibleSectionCount); - uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; - _encoder.Encode(out hdr.e_shstrndx, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? (ushort)ElfNative.SHN_XINDEX : (ushort)shstrSectionIndex); - - Write(hdr); + throw new InvalidOperationException("Internal error. Unexpected offset for ProgramHeaderTable"); } + } - private unsafe void WriteSectionHeader64() - { - var hdr = new Elf64_Ehdr(); - ObjectFile.CopyIdentTo(new Span(hdr.e_ident, EI_NIDENT)); - - _encoder.Encode(out hdr.e_type, (ushort)ObjectFile.FileType); - _encoder.Encode(out hdr.e_machine, (ushort)ObjectFile.Arch.Value); - _encoder.Encode(out hdr.e_version, EV_CURRENT); - _encoder.Encode(out hdr.e_entry, ObjectFile.EntryPointAddress); - _encoder.Encode(out hdr.e_ehsize, Layout.SizeOfElfHeader); - _encoder.Encode(out hdr.e_flags, (uint)ObjectFile.Flags); - - // program headers - _encoder.Encode(out hdr.e_phoff, Layout.OffsetOfProgramHeaderTable); - _encoder.Encode(out hdr.e_phentsize, Layout.SizeOfProgramHeaderEntry); - _encoder.Encode(out hdr.e_phnum, (ushort)ObjectFile.Segments.Count); - - // entries for sections - _encoder.Encode(out hdr.e_shoff, Layout.OffsetOfSectionHeaderTable); - _encoder.Encode(out hdr.e_shentsize, (ushort)sizeof(Elf64_Shdr)); - _encoder.Encode(out hdr.e_shnum, ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE ? (ushort)0 : (ushort)ObjectFile.VisibleSectionCount); - uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; - _encoder.Encode(out hdr.e_shstrndx, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? (ushort)ElfNative.SHN_XINDEX : (ushort)shstrSectionIndex); + private void WriteSections() + { + var sections = ObjectFile.Sections; + if (sections.Count == 0) return; - Write(hdr); - } + sections = ObjectFile.GetSectionsOrderedByStreamIndex(); - private void CheckProgramHeaders() + // We write the content all sections including shadows + for (var i = 0; i < sections.Count; i++) { - if (ObjectFile.Segments.Count == 0) - { - return; - } + var section = sections[i]; - var offset = (ulong)Stream.Position - _startOfFile; - if (offset != Layout.OffsetOfProgramHeaderTable) + // Write only section with content + if (section.HasContent) { - throw new InvalidOperationException("Internal error. Unexpected offset for ProgramHeaderTable"); + Stream.Position = (long)(_startOfFile + section.Position); + section.Write(this); } } - private void WriteSections() - { - var sections = ObjectFile.Sections; - if (sections.Count == 0) return; - - sections = ObjectFile.GetSectionsOrderedByStreamIndex(); - - // We write the content all sections including shadows - for (var i = 0; i < sections.Count; i++) - { - var section = sections[i]; - - // Write only section with content - if (section.HasContent) - { - Stream.Position = (long)(_startOfFile + section.Position); - section.WriteInternal(this); - } - } + // Write section header table + WriteSectionHeaderTable(); + } - // Write section header table - WriteSectionHeaderTable(); + private void WriteSectionHeaderTable() + { + var offset = (ulong)Stream.Position - _startOfFile; + var diff = Layout.OffsetOfSectionHeaderTable - offset; + if (diff < 0 || diff > 8) + { + throw new InvalidOperationException("Internal error. Unexpected offset for SectionHeaderTable"); } - - private void WriteSectionHeaderTable() + else if (diff != 0) { - var offset = (ulong)Stream.Position - _startOfFile; - var diff = Layout.OffsetOfSectionHeaderTable - offset; - if (diff < 0 || diff > 8) - { - throw new InvalidOperationException("Internal error. Unexpected offset for SectionHeaderTable"); - } - else if (diff != 0) - { - // Alignment - Stream.Write(stackalloc byte[(int)diff]); - } + // Alignment + Stream.Write(stackalloc byte[(int)diff]); + } - // Then write all regular sections - var sections = ObjectFile.Sections; - for (var i = 0; i < sections.Count; i++) - { - var section = sections[i]; - if (section.IsShadow) continue; - WriteSectionTableEntry(section); - } + // Then write all regular sections + var sections = ObjectFile.Sections; + for (var i = 0; i < sections.Count; i++) + { + var section = sections[i]; + if (section.IsShadow) continue; + WriteSectionTableEntry(section); } + } - private void WriteSectionTableEntry(ElfSection section) + private void WriteSectionTableEntry(ElfSection section) + { + if (ObjectFile.FileClass == ElfFileClass.Is32) { - if (ObjectFile.FileClass == ElfFileClass.Is32) - { - WriteSectionTableEntry32(section); - } - else - { - WriteSectionTableEntry64(section); - } + WriteSectionTableEntry32(section); + } + else + { + WriteSectionTableEntry64(section); } + } - private void WriteSectionTableEntry32(ElfSection section) + private void WriteSectionTableEntry32(ElfSection section) + { + var shdr = new Elf32_Shdr(); + _encoder.Encode(out shdr.sh_name, ObjectFile.SectionHeaderStringTable?.GetOrCreateIndex(section.Name) ?? 0); + _encoder.Encode(out shdr.sh_type, (uint)section.Type); + _encoder.Encode(out shdr.sh_flags, (uint)section.Flags); + _encoder.Encode(out shdr.sh_addr, (uint)section.VirtualAddress); + _encoder.Encode(out shdr.sh_offset, (uint)section.Position); + if (section.Index == 0 && ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE) { - var shdr = new Elf32_Shdr(); - _encoder.Encode(out shdr.sh_name, ObjectFile.SectionHeaderStringTable?.GetOrCreateIndex(section.Name) ?? 0); - _encoder.Encode(out shdr.sh_type, (uint)section.Type); - _encoder.Encode(out shdr.sh_flags, (uint)section.Flags); - _encoder.Encode(out shdr.sh_addr, (uint)section.VirtualAddress); - _encoder.Encode(out shdr.sh_offset, (uint)section.Position); - if (section.Index == 0 && ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE) - { - _encoder.Encode(out shdr.sh_size, ObjectFile.VisibleSectionCount); - uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; - _encoder.Encode(out shdr.sh_link, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? shstrSectionIndex : 0); - } - else - { - _encoder.Encode(out shdr.sh_size, (uint)section.Size); - _encoder.Encode(out shdr.sh_link, section.Link.GetIndex()); - } - _encoder.Encode(out shdr.sh_info, section.Info.GetIndex()); - _encoder.Encode(out shdr.sh_addralign, (uint)section.Alignment); - _encoder.Encode(out shdr.sh_entsize, (uint)section.TableEntrySize); - Write(shdr); + _encoder.Encode(out shdr.sh_size, ObjectFile.VisibleSectionCount); + uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; + _encoder.Encode(out shdr.sh_link, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? shstrSectionIndex : 0); + } + else + { + _encoder.Encode(out shdr.sh_size, (uint)section.Size); + _encoder.Encode(out shdr.sh_link, section.Link.GetIndex()); } + _encoder.Encode(out shdr.sh_info, section.Info.GetIndex()); + _encoder.Encode(out shdr.sh_addralign, (uint)section.Alignment); + _encoder.Encode(out shdr.sh_entsize, (uint)section.TableEntrySize); + Write(shdr); + } - private void WriteSectionTableEntry64(ElfSection section) + private void WriteSectionTableEntry64(ElfSection section) + { + var shdr = new Elf64_Shdr(); + _encoder.Encode(out shdr.sh_name, ObjectFile.SectionHeaderStringTable?.GetOrCreateIndex(section.Name) ?? 0); + _encoder.Encode(out shdr.sh_type, (uint)section.Type); + _encoder.Encode(out shdr.sh_flags, (uint)section.Flags); + _encoder.Encode(out shdr.sh_addr, section.VirtualAddress); + _encoder.Encode(out shdr.sh_offset, section.Position); + if (section.Index == 0 && ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE) { - var shdr = new Elf64_Shdr(); - _encoder.Encode(out shdr.sh_name, ObjectFile.SectionHeaderStringTable?.GetOrCreateIndex(section.Name) ?? 0); - _encoder.Encode(out shdr.sh_type, (uint)section.Type); - _encoder.Encode(out shdr.sh_flags, (uint)section.Flags); - _encoder.Encode(out shdr.sh_addr, section.VirtualAddress); - _encoder.Encode(out shdr.sh_offset, section.Position); - if (section.Index == 0 && ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE) - { - _encoder.Encode(out shdr.sh_size, ObjectFile.VisibleSectionCount); - uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; - _encoder.Encode(out shdr.sh_link, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? shstrSectionIndex : 0); - } - else - { - _encoder.Encode(out shdr.sh_size, section.Size); - _encoder.Encode(out shdr.sh_link, section.Link.GetIndex()); - } - _encoder.Encode(out shdr.sh_info, section.Info.GetIndex()); - _encoder.Encode(out shdr.sh_addralign, section.Alignment); - _encoder.Encode(out shdr.sh_entsize, section.TableEntrySize); - Write(shdr); + _encoder.Encode(out shdr.sh_size, ObjectFile.VisibleSectionCount); + uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; + _encoder.Encode(out shdr.sh_link, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? shstrSectionIndex : 0); + } + else + { + _encoder.Encode(out shdr.sh_size, section.Size); + _encoder.Encode(out shdr.sh_link, section.Link.GetIndex()); } + _encoder.Encode(out shdr.sh_info, section.Info.GetIndex()); + _encoder.Encode(out shdr.sh_addralign, section.Alignment); + _encoder.Encode(out shdr.sh_entsize, section.TableEntrySize); + Write(shdr); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/IElfDecoder.cs b/src/LibObjectFile/Elf/IElfDecoder.cs index eba5309..434a007 100644 --- a/src/LibObjectFile/Elf/IElfDecoder.cs +++ b/src/LibObjectFile/Elf/IElfDecoder.cs @@ -2,48 +2,47 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A decoder interface for the various Elf types that provides decoding of data based on LSB/MSB. +/// +/// +public interface IElfDecoder { - /// - /// A decoder interface for the various Elf types that provides decoding of data based on LSB/MSB. - /// - /// - public interface IElfDecoder - { - ushort Decode(ElfNative.Elf32_Half src); + ushort Decode(ElfNative.Elf32_Half src); - ushort Decode(ElfNative.Elf64_Half src); + ushort Decode(ElfNative.Elf64_Half src); - uint Decode(ElfNative.Elf32_Word src); + uint Decode(ElfNative.Elf32_Word src); - uint Decode(ElfNative.Elf64_Word src); + uint Decode(ElfNative.Elf64_Word src); - int Decode(ElfNative.Elf32_Sword src); + int Decode(ElfNative.Elf32_Sword src); - int Decode(ElfNative.Elf64_Sword src); + int Decode(ElfNative.Elf64_Sword src); - ulong Decode(ElfNative.Elf32_Xword src); + ulong Decode(ElfNative.Elf32_Xword src); - long Decode(ElfNative.Elf32_Sxword src); + long Decode(ElfNative.Elf32_Sxword src); - ulong Decode(ElfNative.Elf64_Xword src); + ulong Decode(ElfNative.Elf64_Xword src); - long Decode(ElfNative.Elf64_Sxword src); + long Decode(ElfNative.Elf64_Sxword src); - uint Decode(ElfNative.Elf32_Addr src); + uint Decode(ElfNative.Elf32_Addr src); - ulong Decode(ElfNative.Elf64_Addr src); + ulong Decode(ElfNative.Elf64_Addr src); - uint Decode(ElfNative.Elf32_Off src); + uint Decode(ElfNative.Elf32_Off src); - ulong Decode(ElfNative.Elf64_Off src); + ulong Decode(ElfNative.Elf64_Off src); - ushort Decode(ElfNative.Elf32_Section src); + ushort Decode(ElfNative.Elf32_Section src); - ushort Decode(ElfNative.Elf64_Section src); + ushort Decode(ElfNative.Elf64_Section src); - ushort Decode(ElfNative.Elf32_Versym src); + ushort Decode(ElfNative.Elf32_Versym src); - ushort Decode(ElfNative.Elf64_Versym src); - } + ushort Decode(ElfNative.Elf64_Versym src); } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/IElfEncoder.cs b/src/LibObjectFile/Elf/IElfEncoder.cs index 702612a..d1e75fc 100644 --- a/src/LibObjectFile/Elf/IElfEncoder.cs +++ b/src/LibObjectFile/Elf/IElfEncoder.cs @@ -2,48 +2,47 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// An encoder interface for the various Elf types that provides encoding of data based on LSB/MSB. +/// +/// +public interface IElfEncoder { - /// - /// An encoder interface for the various Elf types that provides encoding of data based on LSB/MSB. - /// - /// - public interface IElfEncoder - { - void Encode(out ElfNative.Elf32_Half dest, ushort value); + void Encode(out ElfNative.Elf32_Half dest, ushort value); - void Encode(out ElfNative.Elf64_Half dest, ushort value); + void Encode(out ElfNative.Elf64_Half dest, ushort value); - void Encode(out ElfNative.Elf32_Word dest, uint value); + void Encode(out ElfNative.Elf32_Word dest, uint value); - void Encode(out ElfNative.Elf64_Word dest, uint value); + void Encode(out ElfNative.Elf64_Word dest, uint value); - void Encode(out ElfNative.Elf32_Sword dest, int value); + void Encode(out ElfNative.Elf32_Sword dest, int value); - void Encode(out ElfNative.Elf64_Sword dest, int value); + void Encode(out ElfNative.Elf64_Sword dest, int value); - void Encode(out ElfNative.Elf32_Xword dest, ulong value); + void Encode(out ElfNative.Elf32_Xword dest, ulong value); - void Encode(out ElfNative.Elf32_Sxword dest, long value); + void Encode(out ElfNative.Elf32_Sxword dest, long value); - void Encode(out ElfNative.Elf64_Xword dest, ulong value); + void Encode(out ElfNative.Elf64_Xword dest, ulong value); - void Encode(out ElfNative.Elf64_Sxword dest, long value); + void Encode(out ElfNative.Elf64_Sxword dest, long value); - void Encode(out ElfNative.Elf32_Addr dest, uint value); + void Encode(out ElfNative.Elf32_Addr dest, uint value); - void Encode(out ElfNative.Elf64_Addr dest, ulong value); + void Encode(out ElfNative.Elf64_Addr dest, ulong value); - void Encode(out ElfNative.Elf32_Off dest, uint offset); + void Encode(out ElfNative.Elf32_Off dest, uint offset); - void Encode(out ElfNative.Elf64_Off dest, ulong offset); + void Encode(out ElfNative.Elf64_Off dest, ulong offset); - void Encode(out ElfNative.Elf32_Section dest, ushort index); + void Encode(out ElfNative.Elf32_Section dest, ushort index); - void Encode(out ElfNative.Elf64_Section dest, ushort index); + void Encode(out ElfNative.Elf64_Section dest, ushort index); - void Encode(out ElfNative.Elf32_Versym dest, ushort value); + void Encode(out ElfNative.Elf32_Versym dest, ushort value); - void Encode(out ElfNative.Elf64_Versym dest, ushort value); - } + void Encode(out ElfNative.Elf64_Versym dest, ushort value); } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs b/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs index 3d896be..aa0d6cd 100644 --- a/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs @@ -6,63 +6,60 @@ using System.Buffers; using LibObjectFile.Utils; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A shadow section allowing to align the following section from +/// to respect the of this section. +/// This section is used to make sure the offset of the following section will be respect +/// a specific alignment. +/// +public sealed class ElfAlignedShadowSection : ElfShadowSection { + public ElfAlignedShadowSection() : this(0x1000) + { + } + + public ElfAlignedShadowSection(uint upperAlignment) + { + UpperAlignment = upperAlignment; + } + /// - /// A shadow section allowing to align the following section from - /// to respect the of this section. - /// This section is used to make sure the offset of the following section will be respect - /// a specific alignment. + /// Gets or sets teh alignment requirement that this section will ensure for the + /// following sections placed after this section, so that the offset of the following + /// section is respecting the alignment. /// - public sealed class ElfAlignedShadowSection : ElfShadowSection - { - public ElfAlignedShadowSection() : this(0x1000) - { - } + public uint UpperAlignment { get; set; } - public ElfAlignedShadowSection(uint upperAlignment) + public override void UpdateLayout(ElfVisitorContext context) + { + var nextSectionOffset = AlignHelper.AlignToUpper(Position, UpperAlignment); + Size = nextSectionOffset - Position; + if (Size >= int.MaxValue) { - UpperAlignment = upperAlignment; + context.Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidAlignmentOutOfRange, $"Invalid alignment 0x{UpperAlignment:x} resulting in an offset beyond int.MaxValue"); } + } - /// - /// Gets or sets teh alignment requirement that this section will ensure for the - /// following sections placed after this section, so that the offset of the following - /// section is respecting the alignment. - /// - public uint UpperAlignment { get; set; } - - public override void UpdateLayout(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + public override void Read(ElfReader reader) + { + throw new NotSupportedException($"An {nameof(ElfAlignedShadowSection)} does not support read and is only used for writing"); + } - var nextSectionOffset = AlignHelper.AlignToUpper(Position, UpperAlignment); - Size = nextSectionOffset - Position; - if (Size >= int.MaxValue) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidAlignmentOutOfRange, $"Invalid alignment 0x{UpperAlignment:x} resulting in an offset beyond int.MaxValue"); - } - } + public override void Write(ElfWriter writer) + { + if (Size == 0) return; - protected override void Read(ElfReader reader) + var sharedBuffer = ArrayPool.Shared.Rent((int)Size); + Array.Clear(sharedBuffer, 0, sharedBuffer.Length); + try { - throw new NotSupportedException($"An {nameof(ElfAlignedShadowSection)} does not support read and is only used for writing"); + writer.Stream.Write(sharedBuffer, 0, (int) Size); } - - protected override void Write(ElfWriter writer) + finally { - if (Size == 0) return; - - var sharedBuffer = ArrayPool.Shared.Rent((int)Size); - Array.Clear(sharedBuffer, 0, sharedBuffer.Length); - try - { - writer.Stream.Write(sharedBuffer, 0, (int) Size); - } - finally - { - ArrayPool.Shared.Return(sharedBuffer); - } + ArrayPool.Shared.Return(sharedBuffer); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs b/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs index 9fe9818..83c2f8a 100644 --- a/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs @@ -5,78 +5,73 @@ using System; using System.IO; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A custom section associated with its stream of data to read/write. +/// +public sealed class ElfBinarySection : ElfSection { - /// - /// A custom section associated with its stream of data to read/write. - /// - public sealed class ElfBinarySection : ElfSection + public ElfBinarySection() { - public ElfBinarySection() - { - } + } - public ElfBinarySection(Stream stream) - { - Stream = stream ?? throw new ArgumentNullException(nameof(stream)); - } + public ElfBinarySection(Stream stream) + { + Stream = stream ?? throw new ArgumentNullException(nameof(stream)); + } - public override ElfSectionType Type + public override ElfSectionType Type + { + get => base.Type; + set { - get => base.Type; - set + // Don't allow relocation or symbol table to enforce proper usage + if (value == ElfSectionType.Relocation || value == ElfSectionType.RelocationAddends) { - // Don't allow relocation or symbol table to enforce proper usage - if (value == ElfSectionType.Relocation || value == ElfSectionType.RelocationAddends) - { - throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}]. Must be used on a `{nameof(ElfRelocationTable)}` instead"); - } + throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}]. Must be used on a `{nameof(ElfRelocationTable)}` instead"); + } - if (value == ElfSectionType.SymbolTable || value == ElfSectionType.DynamicLinkerSymbolTable) - { - throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}]. Must be used on a `{nameof(ElfSymbolTable)}` instead"); - } - - base.Type = value; + if (value == ElfSectionType.SymbolTable || value == ElfSectionType.DynamicLinkerSymbolTable) + { + throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}]. Must be used on a `{nameof(ElfSymbolTable)}` instead"); } + + base.Type = value; } + } - public override ulong TableEntrySize => OriginalTableEntrySize; + public override ulong TableEntrySize => OriginalTableEntrySize; - /// - /// Gets or sets the associated stream to this section. - /// - public Stream? Stream { get; set; } + /// + /// Gets or sets the associated stream to this section. + /// + public Stream? Stream { get; set; } - protected override void Read(ElfReader reader) - { - Stream = reader.ReadAsStream(Size); - } + public override void Read(ElfReader reader) + { + Stream = reader.ReadAsStream(Size); + } - protected override void Write(ElfWriter writer) - { - if (Stream == null) return; - writer.Write(Stream); - } + public override void Write(ElfWriter writer) + { + if (Stream == null) return; + writer.Write(Stream); + } - public override void UpdateLayout(DiagnosticBag diagnostics) + public override void UpdateLayout(ElfVisitorContext context) + { + if (Type != ElfSectionType.NoBits) { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - - if (Type != ElfSectionType.NoBits) - { - Size = Stream != null ? (ulong)Stream.Length : 0; - } + Size = Stream != null ? (ulong)Stream.Length : 0; } + } - public override void Verify(DiagnosticBag diagnostics) + public override void Verify(ElfVisitorContext context) + { + if (Type == ElfSectionType.NoBits && Stream != null) { - base.Verify(diagnostics); - - if (Type == ElfSectionType.NoBits && Stream != null) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidStreamForSectionNoBits, $"The {Type} section {this} must have a null stream"); - } + context.Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidStreamForSectionNoBits, $"The {Type} section {this} must have a null stream"); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs b/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs index 00a6aa3..8a6181d 100644 --- a/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs @@ -5,35 +5,32 @@ using System; using System.IO; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Equivalent of but used for shadow. +/// +public sealed class ElfBinaryShadowSection : ElfShadowSection { - /// - /// Equivalent of but used for shadow. - /// - public sealed class ElfBinaryShadowSection : ElfShadowSection + public ElfBinaryShadowSection() { - public ElfBinaryShadowSection() - { - } - - public Stream? Stream { get; set; } + } - protected override void Read(ElfReader reader) - { - Stream = reader.ReadAsStream(Size); - } + public Stream? Stream { get; set; } - protected override void Write(ElfWriter writer) - { - if (Stream == null) return; - writer.Write(Stream); - } + public override void Read(ElfReader reader) + { + Stream = reader.ReadAsStream(Size); + } - public override void UpdateLayout(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + public override void Write(ElfWriter writer) + { + if (Stream == null) return; + writer.Write(Stream); + } - Size = Stream != null ? (ulong)Stream.Length : 0; - } + public override void UpdateLayout(ElfVisitorContext context) + { + Size = Stream != null ? (ulong)Stream.Length : 0; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfCustomNote.cs b/src/LibObjectFile/Elf/Sections/ElfCustomNote.cs index 6ddc185..e98b64c 100644 --- a/src/LibObjectFile/Elf/Sections/ElfCustomNote.cs +++ b/src/LibObjectFile/Elf/Sections/ElfCustomNote.cs @@ -6,108 +6,107 @@ using System.IO; using System.Text; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A custom note entry in +/// +public class ElfCustomNote : ElfNote { + public ElfCustomNote(string name, ElfNoteTypeEx type) + { + Name = name; + Type = type; + } + /// - /// A custom note entry in + /// Gets or sets the name of this note. /// - public class ElfCustomNote : ElfNote - { - public ElfCustomNote(string name, ElfNoteTypeEx type) - { - Name = name; - Type = type; - } + public string Name { get; } - /// - /// Gets or sets the name of this note. - /// - public string Name { get; } + /// + /// Gets or sets the associated descriptor data. + /// + public Stream? Descriptor { get; set; } - /// - /// Gets or sets the associated descriptor data. - /// - public Stream? Descriptor { get; set; } + /// + /// Gets or sets the type of this note. + /// + public ElfNoteTypeEx Type { get; } - /// - /// Gets or sets the type of this note. - /// - public ElfNoteTypeEx Type { get; } + public override string GetName() + { + return Name; + } - public override string GetName() - { - return Name; - } + public override ElfNoteTypeEx GetNoteType() + { + return Type; + } - public override ElfNoteTypeEx GetNoteType() - { - return Type; - } + public override uint GetDescriptorSize() + { + return Descriptor == null ? 0 : (uint)Descriptor.Length; + } - public override uint GetDescriptorSize() - { - return Descriptor == null ? 0 : (uint)Descriptor.Length; - } + public override string GetDescriptorAsText() + { + if (Descriptor == null || Descriptor.Length == 0) return string.Empty; - public override string GetDescriptorAsText() - { - if (Descriptor == null || Descriptor.Length == 0) return string.Empty; + Descriptor.Position = 0; + var length = (int) Descriptor.Length; + var buffer = ArrayPool.Shared.Rent(length); + try + { + length = Descriptor.Read(buffer, 0, length); Descriptor.Position = 0; + var hasBinary = false; - var length = (int) Descriptor.Length; - var buffer = ArrayPool.Shared.Rent(length); - try + // If we have any binary data (don't take into account a potential null terminated string) + for (int i = 0; i < length - 1; i++) { - length = Descriptor.Read(buffer, 0, length); - Descriptor.Position = 0; - var hasBinary = false; - - // If we have any binary data (don't take into account a potential null terminated string) - for (int i = 0; i < length - 1; i++) + if (buffer[i] < ' ') { - if (buffer[i] < ' ') - { - hasBinary = true; - break; - } + hasBinary = true; + break; } + } - if (hasBinary) - { - var builder = new StringBuilder(); - for (int i = 0; i < length; i++) - { - builder.Append($"{buffer[i]:x2}"); - } - - return builder.ToString(); - } - else + if (hasBinary) + { + var builder = new StringBuilder(); + for (int i = 0; i < length; i++) { - return Encoding.UTF8.GetString(buffer, 0, length); + builder.Append($"{buffer[i]:x2}"); } + + return builder.ToString(); } - finally + else { - ArrayPool.Shared.Return(buffer); + return Encoding.UTF8.GetString(buffer, 0, length); } } + finally + { + ArrayPool.Shared.Return(buffer); + } + } - protected override void ReadDescriptor(ElfReader reader, uint descriptorLength) + protected override void ReadDescriptor(ElfReader reader, uint descriptorLength) + { + if (descriptorLength > 0) { - if (descriptorLength > 0) - { - Descriptor = reader.ReadAsStream(descriptorLength); - } + Descriptor = reader.ReadAsStream(descriptorLength); } + } - protected override void WriteDescriptor(ElfWriter writer) + protected override void WriteDescriptor(ElfWriter writer) + { + if (Descriptor != null) { - if (Descriptor != null) - { - writer.Write(Descriptor); - } + writer.Write(Descriptor); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfGnuNote.cs b/src/LibObjectFile/Elf/Sections/ElfGnuNote.cs index 0733329..601ab3a 100644 --- a/src/LibObjectFile/Elf/Sections/ElfGnuNote.cs +++ b/src/LibObjectFile/Elf/Sections/ElfGnuNote.cs @@ -2,13 +2,12 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +public abstract class ElfGnuNote : ElfNote { - public abstract class ElfGnuNote : ElfNote + public override string GetName() { - public override string GetName() - { - return "GNU"; - } + return "GNU"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfGnuNoteABITag.cs b/src/LibObjectFile/Elf/Sections/ElfGnuNoteABITag.cs index 20cd616..4731d39 100644 --- a/src/LibObjectFile/Elf/Sections/ElfGnuNoteABITag.cs +++ b/src/LibObjectFile/Elf/Sections/ElfGnuNoteABITag.cs @@ -5,79 +5,78 @@ using System; using System.Text; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +public class ElfGnuNoteABITag : ElfGnuNote { - public class ElfGnuNoteABITag : ElfGnuNote - { - public ElfGnuNoteOSKind OSKind { get; set; } + public ElfGnuNoteOSKind OSKind { get; set; } - public uint MajorVersion { get; set; } + public uint MajorVersion { get; set; } - public uint MinorVersion { get; set; } + public uint MinorVersion { get; set; } - public uint SubMinorVersion { get; set; } + public uint SubMinorVersion { get; set; } - public override ElfNoteTypeEx GetNoteType() => new ElfNoteTypeEx(ElfNoteType.GNU_ABI_TAG); + public override ElfNoteTypeEx GetNoteType() => new ElfNoteTypeEx(ElfNoteType.GNU_ABI_TAG); - public override uint GetDescriptorSize() => 4 * sizeof(int); + public override uint GetDescriptorSize() => 4 * sizeof(int); - public override string GetDescriptorAsText() + public override string GetDescriptorAsText() + { + var builder = new StringBuilder(); + builder.Append("OS: "); + switch (OSKind) { - var builder = new StringBuilder(); - builder.Append("OS: "); - switch (OSKind) - { - case ElfGnuNoteOSKind.Linux: - builder.Append("Linux"); - break; - case ElfGnuNoteOSKind.Gnu: - builder.Append("Gnu"); - break; - case ElfGnuNoteOSKind.Solaris: - builder.Append("Solaris"); - break; - case ElfGnuNoteOSKind.FreeBSD: - builder.Append("FreeBSD"); - break; - default: - builder.Append($"0x{(uint) OSKind:x8}"); - break; - } - - builder.Append($", ABI: {MajorVersion}.{MinorVersion}.{SubMinorVersion}"); - return builder.ToString(); + case ElfGnuNoteOSKind.Linux: + builder.Append("Linux"); + break; + case ElfGnuNoteOSKind.Gnu: + builder.Append("Gnu"); + break; + case ElfGnuNoteOSKind.Solaris: + builder.Append("Solaris"); + break; + case ElfGnuNoteOSKind.FreeBSD: + builder.Append("FreeBSD"); + break; + default: + builder.Append($"0x{(uint) OSKind:x8}"); + break; } - protected override void ReadDescriptor(ElfReader reader, uint descriptorLength) - { - NativeGnuNoteOS nativeGnuNote; - if (!reader.TryReadData((int)descriptorLength, out nativeGnuNote)) - { - reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleNoteGnuAbiTag, $"The {nameof(ElfGnuNoteABITag)} is incomplete in size. Expecting: {GetDescriptorSize()} but got {descriptorLength}"); - } - - OSKind = (ElfGnuNoteOSKind) reader.Decode(nativeGnuNote.OS); - MajorVersion = reader.Decode(nativeGnuNote.MajorVersion); - MinorVersion = reader.Decode(nativeGnuNote.MinorVersion); - SubMinorVersion = reader.Decode(nativeGnuNote.SubMinorVersion); - } + builder.Append($", ABI: {MajorVersion}.{MinorVersion}.{SubMinorVersion}"); + return builder.ToString(); + } - protected override void WriteDescriptor(ElfWriter writer) + protected override void ReadDescriptor(ElfReader reader, uint descriptorLength) + { + NativeGnuNoteOS nativeGnuNote; + if (!reader.TryReadData((int)descriptorLength, out nativeGnuNote)) { - NativeGnuNoteOS nativeGnuNote; - writer.Encode(out nativeGnuNote.OS, (uint) OSKind); - writer.Encode(out nativeGnuNote.MajorVersion, (uint)MajorVersion); - writer.Encode(out nativeGnuNote.MinorVersion, (uint)MinorVersion); - writer.Encode(out nativeGnuNote.SubMinorVersion, (uint)SubMinorVersion); - writer.Write(nativeGnuNote); + reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleNoteGnuAbiTag, $"The {nameof(ElfGnuNoteABITag)} is incomplete in size. Expecting: {GetDescriptorSize()} but got {descriptorLength}"); } - private struct NativeGnuNoteOS - { - public ElfNative.Elf32_Word OS; - public ElfNative.Elf32_Word MajorVersion; - public ElfNative.Elf32_Word MinorVersion; - public ElfNative.Elf32_Word SubMinorVersion; - } + OSKind = (ElfGnuNoteOSKind) reader.Decode(nativeGnuNote.OS); + MajorVersion = reader.Decode(nativeGnuNote.MajorVersion); + MinorVersion = reader.Decode(nativeGnuNote.MinorVersion); + SubMinorVersion = reader.Decode(nativeGnuNote.SubMinorVersion); + } + + protected override void WriteDescriptor(ElfWriter writer) + { + NativeGnuNoteOS nativeGnuNote; + writer.Encode(out nativeGnuNote.OS, (uint) OSKind); + writer.Encode(out nativeGnuNote.MajorVersion, (uint)MajorVersion); + writer.Encode(out nativeGnuNote.MinorVersion, (uint)MinorVersion); + writer.Encode(out nativeGnuNote.SubMinorVersion, (uint)SubMinorVersion); + writer.Write(nativeGnuNote); + } + + private struct NativeGnuNoteOS + { + public ElfNative.Elf32_Word OS; + public ElfNative.Elf32_Word MajorVersion; + public ElfNative.Elf32_Word MinorVersion; + public ElfNative.Elf32_Word SubMinorVersion; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfGnuNoteBuildId.cs b/src/LibObjectFile/Elf/Sections/ElfGnuNoteBuildId.cs index bf8958d..fa7e69b 100644 --- a/src/LibObjectFile/Elf/Sections/ElfGnuNoteBuildId.cs +++ b/src/LibObjectFile/Elf/Sections/ElfGnuNoteBuildId.cs @@ -6,53 +6,52 @@ using System.IO; using System.Text; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +public class ElfGnuNoteBuildId : ElfGnuNote { - public class ElfGnuNoteBuildId : ElfGnuNote - { - public override ElfNoteTypeEx GetNoteType() => new ElfNoteTypeEx(ElfNoteType.GNU_BUILD_ID); + public override ElfNoteTypeEx GetNoteType() => new ElfNoteTypeEx(ElfNoteType.GNU_BUILD_ID); - public Stream? BuildId { get; set; } + public Stream? BuildId { get; set; } - public override uint GetDescriptorSize() => BuildId != null ? (uint)BuildId.Length : 0; + public override uint GetDescriptorSize() => BuildId != null ? (uint)BuildId.Length : 0; + + public override string GetDescriptorAsText() + { + var builder = new StringBuilder(); + builder.Append("Build ID: "); - public override string GetDescriptorAsText() + if (BuildId != null) { - var builder = new StringBuilder(); - builder.Append("Build ID: "); + BuildId.Position = 0; + var length = (int)BuildId.Length; + var buffer = ArrayPool.Shared.Rent(length); + length = BuildId.Read(buffer, 0, length); + BuildId.Position = 0; - if (BuildId != null) + for (int i = 0; i < length; i++) { - BuildId.Position = 0; - var length = (int)BuildId.Length; - var buffer = ArrayPool.Shared.Rent(length); - length = BuildId.Read(buffer, 0, length); - BuildId.Position = 0; - - for (int i = 0; i < length; i++) - { - builder.Append($"{buffer[i]:x2}"); - } + builder.Append($"{buffer[i]:x2}"); } - - return builder.ToString(); } + return builder.ToString(); + } + - protected override void ReadDescriptor(ElfReader reader, uint descriptorLength) + protected override void ReadDescriptor(ElfReader reader, uint descriptorLength) + { + if (descriptorLength > 0) { - if (descriptorLength > 0) - { - BuildId = reader.ReadAsStream(descriptorLength); - } + BuildId = reader.ReadAsStream(descriptorLength); } + } - protected override void WriteDescriptor(ElfWriter writer) + protected override void WriteDescriptor(ElfWriter writer) + { + if (BuildId != null) { - if (BuildId != null) - { - writer.Write(BuildId); - } + writer.Write(BuildId); } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfGnuNoteOSKind.cs b/src/LibObjectFile/Elf/Sections/ElfGnuNoteOSKind.cs index e2ec7c7..21e9b9b 100644 --- a/src/LibObjectFile/Elf/Sections/ElfGnuNoteOSKind.cs +++ b/src/LibObjectFile/Elf/Sections/ElfGnuNoteOSKind.cs @@ -2,31 +2,30 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Type of Operating System for a +/// +public enum ElfGnuNoteOSKind : uint { /// - /// Type of Operating System for a + /// Linux operating system. /// - public enum ElfGnuNoteOSKind : uint - { - /// - /// Linux operating system. - /// - Linux = ElfNative.ELF_NOTE_OS_LINUX, + Linux = ElfNative.ELF_NOTE_OS_LINUX, - /// - /// A Gnu operating system. - /// - Gnu = ElfNative.ELF_NOTE_OS_GNU, + /// + /// A Gnu operating system. + /// + Gnu = ElfNative.ELF_NOTE_OS_GNU, - /// - /// Solaris operating system. - /// - Solaris = ElfNative.ELF_NOTE_OS_SOLARIS2, + /// + /// Solaris operating system. + /// + Solaris = ElfNative.ELF_NOTE_OS_SOLARIS2, - /// - /// FreeBSD operating system. - /// - FreeBSD = ElfNative.ELF_NOTE_OS_FREEBSD, - } + /// + /// FreeBSD operating system. + /// + FreeBSD = ElfNative.ELF_NOTE_OS_FREEBSD, } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfNote.cs b/src/LibObjectFile/Elf/Sections/ElfNote.cs index 19842c1..9ccbd31 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNote.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNote.cs @@ -2,47 +2,46 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A Note entry in +/// +public abstract class ElfNote { - /// - /// A Note entry in - /// - public abstract class ElfNote + protected ElfNote() { - protected ElfNote() - { - } + } - /// - /// Gets or sets the name of this note. - /// - public abstract string GetName(); + /// + /// Gets or sets the name of this note. + /// + public abstract string GetName(); - /// - /// Gets or sets the type of this note. - /// - public abstract ElfNoteTypeEx GetNoteType(); + /// + /// Gets or sets the type of this note. + /// + public abstract ElfNoteTypeEx GetNoteType(); - public abstract uint GetDescriptorSize(); + public abstract uint GetDescriptorSize(); - public abstract string GetDescriptorAsText(); + public abstract string GetDescriptorAsText(); - public override string ToString() - { - return $"{nameof(ElfNote)} {GetName()}, Type: {GetNoteType()}"; - } + public override string ToString() + { + return $"{nameof(ElfNote)} {GetName()}, Type: {GetNoteType()}"; + } - internal void ReadDescriptorInternal(ElfReader reader, uint descriptorLength) - { - ReadDescriptor(reader, descriptorLength); - } - - internal void WriteDescriptorInternal(ElfWriter writer) - { - WriteDescriptor(writer); - } - protected abstract void ReadDescriptor(ElfReader reader, uint descriptorLength); - - protected abstract void WriteDescriptor(ElfWriter writer); + internal void ReadDescriptorInternal(ElfReader reader, uint descriptorLength) + { + ReadDescriptor(reader, descriptorLength); } + + internal void WriteDescriptorInternal(ElfWriter writer) + { + WriteDescriptor(writer); + } + protected abstract void ReadDescriptor(ElfReader reader, uint descriptorLength); + + protected abstract void WriteDescriptor(ElfWriter writer); } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs b/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs index 57219a8..7a37fa0 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs @@ -8,164 +8,159 @@ using System.Text; using LibObjectFile.Utils; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A note section with the type . +/// +public sealed class ElfNoteTable : ElfSection { - /// - /// A note section with the type . - /// - public sealed class ElfNoteTable : ElfSection + public ElfNoteTable() : base(ElfSectionType.Note) { - public ElfNoteTable() : base(ElfSectionType.Note) - { - Entries = new List(); - } + Entries = new List(); + } - /// - /// Gets a list of entries. - /// - public List Entries { get; } + /// + /// Gets a list of entries. + /// + public List Entries { get; } - public override ElfSectionType Type + public override ElfSectionType Type + { + get => base.Type; + set { - get => base.Type; - set + if (value != ElfSectionType.Note) { - if (value != ElfSectionType.Note) - { - throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}] `{nameof(ElfNoteTable)}` while `{ElfSectionType.Note}` is expected"); - } - base.Type = value; + throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}] `{nameof(ElfNoteTable)}` while `{ElfSectionType.Note}` is expected"); } + base.Type = value; } + } - public override unsafe void UpdateLayout(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - ulong size = 0; - ulong entrySize = (ulong)sizeof(ElfNative.Elf32_Nhdr); + public override unsafe void UpdateLayout(ElfVisitorContext context) + { + ulong size = 0; + ulong entrySize = (ulong)sizeof(ElfNative.Elf32_Nhdr); - foreach (var elfNote in Entries) - { - var name = elfNote.GetName(); - if (name != null) - { - size += (ulong)Encoding.UTF8.GetByteCount(name) + 1; - size = AlignHelper.AlignToUpper(size, 4); - } + foreach (var elfNote in Entries) + { + var name = elfNote.GetName(); + size += (ulong)Encoding.UTF8.GetByteCount(name) + 1; + size = AlignHelper.AlignToUpper(size, 4); - size += (ulong)elfNote.GetDescriptorSize(); - size = AlignHelper.AlignToUpper(size, 4); + size += (ulong)elfNote.GetDescriptorSize(); + size = AlignHelper.AlignToUpper(size, 4); - size += entrySize; - } - Size = size; + size += entrySize; } - - protected override unsafe void Read(ElfReader reader) - { - var sizeToRead = (long)base.Size; + Size = size; + } - var entrySize = (long)sizeof(ElfNative.Elf32_Nhdr); + public override unsafe void Read(ElfReader reader) + { + var sizeToRead = (long)base.Size; + + var entrySize = (long)sizeof(ElfNative.Elf32_Nhdr); - var startPosition = (ulong)reader.Stream.Position; - while (sizeToRead >= entrySize) + var startPosition = (ulong)reader.Stream.Position; + while (sizeToRead >= entrySize) + { + ElfNative.Elf32_Nhdr nativeNote; + ulong noteStartOffset = (ulong)reader.Stream.Position; + if (!reader.TryReadData((int)entrySize, out nativeNote)) { - ElfNative.Elf32_Nhdr nativeNote; - ulong noteStartOffset = (ulong)reader.Stream.Position; - if (!reader.TryReadData((int)entrySize, out nativeNote)) - { - reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteNoteEntrySize, $"Unable to read entirely the note entry [{Entries.Count}] from {Type} section [{Index}]. Not enough data (size: {entrySize}) read at offset {noteStartOffset} from the stream"); - break; - } - - var nameLength = reader.Decode(nativeNote.n_namesz); - var descriptorLength = reader.Decode(nativeNote.n_descsz); + reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteNoteEntrySize, $"Unable to read entirely the note entry [{Entries.Count}] from {Type} section [{Index}]. Not enough data (size: {entrySize}) read at offset {noteStartOffset} from the stream"); + break; + } + + var nameLength = reader.Decode(nativeNote.n_namesz); + var descriptorLength = reader.Decode(nativeNote.n_descsz); - var noteType = new ElfNoteTypeEx(reader.Decode(nativeNote.n_type)); - var noteName = reader.ReadStringUTF8NullTerminated(nameLength); - SkipPaddingAlignedTo4Bytes(reader, (ulong)reader.Stream.Position - startPosition); + var noteType = new ElfNoteTypeEx(reader.Decode(nativeNote.n_type)); + var noteName = reader.ReadStringUTF8NullTerminated(nameLength); + SkipPaddingAlignedTo4Bytes(reader, (ulong)reader.Stream.Position - startPosition); - var note = CreateNote(reader, noteName, noteType); + var note = CreateNote(reader, noteName, noteType); - note.ReadDescriptorInternal(reader, descriptorLength); + note.ReadDescriptorInternal(reader, descriptorLength); - SkipPaddingAlignedTo4Bytes(reader, (ulong)reader.Stream.Position - startPosition); + SkipPaddingAlignedTo4Bytes(reader, (ulong)reader.Stream.Position - startPosition); - Entries.Add(note); + Entries.Add(note); - ulong noteEndOffset = (ulong)reader.Stream.Position; - sizeToRead = sizeToRead - (long)(noteEndOffset - noteStartOffset); - } + ulong noteEndOffset = (ulong)reader.Stream.Position; + sizeToRead = sizeToRead - (long)(noteEndOffset - noteStartOffset); } + } - private void SkipPaddingAlignedTo4Bytes(ElfReader reader, ulong offset) + private void SkipPaddingAlignedTo4Bytes(ElfReader reader, ulong offset) + { + if ((offset & 3) != 0) { - if ((offset & 3) != 0) - { - var toWrite = 4 - (int)(offset & 3); - reader.Stream.Position += toWrite; - } + var toWrite = 4 - (int)(offset & 3); + reader.Stream.Position += toWrite; } + } - protected override void Write(ElfWriter writer) + public override void Write(ElfWriter writer) + { + var expectedSizeWritten = Size; + var startPosition = (ulong) writer.Stream.Position; + foreach (var elfNote in Entries) { - var expectedSizeWritten = Size; - var startPosition = (ulong) writer.Stream.Position; - foreach (var elfNote in Entries) - { - ElfNative.Elf32_Nhdr nativeNote; + ElfNative.Elf32_Nhdr nativeNote; - var noteName = elfNote.GetName(); - writer.Encode(out nativeNote.n_namesz, noteName == null ? 0 : ((uint) Encoding.UTF8.GetByteCount(noteName) + 1)); - writer.Encode(out nativeNote.n_descsz, elfNote.GetDescriptorSize()); - writer.Encode(out nativeNote.n_type, (uint)elfNote.GetNoteType().Value); - - writer.Write(nativeNote); + var noteName = elfNote.GetName(); + writer.Encode(out nativeNote.n_namesz, noteName == null ? 0 : ((uint) Encoding.UTF8.GetByteCount(noteName) + 1)); + writer.Encode(out nativeNote.n_descsz, elfNote.GetDescriptorSize()); + writer.Encode(out nativeNote.n_type, (uint)elfNote.GetNoteType().Value); - if (noteName != null) - { - writer.WriteStringUTF8NullTerminated(noteName); - WritePaddingAlignedTo4Bytes(writer, (ulong)writer.Stream.Position - startPosition); - } + writer.Write(nativeNote); - elfNote.WriteDescriptorInternal(writer); + if (noteName != null) + { + writer.WriteStringUTF8NullTerminated(noteName); WritePaddingAlignedTo4Bytes(writer, (ulong)writer.Stream.Position - startPosition); } - var sizeWritten = (ulong) writer.Stream.Position - startPosition; - Debug.Assert(expectedSizeWritten == sizeWritten); + elfNote.WriteDescriptorInternal(writer); + WritePaddingAlignedTo4Bytes(writer, (ulong)writer.Stream.Position - startPosition); } - private void WritePaddingAlignedTo4Bytes(ElfWriter writer, ulong offset) + var sizeWritten = (ulong) writer.Stream.Position - startPosition; + Debug.Assert(expectedSizeWritten == sizeWritten); + } + + private void WritePaddingAlignedTo4Bytes(ElfWriter writer, ulong offset) + { + if ((offset & 3) != 0) { - if ((offset & 3) != 0) - { - var toWrite = 4 - (int)(offset & 3); - for (int i = 0; i < toWrite; i++) writer.Stream.WriteByte(0); - } + var toWrite = 4 - (int)(offset & 3); + for (int i = 0; i < toWrite; i++) writer.Stream.WriteByte(0); } + } - private static ElfNote CreateNote(ElfReader reader, string name, ElfNoteType type) + private static ElfNote CreateNote(ElfReader reader, string name, ElfNoteType type) + { + if (name == "GNU") { - if (name == "GNU") + switch (type) { - switch (type) - { - case ElfNoteType.GNU_ABI_TAG: - return new ElfGnuNoteABITag(); - case ElfNoteType.GNU_BUILD_ID: - return new ElfGnuNoteBuildId(); - } + case ElfNoteType.GNU_ABI_TAG: + return new ElfGnuNoteABITag(); + case ElfNoteType.GNU_BUILD_ID: + return new ElfGnuNoteBuildId(); } + } - ElfNote? note = null; - - if (reader.Options.TryCreateNote != null) - { - note = reader.Options.TryCreateNote(name, type); - } + ElfNote? note = null; - return note ?? new ElfCustomNote(name, type); + if (reader.Options.TryCreateNote != null) + { + note = reader.Options.TryCreateNote(name, type); } + + return note ?? new ElfCustomNote(name, type); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfNoteType.cs b/src/LibObjectFile/Elf/Sections/ElfNoteType.cs index 5639f71..9864489 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNoteType.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNoteType.cs @@ -4,62 +4,61 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Gets the type of a . +/// +public readonly partial struct ElfNoteTypeEx : IEquatable { - /// - /// Gets the type of a . - /// - public readonly partial struct ElfNoteTypeEx : IEquatable + public ElfNoteTypeEx(uint value) { - public ElfNoteTypeEx(uint value) - { - Value = (ElfNoteType)value; - } + Value = (ElfNoteType)value; + } - public ElfNoteTypeEx(ElfNoteType value) - { - Value = value; - } + public ElfNoteTypeEx(ElfNoteType value) + { + Value = value; + } - /// - /// The value of this note type. - /// - public readonly ElfNoteType Value; + /// + /// The value of this note type. + /// + public readonly ElfNoteType Value; - public override string ToString() - { - return ToStringInternal() ?? $"Unknown {nameof(ElfNoteTypeEx)} (0x{(uint)Value:X4})"; - } + public override string ToString() + { + return ToStringInternal() ?? $"Unknown {nameof(ElfNoteTypeEx)} (0x{(uint)Value:X4})"; + } - public bool Equals(ElfNoteTypeEx other) - { - return Value == other.Value; - } + public bool Equals(ElfNoteTypeEx other) + { + return Value == other.Value; + } - public override bool Equals(object? obj) - { - return obj is ElfNoteTypeEx other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is ElfNoteTypeEx other && Equals(other); + } - public override int GetHashCode() - { - return (int)Value; - } + public override int GetHashCode() + { + return (int)Value; + } - public static bool operator ==(ElfNoteTypeEx left, ElfNoteTypeEx right) - { - return left.Equals(right); - } + public static bool operator ==(ElfNoteTypeEx left, ElfNoteTypeEx right) + { + return left.Equals(right); + } - public static bool operator !=(ElfNoteTypeEx left, ElfNoteTypeEx right) - { - return !left.Equals(right); - } + public static bool operator !=(ElfNoteTypeEx left, ElfNoteTypeEx right) + { + return !left.Equals(right); + } - public static explicit operator byte(ElfNoteTypeEx noteType) => (byte)noteType.Value; + public static explicit operator byte(ElfNoteTypeEx noteType) => (byte)noteType.Value; - public static implicit operator ElfNoteTypeEx(ElfNoteType noteType) => new ElfNoteTypeEx(noteType); + public static implicit operator ElfNoteTypeEx(ElfNoteType noteType) => new ElfNoteTypeEx(noteType); - public static implicit operator ElfNoteType(ElfNoteTypeEx noteType) => noteType.Value; - } + public static implicit operator ElfNoteType(ElfNoteTypeEx noteType) => noteType.Value; } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfNullSection.cs b/src/LibObjectFile/Elf/Sections/ElfNullSection.cs index cf6ae37..a7e79b1 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNullSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNullSection.cs @@ -4,42 +4,30 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A null section with the type . +/// +public sealed class ElfNullSection : ElfSection { - /// - /// A null section with the type . - /// - public sealed class ElfNullSection : ElfSection + public override void Verify(ElfVisitorContext context) { - public override void Verify(DiagnosticBag diagnostics) - { - base.Verify(diagnostics); - - if (Type != ElfSectionType.Null || - Flags != ElfSectionFlags.None || - !Name.IsEmpty || - VirtualAddress != 0 || - Alignment != 0 || - !Link.IsEmpty || - !Info.IsEmpty || - Position != 0 || - Size != 0) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidNullSection, "Invalid Null section. This section should not be modified and all properties must be null"); - } - } - - public override void UpdateLayout(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - } - - protected override void Read(ElfReader reader) + if (Type != ElfSectionType.Null || + Flags != ElfSectionFlags.None || + !Name.IsEmpty || + VirtualAddress != 0 || + Alignment != 0 || + !Link.IsEmpty || + !Info.IsEmpty || + Position != 0 || + Size != 0) { + context.Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidNullSection, "Invalid Null section. This section should not be modified and all properties must be null"); } + } - protected override void Write(ElfWriter writer) - { - } + public override void UpdateLayout(ElfVisitorContext context) + { } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs b/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs index e8a257a..0dd3cc5 100644 --- a/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs @@ -4,90 +4,87 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// The program header table as a . +/// +public sealed class ElfProgramHeaderTable : ElfShadowSection { - /// - /// The program header table as a . - /// - public sealed class ElfProgramHeaderTable : ElfShadowSection + public ElfProgramHeaderTable() { - public ElfProgramHeaderTable() - { - Name = ".shadow.phdrtab"; - } + Name = ".shadow.phdrtab"; + } - protected override void Read(ElfReader reader) - { - // This is not read by this instance but by ElfReader directly - } + public override void Read(ElfReader reader) + { + // This is not read by this instance but by ElfReader directly + } - public override unsafe ulong TableEntrySize + public override unsafe ulong TableEntrySize + { + get { - get - { - if (Parent == null) return 0; - return Parent.FileClass == ElfFileClass.Is32 ? (ulong)sizeof(ElfNative.Elf32_Phdr) : (ulong)sizeof(ElfNative.Elf64_Phdr); - } + if (Parent == null) return 0; + return Parent.FileClass == ElfFileClass.Is32 ? (ulong)sizeof(ElfNative.Elf32_Phdr) : (ulong)sizeof(ElfNative.Elf64_Phdr); } + } - public override void UpdateLayout(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - - Size = 0; + public override void UpdateLayout(ElfVisitorContext context) + { + Size = 0; - if (Parent == null) return; + if (Parent == null) return; - Size = (ulong) Parent.Segments.Count * Parent.Layout.SizeOfProgramHeaderEntry; - } + Size = (ulong) Parent.Segments.Count * Parent.Layout.SizeOfProgramHeaderEntry; + } - protected override void Write(ElfWriter writer) + public override void Write(ElfWriter writer) + { + for (int i = 0; i < Parent!.Segments.Count; i++) { - for (int i = 0; i < Parent!.Segments.Count; i++) + var header = Parent.Segments[i]; + if (Parent.FileClass == ElfFileClass.Is32) { - var header = Parent.Segments[i]; - if (Parent.FileClass == ElfFileClass.Is32) - { - WriteProgramHeader32(writer, ref header); - } - else - { - WriteProgramHeader64(writer, ref header); - } + WriteProgramHeader32(writer, ref header); + } + else + { + WriteProgramHeader64(writer, ref header); } } + } - private void WriteProgramHeader32(ElfWriter writer, ref ElfSegment segment) - { - var hdr = new ElfNative.Elf32_Phdr(); + private void WriteProgramHeader32(ElfWriter writer, ref ElfSegment segment) + { + var hdr = new ElfNative.Elf32_Phdr(); - writer.Encode(out hdr.p_type, segment.Type.Value); - writer.Encode(out hdr.p_offset, (uint)segment.Position); - writer.Encode(out hdr.p_vaddr, (uint)segment.VirtualAddress); - writer.Encode(out hdr.p_paddr, (uint)segment.PhysicalAddress); - writer.Encode(out hdr.p_filesz, (uint)segment.Size); - writer.Encode(out hdr.p_memsz, (uint)segment.SizeInMemory); - writer.Encode(out hdr.p_flags, segment.Flags.Value); - writer.Encode(out hdr.p_align, (uint)segment.Alignment); + writer.Encode(out hdr.p_type, segment.Type.Value); + writer.Encode(out hdr.p_offset, (uint)segment.Position); + writer.Encode(out hdr.p_vaddr, (uint)segment.VirtualAddress); + writer.Encode(out hdr.p_paddr, (uint)segment.PhysicalAddress); + writer.Encode(out hdr.p_filesz, (uint)segment.Size); + writer.Encode(out hdr.p_memsz, (uint)segment.SizeInMemory); + writer.Encode(out hdr.p_flags, segment.Flags.Value); + writer.Encode(out hdr.p_align, (uint)segment.Alignment); - writer.Write(hdr); - } + writer.Write(hdr); + } - private void WriteProgramHeader64(ElfWriter writer, ref ElfSegment segment) - { - var hdr = new ElfNative.Elf64_Phdr(); + private void WriteProgramHeader64(ElfWriter writer, ref ElfSegment segment) + { + var hdr = new ElfNative.Elf64_Phdr(); - writer.Encode(out hdr.p_type, segment.Type.Value); - writer.Encode(out hdr.p_offset, segment.Position); - writer.Encode(out hdr.p_vaddr, segment.VirtualAddress); - writer.Encode(out hdr.p_paddr, segment.PhysicalAddress); - writer.Encode(out hdr.p_filesz, segment.Size); - writer.Encode(out hdr.p_memsz, segment.SizeInMemory); - writer.Encode(out hdr.p_flags, segment.Flags.Value); - writer.Encode(out hdr.p_align, segment.Alignment); + writer.Encode(out hdr.p_type, segment.Type.Value); + writer.Encode(out hdr.p_offset, segment.Position); + writer.Encode(out hdr.p_vaddr, segment.VirtualAddress); + writer.Encode(out hdr.p_paddr, segment.PhysicalAddress); + writer.Encode(out hdr.p_filesz, segment.Size); + writer.Encode(out hdr.p_memsz, segment.SizeInMemory); + writer.Encode(out hdr.p_flags, segment.Flags.Value); + writer.Encode(out hdr.p_align, segment.Alignment); - writer.Write(hdr); - } + writer.Write(hdr); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocation.cs b/src/LibObjectFile/Elf/Sections/ElfRelocation.cs index e76dd0f..9868c0c 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocation.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocation.cs @@ -2,57 +2,56 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A relocation entry in the +/// This is the value seen in or +/// +public struct ElfRelocation { + public ElfRelocation(ulong offset, ElfRelocationType type, uint symbolIndex, long addend) + { + Offset = offset; + Type = type; + SymbolIndex = symbolIndex; + Addend = addend; + } + /// - /// A relocation entry in the - /// This is the value seen in or + /// Gets or sets the offset. /// - public struct ElfRelocation - { - public ElfRelocation(ulong offset, ElfRelocationType type, uint symbolIndex, long addend) - { - Offset = offset; - Type = type; - SymbolIndex = symbolIndex; - Addend = addend; - } - - /// - /// Gets or sets the offset. - /// - public ulong Offset { get; set; } - - /// - /// Gets or sets the type of relocation. - /// - public ElfRelocationType Type { get; set; } - - /// - /// Gets or sets the symbol index associated with the symbol table. - /// - public uint SymbolIndex { get; set; } - - /// - /// Gets or sets the addend value. - /// - public long Addend { get; set; } - - /// - /// Gets the computed Info value as expected by - /// - public uint Info32 => - ((uint) SymbolIndex << 8) | ((Type.Value & 0xFF)); - - /// - /// Gets the computed Info value as expected by - /// - public ulong Info64 => - ((ulong)SymbolIndex << 32) | (Type.Value); + public ulong Offset { get; set; } + + /// + /// Gets or sets the type of relocation. + /// + public ElfRelocationType Type { get; set; } + + /// + /// Gets or sets the symbol index associated with the symbol table. + /// + public uint SymbolIndex { get; set; } + + /// + /// Gets or sets the addend value. + /// + public long Addend { get; set; } + + /// + /// Gets the computed Info value as expected by + /// + public uint Info32 => + ((uint) SymbolIndex << 8) | ((Type.Value & 0xFF)); + + /// + /// Gets the computed Info value as expected by + /// + public ulong Info64 => + ((ulong)SymbolIndex << 32) | (Type.Value); - public override string ToString() - { - return $"{nameof(Offset)}: 0x{Offset:X16}, {nameof(Type)}: {Type}, {nameof(SymbolIndex)}: {SymbolIndex}, {nameof(Addend)}: 0x{Addend:X16}"; - } + public override string ToString() + { + return $"{nameof(Offset)}: 0x{Offset:X16}, {nameof(Type)}: {Type}, {nameof(SymbolIndex)}: {SymbolIndex}, {nameof(Addend)}: 0x{Addend:X16}"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocationContext.cs b/src/LibObjectFile/Elf/Sections/ElfRelocationContext.cs index b2f82df..5d22589 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationContext.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationContext.cs @@ -2,19 +2,18 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Context used when applying relocation via . +/// +public struct ElfRelocationContext { - /// - /// Context used when applying relocation via . - /// - public struct ElfRelocationContext - { - public ulong BaseAddress { get; set; } + public ulong BaseAddress { get; set; } - public ulong GlobalObjectTableAddress { get; set; } + public ulong GlobalObjectTableAddress { get; set; } - public ulong GlobalObjectTableOffset { get; set; } + public ulong GlobalObjectTableOffset { get; set; } - public ulong ProcedureLinkageTableAddress { get; set; } - } + public ulong ProcedureLinkageTableAddress { get; set; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs b/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs index 686c4e7..11288f6 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs @@ -6,309 +6,306 @@ using System.Collections.Generic; using System.IO; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A relocation section with the type or +/// +public sealed class ElfRelocationTable : ElfSection { + private readonly List _entries; + public const string DefaultName = ".rel"; + public const string DefaultNameWithAddends = ".rela"; + + public ElfRelocationTable() : base(ElfSectionType.RelocationAddends) + { + Name = DefaultNameWithAddends; + _entries = new List(); + } + /// - /// A relocation section with the type or + /// Gets a list of entries. /// - public sealed class ElfRelocationTable : ElfSection + public List Entries => _entries; + + private static string GetDefaultName(ElfSectionType type) { - private readonly List _entries; - public const string DefaultName = ".rel"; - public const string DefaultNameWithAddends = ".rela"; + return type == ElfSectionType.Relocation? DefaultName : DefaultNameWithAddends; + } - public ElfRelocationTable() : base(ElfSectionType.RelocationAddends) + public override ElfSectionType Type + { + get => base.Type; + set { - Name = DefaultNameWithAddends; - _entries = new List(); + if (value != ElfSectionType.Relocation && value != ElfSectionType.RelocationAddends) + { + throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}] `{nameof(ElfRelocationTable)}` while `{ElfSectionType.Relocation}` or `{ElfSectionType.RelocationAddends}` are expected"); + } + base.Type = value; } + } - /// - /// Gets a list of entries. - /// - public List Entries => _entries; + public bool IsRelocationWithAddends => this.Type == ElfSectionType.RelocationAddends; - private static string GetDefaultName(ElfSectionType type) + public override void Read(ElfReader reader) + { + if (Parent!.FileClass == ElfFileClass.Is32) { - return type == ElfSectionType.Relocation? DefaultName : DefaultNameWithAddends; + Read32(reader); } - - public override ElfSectionType Type + else { - get => base.Type; - set - { - if (value != ElfSectionType.Relocation && value != ElfSectionType.RelocationAddends) - { - throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}] `{nameof(ElfRelocationTable)}` while `{ElfSectionType.Relocation}` or `{ElfSectionType.RelocationAddends}` are expected"); - } - base.Type = value; - } + Read64(reader); } + } - public bool IsRelocationWithAddends => this.Type == ElfSectionType.RelocationAddends; - - protected override void Read(ElfReader reader) + public override void Write(ElfWriter writer) + { + if (Parent!.FileClass == ElfFileClass.Is32) { - if (Parent!.FileClass == ElfFileClass.Is32) - { - Read32(reader); - } - else - { - Read64(reader); - } + Write32(writer); } - - protected override void Write(ElfWriter writer) + else { - if (Parent!.FileClass == ElfFileClass.Is32) - { - Write32(writer); - } - else - { - Write64(writer); - } + Write64(writer); } + } - public override unsafe ulong TableEntrySize => - Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : - Parent.FileClass == ElfFileClass.Is32 ? (ulong) (IsRelocationWithAddends ? sizeof(ElfNative.Elf32_Rela) : sizeof(ElfNative.Elf32_Rel)) : (ulong) (IsRelocationWithAddends ? sizeof(ElfNative.Elf64_Rela) : sizeof(ElfNative.Elf64_Rel)); + public override unsafe ulong TableEntrySize => + Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : + Parent.FileClass == ElfFileClass.Is32 ? (ulong) (IsRelocationWithAddends ? sizeof(ElfNative.Elf32_Rela) : sizeof(ElfNative.Elf32_Rel)) : (ulong) (IsRelocationWithAddends ? sizeof(ElfNative.Elf64_Rela) : sizeof(ElfNative.Elf64_Rel)); - private void Read32(ElfReader reader) + private void Read32(ElfReader reader) + { + var numberOfEntries = base.Size / OriginalTableEntrySize; + if (IsRelocationWithAddends) { - var numberOfEntries = base.Size / OriginalTableEntrySize; - if (IsRelocationWithAddends) + for (ulong i = 0; i < numberOfEntries; i++) { - for (ulong i = 0; i < numberOfEntries; i++) + ElfNative.Elf32_Rela rel; + ulong streamOffset = (ulong)reader.Stream.Position; + if (!reader.TryReadData((int)OriginalTableEntrySize, out rel)) { - ElfNative.Elf32_Rela rel; - ulong streamOffset = (ulong)reader.Stream.Position; - if (!reader.TryReadData((int)OriginalTableEntrySize, out rel)) - { - reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteRelocationAddendsEntry32Size, $"Unable to read entirely the relocation entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); - } - - var offset = reader.Decode(rel.r_offset); - var r_info = reader.Decode(rel.r_info); - var type = new ElfRelocationType(Parent!.Arch, r_info & 0xFF); - var symbolIndex = r_info >> 8; - var addend = reader.Decode(rel.r_addend); - - var entry = new ElfRelocation(offset, type, symbolIndex, addend); - _entries.Add(entry); + reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteRelocationAddendsEntry32Size, $"Unable to read entirely the relocation entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); } + + var offset = reader.Decode(rel.r_offset); + var r_info = reader.Decode(rel.r_info); + var type = new ElfRelocationType(Parent!.Arch, r_info & 0xFF); + var symbolIndex = r_info >> 8; + var addend = reader.Decode(rel.r_addend); + + var entry = new ElfRelocation(offset, type, symbolIndex, addend); + _entries.Add(entry); } - else + } + else + { + for (ulong i = 0; i < numberOfEntries; i++) { - for (ulong i = 0; i < numberOfEntries; i++) + ElfNative.Elf32_Rel rel; + ulong streamOffset = (ulong)reader.Stream.Position; + if (!reader.TryReadData((int)OriginalTableEntrySize, out rel)) { - ElfNative.Elf32_Rel rel; - ulong streamOffset = (ulong)reader.Stream.Position; - if (!reader.TryReadData((int)OriginalTableEntrySize, out rel)) - { - reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteRelocationEntry32Size, $"Unable to read entirely the relocation entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); - } + reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteRelocationEntry32Size, $"Unable to read entirely the relocation entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); + } - var offset = reader.Decode(rel.r_offset); + var offset = reader.Decode(rel.r_offset); - var r_info = reader.Decode(rel.r_info); - var type = new ElfRelocationType(Parent!.Arch, r_info & 0xFF); - var symbolIndex = r_info >> 8; + var r_info = reader.Decode(rel.r_info); + var type = new ElfRelocationType(Parent!.Arch, r_info & 0xFF); + var symbolIndex = r_info >> 8; - var entry = new ElfRelocation(offset, type, symbolIndex, 0); - _entries.Add(entry); - } + var entry = new ElfRelocation(offset, type, symbolIndex, 0); + _entries.Add(entry); } } + } - private void Read64(ElfReader reader) + private void Read64(ElfReader reader) + { + var numberOfEntries = base.Size / OriginalTableEntrySize; + if (IsRelocationWithAddends) { - var numberOfEntries = base.Size / OriginalTableEntrySize; - if (IsRelocationWithAddends) + for (ulong i = 0; i < numberOfEntries; i++) { - for (ulong i = 0; i < numberOfEntries; i++) + ElfNative.Elf64_Rela rel; + ulong streamOffset = (ulong)reader.Stream.Position; + if (!reader.TryReadData((int)OriginalTableEntrySize, out rel)) { - ElfNative.Elf64_Rela rel; - ulong streamOffset = (ulong)reader.Stream.Position; - if (!reader.TryReadData((int)OriginalTableEntrySize, out rel)) - { - reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteRelocationAddendsEntry64Size, $"Unable to read entirely the relocation entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); - } - - var offset = reader.Decode(rel.r_offset); - - var r_info = reader.Decode(rel.r_info); - var type = new ElfRelocationType(Parent!.Arch, (uint)(r_info & 0xFFFFFFFF)); - var symbolIndex = (uint)(r_info >> 32); - var addend = reader.Decode(rel.r_addend); - - var entry = new ElfRelocation(offset, type, symbolIndex, addend); - _entries.Add(entry); + reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteRelocationAddendsEntry64Size, $"Unable to read entirely the relocation entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); } + + var offset = reader.Decode(rel.r_offset); + + var r_info = reader.Decode(rel.r_info); + var type = new ElfRelocationType(Parent!.Arch, (uint)(r_info & 0xFFFFFFFF)); + var symbolIndex = (uint)(r_info >> 32); + var addend = reader.Decode(rel.r_addend); + + var entry = new ElfRelocation(offset, type, symbolIndex, addend); + _entries.Add(entry); } - else + } + else + { + for (ulong i = 0; i < numberOfEntries; i++) { - for (ulong i = 0; i < numberOfEntries; i++) + ElfNative.Elf64_Rel rel; + ulong streamOffset = (ulong)reader.Stream.Position; + if (!reader.TryReadData((int)OriginalTableEntrySize, out rel)) { - ElfNative.Elf64_Rel rel; - ulong streamOffset = (ulong)reader.Stream.Position; - if (!reader.TryReadData((int)OriginalTableEntrySize, out rel)) - { - reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteRelocationEntry64Size, $"Unable to read entirely the relocation entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); - } + reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteRelocationEntry64Size, $"Unable to read entirely the relocation entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); + } - var offset = reader.Decode(rel.r_offset); + var offset = reader.Decode(rel.r_offset); - var r_info = reader.Decode(rel.r_info); - var type = new ElfRelocationType(Parent!.Arch, (uint)(r_info & 0xFFFFFFFF)); - var symbolIndex = (uint)(r_info >> 32); + var r_info = reader.Decode(rel.r_info); + var type = new ElfRelocationType(Parent!.Arch, (uint)(r_info & 0xFFFFFFFF)); + var symbolIndex = (uint)(r_info >> 32); - var entry = new ElfRelocation(offset, type, symbolIndex, 0); - _entries.Add(entry); - } + var entry = new ElfRelocation(offset, type, symbolIndex, 0); + _entries.Add(entry); } } + } - private void Write32(ElfWriter writer) + private void Write32(ElfWriter writer) + { + if (IsRelocationWithAddends) { - if (IsRelocationWithAddends) + // Write all entries + for (int i = 0; i < Entries.Count; i++) { - // Write all entries - for (int i = 0; i < Entries.Count; i++) - { - var entry = Entries[i]; - - var rel = new ElfNative.Elf32_Rela(); - writer.Encode(out rel.r_offset, (uint)entry.Offset); - uint r_info = entry.Info32; - writer.Encode(out rel.r_info, r_info); - writer.Encode(out rel.r_addend, (int)entry.Addend); - writer.Write(rel); - } + var entry = Entries[i]; + + var rel = new ElfNative.Elf32_Rela(); + writer.Encode(out rel.r_offset, (uint)entry.Offset); + uint r_info = entry.Info32; + writer.Encode(out rel.r_info, r_info); + writer.Encode(out rel.r_addend, (int)entry.Addend); + writer.Write(rel); } - else + } + else + { + // Write all entries + for (int i = 0; i < Entries.Count; i++) { - // Write all entries - for (int i = 0; i < Entries.Count; i++) - { - var entry = Entries[i]; + var entry = Entries[i]; - var rel = new ElfNative.Elf32_Rel(); - writer.Encode(out rel.r_offset, (uint)entry.Offset); - uint r_info = entry.Info32; - writer.Encode(out rel.r_info, r_info); - writer.Write(rel); - } + var rel = new ElfNative.Elf32_Rel(); + writer.Encode(out rel.r_offset, (uint)entry.Offset); + uint r_info = entry.Info32; + writer.Encode(out rel.r_info, r_info); + writer.Write(rel); } } + } - private void Write64(ElfWriter writer) + private void Write64(ElfWriter writer) + { + if (IsRelocationWithAddends) { - if (IsRelocationWithAddends) + // Write all entries + for (int i = 0; i < Entries.Count; i++) { - // Write all entries - for (int i = 0; i < Entries.Count; i++) - { - var entry = Entries[i]; - - var rel = new ElfNative.Elf64_Rela(); - writer.Encode(out rel.r_offset, entry.Offset); - ulong r_info = entry.Info64; - writer.Encode(out rel.r_info, r_info); - writer.Encode(out rel.r_addend, entry.Addend); - writer.Write(rel); - } + var entry = Entries[i]; + + var rel = new ElfNative.Elf64_Rela(); + writer.Encode(out rel.r_offset, entry.Offset); + ulong r_info = entry.Info64; + writer.Encode(out rel.r_info, r_info); + writer.Encode(out rel.r_addend, entry.Addend); + writer.Write(rel); } - else + } + else + { + // Write all entries + for (int i = 0; i < Entries.Count; i++) { - // Write all entries - for (int i = 0; i < Entries.Count; i++) - { - var entry = Entries[i]; + var entry = Entries[i]; - var rel = new ElfNative.Elf64_Rel(); - writer.Encode(out rel.r_offset, (uint)entry.Offset); - ulong r_info = entry.Info64; - writer.Encode(out rel.r_info, r_info); - writer.Write(rel); - } + var rel = new ElfNative.Elf64_Rel(); + writer.Encode(out rel.r_offset, (uint)entry.Offset); + ulong r_info = entry.Info64; + writer.Encode(out rel.r_info, r_info); + writer.Write(rel); } } + } - protected override void AfterRead(ElfReader reader) + protected override void AfterRead(ElfReader reader) + { + var name = Name.Value; + if (name == null) { - var name = Name.Value; - if (name == null) - { - return; - } + return; + } - var defaultName = GetDefaultName(Type); + var defaultName = GetDefaultName(Type); - if (!name.StartsWith(defaultName)) - { - reader.Diagnostics.Warning(DiagnosticId.ELF_WRN_InvalidRelocationTablePrefixName, $"The name of the {Type} section `{this}` doesn't start with `{DefaultName}`"); - } - else + if (!name.StartsWith(defaultName)) + { + reader.Diagnostics.Warning(DiagnosticId.ELF_WRN_InvalidRelocationTablePrefixName, $"The name of the {Type} section `{this}` doesn't start with `{DefaultName}`"); + } + else + { + // Check the name of relocation + var currentTargetName = name.Substring(defaultName.Length); + var sectionTargetName = Info.Section?.Name.Value; + if (sectionTargetName != null && currentTargetName != sectionTargetName) { - // Check the name of relocation - var currentTargetName = name.Substring(defaultName.Length); - var sectionTargetName = Info.Section?.Name.Value; - if (sectionTargetName != null && currentTargetName != sectionTargetName) - { - reader.Diagnostics.Warning(DiagnosticId.ELF_WRN_InvalidRelocationTablePrefixTargetName, $"Invalid name `{name}` for relocation table [{Index}] the current link section is named `{sectionTargetName}` so the expected name should be `{defaultName}{sectionTargetName}`", this); - } + reader.Diagnostics.Warning(DiagnosticId.ELF_WRN_InvalidRelocationTablePrefixTargetName, $"Invalid name `{name}` for relocation table [{Index}] the current link section is named `{sectionTargetName}` so the expected name should be `{defaultName}{sectionTargetName}`", this); } } + } - public override void Verify(DiagnosticBag diagnostics) + public override void Verify(ElfVisitorContext context) + { + var diagnostics = context.Diagnostics; + + //if (Info.Section == null) + //{ + // diagnostics.Error($"Invalid {nameof(Info)} of the section [{Index}] `{nameof(ElfRelocationTable)}` that cannot be null and must point to a valid section", this); + //} + //else + if (Info.Section != null && Info.Section.Parent != Parent) { - base.Verify(diagnostics); - - //if (Info.Section == null) - //{ - // diagnostics.Error($"Invalid {nameof(Info)} of the section [{Index}] `{nameof(ElfRelocationTable)}` that cannot be null and must point to a valid section", this); - //} - //else - if (Info.Section != null && Info.Section.Parent != Parent) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidRelocationInfoParent, $"Invalid parent for the {nameof(Info)} of the section [{Index}] `{nameof(ElfRelocationTable)}`. It must point to the same {nameof(ElfObjectFile)} parent instance than this section parent", this); - } + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidRelocationInfoParent, $"Invalid parent for the {nameof(Info)} of the section [{Index}] `{nameof(ElfRelocationTable)}`. It must point to the same {nameof(ElfObjectFile)} parent instance than this section parent", this); + } - var symbolTable = Link.Section as ElfSymbolTable; + var symbolTable = Link.Section as ElfSymbolTable; - // Write all entries - for (int i = 0; i < Entries.Count; i++) + // Write all entries + for (int i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + if (entry.Addend != 0 && !IsRelocationWithAddends) { - var entry = Entries[i]; - if (entry.Addend != 0 && !IsRelocationWithAddends) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidRelocationEntryAddend, $"Invalid relocation entry {i} in section [{Index}] `{nameof(ElfRelocationTable)}`. The addend != 0 while the section is not a `{ElfSectionType.RelocationAddends}`", this); - } + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidRelocationEntryAddend, $"Invalid relocation entry {i} in section [{Index}] `{nameof(ElfRelocationTable)}`. The addend != 0 while the section is not a `{ElfSectionType.RelocationAddends}`", this); + } - if (entry.Type.Arch != Parent!.Arch) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidRelocationEntryArch, $"Invalid Arch `{entry.Type.Arch}` for relocation entry {i} in section [{Index}] `{nameof(ElfRelocationTable)}`. The arch doesn't match the arch `{Parent.Arch}`", this); - } + if (entry.Type.Arch != Parent!.Arch) + { + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidRelocationEntryArch, $"Invalid Arch `{entry.Type.Arch}` for relocation entry {i} in section [{Index}] `{nameof(ElfRelocationTable)}`. The arch doesn't match the arch `{Parent.Arch}`", this); + } - if (symbolTable != null && entry.SymbolIndex > (uint)symbolTable.Entries.Count) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidRelocationSymbolIndex, $"Out of range symbol index `{entry.SymbolIndex}` (max: {symbolTable.Entries.Count + 1} from symbol table {symbolTable}) for relocation entry {i} in section [{Index}] `{nameof(ElfRelocationTable)}`", this); - } + if (symbolTable != null && entry.SymbolIndex > (uint)symbolTable.Entries.Count) + { + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidRelocationSymbolIndex, $"Out of range symbol index `{entry.SymbolIndex}` (max: {symbolTable.Entries.Count + 1} from symbol table {symbolTable}) for relocation entry {i} in section [{Index}] `{nameof(ElfRelocationTable)}`", this); } } + } - public override unsafe void UpdateLayout(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - - Size = Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : - Parent.FileClass == ElfFileClass.Is32 - ? (ulong)Entries.Count * (IsRelocationWithAddends ? (ulong)sizeof(ElfNative.Elf32_Rela) : (ulong)sizeof(ElfNative.Elf32_Rel)) - : (ulong)Entries.Count * (IsRelocationWithAddends ? (ulong)sizeof(ElfNative.Elf64_Rela) : (ulong)sizeof(ElfNative.Elf64_Rel)); + public override unsafe void UpdateLayout(ElfVisitorContext context) + { + Size = Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : + Parent.FileClass == ElfFileClass.Is32 + ? (ulong)Entries.Count * (IsRelocationWithAddends ? (ulong)sizeof(ElfNative.Elf32_Rela) : (ulong)sizeof(ElfNative.Elf32_Rel)) + : (ulong)Entries.Count * (IsRelocationWithAddends ? (ulong)sizeof(ElfNative.Elf64_Rela) : (ulong)sizeof(ElfNative.Elf64_Rel)); - } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs b/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs index 31ac3bb..20adf3e 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs @@ -7,229 +7,228 @@ using System.IO; using LibObjectFile.Dwarf; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Extension methods for +/// +public static class ElfRelocationTableExtensions { /// - /// Extension methods for + /// Applies the relocation defined by this table to the specified stream. The stream must be seekable and writeable. /// - public static class ElfRelocationTableExtensions + public static void Relocate(this ElfRelocationTable relocTable, in ElfRelocationContext context) { - /// - /// Applies the relocation defined by this table to the specified stream. The stream must be seekable and writeable. - /// - public static void Relocate(this ElfRelocationTable relocTable, in ElfRelocationContext context) + var relocTarget = relocTable.Info.Section; + if (!(relocTarget is ElfBinarySection relocTargetBinarySection)) { - var relocTarget = relocTable.Info.Section; - if (!(relocTarget is ElfBinarySection relocTargetBinarySection)) - { - throw new InvalidOperationException($"Invalid ElfRelocationTable.Info section. Can only relocate a section that inherits from {nameof(ElfBinarySection)}."); - } - - Relocate(relocTable, relocTargetBinarySection.Stream!, context); + throw new InvalidOperationException($"Invalid ElfRelocationTable.Info section. Can only relocate a section that inherits from {nameof(ElfBinarySection)}."); } - /// - /// Applies the relocation defined by this table to the specified stream. The stream must be seekable and writeable. - /// - /// - public static void Relocate(this ElfRelocationTable relocTable, Stream stream, in ElfRelocationContext context) - { - if (stream == null) throw new ArgumentNullException(nameof(stream)); + Relocate(relocTable, relocTargetBinarySection.Stream!, context); + } - switch (relocTable.Parent!.Arch.Value) - { - case ElfArch.X86_64: - ApplyX86_64(relocTable, stream, context); - break; - default: - throw new NotImplementedException($"The relocation for architecture {relocTable.Parent.Arch} is not supported/implemented."); - } - stream.Position = 0; - } + /// + /// Applies the relocation defined by this table to the specified stream. The stream must be seekable and writeable. + /// + /// + public static void Relocate(this ElfRelocationTable relocTable, Stream stream, in ElfRelocationContext context) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); - /// - /// Applies the relocation defined by this table to the specified stream. The stream must be seekable and writeable. - /// - /// - private static void ApplyX86_64(this ElfRelocationTable relocTable, Stream stream, in ElfRelocationContext context) + switch (relocTable.Parent!.Arch.Value) { - if (stream == null) throw new ArgumentNullException(nameof(stream)); - bool isLsb = relocTable.Parent!.Encoding == ElfEncoding.Lsb; + case ElfArch.X86_64: + ApplyX86_64(relocTable, stream, context); + break; + default: + throw new NotImplementedException($"The relocation for architecture {relocTable.Parent.Arch} is not supported/implemented."); + } + stream.Position = 0; + } - var GOT = (long)context.GlobalObjectTableAddress; - var B = (long)context.BaseAddress; - var G = (long)context.GlobalObjectTableOffset; + /// + /// Applies the relocation defined by this table to the specified stream. The stream must be seekable and writeable. + /// + /// + private static void ApplyX86_64(this ElfRelocationTable relocTable, Stream stream, in ElfRelocationContext context) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); + bool isLsb = relocTable.Parent!.Encoding == ElfEncoding.Lsb; + + var GOT = (long)context.GlobalObjectTableAddress; + var B = (long)context.BaseAddress; + var G = (long)context.GlobalObjectTableOffset; - var symbolTable = (ElfSymbolTable)relocTable.Link.Section!; + var symbolTable = (ElfSymbolTable)relocTable.Link.Section!; - foreach (var reloc in relocTable.Entries) + foreach (var reloc in relocTable.Entries) + { + var P = (long)reloc.Offset; + var L = (long)context.ProcedureLinkageTableAddress + P; // TODO: Is it really that? + var A = reloc.Addend; + var symbol = symbolTable.Entries[(int)reloc.SymbolIndex - 1]; + var Z = (long)symbol.Size; + + var patchOffset = (long)reloc.Offset; + stream.Position = patchOffset; + + switch (reloc.Type.Value) { - var P = (long)reloc.Offset; - var L = (long)context.ProcedureLinkageTableAddress + P; // TODO: Is it really that? - var A = reloc.Addend; - var symbol = symbolTable.Entries[(int)reloc.SymbolIndex - 1]; - var Z = (long)symbol.Size; + case ElfNative.R_X86_64_NONE: + case ElfNative.R_X86_64_COPY: + case ElfNative.R_X86_64_DTPMOD64: + case ElfNative.R_X86_64_DTPOFF64: + case ElfNative.R_X86_64_TPOFF64: + case ElfNative.R_X86_64_TLSGD: + case ElfNative.R_X86_64_TLSLD: + case ElfNative.R_X86_64_DTPOFF32: + case ElfNative.R_X86_64_GOTTPOFF: + case ElfNative.R_X86_64_TPOFF32: + break; - var patchOffset = (long)reloc.Offset; - stream.Position = patchOffset; + case ElfNative.R_X86_64_64: // S + A + { + var S = (long) stream.ReadU64(isLsb); + stream.Position = patchOffset; + stream.WriteU64(isLsb, (ulong) (S + A)); + break; + } + case ElfNative.R_X86_64_PC32: // S + A - P + { + var S = (long) stream.ReadU32(isLsb); + stream.Position = patchOffset; + stream.WriteU32(isLsb, (uint) (S + A - P)); + break; + } + case ElfNative.R_X86_64_GOT32: // G + A + { + stream.WriteU32(isLsb, (uint) (G + A)); + break; + } + case ElfNative.R_X86_64_PLT32: // L + A - P + { + stream.WriteU32(isLsb, (uint) (L + A - P)); + break; + } + case ElfNative.R_X86_64_GLOB_DAT: // S + case ElfNative.R_X86_64_JUMP_SLOT: // S + break; - switch (reloc.Type.Value) + case ElfNative.R_X86_64_RELATIVE: // B + A { - case ElfNative.R_X86_64_NONE: - case ElfNative.R_X86_64_COPY: - case ElfNative.R_X86_64_DTPMOD64: - case ElfNative.R_X86_64_DTPOFF64: - case ElfNative.R_X86_64_TPOFF64: - case ElfNative.R_X86_64_TLSGD: - case ElfNative.R_X86_64_TLSLD: - case ElfNative.R_X86_64_DTPOFF32: - case ElfNative.R_X86_64_GOTTPOFF: - case ElfNative.R_X86_64_TPOFF32: - break; - - case ElfNative.R_X86_64_64: // S + A - { - var S = (long) stream.ReadU64(isLsb); - stream.Position = patchOffset; - stream.WriteU64(isLsb, (ulong) (S + A)); - break; - } - case ElfNative.R_X86_64_PC32: // S + A - P - { - var S = (long) stream.ReadU32(isLsb); - stream.Position = patchOffset; - stream.WriteU32(isLsb, (uint) (S + A - P)); - break; - } - case ElfNative.R_X86_64_GOT32: // G + A - { - stream.WriteU32(isLsb, (uint) (G + A)); - break; - } - case ElfNative.R_X86_64_PLT32: // L + A - P - { - stream.WriteU32(isLsb, (uint) (L + A - P)); - break; - } - case ElfNative.R_X86_64_GLOB_DAT: // S - case ElfNative.R_X86_64_JUMP_SLOT: // S - break; - - case ElfNative.R_X86_64_RELATIVE: // B + A - { - stream.WriteU64(isLsb, (ulong) (B + A)); - break; - } - case ElfNative.R_X86_64_GOTPCREL: // G + GOT + A - P - { - stream.WriteU32(isLsb, (uint) (G + GOT + A - P)); - break; - } - case ElfNative.R_X86_64_32: // S + A - { - var S = (long) stream.ReadU32(isLsb); - stream.Position = patchOffset; - stream.WriteU32(isLsb, (uint) (S + A)); - break; - } - case ElfNative.R_X86_64_32S: // S + A - { - var S = (long) stream.ReadI32(isLsb); - stream.Position = patchOffset; - stream.WriteI32(isLsb, (int) (S + A)); - break; - } - case ElfNative.R_X86_64_16: // S + A - { - var S = (long) stream.ReadU16(isLsb); - stream.Position = patchOffset; - stream.WriteU16(isLsb, (ushort) (S + A)); - break; - } - case ElfNative.R_X86_64_PC16: // S + A - P - { - var S = (long) stream.ReadU16(isLsb); - stream.Position = patchOffset; - stream.WriteU16(isLsb, (ushort) (S + A - P)); - break; - } - case ElfNative.R_X86_64_8: // S + A - { - var S = (long) stream.ReadU8(); - stream.Position = patchOffset; - stream.WriteU8((byte) (S + A)); - break; - } - case ElfNative.R_X86_64_PC8: // S + A - P - { - var S = (long)stream.ReadU8(); - stream.Position = patchOffset; - stream.WriteU8((byte)(S + A - P)); - break; - } - - case ElfNative.R_X86_64_PC64: // S + A - P - { - var S = (long)stream.ReadU64(isLsb); - stream.Position = patchOffset; - stream.WriteU64(isLsb, (ulong)(S + A - P)); - break; - } - - case ElfNative.R_X86_64_GOTOFF64: // S + A - GOT - { - var S = (long)stream.ReadU64(isLsb); - stream.Position = patchOffset; - stream.WriteU64(isLsb, (ulong)(S + A - GOT)); - break; - } - case ElfNative.R_X86_64_GOTPC32: // GOT + A - P - stream.WriteU32(isLsb, (uint)(GOT + A - P)); - break; - - case ElfNative.R_X86_64_GOT64: // G + A - stream.WriteU64(isLsb, (ulong)(G + A)); - break; - - case ElfNative.R_X86_64_GOTPCREL64: // G + GOT - P + A - stream.WriteU64(isLsb, (ulong)(G + GOT - P + A)); - break; - - case ElfNative.R_X86_64_GOTPC64: // GOT - P + A - stream.WriteU64(isLsb, (ulong)(GOT - P + A)); - break; - - case ElfNative.R_X86_64_GOTPLT64: // G + A - stream.WriteU64(isLsb, (ulong)(G + A)); - break; - - case ElfNative.R_X86_64_PLTOFF64: // L - GOT + A - stream.WriteU64(isLsb, (ulong)(L - GOT + A)); - break; - - case ElfNative.R_X86_64_GOTPC32_TLSDESC: - case ElfNative.R_X86_64_TLSDESC_CALL: - case ElfNative.R_X86_64_TLSDESC: - break; - - case ElfNative.R_X86_64_RELATIVE64: // B + A - stream.WriteU64(isLsb, (ulong)(B + A)); - break; - - case ElfNative.R_X86_64_SIZE32: - stream.WriteU32(isLsb, (uint)(Z + A)); - break; - case ElfNative.R_X86_64_SIZE64: - stream.WriteU64(isLsb, (ulong)(Z + A)); - break; - - case ElfNative.R_X86_64_IRELATIVE: - default: - throw new NotImplementedException($"Relocation {reloc} not implemented/supported"); + stream.WriteU64(isLsb, (ulong) (B + A)); + break; } - } + case ElfNative.R_X86_64_GOTPCREL: // G + GOT + A - P + { + stream.WriteU32(isLsb, (uint) (G + GOT + A - P)); + break; + } + case ElfNative.R_X86_64_32: // S + A + { + var S = (long) stream.ReadU32(isLsb); + stream.Position = patchOffset; + stream.WriteU32(isLsb, (uint) (S + A)); + break; + } + case ElfNative.R_X86_64_32S: // S + A + { + var S = (long) stream.ReadI32(isLsb); + stream.Position = patchOffset; + stream.WriteI32(isLsb, (int) (S + A)); + break; + } + case ElfNative.R_X86_64_16: // S + A + { + var S = (long) stream.ReadU16(isLsb); + stream.Position = patchOffset; + stream.WriteU16(isLsb, (ushort) (S + A)); + break; + } + case ElfNative.R_X86_64_PC16: // S + A - P + { + var S = (long) stream.ReadU16(isLsb); + stream.Position = patchOffset; + stream.WriteU16(isLsb, (ushort) (S + A - P)); + break; + } + case ElfNative.R_X86_64_8: // S + A + { + var S = (long) stream.ReadU8(); + stream.Position = patchOffset; + stream.WriteU8((byte) (S + A)); + break; + } + case ElfNative.R_X86_64_PC8: // S + A - P + { + var S = (long)stream.ReadU8(); + stream.Position = patchOffset; + stream.WriteU8((byte)(S + A - P)); + break; + } + + case ElfNative.R_X86_64_PC64: // S + A - P + { + var S = (long)stream.ReadU64(isLsb); + stream.Position = patchOffset; + stream.WriteU64(isLsb, (ulong)(S + A - P)); + break; + } + + case ElfNative.R_X86_64_GOTOFF64: // S + A - GOT + { + var S = (long)stream.ReadU64(isLsb); + stream.Position = patchOffset; + stream.WriteU64(isLsb, (ulong)(S + A - GOT)); + break; + } + case ElfNative.R_X86_64_GOTPC32: // GOT + A - P + stream.WriteU32(isLsb, (uint)(GOT + A - P)); + break; + + case ElfNative.R_X86_64_GOT64: // G + A + stream.WriteU64(isLsb, (ulong)(G + A)); + break; + + case ElfNative.R_X86_64_GOTPCREL64: // G + GOT - P + A + stream.WriteU64(isLsb, (ulong)(G + GOT - P + A)); + break; + + case ElfNative.R_X86_64_GOTPC64: // GOT - P + A + stream.WriteU64(isLsb, (ulong)(GOT - P + A)); + break; + + case ElfNative.R_X86_64_GOTPLT64: // G + A + stream.WriteU64(isLsb, (ulong)(G + A)); + break; + + case ElfNative.R_X86_64_PLTOFF64: // L - GOT + A + stream.WriteU64(isLsb, (ulong)(L - GOT + A)); + break; + + case ElfNative.R_X86_64_GOTPC32_TLSDESC: + case ElfNative.R_X86_64_TLSDESC_CALL: + case ElfNative.R_X86_64_TLSDESC: + break; - stream.Position = 0; + case ElfNative.R_X86_64_RELATIVE64: // B + A + stream.WriteU64(isLsb, (ulong)(B + A)); + break; + + case ElfNative.R_X86_64_SIZE32: + stream.WriteU32(isLsb, (uint)(Z + A)); + break; + case ElfNative.R_X86_64_SIZE64: + stream.WriteU64(isLsb, (ulong)(Z + A)); + break; + + case ElfNative.R_X86_64_IRELATIVE: + default: + throw new NotImplementedException($"Relocation {reloc} not implemented/supported"); + } } + + stream.Position = 0; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocationType.cs b/src/LibObjectFile/Elf/Sections/ElfRelocationType.cs index a4c87d9..5af606e 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationType.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationType.cs @@ -4,63 +4,62 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Gets the type of a . +/// +public readonly partial struct ElfRelocationType : IEquatable { - /// - /// Gets the type of a . - /// - public readonly partial struct ElfRelocationType : IEquatable + public ElfRelocationType(ElfArchEx arch, uint value) { - public ElfRelocationType(ElfArchEx arch, uint value) - { - Arch = arch; - Value = value; - } + Arch = arch; + Value = value; + } - /// - /// The associated the applies to. - /// - public readonly ElfArchEx Arch; + /// + /// The associated the applies to. + /// + public readonly ElfArchEx Arch; - /// - /// The value of this relocation type. - /// - public readonly uint Value; + /// + /// The value of this relocation type. + /// + public readonly uint Value; - public bool Equals(ElfRelocationType other) - { - return Arch.Equals(other.Arch) && Value == other.Value; - } + public bool Equals(ElfRelocationType other) + { + return Arch.Equals(other.Arch) && Value == other.Value; + } - public override bool Equals(object? obj) - { - return obj is ElfRelocationType other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is ElfRelocationType other && Equals(other); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - return (Arch.GetHashCode() * 397) ^ (int) Value; - } + return (Arch.GetHashCode() * 397) ^ (int) Value; } + } - public static bool operator ==(ElfRelocationType left, ElfRelocationType right) - { - return left.Equals(right); - } + public static bool operator ==(ElfRelocationType left, ElfRelocationType right) + { + return left.Equals(right); + } - public static bool operator !=(ElfRelocationType left, ElfRelocationType right) - { - return !left.Equals(right); - } + public static bool operator !=(ElfRelocationType left, ElfRelocationType right) + { + return !left.Equals(right); + } - public string? Name => ToStringInternal(); + public string? Name => ToStringInternal(); - public override string ToString() - { - if (Arch.Value == 0 && Value == 0) return "Empty ElfRelocationType"; - return ToStringInternal() ?? $"Unknown {nameof(ElfRelocationType)} (0x{Value:X4})"; - } + public override string ToString() + { + if (Arch.Value == 0 && Value == 0) return "Empty ElfRelocationType"; + return ToStringInternal() ?? $"Unknown {nameof(ElfRelocationType)} (0x{Value:X4})"; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfSectionHeaderStringTable.cs b/src/LibObjectFile/Elf/Sections/ElfSectionHeaderStringTable.cs index a0d41fe..92279da 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSectionHeaderStringTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSectionHeaderStringTable.cs @@ -2,18 +2,17 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// The Section Header String Table used by . +/// +public sealed class ElfSectionHeaderStringTable : ElfStringTable { - /// - /// The Section Header String Table used by . - /// - public sealed class ElfSectionHeaderStringTable : ElfStringTable - { - public new const string DefaultName = ".shstrtab"; + public new const string DefaultName = ".shstrtab"; - public ElfSectionHeaderStringTable() - { - Name = DefaultName; - } + public ElfSectionHeaderStringTable() + { + Name = DefaultName; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfShadowSection.cs b/src/LibObjectFile/Elf/Sections/ElfShadowSection.cs index 11fead1..16dea98 100644 --- a/src/LibObjectFile/Elf/Sections/ElfShadowSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfShadowSection.cs @@ -2,18 +2,17 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A shadow section is a section that will not be saved to the section header table but can contain data +/// that will be saved with the . +/// A shadow section is usually associated with an that is referencing a portion of +/// data that is not owned by a visible section. +/// +public abstract class ElfShadowSection : ElfSection { - /// - /// A shadow section is a section that will not be saved to the section header table but can contain data - /// that will be saved with the . - /// A shadow section is usually associated with an that is referencing a portion of - /// data that is not owned by a visible section. - /// - public abstract class ElfShadowSection : ElfSection + protected ElfShadowSection() : base(ElfSectionType.Null) { - protected ElfShadowSection() : base(ElfSectionType.Null) - { - } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfStringTable.cs b/src/LibObjectFile/Elf/Sections/ElfStringTable.cs index ccadc0f..89b27ac 100644 --- a/src/LibObjectFile/Elf/Sections/ElfStringTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfStringTable.cs @@ -10,313 +10,311 @@ using System.Text; using System.Linq; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A string table section with the type . +/// +public class ElfStringTable : ElfSection { - /// - /// A string table section with the type . - /// - public class ElfStringTable : ElfSection - { - private readonly MemoryStream _table; - private readonly HashSet _reservedStrings; - private readonly Dictionary _mapStringToIndex; - private readonly Dictionary _mapIndexToString; + private readonly MemoryStream _table; + private readonly HashSet _reservedStrings; + private readonly Dictionary _mapStringToIndex; + private readonly Dictionary _mapIndexToString; - public const string DefaultName = ".strtab"; + public const string DefaultName = ".strtab"; - public const int DefaultCapacity = 256; + public const int DefaultCapacity = 256; - public ElfStringTable() : this(DefaultCapacity) - { - } + public ElfStringTable() : this(DefaultCapacity) + { + } - public ElfStringTable(int capacityInBytes) : base(ElfSectionType.StringTable) - { - if (capacityInBytes < 0) throw new ArgumentOutOfRangeException(nameof(capacityInBytes)); - Name = DefaultName; - _table = new MemoryStream(capacityInBytes); - _mapStringToIndex = new Dictionary(); - _mapIndexToString = new Dictionary(); - _reservedStrings = new HashSet(); - // Always create an empty string - CreateIndex(string.Empty); - } + public ElfStringTable(int capacityInBytes) : base(ElfSectionType.StringTable) + { + if (capacityInBytes < 0) throw new ArgumentOutOfRangeException(nameof(capacityInBytes)); + Name = DefaultName; + _table = new MemoryStream(capacityInBytes); + _mapStringToIndex = new Dictionary(); + _mapIndexToString = new Dictionary(); + _reservedStrings = new HashSet(); + // Always create an empty string + CreateIndex(string.Empty); + } - public override ElfSectionType Type + public override ElfSectionType Type + { + get => base.Type; + set { - get => base.Type; - set + if (value != ElfSectionType.StringTable) { - if (value != ElfSectionType.StringTable) - { - throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}] `{nameof(ElfStringTable)}`. Only `{ElfSectionType.StringTable}` is valid"); - } - base.Type = value; + throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}] `{nameof(ElfStringTable)}`. Only `{ElfSectionType.StringTable}` is valid"); } + base.Type = value; } + } + + public override void UpdateLayout(ElfVisitorContext context) + { + if (_reservedStrings.Count > 0) FlushReservedStrings(); + Size = (ulong)_table.Length; + } - public override void UpdateLayout(DiagnosticBag diagnostics) + public override void Read(ElfReader reader) + { + Debug.Assert(_table.Position == 1 && _table.Length == 1); + var length = (long) base.Size; + _table.SetLength(length); + var buffer = _table.GetBuffer(); + reader.Stream.Read(buffer, 0, (int)length); + _table.Position = _table.Length; + } + + public override void Write(ElfWriter writer) + { + writer.Stream.Write(_table.GetBuffer(), 0, (int)_table.Length); + } + + internal void ReserveString(string? text) + { + if (text is not null && !_mapStringToIndex.ContainsKey(text) && !_reservedStrings.Contains(text)) { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - if (_reservedStrings.Count > 0) FlushReservedStrings(); - Size = (ulong)_table.Length; + _reservedStrings.Add(text); } + } - protected override void Read(ElfReader reader) + internal void FlushReservedStrings() + { + // TODO: Use CollectionsMarshal.AsSpan + string[] reservedStrings = _reservedStrings.ToArray(); + + // Pre-sort the string based on their matching suffix + MultiKeySort(reservedStrings, 0); + + // Add the strings to string table + string? lastText = null; + for (int i = 0; i < reservedStrings.Length; i++) { - Debug.Assert(_table.Position == 1 && _table.Length == 1); - var length = (long) base.Size; - _table.SetLength(length); - var buffer = _table.GetBuffer(); - reader.Stream.Read(buffer, 0, (int)length); - _table.Position = _table.Length; + var text = reservedStrings[i]; + uint index; + if (lastText != null && lastText.EndsWith(text, StringComparison.Ordinal)) + { + // Suffix matches the last symbol + index = (uint)(_table.Length - Encoding.UTF8.GetByteCount(text) - 1); + _mapIndexToString.Add(index, text); + _mapStringToIndex.Add(text, index); + } + else + { + lastText = text; + CreateIndex(text); + } } - protected override void Write(ElfWriter writer) + _reservedStrings.Clear(); + + static char TailCharacter(string str, int pos) { - writer.Stream.Write(_table.GetBuffer(), 0, (int)_table.Length); + int index = str.Length - pos - 1; + if ((uint)index < str.Length) + return str[index]; + return '\0'; } - internal void ReserveString(string? text) + static void MultiKeySort(Span input, int pos) { - if (text is not null && !_mapStringToIndex.ContainsKey(text) && !_reservedStrings.Contains(text)) + if (!MultiKeySortSmallInput(input, pos)) { - _reservedStrings.Add(text); + MultiKeySortLargeInput(input, pos); } } - internal void FlushReservedStrings() + static void MultiKeySortLargeInput(Span input, int pos) { - // TODO: Use CollectionsMarshal.AsSpan - string[] reservedStrings = _reservedStrings.ToArray(); - - // Pre-sort the string based on their matching suffix - MultiKeySort(reservedStrings, 0); - - // Add the strings to string table - string? lastText = null; - for (int i = 0; i < reservedStrings.Length; i++) + tailcall: + char pivot = TailCharacter(input[0], pos); + int l = 0, h = input.Length; + for (int i = 1; i < h;) { - var text = reservedStrings[i]; - uint index; - if (lastText != null && lastText.EndsWith(text, StringComparison.Ordinal)) + char c = TailCharacter(input[i], pos); + if (c > pivot) { - // Suffix matches the last symbol - index = (uint)(_table.Length - Encoding.UTF8.GetByteCount(text) - 1); - _mapIndexToString.Add(index, text); - _mapStringToIndex.Add(text, index); + (input[l], input[i]) = (input[i], input[l]); + l++; i++; + } + else if (c < pivot) + { + h--; + (input[h], input[i]) = (input[i], input[h]); } else { - lastText = text; - CreateIndex(text); + i++; } } - _reservedStrings.Clear(); - - static char TailCharacter(string str, int pos) - { - int index = str.Length - pos - 1; - if ((uint)index < str.Length) - return str[index]; - return '\0'; - } - - static void MultiKeySort(Span input, int pos) + MultiKeySort(input.Slice(0, l), pos); + MultiKeySort(input.Slice(h), pos); + if (pivot != '\0') { + // Use a loop as a poor man's tailcall + // MultiKeySort(input.Slice(l, h - l), pos + 1); + pos++; + input = input.Slice(l, h - l); if (!MultiKeySortSmallInput(input, pos)) { - MultiKeySortLargeInput(input, pos); + goto tailcall; } } + } + + static bool MultiKeySortSmallInput(Span input, int pos) + { + if (input.Length <= 1) + return true; - static void MultiKeySortLargeInput(Span input, int pos) + // Optimize comparing two strings + if (input.Length == 2) { - tailcall: - char pivot = TailCharacter(input[0], pos); - int l = 0, h = input.Length; - for (int i = 1; i < h;) + while (true) { - char c = TailCharacter(input[i], pos); - if (c > pivot) + char c0 = TailCharacter(input[0], pos); + char c1 = TailCharacter(input[1], pos); + if (c0 < c1) { - (input[l], input[i]) = (input[i], input[l]); - l++; i++; + (input[0], input[1]) = (input[1], input[0]); + break; } - else if (c < pivot) + else if (c0 > c1 || c0 == (char)0) { - h--; - (input[h], input[i]) = (input[i], input[h]); + break; } - else - { - i++; - } - } - - MultiKeySort(input.Slice(0, l), pos); - MultiKeySort(input.Slice(h), pos); - if (pivot != '\0') - { - // Use a loop as a poor man's tailcall - // MultiKeySort(input.Slice(l, h - l), pos + 1); pos++; - input = input.Slice(l, h - l); - if (!MultiKeySortSmallInput(input, pos)) - { - goto tailcall; - } } + return true; } - static bool MultiKeySortSmallInput(Span input, int pos) - { - if (input.Length <= 1) - return true; + return false; + } + } - // Optimize comparing two strings - if (input.Length == 2) - { - while (true) - { - char c0 = TailCharacter(input[0], pos); - char c1 = TailCharacter(input[1], pos); - if (c0 < c1) - { - (input[0], input[1]) = (input[1], input[0]); - break; - } - else if (c0 > c1 || c0 == (char)0) - { - break; - } - pos++; - } - return true; - } + private uint CreateIndex(string text) + { + uint index = (uint) _table.Length; + _mapIndexToString.Add(index, text); + _mapStringToIndex.Add(text, index); - return false; - } + if (index == 0) + { + Debug.Assert(index == 0); + _table.WriteByte(0); } - - private uint CreateIndex(string text) + else { - uint index = (uint) _table.Length; - _mapIndexToString.Add(index, text); - _mapStringToIndex.Add(text, index); - - if (index == 0) + var reservedBytes = Encoding.UTF8.GetByteCount(text) + 1; + var buffer = ArrayPool.Shared.Rent(reservedBytes); + var span = new Span(buffer, 0, reservedBytes); + Encoding.UTF8.GetEncoder().GetBytes(text, span, true); + span[reservedBytes - 1] = 0; + if (_table.Position != index) { - Debug.Assert(index == 0); - _table.WriteByte(0); + _table.Position = index; } - else - { - var reservedBytes = Encoding.UTF8.GetByteCount(text) + 1; - var buffer = ArrayPool.Shared.Rent(reservedBytes); - var span = new Span(buffer, 0, reservedBytes); - Encoding.UTF8.GetEncoder().GetBytes(text, span, true); - span[reservedBytes - 1] = 0; - if (_table.Position != index) - { - _table.Position = index; - } - _table.Write(span); - ArrayPool.Shared.Return(buffer); - } - - return index; + _table.Write(span); + ArrayPool.Shared.Return(buffer); } - public uint GetOrCreateIndex(string? text) - { - // Same as empty string - if (text == null) return 0; + return index; + } - if (_reservedStrings.Count > 0) FlushReservedStrings(); + public uint GetOrCreateIndex(string? text) + { + // Same as empty string + if (text == null) return 0; - if (_mapStringToIndex.TryGetValue(text, out uint index)) - { - return index; - } + if (_reservedStrings.Count > 0) FlushReservedStrings(); - return CreateIndex(text); + if (_mapStringToIndex.TryGetValue(text, out uint index)) + { + return index; } - public bool TryResolve(ElfString inStr, out ElfString outStr) + return CreateIndex(text); + } + + public bool TryResolve(ElfString inStr, out ElfString outStr) + { + outStr = inStr; + if (inStr.Value != null) + { + outStr = inStr.WithIndex(GetOrCreateIndex(inStr.Value)); + } + else { - outStr = inStr; - if (inStr.Value != null) + if (TryFind(inStr.Index, out var text)) { - outStr = inStr.WithIndex(GetOrCreateIndex(inStr.Value)); + outStr = inStr.WithName(text); } else { - if (TryFind(inStr.Index, out var text)) - { - outStr = inStr.WithName(text); - } - else - { - return false; - } + return false; } - return true; } + return true; + } - public bool TryFind(uint index, out string text) + public bool TryFind(uint index, out string text) + { + if (index == 0) { - if (index == 0) - { - text = string.Empty; - return true; - } - - if (_reservedStrings.Count > 0) FlushReservedStrings(); + text = string.Empty; + return true; + } - if (_mapIndexToString.TryGetValue(index, out text!)) - { - return true; - } + if (_reservedStrings.Count > 0) FlushReservedStrings(); - if (index >= _table.Length) - { - return false; - } + if (_mapIndexToString.TryGetValue(index, out text!)) + { + return true; + } - _table.Position = index; + if (index >= _table.Length) + { + return false; + } - var buffer = _table.GetBuffer(); - var indexOfByte0 = Array.IndexOf(buffer, (byte)0, (int)index); + _table.Position = index; - if (indexOfByte0 < 0 || indexOfByte0 >= _table.Length) - { - indexOfByte0 = (int)(_table.Length - 1); - } + var buffer = _table.GetBuffer(); + var indexOfByte0 = Array.IndexOf(buffer, (byte)0, (int)index); - var strLength = (int)(indexOfByte0 - index); - text = Encoding.UTF8.GetString(buffer, (int)index, strLength); - _mapIndexToString.Add(index, text); + if (indexOfByte0 < 0 || indexOfByte0 >= _table.Length) + { + indexOfByte0 = (int)(_table.Length - 1); + } - // Don't try to override an existing mapping - if (!_mapStringToIndex.TryGetValue(text, out var existingIndex)) - { - _mapStringToIndex.Add(text, index); - } + var strLength = (int)(indexOfByte0 - index); + text = Encoding.UTF8.GetString(buffer, (int)index, strLength); + _mapIndexToString.Add(index, text); - return true; + // Don't try to override an existing mapping + if (!_mapStringToIndex.TryGetValue(text, out var existingIndex)) + { + _mapStringToIndex.Add(text, index); } - public void Reset() - { - _table.SetLength(0); - _mapStringToIndex.Clear(); - _mapIndexToString.Clear(); - _reservedStrings.Clear(); + return true; + } - // Always create an empty string - CreateIndex(string.Empty); - } + public void Reset() + { + _table.SetLength(0); + _mapStringToIndex.Clear(); + _mapIndexToString.Clear(); + _reservedStrings.Clear(); + + // Always create an empty string + CreateIndex(string.Empty); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbol.cs b/src/LibObjectFile/Elf/Sections/ElfSymbol.cs index b949970..72c7593 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbol.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbol.cs @@ -4,89 +4,88 @@ using System; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A symbol entry in the +/// This is the value seen in or +/// +public struct ElfSymbol : IEquatable { + public static readonly ElfSymbol Empty = new ElfSymbol(); + + /// + /// Gets or sets the value associated to this symbol. + /// + public ulong Value { get; set; } + + /// + /// Gets or sets the size of this symbol. + /// + public ulong Size { get; set; } + + /// + /// Gets or sets the type of this symbol (e.g or ). + /// + public ElfSymbolType Type { get; set; } + /// - /// A symbol entry in the - /// This is the value seen in or + /// Get or sets the binding applying to this symbol (e.g or ). /// - public struct ElfSymbol : IEquatable + public ElfSymbolBind Bind { get; set; } + + /// + /// Gets or sets the visibility of this symbol (e.g ) + /// + public ElfSymbolVisibility Visibility { get; set; } + + /// + /// Gets or sets the associated section to this symbol. + /// + public ElfSectionLink Section { get; set; } + + /// + /// Gets or sets the name of this symbol. + /// + public ElfString Name { get; set; } + + public override string ToString() { - public static readonly ElfSymbol Empty = new ElfSymbol(); - - /// - /// Gets or sets the value associated to this symbol. - /// - public ulong Value { get; set; } - - /// - /// Gets or sets the size of this symbol. - /// - public ulong Size { get; set; } - - /// - /// Gets or sets the type of this symbol (e.g or ). - /// - public ElfSymbolType Type { get; set; } - - /// - /// Get or sets the binding applying to this symbol (e.g or ). - /// - public ElfSymbolBind Bind { get; set; } - - /// - /// Gets or sets the visibility of this symbol (e.g ) - /// - public ElfSymbolVisibility Visibility { get; set; } - - /// - /// Gets or sets the associated section to this symbol. - /// - public ElfSectionLink Section { get; set; } - - /// - /// Gets or sets the name of this symbol. - /// - public ElfString Name { get; set; } - - public override string ToString() - { - return $"{nameof(Value)}: 0x{Value:X16}, {nameof(Size)}: {Size:#####}, {nameof(Type)}: {Type}, {nameof(Bind)}: {Bind}, {nameof(Visibility)}: {Visibility}, {nameof(Section)}: {Section}, {nameof(Name)}: {Name}"; - } + return $"{nameof(Value)}: 0x{Value:X16}, {nameof(Size)}: {Size:#####}, {nameof(Type)}: {Type}, {nameof(Bind)}: {Bind}, {nameof(Visibility)}: {Visibility}, {nameof(Section)}: {Section}, {nameof(Name)}: {Name}"; + } - public bool Equals(ElfSymbol other) - { - return Value == other.Value && Size == other.Size && Type == other.Type && Bind == other.Bind && Visibility == other.Visibility && Section.Equals(other.Section) && Name == other.Name; - } + public bool Equals(ElfSymbol other) + { + return Value == other.Value && Size == other.Size && Type == other.Type && Bind == other.Bind && Visibility == other.Visibility && Section.Equals(other.Section) && Name == other.Name; + } - public override bool Equals(object? obj) - { - return obj is ElfSymbol other && Equals(other); - } + public override bool Equals(object? obj) + { + return obj is ElfSymbol other && Equals(other); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = Value.GetHashCode(); - hashCode = (hashCode * 397) ^ Size.GetHashCode(); - hashCode = (hashCode * 397) ^ (int) Type; - hashCode = (hashCode * 397) ^ (int) Bind; - hashCode = (hashCode * 397) ^ (int) Visibility; - hashCode = (hashCode * 397) ^ Section.GetHashCode(); - hashCode = (hashCode * 397) ^ Name.GetHashCode(); - return hashCode; - } + var hashCode = Value.GetHashCode(); + hashCode = (hashCode * 397) ^ Size.GetHashCode(); + hashCode = (hashCode * 397) ^ (int) Type; + hashCode = (hashCode * 397) ^ (int) Bind; + hashCode = (hashCode * 397) ^ (int) Visibility; + hashCode = (hashCode * 397) ^ Section.GetHashCode(); + hashCode = (hashCode * 397) ^ Name.GetHashCode(); + return hashCode; } + } - public static bool operator ==(ElfSymbol left, ElfSymbol right) - { - return left.Equals(right); - } + public static bool operator ==(ElfSymbol left, ElfSymbol right) + { + return left.Equals(right); + } - public static bool operator !=(ElfSymbol left, ElfSymbol right) - { - return !left.Equals(right); - } + public static bool operator !=(ElfSymbol left, ElfSymbol right) + { + return !left.Equals(right); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolBind.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolBind.cs index 742af66..319604e 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolBind.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolBind.cs @@ -2,63 +2,62 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines a symbol binding +/// This is the value seen compressed in or +/// as well as the various defines (e.g ). +/// +public enum ElfSymbolBind : byte { /// - /// Defines a symbol binding - /// This is the value seen compressed in or - /// as well as the various defines (e.g ). + /// Local symbol /// - public enum ElfSymbolBind : byte - { - /// - /// Local symbol - /// - Local = ElfNative.STB_LOCAL, + Local = ElfNative.STB_LOCAL, - /// - /// Global symbol - /// - Global = ElfNative.STB_GLOBAL, + /// + /// Global symbol + /// + Global = ElfNative.STB_GLOBAL, - /// - /// Weak symbol - /// - Weak = ElfNative.STB_WEAK, + /// + /// Weak symbol + /// + Weak = ElfNative.STB_WEAK, - /// - /// Unique symbol - /// - GnuUnique = ElfNative.STB_GNU_UNIQUE, + /// + /// Unique symbol + /// + GnuUnique = ElfNative.STB_GNU_UNIQUE, - /// - /// OS-specific 0 - /// - SpecificOS0 = ElfNative.STB_GNU_UNIQUE, + /// + /// OS-specific 0 + /// + SpecificOS0 = ElfNative.STB_GNU_UNIQUE, - /// - /// OS-specific 1 - /// - SpecificOS1 = ElfNative.STB_GNU_UNIQUE + 1, + /// + /// OS-specific 1 + /// + SpecificOS1 = ElfNative.STB_GNU_UNIQUE + 1, - /// - /// OS-specific 2 - /// - SpecificOS2 = ElfNative.STB_GNU_UNIQUE + 2, + /// + /// OS-specific 2 + /// + SpecificOS2 = ElfNative.STB_GNU_UNIQUE + 2, - /// - /// Processor-specific 0 - /// - SpecificProcessor0 = ElfNative.STB_LOPROC, + /// + /// Processor-specific 0 + /// + SpecificProcessor0 = ElfNative.STB_LOPROC, - /// - /// Processor-specific 1 - /// - SpecificProcessor1 = ElfNative.STB_LOPROC + 1, + /// + /// Processor-specific 1 + /// + SpecificProcessor1 = ElfNative.STB_LOPROC + 1, - /// - /// Processor-specific 2 - /// - SpecificProcessor2 = ElfNative.STB_LOPROC + 2, - } + /// + /// Processor-specific 2 + /// + SpecificProcessor2 = ElfNative.STB_LOPROC + 2, } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs index 491db01..760ce19 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs @@ -5,282 +5,280 @@ using System; using System.Collections.Generic; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A symbol table section with the type or +/// +public sealed class ElfSymbolTable : ElfSection { - /// - /// A symbol table section with the type or - /// - public sealed class ElfSymbolTable : ElfSection - { - public const string DefaultName = ".symtab"; + public const string DefaultName = ".symtab"; - public ElfSymbolTable() : base(ElfSectionType.SymbolTable) - { - Name = DefaultName; - Entries = new List(); - Entries.Add(new ElfSymbol()); - } + public ElfSymbolTable() : base(ElfSectionType.SymbolTable) + { + Name = DefaultName; + Entries = new List(); + Entries.Add(new ElfSymbol()); + } - public override ElfSectionType Type + public override ElfSectionType Type + { + get => base.Type; + set { - get => base.Type; - set + if (value != ElfSectionType.SymbolTable && value != ElfSectionType.DynamicLinkerSymbolTable) { - if (value != ElfSectionType.SymbolTable && value != ElfSectionType.DynamicLinkerSymbolTable) - { - throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}] `{nameof(ElfSymbolTable)}`. Only `{ElfSectionType.SymbolTable}` or `{ElfSectionType.DynamicLinkerSymbolTable}` are valid"); - } - base.Type = value; + throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}] `{nameof(ElfSymbolTable)}`. Only `{ElfSectionType.SymbolTable}` or `{ElfSectionType.DynamicLinkerSymbolTable}` are valid"); } + base.Type = value; } + } - /// - /// Gets a list of entries. - /// - public List Entries { get; } + /// + /// Gets a list of entries. + /// + public List Entries { get; } - public override unsafe ulong TableEntrySize => - Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : - Parent.FileClass == ElfFileClass.Is32 ? (ulong) sizeof(ElfNative.Elf32_Sym) : (ulong) sizeof(ElfNative.Elf64_Sym); + public override unsafe ulong TableEntrySize => + Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : + Parent.FileClass == ElfFileClass.Is32 ? (ulong) sizeof(ElfNative.Elf32_Sym) : (ulong) sizeof(ElfNative.Elf64_Sym); - protected override void Read(ElfReader reader) + public override void Read(ElfReader reader) + { + if (Parent!.FileClass == ElfFileClass.Is32) { - if (Parent!.FileClass == ElfFileClass.Is32) - { - Read32(reader); - } - else - { - Read64(reader); - } + Read32(reader); } + else + { + Read64(reader); + } + } - protected override void Write(ElfWriter writer) + public override void Write(ElfWriter writer) + { + if (Parent!.FileClass == ElfFileClass.Is32) { - if (Parent!.FileClass == ElfFileClass.Is32) - { - Write32(writer); - } - else - { - Write64(writer); - } + Write32(writer); } + else + { + Write64(writer); + } + } - private void Read32(ElfReader reader) + private void Read32(ElfReader reader) + { + var numberOfEntries = base.Size / OriginalTableEntrySize; + for (ulong i = 0; i < numberOfEntries; i++) { - var numberOfEntries = base.Size / OriginalTableEntrySize; - for (ulong i = 0; i < numberOfEntries; i++) + ElfNative.Elf32_Sym sym; + ulong streamOffset = (ulong)reader.Stream.Position; + if (!reader.TryReadData((int)OriginalTableEntrySize, out sym)) { - ElfNative.Elf32_Sym sym; - ulong streamOffset = (ulong)reader.Stream.Position; - if (!reader.TryReadData((int)OriginalTableEntrySize, out sym)) - { - reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSymbolEntry32Size, $"Unable to read entirely the symbol entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); - } - - var entry = new ElfSymbol(); - entry.Name = new ElfString(reader.Decode(sym.st_name)); - entry.Value = reader.Decode(sym.st_value); - entry.Size = reader.Decode(sym.st_size); + reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSymbolEntry32Size, $"Unable to read entirely the symbol entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); + } - var st_info = sym.st_info; - entry.Type = (ElfSymbolType) (st_info & 0xF); - entry.Bind = (ElfSymbolBind)(st_info >> 4); - entry.Visibility = (ElfSymbolVisibility) sym.st_other; - entry.Section = new ElfSectionLink(reader.Decode(sym.st_shndx)); + var entry = new ElfSymbol(); + entry.Name = new ElfString(reader.Decode(sym.st_name)); + entry.Value = reader.Decode(sym.st_value); + entry.Size = reader.Decode(sym.st_size); - // If the entry 0 was validated - if (i == 0 && entry == ElfSymbol.Empty) - { - continue; - } + var st_info = sym.st_info; + entry.Type = (ElfSymbolType) (st_info & 0xF); + entry.Bind = (ElfSymbolBind)(st_info >> 4); + entry.Visibility = (ElfSymbolVisibility) sym.st_other; + entry.Section = new ElfSectionLink(reader.Decode(sym.st_shndx)); - Entries.Add(entry); + // If the entry 0 was validated + if (i == 0 && entry == ElfSymbol.Empty) + { + continue; } + + Entries.Add(entry); } + } - private void Read64(ElfReader reader) + private void Read64(ElfReader reader) + { + var numberOfEntries = base.Size / OriginalTableEntrySize; + for (ulong i = 0; i < numberOfEntries; i++) { - var numberOfEntries = base.Size / OriginalTableEntrySize; - for (ulong i = 0; i < numberOfEntries; i++) + ElfNative.Elf64_Sym sym; + ulong streamOffset = (ulong)reader.Stream.Position; + if (!reader.TryReadData((int)OriginalTableEntrySize, out sym)) { - ElfNative.Elf64_Sym sym; - ulong streamOffset = (ulong)reader.Stream.Position; - if (!reader.TryReadData((int)OriginalTableEntrySize, out sym)) - { - reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSymbolEntry64Size, $"Unable to read entirely the symbol entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); - } - - var entry = new ElfSymbol(); - entry.Name = new ElfString(reader.Decode(sym.st_name)); - entry.Value = reader.Decode(sym.st_value); - entry.Size = reader.Decode(sym.st_size); + reader.Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSymbolEntry64Size, $"Unable to read entirely the symbol entry [{i}] from {Type} section [{Index}]. Not enough data (size: {OriginalTableEntrySize}) read at offset {streamOffset} from the stream"); + } - var st_info = sym.st_info; - entry.Type = (ElfSymbolType)(st_info & 0xF); - entry.Bind = (ElfSymbolBind)(st_info >> 4); - entry.Visibility = (ElfSymbolVisibility)sym.st_other; - entry.Section = new ElfSectionLink(reader.Decode(sym.st_shndx)); + var entry = new ElfSymbol(); + entry.Name = new ElfString(reader.Decode(sym.st_name)); + entry.Value = reader.Decode(sym.st_value); + entry.Size = reader.Decode(sym.st_size); - // If the entry 0 was validated - if (i == 0 && entry == ElfSymbol.Empty) - { - continue; - } + var st_info = sym.st_info; + entry.Type = (ElfSymbolType)(st_info & 0xF); + entry.Bind = (ElfSymbolBind)(st_info >> 4); + entry.Visibility = (ElfSymbolVisibility)sym.st_other; + entry.Section = new ElfSectionLink(reader.Decode(sym.st_shndx)); - Entries.Add(entry); + // If the entry 0 was validated + if (i == 0 && entry == ElfSymbol.Empty) + { + continue; } + + Entries.Add(entry); } + } - private void Write32(ElfWriter writer) - { - var stringTable = (ElfStringTable)Link.Section!; + private void Write32(ElfWriter writer) + { + var stringTable = (ElfStringTable)Link.Section!; - // Write all entries - for (int i = 0; i < Entries.Count; i++) - { - var entry = Entries[i]; - - var sym = new ElfNative.Elf32_Sym(); - writer.Encode(out sym.st_name, (ushort)stringTable.GetOrCreateIndex(entry.Name!)); - writer.Encode(out sym.st_value, (uint)entry.Value); - writer.Encode(out sym.st_size, (uint)entry.Size); - sym.st_info = (byte)(((byte) entry.Bind << 4) | (byte) entry.Type); - sym.st_other = (byte) ((byte) entry.Visibility & 3); - var sectionIndex = entry.Section.GetIndex(); - writer.Encode(out sym.st_shndx, sectionIndex < ElfNative.SHN_LORESERVE || entry.Section.IsSpecial ? (ElfNative.Elf32_Half)sectionIndex : (ElfNative.Elf32_Half)ElfNative.SHN_XINDEX); - - writer.Write(sym); - } + // Write all entries + for (int i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + + var sym = new ElfNative.Elf32_Sym(); + writer.Encode(out sym.st_name, (ushort)stringTable.GetOrCreateIndex(entry.Name!)); + writer.Encode(out sym.st_value, (uint)entry.Value); + writer.Encode(out sym.st_size, (uint)entry.Size); + sym.st_info = (byte)(((byte) entry.Bind << 4) | (byte) entry.Type); + sym.st_other = (byte) ((byte) entry.Visibility & 3); + var sectionIndex = entry.Section.GetIndex(); + writer.Encode(out sym.st_shndx, sectionIndex < ElfNative.SHN_LORESERVE || entry.Section.IsSpecial ? (ElfNative.Elf32_Half)sectionIndex : (ElfNative.Elf32_Half)ElfNative.SHN_XINDEX); + + writer.Write(sym); } + } - private void Write64(ElfWriter writer) - { - var stringTable = (ElfStringTable)Link.Section!; + private void Write64(ElfWriter writer) + { + var stringTable = (ElfStringTable)Link.Section!; - for (int i = 0; i < Entries.Count; i++) - { - var entry = Entries[i]; - - var sym = new ElfNative.Elf64_Sym(); - writer.Encode(out sym.st_name, stringTable.GetOrCreateIndex(entry.Name!)); - writer.Encode(out sym.st_value, entry.Value); - writer.Encode(out sym.st_size, entry.Size); - sym.st_info = (byte)(((byte)entry.Bind << 4) | (byte)entry.Type); - sym.st_other = (byte)((byte)entry.Visibility & 3); - var sectionIndex = entry.Section.GetIndex(); - writer.Encode(out sym.st_shndx, sectionIndex < ElfNative.SHN_LORESERVE || entry.Section.IsSpecial ? (ElfNative.Elf64_Half)sectionIndex : (ElfNative.Elf64_Half)ElfNative.SHN_XINDEX); - - writer.Write(sym); - } + for (int i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + + var sym = new ElfNative.Elf64_Sym(); + writer.Encode(out sym.st_name, stringTable.GetOrCreateIndex(entry.Name!)); + writer.Encode(out sym.st_value, entry.Value); + writer.Encode(out sym.st_size, entry.Size); + sym.st_info = (byte)(((byte)entry.Bind << 4) | (byte)entry.Type); + sym.st_other = (byte)((byte)entry.Visibility & 3); + var sectionIndex = entry.Section.GetIndex(); + writer.Encode(out sym.st_shndx, sectionIndex < ElfNative.SHN_LORESERVE || entry.Section.IsSpecial ? (ElfNative.Elf64_Half)sectionIndex : (ElfNative.Elf64_Half)ElfNative.SHN_XINDEX); + + writer.Write(sym); } + } - protected override void AfterRead(ElfReader reader) + protected override void AfterRead(ElfReader reader) + { + // Verify that the link is safe and configured as expected + Link.TryGetSectionSafe(nameof(ElfSymbolTable), nameof(Link), this, reader.Diagnostics, out var stringTable, ElfSectionType.StringTable); + + for (int i = 0; i < Entries.Count; i++) { - // Verify that the link is safe and configured as expected - Link.TryGetSectionSafe(nameof(ElfSymbolTable), nameof(Link), this, reader.Diagnostics, out var stringTable, ElfSectionType.StringTable); + var entry = Entries[i]; - for (int i = 0; i < Entries.Count; i++) + if (stringTable != null) { - var entry = Entries[i]; - - if (stringTable != null) + if (stringTable.TryResolve(entry.Name, out var newEntry)) { - if (stringTable.TryResolve(entry.Name, out var newEntry)) - { - entry.Name = newEntry; - } - else - { - reader.Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSymbolEntryNameIndex, $"Invalid name index [{entry.Name.Index}] for symbol [{i}] in section [{this}]"); - } + entry.Name = newEntry; } - - if (entry.Section.SpecialIndex < ElfNative.SHN_LORESERVE) + else { - entry.Section = reader.ResolveLink(entry.Section, $"Invalid link section index {entry.Section.SpecialIndex} for symbol table entry [{i}] from symbol table section [{this}]"); + reader.Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSymbolEntryNameIndex, $"Invalid name index [{entry.Name.Index}] for symbol [{i}] in section [{this}]"); } + } - Entries[i] = entry; + if (entry.Section.SpecialIndex < ElfNative.SHN_LORESERVE) + { + entry.Section = reader.ResolveLink(entry.Section, $"Invalid link section index {entry.Section.SpecialIndex} for symbol table entry [{i}] from symbol table section [{this}]"); } + + Entries[i] = entry; } + } + + public override void Verify(ElfVisitorContext context) + { + var diagnostics = context.Diagnostics; - public override void Verify(DiagnosticBag diagnostics) + // Verify that the link is safe and configured as expected + if (!Link.TryGetSectionSafe(nameof(ElfSymbolTable), nameof(Link), this, diagnostics, out var stringTable, ElfSectionType.StringTable)) { - base.Verify(diagnostics); + return; + } - // Verify that the link is safe and configured as expected - if (!Link.TryGetSectionSafe(nameof(ElfSymbolTable), nameof(Link), this, diagnostics, out var stringTable, ElfSectionType.StringTable)) - { - return; - } + bool isAllowingLocal = true; + bool needsSectionHeaderIndices = false; - bool isAllowingLocal = true; - bool needsSectionHeaderIndices = false; + for (int i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; - for (int i = 0; i < Entries.Count; i++) + if (i == 0 && entry != ElfSymbol.Empty) { - var entry = Entries[i]; + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidFirstSymbolEntryNonNull, $"Invalid entry #{i} in the {nameof(ElfSymbolTable)} section [{Index}]. The first entry must be null/undefined"); + } - if (i == 0 && entry != ElfSymbol.Empty) + if (entry.Section.Section != null) + { + if (entry.Section.Section.Parent != Parent) { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidFirstSymbolEntryNonNull, $"Invalid entry #{i} in the {nameof(ElfSymbolTable)} section [{Index}]. The first entry must be null/undefined"); + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSymbolEntrySectionParent, $"Invalid section for the symbol entry #{i} in the {nameof(ElfSymbolTable)} section [{Index}]. The section of the entry `{entry}` must the same than this symbol table section"); } - if (entry.Section.Section != null) - { - if (entry.Section.Section.Parent != Parent) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSymbolEntrySectionParent, $"Invalid section for the symbol entry #{i} in the {nameof(ElfSymbolTable)} section [{Index}]. The section of the entry `{entry}` must the same than this symbol table section"); - } - - needsSectionHeaderIndices |= entry.Section.GetIndex() >= ElfNative.SHN_LORESERVE; - } + needsSectionHeaderIndices |= entry.Section.GetIndex() >= ElfNative.SHN_LORESERVE; + } - stringTable.ReserveString(entry.Name); + stringTable.ReserveString(entry.Name); - // Update the last local index - if (entry.Bind == ElfSymbolBind.Local) - { - // + 1 For the plus one - Info = new ElfSectionLink((uint)(i + 1)); - if (!isAllowingLocal) - { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSymbolEntryLocalPosition, $"Invalid position for the LOCAL symbol entry #{i} in the {nameof(ElfSymbolTable)} section [{Index}]. A LOCAL symbol entry must be before any other symbol entry"); - } - } - else + // Update the last local index + if (entry.Bind == ElfSymbolBind.Local) + { + // + 1 For the plus one + Info = new ElfSectionLink((uint)(i + 1)); + if (!isAllowingLocal) { - isAllowingLocal = false; + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSymbolEntryLocalPosition, $"Invalid position for the LOCAL symbol entry #{i} in the {nameof(ElfSymbolTable)} section [{Index}]. A LOCAL symbol entry must be before any other symbol entry"); } } + else + { + isAllowingLocal = false; + } + } - if (needsSectionHeaderIndices) + if (needsSectionHeaderIndices) + { + bool foundSectionHeaderIndices = false; + foreach (ElfSection otherSection in Parent!.Sections) { - bool foundSectionHeaderIndices = false; - foreach (ElfSection otherSection in Parent!.Sections) + if (otherSection is ElfSymbolTableSectionHeaderIndices && otherSection.Link.Section == this) { - if (otherSection is ElfSymbolTableSectionHeaderIndices && otherSection.Link.Section == this) - { - foundSectionHeaderIndices = true; - break; - } + foundSectionHeaderIndices = true; + break; } + } - if (!foundSectionHeaderIndices) - { - diagnostics.Error(DiagnosticId.ELF_ERR_MissingSectionHeaderIndices, $"Symbol table [{Name.Value}] references section indexes higher than SHN_LORESERVE and accompanying {nameof(ElfSymbolTableSectionHeaderIndices)} section is missing"); - } + if (!foundSectionHeaderIndices) + { + diagnostics.Error(DiagnosticId.ELF_ERR_MissingSectionHeaderIndices, $"Symbol table [{Name.Value}] references section indexes higher than SHN_LORESERVE and accompanying {nameof(ElfSymbolTableSectionHeaderIndices)} section is missing"); } } + } - public override unsafe void UpdateLayout(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - Size = Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : - Parent.FileClass == ElfFileClass.Is32 ? (ulong)(Entries.Count * sizeof(ElfNative.Elf32_Sym)) : (ulong)(Entries.Count * sizeof(ElfNative.Elf64_Sym)); - } + public override unsafe void UpdateLayout(ElfVisitorContext context) + { + Size = Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : + Parent.FileClass == ElfFileClass.Is32 ? (ulong)(Entries.Count * sizeof(ElfNative.Elf32_Sym)) : (ulong)(Entries.Count * sizeof(ElfNative.Elf64_Sym)); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs index 1b44a55..79af1e1 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs @@ -5,134 +5,129 @@ using System; using System.Collections.Generic; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// A section with the type +/// +public sealed class ElfSymbolTableSectionHeaderIndices : ElfSection { - /// - /// A section with the type - /// - public sealed class ElfSymbolTableSectionHeaderIndices : ElfSection - { - public const string DefaultName = ".symtab_shndx"; + public const string DefaultName = ".symtab_shndx"; - private readonly List _entries; + private readonly List _entries; - public ElfSymbolTableSectionHeaderIndices() : base(ElfSectionType.SymbolTableSectionHeaderIndices) - { - Name = DefaultName; - _entries = new List(); - } + public ElfSymbolTableSectionHeaderIndices() : base(ElfSectionType.SymbolTableSectionHeaderIndices) + { + Name = DefaultName; + _entries = new List(); + } - public override ElfSectionType Type + public override ElfSectionType Type + { + get => base.Type; + set { - get => base.Type; - set + if (value != ElfSectionType.SymbolTableSectionHeaderIndices) { - if (value != ElfSectionType.SymbolTableSectionHeaderIndices) - { - throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}] `{nameof(ElfSymbolTableSectionHeaderIndices)}`. Only `{ElfSectionType.SymbolTableSectionHeaderIndices}` is valid"); - } - base.Type = value; + throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}] `{nameof(ElfSymbolTableSectionHeaderIndices)}`. Only `{ElfSectionType.SymbolTableSectionHeaderIndices}` is valid"); } + base.Type = value; } + } - public override unsafe ulong TableEntrySize => sizeof(uint); + public override unsafe ulong TableEntrySize => sizeof(uint); - protected override void Read(ElfReader reader) + public override void Read(ElfReader reader) + { + var numberOfEntries = base.Size / TableEntrySize; + _entries.Clear(); + _entries.Capacity = (int)numberOfEntries; + for (ulong i = 0; i < numberOfEntries; i++) { - var numberOfEntries = base.Size / TableEntrySize; - _entries.Clear(); - _entries.Capacity = (int)numberOfEntries; - for (ulong i = 0; i < numberOfEntries; i++) - { - _entries.Add(reader.ReadU32()); - } + _entries.Add(reader.ReadU32()); } + } - protected override void Write(ElfWriter writer) + public override void Write(ElfWriter writer) + { + // Write all entries + for (int i = 0; i < _entries.Count; i++) { - // Write all entries - for (int i = 0; i < _entries.Count; i++) - { - writer.WriteU32(_entries[i]); - } + writer.WriteU32(_entries[i]); } + } - protected override void AfterRead(ElfReader reader) - { - // Verify that the link is safe and configured as expected - Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, reader.Diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable); - - if (symbolTable is null) - { - return; - } - - for (int i = 0; i < _entries.Count; i++) - { - var entry = _entries[i]; - if (entry != 0) - { - var resolvedLink = reader.ResolveLink(new ElfSectionLink(entry), $"Invalid link section index {entry} for symbol table entry [{i}] from symbol table section [{this}]"); + protected override void AfterRead(ElfReader reader) + { + // Verify that the link is safe and configured as expected + Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, reader.Diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable); - // Update the link in symbol table - var symbolTableEntry = symbolTable.Entries[i]; - symbolTableEntry.Section = resolvedLink; - symbolTable.Entries[i] = symbolTableEntry; - } - } + if (symbolTable is null) + { + return; } - public override void Verify(DiagnosticBag diagnostics) + for (int i = 0; i < _entries.Count; i++) { - base.Verify(diagnostics); - - // Verify that the link is safe and configured as expected - if (!Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable)) + var entry = _entries[i]; + if (entry != 0) { - return; + var resolvedLink = reader.ResolveLink(new ElfSectionLink(entry), $"Invalid link section index {entry} for symbol table entry [{i}] from symbol table section [{this}]"); + + // Update the link in symbol table + var symbolTableEntry = symbolTable.Entries[i]; + symbolTableEntry.Section = resolvedLink; + symbolTable.Entries[i] = symbolTableEntry; } } + } - public override unsafe void UpdateLayout(DiagnosticBag diagnostics) + public override void Verify(ElfVisitorContext context) + { + // Verify that the link is safe and configured as expected + if (!Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, context.Diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable)) { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + return; + } + } - // Verify that the link is safe and configured as expected - Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable); + public override void UpdateLayout(ElfVisitorContext context) + { + // Verify that the link is safe and configured as expected + Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, context.Diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable); - int numberOfEntries = 0; + int numberOfEntries = 0; - if (symbolTable is not null) + if (symbolTable is not null) + { + for (int i = 0; i < symbolTable.Entries.Count; i++) { - for (int i = 0; i < symbolTable.Entries.Count; i++) + if (symbolTable.Entries[i].Section.Section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) { - if (symbolTable.Entries[i].Section.Section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) - { - numberOfEntries = i + 1; - } + numberOfEntries = i + 1; } } + } - _entries.Capacity = numberOfEntries; - _entries.Clear(); + _entries.Capacity = numberOfEntries; + _entries.Clear(); - if (symbolTable is not null) + if (symbolTable is not null) + { + for (int i = 0; i < numberOfEntries; i++) { - for (int i = 0; i < numberOfEntries; i++) + var section = symbolTable.Entries[i].Section.Section; + if (section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) + { + _entries.Add(section.SectionIndex); + } + else { - var section = symbolTable.Entries[i].Section.Section; - if (section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) - { - _entries.Add(section.SectionIndex); - } - else - { - _entries.Add(0); - } + _entries.Add(0); } } - - Size = Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : (ulong)numberOfEntries * sizeof(uint); } + + Size = Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : (ulong)numberOfEntries * sizeof(uint); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolType.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolType.cs index df20d20..cd09772 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolType.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolType.cs @@ -2,83 +2,82 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines a symbol type. +/// This is the value seen compressed in or +/// as well as the various defines (e.g ). +/// +public enum ElfSymbolType : byte { /// - /// Defines a symbol type. - /// This is the value seen compressed in or - /// as well as the various defines (e.g ). + /// Symbol type is unspecified /// - public enum ElfSymbolType : byte - { - /// - /// Symbol type is unspecified - /// - NoType = ElfNative.STT_NOTYPE, + NoType = ElfNative.STT_NOTYPE, - /// - /// Symbol is a data object - /// - Object = ElfNative.STT_OBJECT, + /// + /// Symbol is a data object + /// + Object = ElfNative.STT_OBJECT, - /// - /// Symbol is a code object - /// - Function = ElfNative.STT_FUNC, + /// + /// Symbol is a code object + /// + Function = ElfNative.STT_FUNC, - /// - /// Symbol associated with a section - /// - Section = ElfNative.STT_SECTION, + /// + /// Symbol associated with a section + /// + Section = ElfNative.STT_SECTION, - /// - /// Symbol's name is file name - /// - File = ElfNative.STT_FILE, + /// + /// Symbol's name is file name + /// + File = ElfNative.STT_FILE, - /// - /// Symbol is a common data object - /// - Common = ElfNative.STT_COMMON, + /// + /// Symbol is a common data object + /// + Common = ElfNative.STT_COMMON, - /// - /// Symbol is thread-local data object - /// - Tls = ElfNative.STT_TLS, + /// + /// Symbol is thread-local data object + /// + Tls = ElfNative.STT_TLS, - /// - /// Symbol is indirect code object - /// - GnuIndirectFunction = ElfNative.STT_GNU_IFUNC, + /// + /// Symbol is indirect code object + /// + GnuIndirectFunction = ElfNative.STT_GNU_IFUNC, - /// - /// OS-specific 0 - /// - SpecificOS0 = ElfNative.STT_GNU_IFUNC, + /// + /// OS-specific 0 + /// + SpecificOS0 = ElfNative.STT_GNU_IFUNC, - /// - /// OS-specific 1 - /// - SpecificOS1 = ElfNative.STT_GNU_IFUNC + 1, + /// + /// OS-specific 1 + /// + SpecificOS1 = ElfNative.STT_GNU_IFUNC + 1, - /// - /// OS-specific 2 - /// - SpecificOS2 = ElfNative.STT_GNU_IFUNC + 2, + /// + /// OS-specific 2 + /// + SpecificOS2 = ElfNative.STT_GNU_IFUNC + 2, - /// - /// Processor-specific 0 - /// - SpecificProcessor0 = ElfNative.STT_LOPROC, + /// + /// Processor-specific 0 + /// + SpecificProcessor0 = ElfNative.STT_LOPROC, - /// - /// Processor-specific 1 - /// - SpecificProcessor1 = ElfNative.STT_LOPROC + 1, + /// + /// Processor-specific 1 + /// + SpecificProcessor1 = ElfNative.STT_LOPROC + 1, - /// - /// Processor-specific 2 - /// - SpecificProcessor2 = ElfNative.STT_LOPROC + 2, - } + /// + /// Processor-specific 2 + /// + SpecificProcessor2 = ElfNative.STT_LOPROC + 2, } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolVisibility.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolVisibility.cs index 08d3725..0830e1e 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolVisibility.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolVisibility.cs @@ -2,33 +2,32 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines the visibility of a symbol +/// This is the value seen compressed in or +/// as well as the various defines (e.g ). +/// +public enum ElfSymbolVisibility : byte { /// - /// Defines the visibility of a symbol - /// This is the value seen compressed in or - /// as well as the various defines (e.g ). + /// Default symbol visibility rules /// - public enum ElfSymbolVisibility : byte - { - /// - /// Default symbol visibility rules - /// - Default = ElfNative.STV_DEFAULT, + Default = ElfNative.STV_DEFAULT, - /// - /// Processor specific hidden class - /// - Internal = ElfNative.STV_INTERNAL, + /// + /// Processor specific hidden class + /// + Internal = ElfNative.STV_INTERNAL, - /// - /// Sym unavailable in other modules - /// - Hidden = ElfNative.STV_HIDDEN, + /// + /// Sym unavailable in other modules + /// + Hidden = ElfNative.STV_HIDDEN, - /// - /// Not preemptible, not exported - /// - Protected = ElfNative.STV_PROTECTED, - } + /// + /// Not preemptible, not exported + /// + Protected = ElfNative.STV_PROTECTED, } \ No newline at end of file diff --git a/src/LibObjectFile/IObjectFileNodeLink.cs b/src/LibObjectFile/IObjectFileNodeLink.cs index 570138e..980a844 100644 --- a/src/LibObjectFile/IObjectFileNodeLink.cs +++ b/src/LibObjectFile/IObjectFileNodeLink.cs @@ -2,12 +2,11 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile +namespace LibObjectFile; + +public interface IObjectFileNodeLink { - public interface IObjectFileNodeLink - { - ulong GetRelativeOffset(); + ulong GetRelativeOffset(); - ObjectFileNodeBase GetObjectFileNode(); - } + ObjectFileElement GetObjectFileNode(); } \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileNodeBase.cs b/src/LibObjectFile/ObjectFileElement.cs similarity index 65% rename from src/LibObjectFile/ObjectFileNodeBase.cs rename to src/LibObjectFile/ObjectFileElement.cs index c2a5eb1..91f9a7d 100644 --- a/src/LibObjectFile/ObjectFileNodeBase.cs +++ b/src/LibObjectFile/ObjectFileElement.cs @@ -2,22 +2,22 @@ // 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.Diagnostics.CodeAnalysis; +using System; +using System.Text; namespace LibObjectFile; -public abstract class ObjectFileNodeBase +public abstract class ObjectFileElement { [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private ObjectFileNodeBase? _parent; + private ObjectFileElement? _parent; - protected ObjectFileNodeBase() + protected ObjectFileElement() { Index = -1; } - + /// /// Gets or sets the position of this element relative to the top level parent. /// @@ -26,7 +26,7 @@ protected ObjectFileNodeBase() /// /// Gets the containing parent. /// - public ObjectFileNodeBase? Parent + public ObjectFileElement? Parent { get => _parent; @@ -45,7 +45,7 @@ internal set } } - protected virtual void ValidateParent(ObjectFileNodeBase parent) + protected virtual void ValidateParent(ObjectFileElement parent) { } @@ -77,35 +77,38 @@ public bool Contains(ulong offset) /// /// Checks this instance contains either the beginning or the end of the specified section or segment. /// - /// The specified section or segment. + /// The specified section or segment. /// true if the either the offset or end of the part is within this segment or section range. - public bool Contains(ObjectFileNodeBase node) + public bool Contains(ObjectFileElement element) + { + ArgumentNullException.ThrowIfNull(element); + return Contains((ulong)element.Position) || element.Size != 0 && Contains((ulong)(element.Position + element.Size - 1)); + } + + public sealed override string ToString() { - ArgumentNullException.ThrowIfNull(node); - return Contains((ulong)node.Position) || node.Size != 0 && Contains((ulong)(node.Position + node.Size - 1)); + var builder = new StringBuilder(); + PrintName(builder); + builder.Append(" { "); + if (PrintMembers(builder)) + { + builder.Append(' '); + } + builder.Append('}'); + return builder.ToString(); } - /// - /// Verifies the integrity of this file. - /// - /// The result of the diagnostics - public DiagnosticBag Verify() + protected virtual void PrintName(StringBuilder builder) { - var diagnostics = new DiagnosticBag(); - Verify(diagnostics); - return diagnostics; + builder.Append(GetType().Name); } - /// - /// Verifies the integrity of this file. - /// - /// A DiagnosticBag instance to receive the diagnostics. - public virtual void Verify(DiagnosticBag diagnostics) + protected virtual bool PrintMembers(StringBuilder builder) { - ArgumentNullException.ThrowIfNull(diagnostics); + return false; } - protected static void AssignChild(TParent parent, T child, out T field) where T : ObjectFileNodeBase where TParent : ObjectFileNodeBase + protected static void AssignChild(TParent parent, T child, out T field) where T : ObjectFileElement where TParent : ObjectFileElement { if (parent == null) throw new ArgumentNullException(nameof(parent)); if (child == null) throw new ArgumentNullException(nameof(child)); @@ -118,8 +121,8 @@ protected static void AssignChild(TParent parent, T child, out T fie child.Parent = parent; } } - - protected static void AttachChild(TParent parent, T child, ref T field) where T : ObjectFileNodeBase where TParent : ObjectFileNodeBase + + protected static void AttachChild(TParent parent, T child, ref T field) where T : ObjectFileElement where TParent : ObjectFileElement { if (parent == null) throw new ArgumentNullException(nameof(parent)); field.Parent = null; @@ -133,7 +136,7 @@ protected static void AttachChild(TParent parent, T child, ref T fie } } - protected static void AttachNullableChild(TParent parent, T? child, ref T? field) where T : ObjectFileNodeBase where TParent : ObjectFileNodeBase + protected static void AttachNullableChild(TParent parent, T? child, ref T? field) where T : ObjectFileElement where TParent : ObjectFileElement { if (parent == null) throw new ArgumentNullException(nameof(parent)); @@ -151,4 +154,27 @@ protected static void AttachNullableChild(TParent parent, T? child, } } -} \ No newline at end of file +} + +public abstract class ObjectFileElement : ObjectFileElement + where TLayoutContext : VisitorContextBase + where TVerifyContext : VisitorContextBase + where TReader : ObjectFileReaderWriter + where TWriter : ObjectFileReaderWriter +{ + public virtual void UpdateLayout(TLayoutContext layoutContext) + { + } + + public virtual void Verify(TVerifyContext diagnostics) + { + } + + public virtual void Read(TReader reader) + { + } + + public virtual void Write(TWriter writer) + { + } +} diff --git a/src/LibObjectFile/ObjectFileException.cs b/src/LibObjectFile/ObjectFileException.cs index 10f0b58..4bdd2bd 100644 --- a/src/LibObjectFile/ObjectFileException.cs +++ b/src/LibObjectFile/ObjectFileException.cs @@ -4,24 +4,23 @@ using System; -namespace LibObjectFile +namespace LibObjectFile; + +/// +/// An exception used when diagnostics error are happening during read/write. +/// +public class ObjectFileException : Exception { - /// - /// An exception used when diagnostics error are happening during read/write. - /// - public class ObjectFileException : Exception + public ObjectFileException(string message, DiagnosticBag diagnostics) : base(message) { - public ObjectFileException(string message, DiagnosticBag diagnostics) : base(message) - { - Diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics)); - } + Diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics)); + } - public override string Message => base.Message + Environment.NewLine + Diagnostics; + public override string Message => base.Message + Environment.NewLine + Diagnostics; - /// - /// The associated diagnostics messages. - /// - public DiagnosticBag Diagnostics { get; } - } + /// + /// The associated diagnostics messages. + /// + public DiagnosticBag Diagnostics { get; } } \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileExtensions.cs b/src/LibObjectFile/ObjectFileExtensions.cs index 2378333..a1bd817 100644 --- a/src/LibObjectFile/ObjectFileExtensions.cs +++ b/src/LibObjectFile/ObjectFileExtensions.cs @@ -5,137 +5,136 @@ using System; using System.Collections.Generic; -namespace LibObjectFile +namespace LibObjectFile; + +public static class ObjectFileExtensions { - public static class ObjectFileExtensions + /// + /// Adds an attribute to . + /// + /// A attribute + public static void Add(this List list, TParent parent, TChild element) where TChild : ObjectFileElement where TParent: ObjectFileElement { - /// - /// Adds an attribute to . - /// - /// A attribute - public static void Add(this List list, TParent parent, TChild element) where TChild : ObjectFileNodeBase where TParent: ObjectFileNodeBase + if (element == null) throw new ArgumentNullException(nameof(element)); + if (element.Parent != null) { - if (element == null) throw new ArgumentNullException(nameof(element)); - if (element.Parent != null) - { - if (element.Parent == parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added"); - if (element.Parent != parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added to another {parent.GetType()} instance"); - } + if (element.Parent == parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added"); + if (element.Parent != parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added to another {parent.GetType()} instance"); + } + + element.Parent = parent; + element.Index = list.Count; + list.Add(element); + } + /// + /// Adds an element to the sorted list + /// + /// An element to add + public static void AddSorted(this List list, TParent parent, TChild element, bool requiresUnique = false) where TChild : ObjectFileElement, IComparable where TParent : ObjectFileElement + { + if (element == null) throw new ArgumentNullException(nameof(element)); + if (element.Parent != null) + { + if (element.Parent == parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added"); + if (element.Parent != parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added to another {parent.GetType()} instance"); + } + + int index; + + // Optimistic case, we add in order + if (list.Count == 0 || list[^1].CompareTo(element) < 0) + { element.Parent = parent; - element.Index = list.Count; + index = list.Count; list.Add(element); } - - /// - /// Adds an element to the sorted list - /// - /// An element to add - public static void AddSorted(this List list, TParent parent, TChild element, bool requiresUnique = false) where TChild : ObjectFileNodeBase, IComparable where TParent : ObjectFileNodeBase + else { - if (element == null) throw new ArgumentNullException(nameof(element)); - if (element.Parent != null) + index = list.BinarySearch(element); + if (index < 0) + index = ~index; + else if (requiresUnique && list[index].CompareTo(element) == 0) { - if (element.Parent == parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added"); - if (element.Parent != parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added to another {parent.GetType()} instance"); + throw new InvalidOperationException($"A similar element to `{element}` has been already added to this collection at index {index}"); } - int index; - - // Optimistic case, we add in order - if (list.Count == 0 || list[^1].CompareTo(element) < 0) - { - element.Parent = parent; - index = list.Count; - list.Add(element); - } - else - { - index = list.BinarySearch(element); - if (index < 0) - index = ~index; - else if (requiresUnique && list[index].CompareTo(element) == 0) - { - throw new InvalidOperationException($"A similar element to `{element}` has been already added to this collection at index {index}"); - } - - element.Parent = parent; - list.Insert(index, element); - } + element.Parent = parent; + list.Insert(index, element); + } - element.Index = index; + element.Index = index; - // Update the index of following attributes - for (int i = index + 1; i < list.Count; i++) - { - var nextAttribute = list[i]; - nextAttribute.Index++; - } + // Update the index of following attributes + for (int i = index + 1; i < list.Count; i++) + { + var nextAttribute = list[i]; + nextAttribute.Index++; } + } - /// - /// Inserts an attribute into at the specified index. - /// - /// Index into to insert the specified attribute - /// The attribute to insert - public static void InsertAt(this List list, TParent parent, int index, TChild element) where TChild : ObjectFileNodeBase where TParent : ObjectFileNodeBase + /// + /// Inserts an attribute into at the specified index. + /// + /// Index into to insert the specified attribute + /// The attribute to insert + public static void InsertAt(this List list, TParent parent, int index, TChild element) where TChild : ObjectFileElement where TParent : ObjectFileElement + { + if (index < 0 || index > list.Count) throw new ArgumentOutOfRangeException(nameof(index), $"Invalid index {index}, Must be >= 0 && <= {list.Count}"); + if (element == null) throw new ArgumentNullException(nameof(element)); + if (element.Parent != null) { - if (index < 0 || index > list.Count) throw new ArgumentOutOfRangeException(nameof(index), $"Invalid index {index}, Must be >= 0 && <= {list.Count}"); - if (element == null) throw new ArgumentNullException(nameof(element)); - if (element.Parent != null) - { - if (element.Parent == parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added"); - if (element.Parent != parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added to another {parent.GetType()} instance"); - } + if (element.Parent == parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added"); + if (element.Parent != parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added to another {parent.GetType()} instance"); + } - element.Index = index; - list.Insert(index, element); - element.Parent = parent; + element.Index = index; + list.Insert(index, element); + element.Parent = parent; - // Update the index of following attributes - for (int i = index + 1; i < list.Count; i++) - { - var nextAttribute = list[i]; - nextAttribute.Index++; - } + // Update the index of following attributes + for (int i = index + 1; i < list.Count; i++) + { + var nextAttribute = list[i]; + nextAttribute.Index++; } + } - /// - /// Removes an attribute from - /// - /// The attribute to remove - public static void Remove(this List list, TParent parent, TChild child) where TChild : ObjectFileNodeBase where TParent : ObjectFileNodeBase + /// + /// Removes an attribute from + /// + /// The attribute to remove + public static void Remove(this List list, TParent parent, TChild child) where TChild : ObjectFileElement where TParent : ObjectFileElement + { + if (child == null) throw new ArgumentNullException(nameof(child)); + if (!ReferenceEquals(child.Parent, parent)) { - if (child == null) throw new ArgumentNullException(nameof(child)); - if (!ReferenceEquals(child.Parent, parent)) - { - throw new InvalidOperationException($"Cannot remove the {nameof(TChild)} as it is not part of this {parent.GetType()} instance"); - } - - var i = (int)child.Index; - list.RemoveAt(i); - child.Index = 0; - - // Update indices for other sections - for (int j = i + 1; j < list.Count; j++) - { - var nextEntry = list[j]; - nextEntry.Index--; - } - - child.Parent = null; + throw new InvalidOperationException($"Cannot remove the {nameof(TChild)} as it is not part of this {parent.GetType()} instance"); } - /// - /// Removes an attribute from at the specified index. - /// - /// Index into to remove the specified attribute - public static TChild RemoveAt(this List list, TParent parent, int index) where TChild : ObjectFileNodeBase where TParent : ObjectFileNodeBase + var i = (int)child.Index; + list.RemoveAt(i); + child.Index = 0; + + // Update indices for other sections + for (int j = i + 1; j < list.Count; j++) { - if (index < 0 || index > list.Count) throw new ArgumentOutOfRangeException(nameof(index), $"Invalid index {index}, Must be >= 0 && <= {list.Count}"); - var child = list[index]; - Remove(list, parent, child); - return child; + var nextEntry = list[j]; + nextEntry.Index--; } + + child.Parent = null; + } + + /// + /// Removes an attribute from at the specified index. + /// + /// Index into to remove the specified attribute + public static TChild RemoveAt(this List list, TParent parent, int index) where TChild : ObjectFileElement where TParent : ObjectFileElement + { + if (index < 0 || index > list.Count) throw new ArgumentOutOfRangeException(nameof(index), $"Invalid index {index}, Must be >= 0 && <= {list.Count}"); + var child = list[index]; + Remove(list, parent, child); + return child; } } \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileNode.cs b/src/LibObjectFile/ObjectFileNode.cs deleted file mode 100644 index e309749..0000000 --- a/src/LibObjectFile/ObjectFileNode.cs +++ /dev/null @@ -1,17 +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; - -/// -/// Defines a node in an object file with its layout that can be updated. -/// -public abstract class ObjectFileNode : ObjectFileNodeBase -{ - /// - /// Updates the size of this node. - /// - /// The diagnostics. - public abstract void UpdateLayout(DiagnosticBag diagnostics); -} \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs index 9ececa6..91cb10f 100644 --- a/src/LibObjectFile/ObjectFileReaderWriter.cs +++ b/src/LibObjectFile/ObjectFileReaderWriter.cs @@ -10,327 +10,320 @@ using System.Text; using LibObjectFile.Utils; -namespace LibObjectFile -{ - /// - /// Base class used for reading / writing an object file to/from a stream. - /// - public abstract class ObjectFileReaderWriter - { - private Stream _stream; +namespace LibObjectFile; - protected ObjectFileReaderWriter(Stream stream) : this(stream, new DiagnosticBag()) - { - } +/// +/// Base class used for reading / writing an object file to/from a stream. +/// +public abstract class ObjectFileReaderWriter : VisitorContextBase +{ + private Stream _stream; - protected ObjectFileReaderWriter(Stream stream, DiagnosticBag diagnostics) - { - _stream = stream; - Diagnostics = diagnostics; - IsLittleEndian = true; - } + protected ObjectFileReaderWriter(ObjectFileElement file, Stream stream) : this(file, stream, new DiagnosticBag()) + { + } - /// - /// Gets or sets stream of the object file. - /// - public Stream Stream - { - get => _stream; - set => _stream = value; - } + protected ObjectFileReaderWriter(ObjectFileElement file, Stream stream, DiagnosticBag diagnostics) : base(file, diagnostics) + { + _stream = stream; + IsLittleEndian = true; + } - public ulong Position - { - get => (ulong) Stream.Position; - set => Stream.Seek((long)value, SeekOrigin.Begin); - } + /// + /// Gets or sets stream of the object file. + /// + public Stream Stream + { + get => _stream; + set => _stream = value; + } - public ulong Length - { - get => (ulong) Stream.Length; - } + public ulong Position + { + get => (ulong) Stream.Position; + set => Stream.Seek((long)value, SeekOrigin.Begin); + } - /// - /// The diagnostics while read/writing this object file. - /// - public DiagnosticBag Diagnostics { get; protected set; } + public ulong Length + { + get => (ulong) Stream.Length; + } - /// - /// Gets a boolean indicating if this reader is operating in read-only mode. - /// - public abstract bool IsReadOnly { get; } + /// + /// Gets a boolean indicating if this reader is operating in read-only mode. + /// + public abstract bool IsReadOnly { get; } - public bool IsLittleEndian { get; protected set; } + public bool IsLittleEndian { get; protected set; } - public TextWriter? Log { get; set; } + public TextWriter? Log { get; set; } - /// - /// Reads from the and current position to the specified buffer. - /// - /// The buffer to receive the content of the read. - /// The offset into the buffer. - /// The number of bytes to write from the buffer. - public int Read(byte[] buffer, int offset, int count) => Stream.Read(buffer, offset, count); + /// + /// Reads from the and current position to the specified buffer. + /// + /// The buffer to receive the content of the read. + /// The offset into the buffer. + /// The number of bytes to write from the buffer. + public int Read(byte[] buffer, int offset, int count) => Stream.Read(buffer, offset, count); - public int Read(Span buffer) => Stream.Read(buffer); + public int Read(Span buffer) => Stream.Read(buffer); - public void ReadExactly(Span buffer) => Stream.ReadExactly(buffer); + public void ReadExactly(Span buffer) => Stream.ReadExactly(buffer); - /// - /// Reads a null terminated UTF8 string from the stream. - /// - /// true if the string was successfully read from the stream, false otherwise - public string ReadStringUTF8NullTerminated() - { - return Stream.ReadStringUTF8NullTerminated(); - } + /// + /// Reads a null terminated UTF8 string from the stream. + /// + /// true if the string was successfully read from the stream, false otherwise + public string ReadStringUTF8NullTerminated() + { + return Stream.ReadStringUTF8NullTerminated(); + } - /// - /// Reads a null terminated UTF8 string from the stream. - /// - /// The number of bytes to read including the null - /// A string - public string ReadStringUTF8NullTerminated(uint byteLength) - { - return Stream.ReadStringUTF8NullTerminated(byteLength); - } + /// + /// Reads a null terminated UTF8 string from the stream. + /// + /// The number of bytes to read including the null + /// A string + public string ReadStringUTF8NullTerminated(uint byteLength) + { + return Stream.ReadStringUTF8NullTerminated(byteLength); + } - public byte ReadU8() - { - return Stream.ReadU8(); - } + public byte ReadU8() + { + return Stream.ReadU8(); + } - public sbyte ReadI8() - { - return Stream.ReadI8(); - } + public sbyte ReadI8() + { + return Stream.ReadI8(); + } - public short ReadI16() - { - return Stream.ReadI16(IsLittleEndian); - } + public short ReadI16() + { + return Stream.ReadI16(IsLittleEndian); + } - public ushort ReadU16() - { - return Stream.ReadU16(IsLittleEndian); - } + public ushort ReadU16() + { + return Stream.ReadU16(IsLittleEndian); + } - public int ReadI32() - { - return Stream.ReadI32(IsLittleEndian); - } + public int ReadI32() + { + return Stream.ReadI32(IsLittleEndian); + } - public uint ReadU32() - { - return Stream.ReadU32(IsLittleEndian); - } + public uint ReadU32() + { + return Stream.ReadU32(IsLittleEndian); + } - public long ReadI64() - { - return Stream.ReadI64(IsLittleEndian); - } + public long ReadI64() + { + return Stream.ReadI64(IsLittleEndian); + } - public ulong ReadU64() - { - return Stream.ReadU64(IsLittleEndian); - } + public ulong ReadU64() + { + return Stream.ReadU64(IsLittleEndian); + } - public void WriteI8(sbyte value) - { - Stream.WriteI8(value); - } + public void WriteI8(sbyte value) + { + Stream.WriteI8(value); + } - public void WriteU8(byte value) - { - Stream.WriteU8(value); - } + public void WriteU8(byte value) + { + Stream.WriteU8(value); + } - public void WriteU16(ushort value) - { - Stream.WriteU16(IsLittleEndian, value); - } + public void WriteU16(ushort value) + { + Stream.WriteU16(IsLittleEndian, value); + } - public void WriteU32(uint value) - { - Stream.WriteU32(IsLittleEndian, value); - } + public void WriteU32(uint value) + { + Stream.WriteU32(IsLittleEndian, value); + } - public void WriteU64(ulong value) - { - Stream.WriteU64(IsLittleEndian, value); - } + public void WriteU64(ulong value) + { + Stream.WriteU64(IsLittleEndian, value); + } - /// - /// Writes a null terminated UTF8 string to the stream. - /// - public void WriteStringUTF8NullTerminated(string text) - { - Stream.WriteStringUTF8NullTerminated(text); - } + /// + /// Writes a null terminated UTF8 string to the stream. + /// + public void WriteStringUTF8NullTerminated(string text) + { + Stream.WriteStringUTF8NullTerminated(text); + } - /// - /// Tries to read an element of type with a specified size. - /// - /// Type of the element to read. - /// Size of the element to read (might be smaller or bigger). - /// The data read. - /// true if reading was successful. false otherwise. - public unsafe bool TryReadData(int sizeToRead, out T data) where T : unmanaged - { - if (sizeToRead <= 0) throw new ArgumentOutOfRangeException(nameof(sizeToRead)); + /// + /// Tries to read an element of type with a specified size. + /// + /// Type of the element to read. + /// Size of the element to read (might be smaller or bigger). + /// The data read. + /// true if reading was successful. false otherwise. + public unsafe bool TryReadData(int sizeToRead, out T data) where T : unmanaged + { + if (sizeToRead <= 0) throw new ArgumentOutOfRangeException(nameof(sizeToRead)); - int dataByteCount = sizeof(T); - int byteRead; + int dataByteCount = sizeof(T); + int byteRead; - // If we are requested to read more data than the sizeof(T) - // we need to read it to an intermediate buffer before transferring it to T data - if (sizeToRead > dataByteCount) - { - var buffer = ArrayPool.Shared.Rent(sizeToRead); - var span = new Span(buffer, 0, sizeToRead); - byteRead = Stream.Read(span); - data = MemoryMarshal.Cast(span)[0]; - ArrayPool.Shared.Return(buffer); - } - else - { - // Clear the data if the size requested is less than the expected struct to read - if (sizeToRead < dataByteCount) - { - data = default; - } - - fixed (void* pData = &data) - { - var span = new Span(pData, sizeToRead); - byteRead = Stream.Read(span); - } - } - return byteRead == sizeToRead; + // If we are requested to read more data than the sizeof(T) + // we need to read it to an intermediate buffer before transferring it to T data + if (sizeToRead > dataByteCount) + { + var buffer = ArrayPool.Shared.Rent(sizeToRead); + var span = new Span(buffer, 0, sizeToRead); + byteRead = Stream.Read(span); + data = MemoryMarshal.Cast(span)[0]; + ArrayPool.Shared.Return(buffer); } - - /// - /// Reads from the current bytes and return the data as - /// a if is false otherwise as a - /// . - /// - /// Size of the data to read. - /// A if is false otherwise as a - /// . - public Stream ReadAsStream(ulong size) + else { - if (IsReadOnly) + // Clear the data if the size requested is less than the expected struct to read + if (sizeToRead < dataByteCount) { - var stream = ReadAsSubStream(size); - Stream.Seek(stream.Length, SeekOrigin.Current); - return stream; + data = default; } - return ReadAsMemoryStream(size); + fixed (void* pData = &data) + { + var span = new Span(pData, sizeToRead); + byteRead = Stream.Read(span); + } } + return byteRead == sizeToRead; + } - /// - /// Writes to the and current position from the specified buffer. - /// - /// The buffer to write the content from. - /// The offset into the buffer. - /// The number of bytes to read from the buffer and write to the stream. - public void Write(byte[] buffer, int offset, int count) + /// + /// Reads from the current bytes and return the data as + /// a if is false otherwise as a + /// . + /// + /// Size of the data to read. + /// A if is false otherwise as a + /// . + public Stream ReadAsStream(ulong size) + { + if (IsReadOnly) { - Stream.Write(buffer, offset, count); + var stream = ReadAsSubStream(size); + Stream.Seek(stream.Length, SeekOrigin.Current); + return stream; } - /// - /// Writes to the and current position from the specified buffer. - /// - /// The buffer to write - public void Write(ReadOnlySpan buffer) => Stream.Write(buffer); - - /// - /// Writes an element of type to the stream. - /// - /// Type of the element to read. - /// The data to write. - public unsafe void Write(in T data) where T : unmanaged + return ReadAsMemoryStream(size); + } + + /// + /// Writes to the and current position from the specified buffer. + /// + /// The buffer to write the content from. + /// The offset into the buffer. + /// The number of bytes to read from the buffer and write to the stream. + public void Write(byte[] buffer, int offset, int count) + { + Stream.Write(buffer, offset, count); + } + + /// + /// Writes to the and current position from the specified buffer. + /// + /// The buffer to write + public void Write(ReadOnlySpan buffer) => Stream.Write(buffer); + + /// + /// Writes an element of type to the stream. + /// + /// Type of the element to read. + /// The data to write. + public unsafe void Write(in T data) where T : unmanaged + { + fixed (void* pData = &data) { - fixed (void* pData = &data) - { - var span = new ReadOnlySpan(pData, sizeof(T)); - Stream.Write(span); - } + var span = new ReadOnlySpan(pData, sizeof(T)); + Stream.Write(span); } + } - /// - /// Writes from the specified stream to the current of this instance. - /// The position of the input stream is set to 0 before writing and reset back to 0 after writing. - /// - /// The input stream to read from and write to - /// The amount of data to read from the input stream (if == 0, by default, it will read the entire input stream) - /// The size of the intermediate buffer used to transfer the data. - public void Write(Stream inputStream, ulong size = 0, int bufferSize = 4096) - { - if (inputStream == null) throw new ArgumentNullException(nameof(inputStream)); - if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize)); + /// + /// Writes from the specified stream to the current of this instance. + /// The position of the input stream is set to 0 before writing and reset back to 0 after writing. + /// + /// The input stream to read from and write to + /// The amount of data to read from the input stream (if == 0, by default, it will read the entire input stream) + /// The size of the intermediate buffer used to transfer the data. + public void Write(Stream inputStream, ulong size = 0, int bufferSize = 4096) + { + if (inputStream == null) throw new ArgumentNullException(nameof(inputStream)); + if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize)); - inputStream.Seek(0, SeekOrigin.Begin); - size = size == 0 ? (ulong)inputStream.Length : size; - var buffer = ArrayPool.Shared.Rent(bufferSize); + inputStream.Seek(0, SeekOrigin.Begin); + size = size == 0 ? (ulong)inputStream.Length : size; + var buffer = ArrayPool.Shared.Rent(bufferSize); - while (size != 0) - { - var sizeToRead = size >= (ulong)buffer.Length ? buffer.Length : (int)size; - var sizeRead = inputStream.Read(buffer, 0, sizeToRead); - if (sizeRead <= 0) break; + while (size != 0) + { + var sizeToRead = size >= (ulong)buffer.Length ? buffer.Length : (int)size; + var sizeRead = inputStream.Read(buffer, 0, sizeToRead); + if (sizeRead <= 0) break; - Stream.Write(buffer, 0, sizeRead); - size -= (ulong)sizeRead; - } + Stream.Write(buffer, 0, sizeRead); + size -= (ulong)sizeRead; + } - inputStream.Seek(0, SeekOrigin.Begin); - if (size != 0) - { - throw new InvalidOperationException("Unable to write stream entirely"); - } + inputStream.Seek(0, SeekOrigin.Begin); + if (size != 0) + { + throw new InvalidOperationException("Unable to write stream entirely"); } + } - private SubStream ReadAsSubStream(ulong size) + private SubStream ReadAsSubStream(ulong size) + { + var position = Stream.Position; + if (position + (long)size > Stream.Length) { - var position = Stream.Position; - if (position + (long)size > Stream.Length) + if (position < Stream.Length) { - if (position < Stream.Length) - { - size = Stream.Position < Stream.Length ? (ulong)(Stream.Length - Stream.Position) : 0; - Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Expecting to slice {size} bytes at offset {position} while remaining length is {size}"); - } - else - { - position = Stream.Length; - size = 0; - Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Position of slice {position} is outside of the stream length {Stream.Length} in bytes"); - } + size = Stream.Position < Stream.Length ? (ulong)(Stream.Length - Stream.Position) : 0; + Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Expecting to slice {size} bytes at offset {position} while remaining length is {size}"); + } + else + { + position = Stream.Length; + size = 0; + Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Position of slice {position} is outside of the stream length {Stream.Length} in bytes"); } - - return new SubStream(Stream, position, (long)size); } - private MemoryStream ReadAsMemoryStream(ulong size) - { - var memoryStream = new MemoryStream((int)size); - if (size == 0) return memoryStream; + return new SubStream(Stream, position, (long)size); + } - memoryStream.SetLength((long)size); + private MemoryStream ReadAsMemoryStream(ulong size) + { + var memoryStream = new MemoryStream((int)size); + if (size == 0) return memoryStream; - var buffer = memoryStream.GetBuffer(); - var span = new Span(buffer, 0, (int)size); - var readSize = Stream.Read(span); + memoryStream.SetLength((long)size); - if ((int)size != readSize) - { - Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Expecting to read {size} bytes at offset {Stream.Position}"); - } - - return memoryStream; + var buffer = memoryStream.GetBuffer(); + var span = new Span(buffer, 0, (int)size); + var readSize = Stream.Read(span); + + if ((int)size != readSize) + { + Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Expecting to read {size} bytes at offset {Stream.Position}"); } + + return memoryStream; } } \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileStreamExtensions.cs b/src/LibObjectFile/ObjectFileStreamExtensions.cs index 027abd2..220887d 100644 --- a/src/LibObjectFile/ObjectFileStreamExtensions.cs +++ b/src/LibObjectFile/ObjectFileStreamExtensions.cs @@ -9,226 +9,225 @@ using System.Text; using LibObjectFile.Utils; -namespace LibObjectFile +namespace LibObjectFile; + +public static class ObjectFileStreamExtensions { - public static class ObjectFileStreamExtensions + public static byte ReadU8(this Stream stream) { - public static byte ReadU8(this Stream stream) - { - int nextValue = stream.ReadByte(); - if (nextValue < 0) throw new EndOfStreamException(); - return (byte)nextValue; - } + int nextValue = stream.ReadByte(); + if (nextValue < 0) throw new EndOfStreamException(); + return (byte)nextValue; + } - public static void WriteU8(this Stream stream, byte value) - { - stream.WriteByte(value); - } + public static void WriteU8(this Stream stream, byte value) + { + stream.WriteByte(value); + } - public static sbyte ReadI8(this Stream stream) - { - int nextValue = stream.ReadByte(); - if (nextValue < 0) throw new EndOfStreamException(); - return (sbyte)nextValue; - } + public static sbyte ReadI8(this Stream stream) + { + int nextValue = stream.ReadByte(); + if (nextValue < 0) throw new EndOfStreamException(); + return (sbyte)nextValue; + } - public static void WriteI8(this Stream stream, sbyte value) - { - stream.WriteByte((byte)value); - } + public static void WriteI8(this Stream stream, sbyte value) + { + stream.WriteByte((byte)value); + } - /// - /// Reads a null terminated UTF8 string from the stream. - /// - /// true if the string was successfully read from the stream, false otherwise - public static string ReadStringUTF8NullTerminated(this Stream stream) + /// + /// Reads a null terminated UTF8 string from the stream. + /// + /// true if the string was successfully read from the stream, false otherwise + public static string ReadStringUTF8NullTerminated(this Stream stream) + { + var buffer = ArrayPool.Shared.Rent((int)128); + int textLength = 0; + try { - var buffer = ArrayPool.Shared.Rent((int)128); - int textLength = 0; - try + while (true) { - while (true) + int nextByte = stream.ReadByte(); + if (nextByte < 0) { - int nextByte = stream.ReadByte(); - if (nextByte < 0) - { - throw new EndOfStreamException("Unexpected end of stream while trying to read a null terminated UTF8 string"); - } - - if (nextByte == 0) - { - break; - } - - if (textLength >= buffer.Length) - { - var newBuffer = ArrayPool.Shared.Rent((int)textLength * 2); - Array.Copy(buffer, 0, newBuffer, 0, buffer.Length); - ArrayPool.Shared.Return(buffer); - buffer = newBuffer; - } - - buffer[textLength++] = (byte)nextByte; + throw new EndOfStreamException("Unexpected end of stream while trying to read a null terminated UTF8 string"); } - return Encoding.UTF8.GetString(buffer, 0, textLength); - } - finally - { - ArrayPool.Shared.Return(buffer); + if (nextByte == 0) + { + break; + } + + if (textLength >= buffer.Length) + { + var newBuffer = ArrayPool.Shared.Rent((int)textLength * 2); + Array.Copy(buffer, 0, newBuffer, 0, buffer.Length); + ArrayPool.Shared.Return(buffer); + buffer = newBuffer; + } + + buffer[textLength++] = (byte)nextByte; } - } - /// - /// Reads a null terminated UTF8 string from the stream. - /// - /// The number of bytes to read including the null - /// A string - public static string ReadStringUTF8NullTerminated(this Stream stream, uint byteLength) + return Encoding.UTF8.GetString(buffer, 0, textLength); + } + finally { - if (byteLength == 0) return string.Empty; + ArrayPool.Shared.Return(buffer); + } + } - var buffer = ArrayPool.Shared.Rent((int)byteLength); - try - { - var dataLength = stream.Read(buffer, 0, (int)byteLength); - if (dataLength < 0) throw new EndOfStreamException("Unexpected end of stream while trying to read data"); + /// + /// Reads a null terminated UTF8 string from the stream. + /// + /// The number of bytes to read including the null + /// A string + public static string ReadStringUTF8NullTerminated(this Stream stream, uint byteLength) + { + if (byteLength == 0) return string.Empty; - var byteReadLength = (uint)dataLength; - if (byteReadLength != byteLength) throw new EndOfStreamException($"Not enough data read {byteReadLength} bytes while expecting to read {byteLength} bytes"); + var buffer = ArrayPool.Shared.Rent((int)byteLength); + try + { + var dataLength = stream.Read(buffer, 0, (int)byteLength); + if (dataLength < 0) throw new EndOfStreamException("Unexpected end of stream while trying to read data"); - var isNullTerminated = buffer[byteReadLength - 1] == 0; + var byteReadLength = (uint)dataLength; + if (byteReadLength != byteLength) throw new EndOfStreamException($"Not enough data read {byteReadLength} bytes while expecting to read {byteLength} bytes"); - var text = Encoding.UTF8.GetString(buffer, 0, (int)(isNullTerminated ? byteReadLength - 1 : byteReadLength)); - return text; - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } + var isNullTerminated = buffer[byteReadLength - 1] == 0; - - public static short ReadI16(this Stream stream, bool isLittleEndian) + var text = Encoding.UTF8.GetString(buffer, 0, (int)(isNullTerminated ? byteReadLength - 1 : byteReadLength)); + return text; + } + finally { - return (short) ReadU16(stream, isLittleEndian); + ArrayPool.Shared.Return(buffer); } + } - public static ushort ReadU16(this Stream stream, bool isLittleEndian) - { - ushort value = 0; - int nextValue = stream.ReadByte(); - if (nextValue < 0) throw new EndOfStreamException(); - value = (byte)nextValue; - nextValue = stream.ReadByte(); - if (nextValue < 0) throw new EndOfStreamException(); - value = (ushort)((nextValue << 8) | (byte)value); + public static short ReadI16(this Stream stream, bool isLittleEndian) + { + return (short) ReadU16(stream, isLittleEndian); + } - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } + public static ushort ReadU16(this Stream stream, bool isLittleEndian) + { + ushort value = 0; + int nextValue = stream.ReadByte(); + if (nextValue < 0) throw new EndOfStreamException(); + value = (byte)nextValue; - return value; - } + nextValue = stream.ReadByte(); + if (nextValue < 0) throw new EndOfStreamException(); + value = (ushort)((nextValue << 8) | (byte)value); - public static int ReadI32(this Stream stream, bool isLittleEndian) + if (isLittleEndian != BitConverter.IsLittleEndian) { - return (int)ReadU32(stream, isLittleEndian); + value = BinaryPrimitives.ReverseEndianness(value); } - public static unsafe uint ReadU32(this Stream stream, bool isLittleEndian) - { - uint value = 0; - var span = new Span((byte*)&value, sizeof(uint)); - if (stream.Read(span) != sizeof(uint)) - { - throw new EndOfStreamException(); - } + return value; + } - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } - return value; - } - public static long ReadI64(this Stream stream, bool isLittleEndian) + public static int ReadI32(this Stream stream, bool isLittleEndian) + { + return (int)ReadU32(stream, isLittleEndian); + } + + public static unsafe uint ReadU32(this Stream stream, bool isLittleEndian) + { + uint value = 0; + var span = new Span((byte*)&value, sizeof(uint)); + if (stream.Read(span) != sizeof(uint)) { - return (long)ReadU64(stream, isLittleEndian); + throw new EndOfStreamException(); } - public static unsafe ulong ReadU64(this Stream stream, bool isLittleEndian) + if (isLittleEndian != BitConverter.IsLittleEndian) { - ulong value = 0; - var span = new Span((byte*)&value, sizeof(ulong)); - if (stream.Read(span) != sizeof(ulong)) - { - throw new EndOfStreamException(); - } - - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } - return value; + value = BinaryPrimitives.ReverseEndianness(value); } + return value; + } + public static long ReadI64(this Stream stream, bool isLittleEndian) + { + return (long)ReadU64(stream, isLittleEndian); + } - public static unsafe void WriteU16(this Stream stream, bool isLittleEndian, ushort value) + public static unsafe ulong ReadU64(this Stream stream, bool isLittleEndian) + { + ulong value = 0; + var span = new Span((byte*)&value, sizeof(ulong)); + if (stream.Read(span) != sizeof(ulong)) { - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } - var span = new Span((byte*)&value, sizeof(ushort)); - stream.Write(span); + throw new EndOfStreamException(); } - public static void WriteI32(this Stream stream, bool isLittleEndian, int value) + if (isLittleEndian != BitConverter.IsLittleEndian) { - WriteU32(stream, isLittleEndian, (uint)value); + value = BinaryPrimitives.ReverseEndianness(value); } + return value; + } - public static unsafe void WriteU32(this Stream stream, bool isLittleEndian, uint value) + public static unsafe void WriteU16(this Stream stream, bool isLittleEndian, ushort value) + { + if (isLittleEndian != BitConverter.IsLittleEndian) { - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } - var span = new Span((byte*)&value, sizeof(uint)); - stream.Write(span); + value = BinaryPrimitives.ReverseEndianness(value); } + var span = new Span((byte*)&value, sizeof(ushort)); + stream.Write(span); + } + + public static void WriteI32(this Stream stream, bool isLittleEndian, int value) + { + WriteU32(stream, isLittleEndian, (uint)value); + } - public static unsafe void WriteU64(this Stream stream, bool isLittleEndian, ulong value) + public static unsafe void WriteU32(this Stream stream, bool isLittleEndian, uint value) + { + if (isLittleEndian != BitConverter.IsLittleEndian) { - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } - var span = new Span((byte*)&value, sizeof(ulong)); - stream.Write(span); + value = BinaryPrimitives.ReverseEndianness(value); } + var span = new Span((byte*)&value, sizeof(uint)); + stream.Write(span); + } - /// - /// Writes a null terminated UTF8 string to the stream. - /// - public static void WriteStringUTF8NullTerminated(this Stream stream, string text) + public static unsafe void WriteU64(this Stream stream, bool isLittleEndian, ulong value) + { + if (isLittleEndian != BitConverter.IsLittleEndian) { - if (text == null) throw new ArgumentNullException(nameof(text)); - - int byteLength = Encoding.UTF8.GetByteCount(text); - var buffer = ArrayPool.Shared.Rent(byteLength + 1); - try - { - Encoding.UTF8.GetBytes(text, 0, text.Length, buffer, 0); - buffer[byteLength] = 0; - stream.Write(buffer, 0, byteLength + 1); - } - finally - { - ArrayPool.Shared.Return(buffer); - } + value = BinaryPrimitives.ReverseEndianness(value); } + var span = new Span((byte*)&value, sizeof(ulong)); + stream.Write(span); + } + + /// + /// Writes a null terminated UTF8 string to the stream. + /// + public static void WriteStringUTF8NullTerminated(this Stream stream, string text) + { + if (text == null) throw new ArgumentNullException(nameof(text)); + int byteLength = Encoding.UTF8.GetByteCount(text); + var buffer = ArrayPool.Shared.Rent(byteLength + 1); + try + { + Encoding.UTF8.GetBytes(text, 0, text.Length, buffer, 0); + buffer[byteLength] = 0; + stream.Write(buffer, 0, byteLength + 1); + } + finally + { + ArrayPool.Shared.Return(buffer); + } } + } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs index fadd182..10f66c8 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs @@ -19,7 +19,7 @@ public PEBaseRelocationDirectory() : base(ImageDataDirectoryKind.BaseRelocation) public List Blocks { get; } = new(); - public override void UpdateLayout(DiagnosticBag diagnostics) + public override void UpdateLayout(PEVisitorContext context) { var size = 0UL; foreach (var block in Blocks) @@ -29,7 +29,7 @@ public override void UpdateLayout(DiagnosticBag diagnostics) Size = size; } - protected override unsafe void Read(PEImageReader reader) + public override unsafe void Read(PEImageReader reader) { reader.Position = Position; var size = (int)Size; @@ -45,7 +45,7 @@ protected override unsafe void Read(PEImageReader reader) return; } - var allSectionData = reader.PEFile.GetAllSectionData(); + var allSectionData = reader.File.GetAllSectionData(); int blockIndex = 0; while (span.Length > 0) @@ -53,7 +53,7 @@ protected override unsafe void Read(PEImageReader reader) var location = MemoryMarshal.Read(span); span = span.Slice(sizeof(ImageBaseRelocation)); - if (!reader.PEFile.TryFindSection(location.PageVirtualAddress, out var section)) + if (!reader.File.TryFindSection(location.PageVirtualAddress, out var section)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section containing the virtual address {location.PageVirtualAddress} in block #{blockIndex}"); continue; @@ -137,7 +137,7 @@ private bool TryFindSectionData(List allSectionData, int startInd return false; } - protected override void Write(PEImageWriter writer) + public override void Write(PEImageWriter writer) { throw new NotImplementedException(); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs index 7ab7bcd..2b97071 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs @@ -45,7 +45,7 @@ internal static PEDirectory Create(ImageDataDirectoryKind kind, RVALink (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); + public override void UpdateLayout(PEVisitorContext context) + { + UpdateSize(context.File, context.Diagnostics); } - protected override void Read(PEImageReader reader) + public override void Read(PEImageReader reader) { FunctionTable.Read(reader, Position); - UpdateLayout(reader.Diagnostics); + UpdateSize(reader.File, reader.Diagnostics); + } + + private void UpdateSize(PEFile file, DiagnosticBag diagnostics) + { + Size = FunctionTable.CalculateSize(file, diagnostics); } - protected override void Write(PEImageWriter writer) => FunctionTable.Write(writer); + public override void Write(PEImageWriter writer) => FunctionTable.Write(writer); - protected override void ValidateParent(ObjectFileNodeBase parent) + protected override void ValidateParent(ObjectFileElement parent) { if (parent is not PEImportAddressTableDirectory) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs index fa25382..f66d681 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs @@ -17,19 +17,19 @@ public PEImportAddressTableDirectory() : base(ImageDataDirectoryKind.ImportAddre } public ObjectList Tables => _tables; - - public override void UpdateLayout(DiagnosticBag diagnostics) + + public override void UpdateLayout(PEVisitorContext context) { ulong size = 0; foreach (var table in _tables) { - table.UpdateLayout(diagnostics); + table.UpdateLayout(context); 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 + public 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 + public 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 a8f1a58..5629d0b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -20,12 +20,12 @@ public PEImportDirectory() : base(ImageDataDirectoryKind.Import) } public ObjectList Entries => _entries; - public override unsafe void UpdateLayout(DiagnosticBag diagnostics) + public override unsafe void UpdateLayout(PEVisitorContext context) { Size = (ulong)((_entries.Count + 1) * sizeof(RawImportDirectoryEntry)); } - protected override void Read(PEImageReader reader) + public override void Read(PEImageReader reader) { var diagnostics = reader.Diagnostics; @@ -54,7 +54,7 @@ protected override void Read(PEImageReader reader) } // Find the section data for the ImportLookupTableRVA - if (!reader.PEFile.TryFindSectionData(rawEntry.ImportAddressTableRVA, out var sectionData)) + if (!reader.File.TryFindSectionData(rawEntry.ImportAddressTableRVA, out var sectionData)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidImportAddressTableRVA, $"Unable to find the section data for ImportAddressTableRVA {rawEntry.ImportAddressTableRVA}"); return; @@ -64,7 +64,7 @@ protected override void Read(PEImageReader reader) var importLookupAddressTablePositionInFile = sectionData.Position + rawEntry.ImportLookupTableRVA - sectionData.VirtualAddress; // Find the section data for the ImportLookupTableRVA - if (!reader.PEFile.TryFindSectionData(rawEntry.ImportLookupTableRVA, out sectionData)) + if (!reader.File.TryFindSectionData(rawEntry.ImportLookupTableRVA, out sectionData)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidImportLookupTableRVA, $"Unable to find the section data for ImportLookupTableRVA {rawEntry.ImportLookupTableRVA}"); return; @@ -96,12 +96,12 @@ protected override void Read(PEImageReader reader) var entries = CollectionsMarshal.AsSpan(_entries.UnsafeList); foreach (ref var entry in entries) { - entry.ImportAddressTable.ReadInternal(reader); - entry.ImportLookupTable.ReadInternal(reader); + entry.ImportAddressTable.Read(reader); + entry.ImportLookupTable.Read(reader); } } - protected override void Write(PEImageWriter writer) + public override void Write(PEImageWriter writer) { throw new NotImplementedException(); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs index ba1a215..62f8337 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs @@ -53,16 +53,16 @@ public PEImportLookupTable ImportLookupTable _importLookupTable = value; } } - - public override unsafe void UpdateLayout(DiagnosticBag diagnostics) + + public override unsafe void UpdateLayout(PEVisitorContext context) { Size = (ulong)sizeof(RawImportDirectoryEntry); // Update the layout of the import lookup table - ImportLookupTable.UpdateLayout(diagnostics); + ImportLookupTable.UpdateLayout(context); } - protected override void Read(PEImageReader reader) => throw new System.NotSupportedException(); + public override void Read(PEImageReader reader) => throw new System.NotSupportedException(); - protected override void Write(PEImageWriter writer) => throw new System.NotSupportedException(); + public override void Write(PEImageWriter writer) => throw new System.NotSupportedException(); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index fb37aa2..337fc97 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -22,7 +22,7 @@ public unsafe ulong CalculateSize(PEFile peFile, DiagnosticBag diagnostics) public void Read(PEImageReader reader, ulong position) { - var peFile = reader.PEFile; + var peFile = reader.File; reader.Position = position; if (peFile.IsPE32) diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs index c497843..47ae804 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -23,28 +23,26 @@ public PEImportLookupTable() } public List Entries => FunctionTable.Entries; - - public override void UpdateLayout(DiagnosticBag diagnostics) - { - var peFile = Parent?.Parent?.Parent?.Parent; - if (peFile is null) - { - diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidParent, "The parent of the Import Lookup Table is null."); - return; - } - Size = FunctionTable.CalculateSize(peFile, diagnostics); + public override void UpdateLayout(PEVisitorContext context) + { + UpdateSize(context.File, context.Diagnostics); } - protected override void Read(PEImageReader reader) + public override void Read(PEImageReader reader) { FunctionTable.Read(reader, Position); - UpdateLayout(reader.Diagnostics); + UpdateSize(reader.File, reader.Diagnostics); + } + + private void UpdateSize(PEFile file, DiagnosticBag diagnostics) + { + Size = FunctionTable.CalculateSize(file, diagnostics); } - protected override void Write(PEImageWriter writer) => FunctionTable.Write(writer); + public override void Write(PEImageWriter writer) => FunctionTable.Write(writer); - protected override void ValidateParent(ObjectFileNodeBase parent) + protected override void ValidateParent(ObjectFileElement parent) { if (parent is not PEImportDirectoryEntry) { diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 6c4292d..dd10cfd 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -52,7 +52,7 @@ public static bool TryRead(Stream stream, [NotNullWhen(true)] out PEFile? peFile return !reader.Diagnostics.HasErrors; } - protected override void Read(PEImageReader reader) + public override void Read(PEImageReader reader) { Debug.Assert(Unsafe.SizeOf() == 64); @@ -306,6 +306,6 @@ private void InitializeSections(PEImageReader imageReader, ReadOnlySpan GetAllSectionData() return dataList; } - - public override void UpdateLayout(DiagnosticBag diagnostics) - { - + public override void UpdateLayout(PEVisitorContext layoutContext) + { } protected override bool PrintMembers(StringBuilder builder) @@ -252,4 +250,5 @@ protected override bool PrintMembers(StringBuilder builder) builder.Append($"Directories[{Directories.Count}], Sections[{Sections.Count}]"); return true; } + } diff --git a/src/LibObjectFile/PE/PEImageReader.cs b/src/LibObjectFile/PE/PEImageReader.cs index e4ee78c..4242d02 100644 --- a/src/LibObjectFile/PE/PEImageReader.cs +++ b/src/LibObjectFile/PE/PEImageReader.cs @@ -8,13 +8,12 @@ namespace LibObjectFile.PE; public sealed class PEImageReader : ObjectFileReaderWriter { - internal PEImageReader(PEFile file, Stream stream, PEImageReaderOptions readerOptions) : base(stream) + internal PEImageReader(PEFile file, Stream stream, PEImageReaderOptions readerOptions) : base(file, stream) { - PEFile = file; Options = readerOptions; } - public PEFile PEFile { get; } + public new PEFile File => (PEFile)base.File; public PEImageReaderOptions Options { get; } diff --git a/src/LibObjectFile/PE/PEImageWriter.cs b/src/LibObjectFile/PE/PEImageWriter.cs index b9e649d..6571d96 100644 --- a/src/LibObjectFile/PE/PEImageWriter.cs +++ b/src/LibObjectFile/PE/PEImageWriter.cs @@ -8,12 +8,11 @@ namespace LibObjectFile.PE; public sealed class PEImageWriter : ObjectFileReaderWriter { - internal PEImageWriter(PEFile file, Stream stream) : base(stream) + internal PEImageWriter(PEFile file, Stream stream) : base(file, stream) { - PEFile = file; } - public PEFile PEFile { get; } + public PEFile PEFile => (PEFile)base.File; public override bool IsReadOnly => false; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEObject.cs b/src/LibObjectFile/PE/PEObject.cs index 38002ce..a573b8d 100644 --- a/src/LibObjectFile/PE/PEObject.cs +++ b/src/LibObjectFile/PE/PEObject.cs @@ -3,44 +3,8 @@ // See the license.txt file in the project root for more information. using System.Diagnostics; -using System.Text; 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); - - // TODO: move PrintName and PrintMembers to ObjectFileNode - - public sealed override string ToString() - { - var builder = new StringBuilder(); - PrintName(builder); - builder.Append(" { "); - if (PrintMembers(builder)) - { - builder.Append(' '); - } - builder.Append('}'); - return builder.ToString(); - } - - protected virtual void PrintName(StringBuilder builder) - { - builder.Append(GetType().Name); - } - - protected virtual bool PrintMembers(StringBuilder builder) - { - return false; - } -} \ No newline at end of file +public abstract class PEObject : ObjectFileElement; diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index 7a37481..3aef359 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -124,24 +124,24 @@ public bool ContainsVirtual(RVA virtualAddress, uint size) => virtualAddress >= VirtualAddress && virtualAddress + size <= VirtualAddress + VirtualSize; /// - public override void UpdateLayout(DiagnosticBag diagnostics) + public override void UpdateLayout(PEVisitorContext context) { var va = VirtualAddress; foreach (var data in DataParts) { data.VirtualAddress = va; - data.UpdateLayout(diagnostics); + data.UpdateLayout(context); va += (uint)data.Size; } } - protected override void Read(PEImageReader reader) + public override void Read(PEImageReader reader) { throw new NotImplementedException(); } /// - protected override void Write(PEImageWriter writer) + public override void Write(PEImageWriter writer) { throw new NotImplementedException(); } @@ -191,18 +191,18 @@ public static SectionCharacteristics GetDefaultSectionCharacteristics(PESectionN }; } - private static void SectionDataAdded(ObjectFileNode parent, PESectionData sectionData) + private static void SectionDataAdded(ObjectFileElement parent, PESectionData sectionData) { var section = (PESection) parent; section.UpdateSectionDataVirtualAddress(sectionData.Index); } - private static void SectionDataRemoved(ObjectFileNode parent, int index, PESectionData removedSectionData) + private static void SectionDataRemoved(ObjectFileElement parent, int index, PESectionData removedSectionData) { var section = (PESection) parent; section.UpdateSectionDataVirtualAddress(index); } - private static void SectionDataUpdated(ObjectFileNode parent, int index, PESectionData previousSectionData, PESectionData newSectionData) + private static void SectionDataUpdated(ObjectFileElement parent, int index, PESectionData previousSectionData, PESectionData newSectionData) { var section = (PESection) parent; section.UpdateSectionDataVirtualAddress(index); diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index c9b4c86..f00ddd8 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -65,10 +65,4 @@ protected override bool PrintMembers(StringBuilder builder) internal sealed class PESectionDataTemp : PESectionData { public static readonly PESectionDataTemp Instance = new (); - - protected override void Read(PEImageReader reader) => throw new NotSupportedException(); - - protected override void Write(PEImageWriter writer) => throw new NotSupportedException(); - - 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 index 58f3ce4..158645b 100644 --- a/src/LibObjectFile/PE/PESectionStreamData.cs +++ b/src/LibObjectFile/PE/PESectionStreamData.cs @@ -47,16 +47,12 @@ public override ulong Size set => throw new InvalidOperationException(); } - public override void UpdateLayout(DiagnosticBag diagnostics) - { - } - - protected override void Read(PEImageReader reader) + public override void Read(PEImageReader reader) { // No need to read, as the data is already provided via a stream } - protected override void Write(PEImageWriter writer) + public override void Write(PEImageWriter writer) { Stream.Position = 0; Stream.CopyTo(writer.Stream); diff --git a/src/LibObjectFile/PE/PEVisitorContext.cs b/src/LibObjectFile/PE/PEVisitorContext.cs new file mode 100644 index 0000000..65dea68 --- /dev/null +++ b/src/LibObjectFile/PE/PEVisitorContext.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; + +public sealed class PEVisitorContext : VisitorContextBase +{ + internal PEVisitorContext(PEFile peFile, DiagnosticBag diagnostics) : base(peFile, diagnostics) + { + } +} \ No newline at end of file diff --git a/src/LibObjectFile/RelocationSize.cs b/src/LibObjectFile/RelocationSize.cs index 78cd8e5..2e4b70a 100644 --- a/src/LibObjectFile/RelocationSize.cs +++ b/src/LibObjectFile/RelocationSize.cs @@ -2,13 +2,12 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile +namespace LibObjectFile; + +public enum RelocationSize { - public enum RelocationSize - { - I8, - I16, - I32, - I64, - } + I8, + I16, + I32, + I64, } \ No newline at end of file diff --git a/src/LibObjectFile/Utils/AlignHelper.cs b/src/LibObjectFile/Utils/AlignHelper.cs index e2daf53..59ac509 100644 --- a/src/LibObjectFile/Utils/AlignHelper.cs +++ b/src/LibObjectFile/Utils/AlignHelper.cs @@ -5,37 +5,36 @@ using System; using System.Runtime.CompilerServices; -namespace LibObjectFile.Utils +namespace LibObjectFile.Utils; + +/// +/// Helper class to perform alignment. +/// +public static class AlignHelper { /// - /// Helper class to perform alignment. + /// Returns true if alignment is a power of 2. /// - public static class AlignHelper + /// The alignment + /// true if it is a power of 2. + public static bool IsPowerOfTwo(ulong align) { - /// - /// Returns true if alignment is a power of 2. - /// - /// The alignment - /// true if it is a power of 2. - public static bool IsPowerOfTwo(ulong align) - { - return (align & (align - 1)) == 0; - } + return (align & (align - 1)) == 0; + } - /// - /// Aligns a value to the required alignment. - /// - /// The value to align. - /// The alignment. - /// The value aligned or unchanged it is was already aligned. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong AlignToUpper(ulong value, ulong align) - { - if (align == 0) throw new ArgumentOutOfRangeException(nameof(align), "Alignment must be > 0"); - if (!IsPowerOfTwo(align)) throw new ArgumentOutOfRangeException(nameof(align), "Alignment must be a power of 2"); + /// + /// Aligns a value to the required alignment. + /// + /// The value to align. + /// The alignment. + /// The value aligned or unchanged it is was already aligned. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong AlignToUpper(ulong value, ulong align) + { + if (align == 0) throw new ArgumentOutOfRangeException(nameof(align), "Alignment must be > 0"); + if (!IsPowerOfTwo(align)) throw new ArgumentOutOfRangeException(nameof(align), "Alignment must be a power of 2"); - var nextValue = ((value + align - 1) / align) * align; - return nextValue; - } + var nextValue = ((value + align - 1) / align) * align; + return nextValue; } } \ No newline at end of file diff --git a/src/LibObjectFile/Utils/ObjectList.cs b/src/LibObjectFile/Utils/ObjectList.cs index efe1b52..94c0c4d 100644 --- a/src/LibObjectFile/Utils/ObjectList.cs +++ b/src/LibObjectFile/Utils/ObjectList.cs @@ -17,7 +17,7 @@ namespace LibObjectFile.Utils; [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy(typeof(ObjectList<>.ObjectListDebuggerView))] public readonly struct ObjectList : IList - where TObject : ObjectFileNode + where TObject : ObjectFileElement { // We are using an internal list to keep track of the parent object private readonly InternalList _items; @@ -29,7 +29,7 @@ namespace LibObjectFile.Utils; /// Initializes a new instance of the class. /// /// The parent object file node. - public ObjectList(ObjectFileNode parent, Action? added = null, Action? removing = null, Action? removed = null, Action? updated = null) + public ObjectList(ObjectFileElement parent, Action? added = null, Action? removing = null, Action? removed = null, Action? updated = null) { ArgumentNullException.ThrowIfNull(parent); _items = new InternalList(parent, added, removing, removed, updated); @@ -159,13 +159,13 @@ public TObject CheckAdd(TObject item) return item; } - private sealed class InternalList(ObjectFileNode parent, Action? added, Action? removing, Action? removed, Action? updated) : List + private sealed class InternalList(ObjectFileElement parent, Action? added, Action? removing, Action? removed, Action? updated) : List { - private readonly Action? _added = added; - private readonly Action? _removing = removing; - private readonly Action? _removed = removed; - private readonly Action? _updated = updated; - public readonly ObjectFileNode Parent = parent; + private readonly Action? _added = added; + private readonly Action? _removing = removing; + private readonly Action? _removed = removed; + private readonly Action? _updated = updated; + public readonly ObjectFileElement Parent = parent; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Added(TObject item) => _added?.Invoke(Parent, item); diff --git a/src/LibObjectFile/Utils/ThrowHelper.cs b/src/LibObjectFile/Utils/ThrowHelper.cs index dd40aab..28ef197 100644 --- a/src/LibObjectFile/Utils/ThrowHelper.cs +++ b/src/LibObjectFile/Utils/ThrowHelper.cs @@ -8,78 +8,77 @@ using System.IO; using System.Text; -namespace LibObjectFile.Utils +namespace LibObjectFile.Utils; + +/// +/// Internal helper class for throwing exceptions. +/// +internal static class ThrowHelper { - /// - /// Internal helper class for throwing exceptions. - /// - internal static class ThrowHelper + public static InvalidOperationException InvalidEnum(object v) { - public static InvalidOperationException InvalidEnum(object v) - { - return new InvalidOperationException($"Invalid Enum {v.GetType()}.{v}"); - } + return new InvalidOperationException($"Invalid Enum {v.GetType()}.{v}"); } +} - public static class StreamExtensions +public static class StreamExtensions +{ + /// + /// Reads a null terminated UTF8 string from the stream. + /// + /// true if the string was successfully read from the stream, false otherwise + public static string ReadStringUTF8NullTerminated(this Stream stream) { - /// - /// Reads a null terminated UTF8 string from the stream. - /// - /// true if the string was successfully read from the stream, false otherwise - public static string ReadStringUTF8NullTerminated(this Stream stream) + if (!TryReadStringUTF8NullTerminated(stream, out var text)) { - if (!TryReadStringUTF8NullTerminated(stream, out var text)) - { - throw new EndOfStreamException(); - } - return text; + throw new EndOfStreamException(); } + return text; + } - /// - /// Reads a null terminated UTF8 string from the stream. - /// - /// true if the string was successfully read from the stream, false otherwise - public static bool TryReadStringUTF8NullTerminated(this Stream stream, [NotNullWhen(true)] out string? text) + /// + /// Reads a null terminated UTF8 string from the stream. + /// + /// true if the string was successfully read from the stream, false otherwise + public static bool TryReadStringUTF8NullTerminated(this Stream stream, [NotNullWhen(true)] out string? text) + { + text = null; + var buffer = ArrayPool.Shared.Rent((int)128); + int textLength = 0; + try { - text = null; - var buffer = ArrayPool.Shared.Rent((int)128); - int textLength = 0; - try + while (true) { - while (true) + // TODO: not efficient to read byte by byte + int nextByte = stream.ReadByte(); + if (nextByte < 0) { - // TODO: not efficient to read byte by byte - int nextByte = stream.ReadByte(); - if (nextByte < 0) - { - return false; - } - - if (nextByte == 0) - { - break; - } + return false; + } - if (textLength > buffer.Length) - { - var newBuffer = ArrayPool.Shared.Rent((int)textLength * 2); - Array.Copy(buffer, 0, newBuffer, 0, buffer.Length); - ArrayPool.Shared.Return(buffer); - buffer = newBuffer; - } + if (nextByte == 0) + { + break; + } - buffer[textLength++] = (byte)nextByte; + if (textLength > buffer.Length) + { + var newBuffer = ArrayPool.Shared.Rent((int)textLength * 2); + Array.Copy(buffer, 0, newBuffer, 0, buffer.Length); + ArrayPool.Shared.Return(buffer); + buffer = newBuffer; } - text = Encoding.UTF8.GetString(buffer, 0, textLength); - } - finally - { - ArrayPool.Shared.Return(buffer); + buffer[textLength++] = (byte)nextByte; } - return true; + text = Encoding.UTF8.GetString(buffer, 0, textLength); } + finally + { + ArrayPool.Shared.Return(buffer); + } + + return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/ValueKind.cs b/src/LibObjectFile/ValueKind.cs index 70aa41a..b0a8d01 100644 --- a/src/LibObjectFile/ValueKind.cs +++ b/src/LibObjectFile/ValueKind.cs @@ -2,21 +2,20 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile +namespace LibObjectFile; + +/// +/// Defines the way a value is calculated. +/// +public enum ValueKind { /// - /// Defines the way a value is calculated. + /// The associated value is automatically calculated by the system. /// - public enum ValueKind - { - /// - /// The associated value is automatically calculated by the system. - /// - Auto, + Auto, - /// - /// The associated value is set manually. - /// - Manual, - } + /// + /// The associated value is set manually. + /// + Manual, } \ No newline at end of file diff --git a/src/LibObjectFile/VisitorContextBase.cs b/src/LibObjectFile/VisitorContextBase.cs new file mode 100644 index 0000000..c374b35 --- /dev/null +++ b/src/LibObjectFile/VisitorContextBase.cs @@ -0,0 +1,33 @@ +// 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; + +/// +/// Base class used for layout-ing an object file. +/// +/// The root object file. +/// The diagnostics. +public abstract class VisitorContextBase(ObjectFileElement file, DiagnosticBag diagnostics) +{ + public ObjectFileElement File { get; } = file; + + + public DiagnosticBag Diagnostics { get; } = diagnostics; + + + public bool HasErrors => Diagnostics.HasErrors; +} + +/// +/// Base class used for layout-ing an object file. +/// +/// The type of the object file. +/// The root object file. +/// The diagnostics. +public abstract class VisitorContextBase(TFile file, DiagnosticBag diagnostics) : VisitorContextBase(file, diagnostics) + where TFile : ObjectFileElement +{ + public new TFile File => (TFile)base.File; +} \ No newline at end of file From 2ea5f3443f04a2db30295a7860871e01cf538643 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 18 Sep 2024 22:09:57 +0200 Subject: [PATCH 16/87] Cleanup namespaces / usings --- src/LibObjectFile.CodeGen/Program.Dwarf.cs | 1336 ++++++++--------- src/LibObjectFile.CodeGen/Program.cs | 496 +++--- src/LibObjectFile.Tests/Ar/ArTestBase.cs | 49 +- src/LibObjectFile.Tests/Ar/ArTests.cs | 633 ++++---- src/LibObjectFile.Tests/Dwarf/DwarfTests.cs | 985 ++++++------ src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs | 2 +- src/LibObjectFile.Tests/Elf/ElfTestBase.cs | 134 +- src/LibObjectFile.Tests/LinuxUtil.cs | 131 +- src/LibObjectFile/Ar/ArArchiveFile.cs | 3 +- src/LibObjectFile/Ar/ArArchiveFileReader.cs | 1 + src/LibObjectFile/Ar/ArArchiveFileWriter.cs | 1 + src/LibObjectFile/Ar/ArBinaryFile.cs | 1 - src/LibObjectFile/Ar/ArElfFile.cs | 3 +- src/LibObjectFile/Ar/ArFile.cs | 1 + src/LibObjectFile/Ar/ArLongNamesTable.cs | 1 - src/LibObjectFile/Ar/ArSymbolTable.cs | 2 +- src/LibObjectFile/Ar/ArVisitorContext.cs | 2 + .../{Utils => Collections}/ObjectList.cs | 25 +- .../{Utils => Collections}/ReadOnlyList.cs | 12 +- .../{ => Diagnostics}/DiagnosticBag.cs | 6 +- .../{ => Diagnostics}/DiagnosticId.cs | 2 +- .../{ => Diagnostics}/DiagnosticKind.cs | 2 +- .../{ => Diagnostics}/DiagnosticMessage.cs | 4 +- src/LibObjectFile/Dwarf/DwarfAbbreviation.cs | 2 +- .../Dwarf/DwarfAbbreviationItem.cs | 1 + .../Dwarf/DwarfAbbreviationTable.cs | 2 +- .../Dwarf/DwarfAddressRangeTable.cs | 9 +- src/LibObjectFile/Dwarf/DwarfAttribute.cs | 1 + .../Dwarf/DwarfAttributeDescriptors.cs | 1 - src/LibObjectFile/Dwarf/DwarfContainer.cs | 2 - src/LibObjectFile/Dwarf/DwarfDIE.cs | 2 +- src/LibObjectFile/Dwarf/DwarfElfContext.cs | 1 + src/LibObjectFile/Dwarf/DwarfExpression.cs | 2 +- src/LibObjectFile/Dwarf/DwarfFile.cs | 1 + src/LibObjectFile/Dwarf/DwarfHelper.cs | 2 +- src/LibObjectFile/Dwarf/DwarfInfoSection.cs | 2 +- src/LibObjectFile/Dwarf/DwarfLayoutContext.cs | 2 + .../Dwarf/DwarfLineProgramTable.cs | 4 +- src/LibObjectFile/Dwarf/DwarfLineSection.cs | 2 +- src/LibObjectFile/Dwarf/DwarfLineSequence.cs | 2 +- src/LibObjectFile/Dwarf/DwarfLocationList.cs | 4 +- .../Dwarf/DwarfLocationSection.cs | 2 +- src/LibObjectFile/Dwarf/DwarfOperation.cs | 1 + src/LibObjectFile/Dwarf/DwarfReader.cs | 1 + src/LibObjectFile/Dwarf/DwarfReaderWriter.cs | 2 +- .../Dwarf/DwarfRelocatableSection.cs | 1 - src/LibObjectFile/Dwarf/DwarfStringTable.cs | 1 - src/LibObjectFile/Dwarf/DwarfUnit.cs | 1 + src/LibObjectFile/Dwarf/DwarfWriter.cs | 1 + src/LibObjectFile/Dwarf/DwarfWriterContext.cs | 1 - src/LibObjectFile/Elf/ElfObjectFile.cs | 7 +- .../Elf/ElfObjectFileExtensions.cs | 1 + src/LibObjectFile/Elf/ElfPrinter.cs | 2 - src/LibObjectFile/Elf/ElfReader.cs | 1 - src/LibObjectFile/Elf/ElfReaderOptions.cs | 2 + src/LibObjectFile/Elf/ElfReader{TDecoder}.cs | 2 +- src/LibObjectFile/Elf/ElfSection.cs | 1 + src/LibObjectFile/Elf/ElfSectionExtension.cs | 3 +- src/LibObjectFile/Elf/ElfSectionLink.cs | 1 + src/LibObjectFile/Elf/ElfSegment.cs | 6 +- src/LibObjectFile/Elf/ElfVisitorContext.cs | 2 + src/LibObjectFile/Elf/ElfWriter.cs | 1 - .../Elf/Sections/ElfAlignedShadowSection.cs | 3 +- .../Elf/Sections/ElfBinarySection.cs | 1 + .../Elf/Sections/ElfBinaryShadowSection.cs | 1 - .../Elf/Sections/ElfGnuNoteABITag.cs | 2 +- .../Elf/Sections/ElfNoteTable.cs | 5 +- .../Elf/Sections/ElfNullSection.cs | 2 +- .../Elf/Sections/ElfProgramHeaderTable.cs | 2 - .../Elf/Sections/ElfRelocationTable.cs | 2 +- .../Sections/ElfRelocationTableExtensions.cs | 2 - .../Elf/Sections/ElfStringTable.cs | 2 +- .../Elf/Sections/ElfSymbolTable.cs | 1 + .../ThrowHelper.cs => IO/StreamExtensions.cs} | 19 +- src/LibObjectFile/{Utils => IO}/SubStream.cs | 12 +- src/LibObjectFile/ObjectFileElement.cs | 2 +- src/LibObjectFile/ObjectFileException.cs | 1 + src/LibObjectFile/ObjectFileReaderWriter.cs | 5 +- .../ObjectFileStreamExtensions.cs | 1 - .../PEBaseRelocationDirectory.cs | 2 +- .../PEBaseRelocationPageBlock.cs | 1 - .../PE/DataDirectory/PEDirectory.cs | 1 - .../PE/DataDirectory/PEImportAddressTable.cs | 1 + .../PEImportAddressTableDirectory.cs | 2 +- .../PE/DataDirectory/PEImportDirectory.cs | 4 +- .../DataDirectory/PEImportDirectoryEntry.cs | 2 +- .../PE/DataDirectory/PEImportFunctionTable.cs | 1 + .../PE/DataDirectory/PEImportLookupTable.cs | 1 + src/LibObjectFile/PE/ImageOptionalHeader.cs | 1 - src/LibObjectFile/PE/PEFile.Read.cs | 1 + src/LibObjectFile/PE/PEFile.Write.cs | 1 + src/LibObjectFile/PE/PEFile.cs | 2 +- src/LibObjectFile/PE/PEImageReaderOptions.cs | 2 - src/LibObjectFile/PE/PESection.cs | 5 +- .../PE/PESectionDataExtensions.cs | 1 - .../PE/PESectionName.Defaults.cs | 4 - src/LibObjectFile/PE/PESectionName.cs | 1 - src/LibObjectFile/PE/PEVisitorContext.cs | 2 + src/LibObjectFile/PE/RVALink.cs | 2 - src/LibObjectFile/Utils/AlignHelper.cs | 15 +- src/LibObjectFile/VisitorContextBase.cs | 1 + src/objdasm/ObjDisasmApp.cs | 233 ++- src/objdasm/Program.cs | 135 +- 103 files changed, 2187 insertions(+), 2211 deletions(-) rename src/LibObjectFile/{Utils => Collections}/ObjectList.cs (94%) rename src/LibObjectFile/{Utils => Collections}/ReadOnlyList.cs (87%) rename src/LibObjectFile/{ => Diagnostics}/DiagnosticBag.cs (97%) rename src/LibObjectFile/{ => Diagnostics}/DiagnosticId.cs (99%) rename src/LibObjectFile/{ => Diagnostics}/DiagnosticKind.cs (92%) rename src/LibObjectFile/{ => Diagnostics}/DiagnosticMessage.cs (97%) rename src/LibObjectFile/{Utils/ThrowHelper.cs => IO/StreamExtensions.cs} (81%) rename src/LibObjectFile/{Utils => IO}/SubStream.cs (97%) diff --git a/src/LibObjectFile.CodeGen/Program.Dwarf.cs b/src/LibObjectFile.CodeGen/Program.Dwarf.cs index ce8f67f..1bfe71c 100644 --- a/src/LibObjectFile.CodeGen/Program.Dwarf.cs +++ b/src/LibObjectFile.CodeGen/Program.Dwarf.cs @@ -1,6 +1,7 @@ // 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; using System.IO; @@ -9,782 +10,781 @@ using System.Text.RegularExpressions; using CppAst.CodeGen.CSharp; -namespace LibObjectFile.CodeGen +namespace LibObjectFile.CodeGen; + +partial class Program { - partial class Program + private static void GenerateDwarf() { - private static void GenerateDwarf() + var cppOptions = new CSharpConverterOptions() { - var cppOptions = new CSharpConverterOptions() + DefaultClassLib = "DwarfNative", + DefaultNamespace = "LibObjectFile.Dwarf", + DefaultOutputFilePath = "/LibObjectFile.Dwarf.generated.cs", + DefaultDllImportNameAndArguments = "NotUsed", + MappingRules = { - DefaultClassLib = "DwarfNative", - DefaultNamespace = "LibObjectFile.Dwarf", - DefaultOutputFilePath = "/LibObjectFile.Dwarf.generated.cs", - DefaultDllImportNameAndArguments = "NotUsed", - MappingRules = - { - map => map.MapMacroToConst("^DW_TAG_.*", "unsigned short"), - map => map.MapMacroToConst("^DW_FORM_.*", "unsigned short"), - map => map.MapMacroToConst("^DW_AT_.*", "unsigned short"), - map => map.MapMacroToConst("^DW_LN[ES]_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_IDX_.*", "unsigned short"), - map => map.MapMacroToConst("^DW_LANG_.*", "unsigned short"), - map => map.MapMacroToConst("^DW_ID_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_CC_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_ISA_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_CHILDREN_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_OP_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_ACCESS_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_VIS_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_VIRTUALITY_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_INL_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_ORD_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_DSC_.*", "unsigned char"), - map => map.MapMacroToConst("^DW_UT_.*", "unsigned char"), - } - }; + map => map.MapMacroToConst("^DW_TAG_.*", "unsigned short"), + map => map.MapMacroToConst("^DW_FORM_.*", "unsigned short"), + map => map.MapMacroToConst("^DW_AT_.*", "unsigned short"), + map => map.MapMacroToConst("^DW_LN[ES]_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_IDX_.*", "unsigned short"), + map => map.MapMacroToConst("^DW_LANG_.*", "unsigned short"), + map => map.MapMacroToConst("^DW_ID_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_CC_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_ISA_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_CHILDREN_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_OP_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_ACCESS_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_VIS_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_VIRTUALITY_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_INL_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_ORD_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_DSC_.*", "unsigned char"), + map => map.MapMacroToConst("^DW_UT_.*", "unsigned char"), + } + }; - cppOptions.GenerateEnumItemAsFields = false; - cppOptions.IncludeFolders.Add(Environment.CurrentDirectory); + cppOptions.GenerateEnumItemAsFields = false; + cppOptions.IncludeFolders.Add(Environment.CurrentDirectory); - var csCompilation = CSharpConverter.Convert(@"#include ""dwarf.h""", cppOptions); + var csCompilation = CSharpConverter.Convert(@"#include ""dwarf.h""", cppOptions); - AssertCompilation(csCompilation); + AssertCompilation(csCompilation); - // Add pragma - var csFile = csCompilation.Members.OfType().First(); - var ns = csFile.Members.OfType().First(); - csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable 1591") ); - csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable CS8765")); - csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#nullable enable")); + // Add pragma + var csFile = csCompilation.Members.OfType().First(); + var ns = csFile.Members.OfType().First(); + csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable 1591") ); + csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable CS8765")); + csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#nullable enable")); - ProcessEnum(cppOptions, csCompilation, "DW_AT_", "DwarfAttributeKind"); - ProcessEnum(cppOptions, csCompilation, "DW_FORM_", "DwarfAttributeForm"); - ProcessEnum(cppOptions, csCompilation, "DW_TAG_", "DwarfTag"); - ProcessEnum(cppOptions, csCompilation, "DW_OP_", "DwarfOperationKind"); - ProcessEnum(cppOptions, csCompilation, "DW_LANG_", "DwarfLanguageKind"); - ProcessEnum(cppOptions, csCompilation, "DW_CC_", "DwarfCallingConvention"); - ProcessEnum(cppOptions, csCompilation, "DW_UT_", "DwarfUnitKind"); + ProcessEnum(cppOptions, csCompilation, "DW_AT_", "DwarfAttributeKind"); + ProcessEnum(cppOptions, csCompilation, "DW_FORM_", "DwarfAttributeForm"); + ProcessEnum(cppOptions, csCompilation, "DW_TAG_", "DwarfTag"); + ProcessEnum(cppOptions, csCompilation, "DW_OP_", "DwarfOperationKind"); + ProcessEnum(cppOptions, csCompilation, "DW_LANG_", "DwarfLanguageKind"); + ProcessEnum(cppOptions, csCompilation, "DW_CC_", "DwarfCallingConvention"); + ProcessEnum(cppOptions, csCompilation, "DW_UT_", "DwarfUnitKind"); - GenerateDwarfAttributes(ns); - GenerateDwarfDIE(ns); + GenerateDwarfAttributes(ns); + GenerateDwarfDIE(ns); - csCompilation.DumpTo(GetCodeWriter(Path.Combine("LibObjectFile", "generated"))); - } + csCompilation.DumpTo(GetCodeWriter(Path.Combine("LibObjectFile", "generated"))); + } - private static Dictionary MapAttributeCompactNameToType = new Dictionary(); + private static Dictionary MapAttributeCompactNameToType = new Dictionary(); - private static void GenerateDwarfAttributes(CSharpNamespace ns) - { - var alreadyDone = new HashSet(); + private static void GenerateDwarfAttributes(CSharpNamespace ns) + { + var alreadyDone = new HashSet(); - var csHelper = new CSharpClass("DwarfHelper") - { - Modifiers = CSharpModifiers.Static | CSharpModifiers.Partial, - Visibility = CSharpVisibility.Public - }; - ns.Members.Add(csHelper); + var csHelper = new CSharpClass("DwarfHelper") + { + Modifiers = CSharpModifiers.Static | CSharpModifiers.Partial, + Visibility = CSharpVisibility.Public + }; + ns.Members.Add(csHelper); - var csField = new CSharpField("AttributeToEncoding") - { - Modifiers = CSharpModifiers.Static | CSharpModifiers.ReadOnly, - Visibility = CSharpVisibility.Private, - FieldType = new CSharpArrayType(new CSharpFreeType("DwarfAttributeEncoding")) - }; - csHelper.Members.Add(csField); + var csField = new CSharpField("AttributeToEncoding") + { + Modifiers = CSharpModifiers.Static | CSharpModifiers.ReadOnly, + Visibility = CSharpVisibility.Private, + FieldType = new CSharpArrayType(new CSharpFreeType("DwarfAttributeEncoding")) + }; + csHelper.Members.Add(csField); - var fieldArrayBuilder = new StringBuilder(); - fieldArrayBuilder.AppendLine("new DwarfAttributeEncoding[] {"); + var fieldArrayBuilder = new StringBuilder(); + fieldArrayBuilder.AppendLine("new DwarfAttributeEncoding[] {"); - int currentAttributeIndex = 0; + int currentAttributeIndex = 0; - foreach (var attrEncoding in MapAttributeToEncoding) - { - var attrEncodingParts = attrEncoding.Split(' ', StringSplitOptions.RemoveEmptyEntries); - var attributeName = attrEncodingParts[0]; - var attributeIndex = int.Parse(attrEncodingParts[1].Substring(2), System.Globalization.NumberStyles.HexNumber); - var rawName = attributeName.Substring("DW_AT_".Length); - //var csharpName = CSharpifyName(rawName); + foreach (var attrEncoding in MapAttributeToEncoding) + { + var attrEncodingParts = attrEncoding.Split(' ', StringSplitOptions.RemoveEmptyEntries); + var attributeName = attrEncodingParts[0]; + var attributeIndex = int.Parse(attrEncodingParts[1].Substring(2), System.Globalization.NumberStyles.HexNumber); + var rawName = attributeName.Substring("DW_AT_".Length); + //var csharpName = CSharpifyName(rawName); - string attrType = "object"; - var kind = AttributeKind.Managed; + string attrType = "object"; + var kind = AttributeKind.Managed; - if (attributeName == "DW_AT_accessibility") - { - attrType = "DwarfAccessibility"; - kind = AttributeKind.ValueType; - } - else if (attributeName == "DW_AT_visibility") - { - attrType = "DwarfVisibility"; - kind = AttributeKind.ValueType; - } - else if (attributeName == "DW_AT_virtuality") - { - attrType = "DwarfVirtuality"; - kind = AttributeKind.ValueType; - } - else if (attributeName == "DW_AT_language") - { - attrType = "DwarfLanguageKind"; - kind = AttributeKind.ValueType; - } - else if (attributeName == "DW_AT_identifier_case") - { - attrType = "DwarfIdentifierCaseKind"; - kind = AttributeKind.ValueType; - } - else if (attributeName == "DW_AT_calling_convention") - { - attrType = "DwarfCallingConvention"; - kind = AttributeKind.ValueType; - } - else if (attributeName == "DW_AT_inline") - { - attrType = "DwarfInlineKind"; - kind = AttributeKind.ValueType; - } - else if (attributeName == "DW_AT_ordering") - { - attrType = "DwarfArrayOrderingKind"; - kind = AttributeKind.ValueType; - } - else if (attributeName == "DW_AT_discr_list") - { - attrType = "DwarfDiscriminantListKind"; - kind = AttributeKind.ValueType; - } - else if (attrEncodingParts.Length == 3) + if (attributeName == "DW_AT_accessibility") + { + attrType = "DwarfAccessibility"; + kind = AttributeKind.ValueType; + } + else if (attributeName == "DW_AT_visibility") + { + attrType = "DwarfVisibility"; + kind = AttributeKind.ValueType; + } + else if (attributeName == "DW_AT_virtuality") + { + attrType = "DwarfVirtuality"; + kind = AttributeKind.ValueType; + } + else if (attributeName == "DW_AT_language") + { + attrType = "DwarfLanguageKind"; + kind = AttributeKind.ValueType; + } + else if (attributeName == "DW_AT_identifier_case") + { + attrType = "DwarfIdentifierCaseKind"; + kind = AttributeKind.ValueType; + } + else if (attributeName == "DW_AT_calling_convention") + { + attrType = "DwarfCallingConvention"; + kind = AttributeKind.ValueType; + } + else if (attributeName == "DW_AT_inline") + { + attrType = "DwarfInlineKind"; + kind = AttributeKind.ValueType; + } + else if (attributeName == "DW_AT_ordering") + { + attrType = "DwarfArrayOrderingKind"; + kind = AttributeKind.ValueType; + } + else if (attributeName == "DW_AT_discr_list") + { + attrType = "DwarfDiscriminantListKind"; + kind = AttributeKind.ValueType; + } + else if (attrEncodingParts.Length == 3) + { + switch (attrEncodingParts[2]) { - switch (attrEncodingParts[2]) - { - case "string": - attrType = "string"; - break; + case "string": + attrType = "string"; + break; - case "flag": - attrType = "bool"; - kind = AttributeKind.ValueType; - break; + case "flag": + attrType = "bool"; + kind = AttributeKind.ValueType; + break; - case "reference": - attrType = "DwarfDIE"; - break; + case "reference": + attrType = "DwarfDIE"; + break; - case "address": - attrType = "ulong"; - kind = AttributeKind.ValueType; - break; + case "address": + attrType = "ulong"; + kind = AttributeKind.ValueType; + break; - case "constant": - attrType = "DwarfConstant"; - kind = AttributeKind.ValueType; - break; + case "constant": + attrType = "DwarfConstant"; + kind = AttributeKind.ValueType; + break; - case "lineptr": - attrType = "DwarfLineProgramTable"; - break; + case "lineptr": + attrType = "DwarfLineProgramTable"; + break; - case "exprloc": - attrType = "DwarfExpression"; - break; - - case "loclist": - case "loclistptr": - attrType = "DwarfLocation"; - break; - - case "addrptr": - case "macptr": - case "rnglist": - case "rangelistptr": - case "rnglistsptr": - case "stroffsetsptr": - attrType = "ulong"; - kind = AttributeKind.ValueType; - break; - } - } - else if (attrEncodingParts.Length > 3) - { - var key = string.Join(" ", attrEncodingParts.Skip(2).ToArray()); - alreadyDone.Add(key); - - Console.WriteLine(attrEncoding); - - bool hasConstant = false; - for (int i = 2; i < attrEncodingParts.Length; i++) - { - switch (attrEncodingParts[i]) - { - case "loclist": - case "loclistptr": - attrType = "DwarfLocation"; - kind = AttributeKind.ValueType; - goto next; - case "constant": - hasConstant = true; - break; - } - } - - if (hasConstant) - { - attrType = "DwarfConstant"; + case "exprloc": + attrType = "DwarfExpression"; + break; + + case "loclist": + case "loclistptr": + attrType = "DwarfLocation"; + break; + + case "addrptr": + case "macptr": + case "rnglist": + case "rangelistptr": + case "rnglistsptr": + case "stroffsetsptr": + attrType = "ulong"; kind = AttributeKind.ValueType; - } + break; } + } + else if (attrEncodingParts.Length > 3) + { + var key = string.Join(" ", attrEncodingParts.Skip(2).ToArray()); + alreadyDone.Add(key); - next: - - MapAttributeCompactNameToType.Add(attributeName.Replace("_", string.Empty), new AttributeMapping(rawName, attrType, kind)); - - const int PaddingEncodingName = 50; - - for (; currentAttributeIndex < attributeIndex; currentAttributeIndex++) - { - fieldArrayBuilder.AppendLine($" {"DwarfAttributeEncoding.None",-PaddingEncodingName}, // 0x{currentAttributeIndex:x2} (undefined)"); - } + Console.WriteLine(attrEncoding); + bool hasConstant = false; for (int i = 2; i < attrEncodingParts.Length; i++) { - string name; switch (attrEncodingParts[i]) { - case "string": - name = "String"; - break; - - case "flag": - name = "Flag"; - break; - - case "block": - name = "Block"; - break; - - case "reference": - name = "Reference"; - break; - - case "address": - name = "Address"; - break; - - case "constant": - name = "Constant"; - break; - - case "lineptr": - name = "LinePointer"; - break; - - case "exprloc": - name = "ExpressionLocation"; - break; - case "loclist": - name = "LocationList"; - break; - case "loclistptr": - name = "LocationListPointer"; - break; - - case "loclistsptr": - name = "LocationListsPointer"; - break; - - case "addrptr": - name = "AddressPointer"; + attrType = "DwarfLocation"; + kind = AttributeKind.ValueType; + goto next; + case "constant": + hasConstant = true; break; + } + } - case "macptr": - name = "MacroPointer"; - break; + if (hasConstant) + { + attrType = "DwarfConstant"; + kind = AttributeKind.ValueType; + } + } - case "rnglist": - name = "RangeList"; - break; + next: - case "rangelistptr": - name = "RangeListPointer"; - break; + MapAttributeCompactNameToType.Add(attributeName.Replace("_", string.Empty), new AttributeMapping(rawName, attrType, kind)); - case "rnglistsptr": - name = "RangeListsPointer"; - break; + const int PaddingEncodingName = 50; - case "stroffsetsptr": - name = "StringOffsetPointer"; - break; - default: - throw new InvalidOperationException($"Unknown encoding {attrEncodingParts[i]}"); - } + for (; currentAttributeIndex < attributeIndex; currentAttributeIndex++) + { + fieldArrayBuilder.AppendLine($" {"DwarfAttributeEncoding.None",-PaddingEncodingName}, // 0x{currentAttributeIndex:x2} (undefined)"); + } - bool isLast = i + 1 == attrEncodingParts.Length; + for (int i = 2; i < attrEncodingParts.Length; i++) + { + string name; + switch (attrEncodingParts[i]) + { + case "string": + name = "String"; + break; + + case "flag": + name = "Flag"; + break; + + case "block": + name = "Block"; + break; + + case "reference": + name = "Reference"; + break; + + case "address": + name = "Address"; + break; + + case "constant": + name = "Constant"; + break; + + case "lineptr": + name = "LinePointer"; + break; + + case "exprloc": + name = "ExpressionLocation"; + break; + + case "loclist": + name = "LocationList"; + break; + + case "loclistptr": + name = "LocationListPointer"; + break; + + case "loclistsptr": + name = "LocationListsPointer"; + break; + + case "addrptr": + name = "AddressPointer"; + break; + + case "macptr": + name = "MacroPointer"; + break; + + case "rnglist": + name = "RangeList"; + break; + + case "rangelistptr": + name = "RangeListPointer"; + break; + + case "rnglistsptr": + name = "RangeListsPointer"; + break; + + case "stroffsetsptr": + name = "StringOffsetPointer"; + break; + default: + throw new InvalidOperationException($"Unknown encoding {attrEncodingParts[i]}"); + } - fieldArrayBuilder.Append($" {"DwarfAttributeEncoding." + name + (isLast ? "" : " | "),-PaddingEncodingName}"); + bool isLast = i + 1 == attrEncodingParts.Length; - if (isLast) - { - fieldArrayBuilder.Append($", // 0x{currentAttributeIndex:x2} {attributeName} "); - } - fieldArrayBuilder.AppendLine(); + fieldArrayBuilder.Append($" {"DwarfAttributeEncoding." + name + (isLast ? "" : " | "),-PaddingEncodingName}"); + if (isLast) + { + fieldArrayBuilder.Append($", // 0x{currentAttributeIndex:x2} {attributeName} "); } + fieldArrayBuilder.AppendLine(); - currentAttributeIndex++; } - fieldArrayBuilder.Append(" }"); - csField.InitValue = fieldArrayBuilder.ToString(); + currentAttributeIndex++; + } - Console.WriteLine(); - foreach (var key in alreadyDone.ToArray().OrderBy(x => x)) - { - Console.WriteLine(key); - } + fieldArrayBuilder.Append(" }"); + csField.InitValue = fieldArrayBuilder.ToString(); + + Console.WriteLine(); + foreach (var key in alreadyDone.ToArray().OrderBy(x => x)) + { + Console.WriteLine(key); } + } - struct AttributeMapping + struct AttributeMapping + { + public AttributeMapping(string rawName, string attributeType, AttributeKind kind) { - public AttributeMapping(string rawName, string attributeType, AttributeKind kind) - { - RawName = rawName; - AttributeType = attributeType; - Kind = kind; - } + RawName = rawName; + AttributeType = attributeType; + Kind = kind; + } - public string RawName { get; set; } + public string RawName { get; set; } - public string AttributeType { get; set; } + public string AttributeType { get; set; } - public AttributeKind Kind { get; set; } + public AttributeKind Kind { get; set; } - public override string ToString() - { - return $"{nameof(RawName)}: {RawName}, {nameof(AttributeType)}: {AttributeType}, {nameof(Kind)}: {Kind}"; - } + public override string ToString() + { + return $"{nameof(RawName)}: {RawName}, {nameof(AttributeType)}: {AttributeType}, {nameof(Kind)}: {Kind}"; } + } - enum AttributeKind - { - Managed, + enum AttributeKind + { + Managed, - ValueType, + ValueType, - Link, - } + Link, + } - private static void GenerateDwarfDIE(CSharpNamespace ns) - { - var file = File.ReadAllLines(@"C:\code\LibObjectFile\ext\dwarf-specs\attributesbytag.tex"); + private static void GenerateDwarfDIE(CSharpNamespace ns) + { + var file = File.ReadAllLines(@"C:\code\LibObjectFile\ext\dwarf-specs\attributesbytag.tex"); - var regexDWTag = new Regex(@"^\\(DWTAG\w+)"); - var regexDWAT = new Regex(@"^&\\(DWAT\w+)"); + var regexDWTag = new Regex(@"^\\(DWTAG\w+)"); + var regexDWAT = new Regex(@"^&\\(DWAT\w+)"); - int state = 0; + int state = 0; - string currentCompactTagName = null; - CSharpClass currentDIE = null; + string currentCompactTagName = null; + CSharpClass currentDIE = null; - var dieClasses = new List(); - var dieTags = new List(); + var dieClasses = new List(); + var dieTags = new List(); - foreach (var line in file) + foreach (var line in file) + { + if (state == 0) { - if (state == 0) + if (line.StartsWith(@"\begin{longtable}")) { - if (line.StartsWith(@"\begin{longtable}")) - { - continue; - } - else - { - state = 1; - } + continue; } - var match = regexDWTag.Match(line); - if (match.Success) + else { - var compactTagName = match.Groups[1].Value; - if (compactTagName == currentCompactTagName) - { - continue; - } - currentCompactTagName = compactTagName; - var fullTagName = MapTagCompactNameToFullName[compactTagName]; - dieTags.Add(fullTagName); - var csDIEName = fullTagName.Substring("DW_TAG_".Length); - csDIEName = CSharpifyName(csDIEName); - currentDIE = new CSharpClass($"DwarfDIE{csDIEName}"); - currentDIE.BaseTypes.Add(new CSharpFreeType("DwarfDIE")); - ns.Members.Add(currentDIE); - - var csConstructor = new CSharpMethod(string.Empty) - { - Kind = CSharpMethodKind.Constructor, - Body = (writer, element) => writer.WriteLine($"this.Tag = (DwarfTag)DwarfNative.{fullTagName};") - }; - currentDIE.Members.Add(csConstructor); - - dieClasses.Add(currentDIE); + state = 1; } - else + } + var match = regexDWTag.Match(line); + if (match.Success) + { + var compactTagName = match.Groups[1].Value; + if (compactTagName == currentCompactTagName) + { + continue; + } + currentCompactTagName = compactTagName; + var fullTagName = MapTagCompactNameToFullName[compactTagName]; + dieTags.Add(fullTagName); + var csDIEName = fullTagName.Substring("DW_TAG_".Length); + csDIEName = CSharpifyName(csDIEName); + currentDIE = new CSharpClass($"DwarfDIE{csDIEName}"); + currentDIE.BaseTypes.Add(new CSharpFreeType("DwarfDIE")); + ns.Members.Add(currentDIE); + + var csConstructor = new CSharpMethod(string.Empty) { - match = regexDWAT.Match(line); - if (match.Success) + Kind = CSharpMethodKind.Constructor, + Body = (writer, element) => writer.WriteLine($"this.Tag = (DwarfTag)DwarfNative.{fullTagName};") + }; + currentDIE.Members.Add(csConstructor); + + dieClasses.Add(currentDIE); + } + else + { + match = regexDWAT.Match(line); + if (match.Success) + { + var compactAttrName = match.Groups[1].Value; + var csProperty = CreatePropertyFromDwarfAttributeName(compactAttrName); + currentDIE.Members.Add(csProperty); + + // The DW_AT_description attribute can be used on any debugging information + // entry that may have a DW_AT_name attribute. For simplicity, this attribute is + // not explicitly shown. + if (compactAttrName == "DWATname") { - var compactAttrName = match.Groups[1].Value; - var csProperty = CreatePropertyFromDwarfAttributeName(compactAttrName); + csProperty = CreatePropertyFromDwarfAttributeName("DWATdescription"); currentDIE.Members.Add(csProperty); - - // The DW_AT_description attribute can be used on any debugging information - // entry that may have a DW_AT_name attribute. For simplicity, this attribute is - // not explicitly shown. - if (compactAttrName == "DWATname") - { - csProperty = CreatePropertyFromDwarfAttributeName("DWATdescription"); - currentDIE.Members.Add(csProperty); - } + } - } - else if (currentDIE != null && line.Contains("{DECL}")) - { - currentDIE.BaseTypes[0] = new CSharpFreeType("DwarfDIEDeclaration"); - } + } + else if (currentDIE != null && line.Contains("{DECL}")) + { + currentDIE.BaseTypes[0] = new CSharpFreeType("DwarfDIEDeclaration"); } } + } - // Generate the DIEHelper class - var dieHelperClass = new CSharpClass("DIEHelper") - { - Modifiers = CSharpModifiers.Partial | CSharpModifiers.Static, - Visibility = CSharpVisibility.Internal - }; - ns.Members.Add(dieHelperClass); - var dieHelperMethod = new CSharpMethod("ConvertTagToDwarfDIE") - { - Modifiers = CSharpModifiers.Static, - Visibility = CSharpVisibility.Public - }; - dieHelperClass.Members.Add(dieHelperMethod); + // Generate the DIEHelper class + var dieHelperClass = new CSharpClass("DIEHelper") + { + Modifiers = CSharpModifiers.Partial | CSharpModifiers.Static, + Visibility = CSharpVisibility.Internal + }; + ns.Members.Add(dieHelperClass); + var dieHelperMethod = new CSharpMethod("ConvertTagToDwarfDIE") + { + Modifiers = CSharpModifiers.Static, + Visibility = CSharpVisibility.Public + }; + dieHelperClass.Members.Add(dieHelperMethod); - dieHelperMethod.Parameters.Add(new CSharpParameter("tag") { ParameterType = CSharpPrimitiveType.UShort() }); - dieHelperMethod.ReturnType = new CSharpFreeType("DwarfDIE"); + dieHelperMethod.Parameters.Add(new CSharpParameter("tag") { ParameterType = CSharpPrimitiveType.UShort() }); + dieHelperMethod.ReturnType = new CSharpFreeType("DwarfDIE"); - dieHelperMethod.Body = (writer, element) => { + dieHelperMethod.Body = (writer, element) => { - writer.WriteLine("switch (tag)"); - writer.OpenBraceBlock(); - - for (var i = 0; i < dieClasses.Count; i++) - { - var dieCls = dieClasses[i]; - var dieTag = dieTags[i]; - writer.WriteLine($"case DwarfNative.{dieTag}:"); - writer.Indent(); - writer.WriteLine($"return new {dieCls.Name}();"); - writer.UnIndent(); - } - - writer.CloseBraceBlock(); - writer.WriteLine("return new DwarfDIE();"); - }; - } + writer.WriteLine("switch (tag)"); + writer.OpenBraceBlock(); - private static CSharpProperty CreatePropertyFromDwarfAttributeName(string compactAttrName) - { - if (compactAttrName == "DWATuseUTFeight") + for (var i = 0; i < dieClasses.Count; i++) { - compactAttrName = "DWATuseUTF8"; + var dieCls = dieClasses[i]; + var dieTag = dieTags[i]; + writer.WriteLine($"case DwarfNative.{dieTag}:"); + writer.Indent(); + writer.WriteLine($"return new {dieCls.Name}();"); + writer.UnIndent(); } - var map = MapAttributeCompactNameToType[compactAttrName]; - var rawAttrName = map.RawName; - var attrType = map.AttributeType; + writer.CloseBraceBlock(); + writer.WriteLine("return new DwarfDIE();"); + }; + } - var propertyName = CSharpifyName(map.RawName); + private static CSharpProperty CreatePropertyFromDwarfAttributeName(string compactAttrName) + { + if (compactAttrName == "DWATuseUTFeight") + { + compactAttrName = "DWATuseUTF8"; + } - var csProperty = new CSharpProperty(propertyName) - { - Visibility = CSharpVisibility.Public, - ReturnType = new CSharpNullableType(new CSharpFreeType(attrType)), - }; + var map = MapAttributeCompactNameToType[compactAttrName]; + var rawAttrName = map.RawName; + var attrType = map.AttributeType; - var attrName = CSharpifyName(rawAttrName); + var propertyName = CSharpifyName(map.RawName); - switch (map.Kind) - { - case AttributeKind.Managed: - csProperty.GetBody = (writer, element) => writer.WriteLine($"return GetAttributeValue<{attrType}>(DwarfAttributeKind.{attrName});"); - csProperty.SetBody = (writer, element) => writer.WriteLine($"SetAttributeValue<{attrType}>(DwarfAttributeKind.{attrName}, value);"); - break; - case AttributeKind.ValueType: - if (map.AttributeType == "DwarfConstant") - { - csProperty.GetBody = (writer, element) => writer.WriteLine($"return GetAttributeConstantOpt(DwarfAttributeKind.{attrName});"); - csProperty.SetBody = (writer, element) => writer.WriteLine($"SetAttributeConstantOpt(DwarfAttributeKind.{attrName}, value);"); - } - else if (map.AttributeType == "DwarfLocation") - { - csProperty.GetBody = (writer, element) => writer.WriteLine($"return GetAttributeLocationOpt(DwarfAttributeKind.{attrName});"); - csProperty.SetBody = (writer, element) => writer.WriteLine($"SetAttributeLocationOpt(DwarfAttributeKind.{attrName}, value);"); - } - else - { - csProperty.GetBody = (writer, element) => writer.WriteLine($"return GetAttributeValueOpt<{attrType}>(DwarfAttributeKind.{attrName});"); - csProperty.SetBody = (writer, element) => writer.WriteLine($"SetAttributeValueOpt<{attrType}>(DwarfAttributeKind.{attrName}, value);"); - } - break; - case AttributeKind.Link: - csProperty.GetBody = (writer, element) => - { - writer.WriteLine($"var attr = FindAttributeByKey(DwarfAttributeKind.{attrName});"); - writer.WriteLine($"return attr == null ? null : new {attrType}(attr.ValueAsU64, attr.ValueAsObject);"); - }; - csProperty.SetBody = (writer, element) => { writer.WriteLine($"SetAttributeLinkValue(DwarfAttributeKind.{attrName}, value);"); }; - break; - default: - throw new ArgumentOutOfRangeException(); - } + var csProperty = new CSharpProperty(propertyName) + { + Visibility = CSharpVisibility.Public, + ReturnType = new CSharpNullableType(new CSharpFreeType(attrType)), + }; - return csProperty; - } + var attrName = CSharpifyName(rawAttrName); - private static string CSharpifyName(string rawName) + switch (map.Kind) { - if (rawName.EndsWith("_pc")) - { - rawName = rawName.Replace("_pc", "_PC"); - } - - var newName = new StringBuilder(); - bool upperCase = true; - for (var i = 0; i < rawName.Length; i++) - { - var c = rawName[i]; - if (c == '_') + case AttributeKind.Managed: + csProperty.GetBody = (writer, element) => writer.WriteLine($"return GetAttributeValue<{attrType}>(DwarfAttributeKind.{attrName});"); + csProperty.SetBody = (writer, element) => writer.WriteLine($"SetAttributeValue<{attrType}>(DwarfAttributeKind.{attrName}, value);"); + break; + case AttributeKind.ValueType: + if (map.AttributeType == "DwarfConstant") { - upperCase = true; - continue; + csProperty.GetBody = (writer, element) => writer.WriteLine($"return GetAttributeConstantOpt(DwarfAttributeKind.{attrName});"); + csProperty.SetBody = (writer, element) => writer.WriteLine($"SetAttributeConstantOpt(DwarfAttributeKind.{attrName}, value);"); } - - if (upperCase) + else if (map.AttributeType == "DwarfLocation") { - newName.Append(char.ToUpperInvariant(c)); - upperCase = false; + csProperty.GetBody = (writer, element) => writer.WriteLine($"return GetAttributeLocationOpt(DwarfAttributeKind.{attrName});"); + csProperty.SetBody = (writer, element) => writer.WriteLine($"SetAttributeLocationOpt(DwarfAttributeKind.{attrName}, value);"); } else { - newName.Append(c); + csProperty.GetBody = (writer, element) => writer.WriteLine($"return GetAttributeValueOpt<{attrType}>(DwarfAttributeKind.{attrName});"); + csProperty.SetBody = (writer, element) => writer.WriteLine($"SetAttributeValueOpt<{attrType}>(DwarfAttributeKind.{attrName}, value);"); } - } - return newName.ToString(); + break; + case AttributeKind.Link: + csProperty.GetBody = (writer, element) => + { + writer.WriteLine($"var attr = FindAttributeByKey(DwarfAttributeKind.{attrName});"); + writer.WriteLine($"return attr == null ? null : new {attrType}(attr.ValueAsU64, attr.ValueAsObject);"); + }; + csProperty.SetBody = (writer, element) => { writer.WriteLine($"SetAttributeLinkValue(DwarfAttributeKind.{attrName}, value);"); }; + break; + default: + throw new ArgumentOutOfRangeException(); } + return csProperty; + } - private static Dictionary MapTagCompactNameToFullName = new Dictionary() + private static string CSharpifyName(string rawName) + { + if (rawName.EndsWith("_pc")) { - {"DWTAGaccessdeclaration", "DW_TAG_access_declaration"}, - {"DWTAGarraytype", "DW_TAG_array_type"}, - {"DWTAGatomictype", "DW_TAG_atomic_type"}, - {"DWTAGbasetype", "DW_TAG_base_type"}, - {"DWTAGcallsite", "DW_TAG_call_site"}, - {"DWTAGcallsiteparameter", "DW_TAG_call_site_parameter"}, - {"DWTAGcatchblock", "DW_TAG_catch_block"}, - {"DWTAGclasstype", "DW_TAG_class_type"}, - {"DWTAGcoarraytype", "DW_TAG_coarray_type"}, - {"DWTAGcommonblock", "DW_TAG_common_block"}, - {"DWTAGcommoninclusion", "DW_TAG_common_inclusion"}, - {"DWTAGcompileunit", "DW_TAG_compile_unit"}, - {"DWTAGcondition", "DW_TAG_condition"}, - {"DWTAGconsttype", "DW_TAG_const_type"}, - {"DWTAGconstant", "DW_TAG_constant"}, - {"DWTAGdescriptortype", "DW_TAG_descriptor_type"}, - {"DWTAGdwarfprocedure", "DW_TAG_dwarf_procedure"}, - {"DWTAGdynamictype", "DW_TAG_dynamic_type"}, - {"DWTAGentrypoint", "DW_TAG_entry_point"}, - {"DWTAGenumerationtype", "DW_TAG_enumeration_type"}, - {"DWTAGenumerator", "DW_TAG_enumerator"}, - {"DWTAGfiletype", "DW_TAG_file_type"}, - {"DWTAGformalparameter", "DW_TAG_formal_parameter"}, - {"DWTAGfriend", "DW_TAG_friend"}, - {"DWTAGgenericsubrange", "DW_TAG_generic_subrange"}, - {"DWTAGhiuser", "DW_TAG_hi_user"}, - {"DWTAGimmutabletype", "DW_TAG_immutable_type"}, - {"DWTAGimporteddeclaration", "DW_TAG_imported_declaration"}, - {"DWTAGimportedmodule", "DW_TAG_imported_module"}, - {"DWTAGimportedunit", "DW_TAG_imported_unit"}, - {"DWTAGinheritance", "DW_TAG_inheritance"}, - {"DWTAGinlinedsubroutine", "DW_TAG_inlined_subroutine"}, - {"DWTAGinterfacetype", "DW_TAG_interface_type"}, - {"DWTAGlabel", "DW_TAG_label"}, - {"DWTAGlexicalblock", "DW_TAG_lexical_block"}, - {"DWTAGlouser", "DW_TAG_lo_user"}, - {"DWTAGmember", "DW_TAG_member"}, - {"DWTAGmodule", "DW_TAG_module"}, - {"DWTAGnamelist", "DW_TAG_namelist"}, - {"DWTAGnamelistitem", "DW_TAG_namelist_item"}, - {"DWTAGnamespace", "DW_TAG_namespace"}, - {"DWTAGpackedtype", "DW_TAG_packed_type"}, - {"DWTAGpartialunit", "DW_TAG_partial_unit"}, - {"DWTAGpointertype", "DW_TAG_pointer_type"}, - {"DWTAGptrtomembertype", "DW_TAG_ptr_to_member_type"}, - {"DWTAGreferencetype", "DW_TAG_reference_type"}, - {"DWTAGrestricttype", "DW_TAG_restrict_type"}, - {"DWTAGrvaluereferencetype", "DW_TAG_rvalue_reference_type"}, - {"DWTAGsettype", "DW_TAG_set_type"}, - {"DWTAGsharedtype", "DW_TAG_shared_type"}, - {"DWTAGskeletonunit", "DW_TAG_skeleton_unit"}, - {"DWTAGstringtype", "DW_TAG_string_type"}, - {"DWTAGstructuretype", "DW_TAG_structure_type"}, - {"DWTAGsubprogram", "DW_TAG_subprogram"}, - {"DWTAGsubrangetype", "DW_TAG_subrange_type"}, - {"DWTAGsubroutinetype", "DW_TAG_subroutine_type"}, - {"DWTAGtemplatealias", "DW_TAG_template_alias"}, - {"DWTAGtemplatetypeparameter", "DW_TAG_template_type_parameter"}, - {"DWTAGtemplatevalueparameter", "DW_TAG_template_value_parameter"}, - {"DWTAGthrowntype", "DW_TAG_thrown_type"}, - {"DWTAGtryblock", "DW_TAG_try_block"}, - {"DWTAGtypedef", "DW_TAG_typedef"}, - {"DWTAGtypeunit", "DW_TAG_type_unit"}, - {"DWTAGuniontype", "DW_TAG_union_type"}, - {"DWTAGunspecifiedparameters", "DW_TAG_unspecified_parameters"}, - {"DWTAGunspecifiedtype", "DW_TAG_unspecified_type"}, - {"DWTAGvariable", "DW_TAG_variable"}, - {"DWTAGvariant", "DW_TAG_variant"}, - {"DWTAGvariantpart", "DW_TAG_variant_part"}, - {"DWTAGvolatiletype", "DW_TAG_volatile_type"}, - {"DWTAGwithstmt", "DW_TAG_with_stmt"}, - }; + rawName = rawName.Replace("_pc", "_PC"); + } - // Extract from Dwarf 5 specs - Figure 20, Attribute encodings - private static readonly string[] MapAttributeToEncoding = new string[] + var newName = new StringBuilder(); + bool upperCase = true; + for (var i = 0; i < rawName.Length; i++) { - "DW_AT_sibling 0x01 reference", - "DW_AT_location 0x02 exprloc loclist", - "DW_AT_name 0x03 string", - "DW_AT_ordering 0x09 constant", - "DW_AT_byte_size 0x0b constant exprloc reference", - "DW_AT_bit_offset 0x0c constant exprloc reference", - "DW_AT_bit_size 0x0d constant exprloc reference", - "DW_AT_stmt_list 0x10 lineptr", - "DW_AT_low_pc 0x11 address", - "DW_AT_high_pc 0x12 address constant", - "DW_AT_language 0x13 constant", - "DW_AT_discr 0x15 reference", - "DW_AT_discr_value 0x16 constant", - "DW_AT_visibility 0x17 constant", - "DW_AT_import 0x18 reference", - "DW_AT_string_length 0x19 exprloc loclistptr", - "DW_AT_common_reference 0x1a reference", - "DW_AT_comp_dir 0x1b string", - "DW_AT_const_value 0x1c block constant string ", - "DW_AT_containing_type 0x1d reference", - "DW_AT_default_value 0x1e reference", - "DW_AT_inline 0x20 constant", - "DW_AT_is_optional 0x21 flag", - "DW_AT_lower_bound 0x22 constant exprloc reference", - "DW_AT_producer 0x25 string", - "DW_AT_prototyped 0x27 flag", - "DW_AT_return_addr 0x2a exprloc loclistptr", - "DW_AT_start_scope 0x2c constant rangelistptr", - "DW_AT_bit_stride 0x2e constant exprloc reference", - "DW_AT_upper_bound 0x2f constant exprloc reference", - "DW_AT_abstract_origin 0x31 reference", - "DW_AT_accessibility 0x32 constant", - "DW_AT_address_class 0x33 constant", - "DW_AT_artificial 0x34 flag", - "DW_AT_base_types 0x35 reference", - "DW_AT_calling_convention 0x36 constant", - "DW_AT_count 0x37 constant exprloc reference", - "DW_AT_data_member_location 0x38 constant exprloc loclistptr", - "DW_AT_decl_column 0x39 constant", - "DW_AT_decl_file 0x3a constant", - "DW_AT_decl_line 0x3b constant", - "DW_AT_declaration 0x3c flag", - "DW_AT_discr_list 0x3d block", - "DW_AT_encoding 0x3e constant", - "DW_AT_external 0x3f flag", - "DW_AT_frame_base 0x40 exprloc loclistptr", - "DW_AT_friend 0x41 reference", - "DW_AT_identifier_case 0x42 constant", - "DW_AT_macro_info 0x43 macptr", - "DW_AT_namelist_item 0x44 reference", - "DW_AT_priority 0x45 reference", - "DW_AT_segment 0x46 exprloc loclistptr", - "DW_AT_specification 0x47 reference", - "DW_AT_static_link 0x48 exprloc loclistptr", - "DW_AT_type 0x49 reference", - "DW_AT_use_location 0x4a exprloc loclistptr", - "DW_AT_variable_parameter 0x4b flag", - "DW_AT_virtuality 0x4c constant", - "DW_AT_vtable_elem_location 0x4d exprloc loclistptr ", - "DW_AT_allocated 0x4e constant exprloc reference", - "DW_AT_associated 0x4f constant exprloc reference", - "DW_AT_data_location 0x50 exprloc", - "DW_AT_byte_stride 0x51 constant exprloc reference", - "DW_AT_entry_pc 0x52 address", - "DW_AT_use_UTF8 0x53 flag", - "DW_AT_extension 0x54 reference", - "DW_AT_ranges 0x55 rangelistptr", - "DW_AT_trampoline 0x56 address flag reference string", - "DW_AT_call_column 0x57 constant", - "DW_AT_call_file 0x58 constant", - "DW_AT_call_line 0x59 constant", - "DW_AT_description 0x5a string", - "DW_AT_binary_scale 0x5b constant", - "DW_AT_decimal_scale 0x5c constant", - "DW_AT_small 0x5d reference", - "DW_AT_decimal_sign 0x5e constant", - "DW_AT_digit_count 0x5f constant", - "DW_AT_picture_string 0x60 string", - "DW_AT_mutable 0x61 flag ", - "DW_AT_threads_scaled 0x62 flag", - "DW_AT_explicit 0x63 flag", - "DW_AT_object_pointer 0x64 reference", - "DW_AT_endianity 0x65 constant", - "DW_AT_elemental 0x66 flag", - "DW_AT_pure 0x67 flag", - "DW_AT_recursive 0x68 flag", - "DW_AT_signature 0x69 reference", - "DW_AT_main_subprogram 0x6a flag", - "DW_AT_data_bit_offset 0x6b constant", - "DW_AT_const_expr 0x6c flag", - "DW_AT_enum_class 0x6d flag", - "DW_AT_linkage_name 0x6e string ", - "DW_AT_string_length_bit_size 0x6f constant", - "DW_AT_string_length_byte_size 0x70 constant", - "DW_AT_rank 0x71 constant exprloc", - "DW_AT_str_offsets_base 0x72 stroffsetsptr", - "DW_AT_addr_base 0x73 addrptr", - "DW_AT_rnglists_base 0x74 rnglistsptr", - "DW_AT_dwo_name 0x76 string", - "DW_AT_reference 0x77 flag", - "DW_AT_rvalue_reference 0x78 flag", - "DW_AT_macros 0x79 macptr", - "DW_AT_call_all_calls 0x7a flag", - "DW_AT_call_all_source_calls 0x7b flag", - "DW_AT_call_all_tail_calls 0x7c flag", - "DW_AT_call_return_pc 0x7d address", - "DW_AT_call_value 0x7e exprloc", - "DW_AT_call_origin 0x7f exprloc", - "DW_AT_call_parameter 0x80 reference", - "DW_AT_call_pc 0x81 address", - "DW_AT_call_tail_call 0x82 flag", - "DW_AT_call_target 0x83 exprloc", - "DW_AT_call_target_clobbered 0x84 exprloc", - "DW_AT_call_data_location 0x85 exprloc", - "DW_AT_call_data_value 0x86 exprloc", - "DW_AT_noreturn 0x87 flag", - "DW_AT_alignment 0x88 constant", - "DW_AT_export_symbols 0x89 flag", - "DW_AT_deleted 0x8a flag", - "DW_AT_defaulted 0x8b constant", - "DW_AT_loclists_base 0x8c loclistsptr", - }; + var c = rawName[i]; + if (c == '_') + { + upperCase = true; + continue; + } + + if (upperCase) + { + newName.Append(char.ToUpperInvariant(c)); + upperCase = false; + } + else + { + newName.Append(c); + } + } + return newName.ToString(); } + + + private static Dictionary MapTagCompactNameToFullName = new Dictionary() + { + {"DWTAGaccessdeclaration", "DW_TAG_access_declaration"}, + {"DWTAGarraytype", "DW_TAG_array_type"}, + {"DWTAGatomictype", "DW_TAG_atomic_type"}, + {"DWTAGbasetype", "DW_TAG_base_type"}, + {"DWTAGcallsite", "DW_TAG_call_site"}, + {"DWTAGcallsiteparameter", "DW_TAG_call_site_parameter"}, + {"DWTAGcatchblock", "DW_TAG_catch_block"}, + {"DWTAGclasstype", "DW_TAG_class_type"}, + {"DWTAGcoarraytype", "DW_TAG_coarray_type"}, + {"DWTAGcommonblock", "DW_TAG_common_block"}, + {"DWTAGcommoninclusion", "DW_TAG_common_inclusion"}, + {"DWTAGcompileunit", "DW_TAG_compile_unit"}, + {"DWTAGcondition", "DW_TAG_condition"}, + {"DWTAGconsttype", "DW_TAG_const_type"}, + {"DWTAGconstant", "DW_TAG_constant"}, + {"DWTAGdescriptortype", "DW_TAG_descriptor_type"}, + {"DWTAGdwarfprocedure", "DW_TAG_dwarf_procedure"}, + {"DWTAGdynamictype", "DW_TAG_dynamic_type"}, + {"DWTAGentrypoint", "DW_TAG_entry_point"}, + {"DWTAGenumerationtype", "DW_TAG_enumeration_type"}, + {"DWTAGenumerator", "DW_TAG_enumerator"}, + {"DWTAGfiletype", "DW_TAG_file_type"}, + {"DWTAGformalparameter", "DW_TAG_formal_parameter"}, + {"DWTAGfriend", "DW_TAG_friend"}, + {"DWTAGgenericsubrange", "DW_TAG_generic_subrange"}, + {"DWTAGhiuser", "DW_TAG_hi_user"}, + {"DWTAGimmutabletype", "DW_TAG_immutable_type"}, + {"DWTAGimporteddeclaration", "DW_TAG_imported_declaration"}, + {"DWTAGimportedmodule", "DW_TAG_imported_module"}, + {"DWTAGimportedunit", "DW_TAG_imported_unit"}, + {"DWTAGinheritance", "DW_TAG_inheritance"}, + {"DWTAGinlinedsubroutine", "DW_TAG_inlined_subroutine"}, + {"DWTAGinterfacetype", "DW_TAG_interface_type"}, + {"DWTAGlabel", "DW_TAG_label"}, + {"DWTAGlexicalblock", "DW_TAG_lexical_block"}, + {"DWTAGlouser", "DW_TAG_lo_user"}, + {"DWTAGmember", "DW_TAG_member"}, + {"DWTAGmodule", "DW_TAG_module"}, + {"DWTAGnamelist", "DW_TAG_namelist"}, + {"DWTAGnamelistitem", "DW_TAG_namelist_item"}, + {"DWTAGnamespace", "DW_TAG_namespace"}, + {"DWTAGpackedtype", "DW_TAG_packed_type"}, + {"DWTAGpartialunit", "DW_TAG_partial_unit"}, + {"DWTAGpointertype", "DW_TAG_pointer_type"}, + {"DWTAGptrtomembertype", "DW_TAG_ptr_to_member_type"}, + {"DWTAGreferencetype", "DW_TAG_reference_type"}, + {"DWTAGrestricttype", "DW_TAG_restrict_type"}, + {"DWTAGrvaluereferencetype", "DW_TAG_rvalue_reference_type"}, + {"DWTAGsettype", "DW_TAG_set_type"}, + {"DWTAGsharedtype", "DW_TAG_shared_type"}, + {"DWTAGskeletonunit", "DW_TAG_skeleton_unit"}, + {"DWTAGstringtype", "DW_TAG_string_type"}, + {"DWTAGstructuretype", "DW_TAG_structure_type"}, + {"DWTAGsubprogram", "DW_TAG_subprogram"}, + {"DWTAGsubrangetype", "DW_TAG_subrange_type"}, + {"DWTAGsubroutinetype", "DW_TAG_subroutine_type"}, + {"DWTAGtemplatealias", "DW_TAG_template_alias"}, + {"DWTAGtemplatetypeparameter", "DW_TAG_template_type_parameter"}, + {"DWTAGtemplatevalueparameter", "DW_TAG_template_value_parameter"}, + {"DWTAGthrowntype", "DW_TAG_thrown_type"}, + {"DWTAGtryblock", "DW_TAG_try_block"}, + {"DWTAGtypedef", "DW_TAG_typedef"}, + {"DWTAGtypeunit", "DW_TAG_type_unit"}, + {"DWTAGuniontype", "DW_TAG_union_type"}, + {"DWTAGunspecifiedparameters", "DW_TAG_unspecified_parameters"}, + {"DWTAGunspecifiedtype", "DW_TAG_unspecified_type"}, + {"DWTAGvariable", "DW_TAG_variable"}, + {"DWTAGvariant", "DW_TAG_variant"}, + {"DWTAGvariantpart", "DW_TAG_variant_part"}, + {"DWTAGvolatiletype", "DW_TAG_volatile_type"}, + {"DWTAGwithstmt", "DW_TAG_with_stmt"}, + }; + + // Extract from Dwarf 5 specs - Figure 20, Attribute encodings + private static readonly string[] MapAttributeToEncoding = new string[] + { + "DW_AT_sibling 0x01 reference", + "DW_AT_location 0x02 exprloc loclist", + "DW_AT_name 0x03 string", + "DW_AT_ordering 0x09 constant", + "DW_AT_byte_size 0x0b constant exprloc reference", + "DW_AT_bit_offset 0x0c constant exprloc reference", + "DW_AT_bit_size 0x0d constant exprloc reference", + "DW_AT_stmt_list 0x10 lineptr", + "DW_AT_low_pc 0x11 address", + "DW_AT_high_pc 0x12 address constant", + "DW_AT_language 0x13 constant", + "DW_AT_discr 0x15 reference", + "DW_AT_discr_value 0x16 constant", + "DW_AT_visibility 0x17 constant", + "DW_AT_import 0x18 reference", + "DW_AT_string_length 0x19 exprloc loclistptr", + "DW_AT_common_reference 0x1a reference", + "DW_AT_comp_dir 0x1b string", + "DW_AT_const_value 0x1c block constant string ", + "DW_AT_containing_type 0x1d reference", + "DW_AT_default_value 0x1e reference", + "DW_AT_inline 0x20 constant", + "DW_AT_is_optional 0x21 flag", + "DW_AT_lower_bound 0x22 constant exprloc reference", + "DW_AT_producer 0x25 string", + "DW_AT_prototyped 0x27 flag", + "DW_AT_return_addr 0x2a exprloc loclistptr", + "DW_AT_start_scope 0x2c constant rangelistptr", + "DW_AT_bit_stride 0x2e constant exprloc reference", + "DW_AT_upper_bound 0x2f constant exprloc reference", + "DW_AT_abstract_origin 0x31 reference", + "DW_AT_accessibility 0x32 constant", + "DW_AT_address_class 0x33 constant", + "DW_AT_artificial 0x34 flag", + "DW_AT_base_types 0x35 reference", + "DW_AT_calling_convention 0x36 constant", + "DW_AT_count 0x37 constant exprloc reference", + "DW_AT_data_member_location 0x38 constant exprloc loclistptr", + "DW_AT_decl_column 0x39 constant", + "DW_AT_decl_file 0x3a constant", + "DW_AT_decl_line 0x3b constant", + "DW_AT_declaration 0x3c flag", + "DW_AT_discr_list 0x3d block", + "DW_AT_encoding 0x3e constant", + "DW_AT_external 0x3f flag", + "DW_AT_frame_base 0x40 exprloc loclistptr", + "DW_AT_friend 0x41 reference", + "DW_AT_identifier_case 0x42 constant", + "DW_AT_macro_info 0x43 macptr", + "DW_AT_namelist_item 0x44 reference", + "DW_AT_priority 0x45 reference", + "DW_AT_segment 0x46 exprloc loclistptr", + "DW_AT_specification 0x47 reference", + "DW_AT_static_link 0x48 exprloc loclistptr", + "DW_AT_type 0x49 reference", + "DW_AT_use_location 0x4a exprloc loclistptr", + "DW_AT_variable_parameter 0x4b flag", + "DW_AT_virtuality 0x4c constant", + "DW_AT_vtable_elem_location 0x4d exprloc loclistptr ", + "DW_AT_allocated 0x4e constant exprloc reference", + "DW_AT_associated 0x4f constant exprloc reference", + "DW_AT_data_location 0x50 exprloc", + "DW_AT_byte_stride 0x51 constant exprloc reference", + "DW_AT_entry_pc 0x52 address", + "DW_AT_use_UTF8 0x53 flag", + "DW_AT_extension 0x54 reference", + "DW_AT_ranges 0x55 rangelistptr", + "DW_AT_trampoline 0x56 address flag reference string", + "DW_AT_call_column 0x57 constant", + "DW_AT_call_file 0x58 constant", + "DW_AT_call_line 0x59 constant", + "DW_AT_description 0x5a string", + "DW_AT_binary_scale 0x5b constant", + "DW_AT_decimal_scale 0x5c constant", + "DW_AT_small 0x5d reference", + "DW_AT_decimal_sign 0x5e constant", + "DW_AT_digit_count 0x5f constant", + "DW_AT_picture_string 0x60 string", + "DW_AT_mutable 0x61 flag ", + "DW_AT_threads_scaled 0x62 flag", + "DW_AT_explicit 0x63 flag", + "DW_AT_object_pointer 0x64 reference", + "DW_AT_endianity 0x65 constant", + "DW_AT_elemental 0x66 flag", + "DW_AT_pure 0x67 flag", + "DW_AT_recursive 0x68 flag", + "DW_AT_signature 0x69 reference", + "DW_AT_main_subprogram 0x6a flag", + "DW_AT_data_bit_offset 0x6b constant", + "DW_AT_const_expr 0x6c flag", + "DW_AT_enum_class 0x6d flag", + "DW_AT_linkage_name 0x6e string ", + "DW_AT_string_length_bit_size 0x6f constant", + "DW_AT_string_length_byte_size 0x70 constant", + "DW_AT_rank 0x71 constant exprloc", + "DW_AT_str_offsets_base 0x72 stroffsetsptr", + "DW_AT_addr_base 0x73 addrptr", + "DW_AT_rnglists_base 0x74 rnglistsptr", + "DW_AT_dwo_name 0x76 string", + "DW_AT_reference 0x77 flag", + "DW_AT_rvalue_reference 0x78 flag", + "DW_AT_macros 0x79 macptr", + "DW_AT_call_all_calls 0x7a flag", + "DW_AT_call_all_source_calls 0x7b flag", + "DW_AT_call_all_tail_calls 0x7c flag", + "DW_AT_call_return_pc 0x7d address", + "DW_AT_call_value 0x7e exprloc", + "DW_AT_call_origin 0x7f exprloc", + "DW_AT_call_parameter 0x80 reference", + "DW_AT_call_pc 0x81 address", + "DW_AT_call_tail_call 0x82 flag", + "DW_AT_call_target 0x83 exprloc", + "DW_AT_call_target_clobbered 0x84 exprloc", + "DW_AT_call_data_location 0x85 exprloc", + "DW_AT_call_data_value 0x86 exprloc", + "DW_AT_noreturn 0x87 flag", + "DW_AT_alignment 0x88 constant", + "DW_AT_export_symbols 0x89 flag", + "DW_AT_deleted 0x8a flag", + "DW_AT_defaulted 0x8b constant", + "DW_AT_loclists_base 0x8c loclistsptr", + }; } \ No newline at end of file diff --git a/src/LibObjectFile.CodeGen/Program.cs b/src/LibObjectFile.CodeGen/Program.cs index efc9681..0e4182a 100644 --- a/src/LibObjectFile.CodeGen/Program.cs +++ b/src/LibObjectFile.CodeGen/Program.cs @@ -1,6 +1,7 @@ // 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; using System.IO; @@ -10,319 +11,318 @@ using CppAst.CodeGen.CSharp; using Zio.FileSystems; -namespace LibObjectFile.CodeGen +namespace LibObjectFile.CodeGen; + +partial class Program { - partial class Program - { - private const string SrcFolderRelative = @"..\..\..\..\.."; + private const string SrcFolderRelative = @"..\..\..\..\.."; - static void Main(string[] args) - { - GenerateElf(); - GenerateDwarf(); - } + static void Main(string[] args) + { + GenerateElf(); + GenerateDwarf(); + } - private static CodeWriter GetCodeWriter(string subPath) - { - var fs = new PhysicalFileSystem(); - var destFolder = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, SrcFolderRelative, subPath)); - var subfs = new SubFileSystem(fs, fs.ConvertPathFromInternal(destFolder)); - var codeWriter = new CodeWriter(new CodeWriterOptions(subfs)); - return codeWriter; - } + private static CodeWriter GetCodeWriter(string subPath) + { + var fs = new PhysicalFileSystem(); + var destFolder = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, SrcFolderRelative, subPath)); + var subfs = new SubFileSystem(fs, fs.ConvertPathFromInternal(destFolder)); + var codeWriter = new CodeWriter(new CodeWriterOptions(subfs)); + return codeWriter; + } - private static void AssertCompilation(CSharpCompilation csCompilation) + private static void AssertCompilation(CSharpCompilation csCompilation) + { + if (csCompilation.HasErrors) { - if (csCompilation.HasErrors) + foreach (var message in csCompilation.Diagnostics.Messages) { - foreach (var message in csCompilation.Diagnostics.Messages) - { - Console.Error.WriteLine(message); - } - Console.Error.WriteLine("Unexpected parsing errors"); - Environment.Exit(1); + Console.Error.WriteLine(message); } + Console.Error.WriteLine("Unexpected parsing errors"); + Environment.Exit(1); } + } - private static void GenerateElf() + private static void GenerateElf() + { + var cppOptions = new CSharpConverterOptions() { - var cppOptions = new CSharpConverterOptions() + DefaultClassLib = "ElfNative", + DefaultNamespace = "LibObjectFile.Elf", + DefaultOutputFilePath = "/LibObjectFile.Elf.generated.cs", + DefaultDllImportNameAndArguments = "NotUsed", + MappingRules = { - DefaultClassLib = "ElfNative", - DefaultNamespace = "LibObjectFile.Elf", - DefaultOutputFilePath = "/LibObjectFile.Elf.generated.cs", - DefaultDllImportNameAndArguments = "NotUsed", - MappingRules = - { - map => map.MapMacroToConst("^EI.*", "uint8_t"), - map => map.MapMacroToConst("^ELFMAG\\d", "uint8_t"), - map => map.MapMacroToConst("^ELFCLASS.*", "uint8_t"), - map => map.MapMacroToConst("^ELFDATA.*", "uint8_t"), - map => map.MapMacroToConst("^ELFOSABI.*", "uint8_t"), - map => map.MapMacroToConst("^ET_.*", "uint16_t"), - map => map.MapMacroToConst("^EM_.*", "uint16_t"), - map => map.MapMacroToConst("^EV_.*", "uint8_t"), - map => map.MapMacroToConst("^SHN_.*", "uint32_t"), - map => map.MapMacroToConst("^SHT_.*", "uint32_t"), - map => map.MapMacroToConst("^SHF_.*", "uint32_t"), - map => map.MapMacroToConst("^EF_.*", "uint32_t"), - map => map.MapMacroToConst("^PT_.*", "uint32_t"), - map => map.MapMacroToConst("^PF_.*", "uint32_t"), - map => map.MapMacroToConst("^NT_.*", "uint32_t"), - map => map.MapMacroToConst("^DT_.*", "int32_t"), - map => map.MapMacroToConst("^DF_.*", "uint32_t"), - map => map.MapMacroToConst("^DTF_.*", "uint32_t"), - map => map.MapMacroToConst("^VER_DEF_.*", "uint16_t"), - map => map.MapMacroToConst("^VER_FLG_.*", "uint16_t"), - map => map.MapMacroToConst("^VER_NDX_.*", "uint16_t"), - map => map.MapMacroToConst("^VER_NEED_.*", "uint16_t"), - map => map.MapMacroToConst("^ELFCOMPRESS_.*", "int32_t"), - map => map.MapMacroToConst("^SYMINFO_.*", "uint16_t"), - map => map.MapMacroToConst("^STB_.*", "uint8_t"), - map => map.MapMacroToConst("^STT_.*", "uint8_t"), - map => map.MapMacroToConst("^STN_.*", "uint8_t"), - map => map.MapMacroToConst("^STV_.*", "uint8_t"), - map => map.MapMacroToConst("^R_.*", "uint32_t"), - map => map.MapMacroToConst("ELF_NOTE_OS_.*", "uint32_t"), - } - }; + map => map.MapMacroToConst("^EI.*", "uint8_t"), + map => map.MapMacroToConst("^ELFMAG\\d", "uint8_t"), + map => map.MapMacroToConst("^ELFCLASS.*", "uint8_t"), + map => map.MapMacroToConst("^ELFDATA.*", "uint8_t"), + map => map.MapMacroToConst("^ELFOSABI.*", "uint8_t"), + map => map.MapMacroToConst("^ET_.*", "uint16_t"), + map => map.MapMacroToConst("^EM_.*", "uint16_t"), + map => map.MapMacroToConst("^EV_.*", "uint8_t"), + map => map.MapMacroToConst("^SHN_.*", "uint32_t"), + map => map.MapMacroToConst("^SHT_.*", "uint32_t"), + map => map.MapMacroToConst("^SHF_.*", "uint32_t"), + map => map.MapMacroToConst("^EF_.*", "uint32_t"), + map => map.MapMacroToConst("^PT_.*", "uint32_t"), + map => map.MapMacroToConst("^PF_.*", "uint32_t"), + map => map.MapMacroToConst("^NT_.*", "uint32_t"), + map => map.MapMacroToConst("^DT_.*", "int32_t"), + map => map.MapMacroToConst("^DF_.*", "uint32_t"), + map => map.MapMacroToConst("^DTF_.*", "uint32_t"), + map => map.MapMacroToConst("^VER_DEF_.*", "uint16_t"), + map => map.MapMacroToConst("^VER_FLG_.*", "uint16_t"), + map => map.MapMacroToConst("^VER_NDX_.*", "uint16_t"), + map => map.MapMacroToConst("^VER_NEED_.*", "uint16_t"), + map => map.MapMacroToConst("^ELFCOMPRESS_.*", "int32_t"), + map => map.MapMacroToConst("^SYMINFO_.*", "uint16_t"), + map => map.MapMacroToConst("^STB_.*", "uint8_t"), + map => map.MapMacroToConst("^STT_.*", "uint8_t"), + map => map.MapMacroToConst("^STN_.*", "uint8_t"), + map => map.MapMacroToConst("^STV_.*", "uint8_t"), + map => map.MapMacroToConst("^R_.*", "uint32_t"), + map => map.MapMacroToConst("ELF_NOTE_OS_.*", "uint32_t"), + } + }; - cppOptions.ConfigureForWindowsMsvc(CppTargetCpu.X86_64); - cppOptions.Defines.Add("_AMD64_"); - cppOptions.Defines.Add("_TARGET_AMD64_"); - cppOptions.Defines.Add("STARK_NO_ENUM_FLAG"); - cppOptions.GenerateEnumItemAsFields = false; - cppOptions.IncludeFolders.Add(Environment.CurrentDirectory); + cppOptions.ConfigureForWindowsMsvc(CppTargetCpu.X86_64); + cppOptions.Defines.Add("_AMD64_"); + cppOptions.Defines.Add("_TARGET_AMD64_"); + cppOptions.Defines.Add("STARK_NO_ENUM_FLAG"); + cppOptions.GenerateEnumItemAsFields = false; + cppOptions.IncludeFolders.Add(Environment.CurrentDirectory); - var csCompilation = CSharpConverter.Convert(@"#include ""elf.h""", cppOptions); + var csCompilation = CSharpConverter.Convert(@"#include ""elf.h""", cppOptions); - AssertCompilation(csCompilation); + AssertCompilation(csCompilation); - // Add pragma - var csFile = csCompilation.Members.OfType().First(); - var ns = csFile.Members.OfType().First(); - csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable 1591") ); - csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable CS8765")); - csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#nullable enable")); + // Add pragma + var csFile = csCompilation.Members.OfType().First(); + var ns = csFile.Members.OfType().First(); + csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable 1591") ); + csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#pragma warning disable CS8765")); + csFile.Members.Insert(csFile.Members.IndexOf(ns), new CSharpLineElement("#nullable enable")); - ProcessEnum(cppOptions, csCompilation, "EM_", "ElfArch"); - ProcessEnum(cppOptions, csCompilation, "ELFOSABI_", "ElfOSABI"); - ProcessEnum(cppOptions, csCompilation, "R_", "ElfRelocationType"); - ProcessEnum(cppOptions, csCompilation, "NT_", "ElfNoteType"); + ProcessEnum(cppOptions, csCompilation, "EM_", "ElfArch"); + ProcessEnum(cppOptions, csCompilation, "ELFOSABI_", "ElfOSABI"); + ProcessEnum(cppOptions, csCompilation, "R_", "ElfRelocationType"); + ProcessEnum(cppOptions, csCompilation, "NT_", "ElfNoteType"); - csCompilation.DumpTo(GetCodeWriter(Path.Combine("LibObjectFile", "generated"))); - } + csCompilation.DumpTo(GetCodeWriter(Path.Combine("LibObjectFile", "generated"))); + } - private static readonly Dictionary MapRelocMachineToArch = new Dictionary() - { - {"R_386_", "I386"}, - {"R_X86_64_", "X86_64"}, - {"R_ARM_", "ARM"}, - {"R_AARCH64_", "AARCH64"}, - }; + private static readonly Dictionary MapRelocMachineToArch = new Dictionary() + { + {"R_386_", "I386"}, + {"R_X86_64_", "X86_64"}, + {"R_ARM_", "ARM"}, + {"R_AARCH64_", "AARCH64"}, + }; - private static readonly Dictionary MapRelocMachineToMachine = new Dictionary() - { - {"R_386_", "EM_386"}, - {"R_X86_64_", "EM_X86_64"}, - {"R_ARM_", "EM_ARM"}, - {"R_AARCH64_", "EM_AARCH64"}, - }; + private static readonly Dictionary MapRelocMachineToMachine = new Dictionary() + { + {"R_386_", "EM_386"}, + {"R_X86_64_", "EM_X86_64"}, + {"R_ARM_", "EM_ARM"}, + {"R_AARCH64_", "EM_AARCH64"}, + }; - private static void ProcessEnum(CSharpConverterOptions cppOptions, CSharpCompilation csCompilation, string enumPrefix, string enumClassName) - { - var ns = csCompilation.Members.OfType().First().Members.OfType().First(); + private static void ProcessEnum(CSharpConverterOptions cppOptions, CSharpCompilation csCompilation, string enumPrefix, string enumClassName) + { + var ns = csCompilation.Members.OfType().First().Members.OfType().First(); - var rawElfClass = ns.Members.OfType().First(); + var rawElfClass = ns.Members.OfType().First(); - var enumRawFields = rawElfClass.Members.OfType().Where(x => (x.Modifiers & CSharpModifiers.Const) != 0 && x.Name.StartsWith(enumPrefix)).ToList(); + var enumRawFields = rawElfClass.Members.OfType().Where(x => (x.Modifiers & CSharpModifiers.Const) != 0 && x.Name.StartsWith(enumPrefix)).ToList(); - var enumClass = new CSharpStruct(enumClassName) - { - Modifiers = CSharpModifiers.Partial | CSharpModifiers.ReadOnly - }; - ns.Members.Add(enumClass); + var enumClass = new CSharpStruct(enumClassName) + { + Modifiers = CSharpModifiers.Partial | CSharpModifiers.ReadOnly + }; + ns.Members.Add(enumClass); - CSharpEnum stdEnum = null; + CSharpEnum stdEnum = null; - var enumItemType = enumRawFields[0].FieldType; + var enumItemType = enumRawFields[0].FieldType; - bool isReloc = enumPrefix == "R_"; + bool isReloc = enumPrefix == "R_"; - if (!isReloc) + if (!isReloc) + { + enumClass.Name = enumClass.Name + "Ex"; + stdEnum = new CSharpEnum(enumClassName) { - enumClass.Name = enumClass.Name + "Ex"; - stdEnum = new CSharpEnum(enumClassName) - { - Visibility = CSharpVisibility.Public - }; - ns.Members.Add(stdEnum); - stdEnum.BaseTypes.Add(enumItemType); - } + Visibility = CSharpVisibility.Public + }; + ns.Members.Add(stdEnum); + stdEnum.BaseTypes.Add(enumItemType); + } - var filteredFields = new List(); + var filteredFields = new List(); - foreach (var enumRawField in enumRawFields) - { - var rawName = enumRawField.Name; + foreach (var enumRawField in enumRawFields) + { + var rawName = enumRawField.Name; - string relocArch = null; + string relocArch = null; - if (isReloc) + if (isReloc) + { + foreach (var mapReloc in MapRelocMachineToArch) { - foreach (var mapReloc in MapRelocMachineToArch) - { - if (rawName.StartsWith(mapReloc.Key)) - { - relocArch = mapReloc.Value; - break; - } - } - - if (relocArch == null) + if (rawName.StartsWith(mapReloc.Key)) { - continue; + relocArch = mapReloc.Value; + break; } } - // skip lo/hi user - if (rawName.StartsWith("DW_") && (rawName.Contains("_lo_") || rawName.Contains("_hi_"))) + if (relocArch == null) { continue; } + } - // NUM fields - if (rawName.EndsWith("_NUM")) continue; - - filteredFields.Add(enumRawField); - - var csFieldName = isReloc ? rawName : rawName.Substring(enumPrefix.Length); // discard EM_ - if (csFieldName.StartsWith("386")) - { - csFieldName = $"I{csFieldName}"; - } - else - { - switch (csFieldName) - { - case "88K": - csFieldName = "M88K"; - break; - case "860": - csFieldName = "I860"; - break; - case "960": - csFieldName = "I960"; - break; - default: - // assume Motorola - if (csFieldName.StartsWith("68")) - { - csFieldName = $"M{csFieldName}"; - } + // skip lo/hi user + if (rawName.StartsWith("DW_") && (rawName.Contains("_lo_") || rawName.Contains("_hi_"))) + { + continue; + } - break; - } - } + // NUM fields + if (rawName.EndsWith("_NUM")) continue; + + filteredFields.Add(enumRawField); - if (char.IsDigit(csFieldName[0])) + var csFieldName = isReloc ? rawName : rawName.Substring(enumPrefix.Length); // discard EM_ + if (csFieldName.StartsWith("386")) + { + csFieldName = $"I{csFieldName}"; + } + else + { + switch (csFieldName) { - throw new InvalidOperationException($"The enum name `{rawName}` starts with a number and needs to be modified"); - } + case "88K": + csFieldName = "M88K"; + break; + case "860": + csFieldName = "I860"; + break; + case "960": + csFieldName = "I960"; + break; + default: + // assume Motorola + if (csFieldName.StartsWith("68")) + { + csFieldName = $"M{csFieldName}"; + } - if (rawName.StartsWith("DW_")) - { - csFieldName = CSharpifyName(csFieldName); + break; } + } - csFieldName = CSharpHelper.EscapeName(csFieldName); + if (char.IsDigit(csFieldName[0])) + { + throw new InvalidOperationException($"The enum name `{rawName}` starts with a number and needs to be modified"); + } - var enumField = new CSharpField(csFieldName) - { - Modifiers = CSharpModifiers.Static | CSharpModifiers.ReadOnly, - FieldType = enumClass, - Visibility = CSharpVisibility.Public, - Comment = enumRawField.Comment, - InitValue = relocArch != null ? - $"new {enumClass.Name}(ElfArch.{relocArch}, {cppOptions.DefaultClassLib}.{rawName})" : - $"new {enumClass.Name}({cppOptions.DefaultClassLib}.{rawName})" - }; - - enumClass.Members.Add(enumField); - - if (!isReloc) - { - var stdEnumField = new CSharpEnumItem(csFieldName, $"{cppOptions.DefaultClassLib}.{rawName}"); - stdEnum.Members.Add(stdEnumField); - } + if (rawName.StartsWith("DW_")) + { + csFieldName = CSharpifyName(csFieldName); } - var toStringInternal = new CSharpMethod("ToStringInternal") + csFieldName = CSharpHelper.EscapeName(csFieldName); + + var enumField = new CSharpField(csFieldName) { - Visibility = CSharpVisibility.Private, - ReturnType = new CSharpNullableType(CSharpPrimitiveType.String()) + Modifiers = CSharpModifiers.Static | CSharpModifiers.ReadOnly, + FieldType = enumClass, + Visibility = CSharpVisibility.Public, + Comment = enumRawField.Comment, + InitValue = relocArch != null ? + $"new {enumClass.Name}(ElfArch.{relocArch}, {cppOptions.DefaultClassLib}.{rawName})" : + $"new {enumClass.Name}({cppOptions.DefaultClassLib}.{rawName})" }; - enumClass.Members.Add(toStringInternal); - toStringInternal.Body = (writer, element) => + enumClass.Members.Add(enumField); + + if (!isReloc) + { + var stdEnumField = new CSharpEnumItem(csFieldName, $"{cppOptions.DefaultClassLib}.{rawName}"); + stdEnum.Members.Add(stdEnumField); + } + } + + var toStringInternal = new CSharpMethod("ToStringInternal") + { + Visibility = CSharpVisibility.Private, + ReturnType = new CSharpNullableType(CSharpPrimitiveType.String()) + }; + enumClass.Members.Add(toStringInternal); + + toStringInternal.Body = (writer, element) => + { + var values = new HashSet(); + if (isReloc) + { + writer.WriteLine("switch (((ulong)Value << 16) | (ulong)Arch.Value)"); + } + else { - var values = new HashSet(); + writer.WriteLine($"switch (({enumItemType})Value)"); + } + writer.OpenBraceBlock(); + foreach (var rawField in filteredFields) + { + var cppField = ((CppField)rawField.CppElement); if (isReloc) { - writer.WriteLine("switch (((ulong)Value << 16) | (ulong)Arch.Value)"); - } - else - { - writer.WriteLine($"switch (({enumItemType})Value)"); - } - writer.OpenBraceBlock(); - foreach (var rawField in filteredFields) - { - var cppField = ((CppField)rawField.CppElement); - if (isReloc) + string relocMachine = null; + foreach (var mapReloc in MapRelocMachineToMachine) { - string relocMachine = null; - foreach (var mapReloc in MapRelocMachineToMachine) + if (rawField.Name.StartsWith(mapReloc.Key)) { - if (rawField.Name.StartsWith(mapReloc.Key)) - { - relocMachine = mapReloc.Value; - break; - } + relocMachine = mapReloc.Value; + break; } + } - if (relocMachine == null) - { - continue; - } + if (relocMachine == null) + { + continue; + } - if (!values.Add(relocMachine + "$" + cppField.InitValue.Value)) - { - continue; - } - - writer.WriteLine($"case ((ulong){cppOptions.DefaultClassLib}.{rawField.Name} << 16) | {cppOptions.DefaultClassLib}.{relocMachine} : return \"{rawField.Name}\";"); + if (!values.Add(relocMachine + "$" + cppField.InitValue.Value)) + { + continue; } - else + + writer.WriteLine($"case ((ulong){cppOptions.DefaultClassLib}.{rawField.Name} << 16) | {cppOptions.DefaultClassLib}.{relocMachine} : return \"{rawField.Name}\";"); + } + else + { + if (!values.Add(cppField.InitValue.Value)) { - if (!values.Add(cppField.InitValue.Value)) - { - continue; - } - - string descriptionText = rawField.Name; - //if (cppField.Comment != null) - //{ - // descriptionText += " - " + cppField.Comment.ToString().Replace("\"", "\\\""); - //} - descriptionText = descriptionText.Replace("\r\n", "").Replace("\n", ""); - writer.WriteLine($"case {cppOptions.DefaultClassLib}.{rawField.Name}: return \"{descriptionText}\";"); + continue; } + + string descriptionText = rawField.Name; + //if (cppField.Comment != null) + //{ + // descriptionText += " - " + cppField.Comment.ToString().Replace("\"", "\\\""); + //} + descriptionText = descriptionText.Replace("\r\n", "").Replace("\n", ""); + writer.WriteLine($"case {cppOptions.DefaultClassLib}.{rawField.Name}: return \"{descriptionText}\";"); } + } - writer.WriteLine($"default: return null;"); - writer.CloseBraceBlock(); - }; - } + writer.WriteLine($"default: return null;"); + writer.CloseBraceBlock(); + }; } } \ No newline at end of file diff --git a/src/LibObjectFile.Tests/Ar/ArTestBase.cs b/src/LibObjectFile.Tests/Ar/ArTestBase.cs index 1a318e9..3e55e7d 100644 --- a/src/LibObjectFile.Tests/Ar/ArTestBase.cs +++ b/src/LibObjectFile.Tests/Ar/ArTestBase.cs @@ -3,40 +3,39 @@ // See the license.txt file in the project root for more information. using System; -using NUnit.Framework; +using LibObjectFile.Diagnostics; -namespace LibObjectFile.Tests.Ar +namespace LibObjectFile.Tests.Ar; + +public abstract class ArTestBase { - public abstract class ArTestBase + protected static void ExpectNoDiagnostics(DiagnosticBag diagnostics) { - protected static void ExpectNoDiagnostics(DiagnosticBag diagnostics) + if (diagnostics.Messages.Count != 0) { - if (diagnostics.Messages.Count != 0) - { - Console.WriteLine(diagnostics); - Assert.AreEqual(0, diagnostics.Messages.Count, $"Invalid number of diagnostics found, expecting no diagnostics"); - } + Console.WriteLine(diagnostics); + Assert.AreEqual(0, diagnostics.Messages.Count, $"Invalid number of diagnostics found, expecting no diagnostics"); } + } - protected static void ExpectDiagnostics(DiagnosticBag diagnostics, params DiagnosticId[] ids) + protected static void ExpectDiagnostics(DiagnosticBag diagnostics, params DiagnosticId[] ids) + { + if (diagnostics.Messages.Count != ids.Length) { - if (diagnostics.Messages.Count != ids.Length) - { - Console.WriteLine(diagnostics); - Assert.AreEqual(ids.Length, diagnostics.Messages.Count, $"Invalid number of diagnostics found, expecting {ids.Length} entries [{string.Join(", ", ids)}]"); - } + Console.WriteLine(diagnostics); + Assert.AreEqual(ids.Length, diagnostics.Messages.Count, $"Invalid number of diagnostics found, expecting {ids.Length} entries [{string.Join(", ", ids)}]"); + } - for (var i = 0; i < diagnostics.Messages.Count; i++) - { - var diagnosticsMessage = diagnostics.Messages[i]; - var expectedId = ids[i]; + for (var i = 0; i < diagnostics.Messages.Count; i++) + { + var diagnosticsMessage = diagnostics.Messages[i]; + var expectedId = ids[i]; - if (expectedId != diagnosticsMessage.Id) - { - Console.WriteLine(diagnostics); - Assert.AreEqual(expectedId, diagnosticsMessage.Id, $"Invalid Id {diagnosticsMessage.Id} found for diagnostics [{i}] while expecting {expectedId} from entries [{string.Join(", ", ids)}]"); - } + if (expectedId != diagnosticsMessage.Id) + { + Console.WriteLine(diagnostics); + Assert.AreEqual(expectedId, diagnosticsMessage.Id, $"Invalid Id {diagnosticsMessage.Id} found for diagnostics [{i}] while expecting {expectedId} from entries [{string.Join(", ", ids)}]"); } } - } + } } \ No newline at end of file diff --git a/src/LibObjectFile.Tests/Ar/ArTests.cs b/src/LibObjectFile.Tests/Ar/ArTests.cs index 9cbc9be..52aea87 100644 --- a/src/LibObjectFile.Tests/Ar/ArTests.cs +++ b/src/LibObjectFile.Tests/Ar/ArTests.cs @@ -7,423 +7,422 @@ using System.Linq; using System.Text; using LibObjectFile.Ar; -using NUnit.Framework; +using LibObjectFile.Diagnostics; -namespace LibObjectFile.Tests.Ar +namespace LibObjectFile.Tests.Ar; + +public class ArTests : ArTestBase { - public class ArTests : ArTestBase + [Test] + public void CheckInvalidHeader() { - [Test] - public void CheckInvalidHeader() + // Incorrect magic length { - // Incorrect magic length - { - var stream = new MemoryStream(new byte[] { 1, 2, 3, 4 }); - Assert.False(ArArchiveFile.IsAr(stream, out var diagnostics)); - ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidMagicLength); - } + var stream = new MemoryStream(new byte[] { 1, 2, 3, 4 }); + Assert.False(ArArchiveFile.IsAr(stream, out var diagnostics)); + ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidMagicLength); + } - // Correct length, magic invalid + // Correct length, magic invalid + { + var stream = new MemoryStream(new byte[] { - var stream = new MemoryStream(new byte[] - { - (byte)'!', - (byte)'<', - (byte)'a', - (byte)'r', - (byte)'c', - (byte)'h', - (byte)'>', - (byte)'?', - }); - Assert.False(ArArchiveFile.IsAr(stream, out var diagnostics)); - ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_MagicNotFound); - } + (byte)'!', + (byte)'<', + (byte)'a', + (byte)'r', + (byte)'c', + (byte)'h', + (byte)'>', + (byte)'?', + }); + Assert.False(ArArchiveFile.IsAr(stream, out var diagnostics)); + ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_MagicNotFound); + } - // Valid + // Valid + { + var stream = new MemoryStream(new byte[] { - var stream = new MemoryStream(new byte[] - { - (byte)'!', - (byte)'<', - (byte)'a', - (byte)'r', - (byte)'c', - (byte)'h', - (byte)'>', - (byte)'\n', - }); - Assert.True(ArArchiveFile.IsAr(stream, out var diagnostics)); - ExpectNoDiagnostics(diagnostics); - } + (byte)'!', + (byte)'<', + (byte)'a', + (byte)'r', + (byte)'c', + (byte)'h', + (byte)'>', + (byte)'\n', + }); + Assert.True(ArArchiveFile.IsAr(stream, out var diagnostics)); + ExpectNoDiagnostics(diagnostics); } + } - [Test] - public void CheckInvalidFileEntry() + [Test] + public void CheckInvalidFileEntry() + { + // Incorrect entry length { - // Incorrect entry length - { - var stream = new MemoryStream(); - stream.Write(ArArchiveFile.Magic); - stream.Write(new byte[] {(byte)'a', (byte)'b'}); - stream.Position = 0; + var stream = new MemoryStream(); + stream.Write(ArArchiveFile.Magic); + stream.Write(new byte[] {(byte)'a', (byte)'b'}); + stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); - ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidFileEntryLength); - } + Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); + ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidFileEntryLength); + } - // Input invalid non-numeric characters into decimal/octal fields in file entry + // Input invalid non-numeric characters into decimal/octal fields in file entry + { + var offsets = new int[] { - var offsets = new int[] - { - ArFile.FieldTimestampOffset, - ArFile.FieldOwnerIdOffset, - ArFile.FieldGroupIdOffset, - ArFile.FieldFileModeOffset, - ArFile.FieldFileSizeOffset, - ArFile.FieldEndCharactersOffset - }; - foreach (var offset in offsets) - { - var stream = new MemoryStream(); - stream.Write(ArArchiveFile.Magic); - - var entry = new Span(new byte[ArFile.FileEntrySizeInBytes]); - entry.Fill((byte)' '); - entry[offset] = (byte)'a'; - stream.Write(entry); - - stream.Position = 0; - - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); - ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry); - } - } - - // Input name with `/` + ArFile.FieldTimestampOffset, + ArFile.FieldOwnerIdOffset, + ArFile.FieldGroupIdOffset, + ArFile.FieldFileModeOffset, + ArFile.FieldFileSizeOffset, + ArFile.FieldEndCharactersOffset + }; + foreach (var offset in offsets) { var stream = new MemoryStream(); stream.Write(ArArchiveFile.Magic); + var entry = new Span(new byte[ArFile.FileEntrySizeInBytes]); entry.Fill((byte)' '); - entry[0] = (byte)'a'; - entry[1] = (byte)'b'; - entry[2] = (byte)'/'; - entry[3] = (byte)'c'; - - entry[ArFile.FieldEndCharactersOffset] = (byte)'`'; - entry[ArFile.FieldEndCharactersOffset + 1] = (byte)'\n'; - + entry[offset] = (byte)'a'; stream.Write(entry); stream.Position = 0; Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); - ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidCharacterInFileEntryName); + ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry); } + } - // Input length of content - { - var stream = new MemoryStream(); - stream.Write(ArArchiveFile.Magic); - var entry = new Span(new byte[ArFile.FileEntrySizeInBytes]); - entry.Fill((byte)' '); - entry[0] = (byte)'a'; - entry[ArFile.FieldFileSizeOffset] = (byte)'2'; - entry[ArFile.FieldEndCharactersOffset] = (byte)'`'; - entry[ArFile.FieldEndCharactersOffset + 1] = (byte)'\n'; + // Input name with `/` + { + var stream = new MemoryStream(); + stream.Write(ArArchiveFile.Magic); + var entry = new Span(new byte[ArFile.FileEntrySizeInBytes]); + entry.Fill((byte)' '); + entry[0] = (byte)'a'; + entry[1] = (byte)'b'; + entry[2] = (byte)'/'; + entry[3] = (byte)'c'; - stream.Write(entry); + entry[ArFile.FieldEndCharactersOffset] = (byte)'`'; + entry[ArFile.FieldEndCharactersOffset + 1] = (byte)'\n'; - var continuePosition = stream.Position; + stream.Write(entry); - stream.Position = 0; + stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); - ExpectDiagnostics(diagnostics, DiagnosticId.CMN_ERR_UnexpectedEndOfFile, DiagnosticId.CMN_ERR_UnexpectedEndOfFile); + Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); + ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidCharacterInFileEntryName); + } - stream.Position = continuePosition; + // Input length of content + { + var stream = new MemoryStream(); + stream.Write(ArArchiveFile.Magic); + var entry = new Span(new byte[ArFile.FileEntrySizeInBytes]); + entry.Fill((byte)' '); + entry[0] = (byte)'a'; + entry[ArFile.FieldFileSizeOffset] = (byte)'2'; + entry[ArFile.FieldEndCharactersOffset] = (byte)'`'; + entry[ArFile.FieldEndCharactersOffset + 1] = (byte)'\n'; - stream.WriteByte(0); - stream.WriteByte(1); + stream.Write(entry); - stream.Position = 0; + var continuePosition = stream.Position; + + stream.Position = 0; + + Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); + ExpectDiagnostics(diagnostics, DiagnosticId.CMN_ERR_UnexpectedEndOfFile, DiagnosticId.CMN_ERR_UnexpectedEndOfFile); + + stream.Position = continuePosition; + + stream.WriteByte(0); + stream.WriteByte(1); - // Check that we can actually read the content + stream.Position = 0; - var result = ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out var arFile, out diagnostics); - ExpectNoDiagnostics(diagnostics); - Assert.True(result, $"Error while reading file: {diagnostics}"); - Assert.AreEqual(1, arFile.Files.Count, "Invalid number of file entries found"); - Assert.AreEqual("a", arFile.Files[0].Name, "Invalid name of file entry[0] found"); - Assert.AreEqual(2, arFile.Files[0].Size, "Invalid size of file entry[0] found"); - Assert.IsInstanceOf(arFile.Files[0], "Invalid instance of of file entry[0] "); + // Check that we can actually read the content + + var result = ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out var arFile, out diagnostics); + ExpectNoDiagnostics(diagnostics); + Assert.True(result, $"Error while reading file: {diagnostics}"); + Assert.AreEqual(1, arFile.Files.Count, "Invalid number of file entries found"); + Assert.AreEqual("a", arFile.Files[0].Name, "Invalid name of file entry[0] found"); + Assert.AreEqual(2, arFile.Files[0].Size, "Invalid size of file entry[0] found"); + Assert.IsInstanceOf(arFile.Files[0], "Invalid instance of of file entry[0] "); - var fileStream = ((ArBinaryFile) arFile.Files[0]).Stream; - var read = new byte[] - { - (byte)fileStream.ReadByte(), - (byte)fileStream.ReadByte() - }; - Assert.AreEqual(new byte[] { 0, 1}, read, "Invalid content of of file entry[0] "); + var fileStream = ((ArBinaryFile) arFile.Files[0]).Stream; + var read = new byte[] + { + (byte)fileStream.ReadByte(), + (byte)fileStream.ReadByte() + }; + Assert.AreEqual(new byte[] { 0, 1}, read, "Invalid content of of file entry[0] "); - Assert.Null(arFile.SymbolTable, "Invalid non-null symbol table found"); + Assert.Null(arFile.SymbolTable, "Invalid non-null symbol table found"); - } + } - // Input length of content - { - var stream = new MemoryStream(); - stream.Write(ArArchiveFile.Magic); - var entry = new Span(new byte[ArFile.FileEntrySizeInBytes]); - entry.Fill((byte)' '); - entry[0] = (byte)'a'; - entry[ArFile.FieldFileSizeOffset] = (byte)'1'; - entry[ArFile.FieldEndCharactersOffset] = (byte)'`'; - entry[ArFile.FieldEndCharactersOffset + 1] = (byte)'\n'; + // Input length of content + { + var stream = new MemoryStream(); + stream.Write(ArArchiveFile.Magic); + var entry = new Span(new byte[ArFile.FileEntrySizeInBytes]); + entry.Fill((byte)' '); + entry[0] = (byte)'a'; + entry[ArFile.FieldFileSizeOffset] = (byte)'1'; + entry[ArFile.FieldEndCharactersOffset] = (byte)'`'; + entry[ArFile.FieldEndCharactersOffset + 1] = (byte)'\n'; - stream.Write(entry); - stream.WriteByte(0); + stream.Write(entry); + stream.WriteByte(0); - var continuePosition = stream.Position; - stream.Position = 0; + var continuePosition = stream.Position; + stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); - ExpectDiagnostics(diagnostics, DiagnosticId.CMN_ERR_UnexpectedEndOfFile); + Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); + ExpectDiagnostics(diagnostics, DiagnosticId.CMN_ERR_UnexpectedEndOfFile); - stream.Position = continuePosition; - stream.WriteByte(0); - stream.Position = 0; + stream.Position = continuePosition; + stream.WriteByte(0); + stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out diagnostics)); - ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_ExpectingNewLineCharacter); + Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out diagnostics)); + ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_ExpectingNewLineCharacter); - stream.Position = continuePosition; - stream.WriteByte((byte)'\n'); - stream.Position = 0; + stream.Position = continuePosition; + stream.WriteByte((byte)'\n'); + stream.Position = 0; - Assert.True(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out diagnostics)); - ExpectNoDiagnostics(diagnostics); - } + Assert.True(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out diagnostics)); + ExpectNoDiagnostics(diagnostics); } + } - [Test] - public void CheckInvalidBSDFileEntry() + [Test] + public void CheckInvalidBSDFileEntry() + { + // Input invalid BSD Length { - // Input invalid BSD Length - { - var stream = new MemoryStream(); - stream.Write(ArArchiveFile.Magic); - var entry = new Span(new byte[ArFile.FileEntrySizeInBytes]); - entry.Fill((byte)' '); - entry[0] = (byte)'#'; - entry[1] = (byte)'1'; - entry[2] = (byte)'/'; - entry[3] = (byte)'a'; + var stream = new MemoryStream(); + stream.Write(ArArchiveFile.Magic); + var entry = new Span(new byte[ArFile.FileEntrySizeInBytes]); + entry.Fill((byte)' '); + entry[0] = (byte)'#'; + entry[1] = (byte)'1'; + entry[2] = (byte)'/'; + entry[3] = (byte)'a'; - stream.Write(entry); + stream.Write(entry); - stream.Position = 0; - - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.BSD, out _, out var diagnostics)); - ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry); - } + stream.Position = 0; - // Input invalid BSD Length - { - var stream = new MemoryStream(); - stream.Write(ArArchiveFile.Magic); - var entry = new Span(new byte[ArFile.FileEntrySizeInBytes]); - entry.Fill((byte)' '); - entry[0] = (byte)'#'; - entry[1] = (byte)'1'; - entry[2] = (byte)'/'; - entry[3] = (byte)'2'; // it will try to read 2 bytes for the name but won't find it + Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.BSD, out _, out var diagnostics)); + ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry); + } - entry[ArFile.FieldEndCharactersOffset] = (byte)'`'; - entry[ArFile.FieldEndCharactersOffset + 1] = (byte)'\n'; + // Input invalid BSD Length + { + var stream = new MemoryStream(); + stream.Write(ArArchiveFile.Magic); + var entry = new Span(new byte[ArFile.FileEntrySizeInBytes]); + entry.Fill((byte)' '); + entry[0] = (byte)'#'; + entry[1] = (byte)'1'; + entry[2] = (byte)'/'; + entry[3] = (byte)'2'; // it will try to read 2 bytes for the name but won't find it + + entry[ArFile.FieldEndCharactersOffset] = (byte)'`'; + entry[ArFile.FieldEndCharactersOffset + 1] = (byte)'\n'; - stream.Write(entry); + stream.Write(entry); - var continuePosition = stream.Position; + var continuePosition = stream.Position; - stream.Position = 0; + stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.BSD, out _, out var diagnostics)); - ExpectDiagnostics(diagnostics, DiagnosticId.CMN_ERR_UnexpectedEndOfFile); + Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.BSD, out _, out var diagnostics)); + ExpectDiagnostics(diagnostics, DiagnosticId.CMN_ERR_UnexpectedEndOfFile); - // Check validity of BSD name + // Check validity of BSD name - stream.Position = continuePosition; - stream.WriteByte((byte)'a'); - stream.WriteByte((byte)'b'); + stream.Position = continuePosition; + stream.WriteByte((byte)'a'); + stream.WriteByte((byte)'b'); - stream.Position = 0; + stream.Position = 0; - var result = ArArchiveFile.TryRead(stream, ArArchiveKind.BSD, out var arFile, out diagnostics); - Assert.True(result, $"Error while reading file: {diagnostics}"); - Assert.AreEqual(1, arFile.Files.Count, "Invalid number of file entries found"); - Assert.AreEqual("ab", arFile.Files[0].Name, "Invalid name of file entry[0] found"); - Assert.Null(arFile.SymbolTable, "Invalid non-null symbol table found"); - } + var result = ArArchiveFile.TryRead(stream, ArArchiveKind.BSD, out var arFile, out diagnostics); + Assert.True(result, $"Error while reading file: {diagnostics}"); + Assert.AreEqual(1, arFile.Files.Count, "Invalid number of file entries found"); + Assert.AreEqual("ab", arFile.Files[0].Name, "Invalid name of file entry[0] found"); + Assert.Null(arFile.SymbolTable, "Invalid non-null symbol table found"); } + } - [Test] - public void CheckLibraryWithELF() + [Test] + public void CheckLibraryWithELF() + { + var cppName = "helloworld"; + var cppObj = $"{cppName}.o"; + var cppLib = $"lib{cppName}.a"; + File.Delete(cppObj); + File.Delete(cppLib); + LinuxUtil.RunLinuxExe("gcc", $"{cppName}.cpp -c -o {cppObj}"); + LinuxUtil.RunLinuxExe("ar", $"rcs {cppLib} {cppObj}"); + + using (var stream = new FileStream(cppLib, FileMode.Open, FileAccess.Read)) { - var cppName = "helloworld"; - var cppObj = $"{cppName}.o"; - var cppLib = $"lib{cppName}.a"; - File.Delete(cppObj); - File.Delete(cppLib); - LinuxUtil.RunLinuxExe("gcc", $"{cppName}.cpp -c -o {cppObj}"); - LinuxUtil.RunLinuxExe("ar", $"rcs {cppLib} {cppObj}"); - - using (var stream = new FileStream(cppLib, FileMode.Open, FileAccess.Read)) - { - Assert.True(ArArchiveFile.IsAr(stream)); + Assert.True(ArArchiveFile.IsAr(stream)); - var arFile = ArArchiveFile.Read(stream, new ArArchiveFileReaderOptions(ArArchiveKind.GNU) {ProcessObjectFiles = false}); + var arFile = ArArchiveFile.Read(stream, new ArArchiveFileReaderOptions(ArArchiveKind.GNU) {ProcessObjectFiles = false}); - var elfFile = arFile.Files.FirstOrDefault(x => x.Name == cppObj); + var elfFile = arFile.Files.FirstOrDefault(x => x.Name == cppObj); - Assert.NotNull(elfFile, $"Unable to find {cppObj} file in {cppLib}"); + Assert.NotNull(elfFile, $"Unable to find {cppObj} file in {cppLib}"); - Assert.NotNull(arFile.SymbolTable, $"Unable to find symbol table in {cppLib}"); + Assert.NotNull(arFile.SymbolTable, $"Unable to find symbol table in {cppLib}"); - Assert.AreEqual(1, arFile.SymbolTable.Symbols.Count, "Invalid number of symbols in Symbol table"); - Assert.AreEqual("main", arFile.SymbolTable.Symbols[0].Name, "Invalid symbol found"); - Assert.AreEqual(elfFile, arFile.SymbolTable.Symbols[0].File, "Invalid symbol to file found"); + Assert.AreEqual(1, arFile.SymbolTable.Symbols.Count, "Invalid number of symbols in Symbol table"); + Assert.AreEqual("main", arFile.SymbolTable.Symbols[0].Name, "Invalid symbol found"); + Assert.AreEqual(elfFile, arFile.SymbolTable.Symbols[0].File, "Invalid symbol to file found"); - var outStream = new MemoryStream(); - arFile.Write(outStream); - var newArray = outStream.ToArray(); - outStream.Position = 0; + var outStream = new MemoryStream(); + arFile.Write(outStream); + var newArray = outStream.ToArray(); + outStream.Position = 0; - var cppLibCopy = $"lib{cppName}_copy.a"; - using (var copyStream = new FileStream(cppLibCopy, FileMode.Create, FileAccess.Write)) - { - outStream.CopyTo(copyStream); - } + var cppLibCopy = $"lib{cppName}_copy.a"; + using (var copyStream = new FileStream(cppLibCopy, FileMode.Create, FileAccess.Write)) + { + outStream.CopyTo(copyStream); + } - var originalStream = new MemoryStream(); - stream.Position = 0; - stream.CopyTo(originalStream); - var originalArray = originalStream.ToArray(); + var originalStream = new MemoryStream(); + stream.Position = 0; + stream.CopyTo(originalStream); + var originalArray = originalStream.ToArray(); - Assert.AreEqual(originalArray, newArray, $"Non binary matching between file {cppLib} and {cppLibCopy}"); - } + Assert.AreEqual(originalArray, newArray, $"Non binary matching between file {cppLib} and {cppLibCopy}"); } + } - [Test] - public void CheckCreateArLibrary() - { - var libName = "libcustom.a"; + [Test] + public void CheckCreateArLibrary() + { + var libName = "libcustom.a"; - var file = new ArArchiveFile(); - using (var stream = new FileStream(libName, FileMode.Create, FileAccess.Write)) - { - var symbolTable = new ArSymbolTable(); - file.AddFile(symbolTable); + var file = new ArArchiveFile(); + using (var stream = new FileStream(libName, FileMode.Create, FileAccess.Write)) + { + var symbolTable = new ArSymbolTable(); + file.AddFile(symbolTable); - file.AddFile(new ArBinaryFile() { Name = "file2.txt", OwnerId = 0666, Stream = new MemoryStream(Encoding.UTF8.GetBytes("this is file")) }); + file.AddFile(new ArBinaryFile() { Name = "file2.txt", OwnerId = 0666, Stream = new MemoryStream(Encoding.UTF8.GetBytes("this is file")) }); - file.AddFile(new ArBinaryFile() { Name = "file3.txt", OwnerId = 0777, GroupId = 0744, Stream = new MemoryStream(Encoding.UTF8.GetBytes("this is file3")) }); + file.AddFile(new ArBinaryFile() { Name = "file3.txt", OwnerId = 0777, GroupId = 0744, Stream = new MemoryStream(Encoding.UTF8.GetBytes("this is file3")) }); - file.AddFile(new ArBinaryFile() { Name = "file4.txt", OwnerId = 0777, GroupId = 0744, Stream = new MemoryStream(Encoding.UTF8.GetBytes("this is file4")) }); + file.AddFile(new ArBinaryFile() { Name = "file4.txt", OwnerId = 0777, GroupId = 0744, Stream = new MemoryStream(Encoding.UTF8.GetBytes("this is file4")) }); - file.AddFile(new ArBinaryFile() { Name = "file5.txt", OwnerId = 0706, GroupId = 0705, FileMode = 0x123456, Stream = new MemoryStream(Encoding.UTF8.GetBytes("this is file5")) }); + file.AddFile(new ArBinaryFile() { Name = "file5.txt", OwnerId = 0706, GroupId = 0705, FileMode = 0x123456, Stream = new MemoryStream(Encoding.UTF8.GetBytes("this is file5")) }); - file.AddFile(new ArBinaryFile() { Name = "long_file_name_large_file6.txt", Timestamp = DateTimeOffset.UtcNow.AddSeconds(-2), Stream = new MemoryStream(Encoding.UTF8.GetBytes("this is file6 yoyo")) }); + file.AddFile(new ArBinaryFile() { Name = "long_file_name_large_file6.txt", Timestamp = DateTimeOffset.UtcNow.AddSeconds(-2), Stream = new MemoryStream(Encoding.UTF8.GetBytes("this is file6 yoyo")) }); - symbolTable.Symbols.Add(new ArSymbol() { File = file.Files[1], Name = "my_symbol" }); + symbolTable.Symbols.Add(new ArSymbol() { File = file.Files[1], Name = "my_symbol" }); - file.Write(stream); - stream.Flush(); - } + file.Write(stream); + stream.Flush(); + } - // Check that AR is able to read back what we just serialized + // Check that AR is able to read back what we just serialized + { + var fileNameBuilder = new StringBuilder(); + foreach (var fileEntry in file.Files) { - var fileNameBuilder = new StringBuilder(); - foreach (var fileEntry in file.Files) - { - if (fileEntry.IsSystem) continue; - fileNameBuilder.Append($"{fileEntry.Name}\n"); - } - - var fileNameList = fileNameBuilder.ToString().Trim(); - var fileNameListFromAr = LinuxUtil.RunLinuxExe("ar", $"t {libName}").Trim(); - Assert.AreEqual(fileNameListFromAr, fileNameList); + if (fileEntry.IsSystem) continue; + fileNameBuilder.Append($"{fileEntry.Name}\n"); } - // Display the content of each file via AR - { - var contentBuilder = new StringBuilder(); - foreach (var fileEntry in file.Files) - { - if (!(fileEntry is ArBinaryFile arBinary)) continue; + var fileNameList = fileNameBuilder.ToString().Trim(); + var fileNameListFromAr = LinuxUtil.RunLinuxExe("ar", $"t {libName}").Trim(); + Assert.AreEqual(fileNameListFromAr, fileNameList); + } - arBinary.Stream.Position = 0; - contentBuilder.Append(Encoding.UTF8.GetString(((MemoryStream) arBinary.Stream).ToArray())); - } + // Display the content of each file via AR + { + var contentBuilder = new StringBuilder(); + foreach (var fileEntry in file.Files) + { + if (!(fileEntry is ArBinaryFile arBinary)) continue; - var content = contentBuilder.ToString().Trim(); - var contentFromAr = LinuxUtil.RunLinuxExe("ar", $"p {libName}").Trim(); - Assert.AreEqual(contentFromAr, content); + arBinary.Stream.Position = 0; + contentBuilder.Append(Encoding.UTF8.GetString(((MemoryStream) arBinary.Stream).ToArray())); } - ArArchiveFile file2; - using (var stream = new FileStream(libName, FileMode.Open, FileAccess.Read)) - { - file2 = ArArchiveFile.Read(stream, ArArchiveKind.GNU); - } + var content = contentBuilder.ToString().Trim(); + var contentFromAr = LinuxUtil.RunLinuxExe("ar", $"p {libName}").Trim(); + Assert.AreEqual(contentFromAr, content); + } - Assert.NotNull(file2.SymbolTable); - CompareArFiles(file, file2); + ArArchiveFile file2; + using (var stream = new FileStream(libName, FileMode.Open, FileAccess.Read)) + { + file2 = ArArchiveFile.Read(stream, ArArchiveKind.GNU); + } - var libNameBsd = "libcustom_bsd.a"; - file.Kind = ArArchiveKind.BSD; - using (var stream = new FileStream(libNameBsd, FileMode.Create, FileAccess.Write)) - { - file.Write(stream); - stream.Flush(); - } + Assert.NotNull(file2.SymbolTable); + CompareArFiles(file, file2); - ArArchiveFile archiveFileBsd; - using (var stream = new FileStream(libNameBsd, FileMode.Open, FileAccess.Read)) - { - archiveFileBsd = ArArchiveFile.Read(stream, ArArchiveKind.BSD); - } + var libNameBsd = "libcustom_bsd.a"; + file.Kind = ArArchiveKind.BSD; + using (var stream = new FileStream(libNameBsd, FileMode.Create, FileAccess.Write)) + { + file.Write(stream); + stream.Flush(); + } - CompareArFiles(file, archiveFileBsd); + ArArchiveFile archiveFileBsd; + using (var stream = new FileStream(libNameBsd, FileMode.Open, FileAccess.Read)) + { + archiveFileBsd = ArArchiveFile.Read(stream, ArArchiveKind.BSD); } - private static void CompareArFiles(ArArchiveFile archiveFile, ArArchiveFile file2) + CompareArFiles(file, archiveFileBsd); + } + + private static void CompareArFiles(ArArchiveFile archiveFile, ArArchiveFile file2) + { + Assert.AreEqual(archiveFile.Files.Count, file2.Files.Count, "File entries count don't match"); + for (var i = 0; i < archiveFile.Files.Count; i++) { - Assert.AreEqual(archiveFile.Files.Count, file2.Files.Count, "File entries count don't match"); - for (var i = 0; i < archiveFile.Files.Count; i++) + var fileEntry = archiveFile.Files[i]; + var file2Entry = file2.Files[i]; + Assert.AreEqual(fileEntry.GetType(), file2Entry.GetType(), $"File entry [{i}] for {archiveFile} type don't match "); + Assert.AreEqual(fileEntry.Name, file2Entry.Name, $"File entry Name [{i}] for {archiveFile}"); + Assert.AreEqual(fileEntry.Timestamp, file2Entry.Timestamp, $"File entry Timestamp [{i}] for {archiveFile}"); + Assert.AreEqual(fileEntry.OwnerId, file2Entry.OwnerId, $"File entry Timestamp [{i}] for {archiveFile}"); + Assert.AreEqual(fileEntry.GroupId, file2Entry.GroupId, $"File entry Timestamp [{i}] for {archiveFile}"); + Assert.AreEqual(fileEntry.FileMode, file2Entry.FileMode, $"File entry Timestamp [{i}] for {archiveFile}"); + Assert.AreEqual(fileEntry.Size, file2Entry.Size, $"File entry Size [{i}] for {archiveFile}"); + Assert.AreEqual(fileEntry.ToString(), file2Entry.ToString(), $"File entry ToString() [{i}] for {archiveFile}"); + + if (fileEntry is ArSymbolTable fileSymbolTable) { - var fileEntry = archiveFile.Files[i]; - var file2Entry = file2.Files[i]; - Assert.AreEqual(fileEntry.GetType(), file2Entry.GetType(), $"File entry [{i}] for {archiveFile} type don't match "); - Assert.AreEqual(fileEntry.Name, file2Entry.Name, $"File entry Name [{i}] for {archiveFile}"); - Assert.AreEqual(fileEntry.Timestamp, file2Entry.Timestamp, $"File entry Timestamp [{i}] for {archiveFile}"); - Assert.AreEqual(fileEntry.OwnerId, file2Entry.OwnerId, $"File entry Timestamp [{i}] for {archiveFile}"); - Assert.AreEqual(fileEntry.GroupId, file2Entry.GroupId, $"File entry Timestamp [{i}] for {archiveFile}"); - Assert.AreEqual(fileEntry.FileMode, file2Entry.FileMode, $"File entry Timestamp [{i}] for {archiveFile}"); - Assert.AreEqual(fileEntry.Size, file2Entry.Size, $"File entry Size [{i}] for {archiveFile}"); - Assert.AreEqual(fileEntry.ToString(), file2Entry.ToString(), $"File entry ToString() [{i}] for {archiveFile}"); - - if (fileEntry is ArSymbolTable fileSymbolTable) + var file2SymbolTable = (ArSymbolTable) (file2Entry); + for (var j = 0; j < fileSymbolTable.Symbols.Count; j++) { - var file2SymbolTable = (ArSymbolTable) (file2Entry); - for (var j = 0; j < fileSymbolTable.Symbols.Count; j++) - { - var fileSymbol = fileSymbolTable.Symbols[j]; - var file2Symbol = file2SymbolTable.Symbols[j]; - - Assert.AreEqual(fileSymbol.ToString(), file2Symbol.ToString(), $"Invalid symbol [{j}]"); - } + var fileSymbol = fileSymbolTable.Symbols[j]; + var file2Symbol = file2SymbolTable.Symbols[j]; + + Assert.AreEqual(fileSymbol.ToString(), file2Symbol.ToString(), $"Invalid symbol [{j}]"); } } } diff --git a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs index 89dc9fe..554cd7c 100644 --- a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs +++ b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs @@ -4,541 +4,540 @@ using System; using System.IO; +using LibObjectFile.Diagnostics; using LibObjectFile.Dwarf; using LibObjectFile.Elf; -using NUnit.Framework; -namespace LibObjectFile.Tests.Dwarf +namespace LibObjectFile.Tests.Dwarf; + +public class DwarfTests { - public class DwarfTests + [TestCase(0UL)] + [TestCase(1UL)] + [TestCase(50UL)] + [TestCase(0x7fUL)] + [TestCase(0x80UL)] + [TestCase(0x81UL)] + [TestCase(0x12345UL)] + [TestCase(2147483647UL)] // int.MaxValue + [TestCase(4294967295UL)] // uint.MaxValue + [TestCase(ulong.MaxValue)] + public void TestLEB128(ulong value) { - [TestCase(0UL)] - [TestCase(1UL)] - [TestCase(50UL)] - [TestCase(0x7fUL)] - [TestCase(0x80UL)] - [TestCase(0x81UL)] - [TestCase(0x12345UL)] - [TestCase(2147483647UL)] // int.MaxValue - [TestCase(4294967295UL)] // uint.MaxValue - [TestCase(ulong.MaxValue)] - public void TestLEB128(ulong value) - { - var stream = new MemoryStream(); - - stream.WriteULEB128(value); + var stream = new MemoryStream(); + + stream.WriteULEB128(value); - Assert.AreEqual((uint)stream.Position, DwarfHelper.SizeOfULEB128(value)); + Assert.AreEqual((uint)stream.Position, DwarfHelper.SizeOfULEB128(value)); - stream.Position = 0; - var readbackValue = stream.ReadULEB128(); + stream.Position = 0; + var readbackValue = stream.ReadULEB128(); + + Assert.AreEqual(value, readbackValue); + } + + [TestCase(0L)] + [TestCase(1L)] + [TestCase(50L)] + [TestCase(0x7fL)] + [TestCase(0x80L)] + [TestCase(0x81L)] + [TestCase(0x12345L)] + [TestCase(2147483647L)] // int.MaxValue + [TestCase(4294967295L)] // uint.MaxValue + [TestCase(long.MinValue)] + [TestCase(long.MaxValue)] + public void TestSignedLEB128(long value) + { + var stream = new MemoryStream(); + { + // check positive + stream.WriteILEB128(value); + Assert.AreEqual((uint)stream.Position, DwarfHelper.SizeOfILEB128(value)); + + stream.Position = 0; + var readbackValue = stream.ReadSignedLEB128(); Assert.AreEqual(value, readbackValue); } - [TestCase(0L)] - [TestCase(1L)] - [TestCase(50L)] - [TestCase(0x7fL)] - [TestCase(0x80L)] - [TestCase(0x81L)] - [TestCase(0x12345L)] - [TestCase(2147483647L)] // int.MaxValue - [TestCase(4294967295L)] // uint.MaxValue - [TestCase(long.MinValue)] - [TestCase(long.MaxValue)] - public void TestSignedLEB128(long value) - { - var stream = new MemoryStream(); - - { - // check positive - stream.WriteILEB128(value); - Assert.AreEqual((uint)stream.Position, DwarfHelper.SizeOfILEB128(value)); - - stream.Position = 0; - var readbackValue = stream.ReadSignedLEB128(); - Assert.AreEqual(value, readbackValue); - } - - { - stream.Position = 0; - // Check negative - value = -value; - stream.WriteILEB128(value); - Assert.AreEqual((uint)stream.Position, DwarfHelper.SizeOfILEB128(value)); - - stream.Position = 0; - var readbackValue = stream.ReadSignedLEB128(); - Assert.AreEqual(value, readbackValue); - } + { + stream.Position = 0; + // Check negative + value = -value; + stream.WriteILEB128(value); + Assert.AreEqual((uint)stream.Position, DwarfHelper.SizeOfILEB128(value)); + + stream.Position = 0; + var readbackValue = stream.ReadSignedLEB128(); + Assert.AreEqual(value, readbackValue); } + } - [Test] - public void TestDebugLineHelloWorld() - { - var cppName = "helloworld"; - var cppExe = $"{cppName}_debug"; - LinuxUtil.RunLinuxExe("gcc", $"{cppName}.cpp -gdwarf-4 -o {cppExe}"); - - ElfObjectFile elf; - using (var inStream = File.OpenRead(cppExe)) - { - Console.WriteLine($"ReadBack from {cppExe}"); - elf = ElfObjectFile.Read(inStream); - elf.Print(Console.Out); - } - - var elfContext = new DwarfElfContext(elf); - var inputContext = new DwarfReaderContext(elfContext); - inputContext.DebugLinePrinter = Console.Out; - var dwarf = DwarfFile.Read(inputContext); - - inputContext.DebugLineStream.Position = 0; - - var copyInputDebugLineStream = new MemoryStream(); - inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); - inputContext.DebugLineStream.Position = 0; - - var outputContext = new DwarfWriterContext - { - IsLittleEndian = inputContext.IsLittleEndian, - EnableRelocation = false, - AddressSize = inputContext.AddressSize, - DebugLineStream = new MemoryStream() - }; - dwarf.Write(outputContext); - - Console.WriteLine(); - Console.WriteLine("====================================================="); - Console.WriteLine("Readback"); - Console.WriteLine("====================================================="); - Console.WriteLine(); - - var reloadContext = new DwarfReaderContext() - { - IsLittleEndian = outputContext.IsLittleEndian, - AddressSize = outputContext.AddressSize, - DebugLineStream = outputContext.DebugLineStream - }; - - reloadContext.DebugLineStream.Position = 0; - reloadContext.DebugLineStream = outputContext.DebugLineStream; - reloadContext.DebugLinePrinter = Console.Out; - - var dwarf2 = DwarfFile.Read(reloadContext); - - var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); - var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + [Test] + public void TestDebugLineHelloWorld() + { + var cppName = "helloworld"; + var cppExe = $"{cppName}_debug"; + LinuxUtil.RunLinuxExe("gcc", $"{cppName}.cpp -gdwarf-4 -o {cppExe}"); + + ElfObjectFile elf; + using (var inStream = File.OpenRead(cppExe)) + { + Console.WriteLine($"ReadBack from {cppExe}"); + elf = ElfObjectFile.Read(inStream); + elf.Print(Console.Out); } - [Test] - public void TestDebugLineLibMultipleObjs() - { - var cppName = "lib"; - var libShared = $"{cppName}_debug.so"; - LinuxUtil.RunLinuxExe("gcc", $"{cppName}_a.cpp {cppName}_b.cpp -gdwarf-4 -shared -o {libShared}"); - - ElfObjectFile elf; - using (var inStream = File.OpenRead(libShared)) - { - Console.WriteLine($"ReadBack from {libShared}"); - elf = ElfObjectFile.Read(inStream); - elf.Print(Console.Out); - } - - var elfContext = new DwarfElfContext(elf); - var inputContext = new DwarfReaderContext(elfContext); - inputContext.DebugLinePrinter = Console.Out; - var dwarf = DwarfFile.Read(inputContext); - - inputContext.DebugLineStream.Position = 0; - - var copyInputDebugLineStream = new MemoryStream(); - inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); - inputContext.DebugLineStream.Position = 0; - - var outputContext = new DwarfWriterContext - { - IsLittleEndian = inputContext.IsLittleEndian, - EnableRelocation = false, - AddressSize = inputContext.AddressSize, - DebugLineStream = new MemoryStream() - }; - dwarf.Write(outputContext); - - Console.WriteLine(); - Console.WriteLine("====================================================="); - Console.WriteLine("Readback"); - Console.WriteLine("====================================================="); - Console.WriteLine(); - - var reloadContext = new DwarfReaderContext() - { - IsLittleEndian = outputContext.IsLittleEndian, - AddressSize = outputContext.AddressSize, - DebugLineStream = outputContext.DebugLineStream - }; - - reloadContext.DebugLineStream.Position = 0; - reloadContext.DebugLineStream = outputContext.DebugLineStream; - reloadContext.DebugLinePrinter = Console.Out; - - var dwarf2 = DwarfFile.Read(reloadContext); - - var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); - var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + var elfContext = new DwarfElfContext(elf); + var inputContext = new DwarfReaderContext(elfContext); + inputContext.DebugLinePrinter = Console.Out; + var dwarf = DwarfFile.Read(inputContext); + + inputContext.DebugLineStream.Position = 0; + + var copyInputDebugLineStream = new MemoryStream(); + inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); + inputContext.DebugLineStream.Position = 0; + + var outputContext = new DwarfWriterContext + { + IsLittleEndian = inputContext.IsLittleEndian, + EnableRelocation = false, + AddressSize = inputContext.AddressSize, + DebugLineStream = new MemoryStream() + }; + dwarf.Write(outputContext); + + Console.WriteLine(); + Console.WriteLine("====================================================="); + Console.WriteLine("Readback"); + Console.WriteLine("====================================================="); + Console.WriteLine(); + + var reloadContext = new DwarfReaderContext() + { + IsLittleEndian = outputContext.IsLittleEndian, + AddressSize = outputContext.AddressSize, + DebugLineStream = outputContext.DebugLineStream + }; + + reloadContext.DebugLineStream.Position = 0; + reloadContext.DebugLineStream = outputContext.DebugLineStream; + reloadContext.DebugLinePrinter = Console.Out; + + var dwarf2 = DwarfFile.Read(reloadContext); + + var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); + var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); + Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + } + + [Test] + public void TestDebugLineLibMultipleObjs() + { + var cppName = "lib"; + var libShared = $"{cppName}_debug.so"; + LinuxUtil.RunLinuxExe("gcc", $"{cppName}_a.cpp {cppName}_b.cpp -gdwarf-4 -shared -o {libShared}"); + + ElfObjectFile elf; + using (var inStream = File.OpenRead(libShared)) + { + Console.WriteLine($"ReadBack from {libShared}"); + elf = ElfObjectFile.Read(inStream); + elf.Print(Console.Out); } - [Test] - public void TestDebugLineSmall() - { - var cppName = "small"; - var cppObj = $"{cppName}_debug.o"; - LinuxUtil.RunLinuxExe("gcc", $"{cppName}.cpp -gdwarf-4 -c -o {cppObj}"); - ElfObjectFile elf; - using (var inStream = File.OpenRead(cppObj)) - { - Console.WriteLine($"ReadBack from {cppObj}"); - elf = ElfObjectFile.Read(inStream); - elf.Print(Console.Out); - } - - var elfContext = new DwarfElfContext(elf); - var inputContext = new DwarfReaderContext(elfContext); - inputContext.DebugLinePrinter = Console.Out; - var dwarf = DwarfFile.Read(inputContext); - - inputContext.DebugLineStream.Position = 0; - var copyInputDebugLineStream = new MemoryStream(); - inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); - inputContext.DebugLineStream.Position = 0; - - var outputContext = new DwarfWriterContext - { - IsLittleEndian = inputContext.IsLittleEndian, - AddressSize = inputContext.AddressSize, - DebugLineStream = new MemoryStream() - }; - dwarf.Write(outputContext); - - Console.WriteLine(); - Console.WriteLine("====================================================="); - Console.WriteLine("Readback"); - Console.WriteLine("====================================================="); - Console.WriteLine(); - - var reloadContext = new DwarfReaderContext() - { - IsLittleEndian = outputContext.IsLittleEndian, - AddressSize = outputContext.AddressSize, - DebugLineStream = outputContext.DebugLineStream - }; - - reloadContext.DebugLineStream.Position = 0; - reloadContext.DebugLineStream = outputContext.DebugLineStream; - reloadContext.DebugLinePrinter = Console.Out; - - var dwarf2 = DwarfFile.Read(reloadContext); - - var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); - var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + var elfContext = new DwarfElfContext(elf); + var inputContext = new DwarfReaderContext(elfContext); + inputContext.DebugLinePrinter = Console.Out; + var dwarf = DwarfFile.Read(inputContext); + + inputContext.DebugLineStream.Position = 0; + + var copyInputDebugLineStream = new MemoryStream(); + inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); + inputContext.DebugLineStream.Position = 0; + + var outputContext = new DwarfWriterContext + { + IsLittleEndian = inputContext.IsLittleEndian, + EnableRelocation = false, + AddressSize = inputContext.AddressSize, + DebugLineStream = new MemoryStream() + }; + dwarf.Write(outputContext); + + Console.WriteLine(); + Console.WriteLine("====================================================="); + Console.WriteLine("Readback"); + Console.WriteLine("====================================================="); + Console.WriteLine(); + + var reloadContext = new DwarfReaderContext() + { + IsLittleEndian = outputContext.IsLittleEndian, + AddressSize = outputContext.AddressSize, + DebugLineStream = outputContext.DebugLineStream + }; + + reloadContext.DebugLineStream.Position = 0; + reloadContext.DebugLineStream = outputContext.DebugLineStream; + reloadContext.DebugLinePrinter = Console.Out; + + var dwarf2 = DwarfFile.Read(reloadContext); + + var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); + var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); + Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + } + + [Test] + public void TestDebugLineSmall() + { + var cppName = "small"; + var cppObj = $"{cppName}_debug.o"; + LinuxUtil.RunLinuxExe("gcc", $"{cppName}.cpp -gdwarf-4 -c -o {cppObj}"); + ElfObjectFile elf; + using (var inStream = File.OpenRead(cppObj)) + { + Console.WriteLine($"ReadBack from {cppObj}"); + elf = ElfObjectFile.Read(inStream); + elf.Print(Console.Out); } + var elfContext = new DwarfElfContext(elf); + var inputContext = new DwarfReaderContext(elfContext); + inputContext.DebugLinePrinter = Console.Out; + var dwarf = DwarfFile.Read(inputContext); + + inputContext.DebugLineStream.Position = 0; + var copyInputDebugLineStream = new MemoryStream(); + inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); + inputContext.DebugLineStream.Position = 0; + + var outputContext = new DwarfWriterContext + { + IsLittleEndian = inputContext.IsLittleEndian, + AddressSize = inputContext.AddressSize, + DebugLineStream = new MemoryStream() + }; + dwarf.Write(outputContext); + + Console.WriteLine(); + Console.WriteLine("====================================================="); + Console.WriteLine("Readback"); + Console.WriteLine("====================================================="); + Console.WriteLine(); + + var reloadContext = new DwarfReaderContext() + { + IsLittleEndian = outputContext.IsLittleEndian, + AddressSize = outputContext.AddressSize, + DebugLineStream = outputContext.DebugLineStream + }; + + reloadContext.DebugLineStream.Position = 0; + reloadContext.DebugLineStream = outputContext.DebugLineStream; + reloadContext.DebugLinePrinter = Console.Out; + + var dwarf2 = DwarfFile.Read(reloadContext); + + var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); + var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); + Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + } + - [Test] - public void TestDebugLineMultipleFunctions() - { - var cppName = "multiple_functions"; - var cppObj = $"{cppName}_debug.o"; - LinuxUtil.RunLinuxExe("gcc", $"{cppName}.cpp -gdwarf-4 -c -o {cppObj}"); - - ElfObjectFile elf; - using (var inStream = File.OpenRead(cppObj)) - { - Console.WriteLine($"ReadBack from {cppObj}"); - elf = ElfObjectFile.Read(inStream); - elf.Print(Console.Out); - } - - var elfContext = new DwarfElfContext(elf); - var inputContext = new DwarfReaderContext(elfContext); - inputContext.DebugLinePrinter = Console.Out; - var dwarf = DwarfFile.Read(inputContext); - - inputContext.DebugLineStream.Position = 0; - var copyInputDebugLineStream = new MemoryStream(); - inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); - inputContext.DebugLineStream.Position = 0; - - var outputContext = new DwarfWriterContext - { - IsLittleEndian = inputContext.IsLittleEndian, - AddressSize = inputContext.AddressSize, - DebugLineStream = new MemoryStream() - }; - dwarf.Write(outputContext); - - Console.WriteLine(); - Console.WriteLine("====================================================="); - Console.WriteLine("Readback"); - Console.WriteLine("====================================================="); - Console.WriteLine(); - - var reloadContext = new DwarfReaderContext() - { - IsLittleEndian = outputContext.IsLittleEndian, - AddressSize = outputContext.AddressSize, - DebugLineStream = outputContext.DebugLineStream - }; - - reloadContext.DebugLineStream.Position = 0; - reloadContext.DebugLineStream = outputContext.DebugLineStream; - reloadContext.DebugLinePrinter = Console.Out; - - var dwarf2 = DwarfFile.Read(reloadContext); - - var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); - var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + [Test] + public void TestDebugLineMultipleFunctions() + { + var cppName = "multiple_functions"; + var cppObj = $"{cppName}_debug.o"; + LinuxUtil.RunLinuxExe("gcc", $"{cppName}.cpp -gdwarf-4 -c -o {cppObj}"); + + ElfObjectFile elf; + using (var inStream = File.OpenRead(cppObj)) + { + Console.WriteLine($"ReadBack from {cppObj}"); + elf = ElfObjectFile.Read(inStream); + elf.Print(Console.Out); } + var elfContext = new DwarfElfContext(elf); + var inputContext = new DwarfReaderContext(elfContext); + inputContext.DebugLinePrinter = Console.Out; + var dwarf = DwarfFile.Read(inputContext); + + inputContext.DebugLineStream.Position = 0; + var copyInputDebugLineStream = new MemoryStream(); + inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); + inputContext.DebugLineStream.Position = 0; + + var outputContext = new DwarfWriterContext + { + IsLittleEndian = inputContext.IsLittleEndian, + AddressSize = inputContext.AddressSize, + DebugLineStream = new MemoryStream() + }; + dwarf.Write(outputContext); + + Console.WriteLine(); + Console.WriteLine("====================================================="); + Console.WriteLine("Readback"); + Console.WriteLine("====================================================="); + Console.WriteLine(); + + var reloadContext = new DwarfReaderContext() + { + IsLittleEndian = outputContext.IsLittleEndian, + AddressSize = outputContext.AddressSize, + DebugLineStream = outputContext.DebugLineStream + }; + + reloadContext.DebugLineStream.Position = 0; + reloadContext.DebugLineStream = outputContext.DebugLineStream; + reloadContext.DebugLinePrinter = Console.Out; + + var dwarf2 = DwarfFile.Read(reloadContext); + + var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); + var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); + Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + } + + + [Test] + public void TestDebugInfoSmall() + { + var cppName = "small"; + var cppObj = $"{cppName}_debug.o"; + LinuxUtil.RunLinuxExe("gcc", $"{cppName}.cpp -gdwarf-4 -c -o {cppObj}"); - [Test] - public void TestDebugInfoSmall() - { - var cppName = "small"; - var cppObj = $"{cppName}_debug.o"; - LinuxUtil.RunLinuxExe("gcc", $"{cppName}.cpp -gdwarf-4 -c -o {cppObj}"); - - ElfObjectFile elf; - using (var inStream = File.OpenRead(cppObj)) - { - elf = ElfObjectFile.Read(inStream); - elf.Print(Console.Out); - } - - var elfContext = new DwarfElfContext(elf); - var inputContext = new DwarfReaderContext(elfContext); - var dwarf = DwarfFile.Read(inputContext); - - dwarf.AbbreviationTable.Print(Console.Out); - dwarf.InfoSection.Print(Console.Out); - dwarf.AddressRangeTable.Print(Console.Out); - - PrintStreamLength(inputContext); - - Console.WriteLine(); - Console.WriteLine("===================================================================="); - Console.WriteLine("Write Back"); - Console.WriteLine("===================================================================="); - - var outputContext = new DwarfWriterContext - { - IsLittleEndian = inputContext.IsLittleEndian, - AddressSize = inputContext.AddressSize, - DebugAbbrevStream = new MemoryStream(), - DebugLineStream = new MemoryStream(), - DebugInfoStream = new MemoryStream(), - DebugStringStream = new MemoryStream(), - DebugAddressRangeStream = new MemoryStream() - }; - dwarf.Write(outputContext); - - dwarf.AbbreviationTable.Print(Console.Out); - dwarf.InfoSection.Print(Console.Out); - dwarf.InfoSection.PrintRelocations(Console.Out); - dwarf.AddressRangeTable.Print(Console.Out); - dwarf.AddressRangeTable.PrintRelocations(Console.Out); - - dwarf.WriteToElf(elfContext); - - var cppObj2 = $"{cppName}_debug2.o"; - using (var outStream = new FileStream(cppObj2, FileMode.Create)) - { - elf.Write(outStream); - } - - PrintStreamLength(outputContext); + ElfObjectFile elf; + using (var inStream = File.OpenRead(cppObj)) + { + elf = ElfObjectFile.Read(inStream); + elf.Print(Console.Out); } + var elfContext = new DwarfElfContext(elf); + var inputContext = new DwarfReaderContext(elfContext); + var dwarf = DwarfFile.Read(inputContext); + + dwarf.AbbreviationTable.Print(Console.Out); + dwarf.InfoSection.Print(Console.Out); + dwarf.AddressRangeTable.Print(Console.Out); - [Test] - public void CreateDwarf() + PrintStreamLength(inputContext); + + Console.WriteLine(); + Console.WriteLine("===================================================================="); + Console.WriteLine("Write Back"); + Console.WriteLine("===================================================================="); + + var outputContext = new DwarfWriterContext + { + IsLittleEndian = inputContext.IsLittleEndian, + AddressSize = inputContext.AddressSize, + DebugAbbrevStream = new MemoryStream(), + DebugLineStream = new MemoryStream(), + DebugInfoStream = new MemoryStream(), + DebugStringStream = new MemoryStream(), + DebugAddressRangeStream = new MemoryStream() + }; + dwarf.Write(outputContext); + + dwarf.AbbreviationTable.Print(Console.Out); + dwarf.InfoSection.Print(Console.Out); + dwarf.InfoSection.PrintRelocations(Console.Out); + dwarf.AddressRangeTable.Print(Console.Out); + dwarf.AddressRangeTable.PrintRelocations(Console.Out); + + dwarf.WriteToElf(elfContext); + + var cppObj2 = $"{cppName}_debug2.o"; + using (var outStream = new FileStream(cppObj2, FileMode.Create)) { - // Create ELF object - var elf = new ElfObjectFile(ElfArch.X86_64); + elf.Write(outStream); + } - var codeSection = new ElfBinarySection(new MemoryStream(new byte[0x64])).ConfigureAs(ElfSectionSpecialType.Text); - elf.AddSection(codeSection); - var stringSection = new ElfStringTable(); - elf.AddSection(stringSection); - elf.AddSection(new ElfSymbolTable() { Link = stringSection }); - elf.AddSection(new ElfSectionHeaderStringTable()); + PrintStreamLength(outputContext); + } - var elfDiagnostics = new DiagnosticBag(); - elf.UpdateLayout(elfDiagnostics); - Assert.False(elfDiagnostics.HasErrors); - // Create DWARF Object - var dwarfFile = new DwarfFile(); + [Test] + public void CreateDwarf() + { + // Create ELF object + var elf = new ElfObjectFile(ElfArch.X86_64); + + var codeSection = new ElfBinarySection(new MemoryStream(new byte[0x64])).ConfigureAs(ElfSectionSpecialType.Text); + elf.AddSection(codeSection); + var stringSection = new ElfStringTable(); + elf.AddSection(stringSection); + elf.AddSection(new ElfSymbolTable() { Link = stringSection }); + elf.AddSection(new ElfSectionHeaderStringTable()); + + var elfDiagnostics = new DiagnosticBag(); + elf.UpdateLayout(elfDiagnostics); + Assert.False(elfDiagnostics.HasErrors); + + // Create DWARF Object + var dwarfFile = new DwarfFile(); - // Create .debug_line information - var fileName = new DwarfFileName("check1.cpp") - { - Directory = Environment.CurrentDirectory, - }; - var fileName2 = new DwarfFileName("check2.cpp") - { - Directory = Environment.CurrentDirectory, - }; - - // First line table - for (int i = 0; i < 2; i++) - { - var lineTable = new DwarfLineProgramTable(); - dwarfFile.LineSection.AddLineProgramTable(lineTable); - - lineTable.AddressSize = DwarfAddressSize.Bit64; - lineTable.FileNames.Add(fileName); - lineTable.FileNames.Add(fileName2); - lineTable.AddLineSequence(new DwarfLineSequence() - { + // Create .debug_line information + var fileName = new DwarfFileName("check1.cpp") + { + Directory = Environment.CurrentDirectory, + }; + var fileName2 = new DwarfFileName("check2.cpp") + { + Directory = Environment.CurrentDirectory, + }; + + // First line table + for (int i = 0; i < 2; i++) + { + var lineTable = new DwarfLineProgramTable(); + dwarfFile.LineSection.AddLineProgramTable(lineTable); + + lineTable.AddressSize = DwarfAddressSize.Bit64; + lineTable.FileNames.Add(fileName); + lineTable.FileNames.Add(fileName2); + lineTable.AddLineSequence(new DwarfLineSequence() + { - new DwarfLine() - { - File = fileName, - Address = 0, - Column = 1, - Line = 1, - }, - new DwarfLine() - { - File = fileName, - Address = 1, - Column = 1, - Line = 2, - } + new DwarfLine() + { + File = fileName, + Address = 0, + Column = 1, + Line = 1, + }, + new DwarfLine() + { + File = fileName, + Address = 1, + Column = 1, + Line = 2, } - ); - // NOTE: doesn't seem to be generated by regular GCC - // (it seems that only one line sequence is usually used) - lineTable.AddLineSequence(new DwarfLineSequence() + } + ); + // NOTE: doesn't seem to be generated by regular GCC + // (it seems that only one line sequence is usually used) + lineTable.AddLineSequence(new DwarfLineSequence() + { + + new DwarfLine() { + File = fileName2, + Address = 0, + Column = 1, + Line = 1, + }, + } + ); + } - new DwarfLine() - { - File = fileName2, - Address = 0, - Column = 1, - Line = 1, - }, - } - ); - } - - // Create .debug_info - var rootDIE = new DwarfDIECompileUnit() - { - Name = fileName.Name, - LowPC = 0, // 0 relative to base virtual address - HighPC = (int)codeSection.Size, // default is offset/length after LowPC - CompDir = fileName.Directory, - StmtList = dwarfFile.LineSection.LineTables[0], - }; - var subProgram = new DwarfDIESubprogram() - { - Name = "MyFunction", - }; - rootDIE.AddChild(subProgram); - - var locationList = new DwarfLocationList(); - var regExpression = new DwarfExpression(); - regExpression.AddOperation(new DwarfOperation { Kind = DwarfOperationKindEx.Reg0 }); - var regExpression2 = new DwarfExpression(); - regExpression2.AddOperation(new DwarfOperation { Kind = DwarfOperationKindEx.Reg2 }); - locationList.AddLocationListEntry(new DwarfLocationListEntry - { - Start = 0, - End = 0x10, - Expression = regExpression, - }); - locationList.AddLocationListEntry(new DwarfLocationListEntry - { - Start = 0x10, - End = 0x20, - Expression = regExpression2, - }); - var variable = new DwarfDIEVariable() - { - Name = "a", - Location = locationList, - }; - dwarfFile.LocationSection.AddLocationList(locationList); - subProgram.AddChild(variable); - - var cu = new DwarfCompilationUnit() - { - AddressSize = DwarfAddressSize.Bit64, - Root = rootDIE - }; - dwarfFile.InfoSection.AddUnit(cu); + // Create .debug_info + var rootDIE = new DwarfDIECompileUnit() + { + Name = fileName.Name, + LowPC = 0, // 0 relative to base virtual address + HighPC = (int)codeSection.Size, // default is offset/length after LowPC + CompDir = fileName.Directory, + StmtList = dwarfFile.LineSection.LineTables[0], + }; + var subProgram = new DwarfDIESubprogram() + { + Name = "MyFunction", + }; + rootDIE.AddChild(subProgram); + + var locationList = new DwarfLocationList(); + var regExpression = new DwarfExpression(); + regExpression.AddOperation(new DwarfOperation { Kind = DwarfOperationKindEx.Reg0 }); + var regExpression2 = new DwarfExpression(); + regExpression2.AddOperation(new DwarfOperation { Kind = DwarfOperationKindEx.Reg2 }); + locationList.AddLocationListEntry(new DwarfLocationListEntry + { + Start = 0, + End = 0x10, + Expression = regExpression, + }); + locationList.AddLocationListEntry(new DwarfLocationListEntry + { + Start = 0x10, + End = 0x20, + Expression = regExpression2, + }); + var variable = new DwarfDIEVariable() + { + Name = "a", + Location = locationList, + }; + dwarfFile.LocationSection.AddLocationList(locationList); + subProgram.AddChild(variable); + + var cu = new DwarfCompilationUnit() + { + AddressSize = DwarfAddressSize.Bit64, + Root = rootDIE + }; + dwarfFile.InfoSection.AddUnit(cu); - // AddressRange table - dwarfFile.AddressRangeTable.AddressSize = DwarfAddressSize.Bit64; - dwarfFile.AddressRangeTable.Unit = cu; - dwarfFile.AddressRangeTable.Ranges.Add(new DwarfAddressRange(0, 0, codeSection.Size)); + // AddressRange table + dwarfFile.AddressRangeTable.AddressSize = DwarfAddressSize.Bit64; + dwarfFile.AddressRangeTable.Unit = cu; + dwarfFile.AddressRangeTable.Ranges.Add(new DwarfAddressRange(0, 0, codeSection.Size)); - // Transfer DWARF To ELF - var dwarfElfContext = new DwarfElfContext(elf); - dwarfFile.WriteToElf(dwarfElfContext); - - var outputFileName = "create_dwarf.o"; - using (var output = new FileStream(outputFileName, FileMode.Create)) - { - elf.Write(output); - } + // Transfer DWARF To ELF + var dwarfElfContext = new DwarfElfContext(elf); + dwarfFile.WriteToElf(dwarfElfContext); - elf.Print(Console.Out); - Console.WriteLine(); - dwarfFile.AbbreviationTable.Print(Console.Out); - Console.WriteLine(); - dwarfFile.AddressRangeTable.Print(Console.Out); - Console.WriteLine(); - dwarfFile.InfoSection.Print(Console.Out); - - Console.WriteLine("ReadBack --debug-dump=rawline"); - var readelf = LinuxUtil.ReadElf(outputFileName, "--debug-dump=rawline").TrimEnd(); - Console.WriteLine(readelf); + var outputFileName = "create_dwarf.o"; + using (var output = new FileStream(outputFileName, FileMode.Create)) + { + elf.Write(output); } - private static void PrintStreamLength(DwarfReaderWriterContext context) - { - if (context.DebugInfoStream != null) - { - Console.WriteLine($".debug_info {context.DebugInfoStream.Length}"); - } - if (context.DebugAbbrevStream != null) - { - Console.WriteLine($".debug_abbrev {context.DebugAbbrevStream.Length}"); - } - if (context.DebugAddressRangeStream != null) - { - Console.WriteLine($".debug_aranges {context.DebugAddressRangeStream.Length}"); - } - if (context.DebugStringStream != null) - { - Console.WriteLine($".debug_str {context.DebugStringStream.Length}"); - } - if (context.DebugLineStream != null) - { - Console.WriteLine($".debug_line {context.DebugLineStream.Length}"); - } + elf.Print(Console.Out); + Console.WriteLine(); + dwarfFile.AbbreviationTable.Print(Console.Out); + Console.WriteLine(); + dwarfFile.AddressRangeTable.Print(Console.Out); + Console.WriteLine(); + dwarfFile.InfoSection.Print(Console.Out); + + Console.WriteLine("ReadBack --debug-dump=rawline"); + var readelf = LinuxUtil.ReadElf(outputFileName, "--debug-dump=rawline").TrimEnd(); + Console.WriteLine(readelf); + } + + private static void PrintStreamLength(DwarfReaderWriterContext context) + { + if (context.DebugInfoStream != null) + { + Console.WriteLine($".debug_info {context.DebugInfoStream.Length}"); + } + if (context.DebugAbbrevStream != null) + { + Console.WriteLine($".debug_abbrev {context.DebugAbbrevStream.Length}"); + } + if (context.DebugAddressRangeStream != null) + { + Console.WriteLine($".debug_aranges {context.DebugAddressRangeStream.Length}"); + } + if (context.DebugStringStream != null) + { + Console.WriteLine($".debug_str {context.DebugStringStream.Length}"); + } + if (context.DebugLineStream != null) + { + Console.WriteLine($".debug_line {context.DebugLineStream.Length}"); } } } \ No newline at end of file diff --git a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs index 37dee8f..a4f3376 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -5,8 +5,8 @@ using System; using System.IO; using System.Text; +using LibObjectFile.Diagnostics; using LibObjectFile.Elf; -using NUnit.Framework; namespace LibObjectFile.Tests.Elf; diff --git a/src/LibObjectFile.Tests/Elf/ElfTestBase.cs b/src/LibObjectFile.Tests/Elf/ElfTestBase.cs index fa7b486..80e5a1b 100644 --- a/src/LibObjectFile.Tests/Elf/ElfTestBase.cs +++ b/src/LibObjectFile.Tests/Elf/ElfTestBase.cs @@ -5,95 +5,93 @@ using System; using System.IO; using LibObjectFile.Elf; -using NUnit.Framework; -namespace LibObjectFile.Tests.Elf +namespace LibObjectFile.Tests.Elf; + +public abstract class ElfTestBase { - public abstract class ElfTestBase + protected static void AssertReadElf(ElfObjectFile elf, string fileName) { - protected static void AssertReadElf(ElfObjectFile elf, string fileName) - { - AssertReadElfInternal(elf, fileName); - AssertReadBack(elf, fileName, readAsReadOnly: false); - AssertReadBack(elf, fileName, readAsReadOnly: true); - AssertLsbMsb(elf, fileName); - } + AssertReadElfInternal(elf, fileName); + AssertReadBack(elf, fileName, readAsReadOnly: false); + AssertReadBack(elf, fileName, readAsReadOnly: true); + AssertLsbMsb(elf, fileName); + } - protected static void AssertReadElfInternal(ElfObjectFile elf, string fileName, bool writeFile = true, string context = null, string readElfParams = null) + protected static void AssertReadElfInternal(ElfObjectFile elf, string fileName, bool writeFile = true, string context = null, string readElfParams = null) + { + if (writeFile) { - if (writeFile) + using (var stream = new FileStream(Path.Combine(Environment.CurrentDirectory, fileName), FileMode.Create)) { - using (var stream = new FileStream(Path.Combine(Environment.CurrentDirectory, fileName), FileMode.Create)) - { - elf.Write(stream); - stream.Flush(); - Assert.AreEqual(stream.Length, (long)elf.Layout.TotalSize); - } + elf.Write(stream); + stream.Flush(); + Assert.AreEqual(stream.Length, (long)elf.Layout.TotalSize); } + } - var stringWriter = new StringWriter(); - elf.Print(stringWriter); + var stringWriter = new StringWriter(); + elf.Print(stringWriter); - var result = stringWriter.ToString().Replace("\r\n", "\n").TrimEnd(); + var result = stringWriter.ToString().Replace("\r\n", "\n").TrimEnd(); + Console.WriteLine(result); + readElfParams ??= "-W -a"; + var readelf = LinuxUtil.ReadElf(fileName, readElfParams).TrimEnd(); + if (readelf != result) + { + Console.WriteLine("=== Expected:"); + Console.WriteLine(readelf); + Console.WriteLine("=== Result:"); Console.WriteLine(result); - readElfParams ??= "-W -a"; - var readelf = LinuxUtil.ReadElf(fileName, readElfParams).TrimEnd(); - if (readelf != result) + if (context != null) + { + Assert.AreEqual(readelf, result, context); + } + else { - Console.WriteLine("=== Expected:"); - Console.WriteLine(readelf); - Console.WriteLine("=== Result:"); - Console.WriteLine(result); - if (context != null) - { - Assert.AreEqual(readelf, result, context); - } - else - { - Assert.AreEqual(readelf, result); - } + Assert.AreEqual(readelf, result); } } + } - protected static void AssertReadBack(ElfObjectFile elf, string fileName, bool readAsReadOnly) - { - ElfObjectFile newObjectFile; + protected static void AssertReadBack(ElfObjectFile elf, string fileName, bool readAsReadOnly) + { + ElfObjectFile newObjectFile; - var filePath = Path.Combine(Environment.CurrentDirectory, fileName); - using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - newObjectFile = ElfObjectFile.Read(stream, new ElfReaderOptions() {ReadOnly = readAsReadOnly}); + var filePath = Path.Combine(Environment.CurrentDirectory, fileName); + using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + newObjectFile = ElfObjectFile.Read(stream, new ElfReaderOptions() {ReadOnly = readAsReadOnly}); - Console.WriteLine(); - Console.WriteLine("============================================================================="); - Console.WriteLine($"readback {(readAsReadOnly ? "as readonly" : "as readwrite")}"); - Console.WriteLine("============================================================================="); - Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine("============================================================================="); + Console.WriteLine($"readback {(readAsReadOnly ? "as readonly" : "as readwrite")}"); + Console.WriteLine("============================================================================="); + Console.WriteLine(); - AssertReadElfInternal(newObjectFile, fileName, false, $"Unexpected error while reading back {fileName}"); + AssertReadElfInternal(newObjectFile, fileName, false, $"Unexpected error while reading back {fileName}"); - var originalBuffer = File.ReadAllBytes(filePath); - var memoryStream = new MemoryStream(); - newObjectFile.Write(memoryStream); - var newBuffer = memoryStream.ToArray(); + var originalBuffer = File.ReadAllBytes(filePath); + var memoryStream = new MemoryStream(); + newObjectFile.Write(memoryStream); + var newBuffer = memoryStream.ToArray(); - Assert.AreEqual(originalBuffer, newBuffer, "Invalid binary diff between write -> (original) -> read -> write -> (new)"); - } + Assert.AreEqual(originalBuffer, newBuffer, "Invalid binary diff between write -> (original) -> read -> write -> (new)"); } + } - private static void AssertLsbMsb(ElfObjectFile elf, string fileName) - { - Console.WriteLine(); - Console.WriteLine("*****************************************************************************"); - Console.WriteLine("LSB to MSB"); - Console.WriteLine("*****************************************************************************"); - Console.WriteLine(); + private static void AssertLsbMsb(ElfObjectFile elf, string fileName) + { + Console.WriteLine(); + Console.WriteLine("*****************************************************************************"); + Console.WriteLine("LSB to MSB"); + Console.WriteLine("*****************************************************************************"); + Console.WriteLine(); - elf.Encoding = ElfEncoding.Msb; - var newFileName = Path.GetFileNameWithoutExtension(fileName) + "_msb.elf"; - AssertReadElfInternal(elf, newFileName); - AssertReadBack(elf, newFileName, readAsReadOnly: false); - AssertReadBack(elf, newFileName, readAsReadOnly: true); - } + elf.Encoding = ElfEncoding.Msb; + var newFileName = Path.GetFileNameWithoutExtension(fileName) + "_msb.elf"; + AssertReadElfInternal(elf, newFileName); + AssertReadBack(elf, newFileName, readAsReadOnly: false); + AssertReadBack(elf, newFileName, readAsReadOnly: true); } } \ No newline at end of file diff --git a/src/LibObjectFile.Tests/LinuxUtil.cs b/src/LibObjectFile.Tests/LinuxUtil.cs index 06205fc..3f8c89c 100644 --- a/src/LibObjectFile.Tests/LinuxUtil.cs +++ b/src/LibObjectFile.Tests/LinuxUtil.cs @@ -8,94 +8,93 @@ using System.Runtime.InteropServices; using System.Text; -namespace LibObjectFile.Tests +namespace LibObjectFile.Tests; + +public static class LinuxUtil { - public static class LinuxUtil + public static string ReadElf(string file, string arguments = "-W -a") + { + return RunLinuxExe("readelf", $"{file} {arguments}"); + } + + public static string RunLinuxExe(string exe, string arguments, string distribution = "Ubuntu") { - public static string ReadElf(string file, string arguments = "-W -a") + if (exe == null) throw new ArgumentNullException(nameof(exe)); + if (arguments == null) throw new ArgumentNullException(nameof(arguments)); + if (distribution == null) throw new ArgumentNullException(nameof(distribution)); + + // redirect to a file the output as there is a bug reading back stdout with WSL + var wslOut = $"wsl_stdout_{Guid.NewGuid()}.txt"; + + bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + if (isWindows) { - return RunLinuxExe("readelf", $"{file} {arguments}"); + arguments = $"-d {distribution} {exe} {arguments} > {wslOut}"; + exe = "wsl.exe"; } - public static string RunLinuxExe(string exe, string arguments, string distribution = "Ubuntu") + StringBuilder errorBuilder = null; + StringBuilder outputBuilder = new StringBuilder(); + + using (var process = new Process() + { + StartInfo = new ProcessStartInfo(exe, arguments) + { + UseShellExecute = false, + RedirectStandardOutput = !isWindows, + CreateNoWindow = true, + RedirectStandardError = true, + }, + }) { - if (exe == null) throw new ArgumentNullException(nameof(exe)); - if (arguments == null) throw new ArgumentNullException(nameof(arguments)); - if (distribution == null) throw new ArgumentNullException(nameof(distribution)); - // redirect to a file the output as there is a bug reading back stdout with WSL - var wslOut = $"wsl_stdout_{Guid.NewGuid()}.txt"; - - bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - if (isWindows) + process.ErrorDataReceived += (sender, args) => { - arguments = $"-d {distribution} {exe} {arguments} > {wslOut}"; - exe = "wsl.exe"; - } + if (errorBuilder == null) + { + errorBuilder = new StringBuilder(); + } - StringBuilder errorBuilder = null; - StringBuilder outputBuilder = new StringBuilder(); + errorBuilder.Append(args.Data).Append('\n'); + }; - using (var process = new Process() - { - StartInfo = new ProcessStartInfo(exe, arguments) - { - UseShellExecute = false, - RedirectStandardOutput = !isWindows, - CreateNoWindow = true, - RedirectStandardError = true, - }, - }) + if (!isWindows) { + process.OutputDataReceived += (sender, args) => { outputBuilder.Append(args.Data).Append('\n'); }; + } - process.ErrorDataReceived += (sender, args) => - { - if (errorBuilder == null) - { - errorBuilder = new StringBuilder(); - } + process.Start(); + process.BeginErrorReadLine(); - errorBuilder.Append(args.Data).Append('\n'); - }; + if (!isWindows) + { + process.BeginOutputReadLine(); + } - if (!isWindows) - { - process.OutputDataReceived += (sender, args) => { outputBuilder.Append(args.Data).Append('\n'); }; - } + process.WaitForExit(); - process.Start(); - process.BeginErrorReadLine(); + if (process.ExitCode != 0) + { + throw new InvalidOperationException($"Error while running command `{exe} {arguments}`: {errorBuilder}"); + } - if (!isWindows) + if (isWindows) + { + var generated = Path.Combine(Environment.CurrentDirectory, wslOut); + var result = File.ReadAllText(generated); + try { - process.BeginOutputReadLine(); + File.Delete(generated); } - - process.WaitForExit(); - - if (process.ExitCode != 0) + catch { - throw new InvalidOperationException($"Error while running command `{exe} {arguments}`: {errorBuilder}"); + // ignore } - if (isWindows) - { - var generated = Path.Combine(Environment.CurrentDirectory, wslOut); - var result = File.ReadAllText(generated); - try - { - File.Delete(generated); - } - catch - { - // ignore - } - - return result; - } + return result; } - - return outputBuilder.ToString(); } + + return outputBuilder.ToString(); } } \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArArchiveFile.cs b/src/LibObjectFile/Ar/ArArchiveFile.cs index 301b646..cb22ee5 100644 --- a/src/LibObjectFile/Ar/ArArchiveFile.cs +++ b/src/LibObjectFile/Ar/ArArchiveFile.cs @@ -6,7 +6,8 @@ using System.Collections.Generic; using System.IO; using System.Text; -using LibObjectFile.Utils; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Ar; diff --git a/src/LibObjectFile/Ar/ArArchiveFileReader.cs b/src/LibObjectFile/Ar/ArArchiveFileReader.cs index a137582..564d144 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileReader.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileReader.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; +using LibObjectFile.Diagnostics; using LibObjectFile.Elf; namespace LibObjectFile.Ar; diff --git a/src/LibObjectFile/Ar/ArArchiveFileWriter.cs b/src/LibObjectFile/Ar/ArArchiveFileWriter.cs index 4c59bf1..d9f4853 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileWriter.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileWriter.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.IO; using System.Text; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Ar; diff --git a/src/LibObjectFile/Ar/ArBinaryFile.cs b/src/LibObjectFile/Ar/ArBinaryFile.cs index 3b920c8..83743de 100644 --- a/src/LibObjectFile/Ar/ArBinaryFile.cs +++ b/src/LibObjectFile/Ar/ArBinaryFile.cs @@ -2,7 +2,6 @@ // 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.Ar; diff --git a/src/LibObjectFile/Ar/ArElfFile.cs b/src/LibObjectFile/Ar/ArElfFile.cs index 7ece843..322a136 100644 --- a/src/LibObjectFile/Ar/ArElfFile.cs +++ b/src/LibObjectFile/Ar/ArElfFile.cs @@ -2,9 +2,8 @@ // 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.Elf; -using LibObjectFile.Utils; +using LibObjectFile.IO; namespace LibObjectFile.Ar; diff --git a/src/LibObjectFile/Ar/ArFile.cs b/src/LibObjectFile/Ar/ArFile.cs index 59fefe7..5940ba6 100644 --- a/src/LibObjectFile/Ar/ArFile.cs +++ b/src/LibObjectFile/Ar/ArFile.cs @@ -4,6 +4,7 @@ using System; using System.Text; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Ar; diff --git a/src/LibObjectFile/Ar/ArLongNamesTable.cs b/src/LibObjectFile/Ar/ArLongNamesTable.cs index f7b8932..dab75ca 100644 --- a/src/LibObjectFile/Ar/ArLongNamesTable.cs +++ b/src/LibObjectFile/Ar/ArLongNamesTable.cs @@ -2,7 +2,6 @@ // 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.Text; diff --git a/src/LibObjectFile/Ar/ArSymbolTable.cs b/src/LibObjectFile/Ar/ArSymbolTable.cs index 28d9d06..4d4a2f4 100644 --- a/src/LibObjectFile/Ar/ArSymbolTable.cs +++ b/src/LibObjectFile/Ar/ArSymbolTable.cs @@ -2,10 +2,10 @@ // 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; using System.Diagnostics; using System.Text; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Ar; diff --git a/src/LibObjectFile/Ar/ArVisitorContext.cs b/src/LibObjectFile/Ar/ArVisitorContext.cs index 9c62fd0..ccd600b 100644 --- a/src/LibObjectFile/Ar/ArVisitorContext.cs +++ b/src/LibObjectFile/Ar/ArVisitorContext.cs @@ -2,6 +2,8 @@ // 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; + namespace LibObjectFile.Ar; public class ArVisitorContext : VisitorContextBase diff --git a/src/LibObjectFile/Utils/ObjectList.cs b/src/LibObjectFile/Collections/ObjectList.cs similarity index 94% rename from src/LibObjectFile/Utils/ObjectList.cs rename to src/LibObjectFile/Collections/ObjectList.cs index 94c0c4d..6bad277 100644 --- a/src/LibObjectFile/Utils/ObjectList.cs +++ b/src/LibObjectFile/Collections/ObjectList.cs @@ -8,7 +8,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; -namespace LibObjectFile.Utils; +namespace LibObjectFile.Collections; /// /// A list of objects that are attached to a parent object. @@ -29,7 +29,7 @@ namespace LibObjectFile.Utils; /// Initializes a new instance of the class. /// /// The parent object file node. - public ObjectList(ObjectFileElement parent, Action? added = null, Action? removing = null, Action? removed = null, Action? updated = null) + public ObjectList(ObjectFileElement parent, Action? added = null, Action? removing = null, Action? removed = null, Action? updated = null) { ArgumentNullException.ThrowIfNull(parent); _items = new InternalList(parent, added, removing, removed, updated); @@ -81,7 +81,7 @@ public bool Remove(TObject item) item.Parent = null; item.ResetIndex(); - + return items.Remove(item); } @@ -91,7 +91,7 @@ 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; @@ -106,7 +106,7 @@ public void RemoveAt(int index) var item = items[index]; item.Parent = null; item.Index = 0; - + items.RemoveAt(index); for (int i = index; i < items.Count; i++) @@ -147,7 +147,7 @@ IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); } - + public TObject CheckAdd(TObject item) { ArgumentNullException.ThrowIfNull(item); @@ -158,13 +158,14 @@ public TObject CheckAdd(TObject item) item.Parent = _items.Parent; return item; } - + private sealed class InternalList(ObjectFileElement parent, Action? added, Action? removing, Action? removed, Action? updated) : List { private readonly Action? _added = added; private readonly Action? _removing = removing; private readonly Action? _removed = removed; private readonly Action? _updated = updated; + public readonly ObjectFileElement Parent = parent; [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -172,10 +173,10 @@ private sealed class InternalList(ObjectFileElement parent, Action _removing?.Invoke(Parent, item); - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Updated(int index, TObject previousItem, TObject newItem) => _updated?.Invoke(Parent, index, previousItem, newItem); - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Removed(int index, TObject removedItem) => _removed?.Invoke(Parent, index, removedItem); } @@ -186,8 +187,8 @@ internal sealed class ObjectListDebuggerView public ObjectListDebuggerView(ObjectList collection) { - ArgumentNullException.ThrowIfNull((object)collection, nameof(collection)); - this._collection = collection.UnsafeList; + ArgumentNullException.ThrowIfNull(collection, nameof(collection)); + _collection = collection.UnsafeList; } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] @@ -195,7 +196,7 @@ public TObject[] Items { get { - var array = new TObject[this._collection.Count]; + var array = new TObject[_collection.Count]; _collection.CopyTo(array, 0); return array; } diff --git a/src/LibObjectFile/Utils/ReadOnlyList.cs b/src/LibObjectFile/Collections/ReadOnlyList.cs similarity index 87% rename from src/LibObjectFile/Utils/ReadOnlyList.cs rename to src/LibObjectFile/Collections/ReadOnlyList.cs index 5d6b29c..05b96db 100644 --- a/src/LibObjectFile/Utils/ReadOnlyList.cs +++ b/src/LibObjectFile/Collections/ReadOnlyList.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; -namespace LibObjectFile.Utils; +namespace LibObjectFile.Collections; /// /// A lightweight read-only wrapper around a List<T> that avoids the cost of interface dispatch from IReadOnlyList<T>. @@ -48,15 +48,15 @@ IEnumerator IEnumerable.GetEnumerator() /// /// The list to convert. public static implicit operator ReadOnlyList(List items) => new(items); - + internal sealed class ReadOnlyListView { private readonly List _collection; public ReadOnlyListView(List collection) { - ArgumentNullException.ThrowIfNull((object)collection, nameof(collection)); - this._collection = collection; + ArgumentNullException.ThrowIfNull(collection, nameof(collection)); + _collection = collection; } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] @@ -64,8 +64,8 @@ public T[] Items { get { - T[] array = new T[this._collection.Count]; - this._collection.CopyTo(array, 0); + T[] array = new T[_collection.Count]; + _collection.CopyTo(array, 0); return array; } } diff --git a/src/LibObjectFile/DiagnosticBag.cs b/src/LibObjectFile/Diagnostics/DiagnosticBag.cs similarity index 97% rename from src/LibObjectFile/DiagnosticBag.cs rename to src/LibObjectFile/Diagnostics/DiagnosticBag.cs index 34d1aa1..261e4c2 100644 --- a/src/LibObjectFile/DiagnosticBag.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticBag.cs @@ -6,9 +6,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; -using LibObjectFile.Utils; +using LibObjectFile.Collections; -namespace LibObjectFile; +namespace LibObjectFile.Diagnostics; /// /// A container for used for error reporting while reading/writing object files. @@ -54,7 +54,7 @@ public void CopyTo(DiagnosticBag diagnostics) diagnostics.Log(diagnosticMessage); } } - + /// /// Logs the specified . /// diff --git a/src/LibObjectFile/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs similarity index 99% rename from src/LibObjectFile/DiagnosticId.cs rename to src/LibObjectFile/Diagnostics/DiagnosticId.cs index d7f2910..318a164 100644 --- a/src/LibObjectFile/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -2,7 +2,7 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile; +namespace LibObjectFile.Diagnostics; /// /// Defines the various diagnostic message ids. diff --git a/src/LibObjectFile/DiagnosticKind.cs b/src/LibObjectFile/Diagnostics/DiagnosticKind.cs similarity index 92% rename from src/LibObjectFile/DiagnosticKind.cs rename to src/LibObjectFile/Diagnostics/DiagnosticKind.cs index 5b25bba..d663cec 100644 --- a/src/LibObjectFile/DiagnosticKind.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticKind.cs @@ -2,7 +2,7 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile; +namespace LibObjectFile.Diagnostics; /// /// Defines the kind of a diff --git a/src/LibObjectFile/DiagnosticMessage.cs b/src/LibObjectFile/Diagnostics/DiagnosticMessage.cs similarity index 97% rename from src/LibObjectFile/DiagnosticMessage.cs rename to src/LibObjectFile/Diagnostics/DiagnosticMessage.cs index 3e69010..a384b90 100644 --- a/src/LibObjectFile/DiagnosticMessage.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticMessage.cs @@ -2,7 +2,7 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile; +namespace LibObjectFile.Diagnostics; /// /// A diagnostic message. @@ -44,7 +44,7 @@ public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string message, o /// Gets the associated text of this message. /// public string Message { get; } - + public override string ToString() { return $"{Kind} LB{(uint)Id:0000}: {Message}"; diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs index 728f92f..3ebb6b2 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.IO; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs index 3ff5e5a..64186c7 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs index fa6a41b..74031aa 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; -using LibObjectFile.Utils; +using LibObjectFile.Collections; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs index b96ffff..61bedea 100644 --- a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs @@ -2,10 +2,9 @@ // 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; using System.Diagnostics; -using System.IO; +using LibObjectFile.Diagnostics; using LibObjectFile.Utils; namespace LibObjectFile.Dwarf; @@ -58,7 +57,7 @@ public override void Read(DwarfReader reader) var align = (ulong)segment_selector_size + (ulong)AddressSize * 2; // SPECS 7.21: The first tuple following the header in each set begins at an offset that is a multiple of the size of a single tuple - reader.Position = AlignHelper.AlignToUpper(reader.Position, align); + reader.Position = AlignHelper.AlignUp(reader.Position, align); while (true) { @@ -161,7 +160,7 @@ public override void UpdateLayout(DwarfLayoutContext layoutContext) var align = (ulong)SegmentSelectorSize + (ulong)AddressSize * 2; // SPECS 7.21: The first tuple following the header in each set begins at an offset that is a multiple of the size of a single tuple - sizeOf = AlignHelper.AlignToUpper(sizeOf, align); + sizeOf = AlignHelper.AlignUp(sizeOf, align); // SizeOf ranges + 1 (for last 0 entry) sizeOf += ((ulong)Ranges.Count + 1UL) * align; @@ -202,7 +201,7 @@ public override void Write(DwarfWriter writer) var align = (ulong)SegmentSelectorSize + (ulong)AddressSize * 2; // SPECS 7.21: The first tuple following the header in each set begins at an offset that is a multiple of the size of a single tuple - var nextOffset = AlignHelper.AlignToUpper(writer.Position, align); + var nextOffset = AlignHelper.AlignUp(writer.Position, align); for (ulong offset = writer.Position; offset < nextOffset; offset++) { writer.WriteU8(0); diff --git a/src/LibObjectFile/Dwarf/DwarfAttribute.cs b/src/LibObjectFile/Dwarf/DwarfAttribute.cs index 8ef8f61..bdb0427 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttribute.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttribute.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.IO; using System.Text; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs b/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs index 366ad84..f6c28d2 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs @@ -3,7 +3,6 @@ // See the license.txt file in the project root for more information. using System; -using System.Collections; using System.Diagnostics; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfContainer.cs b/src/LibObjectFile/Dwarf/DwarfContainer.cs index 994c6c4..55cd4bb 100644 --- a/src/LibObjectFile/Dwarf/DwarfContainer.cs +++ b/src/LibObjectFile/Dwarf/DwarfContainer.cs @@ -2,8 +2,6 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -using System; - namespace LibObjectFile.Dwarf; public abstract class DwarfContainer : DwarfObject diff --git a/src/LibObjectFile/Dwarf/DwarfDIE.cs b/src/LibObjectFile/Dwarf/DwarfDIE.cs index 5d8c1ef..a8d893e 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIE.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIE.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; -using LibObjectFile.Utils; +using LibObjectFile.Collections; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfElfContext.cs b/src/LibObjectFile/Dwarf/DwarfElfContext.cs index df17966..1547da7 100644 --- a/src/LibObjectFile/Dwarf/DwarfElfContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfElfContext.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using LibObjectFile.Diagnostics; using LibObjectFile.Elf; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfExpression.cs b/src/LibObjectFile/Dwarf/DwarfExpression.cs index 81b1cf0..bdde8ba 100644 --- a/src/LibObjectFile/Dwarf/DwarfExpression.cs +++ b/src/LibObjectFile/Dwarf/DwarfExpression.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using LibObjectFile.Utils; +using LibObjectFile.Collections; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfFile.cs b/src/LibObjectFile/Dwarf/DwarfFile.cs index 5462e98..3590c35 100644 --- a/src/LibObjectFile/Dwarf/DwarfFile.cs +++ b/src/LibObjectFile/Dwarf/DwarfFile.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using LibObjectFile.Diagnostics; using LibObjectFile.Elf; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfHelper.cs b/src/LibObjectFile/Dwarf/DwarfHelper.cs index d403144..0aef1b4 100644 --- a/src/LibObjectFile/Dwarf/DwarfHelper.cs +++ b/src/LibObjectFile/Dwarf/DwarfHelper.cs @@ -2,8 +2,8 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -using System.Text; using System.Numerics; +using System.Text; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs index 2cd746d..c5afef4 100644 --- a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; -using LibObjectFile.Utils; +using LibObjectFile.Collections; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs b/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs index da6cc10..fca11a2 100644 --- a/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs @@ -2,6 +2,8 @@ // 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; + namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs index 4826b47..6bd1692 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs @@ -5,9 +5,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Text; -using LibObjectFile.Utils; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfLineSection.cs b/src/LibObjectFile/Dwarf/DwarfLineSection.cs index d426f62..7f55a70 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSection.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; -using LibObjectFile.Utils; +using LibObjectFile.Collections; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs index 9fd1884..9a50ad6 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs @@ -5,7 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using LibObjectFile.Utils; +using LibObjectFile.Collections; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfLocationList.cs b/src/LibObjectFile/Dwarf/DwarfLocationList.cs index f22dfdd..b6d9627 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationList.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationList.cs @@ -2,9 +2,9 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -using System.Text; using System.Collections.Generic; -using LibObjectFile.Utils; +using System.Text; +using LibObjectFile.Collections; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs index eb3fd4b..86d1077 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; -using LibObjectFile.Utils; +using LibObjectFile.Collections; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfOperation.cs b/src/LibObjectFile/Dwarf/DwarfOperation.cs index 5eac45c..ace98ae 100644 --- a/src/LibObjectFile/Dwarf/DwarfOperation.cs +++ b/src/LibObjectFile/Dwarf/DwarfOperation.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.IO; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfReader.cs b/src/LibObjectFile/Dwarf/DwarfReader.cs index 19f56b5..186ba55 100644 --- a/src/LibObjectFile/Dwarf/DwarfReader.cs +++ b/src/LibObjectFile/Dwarf/DwarfReader.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs b/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs index 7566d9f..b369cfe 100644 --- a/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs +++ b/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs @@ -3,7 +3,7 @@ // See the license.txt file in the project root for more information. using System; -using System.IO; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs b/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs index a2023be..3d09422 100644 --- a/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Linq; using LibObjectFile.Elf; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfStringTable.cs b/src/LibObjectFile/Dwarf/DwarfStringTable.cs index 023ab21..528af5e 100644 --- a/src/LibObjectFile/Dwarf/DwarfStringTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfStringTable.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfUnit.cs b/src/LibObjectFile/Dwarf/DwarfUnit.cs index 42fafac..2fd4312 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnit.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfWriter.cs b/src/LibObjectFile/Dwarf/DwarfWriter.cs index ba5889f..af3c975 100644 --- a/src/LibObjectFile/Dwarf/DwarfWriter.cs +++ b/src/LibObjectFile/Dwarf/DwarfWriter.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfWriterContext.cs b/src/LibObjectFile/Dwarf/DwarfWriterContext.cs index c28730f..c1a3001 100644 --- a/src/LibObjectFile/Dwarf/DwarfWriterContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfWriterContext.cs @@ -3,7 +3,6 @@ // See the license.txt file in the project root for more information. using System; -using LibObjectFile.Elf; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Elf/ElfObjectFile.cs b/src/LibObjectFile/Elf/ElfObjectFile.cs index b56a3d2..6c6bec6 100644 --- a/src/LibObjectFile/Elf/ElfObjectFile.cs +++ b/src/LibObjectFile/Elf/ElfObjectFile.cs @@ -5,9 +5,10 @@ using System; using System.Buffers; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; using LibObjectFile.Utils; namespace LibObjectFile.Elf; @@ -256,7 +257,7 @@ public unsafe void UpdateLayout(DiagnosticBag diagnostics) } var align = section.Alignment == 0 ? 1 : section.Alignment; - offset = AlignHelper.AlignToUpper(offset, align); + offset = AlignHelper.AlignUp(offset, align); section.Position = offset; if (section is ElfProgramHeaderTable programHeaderTable) @@ -309,7 +310,7 @@ public unsafe void UpdateLayout(DiagnosticBag diagnostics) } // The Section Header Table will be put just after all the sections - Layout.OffsetOfSectionHeaderTable = AlignHelper.AlignToUpper(offset, FileClass == ElfFileClass.Is32 ? 4u : 8u); + Layout.OffsetOfSectionHeaderTable = AlignHelper.AlignUp(offset, FileClass == ElfFileClass.Is32 ? 4u : 8u); Layout.TotalSize = Layout.OffsetOfSectionHeaderTable + (ulong)VisibleSectionCount * Layout.SizeOfSectionHeaderEntry; } diff --git a/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs b/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs index fb60352..095328a 100644 --- a/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs +++ b/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/ElfPrinter.cs b/src/LibObjectFile/Elf/ElfPrinter.cs index 0eb4f71..4e9a1c7 100644 --- a/src/LibObjectFile/Elf/ElfPrinter.cs +++ b/src/LibObjectFile/Elf/ElfPrinter.cs @@ -3,10 +3,8 @@ // See the license.txt file in the project root for more information. using System; -using System.Buffers; using System.IO; using System.Text; -using System.Xml; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/ElfReader.cs b/src/LibObjectFile/Elf/ElfReader.cs index 191d701..e4a7dbf 100644 --- a/src/LibObjectFile/Elf/ElfReader.cs +++ b/src/LibObjectFile/Elf/ElfReader.cs @@ -3,7 +3,6 @@ // See the license.txt file in the project root for more information. using System; -using System.Buffers; using System.IO; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/ElfReaderOptions.cs b/src/LibObjectFile/Elf/ElfReaderOptions.cs index 0bcf4f4..39fa1ec 100644 --- a/src/LibObjectFile/Elf/ElfReaderOptions.cs +++ b/src/LibObjectFile/Elf/ElfReaderOptions.cs @@ -2,6 +2,8 @@ // 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; + namespace LibObjectFile.Elf; /// diff --git a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs index 94608b9..87af089 100644 --- a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs +++ b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.IO; -using static System.Collections.Specialized.BitVector32; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/ElfSection.cs b/src/LibObjectFile/Elf/ElfSection.cs index fb1f9b1..7c8b0fc 100644 --- a/src/LibObjectFile/Elf/ElfSection.cs +++ b/src/LibObjectFile/Elf/ElfSection.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Text; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/ElfSectionExtension.cs b/src/LibObjectFile/Elf/ElfSectionExtension.cs index 81cccf1..fc39e07 100644 --- a/src/LibObjectFile/Elf/ElfSectionExtension.cs +++ b/src/LibObjectFile/Elf/ElfSectionExtension.cs @@ -3,7 +3,6 @@ // See the license.txt file in the project root for more information. using System; -using LibObjectFile.Utils; namespace LibObjectFile.Elf; @@ -146,7 +145,7 @@ public static TElfSection ConfigureAs(this TElfSection section, Elf section.Flags = ElfSectionFlags.Alloc | ElfSectionFlags.Executable; break; default: - throw ThrowHelper.InvalidEnum(sectionSpecialType); + throw new InvalidOperationException($"Invalid Enum {sectionSpecialType.GetType()}.{sectionSpecialType}"); } return section; } diff --git a/src/LibObjectFile/Elf/ElfSectionLink.cs b/src/LibObjectFile/Elf/ElfSectionLink.cs index deea99b..867ea2f 100644 --- a/src/LibObjectFile/Elf/ElfSectionLink.cs +++ b/src/LibObjectFile/Elf/ElfSectionLink.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/ElfSegment.cs b/src/LibObjectFile/Elf/ElfSegment.cs index 99edbeb..40d709e 100644 --- a/src/LibObjectFile/Elf/ElfSegment.cs +++ b/src/LibObjectFile/Elf/ElfSegment.cs @@ -2,9 +2,9 @@ // 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.Numerics; using System.Text; -using LibObjectFile.Utils; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Elf; @@ -70,7 +70,7 @@ public override void UpdateLayout(ElfVisitorContext context) // TODO: Add checks that Alignment is Power Of 2 var alignment = Alignment == 0 ? Alignment = 1 : Alignment; - if (!AlignHelper.IsPowerOfTwo(alignment)) + if (!BitOperations.IsPow2(alignment)) { diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSegmentAlignmentForLoad, $"Invalid segment alignment requirements: Alignment = {alignment} must be a power of 2"); } diff --git a/src/LibObjectFile/Elf/ElfVisitorContext.cs b/src/LibObjectFile/Elf/ElfVisitorContext.cs index 86b4059..5c09394 100644 --- a/src/LibObjectFile/Elf/ElfVisitorContext.cs +++ b/src/LibObjectFile/Elf/ElfVisitorContext.cs @@ -2,6 +2,8 @@ // 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; + namespace LibObjectFile.Elf; public class ElfVisitorContext : VisitorContextBase diff --git a/src/LibObjectFile/Elf/ElfWriter.cs b/src/LibObjectFile/Elf/ElfWriter.cs index 312bbb0..f364fab 100644 --- a/src/LibObjectFile/Elf/ElfWriter.cs +++ b/src/LibObjectFile/Elf/ElfWriter.cs @@ -3,7 +3,6 @@ // See the license.txt file in the project root for more information. using System; -using System.Buffers; using System.IO; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs b/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs index aa0d6cd..d11215c 100644 --- a/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; +using LibObjectFile.Diagnostics; using LibObjectFile.Utils; namespace LibObjectFile.Elf; @@ -34,7 +35,7 @@ public ElfAlignedShadowSection(uint upperAlignment) public override void UpdateLayout(ElfVisitorContext context) { - var nextSectionOffset = AlignHelper.AlignToUpper(Position, UpperAlignment); + var nextSectionOffset = AlignHelper.AlignUp(Position, UpperAlignment); Size = nextSectionOffset - Position; if (Size >= int.MaxValue) { diff --git a/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs b/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs index 83c2f8a..bff47f2 100644 --- a/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs b/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs index 8a6181d..72d2e4a 100644 --- a/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs @@ -2,7 +2,6 @@ // 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.Elf; diff --git a/src/LibObjectFile/Elf/Sections/ElfGnuNoteABITag.cs b/src/LibObjectFile/Elf/Sections/ElfGnuNoteABITag.cs index 4731d39..7b4c743 100644 --- a/src/LibObjectFile/Elf/Sections/ElfGnuNoteABITag.cs +++ b/src/LibObjectFile/Elf/Sections/ElfGnuNoteABITag.cs @@ -2,8 +2,8 @@ // 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 LibObjectFile.Diagnostics; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs b/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs index 7a37fa0..909ba0d 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using LibObjectFile.Diagnostics; using LibObjectFile.Utils; namespace LibObjectFile.Elf; @@ -47,10 +48,10 @@ public override unsafe void UpdateLayout(ElfVisitorContext context) { var name = elfNote.GetName(); size += (ulong)Encoding.UTF8.GetByteCount(name) + 1; - size = AlignHelper.AlignToUpper(size, 4); + size = AlignHelper.AlignUp(size, 4); size += (ulong)elfNote.GetDescriptorSize(); - size = AlignHelper.AlignToUpper(size, 4); + size = AlignHelper.AlignUp(size, 4); size += entrySize; } diff --git a/src/LibObjectFile/Elf/Sections/ElfNullSection.cs b/src/LibObjectFile/Elf/Sections/ElfNullSection.cs index a7e79b1..4c8d48f 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNullSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNullSection.cs @@ -2,7 +2,7 @@ // 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.Diagnostics; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs b/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs index 0dd3cc5..cb69817 100644 --- a/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs @@ -2,8 +2,6 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -using System; - namespace LibObjectFile.Elf; /// diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs b/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs index 11288f6..e328ddd 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -using System.IO; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs b/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs index 20adf3e..9fbaf27 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs @@ -3,9 +3,7 @@ // See the license.txt file in the project root for more information. using System; -using System.Collections.Generic; using System.IO; -using LibObjectFile.Dwarf; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/Sections/ElfStringTable.cs b/src/LibObjectFile/Elf/Sections/ElfStringTable.cs index 89b27ac..f120f9e 100644 --- a/src/LibObjectFile/Elf/Sections/ElfStringTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfStringTable.cs @@ -7,8 +7,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Text; using System.Linq; +using System.Text; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs index 760ce19..ba9b518 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/Utils/ThrowHelper.cs b/src/LibObjectFile/IO/StreamExtensions.cs similarity index 81% rename from src/LibObjectFile/Utils/ThrowHelper.cs rename to src/LibObjectFile/IO/StreamExtensions.cs index 28ef197..dc21cdd 100644 --- a/src/LibObjectFile/Utils/ThrowHelper.cs +++ b/src/LibObjectFile/IO/StreamExtensions.cs @@ -8,19 +8,8 @@ using System.IO; using System.Text; -namespace LibObjectFile.Utils; +namespace LibObjectFile.IO; -/// -/// Internal helper class for throwing exceptions. -/// -internal static class ThrowHelper -{ - public static InvalidOperationException InvalidEnum(object v) - { - return new InvalidOperationException($"Invalid Enum {v.GetType()}.{v}"); - } -} - public static class StreamExtensions { /// @@ -29,7 +18,7 @@ public static class StreamExtensions /// true if the string was successfully read from the stream, false otherwise public static string ReadStringUTF8NullTerminated(this Stream stream) { - if (!TryReadStringUTF8NullTerminated(stream, out var text)) + if (!stream.TryReadStringUTF8NullTerminated(out var text)) { throw new EndOfStreamException(); } @@ -43,7 +32,7 @@ public static string ReadStringUTF8NullTerminated(this Stream stream) public static bool TryReadStringUTF8NullTerminated(this Stream stream, [NotNullWhen(true)] out string? text) { text = null; - var buffer = ArrayPool.Shared.Rent((int)128); + var buffer = ArrayPool.Shared.Rent(128); int textLength = 0; try { @@ -63,7 +52,7 @@ public static bool TryReadStringUTF8NullTerminated(this Stream stream, [NotNullW if (textLength > buffer.Length) { - var newBuffer = ArrayPool.Shared.Rent((int)textLength * 2); + var newBuffer = ArrayPool.Shared.Rent(textLength * 2); Array.Copy(buffer, 0, newBuffer, 0, buffer.Length); ArrayPool.Shared.Return(buffer); buffer = newBuffer; diff --git a/src/LibObjectFile/Utils/SubStream.cs b/src/LibObjectFile/IO/SubStream.cs similarity index 97% rename from src/LibObjectFile/Utils/SubStream.cs rename to src/LibObjectFile/IO/SubStream.cs index 3a75c9a..d071431 100644 --- a/src/LibObjectFile/Utils/SubStream.cs +++ b/src/LibObjectFile/IO/SubStream.cs @@ -8,7 +8,7 @@ using System.Threading; using System.Threading.Tasks; -namespace LibObjectFile.Utils; +namespace LibObjectFile.IO; /// /// Defines a stream as a slice of another existing stream. @@ -78,10 +78,10 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation _localPosition += read; return read; } - + private void ThrowIfDisposed() { - ObjectDisposedException.ThrowIf(_baseStream == Stream.Null, this); + ObjectDisposedException.ThrowIf(_baseStream == Null, this); } public override long Length @@ -158,7 +158,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); if (disposing) { - if (_baseStream != Stream.Null) + if (_baseStream != Null) { try { @@ -168,7 +168,7 @@ protected override void Dispose(bool disposing) { // ignored } - _baseStream = Stream.Null; + _baseStream = Null; } } } @@ -192,7 +192,7 @@ public override void Write(ReadOnlySpan buffer) ThrowCannotWriteOutside(); } } - + public override void WriteByte(byte value) { ThrowIfDisposed(); diff --git a/src/LibObjectFile/ObjectFileElement.cs b/src/LibObjectFile/ObjectFileElement.cs index 91f9a7d..b0eb280 100644 --- a/src/LibObjectFile/ObjectFileElement.cs +++ b/src/LibObjectFile/ObjectFileElement.cs @@ -2,8 +2,8 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -using System.Diagnostics; using System; +using System.Diagnostics; using System.Text; namespace LibObjectFile; diff --git a/src/LibObjectFile/ObjectFileException.cs b/src/LibObjectFile/ObjectFileException.cs index 4bdd2bd..e8cee79 100644 --- a/src/LibObjectFile/ObjectFileException.cs +++ b/src/LibObjectFile/ObjectFileException.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using LibObjectFile.Diagnostics; namespace LibObjectFile; diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs index 91cb10f..cd82018 100644 --- a/src/LibObjectFile/ObjectFileReaderWriter.cs +++ b/src/LibObjectFile/ObjectFileReaderWriter.cs @@ -4,11 +4,10 @@ using System; using System.Buffers; -using System.Buffers.Binary; using System.IO; using System.Runtime.InteropServices; -using System.Text; -using LibObjectFile.Utils; +using LibObjectFile.Diagnostics; +using LibObjectFile.IO; namespace LibObjectFile; diff --git a/src/LibObjectFile/ObjectFileStreamExtensions.cs b/src/LibObjectFile/ObjectFileStreamExtensions.cs index 220887d..eeebf36 100644 --- a/src/LibObjectFile/ObjectFileStreamExtensions.cs +++ b/src/LibObjectFile/ObjectFileStreamExtensions.cs @@ -7,7 +7,6 @@ using System.Buffers.Binary; using System.IO; using System.Text; -using LibObjectFile.Utils; namespace LibObjectFile; diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs index 10f66c8..44ca038 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs @@ -5,9 +5,9 @@ using System; using System.Buffers; using System.Collections.Generic; -using System.IO; using System.Runtime.InteropServices; using System.Text; +using LibObjectFile.Diagnostics; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs index 04862b1..f6b9f5a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; -using static System.Reflection.Metadata.BlobBuilder; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs index 2b97071..c818b1b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs @@ -3,7 +3,6 @@ // See the license.txt file in the project root for more information. using System; -using System.Text; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs index 27a01a1..753cdab 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using LibObjectFile.Diagnostics; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs index f66d681..b18f995 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs @@ -3,7 +3,7 @@ // See the license.txt file in the project root for more information. using System; -using LibObjectFile.Utils; +using LibObjectFile.Collections; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 5629d0b..fced964 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -3,10 +3,10 @@ // See the license.txt file in the project root for more information. using System; -using System.Collections.Generic; using System.Runtime.InteropServices; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; -using LibObjectFile.Utils; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs index 62f8337..87e738a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs @@ -2,8 +2,8 @@ // 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; +using LibObjectFile.PE.Internal; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index 337fc97..defb21d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Collections.Generic; using System.Runtime.InteropServices; +using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs index 47ae804..4c0a9ff 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using LibObjectFile.Diagnostics; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/ImageOptionalHeader.cs b/src/LibObjectFile/PE/ImageOptionalHeader.cs index d0e2ac5..cb4d0af 100644 --- a/src/LibObjectFile/PE/ImageOptionalHeader.cs +++ b/src/LibObjectFile/PE/ImageOptionalHeader.cs @@ -2,7 +2,6 @@ // 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.CodeAnalysis; using LibObjectFile.PE.Internal; diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index dd10cfd..9b73f57 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -9,6 +9,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/PEFile.Write.cs b/src/LibObjectFile/PE/PEFile.Write.cs index a582818..11bac13 100644 --- a/src/LibObjectFile/PE/PEFile.Write.cs +++ b/src/LibObjectFile/PE/PEFile.Write.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using LibObjectFile.Diagnostics; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 34662dd..5805035 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -10,7 +10,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; -using LibObjectFile.Utils; +using LibObjectFile.Collections; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/PEImageReaderOptions.cs b/src/LibObjectFile/PE/PEImageReaderOptions.cs index 76bf40e..9ba1282 100644 --- a/src/LibObjectFile/PE/PEImageReaderOptions.cs +++ b/src/LibObjectFile/PE/PEImageReaderOptions.cs @@ -2,8 +2,6 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -using System; - namespace LibObjectFile.PE; public class PEImageReaderOptions diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index 3aef359..7d3c02d 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -3,16 +3,13 @@ // See the license.txt file in the project root for more information. using System; -using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Drawing; -using System.Reflection; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; -using LibObjectFile.Utils; +using LibObjectFile.Collections; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/PESectionDataExtensions.cs b/src/LibObjectFile/PE/PESectionDataExtensions.cs index 2bc0bcd..7584039 100644 --- a/src/LibObjectFile/PE/PESectionDataExtensions.cs +++ b/src/LibObjectFile/PE/PESectionDataExtensions.cs @@ -4,7 +4,6 @@ using System; using System.Buffers; -using System.IO; using System.Runtime.InteropServices; using System.Text; diff --git a/src/LibObjectFile/PE/PESectionName.Defaults.cs b/src/LibObjectFile/PE/PESectionName.Defaults.cs index 7a0bd30..fab3579 100644 --- a/src/LibObjectFile/PE/PESectionName.Defaults.cs +++ b/src/LibObjectFile/PE/PESectionName.Defaults.cs @@ -2,10 +2,6 @@ // 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.Numerics; -using System.Text; - namespace LibObjectFile.PE; /// diff --git a/src/LibObjectFile/PE/PESectionName.cs b/src/LibObjectFile/PE/PESectionName.cs index 6fe9af7..5a31c0a 100644 --- a/src/LibObjectFile/PE/PESectionName.cs +++ b/src/LibObjectFile/PE/PESectionName.cs @@ -3,7 +3,6 @@ // See the license.txt file in the project root for more information. using System; -using System.Numerics; using System.Text; namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/PEVisitorContext.cs b/src/LibObjectFile/PE/PEVisitorContext.cs index 65dea68..aca6c4f 100644 --- a/src/LibObjectFile/PE/PEVisitorContext.cs +++ b/src/LibObjectFile/PE/PEVisitorContext.cs @@ -2,6 +2,8 @@ // 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; + namespace LibObjectFile.PE; public sealed class PEVisitorContext : VisitorContextBase diff --git a/src/LibObjectFile/PE/RVALink.cs b/src/LibObjectFile/PE/RVALink.cs index 08923ae..7589023 100644 --- a/src/LibObjectFile/PE/RVALink.cs +++ b/src/LibObjectFile/PE/RVALink.cs @@ -2,8 +2,6 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -using System.Text; - namespace LibObjectFile.PE; /// diff --git a/src/LibObjectFile/Utils/AlignHelper.cs b/src/LibObjectFile/Utils/AlignHelper.cs index 59ac509..aab292d 100644 --- a/src/LibObjectFile/Utils/AlignHelper.cs +++ b/src/LibObjectFile/Utils/AlignHelper.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using System.Numerics; using System.Runtime.CompilerServices; namespace LibObjectFile.Utils; @@ -12,16 +13,6 @@ namespace LibObjectFile.Utils; /// public static class AlignHelper { - /// - /// Returns true if alignment is a power of 2. - /// - /// The alignment - /// true if it is a power of 2. - public static bool IsPowerOfTwo(ulong align) - { - return (align & (align - 1)) == 0; - } - /// /// Aligns a value to the required alignment. /// @@ -29,10 +20,10 @@ public static bool IsPowerOfTwo(ulong align) /// The alignment. /// The value aligned or unchanged it is was already aligned. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong AlignToUpper(ulong value, ulong align) + public static ulong AlignUp(ulong value, ulong align) { if (align == 0) throw new ArgumentOutOfRangeException(nameof(align), "Alignment must be > 0"); - if (!IsPowerOfTwo(align)) throw new ArgumentOutOfRangeException(nameof(align), "Alignment must be a power of 2"); + if (!BitOperations.IsPow2(align)) throw new ArgumentOutOfRangeException(nameof(align), "Alignment must be a power of 2"); var nextValue = ((value + align - 1) / align) * align; return nextValue; diff --git a/src/LibObjectFile/VisitorContextBase.cs b/src/LibObjectFile/VisitorContextBase.cs index c374b35..5989507 100644 --- a/src/LibObjectFile/VisitorContextBase.cs +++ b/src/LibObjectFile/VisitorContextBase.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using LibObjectFile; +using LibObjectFile.Diagnostics; /// /// Base class used for layout-ing an object file. diff --git a/src/objdasm/ObjDisasmApp.cs b/src/objdasm/ObjDisasmApp.cs index 2f5ea0c..2550124 100644 --- a/src/objdasm/ObjDisasmApp.cs +++ b/src/objdasm/ObjDisasmApp.cs @@ -13,168 +13,167 @@ using LibObjectFile.Elf; using Decoder = Iced.Intel.Decoder; -namespace LibObjectFile.Disasm +namespace LibObjectFile.Disasm; + +public class ObjDisasmApp { - public class ObjDisasmApp + public ObjDisasmApp() { - public ObjDisasmApp() - { - FunctionRegexFilters = new List(); - Files = new List(); - Output = Console.Out; - } + FunctionRegexFilters = new List(); + Files = new List(); + Output = Console.Out; + } - public List FunctionRegexFilters { get; } + public List FunctionRegexFilters { get; } - public List Files { get; } + public List Files { get; } - public bool Verbose { get; set; } + public bool Verbose { get; set; } - public bool Listing { get; set; } + public bool Listing { get; set; } - public TextWriter Output { get; set; } + public TextWriter Output { get; set; } - public void Run() + public void Run() + { + foreach (var file in Files) { - foreach (var file in Files) - { - using var stream = new FileStream(file, FileMode.Open, FileAccess.Read); + using var stream = new FileStream(file, FileMode.Open, FileAccess.Read); - if (ArArchiveFile.IsAr(stream)) - { - var options = new ArArchiveFileReaderOptions(ArArchiveKind.GNU); + if (ArArchiveFile.IsAr(stream)) + { + var options = new ArArchiveFileReaderOptions(ArArchiveKind.GNU); - var archive = ArArchiveFile.Read(stream, options); + var archive = ArArchiveFile.Read(stream, options); - foreach (var objFile in archive.Files) + foreach (var objFile in archive.Files) + { + if (objFile is ArElfFile elfFile) { - if (objFile is ArElfFile elfFile) - { - ProcessElf(objFile.Name, elfFile.ElfObjectFile); - } + ProcessElf(objFile.Name, elfFile.ElfObjectFile); } } - else if (ElfObjectFile.IsElf(stream)) - { - var elfObjectFile = ElfObjectFile.Read(stream, new ElfReaderOptions() {ReadOnly = true}); - ProcessElf(Path.GetFileName(file), elfObjectFile); - } + } + else if (ElfObjectFile.IsElf(stream)) + { + var elfObjectFile = ElfObjectFile.Read(stream, new ElfReaderOptions() {ReadOnly = true}); + ProcessElf(Path.GetFileName(file), elfObjectFile); } } + } - private void ProcessElf(string name, ElfObjectFile elfObjectFile) + private void ProcessElf(string name, ElfObjectFile elfObjectFile) + { + foreach(var symbolTable in elfObjectFile.Sections.OfType()) { - foreach(var symbolTable in elfObjectFile.Sections.OfType()) + foreach(var symbol in symbolTable.Entries) { - foreach(var symbol in symbolTable.Entries) - { - if (symbol.Type != ElfSymbolType.Function) continue; - if (symbol.Bind == ElfSymbolBind.Local) continue; + if (symbol.Type != ElfSymbolType.Function) continue; + if (symbol.Bind == ElfSymbolBind.Local) continue; - if (FunctionRegexFilters.Count > 0) + if (FunctionRegexFilters.Count > 0) + { + foreach (var functionRegexFilter in FunctionRegexFilters) { - foreach (var functionRegexFilter in FunctionRegexFilters) + if (functionRegexFilter.Match(symbol.Name).Success) { - if (functionRegexFilter.Match(symbol.Name).Success) - { - DumpFunction(symbol); - break; - } + DumpFunction(symbol); + break; } } - else - { - DumpFunction(symbol); - } + } + else + { + DumpFunction(symbol); } } } + } - private void DumpFunction(ElfSymbol symbol) - { - var functionSize = symbol.Size; - var section = symbol.Section.Section; - Output.WriteLine($"Function: {symbol.Name}"); + private void DumpFunction(ElfSymbol symbol) + { + var functionSize = symbol.Size; + var section = symbol.Section.Section; + Output.WriteLine($"Function: {symbol.Name}"); - if (section is ElfBinarySection binarySection) - { - binarySection.Stream.Position = (long)symbol.Value; + if (section is ElfBinarySection binarySection) + { + binarySection.Stream.Position = (long)symbol.Value; - Disasm(binarySection.Stream, (uint)functionSize, Output); - Output.WriteLine(); - } + Disasm(binarySection.Stream, (uint)functionSize, Output); + Output.WriteLine(); } + } - private static void Disasm(Stream stream, uint size, TextWriter writer, Formatter formatter = null) - { - var buffer = ArrayPool.Shared.Rent((int)size); - var startPosition = stream.Position; - stream.Read(buffer, 0, (int) size); - stream.Position = startPosition; + private static void Disasm(Stream stream, uint size, TextWriter writer, Formatter formatter = null) + { + var buffer = ArrayPool.Shared.Rent((int)size); + var startPosition = stream.Position; + stream.Read(buffer, 0, (int) size); + stream.Position = startPosition; - // You can also pass in a hex string, eg. "90 91 929394", or you can use your own CodeReader - // reading data from a file or memory etc - var codeReader = new StreamCodeReader(stream, size); - var decoder = Decoder.Create(IntPtr.Size * 8, codeReader); - decoder.IP = (ulong) 0; - ulong endRip = decoder.IP + (uint)size; - - // This list is faster than List since it uses refs to the Instructions - // instead of copying them (each Instruction is 32 bytes in size). It has a ref indexer, - // and a ref iterator. Add() uses 'in' (ref readonly). - var instructions = new InstructionList(); - while (decoder.IP < endRip) - { - // The method allocates an uninitialized element at the end of the list and - // returns a reference to it which is initialized by Decode(). - decoder.Decode(out instructions.AllocUninitializedElement()); - } + // You can also pass in a hex string, eg. "90 91 929394", or you can use your own CodeReader + // reading data from a file or memory etc + var codeReader = new StreamCodeReader(stream, size); + var decoder = Decoder.Create(IntPtr.Size * 8, codeReader); + decoder.IP = (ulong) 0; + ulong endRip = decoder.IP + (uint)size; + + // This list is faster than List since it uses refs to the Instructions + // instead of copying them (each Instruction is 32 bytes in size). It has a ref indexer, + // and a ref iterator. Add() uses 'in' (ref readonly). + var instructions = new InstructionList(); + while (decoder.IP < endRip) + { + // The method allocates an uninitialized element at the end of the list and + // returns a reference to it which is initialized by Decode(). + decoder.Decode(out instructions.AllocUninitializedElement()); + } - // Formatters: Masm*, Nasm* and Gas* (AT&T) - if (formatter == null) - { - formatter = new NasmFormatter(); - formatter.Options.DigitSeparator = ""; - formatter.Options.FirstOperandCharIndex = 10; - } + // Formatters: Masm*, Nasm* and Gas* (AT&T) + if (formatter == null) + { + formatter = new NasmFormatter(); + formatter.Options.DigitSeparator = ""; + formatter.Options.FirstOperandCharIndex = 10; + } - var output = new StringOutput(); - // Use InstructionList's ref iterator (C# 7.3) to prevent copying 32 bytes every iteration - foreach (ref var instr in instructions) + var output = new StringOutput(); + // Use InstructionList's ref iterator (C# 7.3) to prevent copying 32 bytes every iteration + foreach (ref var instr in instructions) + { + // Don't use instr.ToString(), it allocates more, uses masm syntax and default options + formatter.Format(instr, output); + writer.Write($"{instr.IP:X16} "); + for (int i = 0; i < instr.Length; i++) { - // Don't use instr.ToString(), it allocates more, uses masm syntax and default options - formatter.Format(instr, output); - writer.Write($"{instr.IP:X16} "); - for (int i = 0; i < instr.Length; i++) - { - writer.Write(buffer[(int)instr.IP + i].ToString("X2")); - } - writer.Write(new string(' ', 16 * 2 - instr.Length * 2)); - writer.WriteLine($"{output.ToStringAndReset()}"); + writer.Write(buffer[(int)instr.IP + i].ToString("X2")); } + writer.Write(new string(' ', 16 * 2 - instr.Length * 2)); + writer.WriteLine($"{output.ToStringAndReset()}"); } + } - private class StreamCodeReader : CodeReader + private class StreamCodeReader : CodeReader + { + private readonly Stream _stream; + private long _size; + + public StreamCodeReader(Stream stream, uint size) { - private readonly Stream _stream; - private long _size; + _stream = stream; + _size = size; + } - public StreamCodeReader(Stream stream, uint size) + public override int ReadByte() + { + if (_size < 0) { - _stream = stream; - _size = size; + return -1; } - public override int ReadByte() - { - if (_size < 0) - { - return -1; - } - - _size--; - return _stream.ReadByte(); - } + _size--; + return _stream.ReadByte(); } } } \ No newline at end of file diff --git a/src/objdasm/Program.cs b/src/objdasm/Program.cs index 56dcba7..44f1118 100644 --- a/src/objdasm/Program.cs +++ b/src/objdasm/Program.cs @@ -1,87 +1,90 @@ -using System; +// 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.Text.RegularExpressions; using Mono.Options; -namespace LibObjectFile.Disasm +namespace LibObjectFile.Disasm; + +/// +/// A minimalistic program to disassemble functions in .a and ELF files +/// TODO: handle correctly RIP, labels, relocation...etc. +/// +class Program { - /// - /// A minimalistic program to disassemble functions in .a and ELF files - /// TODO: handle correctly RIP, labels, relocation...etc. - /// - class Program + static int Main(string[] args) { - static int Main(string[] args) - { - var exeName = Path.GetFileNameWithoutExtension(typeof(Program).Assembly.Location); - bool showHelp = false; + var exeName = Path.GetFileNameWithoutExtension(typeof(Program).Assembly.Location); + bool showHelp = false; - var objDisasmApp = new ObjDisasmApp(); + var objDisasmApp = new ObjDisasmApp(); - var _ = string.Empty; - var options = new OptionSet - { - "Copyright (C) 2019 Alexandre Mutel. All Rights Reserved", - $"{exeName} - Version: " - + - $"{typeof(Program).Assembly.GetName().Version.Major}.{typeof(Program).Assembly.GetName().Version.Minor}.{typeof(Program).Assembly.GetName().Version.Build}" + string.Empty, - _, - $"Usage: {exeName} [options]+ [.o|.a files]", - _, - "Disassemble the global functions found in a list of ELF object .o files or archive `ar` files.", - _, - "## Options", - _, - {"f|func=", "Add a regex filtering for function symbols. Can add multiples.", v=> objDisasmApp.FunctionRegexFilters.Add(new Regex(v)) }, - {"l|list=", "List functions that can be decompiled.", v=> objDisasmApp.Listing = true}, - _, - {"h|help", "Show this message and exit", v => showHelp = true }, - {"v|verbose", "Show more verbose progress logs", v => objDisasmApp.Verbose = true }, - }; + var _ = string.Empty; + var options = new OptionSet + { + "Copyright (C) 2019 Alexandre Mutel. All Rights Reserved", + $"{exeName} - Version: " + + + $"{typeof(Program).Assembly.GetName().Version.Major}.{typeof(Program).Assembly.GetName().Version.Minor}.{typeof(Program).Assembly.GetName().Version.Build}" + string.Empty, + _, + $"Usage: {exeName} [options]+ [.o|.a files]", + _, + "Disassemble the global functions found in a list of ELF object .o files or archive `ar` files.", + _, + "## Options", + _, + {"f|func=", "Add a regex filtering for function symbols. Can add multiples.", v=> objDisasmApp.FunctionRegexFilters.Add(new Regex(v)) }, + {"l|list=", "List functions that can be decompiled.", v=> objDisasmApp.Listing = true}, + _, + {"h|help", "Show this message and exit", v => showHelp = true }, + {"v|verbose", "Show more verbose progress logs", v => objDisasmApp.Verbose = true }, + }; - try - { - var files = options.Parse(args); + try + { + var files = options.Parse(args); - if (showHelp) - { - options.WriteOptionDescriptions(Console.Out); - return 0; - } + if (showHelp) + { + options.WriteOptionDescriptions(Console.Out); + return 0; + } - foreach (var file in files) + foreach (var file in files) + { + var filePath = Path.Combine(Environment.CurrentDirectory, file); + if (!File.Exists(filePath)) { - var filePath = Path.Combine(Environment.CurrentDirectory, file); - if (!File.Exists(filePath)) - { - throw new OptionException($"The file {file} does not exist", "[files]"); - } - - objDisasmApp.Files.Add(filePath); + throw new OptionException($"The file {file} does not exist", "[files]"); } - objDisasmApp.Run(); + objDisasmApp.Files.Add(filePath); } - catch (Exception exception) + + objDisasmApp.Run(); + } + catch (Exception exception) + { + if (exception is OptionException || exception is ObjectFileException) { - if (exception is OptionException || exception is ObjectFileException) - { - var backColor = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(exception.Message); - Console.ForegroundColor = backColor; - Console.WriteLine("See --help for usage"); - return 1; - } - else - { - throw; - } + var backColor = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(exception.Message); + Console.ForegroundColor = backColor; + Console.WriteLine("See --help for usage"); + return 1; + } + else + { + throw; } - - return 0; } + + return 0; } -} +} \ No newline at end of file From ec9dad0d7b4b42a5d5f6b6ff1bab70c4030a839c Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 18 Sep 2024 22:24:43 +0200 Subject: [PATCH 17/87] Cleanup more code --- src/LibObjectFile/Ar/ArSymbolTable.cs | 1 + src/LibObjectFile/Dwarf/DwarfDIE.cs | 54 ++-- src/LibObjectFile/Dwarf/DwarfFile.cs | 14 +- .../Dwarf/DwarfLineProgramTable.cs | 2 +- .../Dwarf/DwarfStreamExtensions.cs | 1 + src/LibObjectFile/Dwarf/DwarfStringTable.cs | 1 + .../ElfOffsetCalculationMode.cs} | 4 +- src/LibObjectFile/Elf/ElfReader{TDecoder}.cs | 4 +- src/LibObjectFile/Elf/ElfSegment.cs | 4 +- .../Sections/ElfRelocationTableExtensions.cs | 1 + src/LibObjectFile/IO/StreamExtensions.cs | 198 +++++++++++++-- src/LibObjectFile/IObjectFileNodeLink.cs | 12 - src/LibObjectFile/ObjectFileException.cs | 2 +- src/LibObjectFile/ObjectFileReaderWriter.cs | 2 - .../ObjectFileStreamExtensions.cs | 232 ------------------ src/LibObjectFile/RelocationSize.cs | 13 - src/LibObjectFile/VisitorContextBase.cs | 6 +- 17 files changed, 230 insertions(+), 321 deletions(-) rename src/LibObjectFile/{ValueKind.cs => Elf/ElfOffsetCalculationMode.cs} (87%) delete mode 100644 src/LibObjectFile/IObjectFileNodeLink.cs delete mode 100644 src/LibObjectFile/ObjectFileStreamExtensions.cs delete mode 100644 src/LibObjectFile/RelocationSize.cs diff --git a/src/LibObjectFile/Ar/ArSymbolTable.cs b/src/LibObjectFile/Ar/ArSymbolTable.cs index 4d4a2f4..d8868d9 100644 --- a/src/LibObjectFile/Ar/ArSymbolTable.cs +++ b/src/LibObjectFile/Ar/ArSymbolTable.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Text; using LibObjectFile.Diagnostics; +using LibObjectFile.IO; namespace LibObjectFile.Ar; diff --git a/src/LibObjectFile/Dwarf/DwarfDIE.cs b/src/LibObjectFile/Dwarf/DwarfDIE.cs index a8d893e..856ce74 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIE.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIE.cs @@ -297,33 +297,33 @@ protected unsafe void SetAttributeValue(DwarfAttributeKind kind, TValue? AddAttribute(new DwarfAttribute() { Kind = kind, ValueAsObject = value}); } - protected void SetAttributeLinkValue(DwarfAttributeKind kind, TLink link) where TLink : IObjectFileNodeLink - { - for (int i = 0; i < _attributes.Count; i++) - { - var attr = _attributes[i]; - if (attr.Kind == kind) - { - if (link == null) - { - RemoveAttributeAt(i); - } - else - { - attr.ValueAsU64 = link.GetRelativeOffset(); - attr.ValueAsObject = link.GetObjectFileNode(); - } - return; - } - } - - AddAttribute(new DwarfAttribute() - { - Kind = kind, - ValueAsU64 = link.GetRelativeOffset(), - ValueAsObject = link.GetObjectFileNode() - }); - } + //protected void SetAttributeLinkValue(DwarfAttributeKind kind, TLink link) where TLink : IObjectFileNodeLink + //{ + // for (int i = 0; i < _attributes.Count; i++) + // { + // var attr = _attributes[i]; + // if (attr.Kind == kind) + // { + // if (link == null) + // { + // RemoveAttributeAt(i); + // } + // else + // { + // attr.ValueAsU64 = link.GetRelativeOffset(); + // attr.ValueAsObject = link.GetObjectFileNode(); + // } + // return; + // } + // } + + // AddAttribute(new DwarfAttribute() + // { + // Kind = kind, + // ValueAsU64 = link.GetRelativeOffset(), + // ValueAsObject = link.GetObjectFileNode() + // }); + //} protected unsafe void SetAttributeValueOpt(DwarfAttributeKind kind, TValue? value) where TValue : unmanaged { diff --git a/src/LibObjectFile/Dwarf/DwarfFile.cs b/src/LibObjectFile/Dwarf/DwarfFile.cs index 3590c35..667e277 100644 --- a/src/LibObjectFile/Dwarf/DwarfFile.cs +++ b/src/LibObjectFile/Dwarf/DwarfFile.cs @@ -148,7 +148,7 @@ public void Write(DwarfWriterContext writerContext) writer.AddressSize = writerContext.AddressSize; writer.EnableRelocation = writerContext.EnableRelocation; - writer.Log = writerContext.DebugLinePrinter; + writer.DebugLog = writerContext.DebugLinePrinter; if (writerContext.DebugLineStream != null) { writer.Stream = writerContext.DebugLineStream; @@ -159,7 +159,7 @@ public void Write(DwarfWriterContext writerContext) LineSection.Write(writer); } - writer.Log = null; + writer.DebugLog = null; if (writerContext.DebugAbbrevStream != null) { writer.Stream = writerContext.DebugAbbrevStream; @@ -376,7 +376,7 @@ public static DwarfFile Read(DwarfReaderContext readerContext) var dwarf = new DwarfFile(); var reader = new DwarfReader(readerContext, dwarf, new DiagnosticBag()); - reader.Log = null; + reader.DebugLog = null; if (readerContext.DebugAbbrevStream != null) { reader.Stream = readerContext.DebugAbbrevStream; @@ -391,14 +391,14 @@ public static DwarfFile Read(DwarfReaderContext readerContext) dwarf.StringTable.Read(reader); } - reader.Log = readerContext.DebugLinePrinter; + reader.DebugLog = readerContext.DebugLinePrinter; if (readerContext.DebugLineStream != null) { reader.Stream = readerContext.DebugLineStream; reader.CurrentSection = dwarf.LineSection; dwarf.LineSection.Read(reader); } - reader.Log = null; + reader.DebugLog = null; if (readerContext.DebugAddressRangeStream != null) { @@ -407,7 +407,7 @@ public static DwarfFile Read(DwarfReaderContext readerContext) dwarf.AddressRangeTable.Read(reader); } - reader.Log = null; + reader.DebugLog = null; if (readerContext.DebugLocationStream != null) { reader.Stream = readerContext.DebugLocationStream; @@ -415,7 +415,7 @@ public static DwarfFile Read(DwarfReaderContext readerContext) dwarf.LocationSection.Read(reader); } - reader.Log = null; + reader.DebugLog = null; if (readerContext.DebugInfoStream != null) { reader.Stream = readerContext.DebugInfoStream; diff --git a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs index 6bd1692..3b66414 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs @@ -103,7 +103,7 @@ public DwarfLineSequence RemoveLineSequenceAt(int index) public override void Read(DwarfReader reader) { - var log = reader.Log; + var log = reader.DebugLog; var startOfSection = reader.Position; reader.OffsetToLineProgramTable.Add(startOfSection, this); diff --git a/src/LibObjectFile/Dwarf/DwarfStreamExtensions.cs b/src/LibObjectFile/Dwarf/DwarfStreamExtensions.cs index e08b357..16d8579 100644 --- a/src/LibObjectFile/Dwarf/DwarfStreamExtensions.cs +++ b/src/LibObjectFile/Dwarf/DwarfStreamExtensions.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using LibObjectFile.IO; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/Dwarf/DwarfStringTable.cs b/src/LibObjectFile/Dwarf/DwarfStringTable.cs index 528af5e..d72076c 100644 --- a/src/LibObjectFile/Dwarf/DwarfStringTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfStringTable.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; +using LibObjectFile.IO; namespace LibObjectFile.Dwarf; diff --git a/src/LibObjectFile/ValueKind.cs b/src/LibObjectFile/Elf/ElfOffsetCalculationMode.cs similarity index 87% rename from src/LibObjectFile/ValueKind.cs rename to src/LibObjectFile/Elf/ElfOffsetCalculationMode.cs index b0a8d01..a9f2ca3 100644 --- a/src/LibObjectFile/ValueKind.cs +++ b/src/LibObjectFile/Elf/ElfOffsetCalculationMode.cs @@ -2,12 +2,12 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -namespace LibObjectFile; +namespace LibObjectFile.Elf; /// /// Defines the way a value is calculated. /// -public enum ValueKind +public enum ElfOffsetCalculationMode { /// /// The associated value is automatically calculated by the system. diff --git a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs index 87af089..d877f43 100644 --- a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs +++ b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs @@ -579,7 +579,7 @@ private void VerifyAndFixProgramHeadersAndSections() // If we found a section, we will bind the program header to this section // and switch the offset calculation to auto segment.Range = section; - segment.OffsetKind = ValueKind.Auto; + segment.OffsetCalculationMode = ElfOffsetCalculationMode.Auto; break; } } @@ -690,7 +690,7 @@ private void VerifyAndFixProgramHeadersAndSections() segment.Range = new ElfSegmentRange(beginSection, segment.Position - beginSection.Position, endSection, (long)(segmentEndOffset - endSection.Position)); } - segment.OffsetKind = ValueKind.Auto; + segment.OffsetCalculationMode = ElfOffsetCalculationMode.Auto; break; } } diff --git a/src/LibObjectFile/Elf/ElfSegment.cs b/src/LibObjectFile/Elf/ElfSegment.cs index 40d709e..20daf80 100644 --- a/src/LibObjectFile/Elf/ElfSegment.cs +++ b/src/LibObjectFile/Elf/ElfSegment.cs @@ -13,7 +13,7 @@ namespace LibObjectFile.Elf; /// public sealed class ElfSegment : ElfObject { - public ValueKind OffsetKind { get; set; } + public ElfOffsetCalculationMode OffsetCalculationMode { get; set; } /// /// Gets or sets the type of this segment. @@ -55,7 +55,7 @@ public override void UpdateLayout(ElfVisitorContext context) { var diagnostics = context.Diagnostics; - if (OffsetKind == ValueKind.Auto) + if (OffsetCalculationMode == ElfOffsetCalculationMode.Auto) { Position = Range.Offset; } diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs b/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs index 9fbaf27..4820b81 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using LibObjectFile.IO; namespace LibObjectFile.Elf; diff --git a/src/LibObjectFile/IO/StreamExtensions.cs b/src/LibObjectFile/IO/StreamExtensions.cs index dc21cdd..bee80ae 100644 --- a/src/LibObjectFile/IO/StreamExtensions.cs +++ b/src/LibObjectFile/IO/StreamExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; +using System.Buffers.Binary; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; @@ -12,37 +13,47 @@ namespace LibObjectFile.IO; public static class StreamExtensions { - /// - /// Reads a null terminated UTF8 string from the stream. - /// - /// true if the string was successfully read from the stream, false otherwise - public static string ReadStringUTF8NullTerminated(this Stream stream) + public static byte ReadU8(this Stream stream) { - if (!stream.TryReadStringUTF8NullTerminated(out var text)) - { - throw new EndOfStreamException(); - } - return text; + int nextValue = stream.ReadByte(); + if (nextValue < 0) throw new EndOfStreamException(); + return (byte)nextValue; + } + + public static void WriteU8(this Stream stream, byte value) + { + stream.WriteByte(value); + } + + public static sbyte ReadI8(this Stream stream) + { + int nextValue = stream.ReadByte(); + if (nextValue < 0) throw new EndOfStreamException(); + return (sbyte)nextValue; + } + + public static void WriteI8(this Stream stream, sbyte value) + { + stream.WriteByte((byte)value); } /// /// Reads a null terminated UTF8 string from the stream. /// /// true if the string was successfully read from the stream, false otherwise - public static bool TryReadStringUTF8NullTerminated(this Stream stream, [NotNullWhen(true)] out string? text) + public static string ReadStringUTF8NullTerminated(this Stream stream) { - text = null; - var buffer = ArrayPool.Shared.Rent(128); + var buffer = ArrayPool.Shared.Rent((int)128); int textLength = 0; try { while (true) { - // TODO: not efficient to read byte by byte + // TODO: Optimize this by reading a block of bytes int nextByte = stream.ReadByte(); if (nextByte < 0) { - return false; + throw new EndOfStreamException("Unexpected end of stream while trying to read a null terminated UTF8 string"); } if (nextByte == 0) @@ -50,9 +61,9 @@ public static bool TryReadStringUTF8NullTerminated(this Stream stream, [NotNullW break; } - if (textLength > buffer.Length) + if (textLength >= buffer.Length) { - var newBuffer = ArrayPool.Shared.Rent(textLength * 2); + var newBuffer = ArrayPool.Shared.Rent((int)textLength * 2); Array.Copy(buffer, 0, newBuffer, 0, buffer.Length); ArrayPool.Shared.Return(buffer); buffer = newBuffer; @@ -61,13 +72,162 @@ public static bool TryReadStringUTF8NullTerminated(this Stream stream, [NotNullW buffer[textLength++] = (byte)nextByte; } - text = Encoding.UTF8.GetString(buffer, 0, textLength); + return Encoding.UTF8.GetString(buffer, 0, textLength); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + /// + /// Reads a null terminated UTF8 string from the stream. + /// + /// The number of bytes to read including the null + /// A string + public static string ReadStringUTF8NullTerminated(this Stream stream, uint byteLength) + { + if (byteLength == 0) return string.Empty; + + var buffer = ArrayPool.Shared.Rent((int)byteLength); + try + { + var dataLength = stream.Read(buffer, 0, (int)byteLength); + if (dataLength < 0) throw new EndOfStreamException("Unexpected end of stream while trying to read data"); + + var byteReadLength = (uint)dataLength; + if (byteReadLength != byteLength) throw new EndOfStreamException($"Not enough data read {byteReadLength} bytes while expecting to read {byteLength} bytes"); + + var isNullTerminated = buffer[byteReadLength - 1] == 0; + + var text = Encoding.UTF8.GetString(buffer, 0, (int)(isNullTerminated ? byteReadLength - 1 : byteReadLength)); + return text; } finally { ArrayPool.Shared.Return(buffer); } + } + + + public static short ReadI16(this Stream stream, bool isLittleEndian) + { + return (short)ReadU16(stream, isLittleEndian); + } + + public static ushort ReadU16(this Stream stream, bool isLittleEndian) + { + ushort value = 0; + int nextValue = stream.ReadByte(); + if (nextValue < 0) throw new EndOfStreamException(); + value = (byte)nextValue; + + nextValue = stream.ReadByte(); + if (nextValue < 0) throw new EndOfStreamException(); + value = (ushort)((nextValue << 8) | (byte)value); + + if (isLittleEndian != BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + + return value; + } + + public static int ReadI32(this Stream stream, bool isLittleEndian) + { + return (int)ReadU32(stream, isLittleEndian); + } + + public static unsafe uint ReadU32(this Stream stream, bool isLittleEndian) + { + uint value = 0; + var span = new Span((byte*)&value, sizeof(uint)); + if (stream.Read(span) != sizeof(uint)) + { + throw new EndOfStreamException(); + } + + if (isLittleEndian != BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + return value; + } + public static long ReadI64(this Stream stream, bool isLittleEndian) + { + return (long)ReadU64(stream, isLittleEndian); + } + + public static unsafe ulong ReadU64(this Stream stream, bool isLittleEndian) + { + ulong value = 0; + var span = new Span((byte*)&value, sizeof(ulong)); + if (stream.Read(span) != sizeof(ulong)) + { + throw new EndOfStreamException(); + } + + if (isLittleEndian != BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + return value; + } + + public static unsafe void WriteU16(this Stream stream, bool isLittleEndian, ushort value) + { + if (isLittleEndian != BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + var span = new Span((byte*)&value, sizeof(ushort)); + stream.Write(span); + } + + public static void WriteI32(this Stream stream, bool isLittleEndian, int value) + { + WriteU32(stream, isLittleEndian, (uint)value); + } - return true; + public static unsafe void WriteU32(this Stream stream, bool isLittleEndian, uint value) + { + if (isLittleEndian != BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + var span = new Span((byte*)&value, sizeof(uint)); + stream.Write(span); + } + + public static unsafe void WriteU64(this Stream stream, bool isLittleEndian, ulong value) + { + if (isLittleEndian != BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + var span = new Span((byte*)&value, sizeof(ulong)); + stream.Write(span); + } + + /// + /// Writes a null terminated UTF8 string to the stream. + /// + public static void WriteStringUTF8NullTerminated(this Stream stream, string text) + { + if (text == null) throw new ArgumentNullException(nameof(text)); + + int byteLength = Encoding.UTF8.GetByteCount(text); + var buffer = ArrayPool.Shared.Rent(byteLength + 1); + try + { + Encoding.UTF8.GetBytes(text, 0, text.Length, buffer, 0); + buffer[byteLength] = 0; + stream.Write(buffer, 0, byteLength + 1); + } + finally + { + ArrayPool.Shared.Return(buffer); + } } } \ No newline at end of file diff --git a/src/LibObjectFile/IObjectFileNodeLink.cs b/src/LibObjectFile/IObjectFileNodeLink.cs deleted file mode 100644 index 980a844..0000000 --- a/src/LibObjectFile/IObjectFileNodeLink.cs +++ /dev/null @@ -1,12 +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; - -public interface IObjectFileNodeLink -{ - ulong GetRelativeOffset(); - - ObjectFileElement GetObjectFileNode(); -} \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileException.cs b/src/LibObjectFile/ObjectFileException.cs index e8cee79..142b763 100644 --- a/src/LibObjectFile/ObjectFileException.cs +++ b/src/LibObjectFile/ObjectFileException.cs @@ -10,7 +10,7 @@ namespace LibObjectFile; /// /// An exception used when diagnostics error are happening during read/write. /// -public class ObjectFileException : Exception +public sealed class ObjectFileException : Exception { public ObjectFileException(string message, DiagnosticBag diagnostics) : base(message) { diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs index cd82018..25df4f8 100644 --- a/src/LibObjectFile/ObjectFileReaderWriter.cs +++ b/src/LibObjectFile/ObjectFileReaderWriter.cs @@ -55,8 +55,6 @@ public ulong Length public bool IsLittleEndian { get; protected set; } - public TextWriter? Log { get; set; } - /// /// Reads from the and current position to the specified buffer. /// diff --git a/src/LibObjectFile/ObjectFileStreamExtensions.cs b/src/LibObjectFile/ObjectFileStreamExtensions.cs deleted file mode 100644 index eeebf36..0000000 --- a/src/LibObjectFile/ObjectFileStreamExtensions.cs +++ /dev/null @@ -1,232 +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. - -using System; -using System.Buffers; -using System.Buffers.Binary; -using System.IO; -using System.Text; - -namespace LibObjectFile; - -public static class ObjectFileStreamExtensions -{ - public static byte ReadU8(this Stream stream) - { - int nextValue = stream.ReadByte(); - if (nextValue < 0) throw new EndOfStreamException(); - return (byte)nextValue; - } - - public static void WriteU8(this Stream stream, byte value) - { - stream.WriteByte(value); - } - - public static sbyte ReadI8(this Stream stream) - { - int nextValue = stream.ReadByte(); - if (nextValue < 0) throw new EndOfStreamException(); - return (sbyte)nextValue; - } - - public static void WriteI8(this Stream stream, sbyte value) - { - stream.WriteByte((byte)value); - } - - /// - /// Reads a null terminated UTF8 string from the stream. - /// - /// true if the string was successfully read from the stream, false otherwise - public static string ReadStringUTF8NullTerminated(this Stream stream) - { - var buffer = ArrayPool.Shared.Rent((int)128); - int textLength = 0; - try - { - while (true) - { - int nextByte = stream.ReadByte(); - if (nextByte < 0) - { - throw new EndOfStreamException("Unexpected end of stream while trying to read a null terminated UTF8 string"); - } - - if (nextByte == 0) - { - break; - } - - if (textLength >= buffer.Length) - { - var newBuffer = ArrayPool.Shared.Rent((int)textLength * 2); - Array.Copy(buffer, 0, newBuffer, 0, buffer.Length); - ArrayPool.Shared.Return(buffer); - buffer = newBuffer; - } - - buffer[textLength++] = (byte)nextByte; - } - - return Encoding.UTF8.GetString(buffer, 0, textLength); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } - - /// - /// Reads a null terminated UTF8 string from the stream. - /// - /// The number of bytes to read including the null - /// A string - public static string ReadStringUTF8NullTerminated(this Stream stream, uint byteLength) - { - if (byteLength == 0) return string.Empty; - - var buffer = ArrayPool.Shared.Rent((int)byteLength); - try - { - var dataLength = stream.Read(buffer, 0, (int)byteLength); - if (dataLength < 0) throw new EndOfStreamException("Unexpected end of stream while trying to read data"); - - var byteReadLength = (uint)dataLength; - if (byteReadLength != byteLength) throw new EndOfStreamException($"Not enough data read {byteReadLength} bytes while expecting to read {byteLength} bytes"); - - var isNullTerminated = buffer[byteReadLength - 1] == 0; - - var text = Encoding.UTF8.GetString(buffer, 0, (int)(isNullTerminated ? byteReadLength - 1 : byteReadLength)); - return text; - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } - - - public static short ReadI16(this Stream stream, bool isLittleEndian) - { - return (short) ReadU16(stream, isLittleEndian); - } - - public static ushort ReadU16(this Stream stream, bool isLittleEndian) - { - ushort value = 0; - int nextValue = stream.ReadByte(); - if (nextValue < 0) throw new EndOfStreamException(); - value = (byte)nextValue; - - nextValue = stream.ReadByte(); - if (nextValue < 0) throw new EndOfStreamException(); - value = (ushort)((nextValue << 8) | (byte)value); - - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } - - return value; - } - - public static int ReadI32(this Stream stream, bool isLittleEndian) - { - return (int)ReadU32(stream, isLittleEndian); - } - - public static unsafe uint ReadU32(this Stream stream, bool isLittleEndian) - { - uint value = 0; - var span = new Span((byte*)&value, sizeof(uint)); - if (stream.Read(span) != sizeof(uint)) - { - throw new EndOfStreamException(); - } - - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } - return value; - } - public static long ReadI64(this Stream stream, bool isLittleEndian) - { - return (long)ReadU64(stream, isLittleEndian); - } - - public static unsafe ulong ReadU64(this Stream stream, bool isLittleEndian) - { - ulong value = 0; - var span = new Span((byte*)&value, sizeof(ulong)); - if (stream.Read(span) != sizeof(ulong)) - { - throw new EndOfStreamException(); - } - - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } - return value; - } - - public static unsafe void WriteU16(this Stream stream, bool isLittleEndian, ushort value) - { - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } - var span = new Span((byte*)&value, sizeof(ushort)); - stream.Write(span); - } - - public static void WriteI32(this Stream stream, bool isLittleEndian, int value) - { - WriteU32(stream, isLittleEndian, (uint)value); - } - - public static unsafe void WriteU32(this Stream stream, bool isLittleEndian, uint value) - { - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } - var span = new Span((byte*)&value, sizeof(uint)); - stream.Write(span); - } - - public static unsafe void WriteU64(this Stream stream, bool isLittleEndian, ulong value) - { - if (isLittleEndian != BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } - var span = new Span((byte*)&value, sizeof(ulong)); - stream.Write(span); - } - - /// - /// Writes a null terminated UTF8 string to the stream. - /// - public static void WriteStringUTF8NullTerminated(this Stream stream, string text) - { - if (text == null) throw new ArgumentNullException(nameof(text)); - - int byteLength = Encoding.UTF8.GetByteCount(text); - var buffer = ArrayPool.Shared.Rent(byteLength + 1); - try - { - Encoding.UTF8.GetBytes(text, 0, text.Length, buffer, 0); - buffer[byteLength] = 0; - stream.Write(buffer, 0, byteLength + 1); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } - -} \ No newline at end of file diff --git a/src/LibObjectFile/RelocationSize.cs b/src/LibObjectFile/RelocationSize.cs deleted file mode 100644 index 2e4b70a..0000000 --- a/src/LibObjectFile/RelocationSize.cs +++ /dev/null @@ -1,13 +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; - -public enum RelocationSize -{ - I8, - I16, - I32, - I64, -} \ No newline at end of file diff --git a/src/LibObjectFile/VisitorContextBase.cs b/src/LibObjectFile/VisitorContextBase.cs index 5989507..af8acec 100644 --- a/src/LibObjectFile/VisitorContextBase.cs +++ b/src/LibObjectFile/VisitorContextBase.cs @@ -2,8 +2,10 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -using LibObjectFile; using LibObjectFile.Diagnostics; +using System.IO; + +namespace LibObjectFile; /// /// Base class used for layout-ing an object file. @@ -19,6 +21,8 @@ public abstract class VisitorContextBase(ObjectFileElement file, DiagnosticBag d public bool HasErrors => Diagnostics.HasErrors; + + public TextWriter? DebugLog { get; set; } } /// From 10824a72c62832f3f4e0b0363de392cd2fbeee9e Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 19 Sep 2024 06:09:47 +0200 Subject: [PATCH 18/87] Fix ObjectList.Clear --- src/LibObjectFile/Collections/ObjectList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LibObjectFile/Collections/ObjectList.cs b/src/LibObjectFile/Collections/ObjectList.cs index 6bad277..e0960c3 100644 --- a/src/LibObjectFile/Collections/ObjectList.cs +++ b/src/LibObjectFile/Collections/ObjectList.cs @@ -54,7 +54,7 @@ public void Add(TObject item) public void Clear() { var items = _items; - for (var i = items.Count - 1; i >= 0; i++) + for (var i = items.Count - 1; i >= 0; i--) { var item = items[i]; items.Removing(item); From 26734488c8b95ff50c35e95289c4da30bb39dd3c Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 19 Sep 2024 06:10:08 +0200 Subject: [PATCH 19/87] Extend usage of ObjectList --- src/LibObjectFile.Tests/Dwarf/DwarfTests.cs | 24 +-- .../Collections/SortedObjectList.cs | 194 ++++++++++++++++++ .../Dwarf/DwarfAbbreviationTable.cs | 23 +-- src/LibObjectFile/Dwarf/DwarfDIE.cs | 96 ++------- src/LibObjectFile/Dwarf/DwarfExpression.cs | 29 +-- src/LibObjectFile/Dwarf/DwarfInfoSection.cs | 23 +-- .../Dwarf/DwarfLineProgramTable.cs | 29 +-- src/LibObjectFile/Dwarf/DwarfLineSection.cs | 23 +-- src/LibObjectFile/Dwarf/DwarfLineSequence.cs | 18 +- src/LibObjectFile/Dwarf/DwarfLocationList.cs | 21 +- .../Dwarf/DwarfLocationSection.cs | 22 +- src/LibObjectFile/Dwarf/DwarfUnit.cs | 2 +- src/LibObjectFile/ObjectFileExtensions.cs | 140 ------------- 13 files changed, 258 insertions(+), 386 deletions(-) create mode 100644 src/LibObjectFile/Collections/SortedObjectList.cs delete mode 100644 src/LibObjectFile/ObjectFileExtensions.cs diff --git a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs index 554cd7c..3696aa5 100644 --- a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs +++ b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs @@ -402,14 +402,14 @@ public void CreateDwarf() for (int i = 0; i < 2; i++) { var lineTable = new DwarfLineProgramTable(); - dwarfFile.LineSection.AddLineProgramTable(lineTable); + dwarfFile.LineSection.LineTables.Add(lineTable); lineTable.AddressSize = DwarfAddressSize.Bit64; lineTable.FileNames.Add(fileName); lineTable.FileNames.Add(fileName2); - lineTable.AddLineSequence(new DwarfLineSequence() - { + lineTable.LineSequences.Add(new DwarfLineSequence() + { new DwarfLine() { File = fileName, @@ -428,7 +428,7 @@ public void CreateDwarf() ); // NOTE: doesn't seem to be generated by regular GCC // (it seems that only one line sequence is usually used) - lineTable.AddLineSequence(new DwarfLineSequence() + lineTable.LineSequences.Add(new DwarfLineSequence() { new DwarfLine() @@ -455,20 +455,20 @@ public void CreateDwarf() { Name = "MyFunction", }; - rootDIE.AddChild(subProgram); + rootDIE.Children.Add(subProgram); var locationList = new DwarfLocationList(); var regExpression = new DwarfExpression(); - regExpression.AddOperation(new DwarfOperation { Kind = DwarfOperationKindEx.Reg0 }); + regExpression.Operations.Add(new DwarfOperation { Kind = DwarfOperationKindEx.Reg0 }); var regExpression2 = new DwarfExpression(); - regExpression2.AddOperation(new DwarfOperation { Kind = DwarfOperationKindEx.Reg2 }); - locationList.AddLocationListEntry(new DwarfLocationListEntry + regExpression2.Operations.Add(new DwarfOperation { Kind = DwarfOperationKindEx.Reg2 }); + locationList.LocationListEntries.Add(new DwarfLocationListEntry { Start = 0, End = 0x10, Expression = regExpression, }); - locationList.AddLocationListEntry(new DwarfLocationListEntry + locationList.LocationListEntries.Add(new DwarfLocationListEntry { Start = 0x10, End = 0x20, @@ -479,15 +479,15 @@ public void CreateDwarf() Name = "a", Location = locationList, }; - dwarfFile.LocationSection.AddLocationList(locationList); - subProgram.AddChild(variable); + dwarfFile.LocationSection.LocationLists.Add(locationList); + subProgram.Children.Add(variable); var cu = new DwarfCompilationUnit() { AddressSize = DwarfAddressSize.Bit64, Root = rootDIE }; - dwarfFile.InfoSection.AddUnit(cu); + dwarfFile.InfoSection.Units.Add(cu); // AddressRange table dwarfFile.AddressRangeTable.AddressSize = DwarfAddressSize.Bit64; diff --git a/src/LibObjectFile/Collections/SortedObjectList.cs b/src/LibObjectFile/Collections/SortedObjectList.cs new file mode 100644 index 0000000..f0a7ee0 --- /dev/null +++ b/src/LibObjectFile/Collections/SortedObjectList.cs @@ -0,0 +1,194 @@ +// 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.Collections; + +/// +/// A list of objects that are attached to a parent object. +/// +/// The type of the object file. +[DebuggerDisplay("Count = {Count}")] +[DebuggerTypeProxy(typeof(SortedObjectList<>.ObjectListDebuggerView))] +public readonly struct SortedObjectList : IList + where TObject : ObjectFileElement, IComparable +{ + // 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 SortedObjectList() => throw new NotSupportedException("This constructor is not supported"); + + /// + /// Initializes a new instance of the class. + /// + /// The parent object file node. + public SortedObjectList(ObjectFileElement parent, Action? added = null, Action? removing = null, Action? removed = null, Action? updated = null) + { + ArgumentNullException.ThrowIfNull(parent); + _items = new InternalList(parent, added, removing, removed, updated); + } + + 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.Removed(i, item); + } + + 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) => throw new NotSupportedException("Insert is not supported"); + + 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.Updated(index, previousItem, 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(ObjectFileElement parent, Action? added, Action? removing, Action? removed, Action? updated) : List + { + private readonly Action? _added = added; + private readonly Action? _removing = removing; + private readonly Action? _removed = removed; + private readonly Action? _updated = updated; + + public readonly ObjectFileElement 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); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Updated(int index, TObject previousItem, TObject newItem) => _updated?.Invoke(Parent, index, previousItem, newItem); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Removed(int index, TObject removedItem) => _removed?.Invoke(Parent, index, removedItem); + } + + internal sealed class ObjectListDebuggerView + { + private readonly List _collection; + + public ObjectListDebuggerView(SortedObjectList collection) + { + ArgumentNullException.ThrowIfNull(collection, nameof(collection)); + _collection = collection.UnsafeList; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public TObject[] Items + { + get + { + var array = new TObject[_collection.Count]; + _collection.CopyTo(array, 0); + return array; + } + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs index 74031aa..796bd0a 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs @@ -10,29 +10,14 @@ namespace LibObjectFile.Dwarf; public class DwarfAbbreviationTable : DwarfSection { - private readonly List _abbreviations; + private readonly ObjectList _abbreviations; public DwarfAbbreviationTable() { - _abbreviations = new List(); + _abbreviations = new ObjectList(this); } - public ReadOnlyList Abbreviations => _abbreviations; - - internal void AddAbbreviation(DwarfAbbreviation abbreviation) - { - _abbreviations.Add(this, abbreviation); - } - - internal void RemoveAbbreviation(DwarfAbbreviation abbreviation) - { - _abbreviations.Remove(this, abbreviation); - } - - internal DwarfAbbreviation RemoveAbbreviationAt(int index) - { - return _abbreviations.RemoveAt(this, index); - } + public ObjectList Abbreviations => _abbreviations; internal void Reset() { @@ -67,7 +52,7 @@ public override void Read(DwarfReader reader) }; abbreviation.Read(reader); endOffset += abbreviation.Size; - AddAbbreviation(abbreviation); + _abbreviations.Add(abbreviation); } Size = endOffset - Position; diff --git a/src/LibObjectFile/Dwarf/DwarfDIE.cs b/src/LibObjectFile/Dwarf/DwarfDIE.cs index 856ce74..4a4e3ab 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIE.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIE.cs @@ -13,9 +13,9 @@ namespace LibObjectFile.Dwarf; public class DwarfDIE : DwarfContainer { [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly List _attributes; + private readonly SortedObjectList _attributes; [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly List _children; + private readonly ObjectList _children; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private DwarfTagEx _tag; @@ -26,8 +26,8 @@ public class DwarfDIE : DwarfContainer public DwarfDIE() { - _attributes = new List(); - _children = new List(); + _attributes = new SortedObjectList(this); + _children = new ObjectList(this); } public virtual DwarfTagEx Tag @@ -36,76 +36,12 @@ public virtual DwarfTagEx Tag set => _tag = value; } - public ReadOnlyList Attributes => _attributes; + public SortedObjectList Attributes => _attributes; - public ReadOnlyList Children => _children; + public ObjectList Children => _children; public DwarfAbbreviationItem? Abbrev { get; internal set; } - /// - /// Adds a child to . - /// - /// A child - public void AddChild(DwarfDIE child) - { - _children.Add(this, child); - } - - /// - /// Inserts a child into at the specified index. - /// - /// Index into to insert the specified child - /// The child to insert - public void InsertChildAt(int index, DwarfDIE child) - { - _children.InsertAt(this, index, child); - } - - /// - /// Removes a child from - /// - /// The child to remove - public void RemoveChild(DwarfDIE child) - { - _children.Remove(this, child); - } - - /// - /// Removes a child from at the specified index. - /// - /// Index into to remove the specified child - public DwarfDIE RemoveChildAt(int index) - { - return _children.RemoveAt(this, index); - } - - /// - /// Adds an attribute to . - /// - /// A attribute - public void AddAttribute(DwarfAttribute attribute) - { - _attributes.AddSorted(this, attribute, true); - } - - /// - /// Removes an attribute from - /// - /// The attribute to remove - public void RemoveAttribute(DwarfAttribute attribute) - { - _attributes.Remove(this, attribute); - } - - /// - /// Removes an attribute from at the specified index. - /// - /// Index into to remove the specified attribute - public DwarfAttribute RemoveAttributeAt(int index) - { - return _attributes.RemoveAt(this, index); - } - public override void Verify(DwarfVerifyContext context) { @@ -184,7 +120,7 @@ protected void SetAttributeConstantOpt(DwarfAttributeKind kind, DwarfConstant? c { if (!cst.HasValue) { - RemoveAttributeAt(i); + Attributes.RemoveAt(i); } else { @@ -199,7 +135,7 @@ protected void SetAttributeConstantOpt(DwarfAttributeKind kind, DwarfConstant? c if (cst.HasValue) { var value = cst.Value; - AddAttribute(new DwarfAttribute() + Attributes.Add(new DwarfAttribute() { Kind = kind, ValueAsU64 = value.AsValue.U64, @@ -237,7 +173,7 @@ protected void SetAttributeLocationOpt(DwarfAttributeKind kind, DwarfLocation? c { if (!cst.HasValue) { - RemoveAttributeAt(i); + Attributes.RemoveAt(i); } else { @@ -252,7 +188,7 @@ protected void SetAttributeLocationOpt(DwarfAttributeKind kind, DwarfLocation? c if (cst.HasValue) { var value = cst.Value; - AddAttribute(new DwarfAttribute() + Attributes.Add(new DwarfAttribute() { Kind = kind, ValueAsU64 = value.AsValue.U64, @@ -283,7 +219,7 @@ protected unsafe void SetAttributeValue(DwarfAttributeKind kind, TValue? { if (value == null) { - RemoveAttributeAt(i); + Attributes.RemoveAt(i); } else { @@ -294,7 +230,7 @@ protected unsafe void SetAttributeValue(DwarfAttributeKind kind, TValue? } if (value == null) return; - AddAttribute(new DwarfAttribute() { Kind = kind, ValueAsObject = value}); + Attributes.Add(new DwarfAttribute() { Kind = kind, ValueAsObject = value}); } //protected void SetAttributeLinkValue(DwarfAttributeKind kind, TLink link) where TLink : IObjectFileNodeLink @@ -336,7 +272,7 @@ protected unsafe void SetAttributeValueOpt(DwarfAttributeKind kind, TVal { if (!value.HasValue) { - RemoveAttributeAt(i); + Attributes.RemoveAt(i); } else { @@ -355,7 +291,7 @@ protected unsafe void SetAttributeValueOpt(DwarfAttributeKind kind, TVal ulong valueU64 = 0; *((TValue*)&valueU64) = value.Value; attr.ValueAsU64 = valueU64; - AddAttribute(attr); + Attributes.Add(attr); } } @@ -419,7 +355,7 @@ public override void Read(DwarfReader reader) attribute.Read(reader); - AddAttribute(attribute); + Attributes.Add(attribute); } } @@ -432,7 +368,7 @@ public override void Read(DwarfReader reader) reader.DIELevel--; if (child == null) break; - AddChild(child); + Children.Add(child); } } diff --git a/src/LibObjectFile/Dwarf/DwarfExpression.cs b/src/LibObjectFile/Dwarf/DwarfExpression.cs index bdde8ba..a2bb571 100644 --- a/src/LibObjectFile/Dwarf/DwarfExpression.cs +++ b/src/LibObjectFile/Dwarf/DwarfExpression.cs @@ -12,36 +12,17 @@ namespace LibObjectFile.Dwarf; [DebuggerDisplay("Count = {Operations.Count,nq}")] public class DwarfExpression : DwarfObject { - private readonly List _operations; + private readonly ObjectList _operations; public DwarfExpression() { - _operations = new List(); + _operations = new ObjectList(this); } - public ReadOnlyList Operations => _operations; - - internal List InternalOperations => _operations; + public ObjectList Operations => _operations; public ulong OperationLengthInBytes { get; internal set; } - public void AddOperation(DwarfOperation operation) - { - if (operation == null) throw new ArgumentNullException(nameof(operation)); - _operations.Add(this, operation); - } - - public void RemoveOperation(DwarfOperation operation) - { - if (operation == null) throw new ArgumentNullException(nameof(operation)); - _operations.Remove(this, operation); - } - - public DwarfOperation RemoveOperationAt(int index) - { - return _operations.RemoveAt(this, index); - } - public override void Verify(DwarfVerifyContext context) { foreach (var op in _operations) @@ -61,7 +42,7 @@ internal void ReadInternal(DwarfReader reader, bool inLocationSection = false) { var op = new DwarfOperation() {Position = reader.Position}; op.Read(reader); - AddOperation(op); + Operations.Add(op); } Size = reader.Position - Position; @@ -105,7 +86,7 @@ internal void UpdateLayout(DwarfLayoutContext layoutContext, bool inLocationSect // We need to shift the expression which is prefixed by its size encoded in LEB128, // or fixed-size U2 in .debug_loc section var deltaLength = inLocationSection ? sizeof(ushort) : DwarfHelper.SizeOfULEB128(Size); - foreach (var op in InternalOperations) + foreach (var op in _operations.UnsafeList) { op.Position += deltaLength; } diff --git a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs index c5afef4..78ca148 100644 --- a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs @@ -10,29 +10,14 @@ namespace LibObjectFile.Dwarf; public sealed class DwarfInfoSection : DwarfRelocatableSection { - private readonly List _units; + private readonly ObjectList _units; public DwarfInfoSection() { - _units = new List(); + _units = new ObjectList(this); } - public ReadOnlyList Units => _units; - - public void AddUnit(DwarfUnit unit) - { - _units.Add(this, unit); - } - - public void RemoveUnit(DwarfUnit unit) - { - _units.Remove(this, unit); - } - - public DwarfUnit RemoveUnitAt(int index) - { - return _units.RemoveAt(this, index); - } + public ObjectList Units => _units; public override void Read(DwarfReader reader) { @@ -62,7 +47,7 @@ public override void Read(DwarfReader reader) addressRangeTable.Unit = cu; } - AddUnit(cu); + _units.Add(cu); } reader.ResolveAttributeReferenceWithinSection(); diff --git a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs index 3b66414..3afa71d 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs @@ -17,7 +17,7 @@ public sealed class DwarfLineProgramTable : DwarfObject private readonly Dictionary _directoryNameToIndex; private readonly Dictionary _fileNameToIndex; private readonly List _directoryNames; - private readonly List _lineSequences; + private readonly ObjectList _lineSequences; private readonly DwarfLine _stateLine; private byte _minimumInstructionLength; private byte _maximumOperationsPerInstruction; @@ -26,7 +26,7 @@ public sealed class DwarfLineProgramTable : DwarfObject public DwarfLineProgramTable() { FileNames = new List(); - _lineSequences = new List(); + _lineSequences = new ObjectList(this); _directoryNameToIndex = new Dictionary(); _fileNameToIndex = new Dictionary(); _directoryNames = new List(); @@ -84,22 +84,7 @@ public byte MaximumOperationsPerInstruction public List FileNames { get; } - public ReadOnlyList LineSequences => _lineSequences; - - public void AddLineSequence(DwarfLineSequence line) - { - _lineSequences.Add(this, line); - } - - public void RemoveLineSequence(DwarfLineSequence line) - { - _lineSequences.Remove(this, line); - } - - public DwarfLineSequence RemoveLineSequenceAt(int index) - { - return _lineSequences.RemoveAt(this, index); - } + public ObjectList LineSequences => _lineSequences; public override void Read(DwarfReader reader) { @@ -291,7 +276,7 @@ public override void Read(DwarfReader reader) switch (opcode) { case DwarfNative.DW_LNS_copy: - currentSequence.Add(state.Clone()); + currentSequence.Lines.Add(state.Clone()); state.Position = reader.Position; state.SpecialReset(); if (log != null) @@ -447,9 +432,9 @@ public override void Read(DwarfReader reader) switch (sub_opcode) { case DwarfNative.DW_LNE_end_sequence: - currentSequence.Add(state.Clone()); + currentSequence.Lines.Add(state.Clone()); currentSequence.Size = reader.Position - currentSequence.Position; - AddLineSequence(currentSequence); + _lineSequences.Add(currentSequence); currentSequence = new DwarfLineSequence() {Position = reader.Position}; @@ -576,7 +561,7 @@ public override void Read(DwarfReader reader) log.WriteLine($" and Line by {line_inc} to {state.Line}"); } - currentSequence.Add(state.Clone()); + currentSequence.Lines.Add(state.Clone()); state.Position = reader.Position; state.SpecialReset(); } diff --git a/src/LibObjectFile/Dwarf/DwarfLineSection.cs b/src/LibObjectFile/Dwarf/DwarfLineSection.cs index 7f55a70..999d8b4 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSection.cs @@ -12,30 +12,15 @@ namespace LibObjectFile.Dwarf; [DebuggerDisplay("Count = {LineTables.Count,nq}")] public sealed class DwarfLineSection : DwarfRelocatableSection { - private readonly List _tables; + private readonly ObjectList _tables; public DwarfLineSection() { - _tables = new List(); + _tables = new ObjectList(this); } - public ReadOnlyList LineTables => _tables; - - public void AddLineProgramTable(DwarfLineProgramTable line) - { - _tables.Add(this, line); - } - - public void RemoveLineProgramTable(DwarfLineProgramTable line) - { - _tables.Remove(this, line); - } - - public DwarfLineProgramTable RemoveLineProgramTableAt(int index) - { - return _tables.RemoveAt(this, index); - } + public ObjectList LineTables => _tables; public override void Read(DwarfReader reader) { @@ -44,7 +29,7 @@ public override void Read(DwarfReader reader) var programTable = new DwarfLineProgramTable(); programTable.Position = reader.Position; programTable.Read(reader); - AddLineProgramTable(programTable); + _tables.Add(programTable); } } diff --git a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs index 9a50ad6..e888e53 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs @@ -15,28 +15,18 @@ namespace LibObjectFile.Dwarf; [DebuggerDisplay("Count = {Lines.Count,nq}")] public class DwarfLineSequence : DwarfObject, IEnumerable { - private readonly List _lines; + private readonly ObjectList _lines; public DwarfLineSequence() { - _lines = new List(); + _lines = new ObjectList(this); } - public ReadOnlyList Lines => _lines; + public ObjectList Lines => _lines; public void Add(DwarfLine line) { - _lines.Add(this, line); - } - - public void Remove(DwarfLine line) - { - _lines.Remove(this, line); - } - - public DwarfLine RemoveAt(int index) - { - return _lines.RemoveAt(this, index); + _lines.Add(line); } public override void UpdateLayout(DwarfLayoutContext layoutContext) diff --git a/src/LibObjectFile/Dwarf/DwarfLocationList.cs b/src/LibObjectFile/Dwarf/DwarfLocationList.cs index b6d9627..1aa8015 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationList.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationList.cs @@ -10,29 +10,14 @@ namespace LibObjectFile.Dwarf; public class DwarfLocationList : DwarfContainer { - private readonly List _locationListEntries; + private readonly ObjectList _locationListEntries; public DwarfLocationList() { - _locationListEntries = new List(); + _locationListEntries = new ObjectList(this); } - public ReadOnlyList LocationListEntries => _locationListEntries; - - public void AddLocationListEntry(DwarfLocationListEntry locationListEntry) - { - _locationListEntries.Add(this, locationListEntry); - } - - public void RemoveLocationList(DwarfLocationListEntry locationListEntry) - { - _locationListEntries.Remove(this, locationListEntry); - } - - public DwarfLocationListEntry RemoveLocationListEntryAt(int index) - { - return _locationListEntries.RemoveAt(this, index); - } + public ObjectList LocationListEntries => _locationListEntries; public override void UpdateLayout(DwarfLayoutContext layoutContext) { diff --git a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs index 86d1077..2cca202 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs @@ -12,29 +12,15 @@ namespace LibObjectFile.Dwarf; [DebuggerDisplay("Count = {LineTables.Count,nq}")] public sealed class DwarfLocationSection : DwarfRelocatableSection { - private readonly List _locationLists; + private readonly ObjectList _locationLists; public DwarfLocationSection() { - _locationLists = new List(); - } - - public ReadOnlyList LocationLists => _locationLists; - - public void AddLocationList(DwarfLocationList locationList) - { - _locationLists.Add(this, locationList); - } + _locationLists = new ObjectList(this); - public void RemoveLocationList(DwarfLocationList locationList) - { - _locationLists.Remove(this, locationList); } - public DwarfLocationList RemoveLineProgramTableAt(int index) - { - return _locationLists.RemoveAt(this, index); - } + public ObjectList LocationLists => _locationLists; public override void Read(DwarfReader reader) { @@ -43,7 +29,7 @@ public override void Read(DwarfReader reader) var locationList = new DwarfLocationList(); locationList.Position = reader.Position; locationList.Read(reader); - AddLocationList(locationList); + _locationLists.Add(locationList); } } diff --git a/src/LibObjectFile/Dwarf/DwarfUnit.cs b/src/LibObjectFile/Dwarf/DwarfUnit.cs index 2fd4312..7d4619c 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnit.cs @@ -114,7 +114,7 @@ public override void UpdateLayout(DwarfLayoutContext layoutContext) { // Before updating the layout, we need to compute the abbreviation Abbreviation = new DwarfAbbreviation(); - layoutContext.File.AbbreviationTable.AddAbbreviation(Abbreviation); + layoutContext.File.AbbreviationTable.Abbreviations.Add(Abbreviation); Root.UpdateAbbreviationItem(layoutContext); diff --git a/src/LibObjectFile/ObjectFileExtensions.cs b/src/LibObjectFile/ObjectFileExtensions.cs deleted file mode 100644 index a1bd817..0000000 --- a/src/LibObjectFile/ObjectFileExtensions.cs +++ /dev/null @@ -1,140 +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. - -using System; -using System.Collections.Generic; - -namespace LibObjectFile; - -public static class ObjectFileExtensions -{ - /// - /// Adds an attribute to . - /// - /// A attribute - public static void Add(this List list, TParent parent, TChild element) where TChild : ObjectFileElement where TParent: ObjectFileElement - { - if (element == null) throw new ArgumentNullException(nameof(element)); - if (element.Parent != null) - { - if (element.Parent == parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added"); - if (element.Parent != parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added to another {parent.GetType()} instance"); - } - - element.Parent = parent; - element.Index = list.Count; - list.Add(element); - } - - /// - /// Adds an element to the sorted list - /// - /// An element to add - public static void AddSorted(this List list, TParent parent, TChild element, bool requiresUnique = false) where TChild : ObjectFileElement, IComparable where TParent : ObjectFileElement - { - if (element == null) throw new ArgumentNullException(nameof(element)); - if (element.Parent != null) - { - if (element.Parent == parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added"); - if (element.Parent != parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added to another {parent.GetType()} instance"); - } - - int index; - - // Optimistic case, we add in order - if (list.Count == 0 || list[^1].CompareTo(element) < 0) - { - element.Parent = parent; - index = list.Count; - list.Add(element); - } - else - { - index = list.BinarySearch(element); - if (index < 0) - index = ~index; - else if (requiresUnique && list[index].CompareTo(element) == 0) - { - throw new InvalidOperationException($"A similar element to `{element}` has been already added to this collection at index {index}"); - } - - element.Parent = parent; - list.Insert(index, element); - } - - element.Index = index; - - // Update the index of following attributes - for (int i = index + 1; i < list.Count; i++) - { - var nextAttribute = list[i]; - nextAttribute.Index++; - } - } - - /// - /// Inserts an attribute into at the specified index. - /// - /// Index into to insert the specified attribute - /// The attribute to insert - public static void InsertAt(this List list, TParent parent, int index, TChild element) where TChild : ObjectFileElement where TParent : ObjectFileElement - { - if (index < 0 || index > list.Count) throw new ArgumentOutOfRangeException(nameof(index), $"Invalid index {index}, Must be >= 0 && <= {list.Count}"); - if (element == null) throw new ArgumentNullException(nameof(element)); - if (element.Parent != null) - { - if (element.Parent == parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added"); - if (element.Parent != parent) throw new InvalidOperationException($"Cannot add the {element.GetType()} as it is already added to another {parent.GetType()} instance"); - } - - element.Index = index; - list.Insert(index, element); - element.Parent = parent; - - // Update the index of following attributes - for (int i = index + 1; i < list.Count; i++) - { - var nextAttribute = list[i]; - nextAttribute.Index++; - } - } - - /// - /// Removes an attribute from - /// - /// The attribute to remove - public static void Remove(this List list, TParent parent, TChild child) where TChild : ObjectFileElement where TParent : ObjectFileElement - { - if (child == null) throw new ArgumentNullException(nameof(child)); - if (!ReferenceEquals(child.Parent, parent)) - { - throw new InvalidOperationException($"Cannot remove the {nameof(TChild)} as it is not part of this {parent.GetType()} instance"); - } - - var i = (int)child.Index; - list.RemoveAt(i); - child.Index = 0; - - // Update indices for other sections - for (int j = i + 1; j < list.Count; j++) - { - var nextEntry = list[j]; - nextEntry.Index--; - } - - child.Parent = null; - } - - /// - /// Removes an attribute from at the specified index. - /// - /// Index into to remove the specified attribute - public static TChild RemoveAt(this List list, TParent parent, int index) where TChild : ObjectFileElement where TParent : ObjectFileElement - { - if (index < 0 || index > list.Count) throw new ArgumentOutOfRangeException(nameof(index), $"Invalid index {index}, Must be >= 0 && <= {list.Count}"); - var child = list[index]; - Remove(list, parent, child); - return child; - } -} \ No newline at end of file From b09d57cce6a2d98fed2e684b6caec81b0d9f8ee3 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 19 Sep 2024 06:13:50 +0200 Subject: [PATCH 20/87] Use ObjectFileElement.ResetIndex --- src/LibObjectFile/Ar/ArArchiveFile.cs | 2 +- src/LibObjectFile/Collections/ObjectList.cs | 2 +- src/LibObjectFile/Collections/SortedObjectList.cs | 2 +- src/LibObjectFile/Elf/ElfObjectFile.cs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/LibObjectFile/Ar/ArArchiveFile.cs b/src/LibObjectFile/Ar/ArArchiveFile.cs index cb22ee5..2f9a004 100644 --- a/src/LibObjectFile/Ar/ArArchiveFile.cs +++ b/src/LibObjectFile/Ar/ArArchiveFile.cs @@ -156,7 +156,7 @@ public void RemoveFile(ArFile file) var i = (int)file.Index; _files.RemoveAt(i); - file.Index = 0; + file.ResetIndex(); // Update indices for other sections for (int j = i + 1; j < _files.Count; j++) diff --git a/src/LibObjectFile/Collections/ObjectList.cs b/src/LibObjectFile/Collections/ObjectList.cs index e0960c3..203dc34 100644 --- a/src/LibObjectFile/Collections/ObjectList.cs +++ b/src/LibObjectFile/Collections/ObjectList.cs @@ -105,7 +105,7 @@ public void RemoveAt(int index) var items = _items; var item = items[index]; item.Parent = null; - item.Index = 0; + item.ResetIndex(); items.RemoveAt(index); diff --git a/src/LibObjectFile/Collections/SortedObjectList.cs b/src/LibObjectFile/Collections/SortedObjectList.cs index f0a7ee0..b198852 100644 --- a/src/LibObjectFile/Collections/SortedObjectList.cs +++ b/src/LibObjectFile/Collections/SortedObjectList.cs @@ -94,7 +94,7 @@ public void RemoveAt(int index) var items = _items; var item = items[index]; item.Parent = null; - item.Index = 0; + item.ResetIndex(); items.RemoveAt(index); diff --git a/src/LibObjectFile/Elf/ElfObjectFile.cs b/src/LibObjectFile/Elf/ElfObjectFile.cs index 6c6bec6..0dc8c00 100644 --- a/src/LibObjectFile/Elf/ElfObjectFile.cs +++ b/src/LibObjectFile/Elf/ElfObjectFile.cs @@ -393,7 +393,7 @@ public void RemoveSegment(ElfSegment segment) var i = (int)segment.Index; _segments.RemoveAt(i); - segment.Index = 0; + segment.ResetIndex(); // Update indices for other sections for (int j = i + 1; j < _segments.Count; j++) @@ -533,7 +533,7 @@ public void RemoveSection(ElfSection section) var i = (int)section.Index; _sections.RemoveAt(i); - section.Index = 0; + section.ResetIndex(); bool wasShadow = section.IsShadow; From b150726c3bf6fcb03d215ef278db17920d6e7a04 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 19 Sep 2024 06:32:31 +0200 Subject: [PATCH 21/87] Remove ObjectFileElement.AssignChild/AttachChild/AttachNullableChild methods --- src/LibObjectFile/Dwarf/DwarfFile.cs | 36 ++++++------- src/LibObjectFile/Dwarf/DwarfUnit.cs | 4 +- src/LibObjectFile/ObjectFileElement.cs | 53 ++------------------ src/LibObjectFile/ObjectFileElementHolder.cs | 47 +++++++++++++++++ 4 files changed, 70 insertions(+), 70 deletions(-) create mode 100644 src/LibObjectFile/ObjectFileElementHolder.cs diff --git a/src/LibObjectFile/Dwarf/DwarfFile.cs b/src/LibObjectFile/Dwarf/DwarfFile.cs index 667e277..be0f216 100644 --- a/src/LibObjectFile/Dwarf/DwarfFile.cs +++ b/src/LibObjectFile/Dwarf/DwarfFile.cs @@ -10,57 +10,57 @@ namespace LibObjectFile.Dwarf; public class DwarfFile : DwarfContainer { - private DwarfAbbreviationTable _abbreviationTable; - private DwarfStringTable _stringTable; - private DwarfLineSection _lineSection; - private DwarfInfoSection _infoSection; - private DwarfAddressRangeTable _addressRangeTable; - private DwarfLocationSection _locationSection; + private ObjectFileElementHolder _abbreviationTable; + private ObjectFileElementHolder _stringTable; + private ObjectFileElementHolder _lineSection; + private ObjectFileElementHolder _infoSection; + private ObjectFileElementHolder _addressRangeTable; + private ObjectFileElementHolder _locationSection; public DwarfFile() { - AssignChild(this, new DwarfAbbreviationTable(), out _abbreviationTable); - AssignChild(this, new DwarfStringTable(), out _stringTable); - AssignChild(this, new DwarfLineSection(), out _lineSection); - AssignChild(this, new DwarfInfoSection(), out _infoSection); - AssignChild(this, new DwarfAddressRangeTable(), out _addressRangeTable); - AssignChild(this, new DwarfLocationSection(), out _locationSection); + _abbreviationTable = new(this, new DwarfAbbreviationTable()); + _stringTable = new(this, new DwarfStringTable()); + _lineSection = new(this, new DwarfLineSection()); + _infoSection = new(this, new DwarfInfoSection()); + _addressRangeTable = new(this, new DwarfAddressRangeTable()); + _locationSection = new(this, new DwarfLocationSection()); } public DwarfAbbreviationTable AbbreviationTable { get => _abbreviationTable; - set => AttachChild(this, value, ref _abbreviationTable); + set => _abbreviationTable.Set(this, value); } public DwarfStringTable StringTable { get => _stringTable; - set => AttachChild(this, value, ref _stringTable); + set => _stringTable.Set(this, value); } public DwarfLineSection LineSection { get => _lineSection; - set => AttachChild(this, value, ref _lineSection); + set => _lineSection.Set(this, value); } public DwarfAddressRangeTable AddressRangeTable { get => _addressRangeTable; - set => AttachChild(this, value, ref _addressRangeTable); + set => _addressRangeTable.Set(this, value); } public DwarfInfoSection InfoSection { get => _infoSection; - set => AttachChild(this, value, ref _infoSection); + set => _infoSection.Set(this, value); } public DwarfLocationSection LocationSection { get => _locationSection; - set => AttachChild(this, value, ref _locationSection); + set => _locationSection.Set(this, value); } public override void Read(DwarfReader reader) diff --git a/src/LibObjectFile/Dwarf/DwarfUnit.cs b/src/LibObjectFile/Dwarf/DwarfUnit.cs index 7d4619c..09dcd9d 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnit.cs @@ -13,7 +13,7 @@ namespace LibObjectFile.Dwarf; /// public abstract class DwarfUnit : DwarfContainer { - private DwarfDIE? _root; + private ObjectFileElementHolder _root; /// /// Gets or sets the encoding of this unit. @@ -51,7 +51,7 @@ public abstract class DwarfUnit : DwarfContainer public DwarfDIE? Root { get => _root; - set => AttachNullableChild(this, value, ref _root); + set => _root.Set(this, value); } /// diff --git a/src/LibObjectFile/ObjectFileElement.cs b/src/LibObjectFile/ObjectFileElement.cs index b0eb280..b2c58a3 100644 --- a/src/LibObjectFile/ObjectFileElement.cs +++ b/src/LibObjectFile/ObjectFileElement.cs @@ -50,7 +50,7 @@ protected virtual void ValidateParent(ObjectFileElement parent) } /// - /// Index within the containing list in a parent. If this object is not part of a list, this value is -1. + /// If the object is part of a list in its parent, this property returns the index within the containing list in the parent. Otherwise, this value is -1. /// public int Index { get; internal set; } @@ -78,7 +78,7 @@ public bool Contains(ulong offset) /// Checks this instance contains either the beginning or the end of the specified section or segment. /// /// The specified section or segment. - /// true if the either the offset or end of the part is within this segment or section range. + /// true if either the offset or end of the part is within this segment or section range. public bool Contains(ObjectFileElement element) { ArgumentNullException.ThrowIfNull(element); @@ -107,53 +107,6 @@ protected virtual bool PrintMembers(StringBuilder builder) { return false; } - - protected static void AssignChild(TParent parent, T child, out T field) where T : ObjectFileElement where TParent : ObjectFileElement - { - if (parent == null) throw new ArgumentNullException(nameof(parent)); - if (child == null) throw new ArgumentNullException(nameof(child)); - - if (child?.Parent != null) throw new InvalidOperationException($"Cannot set the {child.GetType()} as it already belongs to another {child.Parent.GetType()} instance"); - field = child!; - - if (child != null) - { - child.Parent = parent; - } - } - - protected static void AttachChild(TParent parent, T child, ref T field) where T : ObjectFileElement where TParent : ObjectFileElement - { - if (parent == null) throw new ArgumentNullException(nameof(parent)); - field.Parent = null; - - if (child?.Parent != null) throw new InvalidOperationException($"Cannot set the {child.GetType()} as it already belongs to another {child.Parent.GetType()} instance"); - field = child!; - - if (child != null) - { - child.Parent = parent; - } - } - - protected static void AttachNullableChild(TParent parent, T? child, ref T? field) where T : ObjectFileElement where TParent : ObjectFileElement - { - if (parent == null) throw new ArgumentNullException(nameof(parent)); - - if (field is not null) - { - field.Parent = null; - } - - if (child?.Parent != null) throw new InvalidOperationException($"Cannot set the {child.GetType()} as it already belongs to another {child.Parent.GetType()} instance"); - field = child!; - - if (child != null) - { - child.Parent = parent; - } - } - } public abstract class ObjectFileElement : ObjectFileElement @@ -177,4 +130,4 @@ public virtual void Read(TReader reader) public virtual void Write(TWriter writer) { } -} +} \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileElementHolder.cs b/src/LibObjectFile/ObjectFileElementHolder.cs new file mode 100644 index 0000000..7680648 --- /dev/null +++ b/src/LibObjectFile/ObjectFileElementHolder.cs @@ -0,0 +1,47 @@ +// 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; + +namespace LibObjectFile; + +/// +/// Helper struct to hold an element of type and ensure that it is properly set with its parent. +/// +/// The type of the object to hold +public struct ObjectFileElementHolder + where TObject : ObjectFileElement +{ + private TObject _element; + + public ObjectFileElementHolder(ObjectFileElement parent, TObject element) + { + ArgumentNullException.ThrowIfNull(parent); + ArgumentNullException.ThrowIfNull(element); + _element = null!; // Avoid warning + Set(parent, element); + } + + public TObject Element => _element; + + public static implicit operator TObject(ObjectFileElementHolder holder) => holder._element; + + public void Set(ObjectFileElement parent, TObject? element) + { + ArgumentNullException.ThrowIfNull(parent); + if (element?.Parent != null) throw new InvalidOperationException($"Cannot set the {element.GetType()} as it already belongs to another {element.Parent.GetType()} instance"); + + if (_element is not null) + { + _element.Parent = null; + } + + _element = element!; + + if (element != null) + { + element.Parent = parent; + } + } +} From 5880e1562fca17d5140e202f93cd32d2fc2f3f64 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 19 Sep 2024 06:38:10 +0200 Subject: [PATCH 22/87] Some cleanup in ObjectFileElement --- src/LibObjectFile/ObjectFileElement.cs | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/LibObjectFile/ObjectFileElement.cs b/src/LibObjectFile/ObjectFileElement.cs index b2c58a3..02b4d6a 100644 --- a/src/LibObjectFile/ObjectFileElement.cs +++ b/src/LibObjectFile/ObjectFileElement.cs @@ -19,9 +19,14 @@ protected ObjectFileElement() } /// - /// Gets or sets the position of this element relative to the top level parent. + /// Gets or sets the absolute position of this element relative to the top level parent. /// public ulong Position { get; set; } + + /// + /// Gets or sets the size in bytes of this element in the top level parent. This value might need to be updated via UpdateLayout on the top level parent. + /// + public virtual ulong Size { get; set; } /// /// Gets the containing parent. @@ -45,25 +50,11 @@ internal set } } - protected virtual void ValidateParent(ObjectFileElement parent) - { - } - /// /// If the object is part of a list in its parent, this property returns the index within the containing list in the parent. Otherwise, this value is -1. /// public int Index { get; internal set; } - internal void ResetIndex() - { - Index = -1; - } - - /// - /// Gets or sets the size of this section or segment in the parent . - /// - public virtual ulong Size { get; set; } - /// /// Checks if the specified offset is contained by this instance. /// @@ -107,6 +98,15 @@ protected virtual bool PrintMembers(StringBuilder builder) { return false; } + + protected virtual void ValidateParent(ObjectFileElement parent) + { + } + + internal void ResetIndex() + { + Index = -1; + } } public abstract class ObjectFileElement : ObjectFileElement From e851b4417e88d2c3a4d49905ca8c72917920f4ee Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 19 Sep 2024 20:45:02 +0200 Subject: [PATCH 23/87] Add PEVirtualObject --- src/LibObjectFile/Collections/ObjectList.cs | 52 +++++- .../Collections/SortedObjectList.cs | 120 +++++++++----- src/LibObjectFile/ObjectFileReaderWriter.cs | 4 +- .../PEBaseRelocationDirectory.cs | 6 +- .../PEBaseRelocationPageBlock.cs | 17 +- .../PEBaseRelocationPageBlockPart.cs | 2 +- .../PE/DataDirectory/PEDirectory.cs | 26 +-- .../PE/DataDirectory/PEImportAddressTable.cs | 4 +- .../PEImportAddressTableDirectory.cs | 40 ++++- .../PE/DataDirectory/PEImportDirectory.cs | 4 +- .../PE/DataDirectory/PEImportFunctionTable.cs | 4 +- .../PE/DataDirectory/PEImportLookupTable.cs | 4 +- src/LibObjectFile/PE/IVirtualAddressable.cs | 16 -- src/LibObjectFile/PE/PEFile.Read.cs | 14 +- src/LibObjectFile/PE/PEFile.cs | 41 +---- src/LibObjectFile/PE/PEObject.cs | 5 +- src/LibObjectFile/PE/PESection.cs | 118 +++---------- src/LibObjectFile/PE/PESectionData.cs | 49 ++---- ...onStreamData.cs => PEStreamSectionData.cs} | 10 +- src/LibObjectFile/PE/PETempSectionData.cs | 10 ++ src/LibObjectFile/PE/PEVirtualObject.cs | 155 ++++++++++++++++++ .../PE/PEVirtualObjectExtensions.cs | 98 +++++++++++ src/LibObjectFile/PE/RVALink.cs | 2 +- 23 files changed, 516 insertions(+), 285 deletions(-) delete mode 100644 src/LibObjectFile/PE/IVirtualAddressable.cs rename src/LibObjectFile/PE/{PESectionStreamData.cs => PEStreamSectionData.cs} (80%) create mode 100644 src/LibObjectFile/PE/PETempSectionData.cs create mode 100644 src/LibObjectFile/PE/PEVirtualObject.cs create mode 100644 src/LibObjectFile/PE/PEVirtualObjectExtensions.cs diff --git a/src/LibObjectFile/Collections/ObjectList.cs b/src/LibObjectFile/Collections/ObjectList.cs index 203dc34..83a0f92 100644 --- a/src/LibObjectFile/Collections/ObjectList.cs +++ b/src/LibObjectFile/Collections/ObjectList.cs @@ -29,10 +29,18 @@ namespace LibObjectFile.Collections; /// Initializes a new instance of the class. /// /// The parent object file node. - public ObjectList(ObjectFileElement parent, Action? added = null, Action? removing = null, Action? removed = null, Action? updated = null) + public ObjectList( + ObjectFileElement parent, + Action? adding= null, + Action? added = null, + Action? removing = null, + Action? removed = null, + Action? updating = null, + Action? updated = null + ) { ArgumentNullException.ThrowIfNull(parent); - _items = new InternalList(parent, added, removing, removed, updated); + _items = new InternalList(parent, adding, added, removing, removed, updating, updated); } public int Count => _items.Count; @@ -44,9 +52,11 @@ public ObjectList(ObjectFileElement parent, Action? [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Add(TObject item) { + CheckAdd(item); var items = _items; int index = items.Count; - items.Add(CheckAdd(item)); + items.Adding(index, item); + items.Add(AssignAdd(item)); item.Index = index; items.Added(item); } @@ -89,8 +99,12 @@ public bool Remove(TObject item) public void Insert(int index, TObject item) { + if ((uint)index > (uint)_items.Count) throw new ArgumentOutOfRangeException(nameof(index)); + + CheckAdd(item); var items = _items; - items.Insert(index, CheckAdd(item)); + items.Adding(index, item); + items.Insert(index, AssignAdd(item)); for (int i = index; i < items.Count; i++) { @@ -104,6 +118,7 @@ public void RemoveAt(int index) { var items = _items; var item = items[index]; + items.Removing(item); item.Parent = null; item.ResetIndex(); @@ -120,17 +135,18 @@ public TObject this[int index] get => _items[index]; set { - value = CheckAdd(value); + CheckAdd(value); // Unbind previous entry var items = _items; var previousItem = items[index]; + items.Updating(index, previousItem, value); items.Removing(previousItem); previousItem.Parent = null; previousItem.ResetIndex(); // Bind new entry - items[index] = value; + items[index] = AssignAdd(value); value.Index = index; items.Updated(index, previousItem, value); } @@ -148,32 +164,52 @@ IEnumerator IEnumerable.GetEnumerator() return ((IEnumerable)_items).GetEnumerator(); } - public TObject CheckAdd(TObject item) + private void CheckAdd(TObject item) { ArgumentNullException.ThrowIfNull(item); if (item.Parent != null) { throw new ArgumentException($"The object is already attached to another parent", nameof(item)); } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private TObject AssignAdd(TObject item) + { item.Parent = _items.Parent; return item; } - private sealed class InternalList(ObjectFileElement parent, Action? added, Action? removing, Action? removed, Action? updated) : List + private sealed class InternalList(ObjectFileElement parent, + Action? adding, + Action? added, + Action? removing, + Action? removed, + Action? updating, + Action? updated + ) : List { + private readonly Action? _adding = adding; private readonly Action? _added = added; private readonly Action? _removing = removing; private readonly Action? _removed = removed; + private readonly Action? _updating = updating; private readonly Action? _updated = updated; public readonly ObjectFileElement Parent = parent; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Adding(int index, TObject item) => _adding?.Invoke(Parent, index, item); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Added(TObject item) => _added?.Invoke(Parent, item); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Removing(TObject item) => _removing?.Invoke(Parent, item); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Updating(int index, TObject previousItem, TObject newItem) => _updating?.Invoke(Parent, index, previousItem, newItem); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Updated(int index, TObject previousItem, TObject newItem) => _updated?.Invoke(Parent, index, previousItem, newItem); diff --git a/src/LibObjectFile/Collections/SortedObjectList.cs b/src/LibObjectFile/Collections/SortedObjectList.cs index b198852..45b1439 100644 --- a/src/LibObjectFile/Collections/SortedObjectList.cs +++ b/src/LibObjectFile/Collections/SortedObjectList.cs @@ -6,6 +6,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Reflection; using System.Runtime.CompilerServices; namespace LibObjectFile.Collections; @@ -29,10 +30,10 @@ namespace LibObjectFile.Collections; /// Initializes a new instance of the class. /// /// The parent object file node. - public SortedObjectList(ObjectFileElement parent, Action? added = null, Action? removing = null, Action? removed = null, Action? updated = null) + public SortedObjectList(ObjectFileElement parent) { ArgumentNullException.ThrowIfNull(parent); - _items = new InternalList(parent, added, removing, removed, updated); + _items = new InternalList(parent); } public int Count => _items.Count; @@ -44,11 +45,24 @@ public SortedObjectList(ObjectFileElement parent, Action 0 && item.CompareTo(items[^1]) > 0) + { + int index = items.Count; + items.Add(AssignAdd(item)); + item.Index = index; + } + else + { + var index = items.BinarySearch(item); + if (index < 0) + { + index = ~index; + } + items.Insert(index, AssignAdd(item)); + item.Index = index; + } } public void Clear() @@ -57,11 +71,9 @@ public void Clear() 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.Removed(i, item); } items.Clear(); @@ -87,7 +99,31 @@ public bool Remove(TObject item) public int IndexOf(TObject item) => _items.IndexOf(item); - public void Insert(int index, TObject item) => throw new NotSupportedException("Insert is not supported"); + public void Insert(int index, TObject item) + { + if ((uint)index > (uint)_items.Count) throw new ArgumentOutOfRangeException(nameof(index)); + + CheckAdd(item); + var items = _items; + + var expectedIndex = items.BinarySearch(item); + if (expectedIndex < 0) + { + expectedIndex = ~expectedIndex; + } + + if (expectedIndex != index) + { + throw new ArgumentException($"The index {index} is not valid for the item {item} to maintain the order of the list"); + } + + items.Insert(index, AssignAdd(item)); + + for (int i = index; i < items.Count; i++) + { + items[i].Index = i; + } + } public void RemoveAt(int index) { @@ -109,19 +145,37 @@ public TObject this[int index] get => _items[index]; set { - value = CheckAdd(value); + if ((uint)index >= (uint)_items.Count) throw new ArgumentOutOfRangeException(nameof(index)); + 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.Updated(index, previousItem, value); + + var expectedIndex = items.BinarySearch(value); + if (expectedIndex < 0) + { + expectedIndex = ~expectedIndex; + } + if (expectedIndex != index) + { + throw new ArgumentException($"The index {index} is not valid for the item {value} to maintain the order of the list"); + } + + if (index < items.Count) + { + var previousItem = items[index]; + previousItem.Parent = null; + previousItem.ResetIndex(); + + // Bind new entry + items[index] = AssignAdd(value); + value.Index = index; + } + else + { + items.Add(AssignAdd(value)); + value.Index = items.Count - 1; + } } } @@ -137,37 +191,25 @@ IEnumerator IEnumerable.GetEnumerator() return ((IEnumerable)_items).GetEnumerator(); } - public TObject CheckAdd(TObject item) + private void CheckAdd(TObject item) { ArgumentNullException.ThrowIfNull(item); if (item.Parent != null) { throw new ArgumentException($"The object is already attached to another parent", nameof(item)); } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private TObject AssignAdd(TObject item) + { item.Parent = _items.Parent; return item; } - private sealed class InternalList(ObjectFileElement parent, Action? added, Action? removing, Action? removed, Action? updated) : List + private sealed class InternalList(ObjectFileElement parent) : List { - private readonly Action? _added = added; - private readonly Action? _removing = removing; - private readonly Action? _removed = removed; - private readonly Action? _updated = updated; - public readonly ObjectFileElement 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); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Updated(int index, TObject previousItem, TObject newItem) => _updated?.Invoke(Parent, index, previousItem, newItem); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Removed(int index, TObject removedItem) => _removed?.Invoke(Parent, index, removedItem); } internal sealed class ObjectListDebuggerView @@ -191,4 +233,4 @@ public TObject[] Items } } } -} \ No newline at end of file +} diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs index 25df4f8..edca421 100644 --- a/src/LibObjectFile/ObjectFileReaderWriter.cs +++ b/src/LibObjectFile/ObjectFileReaderWriter.cs @@ -18,11 +18,11 @@ public abstract class ObjectFileReaderWriter : VisitorContextBase { private Stream _stream; - protected ObjectFileReaderWriter(ObjectFileElement file, Stream stream) : this(file, stream, new DiagnosticBag()) + internal ObjectFileReaderWriter(ObjectFileElement file, Stream stream) : this(file, stream, new DiagnosticBag()) { } - protected ObjectFileReaderWriter(ObjectFileElement file, Stream stream, DiagnosticBag diagnostics) : base(file, diagnostics) + internal ObjectFileReaderWriter(ObjectFileElement file, Stream stream, DiagnosticBag diagnostics) : base(file, diagnostics) { _stream = stream; IsLittleEndian = true; diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs index 44ca038..8101456 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs @@ -13,7 +13,7 @@ namespace LibObjectFile.PE; public sealed class PEBaseRelocationDirectory : PEDirectory { - public PEBaseRelocationDirectory() : base(ImageDataDirectoryKind.BaseRelocation) + public PEBaseRelocationDirectory() : base(ImageDataDirectoryKind.BaseRelocation, false) { } @@ -24,7 +24,7 @@ public override void UpdateLayout(PEVisitorContext context) var size = 0UL; foreach (var block in Blocks) { - size += block.SizeOf; + size += block.CalculateSizeOf(); } Size = size; } @@ -156,7 +156,7 @@ protected override bool PrintMembers(StringBuilder builder) builder.Append(", "); } - builder.Append($", Blocks[{Blocks.Count}]"); + builder.Append($"Blocks[{Blocks.Count}]"); return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs index f6b9f5a..f042403 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs @@ -35,22 +35,19 @@ public PEBaseRelocationPageBlock(RVALink sectionRVALink) /// /// Gets the size of this block. /// - internal uint SizeOf + internal uint CalculateSizeOf() { - get + uint size = 0; + foreach (var part in Parts) { - uint size = 0; - foreach (var part in Parts) - { - size += part.SizeOf; - } - - return size; + size += part.SizeOf; } + + return size; } public override string ToString() { - return $"{nameof(PEBaseRelocationPageBlock)}, Section = {SectionRVALink.Element?.Name}, RVA = {SectionRVALink.VirtualAddress}, Size = {SizeOf}, Parts[{Parts.Count}]"; + return $"{nameof(PEBaseRelocationPageBlock)}, Section = {SectionRVALink.Element?.Name}, RVA = {SectionRVALink.VirtualAddress}, Size = {CalculateSizeOf()}, Parts[{Parts.Count}]"; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs index d3863c0..9eab9cd 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs @@ -42,7 +42,7 @@ internal uint SizeOf { get { - var size = sizeof(uint) + sizeof(uint) + Relocations.Count * sizeof(ushort); + var size = sizeof(uint) + sizeof(uint) + (Relocations.Count + 1) * sizeof(ushort); // Align to 4 bytes size = (size + 3) & ~3; return (uint)size; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs index c818b1b..0de49a0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs @@ -8,7 +8,7 @@ namespace LibObjectFile.PE; public abstract class PEDirectory : PESectionData { - protected PEDirectory(ImageDataDirectoryKind kind) + protected PEDirectory(ImageDataDirectoryKind kind, bool hasChildren) : base(hasChildren) { Kind = kind; } @@ -55,7 +55,7 @@ protected override void ValidateParent(ObjectFileElement parent) public sealed class PEExportDirectory : PEDirectory { - public PEExportDirectory() : base(ImageDataDirectoryKind.Export) + public PEExportDirectory() : base(ImageDataDirectoryKind.Export, true) { } @@ -77,7 +77,7 @@ public override void Write(PEImageWriter writer) public sealed class PEResourceDirectory : PEDirectory { - public PEResourceDirectory() : base(ImageDataDirectoryKind.Resource) + public PEResourceDirectory() : base(ImageDataDirectoryKind.Resource, false) { } @@ -99,7 +99,7 @@ public override void Write(PEImageWriter writer) public sealed class PEExceptionDirectory : PEDirectory { - public PEExceptionDirectory() : base(ImageDataDirectoryKind.Exception) + public PEExceptionDirectory() : base(ImageDataDirectoryKind.Exception, false) { } @@ -121,7 +121,7 @@ public override void Write(PEImageWriter writer) public sealed class PEDebugDirectory : PEDirectory { - public PEDebugDirectory() : base(ImageDataDirectoryKind.Debug) + public PEDebugDirectory() : base(ImageDataDirectoryKind.Debug, false) { } @@ -143,7 +143,7 @@ public override void Write(PEImageWriter writer) public sealed class PELoadConfigDirectory : PEDirectory { - public PELoadConfigDirectory() : base(ImageDataDirectoryKind.LoadConfig) + public PELoadConfigDirectory() : base(ImageDataDirectoryKind.LoadConfig, false) { } @@ -165,7 +165,7 @@ public override void Write(PEImageWriter writer) public sealed class PEBoundImportDirectory : PEDirectory { - public PEBoundImportDirectory() : base(ImageDataDirectoryKind.BoundImport) + public PEBoundImportDirectory() : base(ImageDataDirectoryKind.BoundImport, false) { } @@ -187,7 +187,7 @@ public override void Write(PEImageWriter writer) public sealed class PETlsDirectory : PEDirectory { - public PETlsDirectory() : base(ImageDataDirectoryKind.Tls) + public PETlsDirectory() : base(ImageDataDirectoryKind.Tls, false) { } @@ -209,7 +209,7 @@ public override void Write(PEImageWriter writer) public sealed class PEDelayImportDirectory : PEDirectory { - public PEDelayImportDirectory() : base(ImageDataDirectoryKind.DelayImport) + public PEDelayImportDirectory() : base(ImageDataDirectoryKind.DelayImport, false) { } @@ -231,7 +231,7 @@ public override void Write(PEImageWriter writer) public sealed class PEClrMetadata : PEDirectory { - public PEClrMetadata() : base(ImageDataDirectoryKind.ClrMetadata) + public PEClrMetadata() : base(ImageDataDirectoryKind.ClrMetadata, false) { } @@ -253,7 +253,7 @@ public override void Write(PEImageWriter writer) public sealed class PEArchitectureDirectory : PEDirectory { - public PEArchitectureDirectory() : base(ImageDataDirectoryKind.Architecture) + public PEArchitectureDirectory() : base(ImageDataDirectoryKind.Architecture, false) { } @@ -275,7 +275,7 @@ public override void Write(PEImageWriter writer) public sealed class PEGlobalPointerDirectory : PEDirectory { - public PEGlobalPointerDirectory() : base(ImageDataDirectoryKind.GlobalPointer) + public PEGlobalPointerDirectory() : base(ImageDataDirectoryKind.GlobalPointer, false) { } @@ -297,7 +297,7 @@ public override void Write(PEImageWriter writer) public sealed class PESecurityDirectory : PEDirectory { - public PESecurityDirectory() : base(ImageDataDirectoryKind.Security) + public PESecurityDirectory() : base(ImageDataDirectoryKind.Security, false) { } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs index 753cdab..08c284e 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs @@ -8,11 +8,11 @@ namespace LibObjectFile.PE; -public class PEImportAddressTable : PEObject +public class PEImportAddressTable : PEVirtualObject { internal readonly PEImportFunctionTable FunctionTable; - public PEImportAddressTable() + public PEImportAddressTable() : base(false) { FunctionTable = new PEImportFunctionTable(); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs index b18f995..1ed051a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs @@ -3,32 +3,62 @@ // See the license.txt file in the project root for more information. using System; +using System.Runtime.InteropServices; using LibObjectFile.Collections; namespace LibObjectFile.PE; public sealed class PEImportAddressTableDirectory : PEDirectory { - private readonly ObjectList _tables; + private readonly ObjectList _content; - public PEImportAddressTableDirectory() : base(ImageDataDirectoryKind.ImportAddressTable) + public PEImportAddressTableDirectory() : base(ImageDataDirectoryKind.ImportAddressTable, true) { - _tables = new ObjectList(this); + _content = CreateObjectList(this); } - public ObjectList Tables => _tables; + public ObjectList Content => _content; public override void UpdateLayout(PEVisitorContext context) { ulong size = 0; - foreach (var table in _tables) + var va = VirtualAddress; + foreach (var table in _content) { + table.VirtualAddress = va; + // Update layout will update virtual address table.UpdateLayout(context); + va += (uint)table.Size; size += table.Size; } Size = size; } + protected override bool TryFindByVirtualAddressInChildren(RVA virtualAddress, out PEVirtualObject? result) + { + var content = CollectionsMarshal.AsSpan(_content.UnsafeList); + foreach (var table in content) + { + if (table.TryFindByVirtualAddress(virtualAddress, out result)) + { + return true; + } + } + + result = null; + return false; + } + + protected override void UpdateVirtualAddressInChildren() + { + var va = VirtualAddress; + foreach (var table in _content) + { + table.UpdateVirtualAddress(va); + va += (uint)table.Size; + } + } + public override void Read(PEImageReader reader) => throw new NotSupportedException(); // Not called directly for this object, we are calling on tables directly public override void Write(PEImageWriter writer) => throw new NotSupportedException(); // Not called directly for this object, we are calling on tables directly diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index fced964..5f8b6a9 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -14,7 +14,7 @@ public sealed class PEImportDirectory : PEDirectory { private readonly ObjectList _entries; - public PEImportDirectory() : base(ImageDataDirectoryKind.Import) + public PEImportDirectory() : base(ImageDataDirectoryKind.Import, false) { _entries = new(this); } @@ -77,7 +77,7 @@ public override void Read(PEImageReader reader) _entries.Add( new PEImportDirectoryEntry( // Name - new(new(PESectionDataTemp.Instance, rawEntry.NameRVA)), + new(new(PETempSectionData.Instance, rawEntry.NameRVA)), // ImportAddressTable new PEImportAddressTable() { diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index defb21d..10b4e65 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -78,7 +78,7 @@ private unsafe void Read32(PEImageReader reader) Entries.Add( entry.IsImportByOrdinal ? new PEImportFunctionEntry(entry.Ordinal) - : new PEImportFunctionEntry(new ZeroTerminatedAsciiStringLink(new(PESectionDataTemp.Instance, entry.HintNameTableRVA))) + : new PEImportFunctionEntry(new ZeroTerminatedAsciiStringLink(new(PETempSectionData.Instance, entry.HintNameTableRVA))) ); } } @@ -104,7 +104,7 @@ private unsafe void Read64(PEImageReader reader) Entries.Add( entry.IsImportByOrdinal ? new PEImportFunctionEntry(entry.Ordinal) - : new PEImportFunctionEntry(new ZeroTerminatedAsciiStringLink(new(PESectionDataTemp.Instance, entry.HintNameTableRVA))) + : new PEImportFunctionEntry(new ZeroTerminatedAsciiStringLink(new(PETempSectionData.Instance, entry.HintNameTableRVA))) ); } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs index 4c0a9ff..56c3384 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -8,11 +8,11 @@ namespace LibObjectFile.PE; -public sealed class PEImportLookupTable : PEObject +public sealed class PEImportLookupTable : PEVirtualObject { internal readonly PEImportFunctionTable FunctionTable; - public PEImportLookupTable() + public PEImportLookupTable() : base(false) { FunctionTable = new PEImportFunctionTable(); } diff --git a/src/LibObjectFile/PE/IVirtualAddressable.cs b/src/LibObjectFile/PE/IVirtualAddressable.cs deleted file mode 100644 index a04d181..0000000 --- a/src/LibObjectFile/PE/IVirtualAddressable.cs +++ /dev/null @@ -1,16 +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; - -/// -/// Interface for an object that has a virtual address. -/// -public interface IVirtualAddressable -{ - /// - /// Gets the virtual address of this object. - /// - RVA VirtualAddress { get; } -} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 9b73f57..583a08c 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -269,7 +269,7 @@ private void InitializeSections(PEImageReader imageReader, ReadOnlySpan TryFindSection(virtualAddress, 0, out section); - - public bool TryFindSection(RVA virtualAddress, uint virtualSize, [NotNullWhen(true)] out PESection? section) - { - nint low = 0; - var sections = CollectionsMarshal.AsSpan(_sections.UnsafeList); - nint high = sections.Length - 1; - ref var firstSection = ref MemoryMarshal.GetReference(sections); - - while (low <= high) - { - nint mid = low + ((high - low) >>> 1); - var midSection = Unsafe.Add(ref firstSection, mid); - - if (midSection.ContainsVirtual(virtualAddress, virtualSize)) - { - section = midSection; - return true; - } - if (midSection.VirtualAddress < virtualAddress) - { - low = mid + 1; - } - else - { - high = mid - 1; - } - } + public bool TryFindSection(RVA virtualAddress, [NotNullWhen(true)] out PESection? section) + => _sections.TryFindByVirtualAddress(virtualAddress, out section); - section = null; - return false; - } + public bool TryFindSection(RVA virtualAddress, uint size, [NotNullWhen(true)] out PESection? section) + => _sections.TryFindByVirtualAddress(virtualAddress, size, out section); public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESectionData? sectionData) { @@ -205,12 +176,8 @@ public List GetAllSectionData() foreach (var section in sections) { - var va = section.VirtualAddress; foreach (var data in section.DataParts) { - var size = (uint)data.Size; - data.VirtualAddress = va; - va += size; dataList.Add(data); } } diff --git a/src/LibObjectFile/PE/PEObject.cs b/src/LibObjectFile/PE/PEObject.cs index a573b8d..4a5ae5a 100644 --- a/src/LibObjectFile/PE/PEObject.cs +++ b/src/LibObjectFile/PE/PEObject.cs @@ -6,5 +6,8 @@ namespace LibObjectFile.PE; +/// +/// Base class for all Portable Executable (PE) objects. +/// [DebuggerDisplay("{ToString(),nq}")] -public abstract class PEObject : ObjectFileElement; +public abstract class PEObject : ObjectFileElement; \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index 7d3c02d..446836b 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -16,16 +16,16 @@ namespace LibObjectFile.PE; /// /// Defines a section in a Portable Executable (PE) image. /// -public class PESection : PEObject, IVirtualAddressable +public sealed class PESection : PEVirtualObject { private readonly ObjectList _dataParts; - public PESection(PESectionName name, RVA virtualAddress, RVA virtualSize) + public PESection(PESectionName name, RVA virtualAddress, RVA virtualSize) : base(true) { Name = name; VirtualAddress = virtualAddress; VirtualSize = virtualSize; - _dataParts = new ObjectList(this, SectionDataAdded, null, SectionDataRemoved, SectionDataUpdated); + _dataParts = PEVirtualObject.CreateObjectList(this); // Most of the time readable Characteristics = SectionCharacteristics.MemRead; } @@ -40,11 +40,6 @@ public PESection(PESectionName name, RVA virtualAddress, RVA virtualSize) /// public PESectionName Name { get; } - /// - /// The address of the first byte of the section when loaded into memory, relative to the image base. - /// - public RVA VirtualAddress { get; } - /// /// The total size of the section when loaded into memory. /// If this value is greater than , the section is zero-padded. @@ -68,57 +63,7 @@ public PESection(PESectionName name, RVA virtualAddress, RVA virtualSize) /// The section data that contains the virtual address, if found. /// true if the section data was found; otherwise, false. public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESectionData? sectionData) - { - // Binary search - nint low = 0; - - var dataParts = CollectionsMarshal.AsSpan(_dataParts.UnsafeList); - nint high = dataParts.Length - 1; - ref var firstData = ref MemoryMarshal.GetReference(dataParts); - - while (low <= high) - { - nint mid = low + (high - low) >>> 1; - var trySectionData = Unsafe.Add(ref firstData, mid); - - if (trySectionData.ContainsVirtual(virtualAddress)) - { - sectionData = trySectionData; - return true; - } - - if (trySectionData.VirtualAddress < virtualAddress) - { - low = mid + 1; - } - else - { - high = mid - 1; - } - } - - sectionData = null; - return false; - } - - /// - /// Checks if the specified virtual address is contained by this section. - /// - /// The virtual address to check if it belongs to this section. - /// true if the virtual address is within the section range. - [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. - /// - /// 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. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ContainsVirtual(RVA virtualAddress, uint size) - => virtualAddress >= VirtualAddress && virtualAddress + size <= VirtualAddress + VirtualSize; + => _dataParts.TryFindByVirtualAddress(virtualAddress, out sectionData); /// public override void UpdateLayout(PEVisitorContext context) @@ -155,6 +100,26 @@ protected override bool PrintMembers(StringBuilder builder) builder.Append($"VirtualAddress = {VirtualAddress}, VirtualSize = 0x{VirtualSize:X4}, DataParts[{DataParts.Count}]"); return true; } + + protected override bool TryFindByVirtualAddressInChildren(RVA virtualAddress, out PEVirtualObject? result) + { + var parts = CollectionsMarshal.AsSpan(_dataParts.UnsafeList); + foreach (var data in parts) + { + if (data.TryFindByVirtualAddress(virtualAddress, out result)) + { + return true; + } + } + + result = null; + return false; + } + + protected override void UpdateVirtualAddressInChildren() + { + // TODO? + } /// /// Gets the default characteristics for a section name. @@ -187,39 +152,4 @@ public static SectionCharacteristics GetDefaultSectionCharacteristics(PESectionN _ => SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemRead }; } - - private static void SectionDataAdded(ObjectFileElement parent, PESectionData sectionData) - { - var section = (PESection) parent; - section.UpdateSectionDataVirtualAddress(sectionData.Index); - } - - private static void SectionDataRemoved(ObjectFileElement parent, int index, PESectionData removedSectionData) - { - var section = (PESection) parent; - section.UpdateSectionDataVirtualAddress(index); - } - private static void SectionDataUpdated(ObjectFileElement parent, int index, PESectionData previousSectionData, PESectionData newSectionData) - { - var section = (PESection) parent; - section.UpdateSectionDataVirtualAddress(index); - } - - private void UpdateSectionDataVirtualAddress(int startIndex) - { - var va = VirtualAddress; - var list = _dataParts.UnsafeList; - if (startIndex > 0) - { - var previousData = list[startIndex - 1]; - va = previousData.VirtualAddress + (uint)previousData.Size; - } - - for (int i = startIndex; i < list.Count; i++) - { - var data = list[i]; - data.VirtualAddress = va; - va += (uint)data.Size; - } - } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index f00ddd8..02189f4 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -11,38 +11,12 @@ namespace LibObjectFile.PE; /// /// Base class for data contained in a . /// -public abstract class PESectionData : PEObject, IVirtualAddressable +public abstract class PESectionData : PEVirtualObject { - /// - /// Gets the parent of this section data. - /// - public new PESection? Parent + protected PESectionData(bool hasChildren) : base(hasChildren) { - get => (PESection?)base.Parent; - - internal set => base.Parent = value; } - /// - /// Gets the virtual address of this section data. - /// - /// - /// This property is updated dynamically based on the previous section data. - /// - public RVA VirtualAddress - { - get; - internal set; - } - - /// - /// Checks if the specified virtual address is contained by this instance. - /// - /// The virtual address to check if it belongs to this instance. - /// true if the specified virtual address is contained by this instance; otherwise, false. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ContainsVirtual(RVA virtualAddress) => VirtualAddress <= virtualAddress && virtualAddress < VirtualAddress + Size; - public virtual int ReadAt(uint offset, Span destination) { throw new NotSupportedException($"The read operation is not supported for {this.GetType().FullName}"); @@ -55,14 +29,19 @@ public virtual void WriteAt(uint offset, ReadOnlySpan source) protected override bool PrintMembers(StringBuilder builder) { - builder.Append($"VirtualAddress: {VirtualAddress}, Size = 0x{Size:X4}"); + builder.Append($"VirtualAddress = {VirtualAddress}, Size = 0x{Size:X4}"); + if (Parent is PESection section) + { + builder.Append($", Section = {section.Name}"); + } return true; } -} - - -internal sealed class PESectionDataTemp : PESectionData -{ - public static readonly PESectionDataTemp Instance = new (); + protected override void ValidateParent(ObjectFileElement parent) + { + if (parent is not PESection && parent is not PESectionData) + { + throw new ArgumentException($"Invalid parent type {parent.GetType().FullName}. Expecting a parent of type {typeof(PESection).FullName} or {typeof(PESectionData).FullName}"); + } + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESectionStreamData.cs b/src/LibObjectFile/PE/PEStreamSectionData.cs similarity index 80% rename from src/LibObjectFile/PE/PESectionStreamData.cs rename to src/LibObjectFile/PE/PEStreamSectionData.cs index 158645b..7e046d7 100644 --- a/src/LibObjectFile/PE/PESectionStreamData.cs +++ b/src/LibObjectFile/PE/PEStreamSectionData.cs @@ -10,23 +10,23 @@ namespace LibObjectFile.PE; /// /// Gets a stream section data in a Portable Executable (PE) image. /// -public class PESectionStreamData : PESectionData +public class PEStreamSectionData : PESectionData { private Stream _stream; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public PESectionStreamData() + public PEStreamSectionData() : base(false) { _stream = Stream.Null; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The stream containing the data of this section data. - public PESectionStreamData(Stream stream) + public PEStreamSectionData(Stream stream) : base(false) { ArgumentNullException.ThrowIfNull(stream); _stream = stream; diff --git a/src/LibObjectFile/PE/PETempSectionData.cs b/src/LibObjectFile/PE/PETempSectionData.cs new file mode 100644 index 0000000..bae1ac3 --- /dev/null +++ b/src/LibObjectFile/PE/PETempSectionData.cs @@ -0,0 +1,10 @@ +// 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; + +internal sealed class PETempSectionData() : PESectionData(false) +{ + public static readonly PETempSectionData Instance = new (); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEVirtualObject.cs b/src/LibObjectFile/PE/PEVirtualObject.cs new file mode 100644 index 0000000..cb4b537 --- /dev/null +++ b/src/LibObjectFile/PE/PEVirtualObject.cs @@ -0,0 +1,155 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using LibObjectFile.Collections; + +namespace LibObjectFile.PE; + +/// +/// Base class for all Portable Executable (PE) objects that have a virtual address. +/// +public abstract class PEVirtualObject : PEObject +{ + protected PEVirtualObject(bool hasChildren) + { + HasChildren = hasChildren; + } + + /// + /// Gets a value indicating whether this object has children. + /// + public bool HasChildren { get; } + + /// + /// The address of the first byte of the section when loaded into memory, relative to the image base. + /// + public RVA VirtualAddress { get; internal set; } + + /// + /// Checks if the specified virtual address is contained in this object. + /// + /// The virtual address to check. + /// true if the specified virtual address is contained in this object; otherwise, false. + public bool ContainsVirtual(RVA virtualAddress) + => VirtualAddress <= virtualAddress && virtualAddress < VirtualAddress + (uint)Size; + + /// + /// Checks if the specified virtual address is contained in this object. + /// + /// The virtual address to check. + /// The size of the data that must be contained. + /// true if the specified virtual address is contained in this object; otherwise, false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ContainsVirtual(RVA virtualAddress, uint size) + => VirtualAddress <= virtualAddress && virtualAddress + size <= VirtualAddress + (uint)Size; + + /// + /// Tries to find a virtual object by its virtual address. + /// + /// The virtual address to search for. + /// The virtual object that contains the virtual address, if found. + /// true if the virtual object was found; otherwise, false. + public bool TryFindByVirtualAddress(RVA virtualAddress, out PEVirtualObject? result) + { + if (ContainsVirtual(virtualAddress)) + { + if (HasChildren) + { + if (TryFindByVirtualAddressInChildren(virtualAddress, out result)) + { + return true; + } + } + else + { + result = this; + return true; + } + } + + result = null; + return false; + } + + /// + /// Try to find a virtual object by its virtual address in children. + /// + /// The virtual address to search for. + /// The virtual object that contains the virtual address, if found. + /// true if the virtual object was found; otherwise, false. + /// + protected virtual bool TryFindByVirtualAddressInChildren(RVA virtualAddress, out PEVirtualObject? result) + { + throw new NotImplementedException("This method must be implemented by PEVirtualObject with children"); + } + + internal void UpdateVirtualAddress(RVA virtualAddress) + { + VirtualAddress = virtualAddress; + if (HasChildren) + { + UpdateVirtualAddressInChildren(); + } + } + + /// + /// Updates the virtual address of children. + /// + protected virtual void UpdateVirtualAddressInChildren() + { + } + + public static ObjectList CreateObjectList(PEVirtualObject parent) where TVirtualObject : PEVirtualObject + { + ObjectList objectList = default; + objectList = new ObjectList(parent, null, SectionDataAdded, null, SectionDataRemoved, null, SectionDataUpdated); + return objectList; + + void SectionDataAdded(ObjectFileElement vParent, TVirtualObject item) + { + // ReSharper disable once AccessToModifiedClosure + UpdateSectionDataVirtualAddress((PEVirtualObject)vParent, objectList, item.Index); + } + + void SectionDataRemoved(ObjectFileElement vParent, int index, TVirtualObject item) + { + // ReSharper disable once AccessToModifiedClosure + UpdateSectionDataVirtualAddress((PEVirtualObject)vParent, objectList, index); + } + + void SectionDataUpdated(ObjectFileElement vParent, int index, TVirtualObject previousItem, TVirtualObject newItem) + { + // ReSharper disable once AccessToModifiedClosure + UpdateSectionDataVirtualAddress((PEVirtualObject)vParent, objectList, index); + } + + static void UpdateSectionDataVirtualAddress(PEVirtualObject parent, ObjectList items, int startIndex) + { + RVA va; + var span = CollectionsMarshal.AsSpan(items.UnsafeList); + if (startIndex > 0) + { + var previousData = span[startIndex - 1]; + va = previousData.VirtualAddress + (uint)previousData.Size; + } + else + { + va = parent.VirtualAddress; + } + + for (int i = startIndex; i < span.Length; i++) + { + var data = span[i]; + + data.VirtualAddress = va; + data.UpdateVirtualAddressInChildren(); + + va += (uint)data.Size; + } + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEVirtualObjectExtensions.cs b/src/LibObjectFile/PE/PEVirtualObjectExtensions.cs new file mode 100644 index 0000000..d9e7c7d --- /dev/null +++ b/src/LibObjectFile/PE/PEVirtualObjectExtensions.cs @@ -0,0 +1,98 @@ +// 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.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using LibObjectFile.Collections; + +namespace LibObjectFile.PE; + +public static class PEVirtualObjectExtensions +{ + /// + /// Tries to find a virtual object by its virtual address. + /// + /// The virtual address to search for. + /// The section data that contains the virtual address, if found. + /// true if the section data was found; otherwise, false. + public static bool TryFindByVirtualAddress(this ObjectList list, RVA virtualAddress, uint size, [NotNullWhen(true)] out TVirtualObject? item) + where TVirtualObject : PEVirtualObject + { + // Binary search + nint low = 0; + + var dataParts = CollectionsMarshal.AsSpan(list.UnsafeList); + nint high = dataParts.Length - 1; + ref var firstData = ref MemoryMarshal.GetReference(dataParts); + + while (low <= high) + { + nint mid = low + ((high - low) >>> 1); + var trySectionData = Unsafe.Add(ref firstData, mid); + + if (trySectionData.ContainsVirtual(virtualAddress, size)) + { + item = trySectionData; + return true; + } + + if (virtualAddress > trySectionData.VirtualAddress) + { + low = mid + 1; + } + else + { + high = mid - 1; + } + } + + item = null; + return false; + } + + + /// + /// Tries to find a virtual object by its virtual address. + /// + /// The virtual address to search for. + /// The section data that contains the virtual address, if found. + /// true if the section data was found; otherwise, false. + public static bool TryFindByVirtualAddress(this ObjectList list, RVA virtualAddress, [NotNullWhen(true)] out TVirtualObject? item) + where TVirtualObject : PEVirtualObject + { + // Binary search + nint low = 0; + + var dataParts = CollectionsMarshal.AsSpan(list.UnsafeList); + nint high = dataParts.Length - 1; + ref var firstData = ref MemoryMarshal.GetReference(dataParts); + + while (low <= high) + { + nint mid = low + ((high - low) >>> 1); + var trySectionData = Unsafe.Add(ref firstData, mid); + + if (trySectionData.ContainsVirtual(virtualAddress)) + { + item = trySectionData; + return true; + } + + if (virtualAddress > trySectionData.VirtualAddress) + { + low = mid + 1; + } + else + { + high = mid - 1; + } + } + + item = null; + return false; + } + +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/RVALink.cs b/src/LibObjectFile/PE/RVALink.cs index 7589023..66d6d1a 100644 --- a/src/LibObjectFile/PE/RVALink.cs +++ b/src/LibObjectFile/PE/RVALink.cs @@ -11,7 +11,7 @@ namespace LibObjectFile.PE; /// The virtual addressable object linked. /// The offset within this element. public record struct RVALink(TVirtualAddressable? Element, uint OffsetInElement) - where TVirtualAddressable : class, IVirtualAddressable + where TVirtualAddressable : PEVirtualObject { /// /// Gets a value indicating whether this instance is null. From 1ed56b8b73715c76a283658c1b353d6568518e0c Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 19 Sep 2024 21:05:26 +0200 Subject: [PATCH 24/87] Revert ObjectFileElement.Size to non virtual --- src/LibObjectFile/ObjectFileElement.cs | 4 ++-- src/LibObjectFile/PE/PEStreamSectionData.cs | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/LibObjectFile/ObjectFileElement.cs b/src/LibObjectFile/ObjectFileElement.cs index 02b4d6a..a822c42 100644 --- a/src/LibObjectFile/ObjectFileElement.cs +++ b/src/LibObjectFile/ObjectFileElement.cs @@ -22,11 +22,11 @@ protected ObjectFileElement() /// Gets or sets the absolute position of this element relative to the top level parent. /// public ulong Position { get; set; } - + /// /// Gets or sets the size in bytes of this element in the top level parent. This value might need to be updated via UpdateLayout on the top level parent. /// - public virtual ulong Size { get; set; } + public ulong Size { get; set; } /// /// Gets the containing parent. diff --git a/src/LibObjectFile/PE/PEStreamSectionData.cs b/src/LibObjectFile/PE/PEStreamSectionData.cs index 7e046d7..ec901b9 100644 --- a/src/LibObjectFile/PE/PEStreamSectionData.cs +++ b/src/LibObjectFile/PE/PEStreamSectionData.cs @@ -20,6 +20,7 @@ public class PEStreamSectionData : PESectionData public PEStreamSectionData() : base(false) { _stream = Stream.Null; + Size = 0; } /// @@ -30,6 +31,7 @@ public PEStreamSectionData(Stream stream) : base(false) { ArgumentNullException.ThrowIfNull(stream); _stream = stream; + Size = (ulong)stream.Length; } /// @@ -38,13 +40,16 @@ public PEStreamSectionData(Stream stream) : base(false) public Stream Stream { get => _stream; - set => _stream = value ?? throw new ArgumentNullException(nameof(value)); + set + { + _stream = value ?? throw new ArgumentNullException(nameof(value)); + Size = (ulong)value.Length; + } } - public override ulong Size + public override void UpdateLayout(PEVisitorContext layoutContext) { - get => (ulong)Stream.Length; - set => throw new InvalidOperationException(); + Size = (ulong)Stream.Length; } public override void Read(PEImageReader reader) From 416833e394104ad20343278b8494dc4c9917f796 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 19 Sep 2024 22:25:15 +0200 Subject: [PATCH 25/87] Fix loading of import table --- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 1 + .../PE/DataDirectory/PEDirectory.cs | 2 +- .../PE/DataDirectory/PEImportAddressTable.cs | 2 +- .../PEImportAddressTableDirectory.cs | 6 +- .../PE/DataDirectory/PEImportDirectory.cs | 19 +++-- .../DataDirectory/PEImportDirectoryEntry.cs | 28 +------ .../PE/DataDirectory/PEImportFunctionTable.cs | 10 +-- .../PE/DataDirectory/PEImportLookupTable.cs | 10 +-- .../PE/Internal/RawImportFunctionEntry32.cs | 6 +- .../PE/Internal/RawImportFunctionEntry64.cs | 6 +- src/LibObjectFile/PE/PEFile.Read.cs | 75 +++++++++++++++++-- 11 files changed, 98 insertions(+), 67 deletions(-) diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 318a164..66bf2aa 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -139,4 +139,5 @@ public enum DiagnosticId PE_ERR_ImportLookupTableInvalidParent = 3043, PE_ERR_ImportDirectoryInvalidImportAddressTableRVA = 3044, PE_ERR_ImportDirectoryInvalidImportLookupTableRVA = 3045, + PE_ERR_ImportAddressTableNotFound = 3046 } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs index 0de49a0..7971b95 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs @@ -21,7 +21,7 @@ protected PEDirectory(ImageDataDirectoryKind kind, bool hasChildren) : base(hasC /// /// The kind of PE directory entry. /// A PE directory entry. - internal static PEDirectory Create(ImageDataDirectoryKind kind, RVALink link) + internal static PEDirectory Create(ImageDataDirectoryKind kind) { return kind switch { diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs index 08c284e..6b09f17 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs @@ -8,7 +8,7 @@ namespace LibObjectFile.PE; -public class PEImportAddressTable : PEVirtualObject +public class PEImportAddressTable : PESectionData { internal readonly PEImportFunctionTable FunctionTable; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs index 1ed051a..86727af 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs @@ -10,14 +10,14 @@ namespace LibObjectFile.PE; public sealed class PEImportAddressTableDirectory : PEDirectory { - private readonly ObjectList _content; + private readonly ObjectList _content; public PEImportAddressTableDirectory() : base(ImageDataDirectoryKind.ImportAddressTable, true) { - _content = CreateObjectList(this); + _content = CreateObjectList(this); } - public ObjectList Content => _content; + public ObjectList Content => _content; public override void UpdateLayout(PEVisitorContext context) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 5f8b6a9..95b0fbb 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -20,9 +20,9 @@ public PEImportDirectory() : base(ImageDataDirectoryKind.Import, false) } public ObjectList Entries => _entries; - public override unsafe void UpdateLayout(PEVisitorContext context) + public override void UpdateLayout(PEVisitorContext context) { - Size = (ulong)((_entries.Count + 1) * sizeof(RawImportDirectoryEntry)); + UpdateSize(); } public override void Read(PEImageReader reader) @@ -54,24 +54,24 @@ public override void Read(PEImageReader reader) } // Find the section data for the ImportLookupTableRVA - if (!reader.File.TryFindSectionData(rawEntry.ImportAddressTableRVA, out var sectionData)) + if (!reader.File.TryFindSection(rawEntry.ImportAddressTableRVA, out var section)) { 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; + var importLookupAddressTablePositionInFile = section.Position + rawEntry.ImportLookupTableRVA - section.VirtualAddress; // Find the section data for the ImportLookupTableRVA - if (!reader.File.TryFindSectionData(rawEntry.ImportLookupTableRVA, out sectionData)) + if (!reader.File.TryFindSection(rawEntry.ImportLookupTableRVA, out section)) { 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; + var importLookupTablePositionInFile = section.Position + rawEntry.ImportLookupTableRVA - section.VirtualAddress; // Store a fake entry for post-processing section data to allow to recreate PEImportLookupTable from existing PESectionStreamData _entries.Add( @@ -92,6 +92,8 @@ public override void Read(PEImageReader reader) ); } + UpdateSize(); + // Resolve ImportLookupTable and ImportAddressTable section data links var entries = CollectionsMarshal.AsSpan(_entries.UnsafeList); foreach (ref var entry in entries) @@ -101,6 +103,11 @@ public override void Read(PEImageReader reader) } } + private unsafe void UpdateSize() + { + Size = (ulong)((_entries.Count + 1) * sizeof(RawImportDirectoryEntry)); + } + public override void Write(PEImageWriter writer) { throw new NotImplementedException(); diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs index 87e738a..3a96b3c 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs @@ -9,8 +9,6 @@ namespace LibObjectFile.PE; public sealed class PEImportDirectoryEntry : PEObject { - private PEImportLookupTable? _importLookupTable; - public PEImportDirectoryEntry(ZeroTerminatedAsciiStringLink importDllNameLink, PEImportAddressTable importAddressTable, PEImportLookupTable importLookupTable) { ImportDllNameLink = importDllNameLink; @@ -28,31 +26,7 @@ public PEImportDirectoryEntry(ZeroTerminatedAsciiStringLink importDllNameLink, P 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 PEImportLookupTable ImportLookupTable { get; set; } public override unsafe void UpdateLayout(PEVisitorContext context) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index 10b4e65..cfd71dd 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -34,8 +34,6 @@ public void Read(PEImageReader reader, ulong position) { Read64(reader); } - - CalculateSize(peFile, reader.Diagnostics); } public void ResolveSectionDataLinks(PEFile peFile, DiagnosticBag diagnostics) @@ -59,10 +57,10 @@ public void ResolveSectionDataLinks(PEFile peFile, DiagnosticBag diagnostics) private unsafe void Read32(PEImageReader reader) { + RawImportFunctionEntry32 entry = default; + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref entry, 1)); while (true) { - RawImportFunctionEntry32 entry = default; - var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref entry, 1)); int read = reader.Read(span); if (read != sizeof(RawImportFunctionEntry32)) { @@ -85,10 +83,10 @@ private unsafe void Read32(PEImageReader reader) private unsafe void Read64(PEImageReader reader) { + RawImportFunctionEntry64 entry = default; + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref entry, 1)); while (true) { - RawImportFunctionEntry64 entry = default; - var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref entry, 1)); int read = reader.Read(span); if (read != sizeof(RawImportFunctionEntry64)) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs index 56c3384..b7a6b5b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -8,7 +8,7 @@ namespace LibObjectFile.PE; -public sealed class PEImportLookupTable : PEVirtualObject +public sealed class PEImportLookupTable : PESectionData { internal readonly PEImportFunctionTable FunctionTable; @@ -42,12 +42,4 @@ private void UpdateSize(PEFile file, DiagnosticBag diagnostics) } public override void Write(PEImageWriter writer) => FunctionTable.Write(writer); - - protected override void ValidateParent(ObjectFileElement parent) - { - if (parent is not PEImportDirectoryEntry) - { - 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 a53ce58..b82fceb 100644 --- a/src/LibObjectFile/PE/Internal/RawImportFunctionEntry32.cs +++ b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry32.cs @@ -16,9 +16,9 @@ public RawImportFunctionEntry32(uint hintNameTableRVA) public uint HintNameTableRVA => IsImportByOrdinal ? 0U : _hintNameTableRVA; - public bool IsNull => HintNameTableRVA == 0; + public bool IsNull => _hintNameTableRVA == 0; - public bool IsImportByOrdinal => (HintNameTableRVA & 0x8000_0000U) != 0; + public bool IsImportByOrdinal => (_hintNameTableRVA & 0x8000_0000U) != 0; - public ushort Ordinal => IsImportByOrdinal ? (ushort)HintNameTableRVA : (ushort)0; + public ushort Ordinal => IsImportByOrdinal ? (ushort)_hintNameTableRVA : (ushort)0; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs index 38161de..9e9c1e0 100644 --- a/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs +++ b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs @@ -16,9 +16,9 @@ public RawImportFunctionEntry64(ulong hintNameTableRVA) public uint HintNameTableRVA => IsImportByOrdinal ? 0U : (uint)_hintNameTableRVA; - public bool IsNull => HintNameTableRVA == 0; + public bool IsNull => _hintNameTableRVA == 0; - public ushort Ordinal => IsImportByOrdinal ? (ushort)HintNameTableRVA : (ushort)0; + public ushort Ordinal => IsImportByOrdinal ? (ushort)_hintNameTableRVA : (ushort)0; - public bool IsImportByOrdinal => (HintNameTableRVA & 0x8000_0000_0000_0000UL) != 0; + public bool IsImportByOrdinal => (_hintNameTableRVA & 0x8000_0000_0000_0000UL) != 0; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 583a08c..e5e01c0 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -4,13 +4,16 @@ using System; using System.Buffers; +using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; +using static System.Collections.Specialized.BitVector32; namespace LibObjectFile.PE; @@ -220,6 +223,8 @@ private void InitializeSections(PEImageReader imageReader, ReadOnlySpan(); + // Create directories and find the section for each directory var maxNumberOfDirectory = (int)Math.Min(OptionalHeader.NumberOfRvaAndSizes, 15); Span dataDirectories = OptionalHeader.DataDirectory; @@ -238,10 +243,53 @@ private void InitializeSections(PEImageReader imageReader, ReadOnlySpan Date: Thu, 19 Sep 2024 22:34:17 +0200 Subject: [PATCH 26/87] Fix parents in PE objects --- src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs | 2 -- .../PE/DataDirectory/PEImportDirectoryEntry.cs | 6 ------ src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs | 6 ------ src/LibObjectFile/PE/PESection.cs | 5 ----- 4 files changed, 19 deletions(-) diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs index 6b09f17..1399732 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs @@ -17,8 +17,6 @@ public PEImportAddressTable() : base(false) FunctionTable = new PEImportFunctionTable(); } - public new PEImportAddressTableDirectory? Parent => (PEImportAddressTableDirectory?)base.Parent; - public List Entries => FunctionTable.Entries; public override void UpdateLayout(PEVisitorContext context) diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs index 3a96b3c..52901e0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs @@ -16,12 +16,6 @@ public PEImportDirectoryEntry(ZeroTerminatedAsciiStringLink importDllNameLink, P ImportLookupTable = importLookupTable; } - public new PEImportDirectory? Parent - { - get => (PEImportDirectory?)base.Parent; - set => base.Parent = value; - } - public ZeroTerminatedAsciiStringLink ImportDllNameLink { get; set; } public PEImportAddressTable ImportAddressTable { get; set; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs index b7a6b5b..fac2fc3 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -17,12 +17,6 @@ public PEImportLookupTable() : base(false) FunctionTable = new PEImportFunctionTable(); } - public new PEImportDirectoryEntry? Parent - { - get => (PEImportDirectoryEntry?)base.Parent; - set => base.Parent = value; - } - public List Entries => FunctionTable.Entries; public override void UpdateLayout(PEVisitorContext context) diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index 446836b..4b373e1 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -30,11 +30,6 @@ public PESection(PESectionName name, RVA virtualAddress, RVA virtualSize) : base Characteristics = SectionCharacteristics.MemRead; } - /// - /// Gets the parent of this section. - /// - public new PEFile? Parent => (PEFile?)base.Parent; - /// /// Gets the name of this section. /// From c60b1e7587285561b73717c8857b5bcaac5a8069 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Fri, 20 Sep 2024 06:44:57 +0200 Subject: [PATCH 27/87] Improve loading of sections/directories --- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 3 +- .../PEBaseRelocationDirectory.cs | 4 +- .../{PEDirectory.cs => PEDataDirectory.cs} | 88 ++++----- .../PEDataDirectoryKind.cs} | 2 +- .../PE/DataDirectory/PEDirectoryTable.cs | 46 ++--- .../PEImportAddressTableDirectory.cs | 10 +- .../PE/DataDirectory/PEImportDirectory.cs | 4 +- .../PE/ImageDataDirectoryArray.cs | 2 +- src/LibObjectFile/PE/PEFile.Read.cs | 171 ++++++++++++------ src/LibObjectFile/PE/PEFile.cs | 5 + 10 files changed, 205 insertions(+), 130 deletions(-) rename src/LibObjectFile/PE/DataDirectory/{PEDirectory.cs => PEDataDirectory.cs} (62%) rename src/LibObjectFile/PE/{ImageDataDirectoryKind.cs => DataDirectory/PEDataDirectoryKind.cs} (97%) diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 66bf2aa..10452b5 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -139,5 +139,6 @@ public enum DiagnosticId PE_ERR_ImportLookupTableInvalidParent = 3043, PE_ERR_ImportDirectoryInvalidImportAddressTableRVA = 3044, PE_ERR_ImportDirectoryInvalidImportLookupTableRVA = 3045, - PE_ERR_ImportAddressTableNotFound = 3046 + PE_ERR_ImportAddressTableNotFound = 3046, + PE_ERR_InvalidInternalState = 3047, } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs index 8101456..baa63c0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs @@ -11,9 +11,9 @@ namespace LibObjectFile.PE; -public sealed class PEBaseRelocationDirectory : PEDirectory +public sealed class PEBaseRelocationDirectory : PEDataDirectory { - public PEBaseRelocationDirectory() : base(ImageDataDirectoryKind.BaseRelocation, false) + public PEBaseRelocationDirectory() : base(PEDataDirectoryKind.BaseRelocation, false) { } diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs similarity index 62% rename from src/LibObjectFile/PE/DataDirectory/PEDirectory.cs rename to src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index 7971b95..f325511 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -6,40 +6,40 @@ namespace LibObjectFile.PE; -public abstract class PEDirectory : PESectionData +public abstract class PEDataDirectory : PESectionData { - protected PEDirectory(ImageDataDirectoryKind kind, bool hasChildren) : base(hasChildren) + protected PEDataDirectory(PEDataDirectoryKind kind, bool hasChildren) : base(hasChildren) { Kind = kind; } - public ImageDataDirectoryKind Kind { get; } + public PEDataDirectoryKind Kind { get; } /// - /// Factory method to create a new instance of based on the kind. + /// Factory method to create a new instance of based on the kind. /// /// The kind of PE directory entry. /// A PE directory entry. - internal static PEDirectory Create(ImageDataDirectoryKind kind) + internal static PEDataDirectory Create(PEDataDirectoryKind kind) { return kind switch { - ImageDataDirectoryKind.Export => new PEExportDirectory(), - ImageDataDirectoryKind.Import => new PEImportDirectory(), - ImageDataDirectoryKind.Resource => new PEResourceDirectory(), - ImageDataDirectoryKind.Exception => new PEExceptionDirectory(), - ImageDataDirectoryKind.Security => new PESecurityDirectory(), - ImageDataDirectoryKind.BaseRelocation => new PEBaseRelocationDirectory(), - ImageDataDirectoryKind.Debug => new PEDebugDirectory(), - ImageDataDirectoryKind.Architecture => new PEArchitectureDirectory(), - ImageDataDirectoryKind.GlobalPointer => new PEGlobalPointerDirectory(), - ImageDataDirectoryKind.Tls => new PETlsDirectory(), - ImageDataDirectoryKind.LoadConfig => new PELoadConfigDirectory(), - ImageDataDirectoryKind.BoundImport => new PEBoundImportDirectory(), - ImageDataDirectoryKind.DelayImport => new PEDelayImportDirectory(), - ImageDataDirectoryKind.ImportAddressTable => new PEImportAddressTableDirectory(), - ImageDataDirectoryKind.ClrMetadata => new PEClrMetadata(), + PEDataDirectoryKind.Export => new PEExportDirectory(), + PEDataDirectoryKind.Import => new PEImportDirectory(), + PEDataDirectoryKind.Resource => new PEResourceDirectory(), + PEDataDirectoryKind.Exception => new PEExceptionDirectory(), + PEDataDirectoryKind.Security => new PESecurityDirectory(), + PEDataDirectoryKind.BaseRelocation => new PEBaseRelocationDirectory(), + PEDataDirectoryKind.Debug => new PEDebugDirectory(), + PEDataDirectoryKind.Architecture => new PEArchitectureDirectory(), + PEDataDirectoryKind.GlobalPointer => new PEGlobalPointerDirectory(), + PEDataDirectoryKind.Tls => new PETlsDirectory(), + PEDataDirectoryKind.LoadConfig => new PELoadConfigDirectory(), + PEDataDirectoryKind.BoundImport => new PEBoundImportDirectory(), + PEDataDirectoryKind.DelayImport => new PEDelayImportDirectory(), + PEDataDirectoryKind.ImportAddressTable => new PEImportAddressTableDirectory(), + PEDataDirectoryKind.ClrMetadata => new PEClrMetadata(), _ => throw new ArgumentOutOfRangeException(nameof(kind)) }; } @@ -53,9 +53,9 @@ protected override void ValidateParent(ObjectFileElement parent) } } -public sealed class PEExportDirectory : PEDirectory +public sealed class PEExportDirectory : PEDataDirectory { - public PEExportDirectory() : base(ImageDataDirectoryKind.Export, true) + public PEExportDirectory() : base(PEDataDirectoryKind.Export, true) { } @@ -75,9 +75,9 @@ public override void Write(PEImageWriter writer) } } -public sealed class PEResourceDirectory : PEDirectory +public sealed class PEResourceDirectory : PEDataDirectory { - public PEResourceDirectory() : base(ImageDataDirectoryKind.Resource, false) + public PEResourceDirectory() : base(PEDataDirectoryKind.Resource, false) { } @@ -97,9 +97,9 @@ public override void Write(PEImageWriter writer) } } -public sealed class PEExceptionDirectory : PEDirectory +public sealed class PEExceptionDirectory : PEDataDirectory { - public PEExceptionDirectory() : base(ImageDataDirectoryKind.Exception, false) + public PEExceptionDirectory() : base(PEDataDirectoryKind.Exception, false) { } @@ -119,9 +119,9 @@ public override void Write(PEImageWriter writer) } } -public sealed class PEDebugDirectory : PEDirectory +public sealed class PEDebugDirectory : PEDataDirectory { - public PEDebugDirectory() : base(ImageDataDirectoryKind.Debug, false) + public PEDebugDirectory() : base(PEDataDirectoryKind.Debug, false) { } @@ -141,9 +141,9 @@ public override void Write(PEImageWriter writer) } } -public sealed class PELoadConfigDirectory : PEDirectory +public sealed class PELoadConfigDirectory : PEDataDirectory { - public PELoadConfigDirectory() : base(ImageDataDirectoryKind.LoadConfig, false) + public PELoadConfigDirectory() : base(PEDataDirectoryKind.LoadConfig, false) { } @@ -163,9 +163,9 @@ public override void Write(PEImageWriter writer) } } -public sealed class PEBoundImportDirectory : PEDirectory +public sealed class PEBoundImportDirectory : PEDataDirectory { - public PEBoundImportDirectory() : base(ImageDataDirectoryKind.BoundImport, false) + public PEBoundImportDirectory() : base(PEDataDirectoryKind.BoundImport, false) { } @@ -185,9 +185,9 @@ public override void Write(PEImageWriter writer) } } -public sealed class PETlsDirectory : PEDirectory +public sealed class PETlsDirectory : PEDataDirectory { - public PETlsDirectory() : base(ImageDataDirectoryKind.Tls, false) + public PETlsDirectory() : base(PEDataDirectoryKind.Tls, false) { } @@ -207,9 +207,9 @@ public override void Write(PEImageWriter writer) } } -public sealed class PEDelayImportDirectory : PEDirectory +public sealed class PEDelayImportDirectory : PEDataDirectory { - public PEDelayImportDirectory() : base(ImageDataDirectoryKind.DelayImport, false) + public PEDelayImportDirectory() : base(PEDataDirectoryKind.DelayImport, false) { } @@ -229,9 +229,9 @@ public override void Write(PEImageWriter writer) } } -public sealed class PEClrMetadata : PEDirectory +public sealed class PEClrMetadata : PEDataDirectory { - public PEClrMetadata() : base(ImageDataDirectoryKind.ClrMetadata, false) + public PEClrMetadata() : base(PEDataDirectoryKind.ClrMetadata, false) { } @@ -251,9 +251,9 @@ public override void Write(PEImageWriter writer) } } -public sealed class PEArchitectureDirectory : PEDirectory +public sealed class PEArchitectureDirectory : PEDataDirectory { - public PEArchitectureDirectory() : base(ImageDataDirectoryKind.Architecture, false) + public PEArchitectureDirectory() : base(PEDataDirectoryKind.Architecture, false) { } @@ -273,9 +273,9 @@ public override void Write(PEImageWriter writer) } } -public sealed class PEGlobalPointerDirectory : PEDirectory +public sealed class PEGlobalPointerDirectory : PEDataDirectory { - public PEGlobalPointerDirectory() : base(ImageDataDirectoryKind.GlobalPointer, false) + public PEGlobalPointerDirectory() : base(PEDataDirectoryKind.GlobalPointer, false) { } @@ -295,9 +295,9 @@ public override void Write(PEImageWriter writer) } } -public sealed class PESecurityDirectory : PEDirectory +public sealed class PESecurityDirectory : PEDataDirectory { - public PESecurityDirectory() : base(ImageDataDirectoryKind.Security, false) + public PESecurityDirectory() : base(PEDataDirectoryKind.Security, false) { } diff --git a/src/LibObjectFile/PE/ImageDataDirectoryKind.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectoryKind.cs similarity index 97% rename from src/LibObjectFile/PE/ImageDataDirectoryKind.cs rename to src/LibObjectFile/PE/DataDirectory/PEDataDirectoryKind.cs index 985f113..931f913 100644 --- a/src/LibObjectFile/PE/ImageDataDirectoryKind.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectoryKind.cs @@ -7,7 +7,7 @@ namespace LibObjectFile.PE; /// /// Defines directory entry indices. /// -public enum ImageDataDirectoryKind : ushort +public enum PEDataDirectoryKind : ushort { /// /// Export Directory diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs index e7edd4a..6a427ca 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -15,7 +15,7 @@ namespace LibObjectFile.PE; /// Contains the array of directory entries in a Portable Executable (PE) file. /// [DebuggerDisplay($"{nameof(PEDirectoryTable)} {nameof(Count)} = {{{nameof(Count)}}}")] -public sealed class PEDirectoryTable : IEnumerable +public sealed class PEDirectoryTable : IEnumerable { private InternalArray _entries; private int _count; @@ -24,7 +24,7 @@ internal PEDirectoryTable() { } - public PEDirectory? this[ImageDataDirectoryKind kind] => _entries[(int)kind]; + public PEDataDirectory? this[PEDataDirectoryKind kind] => _entries[(int)kind]; /// /// Gets the number of directory entries in the array. @@ -34,77 +34,77 @@ internal PEDirectoryTable() /// /// Gets the export directory information from the PE file. /// - public PEExportDirectory? Export => (PEExportDirectory?)this[ImageDataDirectoryKind.Export]; + public PEExportDirectory? Export => (PEExportDirectory?)this[PEDataDirectoryKind.Export]; /// /// Gets the import directory information from the PE file. /// - public PEImportDirectory? Import => (PEImportDirectory?)this[ImageDataDirectoryKind.Import]; + public PEImportDirectory? Import => (PEImportDirectory?)this[PEDataDirectoryKind.Import]; /// /// Gets the resource directory information from the PE file. /// - public PEResourceDirectory? Resource => (PEResourceDirectory?)this[ImageDataDirectoryKind.Resource]; + public PEResourceDirectory? Resource => (PEResourceDirectory?)this[PEDataDirectoryKind.Resource]; /// /// Gets the exception directory information from the PE file. /// - public PEExceptionDirectory? Exception => (PEExceptionDirectory?)this[ImageDataDirectoryKind.Exception]; + public PEExceptionDirectory? Exception => (PEExceptionDirectory?)this[PEDataDirectoryKind.Exception]; /// /// Gets the security directory information from the PE file. /// - public PESecurityDirectory? Security => (PESecurityDirectory?)this[ImageDataDirectoryKind.Security]; + public PESecurityDirectory? Security => (PESecurityDirectory?)this[PEDataDirectoryKind.Security]; /// /// Gets the base relocation directory information from the PE file. /// - public PEBaseRelocationDirectory? BaseRelocation => (PEBaseRelocationDirectory?)this[ImageDataDirectoryKind.BaseRelocation]; + public PEBaseRelocationDirectory? BaseRelocation => (PEBaseRelocationDirectory?)this[PEDataDirectoryKind.BaseRelocation]; /// /// Gets the debug directory information from the PE file. /// - public PEDebugDirectory? Debug => (PEDebugDirectory?)this[ImageDataDirectoryKind.Debug]; + public PEDebugDirectory? Debug => (PEDebugDirectory?)this[PEDataDirectoryKind.Debug]; /// /// Gets the architecture-specific directory information from the PE file. /// - public PEArchitectureDirectory? Architecture => (PEArchitectureDirectory?)this[ImageDataDirectoryKind.Architecture]; + public PEArchitectureDirectory? Architecture => (PEArchitectureDirectory?)this[PEDataDirectoryKind.Architecture]; /// /// Gets the global pointer directory information from the PE file. /// - public PEGlobalPointerDirectory? GlobalPointer => (PEGlobalPointerDirectory?)this[ImageDataDirectoryKind.GlobalPointer]; + public PEGlobalPointerDirectory? GlobalPointer => (PEGlobalPointerDirectory?)this[PEDataDirectoryKind.GlobalPointer]; /// /// Gets the TLS (Thread Local Storage) directory information from the PE file. /// - public PETlsDirectory? Tls => (PETlsDirectory?)this[ImageDataDirectoryKind.Tls]; + public PETlsDirectory? Tls => (PETlsDirectory?)this[PEDataDirectoryKind.Tls]; /// /// Gets the load configuration directory information from the PE file. /// - public PELoadConfigDirectory? LoadConfig => (PELoadConfigDirectory?)this[ImageDataDirectoryKind.LoadConfig]; + public PELoadConfigDirectory? LoadConfig => (PELoadConfigDirectory?)this[PEDataDirectoryKind.LoadConfig]; /// /// Gets the bound import directory information from the PE file. /// - public PEBoundImportDirectory? BoundImport => (PEBoundImportDirectory?)this[ImageDataDirectoryKind.BoundImport]; + public PEBoundImportDirectory? BoundImport => (PEBoundImportDirectory?)this[PEDataDirectoryKind.BoundImport]; /// /// Gets the delay import directory information from the PE file. /// - public PEDelayImportDirectory? DelayImport => (PEDelayImportDirectory?)this[ImageDataDirectoryKind.DelayImport]; + public PEDelayImportDirectory? DelayImport => (PEDelayImportDirectory?)this[PEDataDirectoryKind.DelayImport]; /// /// Gets the import address table directory information from the PE file. /// - public PEImportAddressTableDirectory? ImportAddressTable => (PEImportAddressTableDirectory?)this[ImageDataDirectoryKind.ImportAddressTable]; + public PEImportAddressTableDirectory? ImportAddressTableDirectory => (PEImportAddressTableDirectory?)this[PEDataDirectoryKind.ImportAddressTable]; /// /// Gets the CLR metadata directory information from the PE file, if present. /// - public PEClrMetadata? ClrMetadata => (PEClrMetadata?)this[ImageDataDirectoryKind.ClrMetadata]; + public PEClrMetadata? ClrMetadata => (PEClrMetadata?)this[PEDataDirectoryKind.ClrMetadata]; /// /// Gets the enumerator for the directory entries. @@ -112,7 +112,7 @@ internal PEDirectoryTable() [EditorBrowsable(EditorBrowsableState.Never)] public Enumerator GetEnumerator() => new(this); - internal void Set(ImageDataDirectoryKind kind, PEDirectory? directory) + internal void Set(PEDataDirectoryKind kind, PEDataDirectory? directory) { ref var entry = ref _entries[(int)kind]; var previousEntry = entry; @@ -132,13 +132,13 @@ internal void Set(ImageDataDirectoryKind kind, PEDirectory? directory) [InlineArray(15)] private struct InternalArray { - private PEDirectory? _element; + private PEDataDirectory? _element; } /// /// Enumerator for the directory entries. /// - public struct Enumerator : IEnumerator + public struct Enumerator : IEnumerator { private readonly PEDirectoryTable _table; private int _index; @@ -149,7 +149,7 @@ internal Enumerator(PEDirectoryTable table) _index = -1; } - public PEDirectory Current => _index >= 0 ? _table._entries[_index]! : null!; + public PEDataDirectory Current => _index >= 0 ? _table._entries[_index]! : null!; object? IEnumerator.Current => Current; @@ -160,7 +160,7 @@ public void Dispose() public bool MoveNext() { - Span entries = _table._entries; + Span entries = _table._entries; while (++_index < entries.Length) { if (_table._entries[_index] is not null) @@ -178,7 +178,7 @@ public void Reset() } } - IEnumerator IEnumerable.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs index 86727af..5c1f0bf 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs @@ -8,16 +8,16 @@ namespace LibObjectFile.PE; -public sealed class PEImportAddressTableDirectory : PEDirectory +public sealed class PEImportAddressTableDirectory : PEDataDirectory { - private readonly ObjectList _content; + private readonly ObjectList _content; - public PEImportAddressTableDirectory() : base(ImageDataDirectoryKind.ImportAddressTable, true) + public PEImportAddressTableDirectory() : base(PEDataDirectoryKind.ImportAddressTable, true) { - _content = CreateObjectList(this); + _content = CreateObjectList(this); } - public ObjectList Content => _content; + public ObjectList Content => _content; public override void UpdateLayout(PEVisitorContext context) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 95b0fbb..7e81443 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -10,11 +10,11 @@ namespace LibObjectFile.PE; -public sealed class PEImportDirectory : PEDirectory +public sealed class PEImportDirectory : PEDataDirectory { private readonly ObjectList _entries; - public PEImportDirectory() : base(ImageDataDirectoryKind.Import, false) + public PEImportDirectory() : base(PEDataDirectoryKind.Import, false) { _entries = new(this); } diff --git a/src/LibObjectFile/PE/ImageDataDirectoryArray.cs b/src/LibObjectFile/PE/ImageDataDirectoryArray.cs index 7417410..add7bd3 100644 --- a/src/LibObjectFile/PE/ImageDataDirectoryArray.cs +++ b/src/LibObjectFile/PE/ImageDataDirectoryArray.cs @@ -23,5 +23,5 @@ public struct ImageDataDirectoryArray /// The index of the to get or set. /// The at the specified index. [UnscopedRef] - public ref ImageDataDirectory this[ImageDataDirectoryKind kind] => ref this[(int)kind]; + public ref ImageDataDirectory this[PEDataDirectoryKind kind] => ref this[(int)kind]; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index e5e01c0..2d4785d 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -11,9 +11,9 @@ using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using LibObjectFile.Collections; using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; -using static System.Collections.Specialized.BitVector32; namespace LibObjectFile.PE; @@ -198,7 +198,19 @@ public override void Read(PEImageReader reader) diagnostics.Error(DiagnosticId.PE_ERR_InvalidSectionHeadersSize, "Invalid section headers"); } - InitializeSections(reader, sectionHeaders); + // Read all sections and directories + if (TryInitializeSections(reader, sectionHeaders, out var positionAfterLastSection)) + { + // Read the remaining of the file + reader.Position = positionAfterLastSection; + var sizeToRead = reader.Length - reader.Position; + + // If we have any data left after the sections, we read it + if (sizeToRead > 0) + { + DataAfterSections = reader.ReadAsStream(sizeToRead); + } + } } finally { @@ -206,10 +218,12 @@ public override void Read(PEImageReader reader) } } - private void InitializeSections(PEImageReader imageReader, ReadOnlySpan headers) + private bool TryInitializeSections(PEImageReader imageReader, ReadOnlySpan headers, out ulong positionAfterLastSection) { _sections.Clear(); + positionAfterLastSection = 0; + // Create sections foreach (var section in headers) { @@ -220,10 +234,18 @@ private void InitializeSections(PEImageReader imageReader, ReadOnlySpan positionAfterLastSection) + { + positionAfterLastSection = positionAfterSection; + } + _sections.Add(peSection); } - var sectionDatas = new List(); + // A list that contains all the section data that we have created (e.g. from directories, import tables...) + var sectionDataList = new List(); // Create directories and find the section for each directory var maxNumberOfDirectory = (int)Math.Min(OptionalHeader.NumberOfRvaAndSizes, 15); @@ -243,13 +265,13 @@ private void InitializeSections(PEImageReader imageReader, ReadOnlySpan + /// For a list of section data (e.g. used by a section or a ImportAddressTableDirectory...), we need to fill any hole with the actual stream of original data from the image + /// + private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, PEObject container, ObjectList dataParts, ulong startPosition, ulong totalSize) + { + var currentPosition = startPosition; - if (sectionPosition < section.Position + section.Size) + for (var i = 0; i < dataParts.Count; i++) + { + var data = dataParts[i]; + if (currentPosition < data.Position) { var sectionData = new PEStreamSectionData() { - Position = sectionPosition, + Position = currentPosition, }; - var size = section.Position + section.Size - sectionPosition; - imageReader.Position = sectionPosition; + var size = data.Position - currentPosition; + imageReader.Position = currentPosition; sectionData.Stream = imageReader.ReadAsStream(size); - dataParts.Add(sectionData); + dataParts.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; } - //for (var i = 0; i < section.DataParts.Count; i++) - //{ - // var sectionData = section.DataParts[i]; - // Console.WriteLine($"section: {section.Name} {sectionData}"); - //} + currentPosition += data.Size; } - // Post fix the ImportLookupTable and ImportAddressTable - // To attach proper links to the actual streams - if (importDirectory is not null) + if (currentPosition < startPosition + totalSize) { - foreach (var entry in importDirectory.Entries) + var sectionData = new PEStreamSectionData() { - entry.ImportAddressTable.FunctionTable.ResolveSectionDataLinks(this, imageReader.Diagnostics); - entry.ImportLookupTable.FunctionTable.ResolveSectionDataLinks(this, imageReader.Diagnostics); - } - } + Position = currentPosition, + }; + var size = startPosition + totalSize - currentPosition; + imageReader.Position = currentPosition; + sectionData.Stream = imageReader.ReadAsStream(size); - // Read directories - // TODO: Read all directories - Directories.BaseRelocation?.Read(imageReader); + dataParts.Add(sectionData); + } + else if (currentPosition > startPosition + totalSize) + { + imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data position {currentPosition} > {startPosition + totalSize} in {container}"); + } } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 1981eaa..cfc4a5f 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -98,6 +98,11 @@ public Stream? DosStubExtra /// public ObjectList Sections => _sections; + /// + /// Gets or sets the data present after the sections in the file. + /// + public Stream? DataAfterSections { get; set; } + public PESection AddSection(PESectionName name, uint virtualAddress, uint virtualSize) { var section = new PESection(name, virtualAddress, virtualSize) From daea0d45b6d9a47eb9d4ac93df8230c714611738 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Fri, 20 Sep 2024 09:54:37 +0200 Subject: [PATCH 28/87] Fixes IAT --- .../PE/DataDirectory/PEImportDirectory.cs | 2 +- src/LibObjectFile/PE/ImageDataDirectory.cs | 2 +- src/LibObjectFile/PE/PEFile.Read.cs | 12 ++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 7e81443..8c8f451 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -61,7 +61,7 @@ public override void Read(PEImageReader reader) } // Calculate its position within the original stream - var importLookupAddressTablePositionInFile = section.Position + rawEntry.ImportLookupTableRVA - section.VirtualAddress; + var importLookupAddressTablePositionInFile = section.Position + rawEntry.ImportAddressTableRVA - section.VirtualAddress; // Find the section data for the ImportLookupTableRVA if (!reader.File.TryFindSection(rawEntry.ImportLookupTableRVA, out section)) diff --git a/src/LibObjectFile/PE/ImageDataDirectory.cs b/src/LibObjectFile/PE/ImageDataDirectory.cs index bb5ed25..8f8f3f8 100644 --- a/src/LibObjectFile/PE/ImageDataDirectory.cs +++ b/src/LibObjectFile/PE/ImageDataDirectory.cs @@ -16,7 +16,7 @@ public struct ImageDataDirectory /// /// The relative virtual address of the data directory. /// - public uint VirtualAddress; + public RVA VirtualAddress; /// /// The size of the data directory, in bytes. diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 2d4785d..7b74b40 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -394,9 +394,14 @@ private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, { var currentPosition = startPosition; - for (var i = 0; i < dataParts.Count; i++) + // We are working on position, while the list is ordered by VirtualAddress + var listOrderedByPosition = new List(); + listOrderedByPosition.AddRange(dataParts.UnsafeList); + listOrderedByPosition.Sort((a, b) => a.Position.CompareTo(b.Position)); + + for (var i = 0; i < listOrderedByPosition.Count; i++) { - var data = dataParts[i]; + var data = listOrderedByPosition[i]; if (currentPosition < data.Position) { var sectionData = new PEStreamSectionData() @@ -407,9 +412,8 @@ private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, imageReader.Position = currentPosition; sectionData.Stream = imageReader.ReadAsStream(size); - dataParts.Insert(i, sectionData); + dataParts.Insert(data.Index, sectionData); currentPosition = data.Position; - i++; } else if (currentPosition > data.Position) { From d595071bebb6155b45930cfcfdbea796635e71b0 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Fri, 20 Sep 2024 20:53:09 +0200 Subject: [PATCH 29/87] Add support for HintName and fix Import directory --- .../PE/DataDirectory/PEImportDirectory.cs | 28 +++- .../DataDirectory/PEImportDirectoryEntry.cs | 4 +- .../PE/DataDirectory/PEImportFunctionEntry.cs | 9 +- .../PE/DataDirectory/PEImportFunctionTable.cs | 19 ++- src/LibObjectFile/PE/PEAsciiStringLink.cs | 23 +++ src/LibObjectFile/PE/PEFile.Read.cs | 17 ++- src/LibObjectFile/PE/PEImportHintName.cs | 12 ++ src/LibObjectFile/PE/PEImportHintNameLink.cs | 23 +++ .../PE/PESectionDataExtensions.cs | 142 +++++++++++++++++- src/LibObjectFile/PE/PEStreamSectionData.cs | 16 +- src/LibObjectFile/PE/PETempSectionData.cs | 10 -- .../PE/ZeroTerminatedAsciiStringLink.cs | 12 -- 12 files changed, 256 insertions(+), 59 deletions(-) create mode 100644 src/LibObjectFile/PE/PEAsciiStringLink.cs create mode 100644 src/LibObjectFile/PE/PEImportHintName.cs create mode 100644 src/LibObjectFile/PE/PEImportHintNameLink.cs delete mode 100644 src/LibObjectFile/PE/PETempSectionData.cs delete mode 100644 src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 8c8f451..be3567d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -25,6 +25,32 @@ public override void UpdateLayout(PEVisitorContext context) UpdateSize(); } + internal void ResolveNames(PEFile peFile, DiagnosticBag diagnostics) + { + var entries = CollectionsMarshal.AsSpan(_entries.UnsafeList); + foreach (ref var entry in entries) + { + var va = entry.ImportDllNameLink.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; + } + + var streamSectionData = sectionData as PEStreamSectionData; + if (streamSectionData is null) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"The section data for HintNameTableRVA {va} is not a stream section data"); + return; + } + + entry = new PEImportDirectoryEntry( + new PEAsciiStringLink(new(streamSectionData, va - sectionData.VirtualAddress)), + entry.ImportAddressTable, + entry.ImportLookupTable); + } + } + public override void Read(PEImageReader reader) { var diagnostics = reader.Diagnostics; @@ -77,7 +103,7 @@ public override void Read(PEImageReader reader) _entries.Add( new PEImportDirectoryEntry( // Name - new(new(PETempSectionData.Instance, rawEntry.NameRVA)), + new(new(PEStreamSectionData.Empty, rawEntry.NameRVA)), // ImportAddressTable new PEImportAddressTable() { diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs index 52901e0..44e8f07 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs @@ -9,14 +9,14 @@ namespace LibObjectFile.PE; public sealed class PEImportDirectoryEntry : PEObject { - public PEImportDirectoryEntry(ZeroTerminatedAsciiStringLink importDllNameLink, PEImportAddressTable importAddressTable, PEImportLookupTable importLookupTable) + public PEImportDirectoryEntry(PEAsciiStringLink importDllNameLink, PEImportAddressTable importAddressTable, PEImportLookupTable importLookupTable) { ImportDllNameLink = importDllNameLink; ImportAddressTable = importAddressTable; ImportLookupTable = importLookupTable; } - public ZeroTerminatedAsciiStringLink ImportDllNameLink { get; set; } + public PEAsciiStringLink ImportDllNameLink { get; set; } public PEImportAddressTable ImportAddressTable { get; set; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs index 6909088..2508075 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs @@ -11,14 +11,14 @@ 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 PEStreamSectionData? _peSectionData; private readonly uint _offset; /// /// Initializes a new instance of the class by name. /// /// The name of the import. - public PEImportFunctionEntry(ZeroTerminatedAsciiStringLink name) + public PEImportFunctionEntry(PEAsciiStringLink name) { _peSectionData = name.Link.Element; _offset = name.Link.OffsetInElement; @@ -42,11 +42,10 @@ public PEImportFunctionEntry(ushort ordinal) /// /// Gets the name of the import if not by ordinal. /// - public ZeroTerminatedAsciiStringLink Name => _peSectionData is null ? default : new ZeroTerminatedAsciiStringLink(new(_peSectionData, _offset)); + public PEImportHintNameLink HintName => _peSectionData is null ? default : new PEImportHintNameLink(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 index cfd71dd..b626fb0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -43,14 +43,21 @@ public void ResolveSectionDataLinks(PEFile peFile, DiagnosticBag diagnostics) { if (!entry.IsImportByOrdinal) { - var va = entry.Name.Link.OffsetInElement; + var va = entry.HintName.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))); + var streamSectionData = sectionData as PEStreamSectionData; + if (streamSectionData is null) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"The section data for HintNameTableRVA {va} is not a stream section data"); + return; + } + + entry = new PEImportFunctionEntry(new PEAsciiStringLink(new(streamSectionData, va - sectionData.VirtualAddress))); } } } @@ -76,7 +83,7 @@ private unsafe void Read32(PEImageReader reader) Entries.Add( entry.IsImportByOrdinal ? new PEImportFunctionEntry(entry.Ordinal) - : new PEImportFunctionEntry(new ZeroTerminatedAsciiStringLink(new(PETempSectionData.Instance, entry.HintNameTableRVA))) + : new PEImportFunctionEntry(new PEAsciiStringLink(new(PEStreamSectionData.Empty, entry.HintNameTableRVA))) ); } } @@ -102,7 +109,7 @@ private unsafe void Read64(PEImageReader reader) Entries.Add( entry.IsImportByOrdinal ? new PEImportFunctionEntry(entry.Ordinal) - : new PEImportFunctionEntry(new ZeroTerminatedAsciiStringLink(new(PETempSectionData.Instance, entry.HintNameTableRVA))) + : new PEImportFunctionEntry(new PEAsciiStringLink(new(PEStreamSectionData.Empty, entry.HintNameTableRVA))) ); } } @@ -128,7 +135,7 @@ private unsafe void Write32(PEImageWriter writer) for (var i = 0; i < Entries.Count; i++) { var entry = Entries[i]; - var va = entry.Name.Link.VirtualAddress; + var va = entry.HintName.Link.VirtualAddress; span[i] = new RawImportFunctionEntry32(entry.IsImportByOrdinal ? 0x8000_0000U | entry.Ordinal : va); } @@ -152,7 +159,7 @@ private unsafe void Write64(PEImageWriter writer) for (var i = 0; i < Entries.Count; i++) { var entry = Entries[i]; - var va = entry.Name.Link.VirtualAddress; + var va = entry.HintName.Link.VirtualAddress; span[i] = new RawImportFunctionEntry64(entry.IsImportByOrdinal ? 0x8000_0000_0000_0000UL | entry.Ordinal : va); } // Last entry is null terminator diff --git a/src/LibObjectFile/PE/PEAsciiStringLink.cs b/src/LibObjectFile/PE/PEAsciiStringLink.cs new file mode 100644 index 0000000..4dda5bc --- /dev/null +++ b/src/LibObjectFile/PE/PEAsciiStringLink.cs @@ -0,0 +1,23 @@ +// 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 link to a null terminated ASCII string in a . +/// +/// The link. +public readonly record struct PEAsciiStringLink(RVALink Link) +{ + /// + /// Gets a value indicating whether this instance is null. + /// + public bool IsNull => Link.IsNull; + + /// + /// Resolves this link to a string. + /// + /// The string resolved. + public string? Resolve() => Link.Element?.ReadAsciiString(Link.OffsetInElement); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 7b74b40..8ddcf37 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -346,6 +346,8 @@ private bool TryInitializeSections(PEImageReader imageReader, ReadOnlySpan +/// A PE Import Hint Name used in . +/// +/// An index into the export name pointer table. A match is attempted first with this value. If it fails, a binary search is performed on the DLL's export name pointer table. +/// This is the string that must be matched to the public name in the DLL +public readonly record struct PEImportHintName(ushort Hint, string? Name); \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEImportHintNameLink.cs b/src/LibObjectFile/PE/PEImportHintNameLink.cs new file mode 100644 index 0000000..713fba5 --- /dev/null +++ b/src/LibObjectFile/PE/PEImportHintNameLink.cs @@ -0,0 +1,23 @@ +// 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 link to a PE Import Hint Name in a . +/// +/// The link. +public readonly record struct PEImportHintNameLink(RVALink Link) +{ + /// + /// Gets a value indicating whether this instance is null. + /// + public bool IsNull => Link.IsNull; + + /// + /// Resolves this link to a PE Import Hint Name. + /// + /// The PE Import Hint Name resolved. + public PEImportHintName Resolve() => Link.Element is null ? default : Link.Element.ReadHintName(Link.OffsetInElement); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESectionDataExtensions.cs b/src/LibObjectFile/PE/PESectionDataExtensions.cs index 7584039..156899f 100644 --- a/src/LibObjectFile/PE/PESectionDataExtensions.cs +++ b/src/LibObjectFile/PE/PESectionDataExtensions.cs @@ -4,31 +4,159 @@ using System; using System.Buffers; +using System.IO; using System.Runtime.InteropServices; +using System.Security.Cryptography; using System.Text; namespace LibObjectFile.PE; public static class PESectionDataExtensions { - public static string ReadZeroTerminatedAsciiString(this PESectionData sectionData, uint offset) + public static string ReadAsciiString(this PEStreamSectionData sectionData, uint offset) { + var stream = sectionData.Stream; + if (offset >= stream.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset), $"The offset {offset} is out of range in the stream > {stream.Length}"); + } + + stream.Position = offset; + return ReadAsciiStringInternal(stream, false, out _); + } + + public static PEImportHintName ReadHintName(this PEStreamSectionData sectionData, uint offset) + { + var stream = sectionData.Stream; + if (offset >= stream.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset), $"The offset {offset} is out of range in the stream > {stream.Length}"); + } + stream.Position = offset; + var name = ReadAsciiStringInternal(stream, true, out var hint); + return new PEImportHintName(hint, name); + } + + public static PEAsciiStringLink WriteAsciiString(this PEStreamSectionData streamData, string value) + { + var position = WriteAsciiString(streamData.Stream, value); + if (position > uint.MaxValue) + { + throw new InvalidOperationException("The position is too large to be stored in a uint"); + } + + return new PEAsciiStringLink(new(streamData, (uint)position)); + } + + public static PEImportHintNameLink WriteHintName(this PEStreamSectionData streamData, PEImportHintName hintName) + { + var position = WriteHintName(streamData.Stream, hintName); + if (position > uint.MaxValue) + { + throw new InvalidOperationException("The position is too large to be stored in a uint"); + } + + return new PEImportHintNameLink(new(streamData, (uint)position)); + } + + public static long WriteAsciiString(this Stream stream, string value) + { + var position = stream.Position; + WriteAsciiStringInternal(stream, false, 0, value); + return position; + } + + public static long WriteHintName(this Stream stream, PEImportHintName hintName) + { + if (hintName.Name is null) throw new ArgumentNullException(nameof(hintName), "The name of the import cannot be null"); + var position = stream.Position; + WriteAsciiStringInternal(stream, true, hintName.Hint, hintName.Name); + return position; + } + + private static void WriteAsciiStringInternal(Stream stream, bool isHint, ushort hint, string value) + { + var maxLength = Encoding.ASCII.GetMaxByteCount(value.Length); + + // Round it to the next even number + if ((maxLength & 1) != 0) + { + maxLength++; + } + + if (maxLength > 256) + { + var array = ArrayPool.Shared.Rent(maxLength + (isHint ? 2 : 0)); + try + { + WriteString(stream, array, isHint, hint, value); + } + finally + { + ArrayPool.Shared.Return(array); + } + } + else + { + Span buffer = stackalloc byte[258]; + WriteString(stream, buffer, isHint, hint, value); + } + + static void WriteString(Stream stream, Span buffer, bool isHint, ushort hint, string value) + { + var text = buffer; + + if (isHint) + { + MemoryMarshal.Write(buffer, hint); + text = buffer.Slice(2); + } + + int actualLength = Encoding.ASCII.GetBytes(value, text); + text[actualLength] = 0; + if ((actualLength & 1) != 0) + { + actualLength++; + } + + if (isHint) + { + actualLength += 2; + } + + stream.Write(buffer.Slice(0, actualLength)); + } + } + + private static string ReadAsciiStringInternal(Stream stream, bool isHint, out ushort hint) + { + hint = 0; Span buffer = stackalloc byte[256]; byte[]? charBuffer = null; Span currentString = default; + + if (isHint) + { + int read = stream.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref hint, 1))); + if (read != 2) + { + throw new EndOfStreamException(); + } + } + try { while (true) { - int read = sectionData.ReadAt(offset, buffer); + int read = stream.Read(buffer); if (read == 0) { break; } var indexOfZero = buffer.IndexOf((byte)0); - var length = indexOfZero >= 0 ? indexOfZero : read; - + var length = indexOfZero >= 0 ? indexOfZero : read; + var sliceRead = buffer.Slice(0, length); if (charBuffer is null) { @@ -50,6 +178,11 @@ public static string ReadZeroTerminatedAsciiString(this PESectionData sectionDat Encoding.ASCII.GetChars(sliceRead, MemoryMarshal.Cast(currentString.Slice(previousLength))); } + if (charBuffer is null) + { + throw new EndOfStreamException(); + } + return Encoding.ASCII.GetString(currentString); } finally @@ -60,4 +193,5 @@ public static string ReadZeroTerminatedAsciiString(this PESectionData sectionDat } } } + } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEStreamSectionData.cs b/src/LibObjectFile/PE/PEStreamSectionData.cs index ec901b9..8cfbe92 100644 --- a/src/LibObjectFile/PE/PEStreamSectionData.cs +++ b/src/LibObjectFile/PE/PEStreamSectionData.cs @@ -12,12 +12,14 @@ namespace LibObjectFile.PE; /// public class PEStreamSectionData : PESectionData { - private Stream _stream; + private readonly Stream _stream; + internal static PEStreamSectionData Empty = new(); + /// /// Initializes a new instance of the class. /// - public PEStreamSectionData() : base(false) + private PEStreamSectionData() : base(false) { _stream = Stream.Null; Size = 0; @@ -37,15 +39,7 @@ public PEStreamSectionData(Stream stream) : base(false) /// /// Gets the stream containing the data of this section data. /// - public Stream Stream - { - get => _stream; - set - { - _stream = value ?? throw new ArgumentNullException(nameof(value)); - Size = (ulong)value.Length; - } - } + public Stream Stream => _stream; public override void UpdateLayout(PEVisitorContext layoutContext) { diff --git a/src/LibObjectFile/PE/PETempSectionData.cs b/src/LibObjectFile/PE/PETempSectionData.cs deleted file mode 100644 index bae1ac3..0000000 --- a/src/LibObjectFile/PE/PETempSectionData.cs +++ /dev/null @@ -1,10 +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; - -internal sealed class PETempSectionData() : PESectionData(false) -{ - public static readonly PETempSectionData Instance = new (); -} \ No newline at end of file diff --git a/src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs b/src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs deleted file mode 100644 index d7eb8a6..0000000 --- a/src/LibObjectFile/PE/ZeroTerminatedAsciiStringLink.cs +++ /dev/null @@ -1,12 +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; - -public record struct ZeroTerminatedAsciiStringLink(RVALink Link) -{ - public bool IsNull => Link.IsNull; - - public string? ToText() => Link.Element?.ReadZeroTerminatedAsciiString(Link.OffsetInElement); -} \ No newline at end of file From 61533b8c82533f21d50853fdbf8421e10d5b2607 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sat, 21 Sep 2024 15:38:01 +0200 Subject: [PATCH 30/87] Improve handling of directories and sub section data --- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 8 + src/LibObjectFile/ObjectFileElement.cs | 11 +- src/LibObjectFile/ObjectFileReaderWriter.cs | 10 +- .../PE/DataDirectory/PEBaseRelocation.cs | 2 +- .../PE/DataDirectory/PEBaseRelocationBlock.cs | 125 +++++++++++ .../PEBaseRelocationDirectory.cs | 152 +++++-------- .../PEBaseRelocationPageBlock.cs | 53 ----- .../PEBaseRelocationPageBlockPart.cs | 14 +- .../PE/DataDirectory/PEDataDirectory.cs | 202 +++++++++++------- .../PE/DataDirectory/PEExportAddressTable.cs | 88 ++++++++ .../PE/DataDirectory/PEExportDirectory.cs | 177 +++++++++++++++ .../PE/DataDirectory/PEExportFunctionEntry.cs | 22 ++ .../PE/DataDirectory/PEExportNameTable.cs | 75 +++++++ .../PE/DataDirectory/PEExportOrdinalTable.cs | 43 ++++ .../PE/DataDirectory/PEImportAddressTable.cs | 19 +- .../PEImportAddressTableDirectory.cs | 49 +---- .../PE/DataDirectory/PEImportDirectory.cs | 95 ++++---- .../PE/DataDirectory/PEImportFunctionEntry.cs | 6 +- .../PE/DataDirectory/PEImportFunctionTable.cs | 24 ++- .../PE/DataDirectory/PEImportLookupTable.cs | 11 +- src/LibObjectFile/PE/PEAsciiStringLink.cs | 23 +- src/LibObjectFile/PE/PEFile.Read.cs | 97 +++++---- src/LibObjectFile/PE/PEFile.cs | 13 +- src/LibObjectFile/PE/PEFunctionAddressLink.cs | 26 +++ src/LibObjectFile/PE/PEImageReader.cs | 6 + src/LibObjectFile/PE/PEImportHintNameLink.cs | 23 +- src/LibObjectFile/PE/PESection.cs | 24 +-- src/LibObjectFile/PE/PESectionData.cs | 2 +- .../PE/PESectionDataExtensions.cs | 4 +- src/LibObjectFile/PE/PESectionDataLink.cs | 28 +++ src/LibObjectFile/PE/PESectionLink.cs | 28 +++ src/LibObjectFile/PE/PEVirtualObject.cs | 28 +-- .../PE/PEVirtualObjectExtensions.cs | 8 +- src/LibObjectFile/PE/RVALink.cs | 36 ++-- 34 files changed, 1047 insertions(+), 485 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs delete mode 100644 src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs create mode 100644 src/LibObjectFile/PE/PEFunctionAddressLink.cs create mode 100644 src/LibObjectFile/PE/PESectionDataLink.cs create mode 100644 src/LibObjectFile/PE/PESectionLink.cs diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 10452b5..421f845 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -141,4 +141,12 @@ public enum DiagnosticId PE_ERR_ImportDirectoryInvalidImportLookupTableRVA = 3045, PE_ERR_ImportAddressTableNotFound = 3046, PE_ERR_InvalidInternalState = 3047, + + // PE Export + PE_ERR_ExportAddressTableInvalidRVA = 3060, + PE_ERR_ExportDirectoryInvalidAddressOfNames = 3061, + PE_ERR_ExportDirectoryInvalidAddressOfFunctions = 3062, + PE_ERR_ExportDirectoryInvalidAddressOfNameOrdinals = 3063, + PE_ERR_ExportDirectoryInvalidName = 3064, + PE_ERR_ExportNameTableInvalidRVA = 3065, } \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileElement.cs b/src/LibObjectFile/ObjectFileElement.cs index a822c42..76a117d 100644 --- a/src/LibObjectFile/ObjectFileElement.cs +++ b/src/LibObjectFile/ObjectFileElement.cs @@ -58,11 +58,16 @@ internal set /// /// Checks if the specified offset is contained by this instance. /// - /// The offset to check if it belongs to this instance. + /// The offset to check if it belongs to this instance. /// true if the offset is within the segment or section range. - public bool Contains(ulong offset) + public bool Contains(ulong position) { - return offset >= Position && offset < Position + Size; + return position >= Position && position < Position + Size; + } + + public bool Contains(ulong position, uint size) + { + return position >= Position && position + size <= Position + Size; } /// diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs index edca421..ea8b8af 100644 --- a/src/LibObjectFile/ObjectFileReaderWriter.cs +++ b/src/LibObjectFile/ObjectFileReaderWriter.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibObjectFile.Diagnostics; using LibObjectFile.IO; @@ -64,7 +65,7 @@ public ulong Length public int Read(byte[] buffer, int offset, int count) => Stream.Read(buffer, offset, count); public int Read(Span buffer) => Stream.Read(buffer); - + public void ReadExactly(Span buffer) => Stream.ReadExactly(buffer); /// @@ -191,11 +192,8 @@ public unsafe bool TryReadData(int sizeToRead, out T data) where T : unmanage data = default; } - fixed (void* pData = &data) - { - var span = new Span(pData, sizeToRead); - byteRead = Stream.Read(span); - } + Unsafe.SkipInit(out data); + byteRead = Stream.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data, 1))); } return byteRead == sizeToRead; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs index e7a0c9d..ceaa23b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs @@ -43,7 +43,7 @@ public PEBaseRelocation(PEBaseRelocationType type, ushort virtualOffset) public PEBaseRelocationType Type => (PEBaseRelocationType)(_value & TypeMask); /// - /// Gets the virtual offset of the base relocation relative to the offset of the associated . + /// Gets the virtual offset of the base relocation relative to the offset of the associated . /// public ushort OffsetInBlockPart => (ushort)(_value & VirtualOffsetMask); diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs new file mode 100644 index 0000000..f7bafd7 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs @@ -0,0 +1,125 @@ +// 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 System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +/// +/// A block of base relocations for a page. +/// +[DebuggerDisplay("{ToString(),nq}")] +public sealed class PEBaseRelocationBlock +{ + /// + /// Initializes a new instance of the class. + /// + /// The section link. + public PEBaseRelocationBlock(PESectionLink sectionLink) + { + SectionLink = sectionLink; + } + + /// + /// Gets or sets the linked and its virtual offset within it. + /// + public PESectionLink SectionLink { get; set; } + + /// + /// Gets the list of relocations for this block. + /// + public List Parts { get; } = new(); + + /// + /// Internal buffer used to read the block before transforming it into parts. + /// + internal Memory BlockBuffer { get; set; } + + /// + /// Gets the size of this block. + /// + internal uint CalculateSizeOf() + { + // If we have a block buffer (when reading an image), use it directly until it is transformed into parts + if (!BlockBuffer.IsEmpty) + { + return (uint)BlockBuffer.Length; + } + + uint size = 0; + foreach (var part in Parts) + { + size += part.SizeOf; + } + + return size; + } + + internal void ReadAndBind(PEImageReader reader) + { + var buffer = BlockBuffer; + + var relocSpan = MemoryMarshal.Cast(buffer.Span); + + // Remove padding zeros at the end of the block + if (relocSpan.Length > 0 && relocSpan[^1].IsZero) + { + relocSpan = relocSpan.Slice(0, relocSpan.Length - 1); + } + + PEBaseRelocationPageBlockPart? currentBlockPart = null; + var blockBaseAddress = SectionLink.RVA(); + + var peFile = reader.File; + + // Iterate on all relocations + foreach (var relocation in relocSpan) + { + if (relocation.IsZero) + { + continue; + } + + var va = blockBaseAddress + relocation.OffsetInBlockPart; + + // Find the section data containing the virtual address + if (!peFile.TryFindVirtualContainer(va, out var vObj)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section data containing the virtual address 0x{va:X4}"); + continue; + } + + var sectionData = vObj as PESectionData; + if (sectionData is null) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"The section data containing the virtual address 0x{va:X4} is not a section data"); + continue; + } + + var offsetInSectionData = va - sectionData.VirtualAddress; + + // Create a new block part if the section data is different, or it is the first relocation + if (currentBlockPart is null || currentBlockPart.SectionDataLink.SectionData != sectionData) + { + currentBlockPart = new PEBaseRelocationPageBlockPart(new(sectionData, offsetInSectionData)); + Parts.Add(currentBlockPart); + } + + var newRelocation = new PEBaseRelocation(relocation.Type, (ushort)(offsetInSectionData - currentBlockPart.SectionDataLink.Offset)); + currentBlockPart.Relocations.Add(newRelocation); + } + + // Clear the buffer, as we don't need it anymore + BlockBuffer = Memory.Empty; + } + + public override string ToString() + { + return $"{nameof(PEBaseRelocationBlock)}, Section = {SectionLink.Section?.Name}, RVA = {SectionLink.RVA()}, Size = {CalculateSizeOf()}, Parts[{Parts.Count}]"; + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs index baa63c0..467e29f 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs @@ -13,128 +13,78 @@ namespace LibObjectFile.PE; public sealed class PEBaseRelocationDirectory : PEDataDirectory { - public PEBaseRelocationDirectory() : base(PEDataDirectoryKind.BaseRelocation, false) + public PEBaseRelocationDirectory() : base(PEDataDirectoryKind.BaseRelocation) { } - public List Blocks { get; } = new(); + public List Blocks { get; } = new(); - public override void UpdateLayout(PEVisitorContext context) + protected override uint ComputeHeaderSize(PEVisitorContext context) { - var size = 0UL; + var size = 0U; foreach (var block in Blocks) { size += block.CalculateSizeOf(); } - Size = size; - } + return size; + } + public override unsafe void Read(PEImageReader reader) { reader.Position = Position; var size = (int)Size; - var buffer = ArrayPool.Shared.Rent((int)size); - try + var array = new byte[size]; // Ideally would be nice to have this coming from ArrayPool with a ref counting + var buffer = array.AsMemory(0, (int)size); + int read = reader.Read(buffer.Span); + if (read != size) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidEndOfStream, $"Unable to read the full content of the BaseRelocation directory. Expected {size} bytes, but read {read} bytes"); + return; + } + + var allSectionData = reader.File.GetAllSectionData(); + + int blockIndex = 0; + while (buffer.Length > 0) { - var span = buffer.AsSpan(0, (int)size); - int read = reader.Read(span); - if (read != size) + var location = MemoryMarshal.Read(buffer.Span); + buffer = buffer.Slice(sizeof(ImageBaseRelocation)); + + if (!reader.File.TryFindSection(location.PageVirtualAddress, out var section)) { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidEndOfStream, $"Unable to read the full content of the BaseRelocation directory. Expected {size} bytes, but read {read} bytes"); - return; + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section containing the virtual address {location.PageVirtualAddress} in block #{blockIndex}"); + continue; } - var allSectionData = reader.File.GetAllSectionData(); + var sizeOfRelocations = (int)location.SizeOfBlock - sizeof(ImageBaseRelocation); - int blockIndex = 0; - while (span.Length > 0) + + // Create a block + var block = new PEBaseRelocationBlock(new PESectionLink(section, (uint)(location.PageVirtualAddress - section.VirtualAddress))) { - var location = MemoryMarshal.Read(span); - span = span.Slice(sizeof(ImageBaseRelocation)); - - if (!reader.File.TryFindSection(location.PageVirtualAddress, out var section)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section containing the virtual address {location.PageVirtualAddress} in block #{blockIndex}"); - continue; - } - - var sizeOfRelocations = (int)location.SizeOfBlock - sizeof(ImageBaseRelocation); - - var relocSpan = MemoryMarshal.Cast(span.Slice(0, sizeOfRelocations)); - - // Remove padding zeros at the end of the block - if (relocSpan.Length > 0 && relocSpan[^1].IsZero) - { - relocSpan = relocSpan.Slice(0, relocSpan.Length - 1); - } - - // Create a block - var block = new PEBaseRelocationPageBlock(new(section, location.PageVirtualAddress - section.VirtualAddress)); - Blocks.Add(block); - - PEBaseRelocationPageBlockPart? currentBlockPart = null; - var currentSectionDataIndex = 0; - - // Iterate on all relocations - foreach (var relocation in relocSpan) - { - if (relocation.IsZero) - { - continue; - } - - var va = location.PageVirtualAddress + relocation.OffsetInBlockPart; - - // Find the section data containing the virtual address - if (!TryFindSectionData(allSectionData, currentSectionDataIndex, va, out var newSectionDataIndex)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section data containing the virtual address 0x{va:X4}"); - continue; - } - - var sectionData = allSectionData[newSectionDataIndex]; - var offsetInSectionData = va - sectionData.VirtualAddress; - currentSectionDataIndex = newSectionDataIndex; - - // Create a new block part if the section data is different, or it is the first relocation - if (currentBlockPart is null || currentBlockPart.SectionDataRVALink.Element != sectionData) - { - currentBlockPart = new PEBaseRelocationPageBlockPart(new(sectionData, offsetInSectionData)); - block.Parts.Add(currentBlockPart); - } - - var newRelocation = new PEBaseRelocation(relocation.Type, (ushort)(offsetInSectionData - currentBlockPart.SectionDataRVALink.OffsetInElement)); - - currentBlockPart.Relocations.Add(newRelocation); - } - - // Move to the next block - span = span.Slice(sizeOfRelocations); - blockIndex++; - } - } - finally - { - ArrayPool.Shared.Return(buffer); + BlockBuffer = buffer.Slice(0, sizeOfRelocations) + }; + Blocks.Add(block); + + // Move to the next block + buffer = buffer.Slice(sizeOfRelocations); + blockIndex++; } + + // Update the header size + HeaderSize = ComputeHeaderSize(reader); } - private bool TryFindSectionData(List allSectionData, int startIndex, uint virtualAddress, out int indexFound) + internal override void Bind(PEImageReader reader) { - var span = CollectionsMarshal.AsSpan(allSectionData); - for (int i = startIndex; i < span.Length; i++) + foreach (var block in Blocks) { - ref var sectionData = ref span[i]; - if (sectionData.ContainsVirtual(virtualAddress)) - { - indexFound = i; - return true; - } + block.ReadAndBind(reader); } - indexFound = -1; - return false; + HeaderSize = ComputeHeaderSize(reader); } public override void Write(PEImageWriter writer) @@ -142,13 +92,6 @@ public override void Write(PEImageWriter writer) throw new NotImplementedException(); } -#pragma warning disable CS0649 - private struct ImageBaseRelocation - { - public RVA PageVirtualAddress; - public uint SizeOfBlock; - } - protected override bool PrintMembers(StringBuilder builder) { if (base.PrintMembers(builder)) @@ -159,4 +102,11 @@ protected override bool PrintMembers(StringBuilder builder) builder.Append($"Blocks[{Blocks.Count}]"); return true; } + + private struct ImageBaseRelocation + { +#pragma warning disable CS0649 + public RVA PageVirtualAddress; + public uint SizeOfBlock; + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs deleted file mode 100644 index f042403..0000000 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlock.cs +++ /dev/null @@ -1,53 +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. - -using System.Collections.Generic; -using System.Diagnostics; - -namespace LibObjectFile.PE; - -/// -/// A block of base relocations for a page. -/// -[DebuggerDisplay("{nameof(PEBaseRelocationPageBlock)}, Section = {SectionRVALink.Element.Name}, RVA = {SectionRVALink.VirtualAddress}, Size = {SizeOf}, Parts[{Parts.Count}]")] -public sealed class PEBaseRelocationPageBlock -{ - /// - /// Initializes a new instance of the class. - /// - /// The section link. - public PEBaseRelocationPageBlock(RVALink sectionRVALink) - { - SectionRVALink = sectionRVALink; - } - - /// - /// Gets or sets the linked and its virtual offset within it. - /// - public RVALink SectionRVALink { get; set; } - - /// - /// Gets the list of relocations for this block. - /// - public List Parts { get; } = new(); - - /// - /// Gets the size of this block. - /// - internal uint CalculateSizeOf() - { - uint size = 0; - foreach (var part in Parts) - { - size += part.SizeOf; - } - - return size; - } - - public override string ToString() - { - return $"{nameof(PEBaseRelocationPageBlock)}, Section = {SectionRVALink.Element?.Name}, RVA = {SectionRVALink.VirtualAddress}, Size = {CalculateSizeOf()}, Parts[{Parts.Count}]"; - } -} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs index 9eab9cd..675bd79 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs @@ -10,25 +10,25 @@ namespace LibObjectFile.PE; /// /// A block of base relocations for a page. /// -[DebuggerDisplay("{nameof(PEBaseRelocationPageBlock),nq} Link = {SectionDataRVALink}, Relocations[{Relocations.Count}]")] +[DebuggerDisplay("{nameof(PEBaseRelocationPageBlock),nq} Link = {SectionDataLink}, Relocations[{Relocations.Count}]")] public sealed class PEBaseRelocationPageBlockPart { - public PEBaseRelocationPageBlockPart(RVALink sectionDataRVALink) + public PEBaseRelocationPageBlockPart(PESectionDataLink sectionDataLink) { - SectionDataRVALink = sectionDataRVALink; + SectionDataLink = sectionDataLink; Relocations = new List(); } - internal PEBaseRelocationPageBlockPart(RVALink sectionDataRVALink, List relocations) + internal PEBaseRelocationPageBlockPart(PESectionDataLink sectionDataLink, List relocations) { - SectionDataRVALink = sectionDataRVALink; + SectionDataLink = sectionDataLink; Relocations = relocations; } /// /// Gets or sets the linked and its virtual offset within it. /// - public RVALink SectionDataRVALink { get; } + public PESectionDataLink SectionDataLink { get; } /// /// Gets the list of relocations for this block. @@ -51,6 +51,6 @@ internal uint SizeOf public override string ToString() { - return $"{nameof(PEBaseRelocationPageBlock)} Link = {SectionDataRVALink}, Relocations[{Relocations.Count}]"; + return $"{nameof(PEBaseRelocationBlock)} Link = {SectionDataLink}, Relocations[{Relocations.Count}]"; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index f325511..defc69d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -3,18 +3,86 @@ // 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 { - protected PEDataDirectory(PEDataDirectoryKind kind, bool hasChildren) : base(hasChildren) + protected PEDataDirectory(PEDataDirectoryKind kind) : base(true) { Kind = kind; + Content = CreateObjectList(this); } public PEDataDirectoryKind Kind { get; } + internal uint HeaderSize { get; private protected set; } + + /// + /// Gets the content of this directory. + /// + public ObjectList Content { get; } + + public sealed override void UpdateLayout(PEVisitorContext context) + { + var va = VirtualAddress; + + // 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 + foreach (var table in Content) + { + table.VirtualAddress = va; + + // Update layout will update virtual address + table.UpdateLayout(context); + + va += (uint)table.Size; + size += table.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(ObjectFileElement parent) + { + if (parent is not PESection) + { + throw new ArgumentException($"Invalid parent type [{parent?.GetType()}] for [{GetType()}]"); + } + } + + protected override bool TryFindByVirtualAddressInChildren(RVA virtualAddress, out PEVirtualObject? result) + => Content.TryFindByVirtualAddress(virtualAddress, true, out result); + + protected override void UpdateVirtualAddressInChildren() + { + var va = VirtualAddress; + foreach (var table in Content) + { + table.UpdateVirtualAddress(va); + va += (uint)table.Size; + } + } /// /// Factory method to create a new instance of based on the kind. @@ -43,52 +111,22 @@ internal static PEDataDirectory Create(PEDataDirectoryKind kind) _ => throw new ArgumentOutOfRangeException(nameof(kind)) }; } - - protected override void ValidateParent(ObjectFileElement parent) - { - if (parent is not PESection) - { - throw new ArgumentException($"Invalid parent type [{parent?.GetType()}] for [{GetType()}]"); - } - } -} - -public sealed class PEExportDirectory : PEDataDirectory -{ - public PEExportDirectory() : base(PEDataDirectoryKind.Export, true) - { - } - - public override void UpdateLayout(PEVisitorContext context) - { - throw new NotImplementedException(); - } - - public override void Read(PEImageReader reader) - { - throw new NotImplementedException(); - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } } public sealed class PEResourceDirectory : PEDataDirectory { - public PEResourceDirectory() : base(PEDataDirectoryKind.Resource, false) + public PEResourceDirectory() : base(PEDataDirectoryKind.Resource) { } - - public override void UpdateLayout(PEVisitorContext context) + protected override uint ComputeHeaderSize(PEVisitorContext context) { - throw new NotImplementedException(); + return 0; } + public override void Read(PEImageReader reader) { - throw new NotImplementedException(); + // TBD } public override void Write(PEImageWriter writer) @@ -99,18 +137,18 @@ public override void Write(PEImageWriter writer) public sealed class PEExceptionDirectory : PEDataDirectory { - public PEExceptionDirectory() : base(PEDataDirectoryKind.Exception, false) + public PEExceptionDirectory() : base(PEDataDirectoryKind.Exception) { } - - public override void UpdateLayout(PEVisitorContext context) + protected override uint ComputeHeaderSize(PEVisitorContext context) { - throw new NotImplementedException(); + return 0; } + public override void Read(PEImageReader reader) { - throw new NotImplementedException(); + // TBD } public override void Write(PEImageWriter writer) @@ -121,18 +159,18 @@ public override void Write(PEImageWriter writer) public sealed class PEDebugDirectory : PEDataDirectory { - public PEDebugDirectory() : base(PEDataDirectoryKind.Debug, false) + public PEDebugDirectory() : base(PEDataDirectoryKind.Debug) { } - - public override void UpdateLayout(PEVisitorContext context) + protected override uint ComputeHeaderSize(PEVisitorContext context) { - throw new NotImplementedException(); + return 0; } + public override void Read(PEImageReader reader) { - throw new NotImplementedException(); + // TBD } public override void Write(PEImageWriter writer) @@ -143,18 +181,18 @@ public override void Write(PEImageWriter writer) public sealed class PELoadConfigDirectory : PEDataDirectory { - public PELoadConfigDirectory() : base(PEDataDirectoryKind.LoadConfig, false) + public PELoadConfigDirectory() : base(PEDataDirectoryKind.LoadConfig) { } - - public override void UpdateLayout(PEVisitorContext context) + protected override uint ComputeHeaderSize(PEVisitorContext context) { - throw new NotImplementedException(); + return 0; } + public override void Read(PEImageReader reader) { - throw new NotImplementedException(); + // TBD } public override void Write(PEImageWriter writer) @@ -165,18 +203,17 @@ public override void Write(PEImageWriter writer) public sealed class PEBoundImportDirectory : PEDataDirectory { - public PEBoundImportDirectory() : base(PEDataDirectoryKind.BoundImport, false) + public PEBoundImportDirectory() : base(PEDataDirectoryKind.BoundImport) { } - - public override void UpdateLayout(PEVisitorContext context) + protected override uint ComputeHeaderSize(PEVisitorContext context) { - throw new NotImplementedException(); + return 0; } public override void Read(PEImageReader reader) { - throw new NotImplementedException(); + // TBD } public override void Write(PEImageWriter writer) @@ -187,18 +224,18 @@ public override void Write(PEImageWriter writer) public sealed class PETlsDirectory : PEDataDirectory { - public PETlsDirectory() : base(PEDataDirectoryKind.Tls, false) + public PETlsDirectory() : base(PEDataDirectoryKind.Tls) { } - public override void UpdateLayout(PEVisitorContext context) + protected override uint ComputeHeaderSize(PEVisitorContext context) { - throw new NotImplementedException(); + return 0; } - + public override void Read(PEImageReader reader) { - throw new NotImplementedException(); + // TBD } public override void Write(PEImageWriter writer) @@ -209,18 +246,17 @@ public override void Write(PEImageWriter writer) public sealed class PEDelayImportDirectory : PEDataDirectory { - public PEDelayImportDirectory() : base(PEDataDirectoryKind.DelayImport, false) + public PEDelayImportDirectory() : base(PEDataDirectoryKind.DelayImport) { } - - public override void UpdateLayout(PEVisitorContext context) + protected override uint ComputeHeaderSize(PEVisitorContext context) { - throw new NotImplementedException(); + return 0; } public override void Read(PEImageReader reader) { - throw new NotImplementedException(); + // TBD } public override void Write(PEImageWriter writer) @@ -231,18 +267,18 @@ public override void Write(PEImageWriter writer) public sealed class PEClrMetadata : PEDataDirectory { - public PEClrMetadata() : base(PEDataDirectoryKind.ClrMetadata, false) + public PEClrMetadata() : base(PEDataDirectoryKind.ClrMetadata) { } - public override void UpdateLayout(PEVisitorContext context) + protected override uint ComputeHeaderSize(PEVisitorContext context) { - throw new NotImplementedException(); + return 0; } public override void Read(PEImageReader reader) { - throw new NotImplementedException(); + // TBD } public override void Write(PEImageWriter writer) @@ -253,18 +289,18 @@ public override void Write(PEImageWriter writer) public sealed class PEArchitectureDirectory : PEDataDirectory { - public PEArchitectureDirectory() : base(PEDataDirectoryKind.Architecture, false) + public PEArchitectureDirectory() : base(PEDataDirectoryKind.Architecture) { } - public override void UpdateLayout(PEVisitorContext context) + protected override uint ComputeHeaderSize(PEVisitorContext context) { - throw new NotImplementedException(); + return 0; } public override void Read(PEImageReader reader) { - throw new NotImplementedException(); + // TBD } public override void Write(PEImageWriter writer) @@ -275,18 +311,18 @@ public override void Write(PEImageWriter writer) public sealed class PEGlobalPointerDirectory : PEDataDirectory { - public PEGlobalPointerDirectory() : base(PEDataDirectoryKind.GlobalPointer, false) + public PEGlobalPointerDirectory() : base(PEDataDirectoryKind.GlobalPointer) { } - - public override void UpdateLayout(PEVisitorContext context) + + protected override uint ComputeHeaderSize(PEVisitorContext context) { - throw new NotImplementedException(); + return 0; } public override void Read(PEImageReader reader) { - throw new NotImplementedException(); + // TBD } public override void Write(PEImageWriter writer) @@ -297,18 +333,18 @@ public override void Write(PEImageWriter writer) public sealed class PESecurityDirectory : PEDataDirectory { - public PESecurityDirectory() : base(PEDataDirectoryKind.Security, false) + public PESecurityDirectory() : base(PEDataDirectoryKind.Security) { } - public override void UpdateLayout(PEVisitorContext context) + protected override uint ComputeHeaderSize(PEVisitorContext context) { - throw new NotImplementedException(); + return 0; } public override void Read(PEImageReader reader) { - throw new NotImplementedException(); + // TBD } public override void Write(PEImageWriter writer) diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs new file mode 100644 index 0000000..c7a1f03 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -0,0 +1,88 @@ +// 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.Diagnostics; + +namespace LibObjectFile.PE; + +public class PEExportAddressTable : PESectionData +{ + public PEExportAddressTable() : base(false) + { + } + + public PEExportAddressTable(int count) : base(false) + { + CollectionsMarshal.SetCount(Values, count); + } + + public List Values { get; } = new(); + + public override unsafe void UpdateLayout(PEVisitorContext context) + { + Size = (ulong)(Values.Count * sizeof(RVA)); + } + + public override unsafe void Read(PEImageReader reader) + { + reader.Position = Position; + var buffer = ArrayPool.Shared.Rent(sizeof(RVA) * Values.Count); + var span = buffer.AsSpan(0, sizeof(RVA) * Values.Count); + var spanRva = MemoryMarshal.Cast(span); + try + { + int read = reader.Read(buffer.AsSpan(0, sizeof(RVA) * Values.Count)); + if (read != sizeof(RVA) * Values.Count) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unable to read Export Address Table"); + return; + } + + for (int i = 0; i < Values.Count; i++) + { + var rva = spanRva[i]; + if (!reader.File.TryFindVirtualContainer(rva, out var functionContainer)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); + return; + } + + PEAsciiStringLink forwarderLink = default; + + //if (rva.ForwarderRVA.Value != 0) + //{ + // if (!reader.File.TryFindSectionData(rva.ForwarderRVA, out var sectionData)) + // { + // reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Unable to find the section data for forwarder RVA {rva.ForwarderRVA}"); + // return; + // } + + // var streamSectionData = sectionData as PEStreamSectionData; + // if (streamSectionData is null) + // { + // reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"The section data for forwarder RVA {rva.ForwarderRVA} is not a stream section data"); + // return; + // } + + // forwarderLink = new PEAsciiStringLink(streamSectionData, rva.ForwarderRVA - streamSectionData.VirtualAddress); + //} + + Values[i] = new PEExportFunctionEntry(new(functionContainer, rva - functionContainer.VirtualAddress), forwarderLink); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs new file mode 100644 index 0000000..e07eb9c --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs @@ -0,0 +1,177 @@ +// 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; +using System.Reflection; +using LibObjectFile.Diagnostics; + +namespace LibObjectFile.PE; + +public sealed class PEExportDirectory : PEDataDirectory +{ + public PEExportDirectory() : base(PEDataDirectoryKind.Export) + { + OrdinalBase = 1; + } + + public DateTime TimeStamp { get; set; } + + public ushort MajorVersion { get; set; } + + public ushort MinorVersion { get; set; } + + public uint OrdinalBase { get; set; } + + public PEAsciiStringLink NameLink { get; set; } + + public PEExportAddressTable? ExportFunctionAddressTable { get; set; } + + public PEExportNameTable? ExportNameTable { get; set; } + + public PEExportOrdinalTable? ExportOrdinalTable { get; set; } + + protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) + { + return (uint)sizeof(RawImageExportDirectory); + } + + internal override IEnumerable CollectImplicitSectionDataList() + { + if (ExportFunctionAddressTable is not null) + { + yield return ExportFunctionAddressTable; + } + + if (ExportNameTable is not null) + { + yield return ExportNameTable; + } + + if (ExportOrdinalTable is not null) + { + yield return ExportOrdinalTable; + } + } + + public override unsafe void Read(PEImageReader reader) + { + reader.Position = Position; + if (!reader.TryReadData(sizeof(RawImageExportDirectory), out RawImageExportDirectory exportDirectory)) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unable to read Export Directory"); + return; + } + + TimeStamp = DateTime.UnixEpoch.AddSeconds(exportDirectory.TimeDateStamp); + MajorVersion = exportDirectory.MajorVersion; + MinorVersion = exportDirectory.MinorVersion; + OrdinalBase = (ushort)exportDirectory.Base; + + if (!reader.File.TryFindSection(exportDirectory.Name, out _)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidName, $"Unable to find the section for Name {exportDirectory.Name}"); + return; + } + + // Link to a fake section data until we have recorded the different export tables in the sections + NameLink = new PEAsciiStringLink(PEStreamSectionData.Empty, exportDirectory.Name); + + if (!reader.File.TryFindSection(exportDirectory.AddressOfFunctions, out var sectionAddressOfFunctions)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfFunctions, $"Unable to find the section for AddressOfFunctions {exportDirectory.AddressOfFunctions}"); + return; + } + + if (!reader.File.TryFindSection(exportDirectory.AddressOfNames, out var sectionAddressOfNames)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfNames, $"Unable to find the section for AddressOfNames {exportDirectory.AddressOfNames}"); + return; + } + + if (!reader.File.TryFindSection(exportDirectory.AddressOfNameOrdinals, out var sectionAddressOfNameOrdinals)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfNameOrdinals, $"Unable to find the section for AddressOfNameOrdinals {exportDirectory.AddressOfNameOrdinals}"); + return; + } + + ExportFunctionAddressTable = new PEExportAddressTable((int)exportDirectory.NumberOfFunctions) + { + Position = sectionAddressOfFunctions.Position + exportDirectory.AddressOfFunctions - sectionAddressOfFunctions.VirtualAddress, + Size = (ulong)(exportDirectory.NumberOfFunctions * sizeof(RVA)) + }; + + ExportNameTable = new PEExportNameTable((int)exportDirectory.NumberOfNames) + { + Position = sectionAddressOfNames.Position + exportDirectory.AddressOfNames - sectionAddressOfNames.VirtualAddress, + Size = (ulong)(exportDirectory.NumberOfNames * sizeof(RVA)) + }; + + ExportOrdinalTable = new PEExportOrdinalTable((int)exportDirectory.NumberOfFunctions) + { + Position = sectionAddressOfNameOrdinals.Position + exportDirectory.AddressOfNameOrdinals - sectionAddressOfNameOrdinals.VirtualAddress, + Size = (ulong)(exportDirectory.NumberOfFunctions * sizeof(ushort)) + }; + + // Update the header size + HeaderSize = ComputeHeaderSize(reader); + } + + internal override void Bind(PEImageReader reader) + { + var peFile = reader.File; + var diagnostics = reader.Diagnostics; + + if (!peFile.TryFindVirtualContainer(NameLink.RVA(), out var container)) + { + diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidName, $"Unable to find the section data for Name {NameLink.RVA()}"); + return; + } + + var streamSectionData = container as PEStreamSectionData; + if (streamSectionData is null) + { + diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidName, $"The section data for Name {NameLink.RVA()} is not a stream section data"); + return; + } + + NameLink = new PEAsciiStringLink(streamSectionData, NameLink.Offset); + + if (ExportFunctionAddressTable is not null) + { + ExportFunctionAddressTable.Read(reader); + } + + if (ExportNameTable is not null) + { + ExportNameTable.Read(reader); + } + + if (ExportOrdinalTable is not null) + { + ExportOrdinalTable.Read(reader); + } + } + + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } + + private struct RawImageExportDirectory + { +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value + public uint Characteristics; + public uint TimeDateStamp; + public ushort MajorVersion; + public ushort MinorVersion; + public RVA Name; + public uint Base; + public uint NumberOfFunctions; + public uint NumberOfNames; + public RVA AddressOfFunctions; // RVA from base of image + public RVA AddressOfNames; // RVA from base of image + public RVA AddressOfNameOrdinals; // RVA from base of image + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs new file mode 100644 index 0000000..6675bf5 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs @@ -0,0 +1,22 @@ +// 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 PEExportFunctionEntry +{ + public PEExportFunctionEntry(PEFunctionAddressLink exportRVA, PEAsciiStringLink forwarderRVA) + { + ExportRVA = exportRVA; + ForwarderRVA = forwarderRVA; + } + + + public PEFunctionAddressLink ExportRVA; + + public PEAsciiStringLink ForwarderRVA; + + public override string ToString() => ForwarderRVA.IsNull() ? $"{ExportRVA}" : $"{ExportRVA}, ForwarderRVA = {ForwarderRVA}"; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs new file mode 100644 index 0000000..52ad022 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs @@ -0,0 +1,75 @@ +// 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.Diagnostics; + +namespace LibObjectFile.PE; + +public class PEExportNameTable : PESectionData +{ + public PEExportNameTable() : base(false) + { + } + + public PEExportNameTable(int count) : base(false) + { + CollectionsMarshal.SetCount(Values, count); + } + + public List Values { get; } = new(); + + public override unsafe void UpdateLayout(PEVisitorContext context) + { + Size = (ulong)(Values.Count * sizeof(RVA)); + } + + public override unsafe void Read(PEImageReader reader) + { + reader.Position = Position; + var buffer = ArrayPool.Shared.Rent(sizeof(RVA) * Values.Count); + var span = buffer.AsSpan(0, sizeof(RVA) * Values.Count); + var spanRva = MemoryMarshal.Cast(span); + try + { + int read = reader.Read(buffer.AsSpan(0, sizeof(RVA) * Values.Count)); + if (read != sizeof(RVA) * Values.Count) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unable to read Export Name Table"); + return; + } + + for (int i = 0; i < Values.Count; i++) + { + var rva = spanRva[i]; + if (!reader.File.TryFindVirtualContainer(rva, out var sectionData)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportNameTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); + return; + } + + var streamSectionData = sectionData as PEStreamSectionData; + if (streamSectionData is null) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportNameTableInvalidRVA, $"The section data for RVA {rva} is not a stream section data"); + return; + } + + Values[i] = new PEAsciiStringLink(streamSectionData, rva - streamSectionData.VirtualAddress); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs new file mode 100644 index 0000000..4cc73b3 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs @@ -0,0 +1,43 @@ +// 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.Runtime.InteropServices; +using LibObjectFile.Diagnostics; + +namespace LibObjectFile.PE; + +public class PEExportOrdinalTable : PESectionData +{ + public PEExportOrdinalTable() : base(false) + { + } + + public PEExportOrdinalTable(int count) : base(false) + { + CollectionsMarshal.SetCount(Values, count); + } + + public List Values { get; } = new(); + + + public override void UpdateLayout(PEVisitorContext context) + { + Size = (ulong)Values.Count * sizeof(ushort); + } + + // Special method that can read from a known size + public override void Read(PEImageReader reader) + { + reader.Position = Position; + var span = CollectionsMarshal.AsSpan(Values); + + int read = reader.Read(MemoryMarshal.AsBytes(span)); + if (read != Values.Count * sizeof(ushort)) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unable to read Export Ordinal Table"); + return; + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs index 1399732..27f4990 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs @@ -2,9 +2,7 @@ // 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; -using LibObjectFile.Diagnostics; namespace LibObjectFile.PE; @@ -21,27 +19,14 @@ public PEImportAddressTable() : base(false) public override void UpdateLayout(PEVisitorContext context) { - UpdateSize(context.File, context.Diagnostics); + Size = FunctionTable.CalculateSize(context); } public override void Read(PEImageReader reader) { FunctionTable.Read(reader, Position); - UpdateSize(reader.File, reader.Diagnostics); - } - - private void UpdateSize(PEFile file, DiagnosticBag diagnostics) - { - Size = FunctionTable.CalculateSize(file, diagnostics); + UpdateLayout(reader); } public override void Write(PEImageWriter writer) => FunctionTable.Write(writer); - - protected override void ValidateParent(ObjectFileElement 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 index 5c1f0bf..a60c264 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs @@ -10,56 +10,15 @@ namespace LibObjectFile.PE; public sealed class PEImportAddressTableDirectory : PEDataDirectory { - private readonly ObjectList _content; - - public PEImportAddressTableDirectory() : base(PEDataDirectoryKind.ImportAddressTable, true) - { - _content = CreateObjectList(this); - } - - public ObjectList Content => _content; - - public override void UpdateLayout(PEVisitorContext context) + public PEImportAddressTableDirectory() : base(PEDataDirectoryKind.ImportAddressTable) { - ulong size = 0; - var va = VirtualAddress; - foreach (var table in _content) - { - table.VirtualAddress = va; - // Update layout will update virtual address - table.UpdateLayout(context); - va += (uint)table.Size; - size += table.Size; - } - Size = size; } - protected override bool TryFindByVirtualAddressInChildren(RVA virtualAddress, out PEVirtualObject? result) + public override void Read(PEImageReader reader) { - var content = CollectionsMarshal.AsSpan(_content.UnsafeList); - foreach (var table in content) - { - if (table.TryFindByVirtualAddress(virtualAddress, out result)) - { - return true; - } - } - - result = null; - return false; - } - - protected override void UpdateVirtualAddressInChildren() - { - var va = VirtualAddress; - foreach (var table in _content) - { - table.UpdateVirtualAddress(va); - va += (uint)table.Size; - } } - public override void Read(PEImageReader reader) => throw new NotSupportedException(); // Not called directly for this object, we are calling on tables directly - public override void Write(PEImageWriter writer) => throw new NotSupportedException(); // Not called directly for this object, we are calling on tables directly + + protected override uint ComputeHeaderSize(PEVisitorContext 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 be3567d..033051f 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using LibObjectFile.Collections; using LibObjectFile.Diagnostics; @@ -14,43 +15,13 @@ public sealed class PEImportDirectory : PEDataDirectory { private readonly ObjectList _entries; - public PEImportDirectory() : base(PEDataDirectoryKind.Import, false) + public PEImportDirectory() : base(PEDataDirectoryKind.Import) { _entries = new(this); } - public ObjectList Entries => _entries; - - public override void UpdateLayout(PEVisitorContext context) - { - UpdateSize(); - } - - internal void ResolveNames(PEFile peFile, DiagnosticBag diagnostics) - { - var entries = CollectionsMarshal.AsSpan(_entries.UnsafeList); - foreach (ref var entry in entries) - { - var va = entry.ImportDllNameLink.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; - } - - var streamSectionData = sectionData as PEStreamSectionData; - if (streamSectionData is null) - { - diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"The section data for HintNameTableRVA {va} is not a stream section data"); - return; - } - - entry = new PEImportDirectoryEntry( - new PEAsciiStringLink(new(streamSectionData, va - sectionData.VirtualAddress)), - entry.ImportAddressTable, - entry.ImportLookupTable); - } - } + public ObjectList Entries => _entries; + public override void Read(PEImageReader reader) { var diagnostics = reader.Diagnostics; @@ -103,7 +74,7 @@ public override void Read(PEImageReader reader) _entries.Add( new PEImportDirectoryEntry( // Name - new(new(PEStreamSectionData.Empty, rawEntry.NameRVA)), + new(PEStreamSectionData.Empty, rawEntry.NameRVA), // ImportAddressTable new PEImportAddressTable() { @@ -118,7 +89,8 @@ public override void Read(PEImageReader reader) ); } - UpdateSize(); + // Update the header size + HeaderSize = ComputeHeaderSize(reader); // Resolve ImportLookupTable and ImportAddressTable section data links var entries = CollectionsMarshal.AsSpan(_entries.UnsafeList); @@ -129,17 +101,62 @@ public override void Read(PEImageReader reader) } } - private unsafe void UpdateSize() + protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) => CalculateSize(); + + internal override IEnumerable CollectImplicitSectionDataList() { - Size = (ulong)((_entries.Count + 1) * sizeof(RawImportDirectoryEntry)); + foreach (var entry in _entries.UnsafeList) + { + yield return entry.ImportAddressTable; + yield return entry.ImportLookupTable; + } } - public override void Write(PEImageWriter writer) + internal override void Bind(PEImageReader reader) { - throw new NotImplementedException(); + var peFile = reader.File; + var diagnostics = reader.Diagnostics; + + var entries = CollectionsMarshal.AsSpan(_entries.UnsafeList); + foreach (ref var entry in entries) + { + var va = entry.ImportDllNameLink.Offset; + if (!peFile.TryFindVirtualContainer(va, out var container)) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"Unable to find the section data for HintNameTableRVA {va}"); + return; + } + + var streamSectionData = container as PEStreamSectionData; + if (streamSectionData is null) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"The section data for HintNameTableRVA {va} is not a stream section data"); + return; + } + + entry = new PEImportDirectoryEntry( + new PEAsciiStringLink(streamSectionData, va - container.VirtualAddress), + entry.ImportAddressTable, + entry.ImportLookupTable); + } + + + foreach (var entry in Entries) + { + entry.ImportAddressTable.FunctionTable.Bind(reader); + entry.ImportLookupTable.FunctionTable.Bind(reader); + } } + private unsafe uint CalculateSize() + { + return (uint)(((_entries.Count + 1) * sizeof(RawImportDirectoryEntry))); + } + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } //private struct HintNameTableEntry //{ diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs index 2508075..0ad53d0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs @@ -20,8 +20,8 @@ public readonly struct PEImportFunctionEntry /// The name of the import. public PEImportFunctionEntry(PEAsciiStringLink name) { - _peSectionData = name.Link.Element; - _offset = name.Link.OffsetInElement; + _peSectionData = name.StreamSectionData; + _offset = name.Offset; } /// @@ -42,7 +42,7 @@ public PEImportFunctionEntry(ushort ordinal) /// /// Gets the name of the import if not by ordinal. /// - public PEImportHintNameLink HintName => _peSectionData is null ? default : new PEImportHintNameLink(new(_peSectionData, _offset)); + public PEImportHintNameLink HintName => _peSectionData is null ? default : new PEImportHintNameLink(_peSectionData, _offset); /// /// Gets the ordinal of the import if by ordinal. diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index b626fb0..4677a47 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -15,8 +15,9 @@ internal readonly struct PEImportFunctionTable() { public List Entries { get; } = new(); - public unsafe ulong CalculateSize(PEFile peFile, DiagnosticBag diagnostics) + public unsafe ulong CalculateSize(PEVisitorContext context) { + var peFile = context.File; // +1 for the null terminator return (ulong)((Entries.Count + 1) * (peFile.IsPE32 ? sizeof(RawImportFunctionEntry32) : sizeof(RawImportFunctionEntry64))); } @@ -36,28 +37,31 @@ public void Read(PEImageReader reader, ulong position) } } - public void ResolveSectionDataLinks(PEFile peFile, DiagnosticBag diagnostics) + public void Bind(PEImageReader reader) { + var peFile = reader.File; + var diagnostics = reader.Diagnostics; + var entries = CollectionsMarshal.AsSpan(Entries); foreach (ref var entry in entries) { if (!entry.IsImportByOrdinal) { - var va = entry.HintName.Link.OffsetInElement; - if (!peFile.TryFindSectionData(va, out var sectionData)) + var va = entry.HintName.Offset; + if (!peFile.TryFindVirtualContainer(va, out var container)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"Unable to find the section data for HintNameTableRVA {va}"); return; } - var streamSectionData = sectionData as PEStreamSectionData; + var streamSectionData = container as PEStreamSectionData; if (streamSectionData is null) { diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"The section data for HintNameTableRVA {va} is not a stream section data"); return; } - entry = new PEImportFunctionEntry(new PEAsciiStringLink(new(streamSectionData, va - sectionData.VirtualAddress))); + entry = new PEImportFunctionEntry(new PEAsciiStringLink(streamSectionData, va - container.VirtualAddress)); } } } @@ -83,7 +87,7 @@ private unsafe void Read32(PEImageReader reader) Entries.Add( entry.IsImportByOrdinal ? new PEImportFunctionEntry(entry.Ordinal) - : new PEImportFunctionEntry(new PEAsciiStringLink(new(PEStreamSectionData.Empty, entry.HintNameTableRVA))) + : new PEImportFunctionEntry(new PEAsciiStringLink(PEStreamSectionData.Empty, entry.HintNameTableRVA)) ); } } @@ -109,7 +113,7 @@ private unsafe void Read64(PEImageReader reader) Entries.Add( entry.IsImportByOrdinal ? new PEImportFunctionEntry(entry.Ordinal) - : new PEImportFunctionEntry(new PEAsciiStringLink(new(PEStreamSectionData.Empty, entry.HintNameTableRVA))) + : new PEImportFunctionEntry(new PEAsciiStringLink(PEStreamSectionData.Empty, entry.HintNameTableRVA)) ); } } @@ -135,7 +139,7 @@ private unsafe void Write32(PEImageWriter writer) for (var i = 0; i < Entries.Count; i++) { var entry = Entries[i]; - var va = entry.HintName.Link.VirtualAddress; + var va = entry.HintName.RVA(); span[i] = new RawImportFunctionEntry32(entry.IsImportByOrdinal ? 0x8000_0000U | entry.Ordinal : va); } @@ -159,7 +163,7 @@ private unsafe void Write64(PEImageWriter writer) for (var i = 0; i < Entries.Count; i++) { var entry = Entries[i]; - var va = entry.HintName.Link.VirtualAddress; + var va = entry.HintName.RVA(); span[i] = new RawImportFunctionEntry64(entry.IsImportByOrdinal ? 0x8000_0000_0000_0000UL | entry.Ordinal : va); } // Last entry is null terminator diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs index fac2fc3..0047aac 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -21,19 +21,14 @@ public PEImportLookupTable() : base(false) public override void UpdateLayout(PEVisitorContext context) { - UpdateSize(context.File, context.Diagnostics); + Size = FunctionTable.CalculateSize(context); } public override void Read(PEImageReader reader) { FunctionTable.Read(reader, Position); - UpdateSize(reader.File, reader.Diagnostics); + UpdateLayout(reader); } - - private void UpdateSize(PEFile file, DiagnosticBag diagnostics) - { - Size = FunctionTable.CalculateSize(file, diagnostics); - } - + public override void Write(PEImageWriter writer) => FunctionTable.Write(writer); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEAsciiStringLink.cs b/src/LibObjectFile/PE/PEAsciiStringLink.cs index 4dda5bc..d38704c 100644 --- a/src/LibObjectFile/PE/PEAsciiStringLink.cs +++ b/src/LibObjectFile/PE/PEAsciiStringLink.cs @@ -7,17 +7,26 @@ namespace LibObjectFile.PE; /// /// A link to a null terminated ASCII string in a . /// -/// The link. -public readonly record struct PEAsciiStringLink(RVALink Link) +public readonly struct PEAsciiStringLink : RVALink { - /// - /// Gets a value indicating whether this instance is null. - /// - public bool IsNull => Link.IsNull; + public PEAsciiStringLink(PEStreamSectionData? streamSectionData, uint offsetInSection) + { + StreamSectionData = streamSectionData; + Offset = offsetInSection; + } + + public readonly PEStreamSectionData? StreamSectionData; + + public PEVirtualObject? Container => StreamSectionData; + + public uint Offset { get; } + + /// + public override string ToString() => this.ToDisplayText(); /// /// Resolves this link to a string. /// /// The string resolved. - public string? Resolve() => Link.Element?.ReadAsciiString(Link.OffsetInElement); + public string? Resolve() => StreamSectionData?.ReadAsciiString(Offset); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 8ddcf37..1852bd4 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -218,7 +218,7 @@ public override void Read(PEImageReader reader) } } - private bool TryInitializeSections(PEImageReader imageReader, ReadOnlySpan headers, out ulong positionAfterLastSection) + private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan headers, out ulong positionAfterLastSection) { _sections.Clear(); @@ -260,7 +260,7 @@ private bool TryInitializeSections(PEImageReader imageReader, ReadOnlySpan {startPosition + totalSize} in {container}"); } } + + private static void FillDirectoryWithStreams(PEImageReader imageReader, PEDataDirectory directory) + { + FillSectionDataWithMissingStreams(imageReader, directory, directory.Content, directory.Position + directory.HeaderSize, directory.Size - directory.HeaderSize); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index cfc4a5f..212d582 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -114,16 +114,17 @@ public PESection AddSection(PESectionName name, uint virtualAddress, uint virtua } public bool TryFindSection(RVA virtualAddress, [NotNullWhen(true)] out PESection? section) - => _sections.TryFindByVirtualAddress(virtualAddress, out section); + { + var result = _sections.TryFindByVirtualAddress(virtualAddress, false, out var sectionObj); + section = sectionObj as PESection; + return result && section is not null; + } public bool TryFindSection(RVA virtualAddress, uint size, [NotNullWhen(true)] out PESection? section) => _sections.TryFindByVirtualAddress(virtualAddress, size, out section); - public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESectionData? sectionData) - { - sectionData = null; - return TryFindSection(virtualAddress, out var section) && section.TryFindSectionData(virtualAddress, out sectionData); - } + public bool TryFindVirtualContainer(RVA virtualAddress, [NotNullWhen(true)] out PEVirtualObject? container) + => _sections.TryFindByVirtualAddress(virtualAddress, true, out container); public void RemoveSection(PESectionName name) { diff --git a/src/LibObjectFile/PE/PEFunctionAddressLink.cs b/src/LibObjectFile/PE/PEFunctionAddressLink.cs new file mode 100644 index 0000000..e8d60c9 --- /dev/null +++ b/src/LibObjectFile/PE/PEFunctionAddressLink.cs @@ -0,0 +1,26 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + +using System.Diagnostics; + +namespace LibObjectFile.PE; + +#pragma warning disable CS0649 +[DebuggerDisplay("{ToString(),nq}")] +public struct PEFunctionAddressLink : RVALink +{ + public PEFunctionAddressLink(PEVirtualObject? container, uint offsetInSection) + { + Container = container; + Offset = offsetInSection; + } + + public PEVirtualObject? Container { get; } + + public uint Offset { get; } + + public RVA RVA => Container is not null ? Container.VirtualAddress + Offset : 0; + + public override string ToString() => Container is not null ? $"{Container}, Offset = {Offset}" : $""; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEImageReader.cs b/src/LibObjectFile/PE/PEImageReader.cs index 4242d02..9ae50c7 100644 --- a/src/LibObjectFile/PE/PEImageReader.cs +++ b/src/LibObjectFile/PE/PEImageReader.cs @@ -11,6 +11,7 @@ public sealed class PEImageReader : ObjectFileReaderWriter internal PEImageReader(PEFile file, Stream stream, PEImageReaderOptions readerOptions) : base(file, stream) { Options = readerOptions; + VisitorContext = new PEVisitorContext(File, Diagnostics); } public new PEFile File => (PEFile)base.File; @@ -18,4 +19,9 @@ internal PEImageReader(PEFile file, Stream stream, PEImageReaderOptions readerOp public PEImageReaderOptions Options { get; } public override bool IsReadOnly => Options.IsReadOnly; + + public PEVisitorContext VisitorContext { get; } + + + public static implicit operator PEVisitorContext(PEImageReader reader) => reader.VisitorContext; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEImportHintNameLink.cs b/src/LibObjectFile/PE/PEImportHintNameLink.cs index 713fba5..c97b5b6 100644 --- a/src/LibObjectFile/PE/PEImportHintNameLink.cs +++ b/src/LibObjectFile/PE/PEImportHintNameLink.cs @@ -7,17 +7,26 @@ namespace LibObjectFile.PE; /// /// A link to a PE Import Hint Name in a . /// -/// The link. -public readonly record struct PEImportHintNameLink(RVALink Link) +public readonly struct PEImportHintNameLink : RVALink { - /// - /// Gets a value indicating whether this instance is null. - /// - public bool IsNull => Link.IsNull; + public PEImportHintNameLink(PEStreamSectionData? streamSectionData, uint offsetInSection) + { + StreamSectionData = streamSectionData; + Offset = offsetInSection; + } + + public readonly PEStreamSectionData? StreamSectionData; + + public PEVirtualObject? Container => StreamSectionData; + + public uint Offset { get; } + /// + public override string ToString() => this.ToDisplayText(); + /// /// Resolves this link to a PE Import Hint Name. /// /// The PE Import Hint Name resolved. - public PEImportHintName Resolve() => Link.Element is null ? default : Link.Element.ReadHintName(Link.OffsetInElement); + public PEImportHintName Resolve() => StreamSectionData?.ReadHintName(Offset) ?? default; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index 4b373e1..3c020d7 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -39,7 +39,7 @@ public PESection(PESectionName name, RVA virtualAddress, RVA virtualSize) : base /// The total size of the section when loaded into memory. /// If this value is greater than , the section is zero-padded. /// - public uint VirtualSize { get; } + public override uint VirtualSize { get; } /// /// Flags that describe the characteristics of the section. @@ -58,7 +58,11 @@ public PESection(PESectionName name, RVA virtualAddress, RVA virtualSize) : base /// The section data that contains the virtual address, if found. /// true if the section data was found; otherwise, false. public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESectionData? sectionData) - => _dataParts.TryFindByVirtualAddress(virtualAddress, out sectionData); + { + var result = _dataParts.TryFindByVirtualAddress(virtualAddress, true, out var sectionObj); + sectionData = sectionObj as PESectionData; + return result && sectionData is not null; + } /// public override void UpdateLayout(PEVisitorContext context) @@ -96,20 +100,8 @@ protected override bool PrintMembers(StringBuilder builder) return true; } - protected override bool TryFindByVirtualAddressInChildren(RVA virtualAddress, out PEVirtualObject? result) - { - var parts = CollectionsMarshal.AsSpan(_dataParts.UnsafeList); - foreach (var data in parts) - { - if (data.TryFindByVirtualAddress(virtualAddress, out result)) - { - return true; - } - } - - result = null; - return false; - } + protected override bool TryFindByVirtualAddressInChildren(RVA virtualAddress, out PEVirtualObject? result) + => _dataParts.TryFindByVirtualAddress(virtualAddress, true, out result); protected override void UpdateVirtualAddressInChildren() { diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index 02189f4..98138e3 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -16,7 +16,7 @@ public abstract class PESectionData : PEVirtualObject protected PESectionData(bool hasChildren) : base(hasChildren) { } - + public virtual int ReadAt(uint offset, Span destination) { throw new NotSupportedException($"The read operation is not supported for {this.GetType().FullName}"); diff --git a/src/LibObjectFile/PE/PESectionDataExtensions.cs b/src/LibObjectFile/PE/PESectionDataExtensions.cs index 156899f..c4fbd25 100644 --- a/src/LibObjectFile/PE/PESectionDataExtensions.cs +++ b/src/LibObjectFile/PE/PESectionDataExtensions.cs @@ -45,7 +45,7 @@ public static PEAsciiStringLink WriteAsciiString(this PEStreamSectionData stream throw new InvalidOperationException("The position is too large to be stored in a uint"); } - return new PEAsciiStringLink(new(streamData, (uint)position)); + return new PEAsciiStringLink(streamData, (uint)position); } public static PEImportHintNameLink WriteHintName(this PEStreamSectionData streamData, PEImportHintName hintName) @@ -56,7 +56,7 @@ public static PEImportHintNameLink WriteHintName(this PEStreamSectionData stream throw new InvalidOperationException("The position is too large to be stored in a uint"); } - return new PEImportHintNameLink(new(streamData, (uint)position)); + return new PEImportHintNameLink(streamData, (uint)position); } public static long WriteAsciiString(this Stream stream, string value) diff --git a/src/LibObjectFile/PE/PESectionDataLink.cs b/src/LibObjectFile/PE/PESectionDataLink.cs new file mode 100644 index 0000000..ad13c64 --- /dev/null +++ b/src/LibObjectFile/PE/PESectionDataLink.cs @@ -0,0 +1,28 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + +using System.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// A link to a section data. +/// +[DebuggerDisplay("{ToString(),nq}")] +public readonly struct PESectionDataLink : RVALink +{ + public PESectionDataLink(PESectionData? sectionData, uint offset) + { + SectionData = sectionData; + Offset = offset; + } + + public PESectionData? SectionData { get; } + + public PEVirtualObject? Container => SectionData; + + public uint Offset { get; } + + public override string ToString() => this.ToDisplayText(); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESectionLink.cs b/src/LibObjectFile/PE/PESectionLink.cs new file mode 100644 index 0000000..3be0937 --- /dev/null +++ b/src/LibObjectFile/PE/PESectionLink.cs @@ -0,0 +1,28 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + +using System.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// A link to a section +/// +[DebuggerDisplay("{ToString(),nq}")] +public readonly struct PESectionLink : RVALink +{ + public PESectionLink(PESection? section, uint offset) + { + Section = section; + Offset = offset; + } + + public PESection? Section { get; } + + public PEVirtualObject? Container => Section; + + public uint Offset { get; } + + public override string ToString() => this.ToDisplayText(); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEVirtualObject.cs b/src/LibObjectFile/PE/PEVirtualObject.cs index cb4b537..eee3e7f 100644 --- a/src/LibObjectFile/PE/PEVirtualObject.cs +++ b/src/LibObjectFile/PE/PEVirtualObject.cs @@ -28,14 +28,19 @@ protected PEVirtualObject(bool hasChildren) /// The address of the first byte of the section when loaded into memory, relative to the image base. /// public RVA VirtualAddress { get; internal set; } - + + /// + /// The size of this object in virtual memory. + /// + public virtual uint VirtualSize => (uint)Size; + /// /// Checks if the specified virtual address is contained in this object. /// /// The virtual address to check. /// true if the specified virtual address is contained in this object; otherwise, false. public bool ContainsVirtual(RVA virtualAddress) - => VirtualAddress <= virtualAddress && virtualAddress < VirtualAddress + (uint)Size; + => VirtualAddress <= virtualAddress && virtualAddress < VirtualAddress + VirtualSize; /// /// Checks if the specified virtual address is contained in this object. @@ -45,7 +50,7 @@ public bool ContainsVirtual(RVA virtualAddress) /// true if the specified virtual address is contained in this object; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ContainsVirtual(RVA virtualAddress, uint size) - => VirtualAddress <= virtualAddress && virtualAddress + size <= VirtualAddress + (uint)Size; + => VirtualAddress <= virtualAddress && virtualAddress + size <= VirtualAddress + VirtualSize; /// /// Tries to find a virtual object by its virtual address. @@ -57,18 +62,13 @@ public bool TryFindByVirtualAddress(RVA virtualAddress, out PEVirtualObject? res { if (ContainsVirtual(virtualAddress)) { - if (HasChildren) - { - if (TryFindByVirtualAddressInChildren(virtualAddress, out result)) - { - return true; - } - } - else + if (HasChildren && TryFindByVirtualAddressInChildren(virtualAddress, out result)) { - result = this; return true; } + + result = this; + return true; } result = null; @@ -139,6 +139,10 @@ static void UpdateSectionDataVirtualAddress(PEVirtualObject parent, ObjectList(this ObjectListThe virtual address to search for. /// The section data that contains the virtual address, if found. /// true if the section data was found; otherwise, false. - public static bool TryFindByVirtualAddress(this ObjectList list, RVA virtualAddress, [NotNullWhen(true)] out TVirtualObject? item) + public static bool TryFindByVirtualAddress(this ObjectList list, RVA virtualAddress, bool recurse, [NotNullWhen(true)] out PEVirtualObject? item) where TVirtualObject : PEVirtualObject { // Binary search @@ -77,6 +77,12 @@ public static bool TryFindByVirtualAddress(this ObjectList -/// Defines a link into a virtual addressable object at a specific virtual offset. -/// -/// The type of the virtual addressable object. -/// The virtual addressable object linked. -/// The offset within this element. -public record struct RVALink(TVirtualAddressable? Element, uint OffsetInElement) - where TVirtualAddressable : PEVirtualObject +// ReSharper disable once InconsistentNaming +public interface RVALink { - /// - /// Gets a value indicating whether this instance is null. - /// - public bool IsNull => Element == null; - - /// - /// Gets the virtual address of within the element. - /// - public RVA VirtualAddress => (Element?.VirtualAddress ?? 0) + OffsetInElement; + public PEVirtualObject? Container { get; } + + public uint Offset { get; } +} + +public interface RVALink : RVALink +{ + public TData Resolve(); +} + +public static class RVALinkExtensions +{ + public static bool IsNull(this TRVALink link) where TRVALink : RVALink => link.Container is null; + + public static RVA RVA(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? link.Container.VirtualAddress + link.Offset : 0; + + public static string ToDisplayText(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? $"{link.Container}, Offset = {link.Offset}" : $""; } From 6af8e78ef1e27443ead4ba1b462c49f3da05cd78 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sat, 21 Sep 2024 18:53:18 +0200 Subject: [PATCH 31/87] Fix handling of PEExportAddressTable --- .../PE/DataDirectory/PEDataDirectory.cs | 1 - .../PE/DataDirectory/PEExportAddressTable.cs | 43 +++++++++++-------- .../PE/DataDirectory/PEExportFunctionEntry.cs | 25 ++++++++--- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index defc69d..7a3bed8 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -123,7 +123,6 @@ protected override uint ComputeHeaderSize(PEVisitorContext context) return 0; } - public override void Read(PEImageReader reader) { // TBD diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index c7a1f03..3a09cc3 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.InteropServices; using LibObjectFile.Diagnostics; @@ -51,28 +52,32 @@ public override unsafe void Read(PEImageReader reader) reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); return; } - - PEAsciiStringLink forwarderLink = default; - //if (rva.ForwarderRVA.Value != 0) - //{ - // if (!reader.File.TryFindSectionData(rva.ForwarderRVA, out var sectionData)) - // { - // reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Unable to find the section data for forwarder RVA {rva.ForwarderRVA}"); - // return; - // } + if (functionContainer is PEStreamSectionData streamSectionData) + { + var parent = functionContainer.Parent; + while (parent != null && !(parent is PESection)) + { + parent = parent.Parent; + } - // var streamSectionData = sectionData as PEStreamSectionData; - // if (streamSectionData is null) - // { - // reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"The section data for forwarder RVA {rva.ForwarderRVA} is not a stream section data"); - // return; - // } + Debug.Assert(parent != null); + var section = (PESection)parent!; + if (section.Name == PESectionName.EData) + { + Values[i] = new PEExportFunctionEntry(new PEAsciiStringLink(streamSectionData, rva - streamSectionData.VirtualAddress)); - // forwarderLink = new PEAsciiStringLink(streamSectionData, rva.ForwarderRVA - streamSectionData.VirtualAddress); - //} - - Values[i] = new PEExportFunctionEntry(new(functionContainer, rva - functionContainer.VirtualAddress), forwarderLink); + } + else + { + Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(streamSectionData, rva - streamSectionData.VirtualAddress)); + } + } + else + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Invalid RVA {rva} for Export Address Table"); + return; + } } } finally diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs index 6675bf5..2886916 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs @@ -5,18 +5,31 @@ namespace LibObjectFile.PE; #pragma warning disable CS0649 -public struct PEExportFunctionEntry +public readonly struct PEExportFunctionEntry { - public PEExportFunctionEntry(PEFunctionAddressLink exportRVA, PEAsciiStringLink forwarderRVA) + private readonly PEVirtualObject? _container; + private readonly uint _offset; + private readonly bool _isForwarderRVA; + + public PEExportFunctionEntry(PEFunctionAddressLink exportRVA) { - ExportRVA = exportRVA; - ForwarderRVA = forwarderRVA; + _container = exportRVA.Container; + _offset = exportRVA.Offset; + _isForwarderRVA = false; } + public PEExportFunctionEntry(PEAsciiStringLink forwarderRVA) + { + _container = forwarderRVA.StreamSectionData; + _offset = forwarderRVA.Offset; + _isForwarderRVA = true; + } - public PEFunctionAddressLink ExportRVA; + public bool IsForwarderRVA => _isForwarderRVA; + + public PEFunctionAddressLink ExportRVA => IsForwarderRVA ? default : new(_container, _offset); - public PEAsciiStringLink ForwarderRVA; + public PEAsciiStringLink ForwarderRVA => IsForwarderRVA ? new(_container as PEStreamSectionData, _offset) : default; public override string ToString() => ForwarderRVA.IsNull() ? $"{ExportRVA}" : $"{ExportRVA}, ForwarderRVA = {ForwarderRVA}"; } \ No newline at end of file From 9ab7ba0613a2b09560e4706da6778eeba2002c4b Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sat, 21 Sep 2024 19:38:15 +0200 Subject: [PATCH 32/87] Add concept of RVO --- src/LibObjectFile.sln.DotSettings | 1 + .../PE/DataDirectory/PEBaseRelocationBlock.cs | 4 +-- .../PEBaseRelocationDirectory.cs | 2 +- .../PE/DataDirectory/PEDataDirectory.cs | 10 +++--- .../PE/DataDirectory/PEExportAddressTable.cs | 34 +++++++++--------- .../PE/DataDirectory/PEExportDirectory.cs | 11 +++--- .../PE/DataDirectory/PEExportFunctionEntry.cs | 4 +-- .../PE/DataDirectory/PEExportNameTable.cs | 2 +- .../PE/DataDirectory/PEImportDirectory.cs | 23 ++++++------ .../DataDirectory/PEImportDirectoryEntry.cs | 17 +-------- .../PE/DataDirectory/PEImportFunctionEntry.cs | 2 +- .../PE/DataDirectory/PEImportFunctionTable.cs | 5 +-- src/LibObjectFile/PE/PEAsciiStringLink.cs | 8 ++--- src/LibObjectFile/PE/PEFile.Read.cs | 6 ++-- src/LibObjectFile/PE/PEFunctionAddressLink.cs | 10 +++--- src/LibObjectFile/PE/PEImportHintNameLink.cs | 8 ++--- src/LibObjectFile/PE/PESection.cs | 14 ++++---- src/LibObjectFile/PE/PESectionData.cs | 2 +- src/LibObjectFile/PE/PESectionDataLink.cs | 6 ++-- src/LibObjectFile/PE/PESectionLink.cs | 6 ++-- src/LibObjectFile/PE/PEVirtualObject.cs | 36 +++++++++---------- .../PE/PEVirtualObjectExtensions.cs | 4 +-- src/LibObjectFile/PE/RVA.cs | 2 +- src/LibObjectFile/PE/RVALink.cs | 6 ++-- src/LibObjectFile/PE/RVO.cs | 27 ++++++++++++++ 25 files changed, 133 insertions(+), 117 deletions(-) create mode 100644 src/LibObjectFile/PE/RVO.cs diff --git a/src/LibObjectFile.sln.DotSettings b/src/LibObjectFile.sln.DotSettings index 31038ce..b2c66c6 100644 --- a/src/LibObjectFile.sln.DotSettings +++ b/src/LibObjectFile.sln.DotSettings @@ -7,6 +7,7 @@ See the license.txt file in the project root for more information. GNU LEB RVA + RVO True True True diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs index f7bafd7..351db69 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs @@ -101,7 +101,7 @@ internal void ReadAndBind(PEImageReader reader) continue; } - var offsetInSectionData = va - sectionData.VirtualAddress; + var offsetInSectionData = va - sectionData.RVA; // Create a new block part if the section data is different, or it is the first relocation if (currentBlockPart is null || currentBlockPart.SectionDataLink.SectionData != sectionData) @@ -110,7 +110,7 @@ internal void ReadAndBind(PEImageReader reader) Parts.Add(currentBlockPart); } - var newRelocation = new PEBaseRelocation(relocation.Type, (ushort)(offsetInSectionData - currentBlockPart.SectionDataLink.Offset)); + var newRelocation = new PEBaseRelocation(relocation.Type, (ushort)(offsetInSectionData - currentBlockPart.SectionDataLink.RVO)); currentBlockPart.Relocations.Add(newRelocation); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs index 467e29f..1bbf649 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs @@ -62,7 +62,7 @@ public override unsafe void Read(PEImageReader reader) // Create a block - var block = new PEBaseRelocationBlock(new PESectionLink(section, (uint)(location.PageVirtualAddress - section.VirtualAddress))) + var block = new PEBaseRelocationBlock(new PESectionLink(section, (uint)(location.PageVirtualAddress - section.RVA))) { BlockBuffer = buffer.Slice(0, sizeOfRelocations) }; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index 7a3bed8..4cba400 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -30,7 +30,7 @@ protected PEDataDirectory(PEDataDirectoryKind kind) : base(true) public sealed override void UpdateLayout(PEVisitorContext context) { - var va = VirtualAddress; + 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 @@ -43,7 +43,7 @@ public sealed override void UpdateLayout(PEVisitorContext context) // So we update the VirtualAddress of each content and update the layout foreach (var table in Content) { - table.VirtualAddress = va; + table.RVA = va; // Update layout will update virtual address table.UpdateLayout(context); @@ -71,12 +71,12 @@ protected override void ValidateParent(ObjectFileElement parent) } } - protected override bool TryFindByVirtualAddressInChildren(RVA virtualAddress, out PEVirtualObject? result) - => Content.TryFindByVirtualAddress(virtualAddress, true, out result); + protected override bool TryFindByVirtualAddressInChildren(RVA rva, out PEVirtualObject? result) + => Content.TryFindByVirtualAddress(rva, true, out result); protected override void UpdateVirtualAddressInChildren() { - var va = VirtualAddress; + var va = RVA; foreach (var table in Content) { table.UpdateVirtualAddress(va); diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index 3a09cc3..a70d3e4 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -47,36 +47,36 @@ public override unsafe void Read(PEImageReader reader) for (int i = 0; i < Values.Count; i++) { var rva = spanRva[i]; - if (!reader.File.TryFindVirtualContainer(rva, out var functionContainer)) + if (!reader.File.TryFindVirtualContainer(rva, out var container)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); return; } - if (functionContainer is PEStreamSectionData streamSectionData) + ObjectFileElement? parent = container; + while (parent != null && parent is not PESection) { - var parent = functionContainer.Parent; - while (parent != null && !(parent is PESection)) - { - parent = parent.Parent; - } + parent = parent.Parent; + } - Debug.Assert(parent != null); - var section = (PESection)parent!; - if (section.Name == PESectionName.EData) - { - Values[i] = new PEExportFunctionEntry(new PEAsciiStringLink(streamSectionData, rva - streamSectionData.VirtualAddress)); + Debug.Assert(parent is not null); + var section = (PESection)parent!; - } - else + if (section.Name == PESectionName.EData) + { + var streamSectionData = container as PEStreamSectionData; + if (streamSectionData is null) { - Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(streamSectionData, rva - streamSectionData.VirtualAddress)); + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Invalid forwarder RVA {rva} for Export Address Table"); + return; } + + Values[i] = new PEExportFunctionEntry(new PEAsciiStringLink(streamSectionData, rva - streamSectionData.RVA)); + } else { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Invalid RVA {rva} for Export Address Table"); - return; + Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(container, rva - container.RVA)); } } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs index e07eb9c..14539f4 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs @@ -76,7 +76,8 @@ public override unsafe void Read(PEImageReader reader) } // Link to a fake section data until we have recorded the different export tables in the sections - NameLink = new PEAsciiStringLink(PEStreamSectionData.Empty, exportDirectory.Name); + // Store a fake RVO that is the RVA until we resolve it in the Bind phase + NameLink = new PEAsciiStringLink(PEStreamSectionData.Empty, (RVO)(uint)exportDirectory.Name); if (!reader.File.TryFindSection(exportDirectory.AddressOfFunctions, out var sectionAddressOfFunctions)) { @@ -98,19 +99,19 @@ public override unsafe void Read(PEImageReader reader) ExportFunctionAddressTable = new PEExportAddressTable((int)exportDirectory.NumberOfFunctions) { - Position = sectionAddressOfFunctions.Position + exportDirectory.AddressOfFunctions - sectionAddressOfFunctions.VirtualAddress, + Position = sectionAddressOfFunctions.Position + exportDirectory.AddressOfFunctions - sectionAddressOfFunctions.RVA, Size = (ulong)(exportDirectory.NumberOfFunctions * sizeof(RVA)) }; ExportNameTable = new PEExportNameTable((int)exportDirectory.NumberOfNames) { - Position = sectionAddressOfNames.Position + exportDirectory.AddressOfNames - sectionAddressOfNames.VirtualAddress, + Position = sectionAddressOfNames.Position + exportDirectory.AddressOfNames - sectionAddressOfNames.RVA, Size = (ulong)(exportDirectory.NumberOfNames * sizeof(RVA)) }; ExportOrdinalTable = new PEExportOrdinalTable((int)exportDirectory.NumberOfFunctions) { - Position = sectionAddressOfNameOrdinals.Position + exportDirectory.AddressOfNameOrdinals - sectionAddressOfNameOrdinals.VirtualAddress, + Position = sectionAddressOfNameOrdinals.Position + exportDirectory.AddressOfNameOrdinals - sectionAddressOfNameOrdinals.RVA, Size = (ulong)(exportDirectory.NumberOfFunctions * sizeof(ushort)) }; @@ -136,7 +137,7 @@ internal override void Bind(PEImageReader reader) return; } - NameLink = new PEAsciiStringLink(streamSectionData, NameLink.Offset); + NameLink = new PEAsciiStringLink(streamSectionData, NameLink.RVO); if (ExportFunctionAddressTable is not null) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs index 2886916..bd3fe74 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs @@ -14,14 +14,14 @@ public readonly struct PEExportFunctionEntry public PEExportFunctionEntry(PEFunctionAddressLink exportRVA) { _container = exportRVA.Container; - _offset = exportRVA.Offset; + _offset = exportRVA.RVO; _isForwarderRVA = false; } public PEExportFunctionEntry(PEAsciiStringLink forwarderRVA) { _container = forwarderRVA.StreamSectionData; - _offset = forwarderRVA.Offset; + _offset = forwarderRVA.RVO; _isForwarderRVA = true; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs index 52ad022..e583ce3 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs @@ -59,7 +59,7 @@ public override unsafe void Read(PEImageReader reader) return; } - Values[i] = new PEAsciiStringLink(streamSectionData, rva - streamSectionData.VirtualAddress); + Values[i] = new PEAsciiStringLink(streamSectionData, rva - streamSectionData.RVA); } } finally diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 033051f..cf4a124 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -13,14 +13,14 @@ namespace LibObjectFile.PE; public sealed class PEImportDirectory : PEDataDirectory { - private readonly ObjectList _entries; + private readonly List _entries; public PEImportDirectory() : base(PEDataDirectoryKind.Import) { - _entries = new(this); + _entries = new(); } - public ObjectList Entries => _entries; + public List Entries => _entries; public override void Read(PEImageReader reader) { @@ -58,7 +58,7 @@ public override void Read(PEImageReader reader) } // Calculate its position within the original stream - var importLookupAddressTablePositionInFile = section.Position + rawEntry.ImportAddressTableRVA - section.VirtualAddress; + var importLookupAddressTablePositionInFile = section.Position + rawEntry.ImportAddressTableRVA - section.RVA; // Find the section data for the ImportLookupTableRVA if (!reader.File.TryFindSection(rawEntry.ImportLookupTableRVA, out section)) @@ -68,13 +68,13 @@ public override void Read(PEImageReader reader) } // Calculate its position within the original stream - var importLookupTablePositionInFile = section.Position + rawEntry.ImportLookupTableRVA - section.VirtualAddress; + var importLookupTablePositionInFile = section.Position + rawEntry.ImportLookupTableRVA - section.RVA; // Store a fake entry for post-processing section data to allow to recreate PEImportLookupTable from existing PESectionStreamData _entries.Add( new PEImportDirectoryEntry( // Name - new(PEStreamSectionData.Empty, rawEntry.NameRVA), + new(PEStreamSectionData.Empty, (RVO)(uint)rawEntry.NameRVA), // Store the RVA as a fake RVO until we bind it in the Bind phase // ImportAddressTable new PEImportAddressTable() { @@ -93,7 +93,7 @@ public override void Read(PEImageReader reader) HeaderSize = ComputeHeaderSize(reader); // Resolve ImportLookupTable and ImportAddressTable section data links - var entries = CollectionsMarshal.AsSpan(_entries.UnsafeList); + var entries = CollectionsMarshal.AsSpan(_entries); foreach (ref var entry in entries) { entry.ImportAddressTable.Read(reader); @@ -105,7 +105,7 @@ public override void Read(PEImageReader reader) internal override IEnumerable CollectImplicitSectionDataList() { - foreach (var entry in _entries.UnsafeList) + foreach (var entry in _entries) { yield return entry.ImportAddressTable; yield return entry.ImportLookupTable; @@ -117,10 +117,11 @@ internal override void Bind(PEImageReader reader) var peFile = reader.File; var diagnostics = reader.Diagnostics; - var entries = CollectionsMarshal.AsSpan(_entries.UnsafeList); + var entries = CollectionsMarshal.AsSpan(_entries); foreach (ref var entry in entries) { - var va = entry.ImportDllNameLink.Offset; + // The RVO is actually an RVA until we bind it here + var va = (RVA)(uint)entry.ImportDllNameLink.RVO; if (!peFile.TryFindVirtualContainer(va, out var container)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"Unable to find the section data for HintNameTableRVA {va}"); @@ -135,7 +136,7 @@ internal override void Bind(PEImageReader reader) } entry = new PEImportDirectoryEntry( - new PEAsciiStringLink(streamSectionData, va - container.VirtualAddress), + new PEAsciiStringLink(streamSectionData, va - container.RVA), entry.ImportAddressTable, entry.ImportLookupTable); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs index 44e8f07..2b1300d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs @@ -2,12 +2,9 @@ // 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.PE.Internal; - namespace LibObjectFile.PE; -public sealed class PEImportDirectoryEntry : PEObject +public sealed class PEImportDirectoryEntry { public PEImportDirectoryEntry(PEAsciiStringLink importDllNameLink, PEImportAddressTable importAddressTable, PEImportLookupTable importLookupTable) { @@ -21,16 +18,4 @@ public PEImportDirectoryEntry(PEAsciiStringLink importDllNameLink, PEImportAddre public PEImportAddressTable ImportAddressTable { get; set; } public PEImportLookupTable ImportLookupTable { get; set; } - - public override unsafe void UpdateLayout(PEVisitorContext context) - { - Size = (ulong)sizeof(RawImportDirectoryEntry); - - // Update the layout of the import lookup table - ImportLookupTable.UpdateLayout(context); - } - - public override void Read(PEImageReader reader) => throw new System.NotSupportedException(); - - public 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 index 0ad53d0..2ad8d69 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs @@ -21,7 +21,7 @@ public readonly struct PEImportFunctionEntry public PEImportFunctionEntry(PEAsciiStringLink name) { _peSectionData = name.StreamSectionData; - _offset = name.Offset; + _offset = name.RVO; } /// diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index 4677a47..27cf3c3 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -47,7 +47,8 @@ public void Bind(PEImageReader reader) { if (!entry.IsImportByOrdinal) { - var va = entry.HintName.Offset; + // The RVO is an RVA until we bind it to a container below + var va = (RVA)(uint)entry.HintName.RVO; if (!peFile.TryFindVirtualContainer(va, out var container)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"Unable to find the section data for HintNameTableRVA {va}"); @@ -61,7 +62,7 @@ public void Bind(PEImageReader reader) return; } - entry = new PEImportFunctionEntry(new PEAsciiStringLink(streamSectionData, va - container.VirtualAddress)); + entry = new PEImportFunctionEntry(new PEAsciiStringLink(streamSectionData, va - container.RVA)); } } } diff --git a/src/LibObjectFile/PE/PEAsciiStringLink.cs b/src/LibObjectFile/PE/PEAsciiStringLink.cs index d38704c..fa68c72 100644 --- a/src/LibObjectFile/PE/PEAsciiStringLink.cs +++ b/src/LibObjectFile/PE/PEAsciiStringLink.cs @@ -9,17 +9,17 @@ namespace LibObjectFile.PE; /// public readonly struct PEAsciiStringLink : RVALink { - public PEAsciiStringLink(PEStreamSectionData? streamSectionData, uint offsetInSection) + public PEAsciiStringLink(PEStreamSectionData? streamSectionData, RVO rvo) { StreamSectionData = streamSectionData; - Offset = offsetInSection; + RVO = rvo; } public readonly PEStreamSectionData? StreamSectionData; public PEVirtualObject? Container => StreamSectionData; - public uint Offset { get; } + public RVO RVO { get; } /// public override string ToString() => this.ToDisplayText(); @@ -28,5 +28,5 @@ public PEAsciiStringLink(PEStreamSectionData? streamSectionData, uint offsetInSe /// Resolves this link to a string. /// /// The string resolved. - public string? Resolve() => StreamSectionData?.ReadAsciiString(Offset); + public string? Resolve() => StreamSectionData?.ReadAsciiString(RVO); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 1852bd4..fa245f8 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -264,7 +264,7 @@ private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan Container is not null ? Container.VirtualAddress + Offset : 0; + public RVA RVA => Container is not null ? Container.RVA + RVO : 0; - public override string ToString() => Container is not null ? $"{Container}, Offset = {Offset}" : $""; + public override string ToString() => Container is not null ? $"{Container}, Offset = {RVO}" : $""; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEImportHintNameLink.cs b/src/LibObjectFile/PE/PEImportHintNameLink.cs index c97b5b6..4f95b86 100644 --- a/src/LibObjectFile/PE/PEImportHintNameLink.cs +++ b/src/LibObjectFile/PE/PEImportHintNameLink.cs @@ -9,17 +9,17 @@ namespace LibObjectFile.PE; /// public readonly struct PEImportHintNameLink : RVALink { - public PEImportHintNameLink(PEStreamSectionData? streamSectionData, uint offsetInSection) + public PEImportHintNameLink(PEStreamSectionData? streamSectionData, RVO rvoInSection) { StreamSectionData = streamSectionData; - Offset = offsetInSection; + RVO = rvoInSection; } public readonly PEStreamSectionData? StreamSectionData; public PEVirtualObject? Container => StreamSectionData; - public uint Offset { get; } + public RVO RVO { get; } /// public override string ToString() => this.ToDisplayText(); @@ -28,5 +28,5 @@ public PEImportHintNameLink(PEStreamSectionData? streamSectionData, uint offsetI /// Resolves this link to a PE Import Hint Name. /// /// The PE Import Hint Name resolved. - public PEImportHintName Resolve() => StreamSectionData?.ReadHintName(Offset) ?? default; + public PEImportHintName Resolve() => StreamSectionData?.ReadHintName(RVO) ?? default; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index 3c020d7..e3ca59a 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -20,10 +20,10 @@ public sealed class PESection : PEVirtualObject { private readonly ObjectList _dataParts; - public PESection(PESectionName name, RVA virtualAddress, RVA virtualSize) : base(true) + public PESection(PESectionName name, RVA rva, RVA virtualSize) : base(true) { Name = name; - VirtualAddress = virtualAddress; + RVA = rva; VirtualSize = virtualSize; _dataParts = PEVirtualObject.CreateObjectList(this); // Most of the time readable @@ -67,10 +67,10 @@ public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESec /// public override void UpdateLayout(PEVisitorContext context) { - var va = VirtualAddress; + var va = RVA; foreach (var data in DataParts) { - data.VirtualAddress = va; + data.RVA = va; data.UpdateLayout(context); va += (uint)data.Size; } @@ -96,12 +96,12 @@ protected override void PrintName(StringBuilder builder) /// protected override bool PrintMembers(StringBuilder builder) { - builder.Append($"VirtualAddress = {VirtualAddress}, VirtualSize = 0x{VirtualSize:X4}, DataParts[{DataParts.Count}]"); + builder.Append($"VirtualAddress = {RVA}, VirtualSize = 0x{VirtualSize:X4}, DataParts[{DataParts.Count}]"); return true; } - protected override bool TryFindByVirtualAddressInChildren(RVA virtualAddress, out PEVirtualObject? result) - => _dataParts.TryFindByVirtualAddress(virtualAddress, true, out result); + protected override bool TryFindByVirtualAddressInChildren(RVA rva, out PEVirtualObject? result) + => _dataParts.TryFindByVirtualAddress(rva, true, out result); protected override void UpdateVirtualAddressInChildren() { diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index 98138e3..c56a925 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -29,7 +29,7 @@ public virtual void WriteAt(uint offset, ReadOnlySpan source) protected override bool PrintMembers(StringBuilder builder) { - builder.Append($"VirtualAddress = {VirtualAddress}, Size = 0x{Size:X4}"); + builder.Append($"VirtualAddress = {RVA}, Size = 0x{Size:X4}"); if (Parent is PESection section) { builder.Append($", Section = {section.Name}"); diff --git a/src/LibObjectFile/PE/PESectionDataLink.cs b/src/LibObjectFile/PE/PESectionDataLink.cs index ad13c64..1e0c202 100644 --- a/src/LibObjectFile/PE/PESectionDataLink.cs +++ b/src/LibObjectFile/PE/PESectionDataLink.cs @@ -12,17 +12,17 @@ namespace LibObjectFile.PE; [DebuggerDisplay("{ToString(),nq}")] public readonly struct PESectionDataLink : RVALink { - public PESectionDataLink(PESectionData? sectionData, uint offset) + public PESectionDataLink(PESectionData? sectionData, uint rvo) { SectionData = sectionData; - Offset = offset; + RVO = rvo; } public PESectionData? SectionData { get; } public PEVirtualObject? Container => SectionData; - public uint Offset { get; } + public RVO RVO { get; } public override string ToString() => this.ToDisplayText(); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESectionLink.cs b/src/LibObjectFile/PE/PESectionLink.cs index 3be0937..b4d338f 100644 --- a/src/LibObjectFile/PE/PESectionLink.cs +++ b/src/LibObjectFile/PE/PESectionLink.cs @@ -12,17 +12,17 @@ namespace LibObjectFile.PE; [DebuggerDisplay("{ToString(),nq}")] public readonly struct PESectionLink : RVALink { - public PESectionLink(PESection? section, uint offset) + public PESectionLink(PESection? section, uint rvo) { Section = section; - Offset = offset; + RVO = rvo; } public PESection? Section { get; } public PEVirtualObject? Container => Section; - public uint Offset { get; } + public RVO RVO { get; } public override string ToString() => this.ToDisplayText(); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEVirtualObject.cs b/src/LibObjectFile/PE/PEVirtualObject.cs index eee3e7f..fd59889 100644 --- a/src/LibObjectFile/PE/PEVirtualObject.cs +++ b/src/LibObjectFile/PE/PEVirtualObject.cs @@ -27,7 +27,7 @@ protected PEVirtualObject(bool hasChildren) /// /// The address of the first byte of the section when loaded into memory, relative to the image base. /// - public RVA VirtualAddress { get; internal set; } + public RVA RVA { get; internal set; } /// /// The size of this object in virtual memory. @@ -37,32 +37,32 @@ protected PEVirtualObject(bool hasChildren) /// /// Checks if the specified virtual address is contained in this object. /// - /// The virtual address to check. + /// The virtual address to check. /// true if the specified virtual address is contained in this object; otherwise, false. - public bool ContainsVirtual(RVA virtualAddress) - => VirtualAddress <= virtualAddress && virtualAddress < VirtualAddress + VirtualSize; + public bool ContainsVirtual(RVA rva) + => RVA <= rva && rva < RVA + VirtualSize; /// /// Checks if the specified virtual address is contained in this object. /// - /// The virtual address to check. + /// The virtual address to check. /// The size of the data that must be contained. /// true if the specified virtual address is contained in this object; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ContainsVirtual(RVA virtualAddress, uint size) - => VirtualAddress <= virtualAddress && virtualAddress + size <= VirtualAddress + VirtualSize; + public bool ContainsVirtual(RVA rva, uint size) + => RVA <= rva && rva + size <= RVA + VirtualSize; /// /// Tries to find a virtual object by its virtual address. /// - /// The virtual address to search for. + /// The virtual address to search for. /// The virtual object that contains the virtual address, if found. /// true if the virtual object was found; otherwise, false. - public bool TryFindByVirtualAddress(RVA virtualAddress, out PEVirtualObject? result) + public bool TryFindByVirtualAddress(RVA rva, out PEVirtualObject? result) { - if (ContainsVirtual(virtualAddress)) + if (ContainsVirtual(rva)) { - if (HasChildren && TryFindByVirtualAddressInChildren(virtualAddress, out result)) + if (HasChildren && TryFindByVirtualAddressInChildren(rva, out result)) { return true; } @@ -78,18 +78,18 @@ public bool TryFindByVirtualAddress(RVA virtualAddress, out PEVirtualObject? res /// /// Try to find a virtual object by its virtual address in children. /// - /// The virtual address to search for. + /// The virtual address to search for. /// The virtual object that contains the virtual address, if found. /// true if the virtual object was found; otherwise, false. /// - protected virtual bool TryFindByVirtualAddressInChildren(RVA virtualAddress, out PEVirtualObject? result) + protected virtual bool TryFindByVirtualAddressInChildren(RVA rva, out PEVirtualObject? result) { throw new NotImplementedException("This method must be implemented by PEVirtualObject with children"); } - internal void UpdateVirtualAddress(RVA virtualAddress) + internal void UpdateVirtualAddress(RVA rva) { - VirtualAddress = virtualAddress; + RVA = rva; if (HasChildren) { UpdateVirtualAddressInChildren(); @@ -134,11 +134,11 @@ static void UpdateSectionDataVirtualAddress(PEVirtualObject parent, ObjectList 0) { var previousData = span[startIndex - 1]; - va = previousData.VirtualAddress + (uint)previousData.Size; + va = previousData.RVA + (uint)previousData.Size; } else { - va = parent.VirtualAddress; + va = parent.RVA; if (parent is PEDataDirectory directory) { va += directory.HeaderSize; @@ -149,7 +149,7 @@ static void UpdateSectionDataVirtualAddress(PEVirtualObject parent, ObjectList(this ObjectList trySectionData.VirtualAddress) + if (virtualAddress > trySectionData.RVA) { low = mid + 1; } @@ -87,7 +87,7 @@ public static bool TryFindByVirtualAddress(this ObjectList trySectionData.VirtualAddress) + if (virtualAddress > trySectionData.RVA) { low = mid + 1; } diff --git a/src/LibObjectFile/PE/RVA.cs b/src/LibObjectFile/PE/RVA.cs index 83e9723..ff0946b 100644 --- a/src/LibObjectFile/PE/RVA.cs +++ b/src/LibObjectFile/PE/RVA.cs @@ -23,5 +23,5 @@ public record struct RVA(uint Value) public static implicit operator RVA(uint value) => new(value); /// - public override string ToString() => $"{Value:X4}"; + public override string ToString() => $"0x{Value:X}"; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/RVALink.cs b/src/LibObjectFile/PE/RVALink.cs index 3550210..5f74969 100644 --- a/src/LibObjectFile/PE/RVALink.cs +++ b/src/LibObjectFile/PE/RVALink.cs @@ -9,7 +9,7 @@ public interface RVALink { public PEVirtualObject? Container { get; } - public uint Offset { get; } + public RVO RVO { get; } } public interface RVALink : RVALink @@ -21,7 +21,7 @@ public static class RVALinkExtensions { public static bool IsNull(this TRVALink link) where TRVALink : RVALink => link.Container is null; - public static RVA RVA(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? link.Container.VirtualAddress + link.Offset : 0; + public static RVA RVA(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? link.Container.RVA + link.RVO : 0; - public static string ToDisplayText(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? $"{link.Container}, Offset = {link.Offset}" : $""; + public static string ToDisplayText(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? $"{link.Container}, Offset = {link.RVO}" : $""; } diff --git a/src/LibObjectFile/PE/RVO.cs b/src/LibObjectFile/PE/RVO.cs new file mode 100644 index 0000000..2cda31a --- /dev/null +++ b/src/LibObjectFile/PE/RVO.cs @@ -0,0 +1,27 @@ +// 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; + +/// +/// Defines a Relative Virtual Offset (RVO) that is relative to an RVA in a Portable Executable (PE) image. +/// +/// The value of the relative offset. +public record struct RVO(uint Value) +{ + /// + /// Converts a to a . + /// + /// The value to convert. + public static implicit operator uint(RVO value) => value.Value; + + /// + /// Converts a to a . + /// + /// The value to convert. + public static implicit operator RVO(uint value) => new(value); + + /// + public override string ToString() => $"0x{Value:X}"; +} \ No newline at end of file From 3a5b653d135d609f53a11a6fe186a599bb609798 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sat, 21 Sep 2024 20:20:27 +0200 Subject: [PATCH 33/87] Improve naming --- .../PE/DataDirectory/PEBaseRelocationBlock.cs | 2 +- .../PEBaseRelocationDirectory.cs | 8 +- .../PE/DataDirectory/PEDataDirectory.cs | 8 +- .../PE/DataDirectory/PEExportAddressTable.cs | 2 +- .../PE/DataDirectory/PEExportDirectory.cs | 2 +- .../PE/DataDirectory/PEExportFunctionEntry.cs | 2 +- .../PE/DataDirectory/PEExportNameTable.cs | 2 +- .../PE/DataDirectory/PEImportDirectory.cs | 2 +- .../PE/DataDirectory/PEImportFunctionTable.cs | 2 +- src/LibObjectFile/PE/ImageDataDirectory.cs | 2 +- .../PE/Internal/RawImageSectionHeader.cs | 2 +- src/LibObjectFile/PE/PEAsciiStringLink.cs | 2 +- src/LibObjectFile/PE/PEFile.Read.cs | 22 +-- src/LibObjectFile/PE/PEFile.cs | 18 +- src/LibObjectFile/PE/PEFunctionAddressLink.cs | 4 +- src/LibObjectFile/PE/PEImportHintNameLink.cs | 2 +- src/LibObjectFile/PE/PEObject.cs | 165 +++++++++++++++++- src/LibObjectFile/PE/PEObjectBase.cs | 13 ++ ...ectExtensions.cs => PEObjectExtensions.cs} | 12 +- src/LibObjectFile/PE/PESection.cs | 30 ++-- src/LibObjectFile/PE/PESectionData.cs | 14 +- src/LibObjectFile/PE/PESectionDataLink.cs | 2 +- src/LibObjectFile/PE/PESectionLink.cs | 2 +- src/LibObjectFile/PE/PEVirtualObject.cs | 159 ----------------- src/LibObjectFile/PE/RVALink.cs | 2 +- src/LibObjectFile/PE/RVO.cs | 2 +- 26 files changed, 247 insertions(+), 236 deletions(-) create mode 100644 src/LibObjectFile/PE/PEObjectBase.cs rename src/LibObjectFile/PE/{PEVirtualObjectExtensions.cs => PEObjectExtensions.cs} (82%) delete mode 100644 src/LibObjectFile/PE/PEVirtualObject.cs diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs index 351db69..e038428 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs @@ -88,7 +88,7 @@ internal void ReadAndBind(PEImageReader reader) var va = blockBaseAddress + relocation.OffsetInBlockPart; // Find the section data containing the virtual address - if (!peFile.TryFindVirtualContainer(va, out var vObj)) + if (!peFile.TryFindContainerByRVA(va, out var vObj)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section data containing the virtual address 0x{va:X4}"); continue; diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs index 1bbf649..19e66d3 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs @@ -52,9 +52,9 @@ public override unsafe void Read(PEImageReader reader) var location = MemoryMarshal.Read(buffer.Span); buffer = buffer.Slice(sizeof(ImageBaseRelocation)); - if (!reader.File.TryFindSection(location.PageVirtualAddress, out var section)) + if (!reader.File.TryFindSection(location.PageRVA, out var section)) { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section containing the virtual address {location.PageVirtualAddress} in block #{blockIndex}"); + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section containing the virtual address {location.PageRVA} in block #{blockIndex}"); continue; } @@ -62,7 +62,7 @@ public override unsafe void Read(PEImageReader reader) // Create a block - var block = new PEBaseRelocationBlock(new PESectionLink(section, (uint)(location.PageVirtualAddress - section.RVA))) + var block = new PEBaseRelocationBlock(new PESectionLink(section, (uint)(location.PageRVA - section.RVA))) { BlockBuffer = buffer.Slice(0, sizeOfRelocations) }; @@ -106,7 +106,7 @@ protected override bool PrintMembers(StringBuilder builder) private struct ImageBaseRelocation { #pragma warning disable CS0649 - public RVA PageVirtualAddress; + public RVA PageRVA; public uint SizeOfBlock; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index 4cba400..7989277 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -71,15 +71,15 @@ protected override void ValidateParent(ObjectFileElement parent) } } - protected override bool TryFindByVirtualAddressInChildren(RVA rva, out PEVirtualObject? result) - => Content.TryFindByVirtualAddress(rva, true, out result); + protected override bool TryFindByRVAInChildren(RVA rva, out PEObject? result) + => Content.TryFindByRVA(rva, true, out result); - protected override void UpdateVirtualAddressInChildren() + protected override void UpdateRVAInChildren() { var va = RVA; foreach (var table in Content) { - table.UpdateVirtualAddress(va); + table.UpdateRVA(va); va += (uint)table.Size; } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index a70d3e4..dd2fa0d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -47,7 +47,7 @@ public override unsafe void Read(PEImageReader reader) for (int i = 0; i < Values.Count; i++) { var rva = spanRva[i]; - if (!reader.File.TryFindVirtualContainer(rva, out var container)) + if (!reader.File.TryFindContainerByRVA(rva, out var container)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs index 14539f4..dad95bb 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs @@ -124,7 +124,7 @@ internal override void Bind(PEImageReader reader) var peFile = reader.File; var diagnostics = reader.Diagnostics; - if (!peFile.TryFindVirtualContainer(NameLink.RVA(), out var container)) + if (!peFile.TryFindContainerByRVA(NameLink.RVA(), out var container)) { diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidName, $"Unable to find the section data for Name {NameLink.RVA()}"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs index bd3fe74..20d997a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs @@ -7,7 +7,7 @@ namespace LibObjectFile.PE; #pragma warning disable CS0649 public readonly struct PEExportFunctionEntry { - private readonly PEVirtualObject? _container; + private readonly PEObject? _container; private readonly uint _offset; private readonly bool _isForwarderRVA; diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs index e583ce3..706a0d9 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs @@ -46,7 +46,7 @@ public override unsafe void Read(PEImageReader reader) for (int i = 0; i < Values.Count; i++) { var rva = spanRva[i]; - if (!reader.File.TryFindVirtualContainer(rva, out var sectionData)) + if (!reader.File.TryFindContainerByRVA(rva, out var sectionData)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportNameTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index cf4a124..b9ccb47 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -122,7 +122,7 @@ internal override void Bind(PEImageReader reader) { // The RVO is actually an RVA until we bind it here var va = (RVA)(uint)entry.ImportDllNameLink.RVO; - if (!peFile.TryFindVirtualContainer(va, out var container)) + if (!peFile.TryFindContainerByRVA(va, out var container)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"Unable to find the section data for HintNameTableRVA {va}"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index 27cf3c3..7808654 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -49,7 +49,7 @@ public void Bind(PEImageReader reader) { // The RVO is an RVA until we bind it to a container below var va = (RVA)(uint)entry.HintName.RVO; - if (!peFile.TryFindVirtualContainer(va, out var container)) + if (!peFile.TryFindContainerByRVA(va, out var container)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"Unable to find the section data for HintNameTableRVA {va}"); return; diff --git a/src/LibObjectFile/PE/ImageDataDirectory.cs b/src/LibObjectFile/PE/ImageDataDirectory.cs index 8f8f3f8..781120e 100644 --- a/src/LibObjectFile/PE/ImageDataDirectory.cs +++ b/src/LibObjectFile/PE/ImageDataDirectory.cs @@ -16,7 +16,7 @@ public struct ImageDataDirectory /// /// The relative virtual address of the data directory. /// - public RVA VirtualAddress; + public RVA RVA; /// /// The size of the data directory, in bytes. diff --git a/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs b/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs index b40d4f2..67e7d6c 100644 --- a/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs +++ b/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs @@ -60,7 +60,7 @@ public uint PhysicalAddress /// /// The address of the first byte of the section when loaded into memory, relative to the image base. /// - public uint VirtualAddress; + public RVA RVA; /// /// The size of the section's raw data in the file, in bytes. diff --git a/src/LibObjectFile/PE/PEAsciiStringLink.cs b/src/LibObjectFile/PE/PEAsciiStringLink.cs index fa68c72..5e4ec16 100644 --- a/src/LibObjectFile/PE/PEAsciiStringLink.cs +++ b/src/LibObjectFile/PE/PEAsciiStringLink.cs @@ -17,7 +17,7 @@ public PEAsciiStringLink(PEStreamSectionData? streamSectionData, RVO rvo) public readonly PEStreamSectionData? StreamSectionData; - public PEVirtualObject? Container => StreamSectionData; + public PEObject? Container => StreamSectionData; public RVO RVO { get; } diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index fa245f8..65d33e5 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -228,7 +228,7 @@ private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan /// For a list of section data (e.g. used by a section or a ImportAddressTableDirectory...), we need to fill any hole with the actual stream of original data from the image /// - private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, PEObject container, ObjectList dataParts, ulong startPosition, ulong totalSize) + private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, PEObjectBase container, ObjectList dataParts, ulong startPosition, ulong totalSize) { var currentPosition = startPosition; diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 212d582..10816c2 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -17,7 +17,7 @@ namespace LibObjectFile.PE; /// /// A Portable Executable file that can be read, modified and written. /// -public partial class PEFile : PEObject +public partial class PEFile : PEObjectBase { private byte[] _dosStub = []; private Stream? _dosStubExtra; @@ -113,18 +113,18 @@ public PESection AddSection(PESectionName name, uint virtualAddress, uint virtua return section; } - public bool TryFindSection(RVA virtualAddress, [NotNullWhen(true)] out PESection? section) + public bool TryFindSection(RVA rva, [NotNullWhen(true)] out PESection? section) { - var result = _sections.TryFindByVirtualAddress(virtualAddress, false, out var sectionObj); + var result = _sections.TryFindByRVA(rva, false, out var sectionObj); section = sectionObj as PESection; return result && section is not null; } - public bool TryFindSection(RVA virtualAddress, uint size, [NotNullWhen(true)] out PESection? section) - => _sections.TryFindByVirtualAddress(virtualAddress, size, out section); + public bool TryFindSection(RVA rva, uint size, [NotNullWhen(true)] out PESection? section) + => _sections.TryFindByRVA(rva, size, out section); - public bool TryFindVirtualContainer(RVA virtualAddress, [NotNullWhen(true)] out PEVirtualObject? container) - => _sections.TryFindByVirtualAddress(virtualAddress, true, out container); + public bool TryFindContainerByRVA(RVA rva, [NotNullWhen(true)] out PEObject? container) + => _sections.TryFindByRVA(rva, true, out container); public void RemoveSection(PESectionName name) { @@ -176,13 +176,13 @@ public List GetAllSectionData() var sections = _sections; foreach (var section in sections) { - count += section.DataParts.Count; + count += section.Content.Count; } dataList.Capacity = count; foreach (var section in sections) { - foreach (var data in section.DataParts) + foreach (var data in section.Content) { dataList.Add(data); } diff --git a/src/LibObjectFile/PE/PEFunctionAddressLink.cs b/src/LibObjectFile/PE/PEFunctionAddressLink.cs index 6d26fd9..62a63e3 100644 --- a/src/LibObjectFile/PE/PEFunctionAddressLink.cs +++ b/src/LibObjectFile/PE/PEFunctionAddressLink.cs @@ -10,13 +10,13 @@ namespace LibObjectFile.PE; [DebuggerDisplay("{ToString(),nq}")] public struct PEFunctionAddressLink : RVALink { - public PEFunctionAddressLink(PEVirtualObject? container, RVO rvo) + public PEFunctionAddressLink(PEObject? container, RVO rvo) { Container = container; RVO = rvo; } - public PEVirtualObject? Container { get; } + public PEObject? Container { get; } public RVO RVO { get; } diff --git a/src/LibObjectFile/PE/PEImportHintNameLink.cs b/src/LibObjectFile/PE/PEImportHintNameLink.cs index 4f95b86..c5da6f0 100644 --- a/src/LibObjectFile/PE/PEImportHintNameLink.cs +++ b/src/LibObjectFile/PE/PEImportHintNameLink.cs @@ -17,7 +17,7 @@ public PEImportHintNameLink(PEStreamSectionData? streamSectionData, RVO rvoInSec public readonly PEStreamSectionData? StreamSectionData; - public PEVirtualObject? Container => StreamSectionData; + public PEObject? Container => StreamSectionData; public RVO RVO { get; } diff --git a/src/LibObjectFile/PE/PEObject.cs b/src/LibObjectFile/PE/PEObject.cs index 4a5ae5a..d634623 100644 --- a/src/LibObjectFile/PE/PEObject.cs +++ b/src/LibObjectFile/PE/PEObject.cs @@ -2,12 +2,169 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. -using System.Diagnostics; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using LibObjectFile.Collections; namespace LibObjectFile.PE; /// -/// Base class for all Portable Executable (PE) objects. +/// Base class for all Portable Executable (PE) objects that have a virtual address. /// -[DebuggerDisplay("{ToString(),nq}")] -public abstract class PEObject : ObjectFileElement; \ No newline at end of file +public abstract class PEObject : PEObjectBase +{ + protected PEObject(bool isContainer) + { + IsContainer = isContainer; + } + + /// + /// Gets a value indicating whether this object has children. + /// + public bool IsContainer { get; } + + /// + /// The address of the first byte of the section when loaded into memory, relative to the image base. + /// + public RVA RVA { get; internal set; } + + /// + /// The size of this object in virtual memory. + /// + public virtual uint VirtualSize => (uint)Size; + + /// + /// Checks if the specified virtual address is contained in this object. + /// + /// The virtual address to check. + /// true if the specified virtual address is contained in this object; otherwise, false. + public bool ContainsVirtual(RVA rva) + => RVA <= rva && rva < RVA + VirtualSize; + + /// + /// Checks if the specified virtual address is contained in this object. + /// + /// The virtual address to check. + /// The size of the data that must be contained. + /// true if the specified virtual address is contained in this object; otherwise, false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ContainsVirtual(RVA rva, uint size) + => RVA <= rva && rva + size <= RVA + VirtualSize; + + /// + /// Tries to find a virtual object by its virtual address. + /// + /// The virtual address to search for. + /// The virtual object that contains the virtual address, if found. + /// true if the virtual object was found; otherwise, false. + public bool TryFindByRVA(RVA rva, out PEObject? result) + { + if (ContainsVirtual(rva)) + { + if (IsContainer && TryFindByRVAInChildren(rva, out result)) + { + return true; + } + + result = this; + return true; + } + + result = null; + return false; + } + + /// + /// Try to find a virtual object by its virtual address in children. + /// + /// The virtual address to search for. + /// The virtual object that contains the virtual address, if found. + /// true if the virtual object was found; otherwise, false. + /// + protected virtual bool TryFindByRVAInChildren(RVA rva, out PEObject? result) + { + throw new NotImplementedException("This method must be implemented by PEVirtualObject with children"); + } + + internal void UpdateRVA(RVA rva) + { + RVA = rva; + if (IsContainer) + { + UpdateRVAInChildren(); + } + } + + /// + /// Updates the virtual address of children. + /// + protected virtual void UpdateRVAInChildren() + { + } + + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"RVA = {RVA}, VirtualSize = 0x{VirtualSize:X}, Size = 0x{Size:X}"); + return true; + } + + protected override void ValidateParent(ObjectFileElement parent) + { + } + + public static ObjectList CreateObjectList(PEObject parent) where TVirtualObject : PEObject + { + ObjectList objectList = default; + objectList = new ObjectList(parent, null, SectionDataAdded, null, SectionDataRemoved, null, SectionDataUpdated); + return objectList; + + void SectionDataAdded(ObjectFileElement vParent, TVirtualObject item) + { + // ReSharper disable once AccessToModifiedClosure + UpdateSectionDataRVA((PEObject)vParent, objectList, item.Index); + } + + void SectionDataRemoved(ObjectFileElement vParent, int index, TVirtualObject item) + { + // ReSharper disable once AccessToModifiedClosure + UpdateSectionDataRVA((PEObject)vParent, objectList, index); + } + + void SectionDataUpdated(ObjectFileElement vParent, int index, TVirtualObject previousItem, TVirtualObject newItem) + { + // ReSharper disable once AccessToModifiedClosure + UpdateSectionDataRVA((PEObject)vParent, objectList, index); + } + + static void UpdateSectionDataRVA(PEObject parent, ObjectList items, int startIndex) + { + RVA va; + var span = CollectionsMarshal.AsSpan(items.UnsafeList); + if (startIndex > 0) + { + var previousData = span[startIndex - 1]; + va = previousData.RVA + (uint)previousData.Size; + } + else + { + va = parent.RVA; + if (parent is PEDataDirectory directory) + { + va += directory.HeaderSize; + } + } + + for (int i = startIndex; i < span.Length; i++) + { + var data = span[i]; + + data.RVA = va; + data.UpdateRVAInChildren(); + + va += (uint)data.Size; + } + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEObjectBase.cs b/src/LibObjectFile/PE/PEObjectBase.cs new file mode 100644 index 0000000..c6ac5bb --- /dev/null +++ b/src/LibObjectFile/PE/PEObjectBase.cs @@ -0,0 +1,13 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + +using System.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// Base class for all Portable Executable (PE) objects. +/// +[DebuggerDisplay("{ToString(),nq}")] +public abstract class PEObjectBase : ObjectFileElement; \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEVirtualObjectExtensions.cs b/src/LibObjectFile/PE/PEObjectExtensions.cs similarity index 82% rename from src/LibObjectFile/PE/PEVirtualObjectExtensions.cs rename to src/LibObjectFile/PE/PEObjectExtensions.cs index 311a31b..0024442 100644 --- a/src/LibObjectFile/PE/PEVirtualObjectExtensions.cs +++ b/src/LibObjectFile/PE/PEObjectExtensions.cs @@ -10,7 +10,7 @@ namespace LibObjectFile.PE; -public static class PEVirtualObjectExtensions +public static class PEObjectExtensions { /// /// Tries to find a virtual object by its virtual address. @@ -18,8 +18,8 @@ public static class PEVirtualObjectExtensions /// The virtual address to search for. /// The section data that contains the virtual address, if found. /// true if the section data was found; otherwise, false. - public static bool TryFindByVirtualAddress(this ObjectList list, RVA virtualAddress, uint size, [NotNullWhen(true)] out TVirtualObject? item) - where TVirtualObject : PEVirtualObject + public static bool TryFindByRVA(this ObjectList list, RVA virtualAddress, uint size, [NotNullWhen(true)] out TPEObject? item) + where TPEObject : PEObject { // Binary search nint low = 0; @@ -60,8 +60,8 @@ public static bool TryFindByVirtualAddress(this ObjectListThe virtual address to search for. /// The section data that contains the virtual address, if found. /// true if the section data was found; otherwise, false. - public static bool TryFindByVirtualAddress(this ObjectList list, RVA virtualAddress, bool recurse, [NotNullWhen(true)] out PEVirtualObject? item) - where TVirtualObject : PEVirtualObject + public static bool TryFindByRVA(this ObjectList list, RVA virtualAddress, bool recurse, [NotNullWhen(true)] out PEObject? item) + where TPEObject : PEObject { // Binary search nint low = 0; @@ -77,7 +77,7 @@ public static bool TryFindByVirtualAddress(this ObjectList /// Defines a section in a Portable Executable (PE) image. /// -public sealed class PESection : PEVirtualObject +public sealed class PESection : PEObject { - private readonly ObjectList _dataParts; + private readonly ObjectList _content; public PESection(PESectionName name, RVA rva, RVA virtualSize) : base(true) { Name = name; RVA = rva; VirtualSize = virtualSize; - _dataParts = PEVirtualObject.CreateObjectList(this); + _content = PEObject.CreateObjectList(this); // Most of the time readable Characteristics = SectionCharacteristics.MemRead; } @@ -49,7 +49,7 @@ public PESection(PESectionName name, RVA rva, RVA virtualSize) : base(true) /// /// Gets the list of data associated with this section. /// - public ObjectList DataParts => _dataParts; + public ObjectList Content => _content; /// /// Tries to find the section data that contains the specified virtual address. @@ -59,7 +59,7 @@ public PESection(PESectionName name, RVA rva, RVA virtualSize) : base(true) /// true if the section data was found; otherwise, false. public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESectionData? sectionData) { - var result = _dataParts.TryFindByVirtualAddress(virtualAddress, true, out var sectionObj); + var result = _content.TryFindByRVA(virtualAddress, true, out var sectionObj); sectionData = sectionObj as PESectionData; return result && sectionData is not null; } @@ -68,7 +68,7 @@ public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESec public override void UpdateLayout(PEVisitorContext context) { var va = RVA; - foreach (var data in DataParts) + foreach (var data in Content) { data.RVA = va; data.UpdateLayout(context); @@ -96,18 +96,28 @@ protected override void PrintName(StringBuilder builder) /// protected override bool PrintMembers(StringBuilder builder) { - builder.Append($"VirtualAddress = {RVA}, VirtualSize = 0x{VirtualSize:X4}, DataParts[{DataParts.Count}]"); + builder.Append($"{Name} "); + base.PrintMembers(builder); + builder.Append($", Content[{Content.Count}]"); return true; } - protected override bool TryFindByVirtualAddressInChildren(RVA rva, out PEVirtualObject? result) - => _dataParts.TryFindByVirtualAddress(rva, true, out result); + protected override bool TryFindByRVAInChildren(RVA rva, out PEObject? result) + => _content.TryFindByRVA(rva, true, out result); - protected override void UpdateVirtualAddressInChildren() + protected override void UpdateRVAInChildren() { // TODO? } + protected override void ValidateParent(ObjectFileElement parent) + { + if (parent is not PEFile) + { + throw new ArgumentException($"Invalid parent type {parent.GetType().FullName}. Expecting a parent of type {typeof(PEFile).FullName}"); + } + } + /// /// Gets the default characteristics for a section name. /// diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index c56a925..bbed329 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -11,9 +11,9 @@ namespace LibObjectFile.PE; /// /// Base class for data contained in a . /// -public abstract class PESectionData : PEVirtualObject +public abstract class PESectionData : PEObject { - protected PESectionData(bool hasChildren) : base(hasChildren) + protected PESectionData(bool isContainer) : base(isContainer) { } @@ -27,16 +27,6 @@ public virtual void WriteAt(uint offset, ReadOnlySpan source) throw new NotSupportedException($"The write operation is not supported for {this.GetType().FullName}"); } - protected override bool PrintMembers(StringBuilder builder) - { - builder.Append($"VirtualAddress = {RVA}, Size = 0x{Size:X4}"); - if (Parent is PESection section) - { - builder.Append($", Section = {section.Name}"); - } - return true; - } - protected override void ValidateParent(ObjectFileElement parent) { if (parent is not PESection && parent is not PESectionData) diff --git a/src/LibObjectFile/PE/PESectionDataLink.cs b/src/LibObjectFile/PE/PESectionDataLink.cs index 1e0c202..6bd5b42 100644 --- a/src/LibObjectFile/PE/PESectionDataLink.cs +++ b/src/LibObjectFile/PE/PESectionDataLink.cs @@ -20,7 +20,7 @@ public PESectionDataLink(PESectionData? sectionData, uint rvo) public PESectionData? SectionData { get; } - public PEVirtualObject? Container => SectionData; + public PEObject? Container => SectionData; public RVO RVO { get; } diff --git a/src/LibObjectFile/PE/PESectionLink.cs b/src/LibObjectFile/PE/PESectionLink.cs index b4d338f..e517b2d 100644 --- a/src/LibObjectFile/PE/PESectionLink.cs +++ b/src/LibObjectFile/PE/PESectionLink.cs @@ -20,7 +20,7 @@ public PESectionLink(PESection? section, uint rvo) public PESection? Section { get; } - public PEVirtualObject? Container => Section; + public PEObject? Container => Section; public RVO RVO { get; } diff --git a/src/LibObjectFile/PE/PEVirtualObject.cs b/src/LibObjectFile/PE/PEVirtualObject.cs deleted file mode 100644 index fd59889..0000000 --- a/src/LibObjectFile/PE/PEVirtualObject.cs +++ /dev/null @@ -1,159 +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. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using LibObjectFile.Collections; - -namespace LibObjectFile.PE; - -/// -/// Base class for all Portable Executable (PE) objects that have a virtual address. -/// -public abstract class PEVirtualObject : PEObject -{ - protected PEVirtualObject(bool hasChildren) - { - HasChildren = hasChildren; - } - - /// - /// Gets a value indicating whether this object has children. - /// - public bool HasChildren { get; } - - /// - /// The address of the first byte of the section when loaded into memory, relative to the image base. - /// - public RVA RVA { get; internal set; } - - /// - /// The size of this object in virtual memory. - /// - public virtual uint VirtualSize => (uint)Size; - - /// - /// Checks if the specified virtual address is contained in this object. - /// - /// The virtual address to check. - /// true if the specified virtual address is contained in this object; otherwise, false. - public bool ContainsVirtual(RVA rva) - => RVA <= rva && rva < RVA + VirtualSize; - - /// - /// Checks if the specified virtual address is contained in this object. - /// - /// The virtual address to check. - /// The size of the data that must be contained. - /// true if the specified virtual address is contained in this object; otherwise, false. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ContainsVirtual(RVA rva, uint size) - => RVA <= rva && rva + size <= RVA + VirtualSize; - - /// - /// Tries to find a virtual object by its virtual address. - /// - /// The virtual address to search for. - /// The virtual object that contains the virtual address, if found. - /// true if the virtual object was found; otherwise, false. - public bool TryFindByVirtualAddress(RVA rva, out PEVirtualObject? result) - { - if (ContainsVirtual(rva)) - { - if (HasChildren && TryFindByVirtualAddressInChildren(rva, out result)) - { - return true; - } - - result = this; - return true; - } - - result = null; - return false; - } - - /// - /// Try to find a virtual object by its virtual address in children. - /// - /// The virtual address to search for. - /// The virtual object that contains the virtual address, if found. - /// true if the virtual object was found; otherwise, false. - /// - protected virtual bool TryFindByVirtualAddressInChildren(RVA rva, out PEVirtualObject? result) - { - throw new NotImplementedException("This method must be implemented by PEVirtualObject with children"); - } - - internal void UpdateVirtualAddress(RVA rva) - { - RVA = rva; - if (HasChildren) - { - UpdateVirtualAddressInChildren(); - } - } - - /// - /// Updates the virtual address of children. - /// - protected virtual void UpdateVirtualAddressInChildren() - { - } - - public static ObjectList CreateObjectList(PEVirtualObject parent) where TVirtualObject : PEVirtualObject - { - ObjectList objectList = default; - objectList = new ObjectList(parent, null, SectionDataAdded, null, SectionDataRemoved, null, SectionDataUpdated); - return objectList; - - void SectionDataAdded(ObjectFileElement vParent, TVirtualObject item) - { - // ReSharper disable once AccessToModifiedClosure - UpdateSectionDataVirtualAddress((PEVirtualObject)vParent, objectList, item.Index); - } - - void SectionDataRemoved(ObjectFileElement vParent, int index, TVirtualObject item) - { - // ReSharper disable once AccessToModifiedClosure - UpdateSectionDataVirtualAddress((PEVirtualObject)vParent, objectList, index); - } - - void SectionDataUpdated(ObjectFileElement vParent, int index, TVirtualObject previousItem, TVirtualObject newItem) - { - // ReSharper disable once AccessToModifiedClosure - UpdateSectionDataVirtualAddress((PEVirtualObject)vParent, objectList, index); - } - - static void UpdateSectionDataVirtualAddress(PEVirtualObject parent, ObjectList items, int startIndex) - { - RVA va; - var span = CollectionsMarshal.AsSpan(items.UnsafeList); - if (startIndex > 0) - { - var previousData = span[startIndex - 1]; - va = previousData.RVA + (uint)previousData.Size; - } - else - { - va = parent.RVA; - if (parent is PEDataDirectory directory) - { - va += directory.HeaderSize; - } - } - - for (int i = startIndex; i < span.Length; i++) - { - var data = span[i]; - - data.RVA = va; - data.UpdateVirtualAddressInChildren(); - - va += (uint)data.Size; - } - } - } -} \ No newline at end of file diff --git a/src/LibObjectFile/PE/RVALink.cs b/src/LibObjectFile/PE/RVALink.cs index 5f74969..fc1c51d 100644 --- a/src/LibObjectFile/PE/RVALink.cs +++ b/src/LibObjectFile/PE/RVALink.cs @@ -7,7 +7,7 @@ namespace LibObjectFile.PE; // ReSharper disable once InconsistentNaming public interface RVALink { - public PEVirtualObject? Container { get; } + public PEObject? Container { get; } public RVO RVO { get; } } diff --git a/src/LibObjectFile/PE/RVO.cs b/src/LibObjectFile/PE/RVO.cs index 2cda31a..f222881 100644 --- a/src/LibObjectFile/PE/RVO.cs +++ b/src/LibObjectFile/PE/RVO.cs @@ -5,7 +5,7 @@ namespace LibObjectFile.PE; /// -/// Defines a Relative Virtual Offset (RVO) that is relative to an RVA in a Portable Executable (PE) image. +/// Defines a Relative Virtual Offset (RVO) that is relative to a in a Portable Executable (PE) image. /// /// The value of the relative offset. public record struct RVO(uint Value) From f15dc0cf5c1a91a26c2aad320eca0d20e8cdec11 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 22 Sep 2024 06:26:49 +0200 Subject: [PATCH 34/87] Add ObjectElement --- src/LibObjectFile/Ar/ArObject.cs | 2 +- src/LibObjectFile/Collections/ObjectList.cs | 46 +++++------ .../Collections/SortedObjectList.cs | 10 +-- src/LibObjectFile/Dwarf/DwarfObject.cs | 8 +- src/LibObjectFile/Dwarf/DwarfSection.cs | 2 +- src/LibObjectFile/Dwarf/DwarfUnit.cs | 2 +- src/LibObjectFile/Elf/ElfObject.cs | 2 +- src/LibObjectFile/Elf/ElfSection.cs | 2 +- src/LibObjectFile/ObjectElement.cs | 80 +++++++++++++++++++ src/LibObjectFile/ObjectFileElement.cs | 66 ++------------- .../PE/DataDirectory/PEDataDirectory.cs | 2 +- .../PE/DataDirectory/PEExportAddressTable.cs | 2 +- src/LibObjectFile/PE/PEObject.cs | 16 ++-- src/LibObjectFile/PE/PESection.cs | 2 +- src/LibObjectFile/PE/PESectionData.cs | 2 +- 15 files changed, 134 insertions(+), 110 deletions(-) create mode 100644 src/LibObjectFile/ObjectElement.cs diff --git a/src/LibObjectFile/Ar/ArObject.cs b/src/LibObjectFile/Ar/ArObject.cs index f734d8b..791d384 100644 --- a/src/LibObjectFile/Ar/ArObject.cs +++ b/src/LibObjectFile/Ar/ArObject.cs @@ -25,7 +25,7 @@ public abstract class ArObject : ArObjectBase internal set => base.Parent = value; } - protected override void ValidateParent(ObjectFileElement parent) + protected override void ValidateParent(ObjectElement parent) { if (!(parent is ArArchiveFile)) { diff --git a/src/LibObjectFile/Collections/ObjectList.cs b/src/LibObjectFile/Collections/ObjectList.cs index 83a0f92..839445f 100644 --- a/src/LibObjectFile/Collections/ObjectList.cs +++ b/src/LibObjectFile/Collections/ObjectList.cs @@ -17,7 +17,7 @@ namespace LibObjectFile.Collections; [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy(typeof(ObjectList<>.ObjectListDebuggerView))] public readonly struct ObjectList : IList - where TObject : ObjectFileElement + where TObject : ObjectElement { // We are using an internal list to keep track of the parent object private readonly InternalList _items; @@ -30,13 +30,13 @@ namespace LibObjectFile.Collections; /// /// The parent object file node. public ObjectList( - ObjectFileElement parent, - Action? adding= null, - Action? added = null, - Action? removing = null, - Action? removed = null, - Action? updating = null, - Action? updated = null + ObjectElement parent, + Action? adding= null, + Action? added = null, + Action? removing = null, + Action? removed = null, + Action? updating = null, + Action? updated = null ) { ArgumentNullException.ThrowIfNull(parent); @@ -180,23 +180,23 @@ private TObject AssignAdd(TObject item) return item; } - private sealed class InternalList(ObjectFileElement parent, - Action? adding, - Action? added, - Action? removing, - Action? removed, - Action? updating, - Action? updated + private sealed class InternalList(ObjectElement parent, + Action? adding, + Action? added, + Action? removing, + Action? removed, + Action? updating, + Action? updated ) : List { - private readonly Action? _adding = adding; - private readonly Action? _added = added; - private readonly Action? _removing = removing; - private readonly Action? _removed = removed; - private readonly Action? _updating = updating; - private readonly Action? _updated = updated; - - public readonly ObjectFileElement Parent = parent; + private readonly Action? _adding = adding; + private readonly Action? _added = added; + private readonly Action? _removing = removing; + private readonly Action? _removed = removed; + private readonly Action? _updating = updating; + private readonly Action? _updated = updated; + + public readonly ObjectElement Parent = parent; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Adding(int index, TObject item) => _adding?.Invoke(Parent, index, item); diff --git a/src/LibObjectFile/Collections/SortedObjectList.cs b/src/LibObjectFile/Collections/SortedObjectList.cs index 45b1439..4dc08e1 100644 --- a/src/LibObjectFile/Collections/SortedObjectList.cs +++ b/src/LibObjectFile/Collections/SortedObjectList.cs @@ -18,7 +18,7 @@ namespace LibObjectFile.Collections; [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy(typeof(SortedObjectList<>.ObjectListDebuggerView))] public readonly struct SortedObjectList : IList - where TObject : ObjectFileElement, IComparable + where TObject : ObjectElement, IComparable { // We are using an internal list to keep track of the parent object private readonly InternalList _items; @@ -30,7 +30,7 @@ namespace LibObjectFile.Collections; /// Initializes a new instance of the class. /// /// The parent object file node. - public SortedObjectList(ObjectFileElement parent) + public SortedObjectList(ObjectElement parent) { ArgumentNullException.ThrowIfNull(parent); _items = new InternalList(parent); @@ -207,11 +207,11 @@ private TObject AssignAdd(TObject item) return item; } - private sealed class InternalList(ObjectFileElement parent) : List + private sealed class InternalList(ObjectElement parent) : List { - public readonly ObjectFileElement Parent = parent; + public readonly ObjectElement Parent = parent; } - + internal sealed class ObjectListDebuggerView { private readonly List _collection; diff --git a/src/LibObjectFile/Dwarf/DwarfObject.cs b/src/LibObjectFile/Dwarf/DwarfObject.cs index b34a3a8..9044976 100644 --- a/src/LibObjectFile/Dwarf/DwarfObject.cs +++ b/src/LibObjectFile/Dwarf/DwarfObject.cs @@ -11,7 +11,7 @@ public abstract class DwarfObject : ObjectFileElement : DwarfObject where TContainer : ObjectFileElement { - protected override void ValidateParent(ObjectFileElement parent) + protected override void ValidateParent(ObjectElement parent) { if (!(parent is TContainer)) { diff --git a/src/LibObjectFile/Dwarf/DwarfSection.cs b/src/LibObjectFile/Dwarf/DwarfSection.cs index 526a453..38e0d04 100644 --- a/src/LibObjectFile/Dwarf/DwarfSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfSection.cs @@ -9,7 +9,7 @@ namespace LibObjectFile.Dwarf; public abstract class DwarfSection : DwarfContainer { - protected override void ValidateParent(ObjectFileElement parent) + protected override void ValidateParent(ObjectElement parent) { if (!(parent is DwarfFile)) { diff --git a/src/LibObjectFile/Dwarf/DwarfUnit.cs b/src/LibObjectFile/Dwarf/DwarfUnit.cs index 09dcd9d..b6bb0a5 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnit.cs @@ -78,7 +78,7 @@ public override void Verify(DwarfVerifyContext context) Root?.Verify(context); } - protected override void ValidateParent(ObjectFileElement parent) + protected override void ValidateParent(ObjectElement parent) { if (!(parent is DwarfSection)) { diff --git a/src/LibObjectFile/Elf/ElfObject.cs b/src/LibObjectFile/Elf/ElfObject.cs index a2dd3e0..a523c6e 100644 --- a/src/LibObjectFile/Elf/ElfObject.cs +++ b/src/LibObjectFile/Elf/ElfObject.cs @@ -16,7 +16,7 @@ public abstract class ElfObjectBase : ObjectFileElement public abstract class ElfObject : ElfObjectBase { - protected override void ValidateParent(ObjectFileElement parent) + protected override void ValidateParent(ObjectElement parent) { if (!(parent is ElfObjectFile)) { diff --git a/src/LibObjectFile/Elf/ElfSection.cs b/src/LibObjectFile/Elf/ElfSection.cs index 7c8b0fc..a07a72e 100644 --- a/src/LibObjectFile/Elf/ElfSection.cs +++ b/src/LibObjectFile/Elf/ElfSection.cs @@ -35,7 +35,7 @@ public virtual ElfSectionType Type } } - protected override void ValidateParent(ObjectFileElement parent) + protected override void ValidateParent(ObjectElement parent) { if (!(parent is ElfObjectFile)) { diff --git a/src/LibObjectFile/ObjectElement.cs b/src/LibObjectFile/ObjectElement.cs new file mode 100644 index 0000000..9036e1d --- /dev/null +++ b/src/LibObjectFile/ObjectElement.cs @@ -0,0 +1,80 @@ +// 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.Text; + +namespace LibObjectFile; + +public abstract class ObjectElement +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private ObjectElement? _parent; + + protected ObjectElement() + { + Index = -1; + } + + /// + /// Gets the containing parent. + /// + public ObjectElement? Parent + { + get => _parent; + + internal set + { + if (value == null) + { + _parent = null; + } + else + { + ValidateParent(value); + } + + _parent = value; + } + } + + /// + /// If the object is part of a list in its parent, this property returns the index within the containing list in the parent. Otherwise, this value is -1. + /// + public int Index { get; internal set; } + + + public sealed override string ToString() + { + var builder = new StringBuilder(); + PrintName(builder); + builder.Append(" { "); + if (PrintMembers(builder)) + { + builder.Append(' '); + } + builder.Append('}'); + return builder.ToString(); + } + + protected virtual void PrintName(StringBuilder builder) + { + builder.Append(GetType().Name); + } + + protected virtual bool PrintMembers(StringBuilder builder) + { + return false; + } + + protected virtual void ValidateParent(ObjectElement parent) + { + } + + internal void ResetIndex() + { + Index = -1; + } +} \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileElement.cs b/src/LibObjectFile/ObjectFileElement.cs index 76a117d..d4bb849 100644 --- a/src/LibObjectFile/ObjectFileElement.cs +++ b/src/LibObjectFile/ObjectFileElement.cs @@ -8,14 +8,11 @@ namespace LibObjectFile; -public abstract class ObjectFileElement +public abstract class ObjectFileElement : ObjectElement { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private ObjectFileElement? _parent; protected ObjectFileElement() { - Index = -1; } /// @@ -28,33 +25,6 @@ protected ObjectFileElement() /// public ulong Size { get; set; } - /// - /// Gets the containing parent. - /// - public ObjectFileElement? Parent - { - get => _parent; - - internal set - { - if (value == null) - { - _parent = null; - } - else - { - ValidateParent(value); - } - - _parent = value; - } - } - - /// - /// If the object is part of a list in its parent, this property returns the index within the containing list in the parent. Otherwise, this value is -1. - /// - public int Index { get; internal set; } - /// /// Checks if the specified offset is contained by this instance. /// @@ -80,37 +50,11 @@ public bool Contains(ObjectFileElement element) ArgumentNullException.ThrowIfNull(element); return Contains((ulong)element.Position) || element.Size != 0 && Contains((ulong)(element.Position + element.Size - 1)); } - - public sealed override string ToString() - { - var builder = new StringBuilder(); - PrintName(builder); - builder.Append(" { "); - if (PrintMembers(builder)) - { - builder.Append(' '); - } - builder.Append('}'); - return builder.ToString(); - } - - protected virtual void PrintName(StringBuilder builder) - { - builder.Append(GetType().Name); - } - - protected virtual bool PrintMembers(StringBuilder builder) - { - return false; - } - - protected virtual void ValidateParent(ObjectFileElement parent) - { - } - - internal void ResetIndex() + + protected override bool PrintMembers(StringBuilder builder) { - Index = -1; + builder.Append($"Position = 0x{Position:X}, Size = 0x{Size:X}"); + return true; } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index 7989277..316bda2 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -63,7 +63,7 @@ internal virtual void Bind(PEImageReader reader) protected abstract uint ComputeHeaderSize(PEVisitorContext context); - protected override void ValidateParent(ObjectFileElement parent) + protected override void ValidateParent(ObjectElement parent) { if (parent is not PESection) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index dd2fa0d..f7b5003 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -53,7 +53,7 @@ public override unsafe void Read(PEImageReader reader) return; } - ObjectFileElement? parent = container; + ObjectElement? parent = container; while (parent != null && parent is not PESection) { parent = parent.Parent; diff --git a/src/LibObjectFile/PE/PEObject.cs b/src/LibObjectFile/PE/PEObject.cs index d634623..b7cc480 100644 --- a/src/LibObjectFile/PE/PEObject.cs +++ b/src/LibObjectFile/PE/PEObject.cs @@ -110,35 +110,35 @@ protected override bool PrintMembers(StringBuilder builder) return true; } - protected override void ValidateParent(ObjectFileElement parent) + protected override void ValidateParent(ObjectElement parent) { } - public static ObjectList CreateObjectList(PEObject parent) where TVirtualObject : PEObject + public static ObjectList CreateObjectList(PEObject parent) where TPEObject : PEObject { - ObjectList objectList = default; - objectList = new ObjectList(parent, null, SectionDataAdded, null, SectionDataRemoved, null, SectionDataUpdated); + ObjectList objectList = default; + objectList = new ObjectList(parent, null, SectionDataAdded, null, SectionDataRemoved, null, SectionDataUpdated); return objectList; - void SectionDataAdded(ObjectFileElement vParent, TVirtualObject item) + void SectionDataAdded(ObjectElement vParent, TPEObject item) { // ReSharper disable once AccessToModifiedClosure UpdateSectionDataRVA((PEObject)vParent, objectList, item.Index); } - void SectionDataRemoved(ObjectFileElement vParent, int index, TVirtualObject item) + void SectionDataRemoved(ObjectElement vParent, int index, TPEObject item) { // ReSharper disable once AccessToModifiedClosure UpdateSectionDataRVA((PEObject)vParent, objectList, index); } - void SectionDataUpdated(ObjectFileElement vParent, int index, TVirtualObject previousItem, TVirtualObject newItem) + void SectionDataUpdated(ObjectElement vParent, int index, TPEObject previousItem, TPEObject newItem) { // ReSharper disable once AccessToModifiedClosure UpdateSectionDataRVA((PEObject)vParent, objectList, index); } - static void UpdateSectionDataRVA(PEObject parent, ObjectList items, int startIndex) + static void UpdateSectionDataRVA(PEObject parent, ObjectList items, int startIndex) { RVA va; var span = CollectionsMarshal.AsSpan(items.UnsafeList); diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index afaf71f..cdf2d98 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -110,7 +110,7 @@ protected override void UpdateRVAInChildren() // TODO? } - protected override void ValidateParent(ObjectFileElement parent) + protected override void ValidateParent(ObjectElement parent) { if (parent is not PEFile) { diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index bbed329..408dd59 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -27,7 +27,7 @@ public virtual void WriteAt(uint offset, ReadOnlySpan source) throw new NotSupportedException($"The write operation is not supported for {this.GetType().FullName}"); } - protected override void ValidateParent(ObjectFileElement parent) + protected override void ValidateParent(ObjectElement parent) { if (parent is not PESection && parent is not PESectionData) { From 5dff88239c126682556a32152d7b29de746ca900 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 22 Sep 2024 10:57:19 +0200 Subject: [PATCH 35/87] Add resource directory --- src/LibObjectFile/Ar/ArArchiveFileReader.cs | 4 +- src/LibObjectFile/Ar/ArArchiveFileWriter.cs | 4 +- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 5 + src/LibObjectFile/Dwarf/DwarfReader.cs | 4 +- src/LibObjectFile/Dwarf/DwarfWriter.cs | 2 +- src/LibObjectFile/Elf/ElfReader.cs | 2 +- src/LibObjectFile/Elf/ElfWriter.cs | 2 +- src/LibObjectFile/IO/StreamExtensions.cs | 101 +++++++ src/LibObjectFile/ObjectFileReaderWriter.cs | 89 +----- .../PE/DataDirectory/PEResourceDataEntry.cs | 109 +++++++ .../PE/DataDirectory/PEResourceDirectory.cs | 207 ++++++++++++++ .../DataDirectory/PEResourceDirectoryEntry.cs | 207 ++++++++++++++ .../PE/DataDirectory/PEResourceEntry.cs | 147 ++++++++++ .../PE/DataDirectory/PEResourceId.cs | 267 ++++++++++++++++++ .../RawImageResourceDirectoryEntry.cs | 31 ++ src/LibObjectFile/PE/PEImageReader.cs | 2 +- src/LibObjectFile/PE/PEImageWriter.cs | 2 +- src/LibObjectFile/PE/PESection.cs | 6 - src/LibObjectFile/PE/PESectionDataLink.cs | 2 +- 19 files changed, 1092 insertions(+), 101 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEResourceId.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageResourceDirectoryEntry.cs diff --git a/src/LibObjectFile/Ar/ArArchiveFileReader.cs b/src/LibObjectFile/Ar/ArArchiveFileReader.cs index 564d144..c5259db 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileReader.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileReader.cs @@ -23,12 +23,12 @@ public class ArArchiveFileReader : ObjectFileReaderWriter internal ArArchiveFileReader(ArArchiveFile arArchiveFile, Stream stream, ArArchiveFileReaderOptions options) : base(arArchiveFile, stream) { Options = options; - IsReadOnly = options.IsReadOnly; + KeepOriginalStreamForSubStreams = options.IsReadOnly; } public ArArchiveFileReaderOptions Options { get; } - public override bool IsReadOnly { get; } + public override bool KeepOriginalStreamForSubStreams { get; } public ArArchiveFile ArArchiveFile => (ArArchiveFile)base.File; diff --git a/src/LibObjectFile/Ar/ArArchiveFileWriter.cs b/src/LibObjectFile/Ar/ArArchiveFileWriter.cs index d9f4853..c775610 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileWriter.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileWriter.cs @@ -20,12 +20,12 @@ public class ArArchiveFileWriter: ObjectFileReaderWriter internal ArArchiveFileWriter(ArArchiveFile archiveFile, Stream stream) : base(archiveFile, stream) { - IsReadOnly = false; + KeepOriginalStreamForSubStreams = false; } public ArArchiveFile ArArchiveFile => (ArArchiveFile)base.File; - public override bool IsReadOnly { get; } + public override bool KeepOriginalStreamForSubStreams { get; } internal void Write() { diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 421f845..95b37ca 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -149,4 +149,9 @@ public enum DiagnosticId PE_ERR_ExportDirectoryInvalidAddressOfNameOrdinals = 3063, PE_ERR_ExportDirectoryInvalidName = 3064, PE_ERR_ExportNameTableInvalidRVA = 3065, + + // PE Resource directory + PE_ERR_InvalidResourceDirectory = 3080, + PE_ERR_InvalidResourceDirectoryEntry = 3081, + PE_ERR_InvalidResourceDirectoryEntryRVAOffsetToData = 3082, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfReader.cs b/src/LibObjectFile/Dwarf/DwarfReader.cs index 186ba55..6de940a 100644 --- a/src/LibObjectFile/Dwarf/DwarfReader.cs +++ b/src/LibObjectFile/Dwarf/DwarfReader.cs @@ -19,7 +19,7 @@ public sealed class DwarfReader : DwarfReaderWriter internal DwarfReader(DwarfReaderContext context, DwarfFile file, DiagnosticBag diagnostics) : base(file, diagnostics) { - IsReadOnly = context.IsInputReadOnly; + KeepOriginalStreamForSubStreams = context.IsInputReadOnly; AddressSize = context.AddressSize; IsLittleEndian = context.IsLittleEndian; _registeredDIEPerCompilationUnit = new Dictionary(); @@ -32,7 +32,7 @@ internal DwarfReader(DwarfReaderContext context, DwarfFile file, DiagnosticBag d _stackWithLineProgramTable = new Stack(); } - public override bool IsReadOnly { get; } + public override bool KeepOriginalStreamForSubStreams { get; } public DwarfUnitKind DefaultUnitKind { get; internal set; } diff --git a/src/LibObjectFile/Dwarf/DwarfWriter.cs b/src/LibObjectFile/Dwarf/DwarfWriter.cs index af3c975..7e83855 100644 --- a/src/LibObjectFile/Dwarf/DwarfWriter.cs +++ b/src/LibObjectFile/Dwarf/DwarfWriter.cs @@ -14,7 +14,7 @@ internal DwarfWriter(DwarfFile file, bool isLittleEndian, DiagnosticBag diagnost IsLittleEndian = isLittleEndian; } - public override bool IsReadOnly => false; + public override bool KeepOriginalStreamForSubStreams => false; public bool EnableRelocation { get; internal set; } diff --git a/src/LibObjectFile/Elf/ElfReader.cs b/src/LibObjectFile/Elf/ElfReader.cs index e4a7dbf..18a466a 100644 --- a/src/LibObjectFile/Elf/ElfReader.cs +++ b/src/LibObjectFile/Elf/ElfReader.cs @@ -24,7 +24,7 @@ private protected ElfReader(ElfObjectFile objectFile, Stream stream, ElfReaderOp /// public ElfReaderOptions Options { get; } - public override bool IsReadOnly => Options.ReadOnly; + public override bool KeepOriginalStreamForSubStreams => Options.ReadOnly; internal abstract void Read(); diff --git a/src/LibObjectFile/Elf/ElfWriter.cs b/src/LibObjectFile/Elf/ElfWriter.cs index f364fab..14d9efe 100644 --- a/src/LibObjectFile/Elf/ElfWriter.cs +++ b/src/LibObjectFile/Elf/ElfWriter.cs @@ -20,7 +20,7 @@ private protected ElfWriter(ElfObjectFile objectFile, Stream stream) : base(obje internal abstract void Write(); - public override bool IsReadOnly => false; + public override bool KeepOriginalStreamForSubStreams => false; internal static ElfWriter Create(ElfObjectFile objectFile, Stream stream) { diff --git a/src/LibObjectFile/IO/StreamExtensions.cs b/src/LibObjectFile/IO/StreamExtensions.cs index bee80ae..8fbfc78 100644 --- a/src/LibObjectFile/IO/StreamExtensions.cs +++ b/src/LibObjectFile/IO/StreamExtensions.cs @@ -2,11 +2,14 @@ // 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 System; using System.Buffers; using System.Buffers.Binary; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; namespace LibObjectFile.IO; @@ -230,4 +233,102 @@ public static void WriteStringUTF8NullTerminated(this Stream stream, string text ArrayPool.Shared.Return(buffer); } } + + /// + /// Tries to read an element of type with a specified size. + /// + /// Type of the element to read. + /// Size of the element to read (might be smaller or bigger). + /// The data read. + /// true if reading was successful. false otherwise. + public static unsafe bool TryReadData(this Stream stream, int sizeToRead, out T data) where T : unmanaged + { + if (sizeToRead <= 0) throw new ArgumentOutOfRangeException(nameof(sizeToRead)); + + int dataByteCount = sizeof(T); + int byteRead; + + // If we are requested to read more data than the sizeof(T) + // we need to read it to an intermediate buffer before transferring it to T data + if (sizeToRead > dataByteCount) + { + var buffer = ArrayPool.Shared.Rent(sizeToRead); + var span = new Span(buffer, 0, sizeToRead); + byteRead = stream.Read(span); + data = MemoryMarshal.Cast(span)[0]; + ArrayPool.Shared.Return(buffer); + } + else + { + // Clear the data if the size requested is less than the expected struct to read + if (sizeToRead < dataByteCount) + { + data = default; + } + + Unsafe.SkipInit(out data); + byteRead = stream.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data, 1))); + } + return byteRead == sizeToRead; + } + + public static SubStream ReadAsSubStream(this Stream stream, ulong size, DiagnosticBag diagnostics) + { + var position = stream.Position; + if (position + (long)size > stream.Length) + { + if (position < stream.Length) + { + size = stream.Position < stream.Length ? (ulong)(stream.Length - stream.Position) : 0; + diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Expecting to slice {size} bytes at offset {position} while remaining length is {size}"); + } + else + { + position = stream.Length; + size = 0; + diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Position of slice {position} is outside of the stream length {stream.Length} in bytes"); + } + } + + return new SubStream(stream, position, (long)size); + } + + public static MemoryStream ReadAsMemoryStream(this Stream stream, ulong size, DiagnosticBag diagnostics) + { + var memoryStream = new MemoryStream((int)size); + if (size == 0) return memoryStream; + + memoryStream.SetLength((long)size); + + var buffer = memoryStream.GetBuffer(); + var span = new Span(buffer, 0, (int)size); + var readSize = stream.Read(span); + + if ((int)size != readSize) + { + diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Expecting to read {size} bytes at offset {stream.Position}"); + } + + return memoryStream; + } + + /// + /// Reads from the current bytes and return the data as + /// a if is false otherwise as a + /// . + /// + /// Size of the data to read. + /// A if is false otherwise as a + /// . + public static Stream ReadAsStream(this Stream sourceStream, ulong size, DiagnosticBag diagnostics, bool keepOriginalStreamForSubStreams) + { + if (keepOriginalStreamForSubStreams) + { + var stream = sourceStream.ReadAsSubStream(size, diagnostics); + sourceStream.Seek(stream.Length, SeekOrigin.Current); + return stream; + } + + return sourceStream.ReadAsMemoryStream(size, diagnostics); + } } \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs index ea8b8af..32ce896 100644 --- a/src/LibObjectFile/ObjectFileReaderWriter.cs +++ b/src/LibObjectFile/ObjectFileReaderWriter.cs @@ -52,7 +52,7 @@ public ulong Length /// /// Gets a boolean indicating if this reader is operating in read-only mode. /// - public abstract bool IsReadOnly { get; } + public abstract bool KeepOriginalStreamForSubStreams { get; } public bool IsLittleEndian { get; protected set; } @@ -168,56 +168,19 @@ public void WriteStringUTF8NullTerminated(string text) /// The data read. /// true if reading was successful. false otherwise. public unsafe bool TryReadData(int sizeToRead, out T data) where T : unmanaged - { - if (sizeToRead <= 0) throw new ArgumentOutOfRangeException(nameof(sizeToRead)); - - int dataByteCount = sizeof(T); - int byteRead; - - // If we are requested to read more data than the sizeof(T) - // we need to read it to an intermediate buffer before transferring it to T data - if (sizeToRead > dataByteCount) - { - var buffer = ArrayPool.Shared.Rent(sizeToRead); - var span = new Span(buffer, 0, sizeToRead); - byteRead = Stream.Read(span); - data = MemoryMarshal.Cast(span)[0]; - ArrayPool.Shared.Return(buffer); - } - else - { - // Clear the data if the size requested is less than the expected struct to read - if (sizeToRead < dataByteCount) - { - data = default; - } - - Unsafe.SkipInit(out data); - byteRead = Stream.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data, 1))); - } - return byteRead == sizeToRead; - } + => Stream.TryReadData(sizeToRead, out data); /// /// Reads from the current bytes and return the data as - /// a if is false otherwise as a + /// a if is false otherwise as a /// . /// /// Size of the data to read. - /// A if is false otherwise as a + /// A if is false otherwise as a /// . public Stream ReadAsStream(ulong size) - { - if (IsReadOnly) - { - var stream = ReadAsSubStream(size); - Stream.Seek(stream.Length, SeekOrigin.Current); - return stream; - } - - return ReadAsMemoryStream(size); - } - + => Stream.ReadAsStream(size, Diagnostics, KeepOriginalStreamForSubStreams); + /// /// Writes to the and current position from the specified buffer. /// @@ -281,44 +244,4 @@ public void Write(Stream inputStream, ulong size = 0, int bufferSize = 4096) throw new InvalidOperationException("Unable to write stream entirely"); } } - - private SubStream ReadAsSubStream(ulong size) - { - var position = Stream.Position; - if (position + (long)size > Stream.Length) - { - if (position < Stream.Length) - { - size = Stream.Position < Stream.Length ? (ulong)(Stream.Length - Stream.Position) : 0; - Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Expecting to slice {size} bytes at offset {position} while remaining length is {size}"); - } - else - { - position = Stream.Length; - size = 0; - Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Position of slice {position} is outside of the stream length {Stream.Length} in bytes"); - } - } - - return new SubStream(Stream, position, (long)size); - } - - private MemoryStream ReadAsMemoryStream(ulong size) - { - var memoryStream = new MemoryStream((int)size); - if (size == 0) return memoryStream; - - memoryStream.SetLength((long)size); - - var buffer = memoryStream.GetBuffer(); - var span = new Span(buffer, 0, (int)size); - var readSize = Stream.Read(span); - - if ((int)size != readSize) - { - Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Expecting to read {size} bytes at offset {Stream.Position}"); - } - - return memoryStream; - } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs new file mode 100644 index 0000000..dfbf466 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs @@ -0,0 +1,109 @@ +// 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.PE.Internal; + +namespace LibObjectFile.PE; + +/// +/// Represents a resource data entry in a PE file. +/// +public sealed class PEResourceDataEntry : PEResourceEntry +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private object? _data; + + /// + /// Initializes a new instance of the class with the specified name. + /// + /// The name of the resource data entry. + public PEResourceDataEntry(string name) : base(name) + { + Data = Stream.Null; + } + + /// + /// Initializes a new instance of the class with the specified ID. + /// + /// The ID of the resource data entry. + public PEResourceDataEntry(PEResourceId id) : base(id) + { + Data = Stream.Null; + } + + /// + /// Gets or sets the code page used for encoding the data. + /// + /// + /// The code page is used to encode the data when the data is a string. + /// + public Encoding? CodePage { get; set; } + + /// + /// Gets or sets the data associated with the resource data entry. + /// + /// + /// The data can be a string, a stream, or a byte array. + /// + public object? Data + { + get => _data; + set + { + 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; + } + } + + private protected override unsafe uint ComputeSize() + { + uint dataSize = 0; + + if (Data is string text) + { + dataSize = (uint)(CodePage?.GetByteCount(text) ?? text.Length * 2); + } + else if (Data is Stream stream) + { + dataSize = (uint)stream.Length; + } + else if (Data is byte[] buffer) + { + dataSize = (uint)buffer.Length; + } + + return (uint)(sizeof(RawImageResourceDataEntry) + dataSize); + } + + protected override bool PrintMembers(StringBuilder builder) + { + if (base.PrintMembers(builder)) + { + builder.Append(", "); + } + + switch (Data) + { + 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; + } + + return true; + } +} diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs new file mode 100644 index 0000000..1cef981 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs @@ -0,0 +1,207 @@ +// 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 +{ + /// + /// Initializes a new instance of the class. + /// + public PEResourceDirectory() : base(PEDataDirectoryKind.Resource) + { + Root = new() + { + Parent = this + }; + } + + /// + /// Gets the root resource directory entry. + /// + public PEResourceDirectoryEntry Root { get; } + + /// + protected override uint ComputeHeaderSize(PEVisitorContext context) + { + var size = Root.ComputeFullSize(); + size = (uint)AlignHelper.AlignUp(size, 4); + return 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; + } + + for (int i = 0; i < data.NumberOfNamedEntries + data.NumberOfIdEntries; i++) + { + var entry = spanEntries[i]; + ReadEntry(reader, currentDirectory, entry); + + if (reader.Diagnostics.HasErrors) + { + return; + } + } + } + + private unsafe void ReadEntry(PEImageReader reader, PEResourceDirectoryEntry parent, RawImageResourceDirectoryEntry rawEntry) + { + 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 + { + var dataEntry = (PEResourceDataEntry)entry; + RawImageResourceDataEntry rawDataEntry; + if (!reader.TryReadData(sizeof(RawImageResourceDataEntry), out rawDataEntry)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource data entry at position {reader.Position}"); + return; + } + + 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); + } + } + } + + /// + 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 new file mode 100644 index 0000000..e04f6ff --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs @@ -0,0 +1,207 @@ +// 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.ComponentModel; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using LibObjectFile.Collections; +using LibObjectFile.PE.Internal; + +namespace LibObjectFile.PE; + +/// +/// Represents a directory entry in the Portable Executable (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 +{ + [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) + { + Entries = new ObjectList(this); + } + + /// + /// Gets or sets the time stamp of the resource directory entry. + /// + public DateTime TimeDateStamp { get; set; } + + /// + /// Gets or sets the major version of the resource directory entry. + /// + public uint MajorVersion { get; set; } + + /// + /// Gets or sets the minor version of the resource directory entry. + /// + public uint MinorVersion { get; set; } + + /// + /// 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); + + /// + /// Tries to get the resource entry with the specified name from 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) + { + if (_nameToIndex.TryGetValue(name, out var index)) + { + entry = Entries[index]; + return true; + } + + entry = null; + return false; + } + + /// + /// 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)) + { + entry = Entries[index]; + return true; + } + + entry = null; + return false; + } + + /// + /// Adds the specified resource entry to the directory. + /// + /// The resource entry to add. + public void Add(PEResourceEntry entry) => Entries.Add(entry); + + /// + /// Gets an enumerator that iterates through the resource entries in the directory. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public List.Enumerator GetEnumerator() => Entries.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + { + return Entries.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)Entries).GetEnumerator(); + } + + private static void AddingEntry(ObjectElement parent, int index, PEResourceEntry entry) + { + var directory = (PEResourceDirectoryEntry)parent; + if (entry.Name != null) + { + directory._nameToIndex.Add(entry.Name, index); + } + else + { + directory._idToIndex.Add(entry.Id, index); + } + } + + private static void RemovingEntry(ObjectElement parent, PEResourceEntry entry) + { + var directory = (PEResourceDirectoryEntry)parent; + if (entry.Name != null) + { + directory._nameToIndex.Remove(entry.Name); + } + else + { + directory._idToIndex.Remove(entry.Id); + } + } + + private static void UpdatingEntry(ObjectElement parent, int index, PEResourceEntry previousEntry, PEResourceEntry entry) + { + RemovingEntry(parent, previousEntry); + AddingEntry(parent, index, entry); + } + + private protected override unsafe uint ComputeSize() + { + var entries = CollectionsMarshal.AsSpan(Entries.UnsafeList); + uint size = (uint)sizeof(RawImageResourceDirectory); + foreach (var entry in entries) + { + size += entry.ComputeFullSize(); + } + + return size; + } + + protected override bool PrintMembers(StringBuilder builder) + { + if (base.PrintMembers(builder)) + { + builder.Append(", "); + } + + builder.Append($"Entries[{Entries.Count}] , TimeDateStamp = {TimeDateStamp}, MajorVersion = {MajorVersion}, MinorVersion = {MinorVersion}"); + + return false; + } +} diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs new file mode 100644 index 0000000..8241942 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs @@ -0,0 +1,147 @@ +// 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; + +namespace LibObjectFile.PE; + +/// +/// Represents an abstract base class for a PE resource entry. +/// +public abstract class PEResourceEntry : ObjectElement +{ + /// + /// Initializes a new instance of the class with a default ID of -1. + /// + private protected PEResourceEntry() + { + Id = new(-1); + } + + /// + /// 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; + } +} diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceId.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceId.cs new file mode 100644 index 0000000..5a50d0b --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceId.cs @@ -0,0 +1,267 @@ +// 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; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace LibObjectFile.PE; + +/// +/// Represents a resource identifier in a PE file. +/// +/// +/// This struct is used to identify different types of resources in a PE file. +/// It provides methods for comparing, converting to string, and retrieving well-known resource type names. +/// +[SuppressMessage("ReSharper", "InconsistentNaming")] +public readonly struct PEResourceId : IEquatable, IComparable, IComparable +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly int _id; + + private static readonly Dictionary WellKnownResourceIDs = new Dictionary() + { + { 1, "RT_CURSOR" }, + { 2, "RT_BITMAP" }, + { 3, "RT_ICON" }, + { 4, "RT_MENU" }, + { 5, "RT_DIALOG" }, + { 6, "RT_STRING" }, + { 7, "RT_FONTDIR" }, + { 8, "RT_FONT" }, + { 9, "RT_ACCELERATOR" }, + { 10, "RT_RCDATA" }, + { 11, "RT_MESSAGETABLE" }, + { 12, "RT_GROUP_CURSOR" }, + { 14, "RT_GROUP_ICON" }, + { 16, "RT_VERSION" }, + { 17, "RT_DLGINCLUDE" }, + { 19, "RT_PLUGPLAY" }, + { 20, "RT_VXD" }, + { 21, "RT_ANICURSOR" }, + { 22, "RT_ANIICON" }, + { 23, "RT_HTML" }, + { 24, "RT_MANIFEST" } + }; + + /// + /// Represents the RT_CURSOR resource type. + /// + public static PEResourceId RT_CURSOR => new(1); + + /// + /// Represents the RT_BITMAP resource type. + /// + public static PEResourceId RT_BITMAP => new(2); + + /// + /// Represents the RT_ICON resource type. + /// + public static PEResourceId RT_ICON => new(3); + + /// + /// Represents the RT_MENU resource type. + /// + public static PEResourceId RT_MENU => new(4); + + /// + /// Represents the RT_DIALOG resource type. + /// + public static PEResourceId RT_DIALOG => new(5); + + /// + /// Represents the RT_STRING resource type. + /// + public static PEResourceId RT_STRING => new(6); + + /// + /// Represents the RT_FONTDIR resource type. + /// + public static PEResourceId RT_FONTDIR => new(7); + + /// + /// Represents the RT_FONT resource type. + /// + public static PEResourceId RT_FONT => new(8); + + /// + /// Represents the RT_ACCELERATOR resource type. + /// + public static PEResourceId RT_ACCELERATOR => new(9); + + /// + /// Represents the RT_RCDATA resource type. + /// + public static PEResourceId RT_RCDATA => new(10); + + /// + /// Represents the RT_MESSAGETABLE resource type. + /// + public static PEResourceId RT_MESSAGETABLE => new(11); + + /// + /// Represents the RT_GROUP_CURSOR resource type. + /// + public static PEResourceId RT_GROUP_CURSOR => new(12); + + /// + /// Represents the RT_GROUP_ICON resource type. + /// + public static PEResourceId RT_GROUP_ICON => new(14); + + /// + /// Represents the RT_VERSION resource type. + /// + public static PEResourceId RT_VERSION => new(16); + + /// + /// Represents the RT_DLGINCLUDE resource type. + /// + public static PEResourceId RT_DLGINCLUDE => new(17); + + /// + /// Represents the RT_PLUGPLAY resource type. + /// + public static PEResourceId RT_PLUGPLAY => new(19); + + /// + /// Represents the RT_VXD resource type. + /// + public static PEResourceId RT_VXD => new(20); + + /// + /// Represents the RT_ANICURSOR resource type. + /// + public static PEResourceId RT_ANICURSOR => new(21); + + /// + /// Represents the RT_ANIICON resource type. + /// + public static PEResourceId RT_ANIICON => new(22); + + /// + /// Represents the RT_HTML resource type. + /// + public static PEResourceId RT_HTML => new(23); + + /// + /// Represents the RT_MANIFEST resource type. + /// + public static PEResourceId RT_MANIFEST => new(24); + + /// + /// Initializes a new instance of the struct. + /// + /// The resource identifier. + public PEResourceId(int id) + { + _id = id; + } + + /// + /// Gets the value of the resource identifier. + /// + public int Value => _id; + + /// + /// Converts the resource identifier to its hexadecimal string representation. + /// + /// The hexadecimal string representation of the resource identifier. + public override string ToString() => $"0x{_id:X}"; + + /// + /// Tries to retrieve the well-known resource type name associated with the resource identifier. + /// + /// The well-known resource type name, if found; otherwise, null. + /// true if the well-known resource type name is found; otherwise, false. + public bool TryGetWellKnownTypeName([NotNullWhen(true)] out string? name) => WellKnownResourceIDs.TryGetValue(_id, out name); + + /// + /// Determines whether the current resource identifier is equal to another resource identifier. + /// + /// The resource identifier to compare with. + /// true if the current resource identifier is equal to the other resource identifier; otherwise, false. + public bool Equals(PEResourceId other) => _id == other._id; + + /// + /// Determines whether the current resource identifier is equal to another object. + /// + /// The object to compare with. + /// true if the current resource identifier is equal to the other object; otherwise, false. + public override bool Equals(object? obj) => obj is PEResourceId other && Equals(other); + + /// + /// Gets the hash code of the resource identifier. + /// + /// The hash code of the resource identifier. + public override int GetHashCode() => _id; + + /// + /// Determines whether two resource identifiers are equal. + /// + /// The first resource identifier to compare. + /// The second resource identifier to compare. + /// true if the two resource identifiers are equal; otherwise, false. + public static bool operator ==(PEResourceId left, PEResourceId right) => left.Equals(right); + + /// + /// Determines whether two resource identifiers are not equal. + /// + /// The first resource identifier to compare. + /// The second resource identifier to compare. + /// true if the two resource identifiers are not equal; otherwise, false. + public static bool operator !=(PEResourceId left, PEResourceId right) => !left.Equals(right); + + /// + /// Compares the current resource identifier with another resource identifier. + /// + /// The resource identifier to compare with. + /// A value indicating the relative order of the resource identifiers. + public int CompareTo(PEResourceId other) => _id.CompareTo(other._id); + + /// + /// Compares the current resource identifier with another object. + /// + /// The object to compare with. + /// A value indicating the relative order of the resource identifiers. + public int CompareTo(object? obj) + { + if (obj is null) return 1; + return obj is PEResourceId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(PEResourceId)}"); + } + + /// + /// Determines whether one resource identifier is less than another resource identifier. + /// + /// The first resource identifier to compare. + /// The second resource identifier to compare. + /// true if the first resource identifier is less than the second resource identifier; otherwise, false. + public static bool operator <(PEResourceId left, PEResourceId right) => left.CompareTo(right) < 0; + + /// + /// Determines whether one resource identifier is greater than another resource identifier. + /// + /// The first resource identifier to compare. + /// The second resource identifier to compare. + /// true if the first resource identifier is greater than the second resource identifier; otherwise, false. + public static bool operator >(PEResourceId left, PEResourceId right) => left.CompareTo(right) > 0; + + /// + /// Determines whether one resource identifier is less than or equal to another resource identifier. + /// + /// The first resource identifier to compare. + /// The second resource identifier to compare. + /// true if the first resource identifier is less than or equal to the second resource identifier; otherwise, false. + public static bool operator <=(PEResourceId left, PEResourceId right) => left.CompareTo(right) <= 0; + + /// + /// Determines whether one resource identifier is greater than or equal to another resource identifier. + /// + /// The first resource identifier to compare. + /// The second resource identifier to compare. + /// true if the first resource identifier is greater than or equal to the second resource identifier; otherwise, false. + public static bool operator >=(PEResourceId left, PEResourceId right) => left.CompareTo(right) >= 0; +} diff --git a/src/LibObjectFile/PE/Internal/RawImageResourceDirectoryEntry.cs b/src/LibObjectFile/PE/Internal/RawImageResourceDirectoryEntry.cs new file mode 100644 index 0000000..fefdd1e --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageResourceDirectoryEntry.cs @@ -0,0 +1,31 @@ +// 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.Internal; + +#pragma warning disable CS0649 +internal struct RawImageResourceDirectoryEntry +{ + public uint NameOrId; + public uint OffsetToDataOrDirectoryEntry; +} + +internal struct RawImageResourceDataEntry +{ + public RVA OffsetToData; + public uint Size; + public uint CodePage; + public uint Reserved; +} + +internal struct RawImageResourceDirectory +{ + public uint Characteristics; + public uint TimeDateStamp; + public ushort MajorVersion; + public ushort MinorVersion; + public ushort NumberOfNamedEntries; + public ushort NumberOfIdEntries; + // IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[]; +} diff --git a/src/LibObjectFile/PE/PEImageReader.cs b/src/LibObjectFile/PE/PEImageReader.cs index 9ae50c7..c12fade 100644 --- a/src/LibObjectFile/PE/PEImageReader.cs +++ b/src/LibObjectFile/PE/PEImageReader.cs @@ -18,7 +18,7 @@ internal PEImageReader(PEFile file, Stream stream, PEImageReaderOptions readerOp public PEImageReaderOptions Options { get; } - public override bool IsReadOnly => Options.IsReadOnly; + public override bool KeepOriginalStreamForSubStreams => Options.IsReadOnly; public PEVisitorContext VisitorContext { get; } diff --git a/src/LibObjectFile/PE/PEImageWriter.cs b/src/LibObjectFile/PE/PEImageWriter.cs index 6571d96..2f13d65 100644 --- a/src/LibObjectFile/PE/PEImageWriter.cs +++ b/src/LibObjectFile/PE/PEImageWriter.cs @@ -14,5 +14,5 @@ internal PEImageWriter(PEFile file, Stream stream) : base(file, stream) public PEFile PEFile => (PEFile)base.File; - public override bool IsReadOnly => false; + public override bool KeepOriginalStreamForSubStreams => false; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index cdf2d98..65eb3ae 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -87,12 +87,6 @@ public override void Write(PEImageWriter writer) throw new NotImplementedException(); } - /// - protected override void PrintName(StringBuilder builder) - { - builder.Append(Name); - } - /// protected override bool PrintMembers(StringBuilder builder) { diff --git a/src/LibObjectFile/PE/PESectionDataLink.cs b/src/LibObjectFile/PE/PESectionDataLink.cs index 6bd5b42..8093058 100644 --- a/src/LibObjectFile/PE/PESectionDataLink.cs +++ b/src/LibObjectFile/PE/PESectionDataLink.cs @@ -12,7 +12,7 @@ namespace LibObjectFile.PE; [DebuggerDisplay("{ToString(),nq}")] public readonly struct PESectionDataLink : RVALink { - public PESectionDataLink(PESectionData? sectionData, uint rvo) + public PESectionDataLink(PESectionData? sectionData, RVO rvo) { SectionData = sectionData; RVO = rvo; From ed167409757a446c7c94913907d7b050b0145572 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 22 Sep 2024 10:57:28 +0200 Subject: [PATCH 36/87] Add exception directory --- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 6 + .../PE/DataDirectory/PEDataDirectory.cs | 43 --- .../PE/DataDirectory/PEExceptionDirectory.cs | 259 ++++++++++++++++++ .../DataDirectory/PEExceptionFunctionEntry.cs | 25 ++ .../PEExceptionFunctionEntryArm.cs | 32 +++ .../PEExceptionFunctionEntryX86.cs | 39 +++ .../Internal/RawExceptionFunctionEntryARM.cs | 13 + .../Internal/RawExceptionFunctionEntryX86.cs | 14 + 8 files changed, 388 insertions(+), 43 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntry.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryArm.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryX86.cs create mode 100644 src/LibObjectFile/PE/Internal/RawExceptionFunctionEntryARM.cs create mode 100644 src/LibObjectFile/PE/Internal/RawExceptionFunctionEntryX86.cs diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 95b37ca..4f25e7c 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -154,4 +154,10 @@ public enum DiagnosticId PE_ERR_InvalidResourceDirectory = 3080, PE_ERR_InvalidResourceDirectoryEntry = 3081, PE_ERR_InvalidResourceDirectoryEntryRVAOffsetToData = 3082, + + + // PE Exception directory + PE_ERR_InvalidExceptionDirectory_Entries = 3100, + PE_ERR_InvalidExceptionDirectory_Entry = 3101, + PE_ERR_InvalidExceptionDirectory_Size = 3102, } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index 316bda2..8f1d442 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -113,49 +113,6 @@ internal static PEDataDirectory Create(PEDataDirectoryKind kind) } } -public sealed class PEResourceDirectory : PEDataDirectory -{ - public PEResourceDirectory() : base(PEDataDirectoryKind.Resource) - { - } - protected override uint ComputeHeaderSize(PEVisitorContext context) - { - return 0; - } - - public override void Read(PEImageReader reader) - { - // TBD - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} - -public sealed class PEExceptionDirectory : PEDataDirectory -{ - public PEExceptionDirectory() : base(PEDataDirectoryKind.Exception) - { - } - protected override uint ComputeHeaderSize(PEVisitorContext context) - { - return 0; - } - - - public override void Read(PEImageReader reader) - { - // TBD - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} - public sealed class PEDebugDirectory : PEDataDirectory { public PEDebugDirectory() : base(PEDataDirectoryKind.Debug) diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs new file mode 100644 index 0000000..c974bdd --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs @@ -0,0 +1,259 @@ +// 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.Diagnostics; +using System.Reflection.PortableExecutable; +using System.Runtime.InteropServices; +using LibObjectFile.Diagnostics; +using LibObjectFile.PE.Internal; + +namespace LibObjectFile.PE; + +/// +/// Represents the exception directory in a PE file. +/// +public sealed class PEExceptionDirectory : PEDataDirectory +{ + /// + /// Initializes a new instance of the class. + /// + public PEExceptionDirectory() : base(PEDataDirectoryKind.Exception) + { + Entries = new List(); + } + + /// + /// Gets the list of entries in the exception directory. + /// + public List Entries { get; } + + /// + protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) + { + var machine = context.File.CoffHeader.Machine; + uint entrySize; + switch (machine) + { + case Machine.Amd64: + case Machine.I386: + entrySize = (uint)sizeof(RawExceptionFunctionEntryX86); + break; + case Machine.Arm: + case Machine.Arm64: + entrySize = (uint)sizeof(RawExceptionFunctionEntryARM); + break; + default: + // We don't read the exception directory for other architectures + // It will be added as raw content as part of this directory + if (Entries.Count > 0) + { + context.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entries, $"Unsupported entries in exception directory for machine {machine}"); + } + return 0; + } + + // TODO: Should be part of validate + + //foreach (var entry in Entries) + //{ + // switch (machine) + // { + // case Machine.Amd64: + // case Machine.I386: + // if (entry is not PEExceptionFunctionEntryX86) + // { + // context.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid entry {entry.GetType().Name} in exception directory for machine {machine}"); + // } + // break; + // case Machine.Arm: + // case Machine.Arm64: + // if (entry is not PEExceptionFunctionEntryARM) + // { + // context.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid entry {entry.GetType().Name} in exception directory for machine {machine}"); + // } + // break; + // } + //} + + return entrySize * (uint)Entries.Count; + } + + /// + public override unsafe void Read(PEImageReader reader) + { + uint entrySize = 0; + + var machine = reader.File.CoffHeader.Machine; + switch (machine) + { + case Machine.Amd64: + case Machine.I386: + entrySize = (uint)sizeof(RawExceptionFunctionEntryX86); + break; + case Machine.Arm: + case Machine.Arm64: + entrySize = (uint)sizeof(RawExceptionFunctionEntryARM); + break; + default: + // We don't read the exception directory for other architectures + // It will be added as raw content as part of this directory + return; + } + + var size = (long)Size; + + var (result, remainder) = Math.DivRem(size, entrySize); + + if (remainder != 0) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Size, $"Invalid size {size} for exception directory"); + return; + } + + + reader.Position = Position; + + var buffer = ArrayPool.Shared.Rent((int)size); + try + { + var span = buffer.AsSpan().Slice(0, (int)size); + int read = reader.Read(span); + if (read != size) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Size, $"Invalid size {size} for exception directory"); + return; + } + + switch (machine) + { + case Machine.Amd64: + case Machine.I386: + ReadEntriesX86(MemoryMarshal.Cast(span)); + break; + case Machine.Arm: + case Machine.Arm64: + ReadEntriesArm(MemoryMarshal.Cast(span)); + break; + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + var headerSize = ComputeHeaderSize(reader); + Debug.Assert(headerSize == size); + HeaderSize = headerSize; + } + + internal override void Bind(PEImageReader reader) + { + var peFile = reader.File; + foreach (var entry in Entries) + { + if (entry is PEExceptionFunctionEntryX86 entryX86) + { + if (!peFile.TryFindContainerByRVA((RVA)(uint)entryX86.BeginAddress.RVO, out var beginAddressContainer)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid begin address {entryX86.BeginAddress.RVO} in exception directory"); + return; + } + + var beginAddressSectionData = beginAddressContainer as PESectionData; + if (beginAddressSectionData is null) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid begin address {entryX86.BeginAddress.RVO} in exception directory. The container found is not a section data"); + return; + } + + // Need to subtract 1 to get the end address + if (!peFile.TryFindContainerByRVA((RVA)(uint)entryX86.EndAddress.RVO - 1, out var endAddressContainer)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid end address {entryX86.EndAddress.RVO} in exception directory"); + return; + } + + var endAddressSectionData = endAddressContainer as PESectionData; + if (endAddressSectionData is null) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid end address {entryX86.EndAddress.RVO} in exception directory. The container found is not a section data"); + return; + } + + + if (!peFile.TryFindContainerByRVA((RVA)(uint)entryX86.UnwindInfoAddress.RVO, out var unwindInfoAddressContainer)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid unwind info address {entryX86.UnwindInfoAddress.RVO} in exception directory"); + return; + } + + var unwindInfoAddressSectionData = unwindInfoAddressContainer as PESectionData; + if (unwindInfoAddressSectionData is null) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid unwind info address {entryX86.UnwindInfoAddress.RVO} in exception directory. The container found is not a section data"); + return; + } + + + entryX86.BeginAddress = new PESectionDataLink(beginAddressSectionData, (uint)entryX86.BeginAddress.RVO - beginAddressSectionData.RVA); + entryX86.EndAddress = new PESectionDataLink(endAddressSectionData, (uint)entryX86.EndAddress.RVO - endAddressSectionData.RVA); + entryX86.UnwindInfoAddress = new PESectionDataLink(unwindInfoAddressSectionData, (uint)entryX86.UnwindInfoAddress.RVO - unwindInfoAddressSectionData.RVA); + } + else if (entry is PEExceptionFunctionEntryArm entryARM) + { + if (!peFile.TryFindContainerByRVA((RVA)(uint)entryARM.BeginAddress.RVO, out var beginAddressContainer)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid begin address {entryARM.BeginAddress.RVO} in exception directory"); + return; + } + + var beginAddressSectionData = beginAddressContainer as PESectionData; + if (beginAddressSectionData is null) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid begin address {entryARM.BeginAddress.RVO} in exception directory. The container found is not a section data"); + return; + } + + entryARM.BeginAddress = new PESectionDataLink(beginAddressSectionData, (uint)entryARM.BeginAddress.RVO - beginAddressSectionData.RVA); + } + } + + } + + /// + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } + + private void ReadEntriesArm(Span rawEntries) + { + foreach (ref var rawEntry in rawEntries) + { + // Create entries with links to data but encode the RVO as the RVA until we bind it to the actual section data + var entry = new PEExceptionFunctionEntryArm( + new PESectionDataLink(PEStreamSectionData.Empty, (RVO)(uint)rawEntry.BeginAddress), + rawEntry.UnwindData + ); + Entries.Add(entry); + } + } + + private void ReadEntriesX86(Span rawEntries) + { + foreach (ref var rawEntry in rawEntries) + { + // Create entries with links to data but encode the RVO as the RVA until we bind it to the actual section data + var entry = new PEExceptionFunctionEntryX86( + new PESectionDataLink(PEStreamSectionData.Empty, (RVO)(uint)rawEntry.BeginAddress), + new PESectionDataLink(PEStreamSectionData.Empty, (RVO)(uint)rawEntry.EndAddress), + new PESectionDataLink(PEStreamSectionData.Empty, (RVO)(uint)rawEntry.UnwindInfoAddress) + ); + Entries.Add(entry); + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntry.cs new file mode 100644 index 0000000..3438a57 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntry.cs @@ -0,0 +1,25 @@ +// 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 an exception function entry in a Portable Executable (PE) file. +/// +public abstract class PEExceptionFunctionEntry +{ + /// + /// Initializes a new instance of the class with the specified begin address. + /// + /// The begin address of the exception function entry. + internal PEExceptionFunctionEntry(PESectionDataLink beginAddress) + { + BeginAddress = beginAddress; + } + + /// + /// Gets or sets the begin address of the exception function entry. + /// + public PESectionDataLink BeginAddress { get; set; } +} diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryArm.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryArm.cs new file mode 100644 index 0000000..91efda9 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryArm.cs @@ -0,0 +1,32 @@ +// 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 an ARM exception function entry in a Portable Executable (PE) file. +/// +public sealed class PEExceptionFunctionEntryArm : PEExceptionFunctionEntry +{ + /// + /// Initializes a new instance of the class with the specified begin address and unwind data. + /// + /// The begin address of the exception function entry. + /// The unwind data. + public PEExceptionFunctionEntryArm(PESectionDataLink beginAddress, uint unwindData) : base(beginAddress) + { + UnwindData = unwindData; + } + + /// + /// Gets or sets the unwind data. + /// + public uint UnwindData { get; set; } + + /// + public override string ToString() + { + return $"{nameof(BeginAddress)} = {BeginAddress.RVA()}, {nameof(UnwindData)} = 0x{UnwindData:X}"; + } +} diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryX86.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryX86.cs new file mode 100644 index 0000000..fe7a4a3 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryX86.cs @@ -0,0 +1,39 @@ +// 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 an exception function entry for the x86 architecture in a Portable Executable (PE) file. +/// +public sealed class PEExceptionFunctionEntryX86 : PEExceptionFunctionEntry +{ + /// + /// Initializes a new instance of the class with the specified begin address, end address, and unwind info address. + /// + /// The begin address of the exception function entry. + /// The end address of the exception function entry. + /// The unwind info address of the exception function entry. + public PEExceptionFunctionEntryX86(PESectionDataLink beginAddress, PESectionDataLink endAddress, PESectionDataLink unwindInfoAddress) : base(beginAddress) + { + EndAddress = endAddress; + UnwindInfoAddress = unwindInfoAddress; + } + + /// + /// Gets or sets the end address of the exception function entry. + /// + public PESectionDataLink EndAddress { get; set; } + + /// + /// Gets or sets the unwind info address of the exception function entry. + /// + public PESectionDataLink UnwindInfoAddress { get; set; } + + /// + public override string ToString() + { + return $"{nameof(BeginAddress)} = {BeginAddress.RVA()}, {nameof(EndAddress)} = {EndAddress.RVA()}, {nameof(UnwindInfoAddress)} = {UnwindInfoAddress.RVA()}"; + } +} diff --git a/src/LibObjectFile/PE/Internal/RawExceptionFunctionEntryARM.cs b/src/LibObjectFile/PE/Internal/RawExceptionFunctionEntryARM.cs new file mode 100644 index 0000000..3149d03 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawExceptionFunctionEntryARM.cs @@ -0,0 +1,13 @@ +// 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.Internal; + +#pragma warning disable CS0649 + +internal struct RawExceptionFunctionEntryARM +{ + public RVA BeginAddress; + public uint UnwindData; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawExceptionFunctionEntryX86.cs b/src/LibObjectFile/PE/Internal/RawExceptionFunctionEntryX86.cs new file mode 100644 index 0000000..cbcfb3a --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawExceptionFunctionEntryX86.cs @@ -0,0 +1,14 @@ +// 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.Internal; + +#pragma warning disable CS0649 + +internal struct RawExceptionFunctionEntryX86 +{ + public RVA BeginAddress; + public RVA EndAddress; + public RVA UnwindInfoAddress; +} \ No newline at end of file From 1a6a69b9ca6b50a2d39c24594a6347438f9e217c Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 22 Sep 2024 14:32:37 +0200 Subject: [PATCH 37/87] Add PESecurityDirectory --- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 3 + src/LibObjectFile/LibObjectFile.csproj | 1 + .../DataDirectory/PEArchitectureDirectory.cs | 34 +++ .../DataDirectory/PEBoundImportDirectory.cs | 28 +++ .../PE/DataDirectory/PEClrMetadata.cs | 29 +++ .../PE/DataDirectory/PEDataDirectory.cs | 197 ------------------ .../PE/DataDirectory/PEDebugDirectory.cs | 29 +++ .../DataDirectory/PEDelayImportDirectory.cs | 28 +++ .../PE/DataDirectory/PEDirectoryTable.cs | 28 ++- .../DataDirectory/PEGlobalPointerDirectory.cs | 33 +++ .../PE/DataDirectory/PELoadConfigDirectory.cs | 29 +++ .../PE/DataDirectory/PESecurityCertificate.cs | 67 ++++++ .../PESecurityCertificateRevision.cs | 21 ++ .../PESecurityCertificateType.cs | 31 +++ .../PE/DataDirectory/PESecurityDirectory.cs | 120 +++++++++++ .../PE/DataDirectory/PETlsDirectory.cs | 29 +++ src/LibObjectFile/PE/PEExtraData.cs | 15 ++ src/LibObjectFile/PE/PEFile.Read.cs | 118 +++++++++-- src/LibObjectFile/PE/PEFile.cs | 6 +- src/LibObjectFile/PE/PEStreamExtraData.cs | 53 +++++ src/LibObjectFile/Utils/AlignHelper.cs | 17 ++ 21 files changed, 693 insertions(+), 223 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEArchitectureDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEGlobalPointerDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PESecurityCertificateRevision.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PESecurityCertificateType.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PESecurityDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs create mode 100644 src/LibObjectFile/PE/PEExtraData.cs create mode 100644 src/LibObjectFile/PE/PEStreamExtraData.cs diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 4f25e7c..682edd4 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -160,4 +160,7 @@ public enum DiagnosticId PE_ERR_InvalidExceptionDirectory_Entries = 3100, PE_ERR_InvalidExceptionDirectory_Entry = 3101, PE_ERR_InvalidExceptionDirectory_Size = 3102, + + // PE Certificate directory + PE_ERR_InvalidCertificateEntry = 3200, } \ No newline at end of file diff --git a/src/LibObjectFile/LibObjectFile.csproj b/src/LibObjectFile/LibObjectFile.csproj index 769948b..4e9a283 100644 --- a/src/LibObjectFile/LibObjectFile.csproj +++ b/src/LibObjectFile/LibObjectFile.csproj @@ -37,5 +37,6 @@ + diff --git a/src/LibObjectFile/PE/DataDirectory/PEArchitectureDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEArchitectureDirectory.cs new file mode 100644 index 0000000..8285b80 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEArchitectureDirectory.cs @@ -0,0 +1,34 @@ +// 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; + +namespace LibObjectFile.PE; + +/// +/// Represents the Architecture directory. +/// +public sealed class PEArchitectureDirectory : PEDataDirectory +{ + /// + /// Initializes a new instance of the class. + /// + public PEArchitectureDirectory() : base(PEDataDirectoryKind.Architecture) + { + } + + protected override uint ComputeHeaderSize(PEVisitorContext context) + { + return 0; + } + + public override void Read(PEImageReader reader) + { + // TBD + } + + public override void Write(PEImageWriter writer) + { + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs new file mode 100644 index 0000000..c6ec9db --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs @@ -0,0 +1,28 @@ +// 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; + +namespace LibObjectFile.PE; + +public sealed class PEBoundImportDirectory : PEDataDirectory +{ + public PEBoundImportDirectory() : base(PEDataDirectoryKind.BoundImport) + { + } + protected override uint ComputeHeaderSize(PEVisitorContext context) + { + return 0; + } + + public override void Read(PEImageReader reader) + { + // TBD + } + + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs b/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs new file mode 100644 index 0000000..d6968e1 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs @@ -0,0 +1,29 @@ +// 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; + +namespace LibObjectFile.PE; + +public sealed class PEClrMetadata : PEDataDirectory +{ + public PEClrMetadata() : base(PEDataDirectoryKind.ClrMetadata) + { + } + + protected override uint ComputeHeaderSize(PEVisitorContext context) + { + return 0; + } + + public override void Read(PEImageReader reader) + { + // TBD + } + + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index 8f1d442..430e415 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -97,7 +97,6 @@ internal static PEDataDirectory Create(PEDataDirectoryKind kind) PEDataDirectoryKind.Import => new PEImportDirectory(), PEDataDirectoryKind.Resource => new PEResourceDirectory(), PEDataDirectoryKind.Exception => new PEExceptionDirectory(), - PEDataDirectoryKind.Security => new PESecurityDirectory(), PEDataDirectoryKind.BaseRelocation => new PEBaseRelocationDirectory(), PEDataDirectoryKind.Debug => new PEDebugDirectory(), PEDataDirectoryKind.Architecture => new PEArchitectureDirectory(), @@ -112,199 +111,3 @@ internal static PEDataDirectory Create(PEDataDirectoryKind kind) }; } } - -public sealed class PEDebugDirectory : PEDataDirectory -{ - public PEDebugDirectory() : base(PEDataDirectoryKind.Debug) - { - } - protected override uint ComputeHeaderSize(PEVisitorContext context) - { - return 0; - } - - - public override void Read(PEImageReader reader) - { - // TBD - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} - -public sealed class PELoadConfigDirectory : PEDataDirectory -{ - public PELoadConfigDirectory() : base(PEDataDirectoryKind.LoadConfig) - { - } - protected override uint ComputeHeaderSize(PEVisitorContext context) - { - return 0; - } - - - public override void Read(PEImageReader reader) - { - // TBD - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} - -public sealed class PEBoundImportDirectory : PEDataDirectory -{ - public PEBoundImportDirectory() : base(PEDataDirectoryKind.BoundImport) - { - } - protected override uint ComputeHeaderSize(PEVisitorContext context) - { - return 0; - } - - public override void Read(PEImageReader reader) - { - // TBD - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} - -public sealed class PETlsDirectory : PEDataDirectory -{ - public PETlsDirectory() : base(PEDataDirectoryKind.Tls) - { - } - - protected override uint ComputeHeaderSize(PEVisitorContext context) - { - return 0; - } - - public override void Read(PEImageReader reader) - { - // TBD - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} - -public sealed class PEDelayImportDirectory : PEDataDirectory -{ - public PEDelayImportDirectory() : base(PEDataDirectoryKind.DelayImport) - { - } - protected override uint ComputeHeaderSize(PEVisitorContext context) - { - return 0; - } - - public override void Read(PEImageReader reader) - { - // TBD - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} - -public sealed class PEClrMetadata : PEDataDirectory -{ - public PEClrMetadata() : base(PEDataDirectoryKind.ClrMetadata) - { - } - - protected override uint ComputeHeaderSize(PEVisitorContext context) - { - return 0; - } - - public override void Read(PEImageReader reader) - { - // TBD - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} - -public sealed class PEArchitectureDirectory : PEDataDirectory -{ - public PEArchitectureDirectory() : base(PEDataDirectoryKind.Architecture) - { - } - - protected override uint ComputeHeaderSize(PEVisitorContext context) - { - return 0; - } - - public override void Read(PEImageReader reader) - { - // TBD - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} - -public sealed class PEGlobalPointerDirectory : PEDataDirectory -{ - public PEGlobalPointerDirectory() : base(PEDataDirectoryKind.GlobalPointer) - { - } - - protected override uint ComputeHeaderSize(PEVisitorContext context) - { - return 0; - } - - public override void Read(PEImageReader reader) - { - // TBD - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} - -public sealed class PESecurityDirectory : PEDataDirectory -{ - public PESecurityDirectory() : base(PEDataDirectoryKind.Security) - { - } - - protected override uint ComputeHeaderSize(PEVisitorContext context) - { - return 0; - } - - public override void Read(PEImageReader reader) - { - // TBD - } - - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } -} diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs new file mode 100644 index 0000000..dd63d4f --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs @@ -0,0 +1,29 @@ +// 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; + +namespace LibObjectFile.PE; + +public sealed class PEDebugDirectory : PEDataDirectory +{ + public PEDebugDirectory() : base(PEDataDirectoryKind.Debug) + { + } + protected override uint ComputeHeaderSize(PEVisitorContext context) + { + return 0; + } + + + public override void Read(PEImageReader reader) + { + // TBD + } + + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs new file mode 100644 index 0000000..2ad2ba0 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs @@ -0,0 +1,28 @@ +// 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; + +namespace LibObjectFile.PE; + +public sealed class PEDelayImportDirectory : PEDataDirectory +{ + public PEDelayImportDirectory() : base(PEDataDirectoryKind.DelayImport) + { + } + protected override uint ComputeHeaderSize(PEVisitorContext context) + { + return 0; + } + + public override void Read(PEImageReader reader) + { + // TBD + } + + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs index 6a427ca..f35b202 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -24,7 +24,7 @@ internal PEDirectoryTable() { } - public PEDataDirectory? this[PEDataDirectoryKind kind] => _entries[(int)kind]; + public PEObjectBase? this[PEDataDirectoryKind kind] => _entries[(int)kind]; /// /// Gets the number of directory entries in the array. @@ -111,6 +111,24 @@ internal PEDirectoryTable() /// [EditorBrowsable(EditorBrowsableState.Never)] public Enumerator GetEnumerator() => new(this); + + internal void Set(PESecurityDirectory? directory) + { + var kind = PEDataDirectoryKind.Security; + ref var entry = ref _entries[(int)kind]; + var previousEntry = entry; + entry = directory; + + if (previousEntry is not null) + { + _count--; + } + + if (directory is not null) + { + _count++; + } + } internal void Set(PEDataDirectoryKind kind, PEDataDirectory? directory) { @@ -132,7 +150,7 @@ internal void Set(PEDataDirectoryKind kind, PEDataDirectory? directory) [InlineArray(15)] private struct InternalArray { - private PEDataDirectory? _element; + private PEObjectBase? _element; } /// @@ -149,7 +167,7 @@ internal Enumerator(PEDirectoryTable table) _index = -1; } - public PEDataDirectory Current => _index >= 0 ? _table._entries[_index]! : null!; + public PEDataDirectory Current => _index >= 0 ? (PEDataDirectory)_table._entries[_index]! : null!; object? IEnumerator.Current => Current; @@ -160,10 +178,10 @@ public void Dispose() public bool MoveNext() { - Span entries = _table._entries; + Span entries = _table._entries; while (++_index < entries.Length) { - if (_table._entries[_index] is not null) + if (_table._entries[_index] is PEDataDirectory) { return true; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEGlobalPointerDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEGlobalPointerDirectory.cs new file mode 100644 index 0000000..3018097 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEGlobalPointerDirectory.cs @@ -0,0 +1,33 @@ +// 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; + +namespace LibObjectFile.PE; + +/// +/// Represents the GlobalPointer directory. +/// +public sealed class PEGlobalPointerDirectory : PEDataDirectory +{ + /// + /// Initializes a new instance of the class. + /// + public PEGlobalPointerDirectory() : base(PEDataDirectoryKind.GlobalPointer) + { + } + + protected override uint ComputeHeaderSize(PEVisitorContext context) + { + return 0; + } + + public override void Read(PEImageReader reader) + { + } + + public override void Write(PEImageWriter writer) + { + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs new file mode 100644 index 0000000..2534db6 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs @@ -0,0 +1,29 @@ +// 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; + +namespace LibObjectFile.PE; + +public sealed class PELoadConfigDirectory : PEDataDirectory +{ + public PELoadConfigDirectory() : base(PEDataDirectoryKind.LoadConfig) + { + } + protected override uint ComputeHeaderSize(PEVisitorContext context) + { + return 0; + } + + + public override void Read(PEImageReader reader) + { + // TBD + } + + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs new file mode 100644 index 0000000..ab985a3 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs @@ -0,0 +1,67 @@ +// 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.IO; +using System.Security.Cryptography.Pkcs; + +namespace LibObjectFile.PE; + +/// +/// Represents a security certificate used in a Portable Executable (PE) file available in . +/// +public sealed class PESecurityCertificate +{ + /// + /// Initializes a new instance of the class. + /// + public PESecurityCertificate() + { + Data = Stream.Null; + Revision = PESecurityCertificateRevision.Revision2; + Type = PESecurityCertificateType.PKCS7; + } + + /// + /// Gets or sets the revision of the security certificate. + /// + public PESecurityCertificateRevision Revision { get; set; } + + /// + /// Gets or sets the type of the security certificate. + /// + public PESecurityCertificateType Type { get; set; } + + /// + /// Gets or sets the data stream of the security certificate. + /// + public Stream Data { get; set; } + + /// + /// Decodes the security certificate and returns a object. + /// + /// The decoded object. + public SignedCms Decode() + { + SignedCms signedCms = new SignedCms(); + var stream = new MemoryStream(); + Data.Position = 0; + Data.CopyTo(stream); + + signedCms.Decode(stream.ToArray()); + + // Optionally verify the signature + signedCms.CheckSignature(true); // true to check certificate validity + + return signedCms; + } + + /// + /// Returns a string representation of the security certificate. + /// + /// A string representation of the security certificate. + public override string ToString() + { + return $"PECertificate {Type} {Revision}"; + } +} diff --git a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateRevision.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateRevision.cs new file mode 100644 index 0000000..29d68e5 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateRevision.cs @@ -0,0 +1,21 @@ +// 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; + +/// +/// Defines the revision of the security certificate. +/// +public enum PESecurityCertificateRevision : ushort +{ + /// + /// Version 1, legacy version of the Win_Certificate structure. It is supported only for purposes of verifying legacy Authenticode signatures + /// + Revision1 = 0x0100, + + /// + /// Version 2 is the current version of the Win_Certificate structure. + /// + Revision2 = 0x0200, +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateType.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateType.cs new file mode 100644 index 0000000..d8ac17d --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateType.cs @@ -0,0 +1,31 @@ +// 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; + +/// +/// Defines the type of the security certificate. +/// +public enum PESecurityCertificateType : ushort +{ + /// + /// X.509 Certificate. Not Supported + /// + X509 = 0x0001, + + /// + /// PKCS#7 SignedData structure. + /// + PKCS7 = 0x0002, + + /// + /// Reserved. Not supported. + /// + Reserved = 0x0003, + + /// + /// Terminal Server Protocol Stack Certificate signing. Not Supported + /// + TerminalServerProtocolStack = 0x0004, +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PESecurityDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityDirectory.cs new file mode 100644 index 0000000..1b1f395 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PESecurityDirectory.cs @@ -0,0 +1,120 @@ +// 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; +using System.Diagnostics; +using LibObjectFile.Diagnostics; +using LibObjectFile.Utils; + +namespace LibObjectFile.PE; + +/// +/// Represents the security directory in a PE file. +/// +public sealed class PESecurityDirectory : PEExtraData +{ + /// + /// Initializes a new instance of the class. + /// + public PESecurityDirectory() + { + Certificates = new(); + } + + /// + /// Gets the list of certificates. + /// + public List Certificates { get; } + + public override unsafe void Read(PEImageReader reader) + { + reader.Position = Position; + long size = (long)Size; + + + while (size > 0) + { + if (!reader.TryReadData(sizeof(RawCertificateHeader), out RawCertificateHeader header)) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Invalid certificate header at position {reader.Position}"); + return; + } + + if (header.Length < sizeof(RawCertificateHeader) || header.Length > size) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidCertificateEntry, $"Invalid certificate length {header.Length} at position {reader.Position}"); + return; + } + + if (header.Revision != PESecurityCertificateRevision.Revision2 && header.Revision != PESecurityCertificateRevision.Revision1) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidCertificateEntry, $"Invalid certificate version {header.Revision} at position {reader.Position}"); + return; + } + + // Check the certificate type + switch (header.Type) + { + case PESecurityCertificateType.X509: + case PESecurityCertificateType.PKCS7: + case PESecurityCertificateType.Reserved: + case PESecurityCertificateType.TerminalServerProtocolStack: + break; + default: + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidCertificateEntry, $"Unsupported certificate type {header.Type} at position {reader.Position}"); + return; + } + + var certificate = new PESecurityCertificate + { + Revision = header.Revision, + Type = header.Type, + }; + + certificate.Data = reader.ReadAsStream(header.Length - 8); + Certificates.Add(certificate); + + if (reader.HasErrors) + { + return; + } + + size -= header.Length; + + // Make sure that we are aligned on the next entry + reader.Position = AlignHelper.AlignUp(reader.Position, 8); + } + + var computedSize = ComputeHeaderSize(reader); + Debug.Assert(computedSize == Size); + } + + private uint ComputeHeaderSize(PEVisitorContext context) + { + var size = 0u; + foreach (var certificate in Certificates) + { + size += 8; // CertificateSize + Version + Type + size += (uint)certificate.Data.Length; + size = AlignHelper.AlignUp(size, 8U); + } + + return size; + } + + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } + + private struct RawCertificateHeader + { +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value + public uint Length; + public PESecurityCertificateRevision Revision; + public PESecurityCertificateType Type; +#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs new file mode 100644 index 0000000..d57ad82 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs @@ -0,0 +1,29 @@ +// 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; + +namespace LibObjectFile.PE; + +public sealed class PETlsDirectory : PEDataDirectory +{ + public PETlsDirectory() : base(PEDataDirectoryKind.Tls) + { + } + + protected override uint ComputeHeaderSize(PEVisitorContext context) + { + return 0; + } + + public override void Read(PEImageReader reader) + { + // TBD + } + + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEExtraData.cs b/src/LibObjectFile/PE/PEExtraData.cs new file mode 100644 index 0000000..9027de5 --- /dev/null +++ b/src/LibObjectFile/PE/PEExtraData.cs @@ -0,0 +1,15 @@ +// 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; + +/// +/// Base class for extra data in a PE file accessible via . +/// +public abstract class PEExtraData : PEObjectBase +{ + protected PEExtraData() + { + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 65d33e5..d16a76a 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -202,14 +202,9 @@ public override void Read(PEImageReader reader) if (TryInitializeSections(reader, sectionHeaders, out var positionAfterLastSection)) { // Read the remaining of the file - reader.Position = positionAfterLastSection; - var sizeToRead = reader.Length - reader.Position; + var sizeToRead = reader.Length - positionAfterLastSection; - // If we have any data left after the sections, we read it - if (sizeToRead > 0) - { - DataAfterSections = reader.ReadAsStream(sizeToRead); - } + FillExtraDataWithMissingStreams(reader, positionAfterLastSection, sizeToRead); } } finally @@ -264,23 +259,42 @@ private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan(); + listOrderedByPosition.AddRange(ExtraData.UnsafeList); + listOrderedByPosition.Sort((a, b) => a.Position.CompareTo(b.Position)); + + for (var i = 0; i < listOrderedByPosition.Count; i++) + { + var data = listOrderedByPosition[i]; + if (currentPosition < data.Position) + { + var size = data.Position - currentPosition; + imageReader.Position = currentPosition; + + var sectionData = new PEStreamExtraData(imageReader.ReadAsStream(size)) + { + Position = currentPosition, + Size = size, + }; + + ExtraData.Insert(data.Index, sectionData); + currentPosition = data.Position; + } + else if (currentPosition > data.Position) + { + imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid extra data position {currentPosition} > {data.Position}"); + return; + } + + currentPosition += data.Size; + } + + if (currentPosition < extraPosition + extraTotalSize) + { + var size = extraPosition + extraTotalSize - currentPosition; + imageReader.Position = currentPosition; + var sectionData = new PEStreamExtraData(imageReader.ReadAsStream(size)) + { + Position = currentPosition, + Size = size, + }; + + ExtraData.Add(sectionData); + } + else if (currentPosition > extraPosition + extraTotalSize) + { + imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid extra data position {currentPosition} > {extraPosition + extraTotalSize}"); + } + } + private static void FillDirectoryWithStreams(PEImageReader imageReader, PEDataDirectory directory) { FillSectionDataWithMissingStreams(imageReader, directory, directory.Content, directory.Position + directory.HeaderSize, directory.Size - directory.HeaderSize); diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 10816c2..27bba46 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -29,6 +29,7 @@ public partial class PEFile : PEObjectBase public PEFile() { _sections = new(this); + ExtraData = new(this); // TODO: Add default initialization } @@ -38,6 +39,7 @@ public PEFile() internal PEFile(bool unused) { _sections = new(this); + ExtraData = new(this); } /// @@ -99,9 +101,9 @@ public Stream? DosStubExtra public ObjectList Sections => _sections; /// - /// Gets or sets the data present after the sections in the file. + /// Gets the data present after the sections in the file (e.g ) /// - public Stream? DataAfterSections { get; set; } + public ObjectList ExtraData { get; } public PESection AddSection(PESectionName name, uint virtualAddress, uint virtualSize) { diff --git a/src/LibObjectFile/PE/PEStreamExtraData.cs b/src/LibObjectFile/PE/PEStreamExtraData.cs new file mode 100644 index 0000000..c107270 --- /dev/null +++ b/src/LibObjectFile/PE/PEStreamExtraData.cs @@ -0,0 +1,53 @@ +// 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; + +/// +/// Defines a stream extra data in the PE file . +/// +public sealed class PEStreamExtraData : PEExtraData +{ + /// + /// Initializes a new instance of the class. + /// + public PEStreamExtraData() + { + Data = Stream.Null; + } + + /// + /// Initializes a new instance of the class. + /// + /// The data stream. + public PEStreamExtraData(Stream data) + { + ArgumentNullException.ThrowIfNull(data); + Data = data; + } + + /// + /// Gets or sets the data stream. + /// + public Stream Data { get; set; } + + public override void UpdateLayout(PEVisitorContext layoutContext) + { + Size = (uint)Data.Length; + } + + public override void Read(PEImageReader reader) + { + reader.Position = Position; + Data = reader.ReadAsStream(Size); + } + + public override void Write(PEImageWriter writer) + { + Data.CopyTo(writer.Stream); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/Utils/AlignHelper.cs b/src/LibObjectFile/Utils/AlignHelper.cs index aab292d..1188355 100644 --- a/src/LibObjectFile/Utils/AlignHelper.cs +++ b/src/LibObjectFile/Utils/AlignHelper.cs @@ -28,4 +28,21 @@ public static ulong AlignUp(ulong value, ulong align) var nextValue = ((value + align - 1) / align) * align; return nextValue; } + /// + /// Aligns a value to the required alignment. + /// + /// The value to align. + /// The alignment. + /// The value aligned or unchanged it is was already aligned. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint AlignUp(uint value, uint align) + { + if (align == 0) throw new ArgumentOutOfRangeException(nameof(align), "Alignment must be > 0"); + if (!BitOperations.IsPow2(align)) throw new ArgumentOutOfRangeException(nameof(align), "Alignment must be a power of 2"); + + var nextValue = ((value + align - 1) / align) * align; + return nextValue; + } + + } \ No newline at end of file From ac58eeb7712d0a821032c586d797b6fb96b9b2d6 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 22 Sep 2024 15:22:05 +0200 Subject: [PATCH 38/87] Allow to update only the size when doing an UpdateLayout within a PEImageReader --- .../PE/DataDirectory/PEDataDirectory.cs | 13 ++++++--- .../PE/DataDirectory/PEExportAddressTable.cs | 2 +- .../PE/DataDirectory/PEExportNameTable.cs | 2 +- .../PE/DataDirectory/PEExportOrdinalTable.cs | 2 +- .../PE/DataDirectory/PEImportAddressTable.cs | 2 +- .../PE/DataDirectory/PEImportLookupTable.cs | 2 +- src/LibObjectFile/PE/PEExtraData.cs | 2 +- src/LibObjectFile/PE/PEFile.Read.cs | 27 ++++++++++++++++--- src/LibObjectFile/PE/PEFile.Write.cs | 2 +- src/LibObjectFile/PE/PEFile.cs | 15 ++++++++--- src/LibObjectFile/PE/PEImageReader.cs | 6 ++--- src/LibObjectFile/PE/PEObjectBase.cs | 2 +- src/LibObjectFile/PE/PESection.cs | 9 ++++++- src/LibObjectFile/PE/PEStreamExtraData.cs | 22 +++++++++++---- src/LibObjectFile/PE/PEStreamSectionData.cs | 2 +- src/LibObjectFile/PE/PEVisitorContext.cs | 12 ++++++++- 16 files changed, 92 insertions(+), 30 deletions(-) diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index 430e415..7313afd 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -28,7 +28,7 @@ protected PEDataDirectory(PEDataDirectoryKind kind) : base(true) /// public ObjectList Content { get; } - public sealed override void UpdateLayout(PEVisitorContext context) + public sealed override void UpdateLayout(PELayoutContext context) { var va = RVA; @@ -41,15 +41,22 @@ public sealed override void UpdateLayout(PEVisitorContext context) // 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; foreach (var table in Content) { table.RVA = va; - + // Update layout will update virtual address - table.UpdateLayout(context); + if (!context.UpdateSizeOnly) + { + table.Position = position; + } + table.UpdateLayout(context); + va += (uint)table.Size; size += table.Size; + position += table.Size; } Size = size; diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index f7b5003..7b36c72 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -24,7 +24,7 @@ public PEExportAddressTable(int count) : base(false) public List Values { get; } = new(); - public override unsafe void UpdateLayout(PEVisitorContext context) + public override unsafe void UpdateLayout(PELayoutContext context) { Size = (ulong)(Values.Count * sizeof(RVA)); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs index 706a0d9..1964001 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs @@ -23,7 +23,7 @@ public PEExportNameTable(int count) : base(false) public List Values { get; } = new(); - public override unsafe void UpdateLayout(PEVisitorContext context) + public override unsafe void UpdateLayout(PELayoutContext context) { Size = (ulong)(Values.Count * sizeof(RVA)); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs index 4cc73b3..0b15903 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs @@ -22,7 +22,7 @@ public PEExportOrdinalTable(int count) : base(false) public List Values { get; } = new(); - public override void UpdateLayout(PEVisitorContext context) + public override void UpdateLayout(PELayoutContext context) { Size = (ulong)Values.Count * sizeof(ushort); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs index 27f4990..b79d88b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs @@ -17,7 +17,7 @@ public PEImportAddressTable() : base(false) public List Entries => FunctionTable.Entries; - public override void UpdateLayout(PEVisitorContext context) + public override void UpdateLayout(PELayoutContext context) { Size = FunctionTable.CalculateSize(context); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs index 0047aac..3fe75aa 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -19,7 +19,7 @@ public PEImportLookupTable() : base(false) public List Entries => FunctionTable.Entries; - public override void UpdateLayout(PEVisitorContext context) + public override void UpdateLayout(PELayoutContext context) { Size = FunctionTable.CalculateSize(context); } diff --git a/src/LibObjectFile/PE/PEExtraData.cs b/src/LibObjectFile/PE/PEExtraData.cs index 9027de5..bc8b010 100644 --- a/src/LibObjectFile/PE/PEExtraData.cs +++ b/src/LibObjectFile/PE/PEExtraData.cs @@ -5,7 +5,7 @@ namespace LibObjectFile.PE; /// -/// Base class for extra data in a PE file accessible via . +/// Base class for extra data in a PE file accessible via . /// public abstract class PEExtraData : PEObjectBase { diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index d16a76a..e1e60e3 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -14,6 +14,7 @@ using LibObjectFile.Collections; using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; +using LibObjectFile.Utils; namespace LibObjectFile.PE; @@ -219,6 +220,22 @@ private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan 0) + { + var firstSectionPosition = headers[0].PointerToRawData; + + var lengthBeforeFirstSection = firstSectionPosition - reader.Position; + if (lengthBeforeFirstSection > 0) + { + var extraData = new PEStreamExtraData(reader.ReadAsStream((ulong)lengthBeforeFirstSection)) + { + Position = reader.Position, + }; + ExtraDataBeforeSections.Add(extraData); + } + } + // Create sections foreach (var section in headers) { @@ -271,7 +288,7 @@ private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan(); - listOrderedByPosition.AddRange(ExtraData.UnsafeList); + listOrderedByPosition.AddRange(ExtraDataAfterSections.UnsafeList); listOrderedByPosition.Sort((a, b) => a.Position.CompareTo(b.Position)); for (var i = 0; i < listOrderedByPosition.Count; i++) @@ -505,7 +524,7 @@ private void FillExtraDataWithMissingStreams(PEImageReader imageReader, ulong ex Size = size, }; - ExtraData.Insert(data.Index, sectionData); + ExtraDataAfterSections.Insert(data.Index, sectionData); currentPosition = data.Position; } else if (currentPosition > data.Position) @@ -527,7 +546,7 @@ private void FillExtraDataWithMissingStreams(PEImageReader imageReader, ulong ex Size = size, }; - ExtraData.Add(sectionData); + ExtraDataAfterSections.Add(sectionData); } else if (currentPosition > extraPosition + extraTotalSize) { diff --git a/src/LibObjectFile/PE/PEFile.Write.cs b/src/LibObjectFile/PE/PEFile.Write.cs index 11bac13..16bc19f 100644 --- a/src/LibObjectFile/PE/PEFile.Write.cs +++ b/src/LibObjectFile/PE/PEFile.Write.cs @@ -35,7 +35,7 @@ public bool TryWrite(Stream stream, out DiagnosticBag diagnostics) var peWriter = new PEImageWriter(this, stream); diagnostics = peWriter.Diagnostics; - var context = new PEVisitorContext(this, diagnostics); + var context = new PELayoutContext(this, diagnostics); Verify(context); if (diagnostics.HasErrors) diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 27bba46..fccfce1 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -29,7 +29,8 @@ public partial class PEFile : PEObjectBase public PEFile() { _sections = new(this); - ExtraData = new(this); + ExtraDataBeforeSections = new(this); + ExtraDataAfterSections = new(this); // TODO: Add default initialization } @@ -39,7 +40,8 @@ public PEFile() internal PEFile(bool unused) { _sections = new(this); - ExtraData = new(this); + ExtraDataBeforeSections = new(this); + ExtraDataAfterSections = new(this); } /// @@ -95,6 +97,11 @@ public Stream? DosStubExtra /// public PEDirectoryTable Directories { get; } = new(); + /// + /// Gets the data present before the sections in the file. + /// + public ObjectList ExtraDataBeforeSections { get; } + /// /// Gets the sections. /// @@ -103,7 +110,7 @@ public Stream? DosStubExtra /// /// Gets the data present after the sections in the file (e.g ) /// - public ObjectList ExtraData { get; } + public ObjectList ExtraDataAfterSections { get; } public PESection AddSection(PESectionName name, uint virtualAddress, uint virtualSize) { @@ -193,7 +200,7 @@ public List GetAllSectionData() return dataList; } - public override void UpdateLayout(PEVisitorContext layoutContext) + public override void UpdateLayout(PELayoutContext layoutContext) { } diff --git a/src/LibObjectFile/PE/PEImageReader.cs b/src/LibObjectFile/PE/PEImageReader.cs index c12fade..7d46f54 100644 --- a/src/LibObjectFile/PE/PEImageReader.cs +++ b/src/LibObjectFile/PE/PEImageReader.cs @@ -11,7 +11,7 @@ public sealed class PEImageReader : ObjectFileReaderWriter internal PEImageReader(PEFile file, Stream stream, PEImageReaderOptions readerOptions) : base(file, stream) { Options = readerOptions; - VisitorContext = new PEVisitorContext(File, Diagnostics); + LayoutContext = new PELayoutContext(File, Diagnostics, true); } public new PEFile File => (PEFile)base.File; @@ -20,8 +20,8 @@ internal PEImageReader(PEFile file, Stream stream, PEImageReaderOptions readerOp public override bool KeepOriginalStreamForSubStreams => Options.IsReadOnly; - public PEVisitorContext VisitorContext { get; } + public PELayoutContext LayoutContext { get; } - public static implicit operator PEVisitorContext(PEImageReader reader) => reader.VisitorContext; + public static implicit operator PELayoutContext(PEImageReader reader) => reader.LayoutContext; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEObjectBase.cs b/src/LibObjectFile/PE/PEObjectBase.cs index c6ac5bb..8885311 100644 --- a/src/LibObjectFile/PE/PEObjectBase.cs +++ b/src/LibObjectFile/PE/PEObjectBase.cs @@ -10,4 +10,4 @@ namespace LibObjectFile.PE; /// Base class for all Portable Executable (PE) objects. /// [DebuggerDisplay("{ToString(),nq}")] -public abstract class PEObjectBase : ObjectFileElement; \ No newline at end of file +public abstract class PEObjectBase : ObjectFileElement; \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index 65eb3ae..0d0daa1 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -65,14 +65,21 @@ public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESec } /// - public override void UpdateLayout(PEVisitorContext context) + public override void UpdateLayout(PELayoutContext context) { var va = RVA; + var position = Position; foreach (var data in Content) { data.RVA = va; + if (!context.UpdateSizeOnly) + { + data.Position = position; + } + data.UpdateLayout(context); va += (uint)data.Size; + position += data.Size; } } diff --git a/src/LibObjectFile/PE/PEStreamExtraData.cs b/src/LibObjectFile/PE/PEStreamExtraData.cs index c107270..7522c6d 100644 --- a/src/LibObjectFile/PE/PEStreamExtraData.cs +++ b/src/LibObjectFile/PE/PEStreamExtraData.cs @@ -8,16 +8,18 @@ namespace LibObjectFile.PE; /// -/// Defines a stream extra data in the PE file . +/// Defines a stream extra data in the PE file . /// public sealed class PEStreamExtraData : PEExtraData { + private Stream _data; + /// /// Initializes a new instance of the class. /// public PEStreamExtraData() { - Data = Stream.Null; + _data = Stream.Null; } /// @@ -27,15 +29,25 @@ public PEStreamExtraData() public PEStreamExtraData(Stream data) { ArgumentNullException.ThrowIfNull(data); - Data = data; + _data = data; + Size = (uint)data.Length; } /// /// Gets or sets the data stream. /// - public Stream Data { get; set; } + public Stream Data + { + get => _data; + set + { + ArgumentNullException.ThrowIfNull(value); + _data = value; + Size = (uint)value.Length; + } + } - public override void UpdateLayout(PEVisitorContext layoutContext) + public override void UpdateLayout(PELayoutContext layoutContext) { Size = (uint)Data.Length; } diff --git a/src/LibObjectFile/PE/PEStreamSectionData.cs b/src/LibObjectFile/PE/PEStreamSectionData.cs index 8cfbe92..aeec9f4 100644 --- a/src/LibObjectFile/PE/PEStreamSectionData.cs +++ b/src/LibObjectFile/PE/PEStreamSectionData.cs @@ -41,7 +41,7 @@ public PEStreamSectionData(Stream stream) : base(false) /// public Stream Stream => _stream; - public override void UpdateLayout(PEVisitorContext layoutContext) + public override void UpdateLayout(PELayoutContext layoutContext) { Size = (ulong)Stream.Length; } diff --git a/src/LibObjectFile/PE/PEVisitorContext.cs b/src/LibObjectFile/PE/PEVisitorContext.cs index aca6c4f..b36b73c 100644 --- a/src/LibObjectFile/PE/PEVisitorContext.cs +++ b/src/LibObjectFile/PE/PEVisitorContext.cs @@ -6,9 +6,19 @@ namespace LibObjectFile.PE; -public sealed class PEVisitorContext : VisitorContextBase +public class PEVisitorContext : VisitorContextBase { internal PEVisitorContext(PEFile peFile, DiagnosticBag diagnostics) : base(peFile, diagnostics) { } +} + +public sealed class PELayoutContext : PEVisitorContext +{ + internal PELayoutContext(PEFile peFile, DiagnosticBag diagnostics, bool updateSizeOnly = false) : base(peFile, diagnostics) + { + UpdateSizeOnly = updateSizeOnly; + } + + public bool UpdateSizeOnly { get; } } \ No newline at end of file From 3aa7097882c79a3b82afc9361026da27fa740245 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 22 Sep 2024 19:32:56 +0200 Subject: [PATCH 39/87] Remove Pkcs dependency for now --- src/LibObjectFile/LibObjectFile.csproj | 2 +- .../PE/DataDirectory/PESecurityCertificate.cs | 32 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/LibObjectFile/LibObjectFile.csproj b/src/LibObjectFile/LibObjectFile.csproj index 4e9a283..2ec2aa8 100644 --- a/src/LibObjectFile/LibObjectFile.csproj +++ b/src/LibObjectFile/LibObjectFile.csproj @@ -37,6 +37,6 @@ - + diff --git a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs index ab985a3..2647f1d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs +++ b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs @@ -3,7 +3,7 @@ // See the license.txt file in the project root for more information. using System.IO; -using System.Security.Cryptography.Pkcs; +//using System.Security.Cryptography.Pkcs; namespace LibObjectFile.PE; @@ -37,24 +37,24 @@ public PESecurityCertificate() /// public Stream Data { get; set; } - /// - /// Decodes the security certificate and returns a object. - /// - /// The decoded object. - public SignedCms Decode() - { - SignedCms signedCms = new SignedCms(); - var stream = new MemoryStream(); - Data.Position = 0; - Data.CopyTo(stream); + ///// + ///// Decodes the security certificate and returns a object. + ///// + ///// The decoded object. + //public SignedCms Decode() + //{ + // SignedCms signedCms = new SignedCms(); + // var stream = new MemoryStream(); + // Data.Position = 0; + // Data.CopyTo(stream); - signedCms.Decode(stream.ToArray()); + // signedCms.Decode(stream.ToArray()); - // Optionally verify the signature - signedCms.CheckSignature(true); // true to check certificate validity + // // Optionally verify the signature + // signedCms.CheckSignature(true); // true to check certificate validity - return signedCms; - } + // return signedCms; + //} /// /// Returns a string representation of the security certificate. From ce899f7a971b3e29cbcf75d626ea60d83128142c Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 22 Sep 2024 19:33:44 +0200 Subject: [PATCH 40/87] Add support for reading base relocation addresses from PETlsDirectory and PEStreamSectionData --- src/LibObjectFile.sln.DotSettings | 1 + src/LibObjectFile/Diagnostics/DiagnosticId.cs | 6 + src/LibObjectFile/ObjectFileElement.cs | 20 ++ .../PEBaseRelocationPageBlockPart.cs | 61 ++++ .../PE/DataDirectory/PETlsCharacteristics.cs | 27 ++ .../PE/DataDirectory/PETlsDirectory.cs | 266 +++++++++++++++++- src/LibObjectFile/PE/PEFile.Read.cs | 5 +- src/LibObjectFile/PE/PEObjectBase.cs | 12 +- src/LibObjectFile/PE/PEStreamSectionData.cs | 12 + src/LibObjectFile/PE/RVA.cs | 2 +- src/LibObjectFile/PE/RVALink.cs | 11 +- src/LibObjectFile/PE/RVALinkExtensions.cs | 14 + src/LibObjectFile/PE/VALink.cs | 102 +++++++ 13 files changed, 521 insertions(+), 18 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PETlsCharacteristics.cs create mode 100644 src/LibObjectFile/PE/RVALinkExtensions.cs create mode 100644 src/LibObjectFile/PE/VALink.cs diff --git a/src/LibObjectFile.sln.DotSettings b/src/LibObjectFile.sln.DotSettings index b2c66c6..4bf95a6 100644 --- a/src/LibObjectFile.sln.DotSettings +++ b/src/LibObjectFile.sln.DotSettings @@ -8,6 +8,7 @@ See the license.txt file in the project root for more information. LEB RVA RVO + VA True True True diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 682edd4..ea132aa 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -163,4 +163,10 @@ public enum DiagnosticId // PE Certificate directory PE_ERR_InvalidCertificateEntry = 3200, + + // PE TLS directory + PE_ERR_InvalidTlsStartAddressOfRawData = 3300, + PE_ERR_InvalidTlsEndAddressOfRawData = 3301, + PE_ERR_InvalidTlsAddressOfIndex = 3302, + PE_ERR_InvalidTlsAddressOfCallBacks = 3303, } \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileElement.cs b/src/LibObjectFile/ObjectFileElement.cs index d4bb849..bb6603e 100644 --- a/src/LibObjectFile/ObjectFileElement.cs +++ b/src/LibObjectFile/ObjectFileElement.cs @@ -56,6 +56,26 @@ protected override bool PrintMembers(StringBuilder builder) builder.Append($"Position = 0x{Position:X}, Size = 0x{Size:X}"); return true; } + + /// + /// Finds the parent of the specified type. + /// + /// The type of the parent to find. + /// The parent of the specified type or null if not found. + public TParent? FindParent() where TParent : ObjectElement + { + ObjectElement? thisObject = this; + while (thisObject is not null) + { + if (thisObject is TParent parent) + { + return parent; + } + thisObject = thisObject.Parent; + } + + return null; + } } public abstract class ObjectFileElement : ObjectFileElement diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs index 675bd79..4bc0677 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs @@ -2,8 +2,10 @@ // 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; using System.Diagnostics; +using System.Runtime.InteropServices; namespace LibObjectFile.PE; @@ -35,6 +37,65 @@ internal PEBaseRelocationPageBlockPart(PESectionDataLink sectionDataLink, List

public List Relocations { get; } + + ///

+ /// Read the address from the relocation. + /// + /// A relocation that belongs to this block. + /// The address read from the relocation. + /// If the section data or the PE file is not found. + public ulong ReadAddress(PEBaseRelocation relocation) + { + var sectionData = SectionDataLink.SectionData; + if (sectionData is null) + { + throw new InvalidOperationException("Cannot read address from a relocation without a section data"); + } + + var peFile = sectionData.GetPEFile(); + if (peFile is null) + { + throw new InvalidOperationException("Cannot read address from a relocation without a PE file"); + } + + return ReadAddress(peFile, relocation); + } + + /// + /// Read the address from the relocation. + /// + /// The PE file containing the section data. + /// A relocation that belongs to this block. + /// The address read from the relocation. + /// If the section data or the PE file is not found. + public ulong ReadAddress(PEFile peFile, PEBaseRelocation relocation) + { + ArgumentNullException.ThrowIfNull(peFile); + + var sectionData = SectionDataLink.SectionData; + if (sectionData is null) + { + throw new InvalidOperationException("Cannot read address from a relocation without a section data"); + } + + var is32 = peFile.IsPE32; + + if (is32) + { + uint address = 0; + Span buffer = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref address, 1)); + sectionData.ReadAt(SectionDataLink.RVO + relocation.OffsetInBlockPart, buffer); + return address; + } + else + { + ulong address = 0; + Span buffer = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref address, 1)); + sectionData.ReadAt(SectionDataLink.RVO + relocation.OffsetInBlockPart, buffer); + return address; + } + } + /// /// Gets the size of this block. /// diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsCharacteristics.cs b/src/LibObjectFile/PE/DataDirectory/PETlsCharacteristics.cs new file mode 100644 index 0000000..ef01831 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PETlsCharacteristics.cs @@ -0,0 +1,27 @@ +// 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; + +namespace LibObjectFile.PE; + +[Flags] +public enum PETlsCharacteristics : uint +{ + Align1Bytes = 1048576, // 0x00100000 + Align2Bytes = 2097152, // 0x00200000 + Align4Bytes = Align2Bytes | Align1Bytes, // 0x00300000 + Align8Bytes = 4194304, // 0x00400000 + Align16Bytes = Align8Bytes | Align1Bytes, // 0x00500000 + Align32Bytes = Align8Bytes | Align2Bytes, // 0x00600000 + Align64Bytes = Align32Bytes | Align1Bytes, // 0x00700000 + Align128Bytes = 8388608, // 0x00800000 + Align256Bytes = Align128Bytes | Align1Bytes, // 0x00900000 + Align512Bytes = Align128Bytes | Align2Bytes, // 0x00A00000 + Align1024Bytes = Align512Bytes | Align1Bytes, // 0x00B00000 + Align2048Bytes = Align128Bytes | Align8Bytes, // 0x00C00000 + Align4096Bytes = Align2048Bytes | Align1Bytes, // 0x00D00000 + Align8192Bytes = Align2048Bytes | Align2Bytes, // 0x00E00000 + AlignMask = Align8192Bytes | Align1Bytes, // 0x00F00000 +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs index d57ad82..0fe6abf 100644 --- a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs @@ -3,27 +3,285 @@ // See the license.txt file in the project root for more information. using System; +using System.Drawing; +using LibObjectFile.Diagnostics; namespace LibObjectFile.PE; +/// +/// Represents the Thread Local Storage (TLS) directory in a PE file. +/// public sealed class PETlsDirectory : PEDataDirectory { + /// + /// Initializes a new instance of the class. + /// public PETlsDirectory() : base(PEDataDirectoryKind.Tls) { + StartAddressOfRawDataLink = new VALink(this); + EndAddressOfRawDataLink = new VALink(this); + AddressOfIndexLink = new VALink(this); + AddressOfCallBacksLink = new VALink(this); } - protected override uint ComputeHeaderSize(PEVisitorContext context) + /// + /// The starting address of the TLS template. + /// + /// + /// The template is a block of data that is used to initialize TLS data. + /// The system copies all of this data each time a thread is created, so it must not be corrupted. + /// Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section. + /// + public VALink StartAddressOfRawDataLink { get; } + + /// + /// Gets or sets the address of the last byte of the TLS, except for the zero fill. + /// + /// + /// As with the Raw Data Start VA field, this is a VA, not an RVA. + /// + public VALink EndAddressOfRawDataLink { get; } + + /// + /// Gets or sets the location to receive the TLS index, which the loader assigns. + /// + /// + /// This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program. + /// + public VALink AddressOfIndexLink { get; } + + /// + /// Gets or sets the address of the TLS callback functions array. + /// + /// + /// The array is null-terminated, so the number of functions is the difference between this field and the AddressOfCallBacks field, divided by the size of a pointer. + /// + public VALink AddressOfCallBacksLink { get; } + + /// + /// Gets or sets the size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. + /// + /// + /// The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data. + /// + public uint SizeOfZeroFill { get; set; } + + /// + /// Gets or sets the alignment characteristics of the TLS directory. + /// + public PETlsCharacteristics Characteristics { get; set; } + + protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) { - return 0; + return context.File.IsPE32 ? (uint)sizeof(RawTlsDirectory32) : (uint)sizeof(RawTlsDirectory64); } public override void Read(PEImageReader reader) { - // TBD + reader.Position = Position; + if (reader.File.IsPE32) + { + Read32(reader); + } + else + { + Read64(reader); + } + + HeaderSize = ComputeHeaderSize(reader); + } + + private unsafe void Read32(PEImageReader reader) + { + RawTlsDirectory32 entry; + if (!reader.TryReadData(sizeof(RawTlsDirectory32), out entry)) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PETlsDirectory)}"); + return; + } + + StartAddressOfRawDataLink.SetTempAddress(reader, entry.StartAddressOfRawData); + EndAddressOfRawDataLink.SetTempAddress(reader, entry.EndAddressOfRawData); + AddressOfIndexLink.SetTempAddress(reader, entry.AddressOfIndex); + AddressOfCallBacksLink.SetTempAddress(reader, entry.AddressOfCallBacks); + + + SizeOfZeroFill = entry.SizeOfZeroFill; + Characteristics = entry.Characteristics; + } + + private unsafe void Read64(PEImageReader reader) + { + RawTlsDirectory64 entry; + if (!reader.TryReadData(sizeof(RawTlsDirectory64), out entry)) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PETlsDirectory)}"); + return; + } + + StartAddressOfRawDataLink.SetTempAddress(reader, entry.StartAddressOfRawData); + EndAddressOfRawDataLink.SetTempAddress(reader, entry.EndAddressOfRawData); + AddressOfIndexLink.SetTempAddress(reader, entry.AddressOfIndex); + AddressOfCallBacksLink.SetTempAddress(reader, entry.AddressOfCallBacks); + + SizeOfZeroFill = entry.SizeOfZeroFill; + Characteristics = entry.Characteristics; + } + + internal override void Bind(PEImageReader reader) + { + if (!StartAddressOfRawDataLink.TryBind(reader)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidTlsStartAddressOfRawData, $"Invalid TLS StartAddressOfRawData {StartAddressOfRawDataLink.Offset}"); + } + + if (!EndAddressOfRawDataLink.TryBind(reader, true)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidTlsEndAddressOfRawData, $"Invalid TLS EndAddressOfRawData {EndAddressOfRawDataLink.Offset}"); + } + + if (!AddressOfIndexLink.TryBind(reader)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidTlsAddressOfIndex, $"Invalid TLS AddressOfIndex {AddressOfIndexLink.Offset}"); + } + + if (!AddressOfCallBacksLink.TryBind(reader)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidTlsAddressOfCallBacks, $"Invalid TLS AddressOfCallBacks {AddressOfCallBacksLink.Offset}"); + } } public override void Write(PEImageWriter writer) { - throw new NotImplementedException(); } + + public override int ReadAt(uint offset, Span destination) + { + var peFile = GetPEFile(); + if (peFile is null) + { + destination.Fill(0); + return 0; + } + + bool isPE32 = peFile.IsPE32; + + int size = 0; + if (isPE32) + { + if (destination.Length != 4) + { + return 0; + } + + switch (offset) + { + case 0: + if (StartAddressOfRawDataLink.TryGetVA(out var startAddressOfRawData)) + { + startAddressOfRawData.Write32(destination); + size = 4; + } + + break; + case 4: + if (EndAddressOfRawDataLink.TryGetVA(out var endAddressOfRawData)) + { + endAddressOfRawData.Write32(destination); + size = 4; + } + + break; + case 8: + if (AddressOfIndexLink.TryGetVA(out var addressOfIndex)) + { + addressOfIndex.Write32(destination); + size = 4; + } + + break; + case 12: + if (AddressOfCallBacksLink.TryGetVA(out var addressOfCallBacks)) + { + addressOfCallBacks.Write32(destination); + size = 4; + } + + break; + } + } + else + { + if (destination.Length != 8) + { + return 0; + } + + switch (offset) + { + case 0: + if (StartAddressOfRawDataLink.TryGetVA(out var startAddressOfRawData)) + { + startAddressOfRawData.Write64(destination); + size = 8; + } + + break; + case 8: + if (EndAddressOfRawDataLink.TryGetVA(out var endAddressOfRawData)) + { + endAddressOfRawData.Write64(destination); + size = 8; + } + + break; + case 16: + if (AddressOfIndexLink.TryGetVA(out var addressOfIndex)) + { + addressOfIndex.Write64(destination); + size = 8; + } + + break; + case 24: + if (AddressOfCallBacksLink.TryGetVA(out var addressOfCallBacks)) + { + addressOfCallBacks.Write64(destination); + size = 8; + } + + break; + } + } + + return size; + } + + public override void WriteAt(uint offset, ReadOnlySpan source) + { + + } + + +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value + private struct RawTlsDirectory32 + { + public uint StartAddressOfRawData; + public uint EndAddressOfRawData; + public uint AddressOfIndex; + public uint AddressOfCallBacks; + public uint SizeOfZeroFill; + public PETlsCharacteristics Characteristics; + } + + private struct RawTlsDirectory64 + { + public ulong StartAddressOfRawData; + public ulong EndAddressOfRawData; + public ulong AddressOfIndex; + public ulong AddressOfCallBacks; + public uint SizeOfZeroFill; + public PETlsCharacteristics Characteristics; + } +#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index e1e60e3..8ee15f2 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -225,12 +225,13 @@ private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan 0) { var extraData = new PEStreamExtraData(reader.ReadAsStream((ulong)lengthBeforeFirstSection)) { - Position = reader.Position, + Position = position, }; ExtraDataBeforeSections.Add(extraData); } diff --git a/src/LibObjectFile/PE/PEObjectBase.cs b/src/LibObjectFile/PE/PEObjectBase.cs index 8885311..5ca90e5 100644 --- a/src/LibObjectFile/PE/PEObjectBase.cs +++ b/src/LibObjectFile/PE/PEObjectBase.cs @@ -10,4 +10,14 @@ namespace LibObjectFile.PE; /// Base class for all Portable Executable (PE) objects. ///
[DebuggerDisplay("{ToString(),nq}")] -public abstract class PEObjectBase : ObjectFileElement; \ No newline at end of file +public abstract class PEObjectBase : ObjectFileElement +{ + /// + /// Gets the PE file containing this object. + /// + /// The PE file containing this object. + /// + /// This method can return null if the object is not attached to a PE file. + /// + public PEFile? GetPEFile() => FindParent(); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEStreamSectionData.cs b/src/LibObjectFile/PE/PEStreamSectionData.cs index aeec9f4..9c6020f 100644 --- a/src/LibObjectFile/PE/PEStreamSectionData.cs +++ b/src/LibObjectFile/PE/PEStreamSectionData.cs @@ -56,4 +56,16 @@ public override void Write(PEImageWriter writer) Stream.Position = 0; Stream.CopyTo(writer.Stream); } + + public override int ReadAt(uint offset, Span destination) + { + Stream.Position = offset; + return Stream.Read(destination); + } + + public override void WriteAt(uint offset, ReadOnlySpan source) + { + Stream.Position = offset; + Stream.Write(source); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/RVA.cs b/src/LibObjectFile/PE/RVA.cs index ff0946b..cb1908b 100644 --- a/src/LibObjectFile/PE/RVA.cs +++ b/src/LibObjectFile/PE/RVA.cs @@ -24,4 +24,4 @@ public record struct RVA(uint Value) /// public override string ToString() => $"0x{Value:X}"; -} \ No newline at end of file +} diff --git a/src/LibObjectFile/PE/RVALink.cs b/src/LibObjectFile/PE/RVALink.cs index fc1c51d..20228e7 100644 --- a/src/LibObjectFile/PE/RVALink.cs +++ b/src/LibObjectFile/PE/RVALink.cs @@ -15,13 +15,4 @@ public interface RVALink public interface RVALink : RVALink { public TData Resolve(); -} - -public static class RVALinkExtensions -{ - public static bool IsNull(this TRVALink link) where TRVALink : RVALink => link.Container is null; - - public static RVA RVA(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? link.Container.RVA + link.RVO : 0; - - public static string ToDisplayText(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? $"{link.Container}, Offset = {link.RVO}" : $""; -} +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/RVALinkExtensions.cs b/src/LibObjectFile/PE/RVALinkExtensions.cs new file mode 100644 index 0000000..36c0695 --- /dev/null +++ b/src/LibObjectFile/PE/RVALinkExtensions.cs @@ -0,0 +1,14 @@ +// 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; + +public static class RVALinkExtensions +{ + public static bool IsNull(this TRVALink link) where TRVALink : RVALink => link.Container is null; + + public static RVA RVA(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? link.Container.RVA + link.RVO : 0; + + public static string ToDisplayText(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? $"{link.Container}, Offset = {link.RVO}" : $""; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/VALink.cs b/src/LibObjectFile/PE/VALink.cs new file mode 100644 index 0000000..28e4c34 --- /dev/null +++ b/src/LibObjectFile/PE/VALink.cs @@ -0,0 +1,102 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +/// +/// A Virtual Address (VA) link in a Portable Executable (PE) image. +/// +public sealed class VALink +{ + public VALink(PEObject owner) + { + Owner = owner; + } + + public PEObject Owner { get; } + + public PEObject? Container { get; set; } + + public RVO Offset { get; set; } + + public bool TryGetVA(out VA va) + { + va = default; + if (Container is null) + { + return false; + } + + var peFile = Owner.GetPEFile(); + if (peFile is null) + { + return false; + } + + va = peFile.OptionalHeader.ImageBase + Container!.RVA + Offset; + return true; + } + + internal void SetTempAddress(PEImageReader reader, ulong va) + { + var file = reader.File; + var rva = (RVA)(uint)(va - file.OptionalHeader.ImageBase); + Container = PEStreamSectionData.Empty; + Offset = (RVO)(uint)rva; + } + + internal bool TryBind(PEImageReader reader, bool isEndOfAddress = false) + { + var file = reader.File; + + var rva = (RVA)(uint)Offset; + if (!file.TryFindContainerByRVA(rva - (isEndOfAddress ? 1U : 0), out var container)) + { + return false; + } + + Container = container; + Offset = (RVO)(uint)(rva - container.RVA); + return true; + } + + public override string ToString() + { + if (TryGetVA(out var va)) + { + return $"{nameof(Container)} = {Container}, Offset = 0x{Offset:X}, VA = {va}"; + } + + return Container is not null ? $"{nameof(Container)} = {Container}, Offset = 0x{Offset:X}" : ""; + } +} + +public record struct VA(ulong Value) +{ + public static implicit operator ulong(VA value) => value.Value; + + public static implicit operator VA(ulong value) => new(value); + + public void Write32(Span destination) + { + if (destination.Length < 4) + throw new ArgumentOutOfRangeException(nameof(destination), "Destination length must be at least 4 bytes"); + + Unsafe.As(ref MemoryMarshal.GetReference(destination)) = (uint)Value; + } + + public void Write64(Span destination) + { + if (destination.Length < 8) + throw new ArgumentOutOfRangeException(nameof(destination), "Destination length must be at least 8 bytes"); + + Unsafe.As(ref MemoryMarshal.GetReference(destination)) = Value; + } + + public override string ToString() => $"0x{Value:X}"; +} \ No newline at end of file From f40ba865d985d1d1cef4f68897352004ac36a388 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 22 Sep 2024 22:22:51 +0200 Subject: [PATCH 41/87] Improve support for PETlsDirectory and PELoadConfigDirectory --- src/LibObjectFile/IO/StreamExtensions.cs | 2 +- .../PE/DataDirectory/PEGuardFlags.cs | 103 +++++++ .../PELoadConfigCodeIntegrity.cs | 16 ++ .../PE/DataDirectory/PELoadConfigDirectory.cs | 94 ++++++- .../DataDirectory/PELoadConfigDirectory32.cs | 75 +++++ .../DataDirectory/PELoadConfigDirectory64.cs | 75 +++++ .../PE/DataDirectory/PETlsDirectory.cs | 257 ++---------------- .../PE/DataDirectory/PETlsDirectory32.cs | 58 ++++ .../PE/DataDirectory/PETlsDirectory64.cs | 58 ++++ src/LibObjectFile/PE/PEFile.cs | 50 ++++ src/LibObjectFile/PE/VA32.cs | 53 ++++ src/LibObjectFile/PE/VA64.cs | 53 ++++ src/LibObjectFile/PE/VALink.cs | 102 ------- src/LibObjectFile/Utils/DataUtils.cs | 69 +++++ 14 files changed, 729 insertions(+), 336 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEGuardFlags.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PELoadConfigCodeIntegrity.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs create mode 100644 src/LibObjectFile/PE/VA32.cs create mode 100644 src/LibObjectFile/PE/VA64.cs delete mode 100644 src/LibObjectFile/PE/VALink.cs create mode 100644 src/LibObjectFile/Utils/DataUtils.cs diff --git a/src/LibObjectFile/IO/StreamExtensions.cs b/src/LibObjectFile/IO/StreamExtensions.cs index 8fbfc78..8ed24f0 100644 --- a/src/LibObjectFile/IO/StreamExtensions.cs +++ b/src/LibObjectFile/IO/StreamExtensions.cs @@ -267,7 +267,7 @@ public static unsafe bool TryReadData(this Stream stream, int sizeToRead, out } Unsafe.SkipInit(out data); - byteRead = stream.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data, 1))); + byteRead = stream.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data, 1)).Slice(0, sizeToRead)); } return byteRead == sizeToRead; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEGuardFlags.cs b/src/LibObjectFile/PE/DataDirectory/PEGuardFlags.cs new file mode 100644 index 0000000..8d19b74 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEGuardFlags.cs @@ -0,0 +1,103 @@ +// 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; + +namespace LibObjectFile.PE; + +/// +/// Control Flow Guard related flags. +/// +[Flags] +public enum PEGuardFlags : uint +{ + /// + /// Module performs control flow integrity checks using system-supplied support. + /// + Instrumented = 0x00000100, + + /// + /// Module performs control flow and write integrity checks. + /// + CfwInstrumented = 0x00000200, + + /// + /// Module contains valid control flow target metadata. + /// + FunctionTablePresent = 0x00000400, + + /// + /// Module does not make use of the /GS security cookie. + /// + SecurityCookieUnused = 0x00000800, + + /// + /// Module supports read-only delay load IAT. + /// + ProtectDelayLoadIAT = 0x00001000, + + /// + /// Delayload import table in its own .didat section (with nothing else in it) that can be freely reprotected. + /// + DelayLoadIATInItsOwnSection = 0x00002000, + + /// + /// Module contains suppressed export information. This also infers that the address taken + /// IAT table is also present in the load config. + /// + CfExportSuppressionInfoPresent = 0x00004000, + + /// + /// Module enables suppression of exports. + /// + CfEnableExportSuppression = 0x00008000, + + /// + /// Module contains longjmp target information. + /// + LongJumpTablePresent = 0x00010000, + + /// + /// Module contains return flow instrumentation and metadata. + /// + RfInstrumented = 0x00020000, + + /// + /// Module requests that the OS enable return flow protection. + /// + RfEnable = 0x00040000, + + /// + /// Module requests that the OS enable return flow protection in strict mode. + /// + RfStrict = 0x00080000, + + /// + /// Module was built with retpoline support. + /// + RetpolinePresent = 0x00100000, + + // DO_NOT_USE (Was EHCont flag on VB (20H1)) + // DO NOT USE + + /// + /// Module contains EH continuation target information. + /// + EhContinuationTablePresent = 0x00400000, + + /// + /// Module was built with xfg. + /// + XfgEnabled = 0x00800000, + + /// + /// Module has CastGuard instrumentation present. + /// + CastGuardPresent = 0x01000000, + + /// + /// Module has Guarded Memcpy instrumentation present. + /// + MemcpyPresent = 0x02000000 +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigCodeIntegrity.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigCodeIntegrity.cs new file mode 100644 index 0000000..168dc5e --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigCodeIntegrity.cs @@ -0,0 +1,16 @@ +// 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 structure representing the Code Integrity information in the Load Configuration Directory. +/// +public struct PELoadConfigCodeIntegrity +{ + public ushort Flags; // Flags to indicate if CI information is available, etc. + public ushort Catalog; // 0xFFFF means not available + public uint CatalogOffset; + public uint Reserved; // Additional bitmask to be defined later +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs index 2534db6..b900796 100644 --- a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs @@ -2,28 +2,112 @@ // 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 System; +using LibObjectFile.Utils; namespace LibObjectFile.PE; +/// +/// Represents the Load Configuration Directory for a PE file. +/// public sealed class PELoadConfigDirectory : PEDataDirectory { + /// + /// Initializes a new instance of the class. + /// public PELoadConfigDirectory() : base(PEDataDirectoryKind.LoadConfig) { } - protected override uint ComputeHeaderSize(PEVisitorContext context) + + /// + /// Initializes a new instance of the class with the specified bitness. + /// + /// A value indicating whether the directory is 32-bit or 64-bit. + public unsafe PELoadConfigDirectory(bool is32Bits) : base(PEDataDirectoryKind.LoadConfig) { - return 0; + Is32Bits = is32Bits; + LoadConfigDirectory32.Size = (uint)sizeof(PELoadConfigDirectory32); + LoadConfigDirectory64.Size = (uint)sizeof(PELoadConfigDirectory64); } + /// + /// Gets a value indicating whether the directory is 32-bit or 64-bit. + /// + public bool Is32Bits { get; private set; } + + /// + /// Gets or sets the 32-bit Load Configuration Directory. + /// + public PELoadConfigDirectory32 LoadConfigDirectory32; + + /// + /// Gets or sets the 64-bit Load Configuration Directory. + /// + public PELoadConfigDirectory64 LoadConfigDirectory64; + + /// + /// Gets or sets the additional data after the Load Configuration Directory. + /// + public byte[]? AdditionalData { get; set; } + + /// + protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) + { + return (Is32Bits ? LoadConfigDirectory32.Size : LoadConfigDirectory64.Size) + (uint)(AdditionalData?.Length ?? 0); + } - public override void Read(PEImageReader reader) + /// + public override unsafe void Read(PEImageReader reader) { - // TBD + Is32Bits = reader.File.IsPE32; + reader.Position = Position; + + int size = (int)Size; + if (Is32Bits) + { + size = Math.Min(size, sizeof(PELoadConfigDirectory32)); + if (!reader.TryReadData(size, out LoadConfigDirectory32)) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PELoadConfigDirectory32)}"); + return; + } + } + else + { + size = Math.Min(size, sizeof(PELoadConfigDirectory64)); + if (!reader.TryReadData(size, out LoadConfigDirectory64)) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PELoadConfigDirectory64)}"); + return; + } + } + + // If we have more data than expected, we read it as additional data + if ((int)Size > size) + { + AdditionalData = new byte[(int)Size - size]; + int read = reader.Read(AdditionalData); + + if (read != AdditionalData.Length) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading additional data in {nameof(PELoadConfigDirectory)}"); + return; + } + } + + HeaderSize = ComputeHeaderSize(reader); } + /// public override void Write(PEImageWriter writer) { throw new NotImplementedException(); } -} \ No newline at end of file + + /// + public override int ReadAt(uint offset, Span destination) => DataUtils.ReadAt(Is32Bits, ref LoadConfigDirectory32, ref LoadConfigDirectory64, offset, destination); + + /// + public override void WriteAt(uint offset, ReadOnlySpan source) => DataUtils.WriteAt(Is32Bits, ref LoadConfigDirectory32, ref LoadConfigDirectory64, offset, source); +} diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs new file mode 100644 index 0000000..c10064d --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs @@ -0,0 +1,75 @@ +// 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 structure representing the Load Configuration Directory for a PE32 file. +/// +public struct PELoadConfigDirectory32 +{ + public uint Size; + public uint TimeDateStamp; + public ushort MajorVersion; + public ushort MinorVersion; + public uint GlobalFlagsClear; + public uint GlobalFlagsSet; + public uint CriticalSectionDefaultTimeout; + public uint DeCommitFreeBlockThreshold; + public uint DeCommitTotalFreeThreshold; + public VA32 LockPrefixTable; // VA + public uint MaximumAllocationSize; + public uint VirtualMemoryThreshold; + public uint ProcessHeapFlags; + public uint ProcessAffinityMask; + public ushort CSDVersion; + public ushort DependentLoadFlags; + public VA32 EditList; // VA + public VA32 SecurityCookie; // VA + public VA32 SEHandlerTable; // VA + public uint SEHandlerCount; + public VA32 GuardCFCheckFunctionPointer; // VA + public VA32 GuardCFDispatchFunctionPointer; // VA + public VA32 GuardCFFunctionTable; // VA + public uint GuardCFFunctionCount; + + private uint _rawGuardFlags; + + public PEGuardFlags GuardFlags + { + get => (PEGuardFlags)(_rawGuardFlags & ~0xF000_0000U); + set => _rawGuardFlags = (_rawGuardFlags & 0xF000_0000U) | (uint)value; + } + + public int TableSizeShift + { + get => (int)(_rawGuardFlags >>> 28); + set => _rawGuardFlags = (_rawGuardFlags & 0x0FFF_FFFFU) | ((uint)value << 28); + } + + public PELoadConfigCodeIntegrity CodeIntegrity; + public VA32 GuardAddressTakenIatEntryTable; // VA + public uint GuardAddressTakenIatEntryCount; + public VA32 GuardLongJumpTargetTable; // VA + public uint GuardLongJumpTargetCount; + public VA32 DynamicValueRelocTable; // VA + public uint CHPEMetadataPointer; + public VA32 GuardRFFailureRoutine; // VA + public VA32 GuardRFFailureRoutineFunctionPointer; // VA + public uint DynamicValueRelocTableOffset; + public ushort DynamicValueRelocTableSection; + public ushort Reserved2; + public VA32 GuardRFVerifyStackPointerFunctionPointer; // VA + public uint HotPatchTableOffset; + public uint Reserved3; + public VA32 EnclaveConfigurationPointer; // VA + public VA32 VolatileMetadataPointer; // VA + public VA32 GuardEHContinuationTable; // VA + public uint GuardEHContinuationCount; + public VA32 GuardXFGCheckFunctionPointer; // VA + public VA32 GuardXFGDispatchFunctionPointer; // VA + public VA32 GuardXFGTableDispatchFunctionPointer; // VA + public VA32 CastGuardOsDeterminedFailureMode; // VA + public VA32 GuardMemcpyFunctionPointer; // VA +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs new file mode 100644 index 0000000..0d253a2 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs @@ -0,0 +1,75 @@ +// 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 structure representing the Load Configuration Directory for a PE64 file. +/// +public struct PELoadConfigDirectory64 +{ + public uint Size; + public uint TimeDateStamp; + public ushort MajorVersion; + public ushort MinorVersion; + public uint GlobalFlagsClear; + public uint GlobalFlagsSet; + public uint CriticalSectionDefaultTimeout; + public ulong DeCommitFreeBlockThreshold; + public ulong DeCommitTotalFreeThreshold; + public VA64 LockPrefixTable; // VA + public ulong MaximumAllocationSize; + public ulong VirtualMemoryThreshold; + public ulong ProcessAffinityMask; + public uint ProcessHeapFlags; + public ushort CSDVersion; + public ushort DependentLoadFlags; + public VA64 EditList; // VA + public VA64 SecurityCookie; // VA + public VA64 SEHandlerTable; // VA + public ulong SEHandlerCount; + public VA64 GuardCFCheckFunctionPointer; // VA + public VA64 GuardCFDispatchFunctionPointer; // VA + public VA64 GuardCFFunctionTable; // VA + public ulong GuardCFFunctionCount; + + private uint _rawGuardFlags; + + public PEGuardFlags GuardFlags + { + get => (PEGuardFlags)(_rawGuardFlags & ~0xF000_0000U); + set => _rawGuardFlags = (_rawGuardFlags & 0xF000_0000U) | (uint)value; + } + + public int TableSizeShift + { + get => (int)(_rawGuardFlags >>> 28); + set => _rawGuardFlags = (_rawGuardFlags & 0x0FFF_FFFFU) | ((uint)value << 28); + } + + public PELoadConfigCodeIntegrity CodeIntegrity; + public VA64 GuardAddressTakenIatEntryTable; // VA + public ulong GuardAddressTakenIatEntryCount; + public VA64 GuardLongJumpTargetTable; // VA + public ulong GuardLongJumpTargetCount; + public VA64 DynamicValueRelocTable; // VA + public VA64 CHPEMetadataPointer; // VA + public VA64 GuardRFFailureRoutine; // VA + public VA64 GuardRFFailureRoutineFunctionPointer; // VA + public uint DynamicValueRelocTableOffset; + public ushort DynamicValueRelocTableSection; + public ushort Reserved2; + public VA64 GuardRFVerifyStackPointerFunctionPointer; // VA + public uint HotPatchTableOffset; + public uint Reserved3; + public VA64 EnclaveConfigurationPointer; // VA + public VA64 VolatileMetadataPointer; // VA + public VA64 GuardEHContinuationTable; // VA + public ulong GuardEHContinuationCount; + public VA64 GuardXFGCheckFunctionPointer; // VA + public VA64 GuardXFGDispatchFunctionPointer; // VA + public VA64 GuardXFGTableDispatchFunctionPointer; // VA + public VA64 CastGuardOsDeterminedFailureMode; // VA + public VA64 GuardMemcpyFunctionPointer; // VA +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs index 0fe6abf..de01d77 100644 --- a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs @@ -3,8 +3,8 @@ // See the license.txt file in the project root for more information. using System; -using System.Drawing; using LibObjectFile.Diagnostics; +using LibObjectFile.Utils; namespace LibObjectFile.PE; @@ -13,275 +13,76 @@ namespace LibObjectFile.PE; ///
public sealed class PETlsDirectory : PEDataDirectory { + /// /// Initializes a new instance of the class. /// public PETlsDirectory() : base(PEDataDirectoryKind.Tls) { - StartAddressOfRawDataLink = new VALink(this); - EndAddressOfRawDataLink = new VALink(this); - AddressOfIndexLink = new VALink(this); - AddressOfCallBacksLink = new VALink(this); } /// - /// The starting address of the TLS template. + /// Initializes a new instance of the class. /// - /// - /// The template is a block of data that is used to initialize TLS data. - /// The system copies all of this data each time a thread is created, so it must not be corrupted. - /// Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section. - /// - public VALink StartAddressOfRawDataLink { get; } + /// True if the PE file is 32 bits, false if it is 64 bits. + public PETlsDirectory(bool is32Bits) : base(PEDataDirectoryKind.Tls) + { + Is32Bits = is32Bits; + } /// - /// Gets or sets the address of the last byte of the TLS, except for the zero fill. + /// Gets a boolean indicating if this TLS directory is for a 32 bits PE file. /// - /// - /// As with the Raw Data Start VA field, this is a VA, not an RVA. - /// - public VALink EndAddressOfRawDataLink { get; } + public bool Is32Bits { get; private set; } - /// - /// Gets or sets the location to receive the TLS index, which the loader assigns. - /// - /// - /// This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program. - /// - public VALink AddressOfIndexLink { get; } /// - /// Gets or sets the address of the TLS callback functions array. + /// Gets the TLS directory for a PE32 file. /// - /// - /// The array is null-terminated, so the number of functions is the difference between this field and the AddressOfCallBacks field, divided by the size of a pointer. - /// - public VALink AddressOfCallBacksLink { get; } + public PETlsDirectory32 TlsDirectory32; + /// - /// Gets or sets the size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. + /// Gets the TLS directory for a PE64 file. /// - /// - /// The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data. - /// - public uint SizeOfZeroFill { get; set; } + public PETlsDirectory64 TlsDirectory64; - /// - /// Gets or sets the alignment characteristics of the TLS directory. - /// - public PETlsCharacteristics Characteristics { get; set; } protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) { - return context.File.IsPE32 ? (uint)sizeof(RawTlsDirectory32) : (uint)sizeof(RawTlsDirectory64); + return Is32Bits ? (uint)sizeof(PETlsDirectory32) : (uint)sizeof(PETlsDirectory64); } - public override void Read(PEImageReader reader) + public override unsafe void Read(PEImageReader reader) { reader.Position = Position; - if (reader.File.IsPE32) - { - Read32(reader); - } - else - { - Read64(reader); - } - - HeaderSize = ComputeHeaderSize(reader); - } - - private unsafe void Read32(PEImageReader reader) - { - RawTlsDirectory32 entry; - if (!reader.TryReadData(sizeof(RawTlsDirectory32), out entry)) - { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PETlsDirectory)}"); - return; - } - - StartAddressOfRawDataLink.SetTempAddress(reader, entry.StartAddressOfRawData); - EndAddressOfRawDataLink.SetTempAddress(reader, entry.EndAddressOfRawData); - AddressOfIndexLink.SetTempAddress(reader, entry.AddressOfIndex); - AddressOfCallBacksLink.SetTempAddress(reader, entry.AddressOfCallBacks); - - - SizeOfZeroFill = entry.SizeOfZeroFill; - Characteristics = entry.Characteristics; - } - - private unsafe void Read64(PEImageReader reader) - { - RawTlsDirectory64 entry; - if (!reader.TryReadData(sizeof(RawTlsDirectory64), out entry)) - { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PETlsDirectory)}"); - return; - } - - StartAddressOfRawDataLink.SetTempAddress(reader, entry.StartAddressOfRawData); - EndAddressOfRawDataLink.SetTempAddress(reader, entry.EndAddressOfRawData); - AddressOfIndexLink.SetTempAddress(reader, entry.AddressOfIndex); - AddressOfCallBacksLink.SetTempAddress(reader, entry.AddressOfCallBacks); - - SizeOfZeroFill = entry.SizeOfZeroFill; - Characteristics = entry.Characteristics; - } - - internal override void Bind(PEImageReader reader) - { - if (!StartAddressOfRawDataLink.TryBind(reader)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidTlsStartAddressOfRawData, $"Invalid TLS StartAddressOfRawData {StartAddressOfRawDataLink.Offset}"); - } - - if (!EndAddressOfRawDataLink.TryBind(reader, true)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidTlsEndAddressOfRawData, $"Invalid TLS EndAddressOfRawData {EndAddressOfRawDataLink.Offset}"); - } + Is32Bits = reader.File.IsPE32; - if (!AddressOfIndexLink.TryBind(reader)) + if (Is32Bits) { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidTlsAddressOfIndex, $"Invalid TLS AddressOfIndex {AddressOfIndexLink.Offset}"); - } - - if (!AddressOfCallBacksLink.TryBind(reader)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidTlsAddressOfCallBacks, $"Invalid TLS AddressOfCallBacks {AddressOfCallBacksLink.Offset}"); - } - } - - public override void Write(PEImageWriter writer) - { - } - - public override int ReadAt(uint offset, Span destination) - { - var peFile = GetPEFile(); - if (peFile is null) - { - destination.Fill(0); - return 0; - } - - bool isPE32 = peFile.IsPE32; - - int size = 0; - if (isPE32) - { - if (destination.Length != 4) + if (!reader.TryReadData(sizeof(PETlsDirectory32), out TlsDirectory32)) { - return 0; - } - - switch (offset) - { - case 0: - if (StartAddressOfRawDataLink.TryGetVA(out var startAddressOfRawData)) - { - startAddressOfRawData.Write32(destination); - size = 4; - } - - break; - case 4: - if (EndAddressOfRawDataLink.TryGetVA(out var endAddressOfRawData)) - { - endAddressOfRawData.Write32(destination); - size = 4; - } - - break; - case 8: - if (AddressOfIndexLink.TryGetVA(out var addressOfIndex)) - { - addressOfIndex.Write32(destination); - size = 4; - } - - break; - case 12: - if (AddressOfCallBacksLink.TryGetVA(out var addressOfCallBacks)) - { - addressOfCallBacks.Write32(destination); - size = 4; - } - - break; + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PETlsDirectory)}"); + return; } } else { - if (destination.Length != 8) + if (!reader.TryReadData(sizeof(PETlsDirectory64), out TlsDirectory64)) { - return 0; - } - - switch (offset) - { - case 0: - if (StartAddressOfRawDataLink.TryGetVA(out var startAddressOfRawData)) - { - startAddressOfRawData.Write64(destination); - size = 8; - } - - break; - case 8: - if (EndAddressOfRawDataLink.TryGetVA(out var endAddressOfRawData)) - { - endAddressOfRawData.Write64(destination); - size = 8; - } - - break; - case 16: - if (AddressOfIndexLink.TryGetVA(out var addressOfIndex)) - { - addressOfIndex.Write64(destination); - size = 8; - } - - break; - case 24: - if (AddressOfCallBacksLink.TryGetVA(out var addressOfCallBacks)) - { - addressOfCallBacks.Write64(destination); - size = 8; - } - - break; + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PETlsDirectory)}"); + return; } } - return size; + HeaderSize = ComputeHeaderSize(reader); } - public override void WriteAt(uint offset, ReadOnlySpan source) + public override void Write(PEImageWriter writer) { - } + public override int ReadAt(uint offset, Span destination) => DataUtils.ReadAt(Is32Bits, ref TlsDirectory32, ref TlsDirectory64, offset, destination); -#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value - private struct RawTlsDirectory32 - { - public uint StartAddressOfRawData; - public uint EndAddressOfRawData; - public uint AddressOfIndex; - public uint AddressOfCallBacks; - public uint SizeOfZeroFill; - public PETlsCharacteristics Characteristics; - } - - private struct RawTlsDirectory64 - { - public ulong StartAddressOfRawData; - public ulong EndAddressOfRawData; - public ulong AddressOfIndex; - public ulong AddressOfCallBacks; - public uint SizeOfZeroFill; - public PETlsCharacteristics Characteristics; - } -#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value + public override void WriteAt(uint offset, ReadOnlySpan source) => DataUtils.WriteAt(Is32Bits, ref TlsDirectory32, ref TlsDirectory64, offset, source); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs new file mode 100644 index 0000000..0fef8da --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs @@ -0,0 +1,58 @@ +// 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 structure representing the Thread Local Storage (TLS) directory for a PE32 file. +/// +public struct PETlsDirectory32 +{ + /// + /// The starting address of the TLS template. + /// + /// + /// The template is a block of data that is used to initialize TLS data. + /// The system copies all of this data each time a thread is created, so it must not be corrupted. + /// Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section. + /// + public VA32 StartAddressOfRawData; + + /// + /// Gets or sets the address of the last byte of the TLS, except for the zero fill. + /// + /// + /// As with the Raw Data Start VA field, this is a VA, not an RVA. + /// + public VA32 EndAddressOfRawData; + + /// + /// Gets or sets the location to receive the TLS index, which the loader assigns. + /// + /// + /// This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program. + /// + public VA32 AddressOfIndex; + + /// + /// Gets or sets the address of the TLS callback functions array. + /// + /// + /// The array is null-terminated, so the number of functions is the difference between this field and the AddressOfCallBacks field, divided by the size of a pointer. + /// + public VA32 AddressOfCallBacks; + + /// + /// Gets or sets the size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. + /// + /// + /// The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data. + /// + public uint SizeOfZeroFill; + + /// + /// Gets or sets the alignment characteristics of the TLS directory. + /// + public PETlsCharacteristics Characteristics; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs new file mode 100644 index 0000000..0d55ccc --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs @@ -0,0 +1,58 @@ +// 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 structure representing the Thread Local Storage (TLS) directory for a PE64 file. +/// +public struct PETlsDirectory64 +{ + /// + /// The starting address of the TLS template. + /// + /// + /// The template is a block of data that is used to initialize TLS data. + /// The system copies all of this data each time a thread is created, so it must not be corrupted. + /// Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section. + /// + public VA64 StartAddressOfRawData; + + /// + /// Gets or sets the address of the last byte of the TLS, except for the zero fill. + /// + /// + /// As with the Raw Data Start VA field, this is a VA, not an RVA. + /// + public VA64 EndAddressOfRawData; + + /// + /// Gets or sets the location to receive the TLS index, which the loader assigns. + /// + /// + /// This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program. + /// + public VA64 AddressOfIndex; + + /// + /// Gets or sets the address of the TLS callback functions array. + /// + /// + /// The array is null-terminated, so the number of functions is the difference between this field and the AddressOfCallBacks field, divided by the size of a pointer. + /// + public VA64 AddressOfCallBacks; + + /// + /// Gets or sets the size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. + /// + /// + /// The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data. + /// + public uint SizeOfZeroFill; + + /// + /// Gets or sets the alignment characteristics of the TLS directory. + /// + public PETlsCharacteristics Characteristics; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index fccfce1..42d6e69 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -200,6 +200,56 @@ public List GetAllSectionData() return dataList; } + /// + /// Tries to find the section data that contains the specified virtual address. + /// + /// The virtual address to search for. + /// The section data that contains the virtual address, if found. + /// The offset from the start of the section data. + /// true if the section data was found; otherwise, false. + /// If the PEFile is not a PE32 image. + public bool TryFindByVA(VA32 va, [NotNullWhen(true)] out PEObject? result, out RVO offset) + { + if (!IsPE32) throw new InvalidOperationException("PEFile is not a PE32 image"); + + var rawRva = va - (uint)OptionalHeader.ImageBase; + var rva = (RVA)(uint)rawRva; + if (rawRva <= int.MaxValue && TryFindContainerByRVA(rva, out result)) + { + offset = rva - result.RVA; + return true; + } + + result = null; + offset = 0; + return false; + } + + /// + /// Tries to find the section data that contains the specified virtual address. + /// + /// The virtual address to search for. + /// The section data that contains the virtual address, if found. + /// The offset from the start of the section data. + /// true if the section data was found; otherwise, false. + /// If the PEFile is not a PE64 image. + public bool TryFindByVA(VA64 va, [NotNullWhen(true)] out PEObject? result, out RVO offset) + { + if (IsPE32) throw new InvalidOperationException("PEFile is not a PE64 image"); + + var rawRva = va - OptionalHeader.ImageBase; + var rva = (RVA)rawRva; + if (rawRva <= uint.MaxValue && TryFindContainerByRVA(rva, out result)) + { + offset = rva - result.RVA; + return true; + } + + result = null; + offset = 0; + return false; + } + public override void UpdateLayout(PELayoutContext layoutContext) { } diff --git a/src/LibObjectFile/PE/VA32.cs b/src/LibObjectFile/PE/VA32.cs new file mode 100644 index 0000000..1119b22 --- /dev/null +++ b/src/LibObjectFile/PE/VA32.cs @@ -0,0 +1,53 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +/// +/// Represents a 32-bit virtual address. +/// +public record struct VA32(uint Value) +{ + /// + /// Implicitly converts a to a . + /// + /// The value to convert. + /// The converted value. + public static implicit operator uint(VA32 value) => value.Value; + + /// + /// Implicitly converts a to a . + /// + /// The value to convert. + /// The converted value. + public static implicit operator VA32(uint value) => new(value); + + /// + /// Gets a value indicating whether the is null (value is 0). + /// + public bool IsNull => Value == 0; + + /// + /// Writes the value to a span of bytes. + /// + /// The destination span of bytes. + /// Thrown when the destination length is less than 4 bytes. + public void Write(Span destination) + { + if (destination.Length < 4) + throw new ArgumentOutOfRangeException(nameof(destination), "Destination length must be at least 4 bytes"); + + Unsafe.As(ref MemoryMarshal.GetReference(destination)) = (uint)Value; + } + + /// + /// Returns a string representation of the value. + /// + /// A string representation of the value. + public override string ToString() => $"0x{Value:X}"; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/VA64.cs b/src/LibObjectFile/PE/VA64.cs new file mode 100644 index 0000000..299272a --- /dev/null +++ b/src/LibObjectFile/PE/VA64.cs @@ -0,0 +1,53 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +/// +/// Represents a 64-bit virtual address. +/// +public record struct VA64(ulong Value) +{ + /// + /// Implicitly converts a to a . + /// + /// The value to convert. + /// The converted value. + public static implicit operator ulong(VA64 value) => value.Value; + + /// + /// Implicitly converts a to a . + /// + /// The value to convert. + /// The converted value. + public static implicit operator VA64(ulong value) => new(value); + + /// + /// Gets a value indicating whether the is null (value is 0). + /// + public bool IsNull => Value == 0; + + /// + /// Writes the value to a span of bytes. + /// + /// The destination span of bytes. + /// Thrown when the destination length is less than 8 bytes. + public void Write(Span destination) + { + if (destination.Length < 8) + throw new ArgumentOutOfRangeException(nameof(destination), "Destination length must be at least 8 bytes"); + + Unsafe.As(ref MemoryMarshal.GetReference(destination)) = Value; + } + + /// + /// Returns a string representation of the value. + /// + /// A string representation of the value. + public override string ToString() => $"0x{Value:X}"; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/VALink.cs b/src/LibObjectFile/PE/VALink.cs deleted file mode 100644 index 28e4c34..0000000 --- a/src/LibObjectFile/PE/VALink.cs +++ /dev/null @@ -1,102 +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. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace LibObjectFile.PE; - -/// -/// A Virtual Address (VA) link in a Portable Executable (PE) image. -/// -public sealed class VALink -{ - public VALink(PEObject owner) - { - Owner = owner; - } - - public PEObject Owner { get; } - - public PEObject? Container { get; set; } - - public RVO Offset { get; set; } - - public bool TryGetVA(out VA va) - { - va = default; - if (Container is null) - { - return false; - } - - var peFile = Owner.GetPEFile(); - if (peFile is null) - { - return false; - } - - va = peFile.OptionalHeader.ImageBase + Container!.RVA + Offset; - return true; - } - - internal void SetTempAddress(PEImageReader reader, ulong va) - { - var file = reader.File; - var rva = (RVA)(uint)(va - file.OptionalHeader.ImageBase); - Container = PEStreamSectionData.Empty; - Offset = (RVO)(uint)rva; - } - - internal bool TryBind(PEImageReader reader, bool isEndOfAddress = false) - { - var file = reader.File; - - var rva = (RVA)(uint)Offset; - if (!file.TryFindContainerByRVA(rva - (isEndOfAddress ? 1U : 0), out var container)) - { - return false; - } - - Container = container; - Offset = (RVO)(uint)(rva - container.RVA); - return true; - } - - public override string ToString() - { - if (TryGetVA(out var va)) - { - return $"{nameof(Container)} = {Container}, Offset = 0x{Offset:X}, VA = {va}"; - } - - return Container is not null ? $"{nameof(Container)} = {Container}, Offset = 0x{Offset:X}" : ""; - } -} - -public record struct VA(ulong Value) -{ - public static implicit operator ulong(VA value) => value.Value; - - public static implicit operator VA(ulong value) => new(value); - - public void Write32(Span destination) - { - if (destination.Length < 4) - throw new ArgumentOutOfRangeException(nameof(destination), "Destination length must be at least 4 bytes"); - - Unsafe.As(ref MemoryMarshal.GetReference(destination)) = (uint)Value; - } - - public void Write64(Span destination) - { - if (destination.Length < 8) - throw new ArgumentOutOfRangeException(nameof(destination), "Destination length must be at least 8 bytes"); - - Unsafe.As(ref MemoryMarshal.GetReference(destination)) = Value; - } - - public override string ToString() => $"0x{Value:X}"; -} \ No newline at end of file diff --git a/src/LibObjectFile/Utils/DataUtils.cs b/src/LibObjectFile/Utils/DataUtils.cs new file mode 100644 index 0000000..91b5539 --- /dev/null +++ b/src/LibObjectFile/Utils/DataUtils.cs @@ -0,0 +1,69 @@ +// 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.Runtime.InteropServices; +using System; + +namespace LibObjectFile.Utils; + +internal static class DataUtils +{ + public static unsafe int ReadAt(bool is32Bits, ref TData32 data32, ref TData64 data64, uint offset, Span destination) + where TData32 : unmanaged + where TData64 : unmanaged + { + if (is32Bits) + { + var endOffset = offset + destination.Length; + if (endOffset > sizeof(TData32)) + { + return 0; + } + + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data32, 1)); + span.Slice((int)offset, (int)destination.Length).CopyTo(destination); + } + else + { + var endOffset = offset + destination.Length; + if (endOffset > sizeof(TData64)) + { + return 0; + } + + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data64, 1)); + span.Slice((int)offset, (int)destination.Length).CopyTo(destination); + } + + return destination.Length; + } + + + public static unsafe void WriteAt(bool is32Bits, ref TData32 data32, ref TData64 data64, uint offset, ReadOnlySpan source) + where TData32 : unmanaged + where TData64 : unmanaged + { + if (is32Bits) + { + var endOffset = offset + source.Length; + if (endOffset > sizeof(TData32)) + { + throw new ArgumentOutOfRangeException(nameof(source), "Source length is too big"); + } + + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data32, 1)); + source.CopyTo(span.Slice((int)offset, (int)source.Length)); + } + else + { + var endOffset = offset + source.Length; + if (endOffset > sizeof(TData64)) + { + throw new ArgumentOutOfRangeException(nameof(source), "Source length is too big"); + } + + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data64, 1)); + source.CopyTo(span.Slice((int)offset, (int)source.Length)); + } + } +} \ No newline at end of file From c56b571c655e03f342dc429b07cfde16575d6b8b Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 22 Sep 2024 22:26:00 +0200 Subject: [PATCH 42/87] Cleanup VA --- src/LibObjectFile/PE/VA32.cs | 17 ----------------- src/LibObjectFile/PE/VA64.cs | 17 ----------------- 2 files changed, 34 deletions(-) diff --git a/src/LibObjectFile/PE/VA32.cs b/src/LibObjectFile/PE/VA32.cs index 1119b22..92073a4 100644 --- a/src/LibObjectFile/PE/VA32.cs +++ b/src/LibObjectFile/PE/VA32.cs @@ -2,10 +2,6 @@ // 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.Runtime.CompilerServices; -using System.Runtime.InteropServices; - namespace LibObjectFile.PE; /// @@ -32,19 +28,6 @@ public record struct VA32(uint Value) /// public bool IsNull => Value == 0; - /// - /// Writes the value to a span of bytes. - /// - /// The destination span of bytes. - /// Thrown when the destination length is less than 4 bytes. - public void Write(Span destination) - { - if (destination.Length < 4) - throw new ArgumentOutOfRangeException(nameof(destination), "Destination length must be at least 4 bytes"); - - Unsafe.As(ref MemoryMarshal.GetReference(destination)) = (uint)Value; - } - /// /// Returns a string representation of the value. /// diff --git a/src/LibObjectFile/PE/VA64.cs b/src/LibObjectFile/PE/VA64.cs index 299272a..c8c7f1a 100644 --- a/src/LibObjectFile/PE/VA64.cs +++ b/src/LibObjectFile/PE/VA64.cs @@ -2,10 +2,6 @@ // 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.Runtime.CompilerServices; -using System.Runtime.InteropServices; - namespace LibObjectFile.PE; /// @@ -32,19 +28,6 @@ public record struct VA64(ulong Value) /// public bool IsNull => Value == 0; - /// - /// Writes the value to a span of bytes. - /// - /// The destination span of bytes. - /// Thrown when the destination length is less than 8 bytes. - public void Write(Span destination) - { - if (destination.Length < 8) - throw new ArgumentOutOfRangeException(nameof(destination), "Destination length must be at least 8 bytes"); - - Unsafe.As(ref MemoryMarshal.GetReference(destination)) = Value; - } - /// /// Returns a string representation of the value. /// From 9469837dad186f10aca404f13f549b59ad663593 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 23 Sep 2024 08:21:42 +0200 Subject: [PATCH 43/87] Add Bound Import Directory --- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 5 + .../DataDirectory/PEBoundImportDirectory.cs | 139 +++++++++++++++++- .../PEBoundImportDirectoryEntry.cs | 37 +++++ .../PEBoundImportForwarderRef.cs | 25 ++++ .../PE/DataDirectory/PEClrMetadata.cs | 2 - .../PE/DataDirectory/PEImportDirectory.cs | 2 +- .../PE/DataDirectory/PEImportFunctionTable.cs | 2 +- .../PE/Internal/RawDelayLoadDescriptor.cs | 47 ++++++ .../PE/Internal/RawPEBoundImportDirectory.cs | 12 ++ .../Internal/RawPEBoundImportForwarderRef.cs | 12 ++ 10 files changed, 276 insertions(+), 7 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectoryEntry.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBoundImportForwarderRef.cs create mode 100644 src/LibObjectFile/PE/Internal/RawDelayLoadDescriptor.cs create mode 100644 src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs create mode 100644 src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.cs diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index ea132aa..6bf8c71 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -169,4 +169,9 @@ public enum DiagnosticId PE_ERR_InvalidTlsEndAddressOfRawData = 3301, PE_ERR_InvalidTlsAddressOfIndex = 3302, PE_ERR_InvalidTlsAddressOfCallBacks = 3303, + + // PE BoundImport directory + PE_ERR_BoundImportDirectoryInvalidEndOfStream = 3400, + PE_ERR_BoundImportDirectoryInvalidModuleName = 3401, + PE_ERR_BoundImportDirectoryInvalidForwarderRefModuleName = 3402, } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs index c6ec9db..4d998aa 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs @@ -2,25 +2,158 @@ // 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; namespace LibObjectFile.PE; +/// +/// Represents the Bound Import Directory in a Portable Executable (PE) file. +/// public sealed class PEBoundImportDirectory : PEDataDirectory { + /// + /// Initializes a new instance of the class. + /// public PEBoundImportDirectory() : base(PEDataDirectoryKind.BoundImport) { + Entries = new List(); } + + /// + /// Gets the list of entries in the Bound Import Directory. + /// + public List Entries { get; } + + /// protected override uint ComputeHeaderSize(PEVisitorContext context) { - return 0; + var size = 0u; + var entries = CollectionsMarshal.AsSpan(Entries); + foreach (var entry in entries) + { + size += entry.Size; + } + + return size; } - public override void Read(PEImageReader reader) + /// + public override unsafe void Read(PEImageReader reader) { - // TBD + reader.Position = Position; + + var diagnostics = reader.Diagnostics; + + // Read Import Directory Entries + RawPEBoundImportDirectory rawEntry = default; + var entrySpan = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref rawEntry, 1)); + + while (true) + { + int read = reader.Read(entrySpan); + if (read != entrySpan.Length) + { + diagnostics.Error(DiagnosticId.PE_ERR_BoundImportDirectoryInvalidEndOfStream, $"Unable to read the full content of the Bound Import Directory. Expected {entrySpan.Length} bytes, but read {read} bytes"); + return; + } + + // Check for null entry (last entry in the import directory) + if (rawEntry.TimeDateStamp == 0 && rawEntry.OffsetModuleName == 0 && rawEntry.NumberOfModuleForwarderRefs == 0) + { + // Last entry + break; + } + + var entry = new PEBoundImportDirectoryEntry + { + // Store a fake entry, will be fully resolved at bind time + ModuleName = new PEAsciiStringLink(PEStreamSectionData.Empty, rawEntry.OffsetModuleName) + }; + + if (rawEntry.NumberOfModuleForwarderRefs > 0) + { + var size = sizeof(RawPEBoundImportForwarderRef) * rawEntry.NumberOfModuleForwarderRefs; + var buffer = ArrayPool.Shared.Rent(size); + var span = buffer.AsSpan(0, size); + var spanForwarderRef = MemoryMarshal.Cast(span); + + read = reader.Read(span); + if (read != size) + { + diagnostics.Error(DiagnosticId.PE_ERR_BoundImportDirectoryInvalidEndOfStream, $"Unable to read the full content of the Bound Import Directory. Expected {size} bytes, but read {read} bytes"); + return; + } + + for (int i = 0; i < rawEntry.NumberOfModuleForwarderRefs; i++) + { + var forwarderRef = spanForwarderRef[i]; + // Store a fake entry, will be fully resolved at bind time + entry.ForwarderRefs.Add(new PEBoundImportForwarderRef(new PEAsciiStringLink(PEStreamSectionData.Empty, forwarderRef.OffsetModuleName))); + } + } + } + + // Update the header size + HeaderSize = ComputeHeaderSize(reader); + } + + /// + internal override void Bind(PEImageReader reader) + { + var peFile = reader.File; + var diagnostics = reader.Diagnostics; + + var entries = CollectionsMarshal.AsSpan(Entries); + foreach (var entry in entries) + { + // The RVO is actually an RVA until we bind it here + var va = (RVA)(uint)entry.ModuleName.RVO; + if (!peFile.TryFindContainerByRVA(va, out var container)) + { + diagnostics.Error(DiagnosticId.PE_ERR_BoundImportDirectoryInvalidModuleName, $"Unable to find the section data for ModuleName {va}"); + return; + } + + var streamSectionData = container as PEStreamSectionData; + if (streamSectionData is null) + { + diagnostics.Error(DiagnosticId.PE_ERR_BoundImportDirectoryInvalidModuleName, $"The section data for ModuleName {va} is not a stream section data"); + return; + } + + // Update the module name + entry.ModuleName = new PEAsciiStringLink(streamSectionData, va - container.RVA); + + var forwarderRefs = CollectionsMarshal.AsSpan(entry.ForwarderRefs); + foreach (ref var forwarderRef in forwarderRefs) + { + // The RVO is actually an RVA until we bind it here + va = (RVA)(uint)forwarderRef.ModuleName.RVO; + if (!peFile.TryFindContainerByRVA(va, out container)) + { + diagnostics.Error(DiagnosticId.PE_ERR_BoundImportDirectoryInvalidForwarderRefModuleName, $"Unable to find the section data for ForwarderRef ModuleName {va}"); + return; + } + + streamSectionData = container as PEStreamSectionData; + if (streamSectionData is null) + { + diagnostics.Error(DiagnosticId.PE_ERR_BoundImportDirectoryInvalidForwarderRefModuleName, $"The section data for ForwarderRef ModuleName {va} is not a stream section data"); + return; + } + + // Update the forwarder ref module name + forwarderRef = new(new PEAsciiStringLink(streamSectionData, va - container.RVA)); + } + } } + /// public override void Write(PEImageWriter writer) { throw new NotImplementedException(); diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectoryEntry.cs new file mode 100644 index 0000000..adb076a --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectoryEntry.cs @@ -0,0 +1,37 @@ +// 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 LibObjectFile.PE.Internal; + +namespace LibObjectFile.PE; + +/// +/// Represents an entry in the Bound Import Directory of a Portable Executable (PE) file. +/// +public sealed class PEBoundImportDirectoryEntry +{ + /// + /// Initializes a new instance of the class. + /// + public PEBoundImportDirectoryEntry() + { + ForwarderRefs = new List(); + } + + /// + /// Gets or sets the module name for this entry. + /// + public PEAsciiStringLink ModuleName { get; set; } + + /// + /// Gets the list of forwarder references for this entry. + /// + public List ForwarderRefs { get; } + + /// + /// Gets the size of this entry in the Bound Import Directory. + /// + internal unsafe uint Size => (uint)(sizeof(RawPEBoundImportDirectory) + ForwarderRefs.Count * sizeof(RawPEBoundImportForwarderRef)); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportForwarderRef.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportForwarderRef.cs new file mode 100644 index 0000000..eeff5c5 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportForwarderRef.cs @@ -0,0 +1,25 @@ +// 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 forwarder reference in the Bound Import Directory of a Portable Executable (PE) file. +/// +public readonly record struct PEBoundImportForwarderRef +{ + /// + /// Initializes a new instance of the struct. + /// + /// The module name of the forwarder reference. + public PEBoundImportForwarderRef(PEAsciiStringLink moduleName) + { + ModuleName = moduleName; + } + + /// + /// Gets the module name of the forwarder reference. + /// + public PEAsciiStringLink ModuleName { get; } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs b/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs index d6968e1..74fb09e 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs @@ -19,11 +19,9 @@ protected override uint ComputeHeaderSize(PEVisitorContext context) public override void Read(PEImageReader reader) { - // TBD } public override void Write(PEImageWriter writer) { - throw new NotImplementedException(); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index b9ccb47..0b44d82 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -151,7 +151,7 @@ internal override void Bind(PEImageReader reader) private unsafe uint CalculateSize() { - return (uint)(((_entries.Count + 1) * sizeof(RawImportDirectoryEntry))); + return _entries.Count == 0 ? 0 : (uint)(((_entries.Count + 1) * sizeof(RawImportDirectoryEntry))); } public override void Write(PEImageWriter writer) diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index 7808654..d0832e8 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -19,7 +19,7 @@ public unsafe ulong CalculateSize(PEVisitorContext context) { var peFile = context.File; // +1 for the null terminator - return (ulong)((Entries.Count + 1) * (peFile.IsPE32 ? sizeof(RawImportFunctionEntry32) : sizeof(RawImportFunctionEntry64))); + return Entries.Count == 0 ? 0 : (ulong)((Entries.Count + 1) * (peFile.IsPE32 ? sizeof(RawImportFunctionEntry32) : sizeof(RawImportFunctionEntry64))); } public void Read(PEImageReader reader, ulong position) diff --git a/src/LibObjectFile/PE/Internal/RawDelayLoadDescriptor.cs b/src/LibObjectFile/PE/Internal/RawDelayLoadDescriptor.cs new file mode 100644 index 0000000..bccb1ec --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawDelayLoadDescriptor.cs @@ -0,0 +1,47 @@ +// 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.Internal; + +#pragma warning disable CS0649 +/// +/// Represents a delay load descriptor in a Portable Executable (PE) image. +/// +internal struct RawDelayLoadDescriptor +{ + /// + /// The attributes that must be zero. + /// + public uint Attributes; + + /// + /// The RVA of the name of the DLL to delay load. + /// + public RVA NameRVA; + + /// + /// The RVA of the delay load import address table, which contains the RVAs of the delay load import name table and the delay load import module. + /// + public RVA DelayLoadImportAddressTableRVA; + + /// + /// The RVA of the delay load import name table, which contains the names of the delay load imports. + /// + public RVA DelayLoadImportNameTableRVA; + + /// + /// The RVA of the bound delay load import address table. This table contains the actual addresses to use for the delay load imports. + /// + public RVA BoundDelayLoadImportAddressTableRVA; + + /// + /// The RVA of the unload delay load import address table. + /// + public RVA UnloadDelayLoadImportAddressTableRVA; + + /// + /// The time/date stamp of the delay load import table. + /// + public uint TimeDateStamp; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs b/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs new file mode 100644 index 0000000..3dd020e --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.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.Internal; + +internal struct RawPEBoundImportDirectory +{ + public uint TimeDateStamp; + public ushort OffsetModuleName; + public ushort NumberOfModuleForwarderRefs; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.cs b/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.cs new file mode 100644 index 0000000..e6425ba --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.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.Internal; + +internal struct RawPEBoundImportForwarderRef +{ + public uint TimeDateStamp; + public ushort OffsetModuleName; + public ushort Reserved; +} \ No newline at end of file From 5aec28d8ee7417bea4c34f8b4d5d2d15a58242ca Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 23 Sep 2024 19:39:41 +0200 Subject: [PATCH 44/87] Add support for the delay import directory --- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 10 + src/LibObjectFile/Dwarf/DwarfAttribute.cs | 5 +- src/LibObjectFile/Dwarf/DwarfDIE.cs | 3 +- src/LibObjectFile/Dwarf/DwarfLineSection.cs | 3 +- .../Dwarf/DwarfLocationSection.cs | 3 +- src/LibObjectFile/Elf/ElfSection.cs | 3 +- src/LibObjectFile/Elf/ElfSegment.cs | 3 +- .../PEBoundImportAddressTable.cs | 12 ++ .../PEBoundImportAddressTable32.cs | 21 ++ .../PEBoundImportAddressTable64.cs | 21 ++ .../PEDelayImportAddressTable.cs | 12 ++ .../DataDirectory/PEDelayImportDirectory.cs | 198 +++++++++++++++++- .../PEDelayImportDirectoryEntry.cs | 28 +++ .../PE/DataDirectory/PEThunkAddressTable.cs | 115 ++++++++++ .../PE/{RVALink.cs => IRVALink.cs} | 4 +- .../PE/Internal/RawDelayLoadDescriptor.cs | 5 + .../PE/Internal/RawPEBoundImportDirectory.cs | 2 + .../Internal/RawPEBoundImportForwarderRef.cs | 2 + src/LibObjectFile/PE/PEAsciiStringLink.cs | 2 +- src/LibObjectFile/PE/PEFunctionAddressLink.cs | 2 +- src/LibObjectFile/PE/PEImportHintNameLink.cs | 2 +- src/LibObjectFile/PE/PEModuleHandleLink.cs | 26 +++ src/LibObjectFile/PE/PEObject.cs | 3 +- src/LibObjectFile/PE/PESectionDataLink.cs | 2 +- src/LibObjectFile/PE/PESectionLink.cs | 2 +- src/LibObjectFile/PE/RVALinkExtensions.cs | 6 +- 26 files changed, 473 insertions(+), 22 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable32.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable64.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDelayImportAddressTable.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs rename src/LibObjectFile/PE/{RVALink.cs => IRVALink.cs} (83%) create mode 100644 src/LibObjectFile/PE/PEModuleHandleLink.cs diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 6bf8c71..b239244 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -174,4 +174,14 @@ public enum DiagnosticId PE_ERR_BoundImportDirectoryInvalidEndOfStream = 3400, PE_ERR_BoundImportDirectoryInvalidModuleName = 3401, PE_ERR_BoundImportDirectoryInvalidForwarderRefModuleName = 3402, + + // PE DelayImport directory + PE_ERR_ImportDirectoryInvalidDelayLoadImportAddressTableRVA = 3500, + PE_ERR_ImportDirectoryInvalidDelayLoadImportNameTableRVA = 3501, + PE_ERR_ImportDirectoryInvalidBoundDelayLoadImportAddressTableRVA = 3502, + PE_ERR_ImportDirectoryInvalidUnloadDelayLoadImportAddressTableRVA = 3503, + PE_ERR_ImportDirectoryInvalidNameRVA = 3504, + PE_ERR_ImportDirectoryInvalidModuleHandleRVA = 3505, + PE_ERR_DelayImportDirectoryInvalidDllNameRVA = 3506, + PE_ERR_DelayImportDirectoryInvalidModuleHandleRVA = 3507, } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAttribute.cs b/src/LibObjectFile/Dwarf/DwarfAttribute.cs index bdb0427..d4c5af6 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttribute.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttribute.cs @@ -119,12 +119,13 @@ protected override bool PrintMembers(StringBuilder builder) { if (ValueAsObject != null) { - builder.Append(ValueAsU64 != 0 ? $"{nameof(Kind)}: {Kind}, Value: {ValueAsObject} Offset: {ValueAsU64}" : $"{nameof(Kind)}: {Kind}, Value: {ValueAsObject}"); + builder.Append(ValueAsU64 != 0 ? $"{nameof(Kind)}: {Kind}, Value: {ValueAsObject} Offset: {ValueAsU64}" : $"{nameof(Kind)}: {Kind}, Value: {ValueAsObject}, "); } else { - builder.Append($"{nameof(Kind)}: {Kind}, Value: {ValueAsU64}"); + builder.Append($"{nameof(Kind)}: {Kind}, Value: {ValueAsU64}, "); } + base.PrintMembers(builder); return true; } diff --git a/src/LibObjectFile/Dwarf/DwarfDIE.cs b/src/LibObjectFile/Dwarf/DwarfDIE.cs index 4a4e3ab..b3f29db 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIE.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIE.cs @@ -58,7 +58,8 @@ public override void Verify(DwarfVerifyContext context) protected override bool PrintMembers(StringBuilder builder) { - builder.Append($"{nameof(Tag)}: {Tag}, {nameof(Attributes)}: {Attributes.Count}, {nameof(Children)}: {Children.Count}"); + builder.Append($"{nameof(Tag)}: {Tag}, {nameof(Attributes)}: {Attributes.Count}, {nameof(Children)}: {Children.Count}, "); + base.PrintMembers(builder); return true; } diff --git a/src/LibObjectFile/Dwarf/DwarfLineSection.cs b/src/LibObjectFile/Dwarf/DwarfLineSection.cs index 999d8b4..53e5200 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSection.cs @@ -68,7 +68,8 @@ public override void Write(DwarfWriter writer) protected override bool PrintMembers(StringBuilder builder) { - builder.Append($"Section .debug_line, Entries: {_tables.Count}"); + builder.Append($"Section .debug_line, Entries: {_tables.Count}, "); + base.PrintMembers(builder); return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs index 2cca202..6f808ea 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs @@ -68,7 +68,8 @@ public override void Write(DwarfWriter writer) protected override bool PrintMembers(StringBuilder builder) { - builder.Append($"Section .debug_loc, Entries: {_locationLists.Count}"); + builder.Append($"Section .debug_loc, Entries: {_locationLists.Count}, "); + base.PrintMembers(builder); return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSection.cs b/src/LibObjectFile/Elf/ElfSection.cs index a07a72e..b27c344 100644 --- a/src/LibObjectFile/Elf/ElfSection.cs +++ b/src/LibObjectFile/Elf/ElfSection.cs @@ -163,7 +163,8 @@ protected virtual void BeforeWrite(ElfWriter writer) protected override bool PrintMembers(StringBuilder builder) { - builder.Append($"Section [{SectionIndex}](Internal: {Index}) `{Name}` "); + builder.Append($"Section [{SectionIndex}](Internal: {Index}) `{Name}`, "); + base.PrintMembers(builder); return true; } diff --git a/src/LibObjectFile/Elf/ElfSegment.cs b/src/LibObjectFile/Elf/ElfSegment.cs index 20daf80..c57f439 100644 --- a/src/LibObjectFile/Elf/ElfSegment.cs +++ b/src/LibObjectFile/Elf/ElfSegment.cs @@ -144,7 +144,8 @@ public override void UpdateLayout(ElfVisitorContext context) protected override bool PrintMembers(StringBuilder builder) { - builder.Append($"[{Index}]"); + builder.Append($"[{Index}] "); + base.PrintMembers(builder); return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable.cs new file mode 100644 index 0000000..bc47466 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable.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; + +public abstract class PEBoundImportAddressTable : PEThunkAddressTable +{ + private protected PEBoundImportAddressTable(bool is32Bits) : base(is32Bits) + { + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable32.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable32.cs new file mode 100644 index 0000000..c07a4c5 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable32.cs @@ -0,0 +1,21 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +public sealed class PEBoundImportAddressTable32() : PEBoundImportAddressTable(true) +{ + public List Entries { get; } = new(); + + public override void Read(PEImageReader reader) => Read32(reader, Entries); + + public override void Write(PEImageWriter writer) => Write32(writer, Entries); + + public override int Count => Entries.Count; + + internal override void SetCount(int count) => CollectionsMarshal.SetCount(Entries, count); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable64.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable64.cs new file mode 100644 index 0000000..79cdd59 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable64.cs @@ -0,0 +1,21 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +public sealed class PEBoundImportAddressTable64() : PEBoundImportAddressTable(false) +{ + public List Entries { get; } = new(); + + public override void Read(PEImageReader reader) => Read64(reader, Entries); + + public override void Write(PEImageWriter writer) => Write64(writer, Entries); + + public override int Count => Entries.Count; + + internal override void SetCount(int count) => CollectionsMarshal.SetCount(Entries, count); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportAddressTable.cs new file mode 100644 index 0000000..70e1d3d --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportAddressTable.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; + +public sealed class PEDelayImportAddressTable : PEImportAddressTable +{ + public PEDelayImportAddressTable() + { + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs index 2ad2ba0..12401e0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs @@ -2,7 +2,11 @@ // 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.Generic; +using System.Runtime.InteropServices; namespace LibObjectFile.PE; @@ -10,17 +14,203 @@ public sealed class PEDelayImportDirectory : PEDataDirectory { public PEDelayImportDirectory() : base(PEDataDirectoryKind.DelayImport) { + Entries = new List(); } - protected override uint ComputeHeaderSize(PEVisitorContext context) + + public List Entries { get; } + + public override void Read(PEImageReader reader) { - return 0; + var diagnostics = reader.Diagnostics; + + bool is32Bits = reader.File.IsPE32; + reader.Position = Position; + + // Read Import Directory Entries + RawDelayLoadDescriptor rawEntry = default; + var entrySpan = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref rawEntry, 1)); + + while (true) + { + int read = reader.Read(entrySpan); + if (read != entrySpan.Length) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidEndOfStream, $"Unable to read the full content of the Import Directory. Expected {entrySpan.Length} bytes, but read {read} bytes"); + return; + } + + // Check for null entry (last entry in the import directory) + if (rawEntry.Attributes == 0 && rawEntry.NameRVA == 0 && rawEntry.DelayLoadImportAddressTableRVA == 0 && rawEntry.DelayLoadImportNameTableRVA == 0 && rawEntry.BoundDelayLoadImportAddressTableRVA == 0 && rawEntry.UnloadDelayLoadImportAddressTableRVA == 0 && rawEntry.TimeDateStamp == 0) + { + // Last entry + break; + } + + // DelayLoadImportAddressTableRVA + if (!reader.File.TryFindSection(rawEntry.DelayLoadImportAddressTableRVA, out var section)) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidDelayLoadImportAddressTableRVA, $"Unable to find the section for DelayLoadImportAddressTableRVA {rawEntry.DelayLoadImportAddressTableRVA}"); + return; + } + + // Calculate its position within the original stream + var importDelayLoadImportAddressTablePositionInFile = section.Position + rawEntry.DelayLoadImportAddressTableRVA - section.RVA; + + // DelayLoadImportNameTableRVA + if (!reader.File.TryFindSection(rawEntry.DelayLoadImportNameTableRVA, out section)) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidDelayLoadImportNameTableRVA, $"Unable to find the section for DelayLoadImportNameTableRVA {rawEntry.DelayLoadImportNameTableRVA}"); + return; + } + + // Calculate its position within the original stream + var importDelayLoadImportNameTablePositionInFile = section.Position + rawEntry.DelayLoadImportNameTableRVA - section.RVA; + + PEBoundImportAddressTable? boundImportAddressTable = null; + if (rawEntry.BoundDelayLoadImportAddressTableRVA != 0) + { + // BoundDelayLoadImportAddressTableRVA + if (!reader.File.TryFindSection(rawEntry.BoundDelayLoadImportAddressTableRVA, out section)) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidBoundDelayLoadImportAddressTableRVA, $"Unable to find the section for BoundDelayLoadImportAddressTableRVA {rawEntry.BoundDelayLoadImportAddressTableRVA}"); + return; + } + + boundImportAddressTable = is32Bits ? new PEBoundImportAddressTable32() : new PEBoundImportAddressTable64(); + boundImportAddressTable.Position = (uint)section.Position + rawEntry.BoundDelayLoadImportAddressTableRVA - section.RVA; + } + + PEBoundImportAddressTable? unloadDelayInformationTable = null; + if (rawEntry.UnloadDelayLoadImportAddressTableRVA != 0) + { + // UnloadDelayLoadImportAddressTableRVA + if (!reader.File.TryFindSection(rawEntry.UnloadDelayLoadImportAddressTableRVA, out section)) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidUnloadDelayLoadImportAddressTableRVA, $"Unable to find the section for UnloadDelayLoadImportAddressTableRVA {rawEntry.UnloadDelayLoadImportAddressTableRVA}"); + return; + } + + unloadDelayInformationTable = is32Bits ? new PEBoundImportAddressTable32() : new PEBoundImportAddressTable64(); + unloadDelayInformationTable.Position = (uint)section.Position + rawEntry.UnloadDelayLoadImportAddressTableRVA - section.RVA; + } + + PEBoundImportAddressTable delayImportAddressTable = is32Bits ? new PEBoundImportAddressTable32() : new PEBoundImportAddressTable64(); + delayImportAddressTable.Position = (uint)importDelayLoadImportAddressTablePositionInFile; + + Entries.Add( + + new PEDelayImportDirectoryEntry( + new PEAsciiStringLink(PEStreamSectionData.Empty, (RVO)(uint)rawEntry.NameRVA), + new PEModuleHandleLink(PEStreamSectionData.Empty, (RVO)(uint)rawEntry.ModuleHandleRVA), + delayImportAddressTable, + new PEImportLookupTable() + { + Position = (uint)importDelayLoadImportNameTablePositionInFile, + } + ) + { + BoundImportAddressTable = boundImportAddressTable, + UnloadDelayInformationTable = unloadDelayInformationTable + } + ); + } + + // Update the header size + HeaderSize = ComputeHeaderSize(reader); + + // Resolve ImportLookupTable and ImportAddressTable section data links + var entries = CollectionsMarshal.AsSpan(Entries); + foreach (ref var entry in entries) + { + entry.DelayImportAddressTable.Read(reader); + entry.DelayImportNameTable.Read(reader); + + if (entry.BoundImportAddressTable != null) + { + entry.BoundImportAddressTable.SetCount(entry.DelayImportAddressTable.Count); + entry.BoundImportAddressTable.Read(reader); + } + + if (entry.UnloadDelayInformationTable != null) + { + entry.UnloadDelayInformationTable.SetCount(entry.DelayImportAddressTable.Count); + entry.UnloadDelayInformationTable.Read(reader); + } + } } + internal override IEnumerable CollectImplicitSectionDataList() + { + foreach (var entry in Entries) + { + yield return entry.DelayImportAddressTable; + yield return entry.DelayImportNameTable; - public override void Read(PEImageReader reader) + if (entry.BoundImportAddressTable != null) + { + yield return entry.BoundImportAddressTable; + } + + if (entry.UnloadDelayInformationTable != null) + { + yield return entry.UnloadDelayInformationTable; + } + } + } + + internal override void Bind(PEImageReader reader) { - // TBD + var peFile = reader.File; + var diagnostics = reader.Diagnostics; + + var entries = CollectionsMarshal.AsSpan(Entries); + + foreach (ref var entry in entries) + { + var rva = (RVA)(uint)entry.DllName.RVO; + if (!peFile.TryFindContainerByRVA(rva, out var container)) + { + diagnostics.Error(DiagnosticId.PE_ERR_DelayImportDirectoryInvalidDllNameRVA, $"Unable to find the section data for DllNameRVA {rva}"); + return; + } + + var streamSectionData = container as PEStreamSectionData; + if (streamSectionData is null) + { + diagnostics.Error(DiagnosticId.PE_ERR_DelayImportDirectoryInvalidDllNameRVA, $"The section data for DllNameRVA {rva} is not a stream section data"); + return; + } + + entry.DllName = new PEAsciiStringLink(streamSectionData, rva - container.RVA); + + rva = (RVA)(uint)entry.ModuleHandle.RVO; + if (!peFile.TryFindContainerByRVA(rva, out container)) + { + diagnostics.Error(DiagnosticId.PE_ERR_DelayImportDirectoryInvalidModuleHandleRVA, $"Unable to find the section data for ModuleHandleRVA {rva}"); + return; + } + + streamSectionData = container as PEStreamSectionData; + if (streamSectionData is null) + { + diagnostics.Error(DiagnosticId.PE_ERR_DelayImportDirectoryInvalidModuleHandleRVA, $"The section data for ModuleHandleRVA {rva} is not a stream section data"); + return; + } + + entry.ModuleHandle = new PEModuleHandleLink(streamSectionData, rva - container.RVA); + + entry.DelayImportNameTable.FunctionTable.Bind(reader); + } } + protected override uint ComputeHeaderSize(PEVisitorContext context) => CalculateSize(); + + + private unsafe uint CalculateSize() + { + return Entries.Count == 0 ? 0 : (uint)(((Entries.Count + 1) * sizeof(RawDelayLoadDescriptor))); + } + + public override void Write(PEImageWriter writer) { throw new NotImplementedException(); diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs new file mode 100644 index 0000000..9ca0f0a --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs @@ -0,0 +1,28 @@ +// 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; + +public class PEDelayImportDirectoryEntry +{ + public PEDelayImportDirectoryEntry(PEAsciiStringLink dllName, PEModuleHandleLink moduleHandle, PEBoundImportAddressTable delayImportAddressTable, PEImportLookupTable delayImportNameTable) + { + DllName = dllName; + ModuleHandle = moduleHandle; + DelayImportAddressTable = delayImportAddressTable; + DelayImportNameTable = delayImportNameTable; + } + + public PEAsciiStringLink DllName { get; set; } + + public PEModuleHandleLink ModuleHandle { get; set; } + + public PEBoundImportAddressTable DelayImportAddressTable { get; set; } + + public PEImportLookupTable DelayImportNameTable { get; set; } + + public PEBoundImportAddressTable? BoundImportAddressTable { get; set; } + + public PEBoundImportAddressTable? UnloadDelayInformationTable { get; set; } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs new file mode 100644 index 0000000..c065ecb --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs @@ -0,0 +1,115 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +public abstract class PEThunkAddressTable : PESectionData +{ + protected PEThunkAddressTable(bool is32Bits) : base(false) + { + Is32Bits = is32Bits; + } + + public bool Is32Bits { get; } + + public abstract int Count { get; } + + internal abstract void SetCount(int count); + + + private protected void Read32(PEImageReader reader, List entries) + { + reader.Position = Position; + + // If the list have been preallocated, we read the actual count of entries directly + if (entries.Count > 0) + { + var spanEntries = CollectionsMarshal.AsSpan(entries); + foreach (ref var entry in spanEntries) + { + entry = reader.ReadU32(); + } + + var lastValue = reader.ReadU32(); + Debug.Assert(lastValue == 0); + } + else + { + while (true) + { + var thunk = reader.ReadU32(); + if (thunk == 0) + { + break; + } + entries.Add(thunk); + } + } + + + UpdateLayout(reader); + } + + private protected void Read64(PEImageReader reader, List entries) + { + reader.Position = Position; + + // If the list have been preallocated, we read the actual count of entries directly + if (entries.Count > 0) + { + var spanEntries = CollectionsMarshal.AsSpan(entries); + foreach (ref var entry in spanEntries) + { + entry = reader.ReadU64(); + } + + var lastValue = reader.ReadU64(); + Debug.Assert(lastValue == 0); + } + else + { + while (true) + { + var thunk = reader.ReadU64(); + if (thunk == 0) + { + break; + } + + entries.Add(thunk); + } + } + + UpdateLayout(reader); + } + + private protected void Write32(PEImageWriter writer, List entries) + { + writer.Position = Position; + + var span = CollectionsMarshal.AsSpan(entries); + var bytes = MemoryMarshal.AsBytes(span); + writer.Write(bytes); + writer.WriteU32(0); + } + + private protected void Write64(PEImageWriter writer, List entries) + { + writer.Position = Position; + + var span = CollectionsMarshal.AsSpan(entries); + var bytes = MemoryMarshal.AsBytes(span); + writer.Write(bytes); + writer.WriteU64(0); + } + + public sealed override unsafe void UpdateLayout(PELayoutContext context) + { + Size = (uint)((Count + 1) * (Is32Bits ? sizeof(VA32) : sizeof(VA64))); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/RVALink.cs b/src/LibObjectFile/PE/IRVALink.cs similarity index 83% rename from src/LibObjectFile/PE/RVALink.cs rename to src/LibObjectFile/PE/IRVALink.cs index 20228e7..8f59912 100644 --- a/src/LibObjectFile/PE/RVALink.cs +++ b/src/LibObjectFile/PE/IRVALink.cs @@ -5,14 +5,14 @@ namespace LibObjectFile.PE; // ReSharper disable once InconsistentNaming -public interface RVALink +public interface IRVALink { public PEObject? Container { get; } public RVO RVO { get; } } -public interface RVALink : RVALink +public interface IRVALink : IRVALink { public TData Resolve(); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawDelayLoadDescriptor.cs b/src/LibObjectFile/PE/Internal/RawDelayLoadDescriptor.cs index bccb1ec..6d9bae4 100644 --- a/src/LibObjectFile/PE/Internal/RawDelayLoadDescriptor.cs +++ b/src/LibObjectFile/PE/Internal/RawDelayLoadDescriptor.cs @@ -20,6 +20,11 @@ internal struct RawDelayLoadDescriptor ///
public RVA NameRVA; + /// + /// The RVA to the HMODULE caching location (PHMODULE) + /// + public RVA ModuleHandleRVA; + /// /// The RVA of the delay load import address table, which contains the RVAs of the delay load import name table and the delay load import module. /// diff --git a/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs b/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs index 3dd020e..da3a990 100644 --- a/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs +++ b/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs @@ -4,6 +4,8 @@ namespace LibObjectFile.PE.Internal; +#pragma warning disable CS0649 + internal struct RawPEBoundImportDirectory { public uint TimeDateStamp; diff --git a/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.cs b/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.cs index e6425ba..2385dd2 100644 --- a/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.cs +++ b/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.cs @@ -4,6 +4,8 @@ namespace LibObjectFile.PE.Internal; +#pragma warning disable CS0649 + internal struct RawPEBoundImportForwarderRef { public uint TimeDateStamp; diff --git a/src/LibObjectFile/PE/PEAsciiStringLink.cs b/src/LibObjectFile/PE/PEAsciiStringLink.cs index 5e4ec16..f0bcfa2 100644 --- a/src/LibObjectFile/PE/PEAsciiStringLink.cs +++ b/src/LibObjectFile/PE/PEAsciiStringLink.cs @@ -7,7 +7,7 @@ namespace LibObjectFile.PE; /// /// A link to a null terminated ASCII string in a . /// -public readonly struct PEAsciiStringLink : RVALink +public readonly struct PEAsciiStringLink : IRVALink { public PEAsciiStringLink(PEStreamSectionData? streamSectionData, RVO rvo) { diff --git a/src/LibObjectFile/PE/PEFunctionAddressLink.cs b/src/LibObjectFile/PE/PEFunctionAddressLink.cs index 62a63e3..4a02bec 100644 --- a/src/LibObjectFile/PE/PEFunctionAddressLink.cs +++ b/src/LibObjectFile/PE/PEFunctionAddressLink.cs @@ -8,7 +8,7 @@ namespace LibObjectFile.PE; #pragma warning disable CS0649 [DebuggerDisplay("{ToString(),nq}")] -public struct PEFunctionAddressLink : RVALink +public struct PEFunctionAddressLink : IRVALink { public PEFunctionAddressLink(PEObject? container, RVO rvo) { diff --git a/src/LibObjectFile/PE/PEImportHintNameLink.cs b/src/LibObjectFile/PE/PEImportHintNameLink.cs index c5da6f0..8cc7ae0 100644 --- a/src/LibObjectFile/PE/PEImportHintNameLink.cs +++ b/src/LibObjectFile/PE/PEImportHintNameLink.cs @@ -7,7 +7,7 @@ namespace LibObjectFile.PE; /// /// A link to a PE Import Hint Name in a . /// -public readonly struct PEImportHintNameLink : RVALink +public readonly struct PEImportHintNameLink : IRVALink { public PEImportHintNameLink(PEStreamSectionData? streamSectionData, RVO rvoInSection) { diff --git a/src/LibObjectFile/PE/PEModuleHandleLink.cs b/src/LibObjectFile/PE/PEModuleHandleLink.cs new file mode 100644 index 0000000..a6ab02f --- /dev/null +++ b/src/LibObjectFile/PE/PEModuleHandleLink.cs @@ -0,0 +1,26 @@ +// 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 link to a module handle. +/// +public readonly struct PEModuleHandleLink : IRVALink +{ + public PEModuleHandleLink(PEStreamSectionData? streamSectionData, RVO rvo) + { + StreamSectionData = streamSectionData; + RVO = rvo; + } + + public readonly PEStreamSectionData? StreamSectionData; + + public PEObject? Container => StreamSectionData; + + public RVO RVO { get; } + + /// + public override string ToString() => this.ToDisplayText(); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEObject.cs b/src/LibObjectFile/PE/PEObject.cs index b7cc480..7098bad 100644 --- a/src/LibObjectFile/PE/PEObject.cs +++ b/src/LibObjectFile/PE/PEObject.cs @@ -106,7 +106,8 @@ protected virtual void UpdateRVAInChildren() protected override bool PrintMembers(StringBuilder builder) { - builder.Append($"RVA = {RVA}, VirtualSize = 0x{VirtualSize:X}, Size = 0x{Size:X}"); + builder.Append($"RVA = {RVA}, VirtualSize = 0x{VirtualSize:X}, "); + base.PrintMembers(builder); return true; } diff --git a/src/LibObjectFile/PE/PESectionDataLink.cs b/src/LibObjectFile/PE/PESectionDataLink.cs index 8093058..e86651b 100644 --- a/src/LibObjectFile/PE/PESectionDataLink.cs +++ b/src/LibObjectFile/PE/PESectionDataLink.cs @@ -10,7 +10,7 @@ namespace LibObjectFile.PE; /// A link to a section data. ///
[DebuggerDisplay("{ToString(),nq}")] -public readonly struct PESectionDataLink : RVALink +public readonly struct PESectionDataLink : IRVALink { public PESectionDataLink(PESectionData? sectionData, RVO rvo) { diff --git a/src/LibObjectFile/PE/PESectionLink.cs b/src/LibObjectFile/PE/PESectionLink.cs index e517b2d..b619403 100644 --- a/src/LibObjectFile/PE/PESectionLink.cs +++ b/src/LibObjectFile/PE/PESectionLink.cs @@ -10,7 +10,7 @@ namespace LibObjectFile.PE; /// A link to a section ///
[DebuggerDisplay("{ToString(),nq}")] -public readonly struct PESectionLink : RVALink +public readonly struct PESectionLink : IRVALink { public PESectionLink(PESection? section, uint rvo) { diff --git a/src/LibObjectFile/PE/RVALinkExtensions.cs b/src/LibObjectFile/PE/RVALinkExtensions.cs index 36c0695..12b8d66 100644 --- a/src/LibObjectFile/PE/RVALinkExtensions.cs +++ b/src/LibObjectFile/PE/RVALinkExtensions.cs @@ -6,9 +6,9 @@ namespace LibObjectFile.PE; public static class RVALinkExtensions { - public static bool IsNull(this TRVALink link) where TRVALink : RVALink => link.Container is null; + public static bool IsNull(this TRVALink link) where TRVALink : IRVALink => link.Container is null; - public static RVA RVA(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? link.Container.RVA + link.RVO : 0; + public static RVA RVA(this TRVALink link) where TRVALink : IRVALink => link.Container is not null ? link.Container.RVA + link.RVO : 0; - public static string ToDisplayText(this TRVALink link) where TRVALink : RVALink => link.Container is not null ? $"{link.Container}, Offset = {link.RVO}" : $""; + public static string ToDisplayText(this TRVALink link) where TRVALink : IRVALink => link.Container is not null ? $"RVA = {RVA(link)}, {link.Container}, Offset = {link.RVO}" : $""; } \ No newline at end of file From 8321b899faaac9cd89dacf6fb1ab72fb6480637d Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 23 Sep 2024 21:51:04 +0200 Subject: [PATCH 45/87] Add missing PEDelayImportDirectoryEntry attributes --- src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs | 1 + .../PE/DataDirectory/PEDelayImportDirectoryEntry.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs index 12401e0..b1be563 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs @@ -109,6 +109,7 @@ public override void Read(PEImageReader reader) } ) { + Attributes = rawEntry.Attributes, BoundImportAddressTable = boundImportAddressTable, UnloadDelayInformationTable = unloadDelayInformationTable } diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs index 9ca0f0a..8706057 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs @@ -13,6 +13,8 @@ public PEDelayImportDirectoryEntry(PEAsciiStringLink dllName, PEModuleHandleLink DelayImportAddressTable = delayImportAddressTable; DelayImportNameTable = delayImportNameTable; } + + public uint Attributes { get; set; } public PEAsciiStringLink DllName { get; set; } From 951975163d15a7e630ff1b99ef5c32b34a4f23c8 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 23 Sep 2024 21:51:26 +0200 Subject: [PATCH 46/87] Add GetRVA() method to PEBaseRelocationPageBlockPart --- .../PE/DataDirectory/PEBaseRelocationPageBlockPart.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs index 4bc0677..5a018f8 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs @@ -38,6 +38,14 @@ internal PEBaseRelocationPageBlockPart(PESectionDataLink sectionDataLink, List

Relocations { get; } + ///

+ /// Gets the RVA of a relocation that belongs to this part. + /// + /// The relocation. + /// The RVA of the relocation. + public RVA GetRVA(PEBaseRelocation relocation) + => SectionDataLink.Container is null ? 0 : SectionDataLink.Container.RVA + SectionDataLink.RVO + relocation.OffsetInBlockPart; + /// /// Read the address from the relocation. /// From f9a3c514ea90abbc87de8804b432e7307748dfc8 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 23 Sep 2024 21:51:34 +0200 Subject: [PATCH 47/87] Add PEPrinter --- src/LibObjectFile/PE/PEPrinter.cs | 502 ++++++++++++++++++++++++++++++ 1 file changed, 502 insertions(+) create mode 100644 src/LibObjectFile/PE/PEPrinter.cs diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs new file mode 100644 index 0000000..bb20ec7 --- /dev/null +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -0,0 +1,502 @@ +// 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; + +public static class PEPrinter +{ + public static void Print(this PEFile file, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(writer); + + PrintHeaders(file, writer); + PrintDataDirectories(file, writer); + PrintSections(file, writer); + } + + public static void PrintHeaders(PEFile file, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(writer); + + PrintDosHeader(file, writer); + PrintDosStub(file, writer); + PrintCoffHeader(file, writer); + PrintOptionalHeader(file, writer); + } + + public static unsafe void PrintDosHeader(PEFile file, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(writer); + + const int indent = -26; + writer.WriteLine("DOS Header"); + writer.WriteLine($" {nameof(ImageDosHeader.Magic),indent} = {file.DosHeader.Magic}"); + writer.WriteLine($" {nameof(ImageDosHeader.ByteCountOnLastPage),indent} = 0x{file.DosHeader.ByteCountOnLastPage:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.PageCount),indent} = 0x{file.DosHeader.PageCount:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.RelocationCount),indent} = 0x{file.DosHeader.RelocationCount:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.SizeOfParagraphsHeader),indent} = 0x{file.DosHeader.SizeOfParagraphsHeader:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.MinExtraParagraphs),indent} = 0x{file.DosHeader.MinExtraParagraphs:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.MaxExtraParagraphs),indent} = 0x{file.DosHeader.MaxExtraParagraphs:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.InitialSSValue),indent} = 0x{file.DosHeader.InitialSSValue:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.InitialSPValue),indent} = 0x{file.DosHeader.InitialSPValue:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.Checksum),indent} = 0x{file.DosHeader.Checksum:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.InitialIPValue),indent} = 0x{file.DosHeader.InitialIPValue:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.InitialCSValue),indent} = 0x{file.DosHeader.InitialCSValue:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.FileAddressRelocationTable),indent} = 0x{file.DosHeader.FileAddressRelocationTable:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.OverlayNumber),indent} = 0x{file.DosHeader.OverlayNumber:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.Reserved),indent} = 0x{file.DosHeader.Reserved[0]:X}, 0x{file.DosHeader.Reserved[1]:X}, 0x{file.DosHeader.Reserved[2]:X}, 0x{file.DosHeader.Reserved[3]:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.OEMIdentifier),indent} = 0x{file.DosHeader.OEMIdentifier:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.OEMInformation),indent} = 0x{file.DosHeader.OEMInformation:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.Reserved2),indent} = 0x{file.DosHeader.Reserved2[0]:X}, 0x{file.DosHeader.Reserved2[1]:X}, 0x{file.DosHeader.Reserved2[2]:X}, 0x{file.DosHeader.Reserved2[3]:X}, 0x{file.DosHeader.Reserved2[4]:X}, 0x{file.DosHeader.Reserved2[5]:X}, 0x{file.DosHeader.Reserved2[6]:X}, 0x{file.DosHeader.Reserved2[7]:X}, 0x{file.DosHeader.Reserved2[8]:X}, 0x{file.DosHeader.Reserved2[9]:X}"); + writer.WriteLine($" {nameof(ImageDosHeader.FileAddressPEHeader),indent} = 0x{file.DosHeader.FileAddressPEHeader:X}"); + writer.WriteLine(); + } + + public static void PrintDosStub(PEFile file, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(writer); + const int indent = -26; + writer.WriteLine("DOS Stub"); + writer.WriteLine($" {nameof(file.DosStub),indent} = {file.DosStub.Length} bytes"); + } + + public static void PrintCoffHeader(PEFile file, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(writer); + + const int indent = -26; + + writer.WriteLine("COFF Header"); + writer.WriteLine($" {nameof(ImageCoffHeader.Machine),indent} = {file.CoffHeader.Machine}"); + writer.WriteLine($" {nameof(ImageCoffHeader.NumberOfSections),indent} = {file.CoffHeader.NumberOfSections}"); + writer.WriteLine($" {nameof(ImageCoffHeader.TimeDateStamp),indent} = {file.CoffHeader.TimeDateStamp}"); + writer.WriteLine($" {nameof(ImageCoffHeader.PointerToSymbolTable),indent} = 0x{file.CoffHeader.PointerToSymbolTable:X}"); + writer.WriteLine($" {nameof(ImageCoffHeader.NumberOfSymbols),indent} = {file.CoffHeader.NumberOfSymbols}"); + writer.WriteLine($" {nameof(ImageCoffHeader.SizeOfOptionalHeader),indent} = {file.CoffHeader.SizeOfOptionalHeader}"); + writer.WriteLine($" {nameof(ImageCoffHeader.Characteristics),indent} = {file.CoffHeader.Characteristics}"); + writer.WriteLine(); + } + + public static void PrintOptionalHeader(PEFile file, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(writer); + + const int indent = -26; + writer.WriteLine("Optional Header"); + writer.WriteLine($" {nameof(ImageOptionalHeader.Magic),indent} = {file.OptionalHeader.Magic}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.MajorLinkerVersion),indent} = {file.OptionalHeader.MajorLinkerVersion}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.MinorLinkerVersion),indent} = {file.OptionalHeader.MinorLinkerVersion}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfCode),indent} = 0x{file.OptionalHeader.SizeOfCode:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfInitializedData),indent} = 0x{file.OptionalHeader.SizeOfInitializedData:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfUninitializedData),indent} = 0x{file.OptionalHeader.SizeOfUninitializedData:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.AddressOfEntryPoint),indent} = 0x{file.OptionalHeader.AddressOfEntryPoint:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.BaseOfCode),indent} = 0x{file.OptionalHeader.BaseOfCode:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.BaseOfData),indent} = 0x{file.OptionalHeader.BaseOfData:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.ImageBase),indent} = 0x{file.OptionalHeader.ImageBase:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.SectionAlignment),indent} = 0x{file.OptionalHeader.SectionAlignment:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.FileAlignment),indent} = 0x{file.OptionalHeader.FileAlignment:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.MajorOperatingSystemVersion),indent} = {file.OptionalHeader.MajorOperatingSystemVersion}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.MinorOperatingSystemVersion),indent} = {file.OptionalHeader.MinorOperatingSystemVersion}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.MajorImageVersion),indent} = {file.OptionalHeader.MajorImageVersion}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.MinorImageVersion),indent} = {file.OptionalHeader.MinorImageVersion}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.MajorSubsystemVersion),indent} = {file.OptionalHeader.MajorSubsystemVersion}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.MinorSubsystemVersion),indent} = {file.OptionalHeader.MinorSubsystemVersion}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.Win32VersionValue),indent} = 0x{file.OptionalHeader.Win32VersionValue:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfImage),indent} = 0x{file.OptionalHeader.SizeOfImage:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfHeaders),indent} = 0x{file.OptionalHeader.SizeOfHeaders:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.CheckSum),indent} = 0x{file.OptionalHeader.CheckSum:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.Subsystem),indent} = {file.OptionalHeader.Subsystem}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.DllCharacteristics),indent} = {file.OptionalHeader.DllCharacteristics}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfStackReserve),indent} = 0x{file.OptionalHeader.SizeOfStackReserve:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfStackCommit),indent} = 0x{file.OptionalHeader.SizeOfStackCommit:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfHeapReserve),indent} = 0x{file.OptionalHeader.SizeOfHeapReserve:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfHeapCommit),indent} = 0x{file.OptionalHeader.SizeOfHeapCommit:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.LoaderFlags),indent} = 0x{file.OptionalHeader.LoaderFlags:X}"); + writer.WriteLine($" {nameof(ImageOptionalHeader.NumberOfRvaAndSizes),indent} = 0x{file.OptionalHeader.NumberOfRvaAndSizes:X}"); + writer.WriteLine(); + } + + public static void PrintDataDirectories(PEFile file, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(writer); + + writer.WriteLine("Data Directories"); + for(int i = (int)PEDataDirectoryKind.Export; i <= (int)PEDataDirectoryKind.ClrMetadata; i++) + { + var kind = (PEDataDirectoryKind)i; + var directory = file.Directories[kind]; + writer.WriteLine(directory is null + ? $" [{i,3}] = null" + : $" [{i,3}] = {PEDescribe(directory)}"); + } + writer.WriteLine(); + } + + public static void PrintSections(PEFile file, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(writer); + + writer.WriteLine("Sections"); + foreach (var section in file.Sections) + { + writer.WriteLine($" {section.Name,8} {PEDescribe(section)}"); + writer.WriteLine(); + foreach (var data in section.Content) + { + PrintSectionData(file, data, writer); + } + writer.WriteLine(); + } + } + + public static void PrintSection(PEFile file, PESection section, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(section); + ArgumentNullException.ThrowIfNull(writer); + + writer.WriteLine($" {section.Name,-8} {PEDescribe(section)}"); + foreach (var data in section.Content) + { + PrintSectionData(file, data, writer); + } + } + + public static void PrintSectionData(PEFile file, PESectionData data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + writer.WriteLine($" [{data.Index,3}] {PEDescribe(data)}"); + switch (data) + { + case PEBaseRelocationDirectory peBaseRelocationDirectory: + Print(file, peBaseRelocationDirectory, writer); + break; + case PEBoundImportDirectory peBoundImportDirectory: + Print(file, peBoundImportDirectory, writer); + break; + case PEClrMetadata peClrMetadata: + Print(file, peClrMetadata, writer); + break; + case PEArchitectureDirectory peArchitectureDirectory: + Print(file, peArchitectureDirectory, writer); + break; + case PEDebugDirectory peDebugDirectory: + Print(file, peDebugDirectory, writer); + break; + case PEDelayImportDirectory peDelayImportDirectory: + Print(file, peDelayImportDirectory, writer); + break; + case PEExceptionDirectory peExceptionDirectory: + Print(file, peExceptionDirectory, writer); + break; + case PEExportDirectory peExportDirectory: + Print(file, peExportDirectory, writer); + break; + case PEGlobalPointerDirectory peGlobalPointerDirectory: + Print(file, peGlobalPointerDirectory, writer); + break; + case PEImportAddressTableDirectory peImportAddressTableDirectory: + Print(file, peImportAddressTableDirectory, writer); + break; + case PEImportDirectory peImportDirectory: + Print(file, peImportDirectory, writer); + break; + case PELoadConfigDirectory peLoadConfigDirectory: + Print(file, peLoadConfigDirectory, writer); + break; + case PEResourceDirectory peResourceDirectory: + Print(file, peResourceDirectory, writer); + break; + case PETlsDirectory peTlsDirectory: + Print(file, peTlsDirectory, writer); + break; + case PEDataDirectory peDataDirectory: + Print(file, peDataDirectory, writer); + break; + case PEBoundImportAddressTable32 peBoundImportAddressTable32: + Print(file, peBoundImportAddressTable32, writer); + break; + case PEBoundImportAddressTable64 peBoundImportAddressTable64: + Print(file, peBoundImportAddressTable64, writer); + break; + case PEDelayImportAddressTable peDelayImportAddressTable: + Print(file, peDelayImportAddressTable, writer); + break; + case PEExportAddressTable peExportAddressTable: + Print(file, peExportAddressTable, writer); + break; + case PEExportNameTable peExportNameTable: + Print(file, peExportNameTable, writer); + break; + case PEExportOrdinalTable peExportOrdinalTable: + Print(file, peExportOrdinalTable, writer); + break; + case PEImportAddressTable peImportAddressTable: + Print(file, peImportAddressTable, writer); + break; + case PEImportLookupTable peImportLookupTable: + Print(file, peImportLookupTable, writer); + break; + case PEStreamSectionData peStreamSectionData: + Print(file, peStreamSectionData, writer); + break; + default: + throw new ArgumentOutOfRangeException(nameof(data)); + } + writer.WriteLine(); + } + + private static void Print(PEFile file, PEBaseRelocationDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + foreach (var entry in data.Blocks) + { + writer.WriteLine($" Block {entry.SectionLink,-20} Parts[{entry.Parts.Count}]"); + + foreach (var part in entry.Parts) + { + writer.WriteLine($" {part.SectionDataLink,-20} Relocs[{part.Relocations.Count}]"); + + foreach (var reloc in part.Relocations) + { + writer.WriteLine($" {reloc.Type,6} Offset = 0x{reloc.OffsetInBlockPart:X4}, RVA = {part.GetRVA(reloc)}"); + } + } + } + } + + private static void Print(PEFile file, PEBoundImportDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + foreach (var entry in data.Entries) + { + writer.WriteLine($" ModuleName = {entry.ModuleName.Resolve()} ({entry.ModuleName}), ForwarderRefs[{entry.ForwarderRefs.Count}]"); + + foreach (var forwarderRef in entry.ForwarderRefs) + { + writer.WriteLine($" ForwarderRef = {forwarderRef.ModuleName.Resolve()} ({forwarderRef.ModuleName})"); + } + } + } + + private static void Print(PEFile file, PEClrMetadata data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEArchitectureDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEDebugDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEDelayImportDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + foreach (var dirEntry in data.Entries) + { + writer.WriteLine($" DllName = {dirEntry.DllName.Resolve()} ({dirEntry.DllName})"); + writer.WriteLine($" Attributes = {dirEntry.Attributes}"); + writer.WriteLine($" DelayImportAddressTable = {dirEntry.DelayImportAddressTable}"); + writer.WriteLine($" DelayImportNameTable = {dirEntry.DelayImportNameTable}"); + writer.WriteLine($" BoundImportAddressTable = {dirEntry.BoundImportAddressTable}"); + writer.WriteLine($" UnloadDelayInformationTable = {dirEntry.UnloadDelayInformationTable}"); + } + } + + private static void Print(PEFile file, PEExceptionDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEExportDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEGlobalPointerDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEImportAddressTableDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEImportDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PELoadConfigDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEResourceDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PETlsDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEDataDirectory data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEBoundImportAddressTable32 data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEBoundImportAddressTable64 data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEDelayImportAddressTable data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEExportAddressTable data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEExportNameTable data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEExportOrdinalTable data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEImportAddressTable data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEImportLookupTable data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static void Print(PEFile file, PEStreamSectionData data, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(data); + ArgumentNullException.ThrowIfNull(writer); + + } + + private static string PEDescribe(PEObjectBase peObjectBase) + { + const int indent = -32; + if (peObjectBase is PEObject peObject) + { + return $"{peObject.GetType().Name, indent} Position = 0x{peObject.Position:X8}, Size = 0x{peObject.Size:X8}, RVA = 0x{peObject.RVA.Value:X8}, VirtualSize = 0x{peObject.VirtualSize:X8}"; + } + else + { + return $"{peObjectBase.GetType().Name,indent} Position = 0x{peObjectBase.Position:X8}, Size = 0x{peObjectBase.Size:X8}"; + } + } + + +} \ No newline at end of file From 335e1a0fcf2d3321749b1cb650f572161be29a09 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 23 Sep 2024 22:22:33 +0200 Subject: [PATCH 48/87] Improve PEPrinter --- src/LibObjectFile/PE/PEPrinter.cs | 144 +++++++++++++++++++++++++++--- 1 file changed, 132 insertions(+), 12 deletions(-) diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index bb20ec7..dfd237d 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -66,6 +66,7 @@ public static void PrintDosStub(PEFile file, TextWriter writer) const int indent = -26; writer.WriteLine("DOS Stub"); writer.WriteLine($" {nameof(file.DosStub),indent} = {file.DosStub.Length} bytes"); + writer.WriteLine(); } public static void PrintCoffHeader(PEFile file, TextWriter writer) @@ -269,15 +270,19 @@ private static void Print(PEFile file, PEBaseRelocationDirectory data, TextWrite foreach (var entry in data.Blocks) { - writer.WriteLine($" Block {entry.SectionLink,-20} Parts[{entry.Parts.Count}]"); + var pageRVA = entry.SectionLink.RVA(); + writer.WriteLine($" Block {pageRVA} Parts[{entry.Parts.Count}]"); foreach (var part in entry.Parts) { - writer.WriteLine($" {part.SectionDataLink,-20} Relocs[{part.Relocations.Count}]"); + writer.WriteLine($" {PEDescribe(part.SectionDataLink.Container)} Relocs[{part.Relocations.Count}]"); foreach (var reloc in part.Relocations) { - writer.WriteLine($" {reloc.Type,6} Offset = 0x{reloc.OffsetInBlockPart:X4}, RVA = {part.GetRVA(reloc)}"); + var relocRVA = part.GetRVA(reloc); + var offsetInPage = relocRVA - pageRVA; + + writer.WriteLine($" {reloc.Type,6} OffsetFromSectionData = 0x{reloc.OffsetInBlockPart:X4}, OffsetFromBlock = 0x{offsetInPage:X4}, RVA = {relocRVA}"); } } } @@ -332,12 +337,12 @@ private static void Print(PEFile file, PEDelayImportDirectory data, TextWriter w foreach (var dirEntry in data.Entries) { - writer.WriteLine($" DllName = {dirEntry.DllName.Resolve()} ({dirEntry.DllName})"); + writer.WriteLine($" DllName = {dirEntry.DllName.Resolve()}, RVA = {dirEntry.DllName.RVA()}"); writer.WriteLine($" Attributes = {dirEntry.Attributes}"); - writer.WriteLine($" DelayImportAddressTable = {dirEntry.DelayImportAddressTable}"); - writer.WriteLine($" DelayImportNameTable = {dirEntry.DelayImportNameTable}"); - writer.WriteLine($" BoundImportAddressTable = {dirEntry.BoundImportAddressTable}"); - writer.WriteLine($" UnloadDelayInformationTable = {dirEntry.UnloadDelayInformationTable}"); + writer.WriteLine($" DelayImportAddressTable RVA = {dirEntry.DelayImportAddressTable.RVA}"); + writer.WriteLine($" DelayImportNameTable RVA = {dirEntry.DelayImportNameTable.RVA}"); + writer.WriteLine($" BoundImportAddressTable RVA = {(dirEntry.BoundImportAddressTable?.RVA ?? (RVA)0)}"); + writer.WriteLine($" UnloadDelayInformationTable RVA = {(dirEntry.UnloadDelayInformationTable?.RVA ?? (RVA)0)}"); } } @@ -387,6 +392,119 @@ private static void Print(PEFile file, PELoadConfigDirectory data, TextWriter wr ArgumentNullException.ThrowIfNull(data); ArgumentNullException.ThrowIfNull(writer); + const int indent = -32; + if (data.Is32Bits) + { + writer.WriteLine($" {nameof(PELoadConfigDirectory32.Size),indent} = 0x{data.LoadConfigDirectory32.Size:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.TimeDateStamp),indent} = 0x{data.LoadConfigDirectory32.TimeDateStamp:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.MajorVersion),indent} = {data.LoadConfigDirectory32.MajorVersion}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.MinorVersion),indent} = {data.LoadConfigDirectory32.MinorVersion}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GlobalFlagsClear),indent} = 0x{data.LoadConfigDirectory32.GlobalFlagsClear:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GlobalFlagsSet),indent} = 0x{data.LoadConfigDirectory32.GlobalFlagsSet:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.CriticalSectionDefaultTimeout),indent} = 0x{data.LoadConfigDirectory32.CriticalSectionDefaultTimeout:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.DeCommitFreeBlockThreshold),indent} = 0x{data.LoadConfigDirectory32.DeCommitFreeBlockThreshold:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.DeCommitTotalFreeThreshold),indent} = 0x{data.LoadConfigDirectory32.DeCommitTotalFreeThreshold:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.LockPrefixTable),indent} = 0x{data.LoadConfigDirectory32.LockPrefixTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.MaximumAllocationSize),indent} = 0x{data.LoadConfigDirectory32.MaximumAllocationSize:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.VirtualMemoryThreshold),indent} = 0x{data.LoadConfigDirectory32.VirtualMemoryThreshold:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.ProcessAffinityMask),indent} = 0x{data.LoadConfigDirectory32.ProcessAffinityMask:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.ProcessHeapFlags),indent} = 0x{data.LoadConfigDirectory32.ProcessHeapFlags:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.CSDVersion),indent} = {data.LoadConfigDirectory32.CSDVersion}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.DependentLoadFlags),indent} = 0x{data.LoadConfigDirectory32.DependentLoadFlags:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.EditList),indent} = {data.LoadConfigDirectory32.EditList}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.SecurityCookie),indent} = {data.LoadConfigDirectory32.SecurityCookie}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.SEHandlerTable),indent} = {data.LoadConfigDirectory32.SEHandlerTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.SEHandlerCount),indent} = 0x{data.LoadConfigDirectory32.SEHandlerCount:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardCFCheckFunctionPointer),indent} = {data.LoadConfigDirectory32.GuardCFCheckFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardCFDispatchFunctionPointer),indent} = {data.LoadConfigDirectory32.GuardCFDispatchFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardCFFunctionTable),indent} = {data.LoadConfigDirectory32.GuardCFFunctionTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardCFFunctionCount),indent} = 0x{data.LoadConfigDirectory32.GuardCFFunctionCount:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardFlags),indent} = {data.LoadConfigDirectory32.GuardFlags}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.TableSizeShift),indent} = 0x{data.LoadConfigDirectory32.TableSizeShift:X}"); + writer.WriteLine($" {$"{nameof(PELoadConfigDirectory32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Flags)}",indent} = 0x{data.LoadConfigDirectory32.CodeIntegrity.Flags:X}"); + writer.WriteLine($" {$"{nameof(PELoadConfigDirectory32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Catalog)}",indent} = 0x{data.LoadConfigDirectory32.CodeIntegrity.Catalog:X}"); + writer.WriteLine($" {$"{nameof(PELoadConfigDirectory32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.CatalogOffset)}",indent} = 0x{data.LoadConfigDirectory32.CodeIntegrity.CatalogOffset:X}"); + writer.WriteLine($" {$"{nameof(PELoadConfigDirectory32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Reserved)}",indent} = 0x{data.LoadConfigDirectory32.CodeIntegrity.Reserved:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardAddressTakenIatEntryTable),indent} = {data.LoadConfigDirectory32.GuardAddressTakenIatEntryTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardAddressTakenIatEntryCount),indent} = 0x{data.LoadConfigDirectory32.GuardAddressTakenIatEntryCount:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardLongJumpTargetTable),indent} = {data.LoadConfigDirectory32.GuardLongJumpTargetTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardLongJumpTargetCount),indent} = 0x{data.LoadConfigDirectory32.GuardLongJumpTargetCount:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.DynamicValueRelocTable),indent} = {data.LoadConfigDirectory32.DynamicValueRelocTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.CHPEMetadataPointer),indent} = {data.LoadConfigDirectory32.CHPEMetadataPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardRFFailureRoutine),indent} = {data.LoadConfigDirectory32.GuardRFFailureRoutine}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardRFFailureRoutineFunctionPointer),indent} = {data.LoadConfigDirectory32.GuardRFFailureRoutineFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.DynamicValueRelocTableOffset),indent} = 0x{data.LoadConfigDirectory32.DynamicValueRelocTableOffset:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.DynamicValueRelocTableSection),indent} = {data.LoadConfigDirectory32.DynamicValueRelocTableSection}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.Reserved2),indent} = {data.LoadConfigDirectory32.Reserved2}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardRFVerifyStackPointerFunctionPointer),indent} = {data.LoadConfigDirectory32.GuardRFVerifyStackPointerFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.HotPatchTableOffset),indent} = 0x{data.LoadConfigDirectory32.HotPatchTableOffset:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.Reserved3),indent} = 0x{data.LoadConfigDirectory32.Reserved3:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.EnclaveConfigurationPointer),indent} = {data.LoadConfigDirectory32.EnclaveConfigurationPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.VolatileMetadataPointer),indent} = {data.LoadConfigDirectory32.VolatileMetadataPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardEHContinuationTable),indent} = {data.LoadConfigDirectory32.GuardEHContinuationTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardEHContinuationCount),indent} = 0x{data.LoadConfigDirectory32.GuardEHContinuationCount}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardXFGCheckFunctionPointer),indent} = {data.LoadConfigDirectory32.GuardXFGCheckFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardXFGDispatchFunctionPointer),indent} = {data.LoadConfigDirectory32.GuardXFGDispatchFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardXFGTableDispatchFunctionPointer),indent} = {data.LoadConfigDirectory32.GuardXFGTableDispatchFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.CastGuardOsDeterminedFailureMode),indent} = {data.LoadConfigDirectory32.CastGuardOsDeterminedFailureMode}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory32.GuardMemcpyFunctionPointer),indent} = {data.LoadConfigDirectory32.GuardMemcpyFunctionPointer}"); + } + else + { + writer.WriteLine($" {nameof(PELoadConfigDirectory64.Size),indent} = 0x{data.LoadConfigDirectory64.Size:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.TimeDateStamp),indent} = 0x{data.LoadConfigDirectory64.TimeDateStamp:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.MajorVersion),indent} = {data.LoadConfigDirectory64.MajorVersion}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.MinorVersion),indent} = {data.LoadConfigDirectory64.MinorVersion}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GlobalFlagsClear),indent} = 0x{data.LoadConfigDirectory64.GlobalFlagsClear:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GlobalFlagsSet),indent} = 0x{data.LoadConfigDirectory64.GlobalFlagsSet:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.CriticalSectionDefaultTimeout),indent} = 0x{data.LoadConfigDirectory64.CriticalSectionDefaultTimeout:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.DeCommitFreeBlockThreshold),indent} = 0x{data.LoadConfigDirectory64.DeCommitFreeBlockThreshold:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.DeCommitTotalFreeThreshold),indent} = 0x{data.LoadConfigDirectory64.DeCommitTotalFreeThreshold:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.LockPrefixTable),indent} = 0x{data.LoadConfigDirectory64.LockPrefixTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.MaximumAllocationSize),indent} = 0x{data.LoadConfigDirectory64.MaximumAllocationSize:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.VirtualMemoryThreshold),indent} = 0x{data.LoadConfigDirectory64.VirtualMemoryThreshold:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.ProcessAffinityMask),indent} = 0x{data.LoadConfigDirectory64.ProcessAffinityMask:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.ProcessHeapFlags),indent} = 0x{data.LoadConfigDirectory64.ProcessHeapFlags:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.CSDVersion),indent} = {data.LoadConfigDirectory64.CSDVersion}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.DependentLoadFlags),indent} = 0x{data.LoadConfigDirectory64.DependentLoadFlags:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.EditList),indent} = {data.LoadConfigDirectory64.EditList}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.SecurityCookie),indent} = {data.LoadConfigDirectory64.SecurityCookie}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.SEHandlerTable),indent} = {data.LoadConfigDirectory64.SEHandlerTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.SEHandlerCount),indent} = 0x{data.LoadConfigDirectory64.SEHandlerCount:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardCFCheckFunctionPointer),indent} = {data.LoadConfigDirectory64.GuardCFCheckFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardCFDispatchFunctionPointer),indent} = {data.LoadConfigDirectory64.GuardCFDispatchFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardCFFunctionTable),indent} = {data.LoadConfigDirectory64.GuardCFFunctionTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardCFFunctionCount),indent} = 0x{data.LoadConfigDirectory64.GuardCFFunctionCount:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardFlags),indent} = {data.LoadConfigDirectory64.GuardFlags}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.TableSizeShift),indent} = 0x{data.LoadConfigDirectory64.TableSizeShift:X}"); + writer.WriteLine($" {$"{nameof(PELoadConfigDirectory64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Flags)}",indent} = 0x{data.LoadConfigDirectory64.CodeIntegrity.Flags:X}"); + writer.WriteLine($" {$"{nameof(PELoadConfigDirectory64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Catalog)}",indent} = 0x{data.LoadConfigDirectory64.CodeIntegrity.Catalog:X}"); + writer.WriteLine($" {$"{nameof(PELoadConfigDirectory64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.CatalogOffset)}",indent} = 0x{data.LoadConfigDirectory64.CodeIntegrity.CatalogOffset:X}"); + writer.WriteLine($" {$"{nameof(PELoadConfigDirectory64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Reserved)}",indent} = 0x{data.LoadConfigDirectory64.CodeIntegrity.Reserved:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardAddressTakenIatEntryTable),indent} = {data.LoadConfigDirectory64.GuardAddressTakenIatEntryTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardAddressTakenIatEntryCount),indent} = 0x{data.LoadConfigDirectory64.GuardAddressTakenIatEntryCount:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardLongJumpTargetTable),indent} = {data.LoadConfigDirectory64.GuardLongJumpTargetTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardLongJumpTargetCount),indent} = 0x{data.LoadConfigDirectory64.GuardLongJumpTargetCount:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.DynamicValueRelocTable),indent} = {data.LoadConfigDirectory64.DynamicValueRelocTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.CHPEMetadataPointer),indent} = {data.LoadConfigDirectory64.CHPEMetadataPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardRFFailureRoutine),indent} = {data.LoadConfigDirectory64.GuardRFFailureRoutine}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardRFFailureRoutineFunctionPointer),indent} = {data.LoadConfigDirectory64.GuardRFFailureRoutineFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.DynamicValueRelocTableOffset),indent} = 0x{data.LoadConfigDirectory64.DynamicValueRelocTableOffset:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.DynamicValueRelocTableSection),indent} = {data.LoadConfigDirectory64.DynamicValueRelocTableSection}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.Reserved2),indent} = {data.LoadConfigDirectory64.Reserved2}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardRFVerifyStackPointerFunctionPointer),indent} = {data.LoadConfigDirectory64.GuardRFVerifyStackPointerFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.HotPatchTableOffset),indent} = 0x{data.LoadConfigDirectory64.HotPatchTableOffset:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.Reserved3),indent} = 0x{data.LoadConfigDirectory64.Reserved3:X}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.EnclaveConfigurationPointer),indent} = {data.LoadConfigDirectory64.EnclaveConfigurationPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.VolatileMetadataPointer),indent} = {data.LoadConfigDirectory64.VolatileMetadataPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardEHContinuationTable),indent} = {data.LoadConfigDirectory64.GuardEHContinuationTable}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardEHContinuationCount),indent} = 0x{data.LoadConfigDirectory64.GuardEHContinuationCount}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardXFGCheckFunctionPointer),indent} = {data.LoadConfigDirectory64.GuardXFGCheckFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardXFGDispatchFunctionPointer),indent} = {data.LoadConfigDirectory64.GuardXFGDispatchFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardXFGTableDispatchFunctionPointer),indent} = {data.LoadConfigDirectory64.GuardXFGTableDispatchFunctionPointer}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.CastGuardOsDeterminedFailureMode),indent} = {data.LoadConfigDirectory64.CastGuardOsDeterminedFailureMode}"); + writer.WriteLine($" {nameof(PELoadConfigDirectory64.GuardMemcpyFunctionPointer),indent} = {data.LoadConfigDirectory64.GuardMemcpyFunctionPointer}"); + } } private static void Print(PEFile file, PEResourceDirectory data, TextWriter writer) @@ -485,18 +603,20 @@ private static void Print(PEFile file, PEStreamSectionData data, TextWriter writ } - private static string PEDescribe(PEObjectBase peObjectBase) + private static string PEDescribe(PEObjectBase? peObjectBase) { const int indent = -32; if (peObjectBase is PEObject peObject) { return $"{peObject.GetType().Name, indent} Position = 0x{peObject.Position:X8}, Size = 0x{peObject.Size:X8}, RVA = 0x{peObject.RVA.Value:X8}, VirtualSize = 0x{peObject.VirtualSize:X8}"; } - else + else if (peObjectBase is not null) { return $"{peObjectBase.GetType().Name,indent} Position = 0x{peObjectBase.Position:X8}, Size = 0x{peObjectBase.Size:X8}"; } + else + { + return "null"; + } } - - } \ No newline at end of file From d3f6062523e5fdc192758ce9a03e07dc12d8ced7 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 24 Sep 2024 08:48:19 +0200 Subject: [PATCH 49/87] Improve handling of directories dependent on 32/64 bits --- .../PE/DataDirectory/PEBaseRelocation.cs | 104 +++---- .../PE/DataDirectory/PEBaseRelocationBlock.cs | 43 +-- .../PEBaseRelocationPageBlockPart.cs | 125 -------- .../PEBoundImportAddressTable32.cs | 14 + .../PEBoundImportAddressTable64.cs | 14 + .../PE/DataDirectory/PEDataDirectory.cs | 6 +- .../PE/DataDirectory/PELoadConfigDirectory.cs | 105 ++----- .../DataDirectory/PELoadConfigDirectory32.cs | 76 +---- .../DataDirectory/PELoadConfigDirectory64.cs | 77 +---- .../PELoadConfigDirectoryData32.cs | 78 +++++ .../PELoadConfigDirectoryData64.cs | 78 +++++ .../PE/DataDirectory/PERawDataDirectory.cs | 102 +++++++ .../PE/DataDirectory/PETlsDirectory.cs | 75 +---- .../PE/DataDirectory/PETlsDirectory32.cs | 63 ++-- .../PE/DataDirectory/PETlsDirectory64.cs | 61 ++-- .../PE/DataDirectory/PETlsDirectoryData32.cs | 58 ++++ .../PE/DataDirectory/PETlsDirectoryData64.cs | 58 ++++ .../PE/Internal/RawImageBaseRelocation.cs | 37 +++ src/LibObjectFile/PE/PEFile.Read.cs | 2 +- src/LibObjectFile/PE/PEPrinter.cs | 274 ++++++++++-------- src/LibObjectFile/Utils/DataUtils.cs | 61 +--- 21 files changed, 740 insertions(+), 771 deletions(-) delete mode 100644 src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectoryData32.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectoryData64.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PETlsDirectoryData32.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PETlsDirectoryData64.cs create mode 100644 src/LibObjectFile/PE/Internal/RawImageBaseRelocation.cs diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs index ceaa23b..4ebbdc1 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs @@ -3,84 +3,58 @@ // See the license.txt file in the project root for more information. using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace LibObjectFile.PE; -#pragma warning disable CS0649 /// /// A base relocation in a Portable Executable (PE) image. /// -public readonly struct PEBaseRelocation : IEquatable +public readonly record struct PEBaseRelocation(PEBaseRelocationType Type, PESectionDataLink Link) { - public const ushort MaxVirtualOffset = (1 << 12) - 1; - private const ushort TypeMask = unchecked((ushort)(~MaxVirtualOffset)); - private const ushort VirtualOffsetMask = MaxVirtualOffset; - - private readonly ushort _value; - - public PEBaseRelocation(PEBaseRelocationType type, ushort virtualOffset) + /// + /// Reads the address from the section data. + /// + /// The PE file. + /// The address read from the section data. + /// The section data link is not set or the type is not supported. + public ulong ReadAddress(PEFile file) { - if (virtualOffset > MaxVirtualOffset) + if (Link.SectionData is null) { - ThrowVirtualOffsetOutOfRange(); + throw new InvalidOperationException("The section data link is not set"); } - _value = (ushort)((ushort)type | virtualOffset); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal PEBaseRelocation(ushort value) => _value = value; - - /// - /// Gets a value indicating whether the base relocation is zero (used for padding). - /// - public bool IsZero => _value == 0; - - /// - /// Gets the type of the base relocation. - /// - public PEBaseRelocationType Type => (PEBaseRelocationType)(_value & TypeMask); - - /// - /// Gets the virtual offset of the base relocation relative to the offset of the associated . - /// - public ushort OffsetInBlockPart => (ushort)(_value & VirtualOffsetMask); - - /// - public bool Equals(PEBaseRelocation other) => _value == other._value; - /// - public override bool Equals(object? obj) => obj is PEBaseRelocation other && Equals(other); + if (Type != PEBaseRelocationType.Dir64) + { + throw new InvalidOperationException($"The base relocation type {Type} not supported. Only Dir64 is supported for this method."); + } + + if (file.IsPE32) + { + VA32 va = default; + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref va, 1)); - /// - public override int GetHashCode() => _value.GetHashCode(); + int read = Link.SectionData!.ReadAt(Link.RVO, span); + if (read != 4) + { + throw new InvalidOperationException($"Unable to read the VA32 from the section data type: {Link.SectionData.GetType().FullName}"); + } - /// - /// Compares two objects for equality. - /// - /// The left value to compare. - /// The right value to compare. - /// true if values are equal; otherwise false. - public static bool operator ==(PEBaseRelocation left, PEBaseRelocation right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The left value to compare. - /// The right value to compare. - /// true if values are not equal; otherwise false. - public static bool operator !=(PEBaseRelocation left, PEBaseRelocation right) => !left.Equals(right); + return va; + } + else + { + VA64 va = default; + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref va, 1)); - /// - public override string ToString() - { - return IsZero ? "Zero Padding" : $"{Type} {OffsetInBlockPart}"; - } + int read = Link.SectionData!.ReadAt(Link.RVO, span); + if (read != 8) + { + throw new InvalidOperationException($"Unable to read the VA64 from the section data type: {Link.SectionData.GetType().FullName}"); + } - [DoesNotReturn, MethodImpl(MethodImplOptions.NoInlining)] - private static void ThrowVirtualOffsetOutOfRange() - { - throw new ArgumentOutOfRangeException(nameof(OffsetInBlockPart), $"The virtual offset must be less than {MaxVirtualOffset}"); + return va; + } } -} \ No newline at end of file +} diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs index e038428..e13caa8 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using LibObjectFile.Diagnostics; +using LibObjectFile.PE.Internal; using System; using System.Collections.Generic; using System.Diagnostics; @@ -22,6 +23,7 @@ public sealed class PEBaseRelocationBlock /// The section link. public PEBaseRelocationBlock(PESectionLink sectionLink) { + ArgumentNullException.ThrowIfNull(sectionLink.Section, nameof(sectionLink)); SectionLink = sectionLink; } @@ -33,7 +35,7 @@ public PEBaseRelocationBlock(PESectionLink sectionLink) /// /// Gets the list of relocations for this block. /// - public List Parts { get; } = new(); + public List Relocations { get; } = new(); /// /// Internal buffer used to read the block before transforming it into parts. @@ -43,7 +45,7 @@ public PEBaseRelocationBlock(PESectionLink sectionLink) /// /// Gets the size of this block. /// - internal uint CalculateSizeOf() + internal unsafe uint CalculateSizeOf() { // If we have a block buffer (when reading an image), use it directly until it is transformed into parts if (!BlockBuffer.IsEmpty) @@ -51,20 +53,14 @@ internal uint CalculateSizeOf() return (uint)BlockBuffer.Length; } - uint size = 0; - foreach (var part in Parts) - { - size += part.SizeOf; - } - - return size; + return (uint)(Relocations.Count * sizeof(ushort)); } internal void ReadAndBind(PEImageReader reader) { var buffer = BlockBuffer; - var relocSpan = MemoryMarshal.Cast(buffer.Span); + var relocSpan = MemoryMarshal.Cast(buffer.Span); // Remove padding zeros at the end of the block if (relocSpan.Length > 0 && relocSpan[^1].IsZero) @@ -72,11 +68,9 @@ internal void ReadAndBind(PEImageReader reader) relocSpan = relocSpan.Slice(0, relocSpan.Length - 1); } - PEBaseRelocationPageBlockPart? currentBlockPart = null; + var section = SectionLink.Section!; var blockBaseAddress = SectionLink.RVA(); - var peFile = reader.File; - // Iterate on all relocations foreach (var relocation in relocSpan) { @@ -88,30 +82,15 @@ internal void ReadAndBind(PEImageReader reader) var va = blockBaseAddress + relocation.OffsetInBlockPart; // Find the section data containing the virtual address - if (!peFile.TryFindContainerByRVA(va, out var vObj)) + if (!section.TryFindSectionData(va, out var sectionData)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section data containing the virtual address 0x{va:X4}"); continue; } - var sectionData = vObj as PESectionData; - if (sectionData is null) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"The section data containing the virtual address 0x{va:X4} is not a section data"); - continue; - } - var offsetInSectionData = va - sectionData.RVA; - - // Create a new block part if the section data is different, or it is the first relocation - if (currentBlockPart is null || currentBlockPart.SectionDataLink.SectionData != sectionData) - { - currentBlockPart = new PEBaseRelocationPageBlockPart(new(sectionData, offsetInSectionData)); - Parts.Add(currentBlockPart); - } - - var newRelocation = new PEBaseRelocation(relocation.Type, (ushort)(offsetInSectionData - currentBlockPart.SectionDataLink.RVO)); - currentBlockPart.Relocations.Add(newRelocation); + var newRelocation = new PEBaseRelocation(relocation.Type, new(sectionData, offsetInSectionData)); + Relocations.Add(newRelocation); } // Clear the buffer, as we don't need it anymore @@ -120,6 +99,6 @@ internal void ReadAndBind(PEImageReader reader) public override string ToString() { - return $"{nameof(PEBaseRelocationBlock)}, Section = {SectionLink.Section?.Name}, RVA = {SectionLink.RVA()}, Size = {CalculateSizeOf()}, Parts[{Parts.Count}]"; + return $"{nameof(PEBaseRelocationBlock)}, Section = {SectionLink.Section?.Name}, RVA = {SectionLink.RVA()}, Size = {CalculateSizeOf()}, Relocations[{Relocations.Count}]"; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs deleted file mode 100644 index 5a018f8..0000000 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationPageBlockPart.cs +++ /dev/null @@ -1,125 +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. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace LibObjectFile.PE; - -/// -/// A block of base relocations for a page. -/// -[DebuggerDisplay("{nameof(PEBaseRelocationPageBlock),nq} Link = {SectionDataLink}, Relocations[{Relocations.Count}]")] -public sealed class PEBaseRelocationPageBlockPart -{ - public PEBaseRelocationPageBlockPart(PESectionDataLink sectionDataLink) - { - SectionDataLink = sectionDataLink; - Relocations = new List(); - } - - internal PEBaseRelocationPageBlockPart(PESectionDataLink sectionDataLink, List relocations) - { - SectionDataLink = sectionDataLink; - Relocations = relocations; - } - - /// - /// Gets or sets the linked and its virtual offset within it. - /// - public PESectionDataLink SectionDataLink { get; } - - /// - /// Gets the list of relocations for this block. - /// - public List Relocations { get; } - - - /// - /// Gets the RVA of a relocation that belongs to this part. - /// - /// The relocation. - /// The RVA of the relocation. - public RVA GetRVA(PEBaseRelocation relocation) - => SectionDataLink.Container is null ? 0 : SectionDataLink.Container.RVA + SectionDataLink.RVO + relocation.OffsetInBlockPart; - - /// - /// Read the address from the relocation. - /// - /// A relocation that belongs to this block. - /// The address read from the relocation. - /// If the section data or the PE file is not found. - public ulong ReadAddress(PEBaseRelocation relocation) - { - var sectionData = SectionDataLink.SectionData; - if (sectionData is null) - { - throw new InvalidOperationException("Cannot read address from a relocation without a section data"); - } - - var peFile = sectionData.GetPEFile(); - if (peFile is null) - { - throw new InvalidOperationException("Cannot read address from a relocation without a PE file"); - } - - return ReadAddress(peFile, relocation); - } - - /// - /// Read the address from the relocation. - /// - /// The PE file containing the section data. - /// A relocation that belongs to this block. - /// The address read from the relocation. - /// If the section data or the PE file is not found. - public ulong ReadAddress(PEFile peFile, PEBaseRelocation relocation) - { - ArgumentNullException.ThrowIfNull(peFile); - - var sectionData = SectionDataLink.SectionData; - if (sectionData is null) - { - throw new InvalidOperationException("Cannot read address from a relocation without a section data"); - } - - var is32 = peFile.IsPE32; - - if (is32) - { - uint address = 0; - Span buffer = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref address, 1)); - sectionData.ReadAt(SectionDataLink.RVO + relocation.OffsetInBlockPart, buffer); - return address; - } - else - { - ulong address = 0; - Span buffer = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref address, 1)); - sectionData.ReadAt(SectionDataLink.RVO + relocation.OffsetInBlockPart, buffer); - return address; - } - } - - /// - /// Gets the size of this block. - /// - internal uint SizeOf - { - get - { - var size = sizeof(uint) + sizeof(uint) + (Relocations.Count + 1) * sizeof(ushort); - // Align to 4 bytes - size = (size + 3) & ~3; - return (uint)size; - } - } - - public override string ToString() - { - return $"{nameof(PEBaseRelocationBlock)} Link = {SectionDataLink}, Relocations[{Relocations.Count}]"; - } -} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable32.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable32.cs index c07a4c5..1d1817a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable32.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable32.cs @@ -2,8 +2,10 @@ // 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; using System.Runtime.InteropServices; +using LibObjectFile.Utils; namespace LibObjectFile.PE; @@ -18,4 +20,16 @@ public sealed class PEBoundImportAddressTable32() : PEBoundImportAddressTable(tr public override int Count => Entries.Count; internal override void SetCount(int count) => CollectionsMarshal.SetCount(Entries, count); + + public override int ReadAt(uint offset, Span destination) + { + var buffer = MemoryMarshal.AsBytes(CollectionsMarshal.AsSpan(Entries)); + return DataUtils.ReadAt(buffer, offset, destination); + } + + public override void WriteAt(uint offset, ReadOnlySpan source) + { + var buffer = MemoryMarshal.AsBytes(CollectionsMarshal.AsSpan(Entries)); + DataUtils.WriteAt(buffer, offset, source); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable64.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable64.cs index 79cdd59..0441aac 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable64.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable64.cs @@ -2,6 +2,8 @@ // 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.Collections.Generic; using System.Runtime.InteropServices; @@ -18,4 +20,16 @@ public sealed class PEBoundImportAddressTable64() : PEBoundImportAddressTable(fa public override int Count => Entries.Count; internal override void SetCount(int count) => CollectionsMarshal.SetCount(Entries, count); + + public override int ReadAt(uint offset, Span destination) + { + var buffer = MemoryMarshal.AsBytes(CollectionsMarshal.AsSpan(Entries)); + return DataUtils.ReadAt(buffer, offset, destination); + } + + public override void WriteAt(uint offset, ReadOnlySpan source) + { + var buffer = MemoryMarshal.AsBytes(CollectionsMarshal.AsSpan(Entries)); + DataUtils.WriteAt(buffer, offset, source); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index 7313afd..21a9c47 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -96,7 +96,7 @@ protected override void UpdateRVAInChildren() /// /// The kind of PE directory entry. /// A PE directory entry. - internal static PEDataDirectory Create(PEDataDirectoryKind kind) + internal static PEDataDirectory Create(PEDataDirectoryKind kind, bool is32Bits) { return kind switch { @@ -108,8 +108,8 @@ internal static PEDataDirectory Create(PEDataDirectoryKind kind) PEDataDirectoryKind.Debug => new PEDebugDirectory(), PEDataDirectoryKind.Architecture => new PEArchitectureDirectory(), PEDataDirectoryKind.GlobalPointer => new PEGlobalPointerDirectory(), - PEDataDirectoryKind.Tls => new PETlsDirectory(), - PEDataDirectoryKind.LoadConfig => new PELoadConfigDirectory(), + PEDataDirectoryKind.Tls => is32Bits ? new PETlsDirectory32() : new PETlsDirectory64(), + PEDataDirectoryKind.LoadConfig => is32Bits ? new PELoadConfigDirectory32() : new PELoadConfigDirectory64(), PEDataDirectoryKind.BoundImport => new PEBoundImportDirectory(), PEDataDirectoryKind.DelayImport => new PEDelayImportDirectory(), PEDataDirectoryKind.ImportAddressTable => new PEImportAddressTableDirectory(), diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs index b900796..64ce6a1 100644 --- a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs @@ -4,6 +4,7 @@ using LibObjectFile.Diagnostics; using System; +using System.Runtime.InteropServices; using LibObjectFile.Utils; namespace LibObjectFile.PE; @@ -11,103 +12,35 @@ namespace LibObjectFile.PE; /// /// Represents the Load Configuration Directory for a PE file. /// -public sealed class PELoadConfigDirectory : PEDataDirectory +public abstract class PELoadConfigDirectory : PERawDataDirectory { /// /// Initializes a new instance of the class. /// - public PELoadConfigDirectory() : base(PEDataDirectoryKind.LoadConfig) + private protected PELoadConfigDirectory(int minSize) : base(PEDataDirectoryKind.LoadConfig, minSize) { } /// - /// Initializes a new instance of the class with the specified bitness. + /// Gets the config size. /// - /// A value indicating whether the directory is 32-bit or 64-bit. - public unsafe PELoadConfigDirectory(bool is32Bits) : base(PEDataDirectoryKind.LoadConfig) - { - Is32Bits = is32Bits; - LoadConfigDirectory32.Size = (uint)sizeof(PELoadConfigDirectory32); - LoadConfigDirectory64.Size = (uint)sizeof(PELoadConfigDirectory64); - } - - /// - /// Gets a value indicating whether the directory is 32-bit or 64-bit. - /// - public bool Is32Bits { get; private set; } - - /// - /// Gets or sets the 32-bit Load Configuration Directory. - /// - public PELoadConfigDirectory32 LoadConfigDirectory32; - - /// - /// Gets or sets the 64-bit Load Configuration Directory. - /// - public PELoadConfigDirectory64 LoadConfigDirectory64; - + /// + /// Use to set the config size. + /// + public sealed override int RawDataSize => (int)MemoryMarshal.Read(RawData); + /// - /// Gets or sets the additional data after the Load Configuration Directory. + /// Sets the config size. /// - public byte[]? AdditionalData { get; set; } - - /// - protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) - { - return (Is32Bits ? LoadConfigDirectory32.Size : LoadConfigDirectory64.Size) + (uint)(AdditionalData?.Length ?? 0); - } - - /// - public override unsafe void Read(PEImageReader reader) + /// The new config size. + /// + /// The underlying buffer will be resized if necessary. + /// + public sealed override void SetRawDataSize(int value) { - Is32Bits = reader.File.IsPE32; - reader.Position = Position; - - int size = (int)Size; - if (Is32Bits) - { - size = Math.Min(size, sizeof(PELoadConfigDirectory32)); - if (!reader.TryReadData(size, out LoadConfigDirectory32)) - { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PELoadConfigDirectory32)}"); - return; - } - } - else - { - size = Math.Min(size, sizeof(PELoadConfigDirectory64)); - if (!reader.TryReadData(size, out LoadConfigDirectory64)) - { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PELoadConfigDirectory64)}"); - return; - } - } - - // If we have more data than expected, we read it as additional data - if ((int)Size > size) - { - AdditionalData = new byte[(int)Size - size]; - int read = reader.Read(AdditionalData); + base.SetRawDataSize(value); - if (read != AdditionalData.Length) - { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading additional data in {nameof(PELoadConfigDirectory)}"); - return; - } - } - - HeaderSize = ComputeHeaderSize(reader); + // Write back the configuration size + MemoryMarshal.Write(RawData, value); } - - /// - public override void Write(PEImageWriter writer) - { - throw new NotImplementedException(); - } - - /// - public override int ReadAt(uint offset, Span destination) => DataUtils.ReadAt(Is32Bits, ref LoadConfigDirectory32, ref LoadConfigDirectory64, offset, destination); - - /// - public override void WriteAt(uint offset, ReadOnlySpan source) => DataUtils.WriteAt(Is32Bits, ref LoadConfigDirectory32, ref LoadConfigDirectory64, offset, source); -} +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs index c10064d..28b2e59 100644 --- a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs @@ -2,74 +2,26 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + namespace LibObjectFile.PE; /// -/// A structure representing the Load Configuration Directory for a PE32 file. +/// Represents the Load Configuration Directory for a PE32 file. /// -public struct PELoadConfigDirectory32 +public class PELoadConfigDirectory32 : PELoadConfigDirectory { - public uint Size; - public uint TimeDateStamp; - public ushort MajorVersion; - public ushort MinorVersion; - public uint GlobalFlagsClear; - public uint GlobalFlagsSet; - public uint CriticalSectionDefaultTimeout; - public uint DeCommitFreeBlockThreshold; - public uint DeCommitTotalFreeThreshold; - public VA32 LockPrefixTable; // VA - public uint MaximumAllocationSize; - public uint VirtualMemoryThreshold; - public uint ProcessHeapFlags; - public uint ProcessAffinityMask; - public ushort CSDVersion; - public ushort DependentLoadFlags; - public VA32 EditList; // VA - public VA32 SecurityCookie; // VA - public VA32 SEHandlerTable; // VA - public uint SEHandlerCount; - public VA32 GuardCFCheckFunctionPointer; // VA - public VA32 GuardCFDispatchFunctionPointer; // VA - public VA32 GuardCFFunctionTable; // VA - public uint GuardCFFunctionCount; - - private uint _rawGuardFlags; - - public PEGuardFlags GuardFlags + /// + /// Initializes a new instance of the class. + /// + public unsafe PELoadConfigDirectory32() : base(sizeof(PELoadConfigDirectoryData32)) { - get => (PEGuardFlags)(_rawGuardFlags & ~0xF000_0000U); - set => _rawGuardFlags = (_rawGuardFlags & 0xF000_0000U) | (uint)value; + SetRawDataSize(sizeof(PELoadConfigDirectoryData32)); } - public int TableSizeShift - { - get => (int)(_rawGuardFlags >>> 28); - set => _rawGuardFlags = (_rawGuardFlags & 0x0FFF_FFFFU) | ((uint)value << 28); - } - - public PELoadConfigCodeIntegrity CodeIntegrity; - public VA32 GuardAddressTakenIatEntryTable; // VA - public uint GuardAddressTakenIatEntryCount; - public VA32 GuardLongJumpTargetTable; // VA - public uint GuardLongJumpTargetCount; - public VA32 DynamicValueRelocTable; // VA - public uint CHPEMetadataPointer; - public VA32 GuardRFFailureRoutine; // VA - public VA32 GuardRFFailureRoutineFunctionPointer; // VA - public uint DynamicValueRelocTableOffset; - public ushort DynamicValueRelocTableSection; - public ushort Reserved2; - public VA32 GuardRFVerifyStackPointerFunctionPointer; // VA - public uint HotPatchTableOffset; - public uint Reserved3; - public VA32 EnclaveConfigurationPointer; // VA - public VA32 VolatileMetadataPointer; // VA - public VA32 GuardEHContinuationTable; // VA - public uint GuardEHContinuationCount; - public VA32 GuardXFGCheckFunctionPointer; // VA - public VA32 GuardXFGDispatchFunctionPointer; // VA - public VA32 GuardXFGTableDispatchFunctionPointer; // VA - public VA32 CastGuardOsDeterminedFailureMode; // VA - public VA32 GuardMemcpyFunctionPointer; // VA + /// + /// Gets the 32-bit Load Configuration Directory. + /// + public ref PELoadConfigDirectoryData32 Data => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs index 0d253a2..b391534 100644 --- a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs @@ -2,74 +2,27 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + namespace LibObjectFile.PE; /// -/// A structure representing the Load Configuration Directory for a PE64 file. +/// Represents the Load Configuration Directory for a PE64 file. /// -public struct PELoadConfigDirectory64 +public class PELoadConfigDirectory64 : PELoadConfigDirectory { - public uint Size; - public uint TimeDateStamp; - public ushort MajorVersion; - public ushort MinorVersion; - public uint GlobalFlagsClear; - public uint GlobalFlagsSet; - public uint CriticalSectionDefaultTimeout; - public ulong DeCommitFreeBlockThreshold; - public ulong DeCommitTotalFreeThreshold; - public VA64 LockPrefixTable; // VA - public ulong MaximumAllocationSize; - public ulong VirtualMemoryThreshold; - public ulong ProcessAffinityMask; - public uint ProcessHeapFlags; - public ushort CSDVersion; - public ushort DependentLoadFlags; - public VA64 EditList; // VA - public VA64 SecurityCookie; // VA - public VA64 SEHandlerTable; // VA - public ulong SEHandlerCount; - public VA64 GuardCFCheckFunctionPointer; // VA - public VA64 GuardCFDispatchFunctionPointer; // VA - public VA64 GuardCFFunctionTable; // VA - public ulong GuardCFFunctionCount; - - private uint _rawGuardFlags; - - public PEGuardFlags GuardFlags - { - get => (PEGuardFlags)(_rawGuardFlags & ~0xF000_0000U); - set => _rawGuardFlags = (_rawGuardFlags & 0xF000_0000U) | (uint)value; - } - - public int TableSizeShift + /// + /// Initializes a new instance of the class. + /// + public unsafe PELoadConfigDirectory64() : base(sizeof(PELoadConfigDirectoryData64)) { - get => (int)(_rawGuardFlags >>> 28); - set => _rawGuardFlags = (_rawGuardFlags & 0x0FFF_FFFFU) | ((uint)value << 28); + // ReSharper disable once VirtualMemberCallInConstructor + SetRawDataSize(sizeof(PELoadConfigDirectoryData64)); } - public PELoadConfigCodeIntegrity CodeIntegrity; - public VA64 GuardAddressTakenIatEntryTable; // VA - public ulong GuardAddressTakenIatEntryCount; - public VA64 GuardLongJumpTargetTable; // VA - public ulong GuardLongJumpTargetCount; - public VA64 DynamicValueRelocTable; // VA - public VA64 CHPEMetadataPointer; // VA - public VA64 GuardRFFailureRoutine; // VA - public VA64 GuardRFFailureRoutineFunctionPointer; // VA - public uint DynamicValueRelocTableOffset; - public ushort DynamicValueRelocTableSection; - public ushort Reserved2; - public VA64 GuardRFVerifyStackPointerFunctionPointer; // VA - public uint HotPatchTableOffset; - public uint Reserved3; - public VA64 EnclaveConfigurationPointer; // VA - public VA64 VolatileMetadataPointer; // VA - public VA64 GuardEHContinuationTable; // VA - public ulong GuardEHContinuationCount; - public VA64 GuardXFGCheckFunctionPointer; // VA - public VA64 GuardXFGDispatchFunctionPointer; // VA - public VA64 GuardXFGTableDispatchFunctionPointer; // VA - public VA64 CastGuardOsDeterminedFailureMode; // VA - public VA64 GuardMemcpyFunctionPointer; // VA + /// + /// Gets the 64-bit Load Configuration Directory. + /// + public ref PELoadConfigDirectoryData64 Data => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectoryData32.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectoryData32.cs new file mode 100644 index 0000000..e688a9d --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectoryData32.cs @@ -0,0 +1,78 @@ +// 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 structure representing the Load Configuration Directory for a PE32 file. +/// +public struct PELoadConfigDirectoryData32 +{ + internal uint SizeInternal; + + public uint Size => SizeInternal; + + public uint TimeDateStamp; + public ushort MajorVersion; + public ushort MinorVersion; + public uint GlobalFlagsClear; + public uint GlobalFlagsSet; + public uint CriticalSectionDefaultTimeout; + public uint DeCommitFreeBlockThreshold; + public uint DeCommitTotalFreeThreshold; + public VA32 LockPrefixTable; // VA + public uint MaximumAllocationSize; + public uint VirtualMemoryThreshold; + public uint ProcessHeapFlags; + public uint ProcessAffinityMask; + public ushort CSDVersion; + public ushort DependentLoadFlags; + public VA32 EditList; // VA + public VA32 SecurityCookie; // VA + public VA32 SEHandlerTable; // VA + public uint SEHandlerCount; + public VA32 GuardCFCheckFunctionPointer; // VA + public VA32 GuardCFDispatchFunctionPointer; // VA + public VA32 GuardCFFunctionTable; // VA + public uint GuardCFFunctionCount; + + private uint _rawGuardFlags; + + public PEGuardFlags GuardFlags + { + get => (PEGuardFlags)(_rawGuardFlags & ~0xF000_0000U); + set => _rawGuardFlags = (_rawGuardFlags & 0xF000_0000U) | (uint)value; + } + + public int TableSizeShift + { + get => (int)(_rawGuardFlags >>> 28); + set => _rawGuardFlags = (_rawGuardFlags & 0x0FFF_FFFFU) | ((uint)value << 28); + } + + public PELoadConfigCodeIntegrity CodeIntegrity; + public VA32 GuardAddressTakenIatEntryTable; // VA + public uint GuardAddressTakenIatEntryCount; + public VA32 GuardLongJumpTargetTable; // VA + public uint GuardLongJumpTargetCount; + public VA32 DynamicValueRelocTable; // VA + public uint CHPEMetadataPointer; + public VA32 GuardRFFailureRoutine; // VA + public VA32 GuardRFFailureRoutineFunctionPointer; // VA + public uint DynamicValueRelocTableOffset; + public ushort DynamicValueRelocTableSection; + public ushort Reserved2; + public VA32 GuardRFVerifyStackPointerFunctionPointer; // VA + public uint HotPatchTableOffset; + public uint Reserved3; + public VA32 EnclaveConfigurationPointer; // VA + public VA32 VolatileMetadataPointer; // VA + public VA32 GuardEHContinuationTable; // VA + public uint GuardEHContinuationCount; + public VA32 GuardXFGCheckFunctionPointer; // VA + public VA32 GuardXFGDispatchFunctionPointer; // VA + public VA32 GuardXFGTableDispatchFunctionPointer; // VA + public VA32 CastGuardOsDeterminedFailureMode; // VA + public VA32 GuardMemcpyFunctionPointer; // VA +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectoryData64.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectoryData64.cs new file mode 100644 index 0000000..6f39aa9 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectoryData64.cs @@ -0,0 +1,78 @@ +// 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 structure representing the Load Configuration Directory for a PE64 file. +/// +public struct PELoadConfigDirectoryData64 +{ + internal uint SizeInternal; + + public uint Size => SizeInternal; + + public uint TimeDateStamp; + public ushort MajorVersion; + public ushort MinorVersion; + public uint GlobalFlagsClear; + public uint GlobalFlagsSet; + public uint CriticalSectionDefaultTimeout; + public ulong DeCommitFreeBlockThreshold; + public ulong DeCommitTotalFreeThreshold; + public VA64 LockPrefixTable; // VA + public ulong MaximumAllocationSize; + public ulong VirtualMemoryThreshold; + public ulong ProcessAffinityMask; + public uint ProcessHeapFlags; + public ushort CSDVersion; + public ushort DependentLoadFlags; + public VA64 EditList; // VA + public VA64 SecurityCookie; // VA + public VA64 SEHandlerTable; // VA + public ulong SEHandlerCount; + public VA64 GuardCFCheckFunctionPointer; // VA + public VA64 GuardCFDispatchFunctionPointer; // VA + public VA64 GuardCFFunctionTable; // VA + public ulong GuardCFFunctionCount; + + private uint _rawGuardFlags; + + public PEGuardFlags GuardFlags + { + get => (PEGuardFlags)(_rawGuardFlags & ~0xF000_0000U); + set => _rawGuardFlags = (_rawGuardFlags & 0xF000_0000U) | (uint)value; + } + + public int TableSizeShift + { + get => (int)(_rawGuardFlags >>> 28); + set => _rawGuardFlags = (_rawGuardFlags & 0x0FFF_FFFFU) | ((uint)value << 28); + } + + public PELoadConfigCodeIntegrity CodeIntegrity; + public VA64 GuardAddressTakenIatEntryTable; // VA + public ulong GuardAddressTakenIatEntryCount; + public VA64 GuardLongJumpTargetTable; // VA + public ulong GuardLongJumpTargetCount; + public VA64 DynamicValueRelocTable; // VA + public VA64 CHPEMetadataPointer; // VA + public VA64 GuardRFFailureRoutine; // VA + public VA64 GuardRFFailureRoutineFunctionPointer; // VA + public uint DynamicValueRelocTableOffset; + public ushort DynamicValueRelocTableSection; + public ushort Reserved2; + public VA64 GuardRFVerifyStackPointerFunctionPointer; // VA + public uint HotPatchTableOffset; + public uint Reserved3; + public VA64 EnclaveConfigurationPointer; // VA + public VA64 VolatileMetadataPointer; // VA + public VA64 GuardEHContinuationTable; // VA + public ulong GuardEHContinuationCount; + public VA64 GuardXFGCheckFunctionPointer; // VA + public VA64 GuardXFGDispatchFunctionPointer; // VA + public VA64 GuardXFGTableDispatchFunctionPointer; // VA + public VA64 CastGuardOsDeterminedFailureMode; // VA + public VA64 GuardMemcpyFunctionPointer; // VA +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs new file mode 100644 index 0000000..967fb00 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs @@ -0,0 +1,102 @@ +// 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.Diagnostics; +using LibObjectFile.Utils; + +namespace LibObjectFile.PE; + +/// +/// Base class for a raw data directory. +/// +public abstract class PERawDataDirectory : PEDataDirectory +{ + private byte[] _rawData; + + /// + /// Initializes a new instance of the class. + /// + private protected PERawDataDirectory(PEDataDirectoryKind kind, int minSize) : base(kind) + { + _rawData = new byte[minSize]; + } + + /// + /// Gets the raw data of the Load Configuration Directory. + /// + public byte[] RawData => _rawData; + + /// + /// Gets the config size. + /// + /// + /// Use to set the config size. + /// + public abstract int RawDataSize { get; } + + /// + /// Sets the config size. + /// + /// The new config size. + /// + /// The underlying buffer will be resized if necessary. + /// + public virtual void SetRawDataSize(int value) + { + ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(value, 0); + + if (value > _rawData.Length) + { + var rawData = new byte[value]; + _rawData.CopyTo(rawData, 0); + _rawData = rawData; + } + else if (value < _rawData.Length) + { + // Clear the rest of the buffer + var span = _rawData.AsSpan().Slice(value); + span.Fill(0); + } + } + + /// + public override void Read(PEImageReader reader) + { + var size = (int)Size; + if (_rawData.Length < size) + { + _rawData = new byte[size]; + } + + reader.Position = Position; + int read = reader.Read(_rawData.AsSpan().Slice(0, size)); + if (read != size) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading additional data in {nameof(PERawDataDirectory)}"); + return; + } + + HeaderSize = ComputeHeaderSize(reader); + } + + /// + public override void Write(PEImageWriter writer) + { + var rawDataSize = RawDataSize; + var span = _rawData.AsSpan().Slice(0, rawDataSize); + writer.Write(span); + } + + /// + public override int ReadAt(uint offset, Span destination) => DataUtils.ReadAt(_rawData, offset, destination); + + /// + public override void WriteAt(uint offset, ReadOnlySpan source) => DataUtils.WriteAt(_rawData, offset, source); + + + protected override uint ComputeHeaderSize(PEVisitorContext 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/PETlsDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs index de01d77..ca6f770 100644 --- a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.cs @@ -1,88 +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. - -using System; -using LibObjectFile.Diagnostics; -using LibObjectFile.Utils; - namespace LibObjectFile.PE; /// /// Represents the Thread Local Storage (TLS) directory in a PE file. /// -public sealed class PETlsDirectory : PEDataDirectory +public abstract class PETlsDirectory : PERawDataDirectory { - - /// - /// Initializes a new instance of the class. - /// - public PETlsDirectory() : base(PEDataDirectoryKind.Tls) - { - } - /// /// Initializes a new instance of the class. /// - /// True if the PE file is 32 bits, false if it is 64 bits. - public PETlsDirectory(bool is32Bits) : base(PEDataDirectoryKind.Tls) + private protected PETlsDirectory(int minSize) : base(PEDataDirectoryKind.Tls, minSize) { - Is32Bits = is32Bits; } - - /// - /// Gets a boolean indicating if this TLS directory is for a 32 bits PE file. - /// - public bool Is32Bits { get; private set; } - - - /// - /// Gets the TLS directory for a PE32 file. - /// - public PETlsDirectory32 TlsDirectory32; - - - /// - /// Gets the TLS directory for a PE64 file. - /// - public PETlsDirectory64 TlsDirectory64; - - - protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) - { - return Is32Bits ? (uint)sizeof(PETlsDirectory32) : (uint)sizeof(PETlsDirectory64); - } - - public override unsafe void Read(PEImageReader reader) - { - reader.Position = Position; - Is32Bits = reader.File.IsPE32; - - if (Is32Bits) - { - if (!reader.TryReadData(sizeof(PETlsDirectory32), out TlsDirectory32)) - { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PETlsDirectory)}"); - return; - } - } - else - { - if (!reader.TryReadData(sizeof(PETlsDirectory64), out TlsDirectory64)) - { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading {nameof(PETlsDirectory)}"); - return; - } - } - - HeaderSize = ComputeHeaderSize(reader); - } - - public override void Write(PEImageWriter writer) - { - } - - public override int ReadAt(uint offset, Span destination) => DataUtils.ReadAt(Is32Bits, ref TlsDirectory32, ref TlsDirectory64, offset, destination); - - public override void WriteAt(uint offset, ReadOnlySpan source) => DataUtils.WriteAt(Is32Bits, ref TlsDirectory32, ref TlsDirectory64, offset, source); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs index 0fef8da..504d4e4 100644 --- a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs @@ -2,57 +2,34 @@ // 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + namespace LibObjectFile.PE; /// -/// A structure representing the Thread Local Storage (TLS) directory for a PE32 file. +/// Represents the 32-bit Thread Local Storage (TLS) directory in a PE file. /// -public struct PETlsDirectory32 +public sealed class PETlsDirectory32 : PETlsDirectory { /// - /// The starting address of the TLS template. - /// - /// - /// The template is a block of data that is used to initialize TLS data. - /// The system copies all of this data each time a thread is created, so it must not be corrupted. - /// Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section. - /// - public VA32 StartAddressOfRawData; - - /// - /// Gets or sets the address of the last byte of the TLS, except for the zero fill. + /// Initializes a new instance of the class. /// - /// - /// As with the Raw Data Start VA field, this is a VA, not an RVA. - /// - public VA32 EndAddressOfRawData; - - /// - /// Gets or sets the location to receive the TLS index, which the loader assigns. - /// - /// - /// This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program. - /// - public VA32 AddressOfIndex; - - /// - /// Gets or sets the address of the TLS callback functions array. - /// - /// - /// The array is null-terminated, so the number of functions is the difference between this field and the AddressOfCallBacks field, divided by the size of a pointer. - /// - public VA32 AddressOfCallBacks; - - /// - /// Gets or sets the size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. - /// - /// - /// The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data. - /// - public uint SizeOfZeroFill; + public unsafe PETlsDirectory32() : base(sizeof(PETlsDirectoryData32)) + { + } + public override unsafe int RawDataSize => sizeof(PETlsDirectoryData32); + /// - /// Gets or sets the alignment characteristics of the TLS directory. + /// Gets the 32-bit Thread Local Storage (TLS) directory. /// - public PETlsCharacteristics Characteristics; + public ref PETlsDirectoryData32 Data => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); + + public override unsafe void SetRawDataSize(int value) + { + ArgumentOutOfRangeException.ThrowIfLessThan(value, sizeof(PETlsDirectoryData32)); + base.SetRawDataSize(value); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs index 0d55ccc..2c6d7ca 100644 --- a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs @@ -2,57 +2,34 @@ // 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + namespace LibObjectFile.PE; /// -/// A structure representing the Thread Local Storage (TLS) directory for a PE64 file. +/// Represents the 64-bit Thread Local Storage (TLS) directory in a PE file. /// -public struct PETlsDirectory64 +public sealed class PETlsDirectory64 : PETlsDirectory { /// - /// The starting address of the TLS template. - /// - /// - /// The template is a block of data that is used to initialize TLS data. - /// The system copies all of this data each time a thread is created, so it must not be corrupted. - /// Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section. - /// - public VA64 StartAddressOfRawData; - - /// - /// Gets or sets the address of the last byte of the TLS, except for the zero fill. - /// - /// - /// As with the Raw Data Start VA field, this is a VA, not an RVA. - /// - public VA64 EndAddressOfRawData; - - /// - /// Gets or sets the location to receive the TLS index, which the loader assigns. - /// - /// - /// This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program. - /// - public VA64 AddressOfIndex; - - /// - /// Gets or sets the address of the TLS callback functions array. + /// Initializes a new instance of the class. /// - /// - /// The array is null-terminated, so the number of functions is the difference between this field and the AddressOfCallBacks field, divided by the size of a pointer. - /// - public VA64 AddressOfCallBacks; + public unsafe PETlsDirectory64() : base(sizeof(PETlsDirectoryData64)) + { + } + + public override unsafe int RawDataSize => sizeof(PETlsDirectoryData64); /// - /// Gets or sets the size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. + /// Gets the 64-bit Thread Local Storage (TLS) directory. /// - /// - /// The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data. - /// - public uint SizeOfZeroFill; + public ref PETlsDirectoryData64 Data => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); - /// - /// Gets or sets the alignment characteristics of the TLS directory. - /// - public PETlsCharacteristics Characteristics; + public override unsafe void SetRawDataSize(int value) + { + ArgumentOutOfRangeException.ThrowIfLessThan(value, sizeof(PETlsDirectoryData64)); + base.SetRawDataSize(value); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectoryData32.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectoryData32.cs new file mode 100644 index 0000000..0bb34b7 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectoryData32.cs @@ -0,0 +1,58 @@ +// 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 structure representing the Thread Local Storage (TLS) directory for a PE32 file. +/// +public struct PETlsDirectoryData32 +{ + /// + /// The starting address of the TLS template. + /// + /// + /// The template is a block of data that is used to initialize TLS data. + /// The system copies all of this data each time a thread is created, so it must not be corrupted. + /// Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section. + /// + public VA32 StartAddressOfRawData; + + /// + /// Gets or sets the address of the last byte of the TLS, except for the zero fill. + /// + /// + /// As with the Raw Data Start VA field, this is a VA, not an RVA. + /// + public VA32 EndAddressOfRawData; + + /// + /// Gets or sets the location to receive the TLS index, which the loader assigns. + /// + /// + /// This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program. + /// + public VA32 AddressOfIndex; + + /// + /// Gets or sets the address of the TLS callback functions array. + /// + /// + /// The array is null-terminated, so the number of functions is the difference between this field and the AddressOfCallBacks field, divided by the size of a pointer. + /// + public VA32 AddressOfCallBacks; + + /// + /// Gets or sets the size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. + /// + /// + /// The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data. + /// + public uint SizeOfZeroFill; + + /// + /// Gets or sets the alignment characteristics of the TLS directory. + /// + public PETlsCharacteristics Characteristics; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectoryData64.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectoryData64.cs new file mode 100644 index 0000000..7c723dc --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectoryData64.cs @@ -0,0 +1,58 @@ +// 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 structure representing the Thread Local Storage (TLS) directory for a PE64 file. +/// +public struct PETlsDirectoryData64 +{ + /// + /// The starting address of the TLS template. + /// + /// + /// The template is a block of data that is used to initialize TLS data. + /// The system copies all of this data each time a thread is created, so it must not be corrupted. + /// Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section. + /// + public VA64 StartAddressOfRawData; + + /// + /// Gets or sets the address of the last byte of the TLS, except for the zero fill. + /// + /// + /// As with the Raw Data Start VA field, this is a VA, not an RVA. + /// + public VA64 EndAddressOfRawData; + + /// + /// Gets or sets the location to receive the TLS index, which the loader assigns. + /// + /// + /// This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program. + /// + public VA64 AddressOfIndex; + + /// + /// Gets or sets the address of the TLS callback functions array. + /// + /// + /// The array is null-terminated, so the number of functions is the difference between this field and the AddressOfCallBacks field, divided by the size of a pointer. + /// + public VA64 AddressOfCallBacks; + + /// + /// Gets or sets the size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. + /// + /// + /// The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data. + /// + public uint SizeOfZeroFill; + + /// + /// Gets or sets the alignment characteristics of the TLS directory. + /// + public PETlsCharacteristics Characteristics; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageBaseRelocation.cs b/src/LibObjectFile/PE/Internal/RawImageBaseRelocation.cs new file mode 100644 index 0000000..500e0f3 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageBaseRelocation.cs @@ -0,0 +1,37 @@ +// 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.Internal; + +/// +/// Structure representing a base relocation. +/// +internal struct RawImageBaseRelocation +{ + public const ushort MaxVirtualOffset = (1 << 12) - 1; + private const ushort TypeMask = unchecked((ushort)~MaxVirtualOffset); + private const ushort VirtualOffsetMask = MaxVirtualOffset; + + private readonly ushort _value; + + public RawImageBaseRelocation(ushort value) + { + _value = value; + } + + /// + /// Gets a value indicating whether the base relocation is zero (used for padding). + /// + public bool IsZero => _value == 0; + + /// + /// Gets the type of the base relocation. + /// + public PEBaseRelocationType Type => (PEBaseRelocationType)(_value & TypeMask); + + /// + /// Gets the virtual offset of the base relocation relative to the offset of the associated . + /// + public ushort OffsetInBlockPart => (ushort)(_value & VirtualOffsetMask); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 8ee15f2..f88048e 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -295,7 +295,7 @@ private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan(bool is32Bits, ref TData32 data32, ref TData64 data64, uint offset, Span destination) - where TData32 : unmanaged - where TData64 : unmanaged + public static int ReadAt(ReadOnlySpan source, uint offset, Span destination) { - if (is32Bits) - { - var endOffset = offset + destination.Length; - if (endOffset > sizeof(TData32)) - { - return 0; - } - - var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data32, 1)); - span.Slice((int)offset, (int)destination.Length).CopyTo(destination); - } - else - { - var endOffset = offset + destination.Length; - if (endOffset > sizeof(TData64)) - { - return 0; - } + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, (uint)source.Length); - var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data64, 1)); - span.Slice((int)offset, (int)destination.Length).CopyTo(destination); - } + var lengthToCopy = Math.Min(source.Length - (int)offset, destination.Length); + source.Slice((int)offset, lengthToCopy).CopyTo(destination); - return destination.Length; + return lengthToCopy; } - - public static unsafe void WriteAt(bool is32Bits, ref TData32 data32, ref TData64 data64, uint offset, ReadOnlySpan source) - where TData32 : unmanaged - where TData64 : unmanaged + public static unsafe void WriteAt(Span destination, uint offset, ReadOnlySpan source) { - if (is32Bits) - { - var endOffset = offset + source.Length; - if (endOffset > sizeof(TData32)) - { - throw new ArgumentOutOfRangeException(nameof(source), "Source length is too big"); - } + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, (uint)destination.Length); - var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data32, 1)); - source.CopyTo(span.Slice((int)offset, (int)source.Length)); - } - else + if (source.Length > (destination.Length - (int)offset)) { - var endOffset = offset + source.Length; - if (endOffset > sizeof(TData64)) - { - throw new ArgumentOutOfRangeException(nameof(source), "Source length is too big"); - } - - var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref data64, 1)); - source.CopyTo(span.Slice((int)offset, (int)source.Length)); + throw new ArgumentOutOfRangeException(nameof(source), $"The source buffer is too large to fit at the offset {offset} in the destination buffer"); } + + source.CopyTo(destination.Slice((int)offset, source.Length)); } } \ No newline at end of file From 6f8a1808454a5d0a482a657ce73aa0eb1141a88c Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 24 Sep 2024 22:22:28 +0200 Subject: [PATCH 50/87] Add Debug Directory, refactor Tls / LoadConfig directories --- src/LibObjectFile.sln.DotSettings | 1 + src/LibObjectFile/Diagnostics/DiagnosticId.cs | 5 + .../PE/DataDirectory/PEBaseRelocation.cs | 16 +- .../PE/DataDirectory/PEBaseRelocationBlock.cs | 8 +- .../PE/DataDirectory/PEBlobDataLink.cs | 13 + .../PE/DataDirectory/PEDebugDataRSDS.cs | 42 +++ .../PE/DataDirectory/PEDebugDirectory.cs | 137 +++++++- .../PE/DataDirectory/PEDebugDirectoryEntry.cs | 111 +++++++ .../PE/DataDirectory/PEDebugKnownType.cs | 119 +++++++ .../PE/DataDirectory/PEDebugType.cs | 75 +++++ .../PE/DataDirectory/PEExportFunctionEntry.cs | 2 +- .../PE/DataDirectory/PEImportFunctionEntry.cs | 2 +- .../DataDirectory/PELoadConfigDirectory32.cs | 302 ++++++++++++++++- .../DataDirectory/PELoadConfigDirectory64.cs | 304 +++++++++++++++++- .../PE/DataDirectory/PETlsDirectory32.cs | 77 ++++- .../PE/DataDirectory/PETlsDirectory64.cs | 77 ++++- .../PE/{IRVALink.cs => IPELink.cs} | 11 +- .../PE/Internal/RawImageDebugDirectory.cs | 19 ++ src/LibObjectFile/PE/PEAsciiStringLink.cs | 21 +- src/LibObjectFile/PE/PEFunctionAddressLink.cs | 14 +- src/LibObjectFile/PE/PEImportHintNameLink.cs | 21 +- src/LibObjectFile/PE/PEModuleHandleLink.cs | 19 +- src/LibObjectFile/PE/PEObjectBase.cs | 11 + .../PE/PEObjectBaseExtensions.cs | 5 + src/LibObjectFile/PE/PEObjectExtensions.cs | 12 +- src/LibObjectFile/PE/PEPrinter.cs | 6 +- src/LibObjectFile/PE/PESectionData.cs | 10 - src/LibObjectFile/PE/PESectionDataLink.cs | 16 +- src/LibObjectFile/PE/PESectionLink.cs | 16 +- src/LibObjectFile/PE/RVALinkExtensions.cs | 14 - 30 files changed, 1348 insertions(+), 138 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEBlobDataLink.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDebugDataRSDS.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDebugKnownType.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDebugType.cs rename src/LibObjectFile/PE/{IRVALink.cs => IPELink.cs} (57%) create mode 100644 src/LibObjectFile/PE/Internal/RawImageDebugDirectory.cs create mode 100644 src/LibObjectFile/PE/PEObjectBaseExtensions.cs delete mode 100644 src/LibObjectFile/PE/RVALinkExtensions.cs diff --git a/src/LibObjectFile.sln.DotSettings b/src/LibObjectFile.sln.DotSettings index 4bf95a6..287d885 100644 --- a/src/LibObjectFile.sln.DotSettings +++ b/src/LibObjectFile.sln.DotSettings @@ -6,6 +6,7 @@ See the license.txt file in the project root for more information. DIE GNU LEB + RSDS RVA RVO VA diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index b239244..1fd7856 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -184,4 +184,9 @@ public enum DiagnosticId PE_ERR_ImportDirectoryInvalidModuleHandleRVA = 3505, PE_ERR_DelayImportDirectoryInvalidDllNameRVA = 3506, PE_ERR_DelayImportDirectoryInvalidModuleHandleRVA = 3507, + + // PE Debug directory + PE_ERR_DebugDirectorySize = 3600, + PE_ERR_DebugDirectorySectionNotFound = 3601, + PE_ERR_DebugDirectoryContainerNotFound = 3602, } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs index 4ebbdc1..f2ab5b3 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using System.Diagnostics; using System.Runtime.InteropServices; namespace LibObjectFile.PE; @@ -10,7 +11,8 @@ namespace LibObjectFile.PE; /// /// A base relocation in a Portable Executable (PE) image. /// -public readonly record struct PEBaseRelocation(PEBaseRelocationType Type, PESectionDataLink Link) +[DebuggerDisplay("{ToString(),nq}")] +public readonly record struct PEBaseRelocation(PEBaseRelocationType Type, PESectionData? Container, RVO RVO) : IPELink { /// /// Reads the address from the section data. @@ -20,7 +22,7 @@ public readonly record struct PEBaseRelocation(PEBaseRelocationType Type, PESect /// The section data link is not set or the type is not supported. public ulong ReadAddress(PEFile file) { - if (Link.SectionData is null) + if (Container is null) { throw new InvalidOperationException("The section data link is not set"); } @@ -35,10 +37,10 @@ public ulong ReadAddress(PEFile file) VA32 va = default; var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref va, 1)); - int read = Link.SectionData!.ReadAt(Link.RVO, span); + int read = Container!.ReadAt(RVO, span); if (read != 4) { - throw new InvalidOperationException($"Unable to read the VA32 from the section data type: {Link.SectionData.GetType().FullName}"); + throw new InvalidOperationException($"Unable to read the VA32 from the section data type: {Container.GetType().FullName}"); } return va; @@ -48,13 +50,15 @@ public ulong ReadAddress(PEFile file) VA64 va = default; var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref va, 1)); - int read = Link.SectionData!.ReadAt(Link.RVO, span); + int read = Container!.ReadAt(RVO, span); if (read != 8) { - throw new InvalidOperationException($"Unable to read the VA64 from the section data type: {Link.SectionData.GetType().FullName}"); + throw new InvalidOperationException($"Unable to read the VA64 from the section data type: {Container.GetType().FullName}"); } return va; } } + + public override string ToString() => $"{Type} {this.ToDisplayTextWithRVA()}"; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs index e13caa8..bc4ef94 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs @@ -23,7 +23,7 @@ public sealed class PEBaseRelocationBlock /// The section link. public PEBaseRelocationBlock(PESectionLink sectionLink) { - ArgumentNullException.ThrowIfNull(sectionLink.Section, nameof(sectionLink)); + ArgumentNullException.ThrowIfNull(sectionLink.Container, nameof(sectionLink)); SectionLink = sectionLink; } @@ -68,7 +68,7 @@ internal void ReadAndBind(PEImageReader reader) relocSpan = relocSpan.Slice(0, relocSpan.Length - 1); } - var section = SectionLink.Section!; + var section = SectionLink.Container!; var blockBaseAddress = SectionLink.RVA(); // Iterate on all relocations @@ -89,7 +89,7 @@ internal void ReadAndBind(PEImageReader reader) } var offsetInSectionData = va - sectionData.RVA; - var newRelocation = new PEBaseRelocation(relocation.Type, new(sectionData, offsetInSectionData)); + var newRelocation = new PEBaseRelocation(relocation.Type, sectionData, offsetInSectionData); Relocations.Add(newRelocation); } @@ -99,6 +99,6 @@ internal void ReadAndBind(PEImageReader reader) public override string ToString() { - return $"{nameof(PEBaseRelocationBlock)}, Section = {SectionLink.Section?.Name}, RVA = {SectionLink.RVA()}, Size = {CalculateSizeOf()}, Relocations[{Relocations.Count}]"; + return $"{nameof(PEBaseRelocationBlock)}, Section = {SectionLink.Container?.Name}, RVA = {SectionLink.RVA()}, Size = {CalculateSizeOf()}, Relocations[{Relocations.Count}]"; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBlobDataLink.cs b/src/LibObjectFile/PE/DataDirectory/PEBlobDataLink.cs new file mode 100644 index 0000000..45f1ae4 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBlobDataLink.cs @@ -0,0 +1,13 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + +using System.Diagnostics; + +namespace LibObjectFile.PE; + +[DebuggerDisplay("{ToString(),nq}")] +public readonly record struct PEBlobDataLink(PEObjectBase? Container, RVO RVO, uint Size) : IPELink +{ + public override string ToString() => $"{this.ToDisplayText()}, Size = 0x{Size:X}"; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDataRSDS.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDataRSDS.cs new file mode 100644 index 0000000..d0fc685 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDataRSDS.cs @@ -0,0 +1,42 @@ +// 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; + +namespace LibObjectFile.PE; + +/// +/// Represents a RSDS debug data. +/// +[DebuggerDisplay("{ToString(),nq}")] +public sealed class PEDebugDataRSDS +{ + /// + /// Initializes a new instance of the class. + /// + public PEDebugDataRSDS() + { + Guid = Guid.Empty; + Age = 0; + PdbPath = string.Empty; + } + + /// + /// Gets or sets the GUID of the PDB. + /// + public Guid Guid { get; set; } + + /// + /// Gets or sets the age of the PDB. + /// + public uint Age { get; set; } + + /// + /// Gets or sets the path of the PDB. + /// + public string PdbPath { get; set; } + + public override string ToString() => $"{nameof(PEDebugDataRSDS)} {Guid} {Age} {PdbPath}"; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs index dd63d4f..7f3a0a8 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs @@ -3,6 +3,12 @@ // See the license.txt file in the project root for more information. using System; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using LibObjectFile.Diagnostics; +using LibObjectFile.PE.Internal; namespace LibObjectFile.PE; @@ -10,20 +16,141 @@ public sealed class PEDebugDirectory : PEDataDirectory { public PEDebugDirectory() : base(PEDataDirectoryKind.Debug) { + Entries = new(); } - protected override uint ComputeHeaderSize(PEVisitorContext context) + + public List Entries { get; } + + public override unsafe void Read(PEImageReader reader) { - return 0; - } + var size = (int)Size; + + var entryCount = size / sizeof(RawImageDebugDirectory); + + var buffer = ArrayPool.Shared.Rent(size); + try + { + var span = buffer.AsSpan(0, size); + var entries = MemoryMarshal.Cast(span); + + reader.Position = Position; + int read = reader.Read(span); + if (read != size) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_DebugDirectorySize, $"Invalid size found when trying to read the Debug directory at {Position}"); + return; + } + + for (int i = 0; i < entryCount; i++) + { + var entry = entries[i]; + var debugEntry = new PEDebugDirectoryEntry + { + Characteristics = entry.Characteristics, + MajorVersion = entry.MajorVersion, + MinorVersion = entry.MinorVersion, + Type = entry.Type, + }; - public override void Read(PEImageReader reader) + if (entry.AddressOfRawData != 0) + { + if (!reader.File.TryFindSection(entry.AddressOfRawData, out var section)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_DebugDirectorySectionNotFound, $"Unable to find the section for the debug directory entry at {entry.AddressOfRawData}"); + continue; + } + + var dataLink = new PEBlobDataLink(section, (RVO)(uint)entry.AddressOfRawData, entry.SizeOfData); + debugEntry.DataLink = dataLink; + } + else if (entry.PointerToRawData != 0) + { + var dataLink = new PEBlobDataLink(reader.File, entry.PointerToRawData, entry.SizeOfData); + debugEntry.DataLink = dataLink; + } + else + { + Debug.Assert(entry.SizeOfData == 0); + } + + Entries.Add(debugEntry); + } + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + HeaderSize = ComputeHeaderSize(reader); + } + + internal override void Bind(PEImageReader reader) { - // TBD + var entries = CollectionsMarshal.AsSpan(Entries); + var peFile = reader.File; + foreach (var entry in entries) + { + + if (entry.DataLink.Container is PEFile) + { + PEObjectBase? container = null; + + foreach (var extraData in peFile.ExtraDataBeforeSections) + { + if (peFile.Contains(entry.DataLink.RVO)) + { + container = extraData; + break; + } + } + + if (container is null) + { + foreach (var section in peFile.ExtraDataAfterSections) + { + if (section.Contains(entry.DataLink.RVO)) + { + container = section; + break; + } + } + } + + if (container is null) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_DebugDirectoryContainerNotFound, $"Unable to find the container for the debug directory entry at {entry.DataLink.RVO}"); + continue; + } + + entry.DataLink = new(container, entry.DataLink.RVO - (uint)container.Position, entry.DataLink.Size); + } + else if (entry.DataLink.Container is PESection) + { + var section = (PESection)entry.DataLink.Container!; + + if (!section.TryFindSectionData((RVA)(uint)entry.DataLink.RVO, out var container)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_DebugDirectoryContainerNotFound, $"Unable to find the container for the debug directory entry at {entry.DataLink.RVO}"); + continue; + } + + entry.DataLink = new(container, entry.DataLink.RVO - container.RVA, entry.DataLink.Size); + } + else + { + // Ignore, there are no links + } + } } public override void Write(PEImageWriter writer) { throw new NotImplementedException(); } + + protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) + { + return (uint)(Entries.Count * sizeof(RawImageDebugDirectory)); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs new file mode 100644 index 0000000..950b716 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs @@ -0,0 +1,111 @@ +// 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.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +/// +/// Represents a debug directory entry in a Portable Executable (PE) file. +/// +[DebuggerDisplay("{ToString(),nq}")] +public sealed class PEDebugDirectoryEntry +{ + /// + /// Gets or sets the characteristics of the debug directory entry. + /// + public uint Characteristics { get; set; } + + /// + /// Gets or sets the time and date stamp of the debug directory entry. + /// + public uint TimeDateStamp { get; set; } + + /// + /// Gets or sets the major version of the debug directory entry. + /// + public ushort MajorVersion { get; set; } + + /// + /// Gets or sets the minor version of the debug directory entry. + /// + public ushort MinorVersion { get; set; } + + /// + /// Gets or sets the type of the debug directory entry. + /// + public PEDebugType Type { get; set; } + + /// + /// Gets or sets the data link of the debug directory entry. + /// + public PEBlobDataLink DataLink { get; set; } + + /// + /// Try to get the CodeView (RSDS) information from this debug directory entry. + /// + /// The CodeView (RSDS) information if the entry is of type . + /// true if the CodeView (RSDS) information has been successfully retrieved. + /// + /// The entry must be of type to be able to get the CodeView information. The data behind the must be a CodeView (RSDS) format. + /// + public unsafe bool TryGetDebugRSDS([NotNullWhen(true)] out PEDebugDataRSDS? debugRSDS) + { + debugRSDS = null; + if (Type.Value != (uint)PEDebugKnownType.CodeView) + { + return false; + } + + var dataLink = DataLink; + if (dataLink.Container == null) + { + return false; + } + + var container = dataLink.Container; + var buffer = ArrayPool.Shared.Rent((int)dataLink.Size); + var span = buffer.AsSpan(0, (int)dataLink.Size); + try + { + var read = container.ReadAt(DataLink.RVO, span); + if (read != span.Length) + { + return false; + } + + var signature = MemoryMarshal.Read(span); + if (signature != 0x53445352) + { + return false; + } + + var pdbPath = span.Slice(sizeof(uint) + sizeof(Guid) + sizeof(uint)); + var indexOfZero = pdbPath.IndexOf((byte)0); + if (indexOfZero < 0) + { + return false; + } + + debugRSDS = new PEDebugDataRSDS + { + Guid = MemoryMarshal.Read(span.Slice(4)), + Age = MemoryMarshal.Read(span.Slice(sizeof(uint) + sizeof(Guid))), + PdbPath = System.Text.Encoding.UTF8.GetString(pdbPath.Slice(0, indexOfZero)) + }; + + return true; + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + public override string ToString() => $"{nameof(PEDebugDirectoryEntry)} {Type} {DataLink}"; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugKnownType.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugKnownType.cs new file mode 100644 index 0000000..44d6124 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugKnownType.cs @@ -0,0 +1,119 @@ +// 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; + +/// +/// Enum representing the different types of debug information found in a PE file. +/// +public enum PEDebugKnownType +{ + /// + /// An unknown value that is ignored by all tools. + /// + Unknown = 0, + + /// + /// The COFF debug information (line numbers, symbol table, and string table). + /// This type of debug information is also pointed to by fields in the file headers. + /// + Coff = 1, + + /// + /// The Visual C++ debug information. + /// + CodeView = 2, + + /// + /// The frame pointer omission (FPO) information. This information tells the debugger + /// how to interpret nonstandard stack frames, which use the EBP register for a purpose + /// other than as a frame pointer. + /// + Fpo = 3, + + /// + /// The location of a DBG file. + /// + Misc = 4, + + /// + /// A copy of the .pdata section. + /// + Exception = 5, + + /// + /// Reserved. + /// + Fixup = 6, + + /// + /// The mapping from an RVA in the image to an RVA in the source image. + /// + OmapToSrc = 7, + + /// + /// The mapping from an RVA in the source image to an RVA in the image. + /// + OmapFromSrc = 8, + + /// + /// Reserved for Borland. + /// + Borland = 9, + + /// + /// Reserved. + /// + Reserved10 = 10, + + /// + /// Reserved. + /// + Clsid = 11, + + /// + /// Visual C++ (CodeView + /// + VCFeature = 12, + + /// + /// POGO + /// + POGO = 13, + + /// + /// ILTCG + /// + ILTCG = 14, + + /// + /// MPX + /// + MPX = 15, + + /// + /// PE determinism or reproducibility. + /// + Repro = 16, + + /// + /// Debugging information is embedded in the PE file at the location specified by PointerToRawData. + /// + EmbeddedRawData = 17, + + /// + /// Undefined. + /// + SPGO = 18, + + /// + /// Stores a cryptographic hash for the content of the symbol file used to build the PE/COFF file. + /// + SymbolFileHash = 19, + + /// + /// Extended DLL characteristics bits. + /// + ExDllCharacteristics = 20 +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugType.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugType.cs new file mode 100644 index 0000000..2447044 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugType.cs @@ -0,0 +1,75 @@ +// 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; + +/// +/// Debug type information. This value can be a known type or a custom type. +/// +/// The raw value of the type. +public record struct PEDebugType(uint Value) +{ + /// + /// Gets whether the type is a known type . + /// + public bool IsKnownType => Value <= (uint)PEDebugKnownType.ExDllCharacteristics; + + /// + /// Converts a known type to a debug type. + /// + /// The known type to convert. + public static implicit operator PEDebugType(PEDebugKnownType value) => new PEDebugType((uint)value); + + /// + public override string ToString() + { + switch ((PEDebugKnownType)Value) + { + case PEDebugKnownType.Unknown: + return "Unknown"; + case PEDebugKnownType.Coff: + return "COFF"; + case PEDebugKnownType.CodeView: + return "CodeView"; + case PEDebugKnownType.Fpo: + return "FPO"; + case PEDebugKnownType.Misc: + return "Misc"; + case PEDebugKnownType.Exception: + return "Exception"; + case PEDebugKnownType.Fixup: + return "Fixup"; + case PEDebugKnownType.OmapToSrc: + return "OmapToSrc"; + case PEDebugKnownType.OmapFromSrc: + return "OmapFromSrc"; + case PEDebugKnownType.Borland: + return "Borland"; + case PEDebugKnownType.Reserved10: + return "Reserved10"; + case PEDebugKnownType.Clsid: + return "Clsid"; + case PEDebugKnownType.Repro: + return "Repro"; + case PEDebugKnownType.EmbeddedRawData: + return "EmbeddedRawData"; + case PEDebugKnownType.SymbolFileHash: + return "SymbolFileHash"; + case PEDebugKnownType.ExDllCharacteristics: + return "ExDllCharacteristics"; + case PEDebugKnownType.VCFeature: + return "VCFeature"; + case PEDebugKnownType.POGO: + return "POGO"; + case PEDebugKnownType.ILTCG: + return "ILTCG"; + case PEDebugKnownType.MPX: + return "MPX"; + case PEDebugKnownType.SPGO: + return "SPGO"; + default: + return $"0x{Value:X}"; + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs index 20d997a..a6123e3 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs @@ -20,7 +20,7 @@ public PEExportFunctionEntry(PEFunctionAddressLink exportRVA) public PEExportFunctionEntry(PEAsciiStringLink forwarderRVA) { - _container = forwarderRVA.StreamSectionData; + _container = forwarderRVA.Container; _offset = forwarderRVA.RVO; _isForwarderRVA = true; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs index 2ad8d69..6a786c4 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs @@ -20,7 +20,7 @@ public readonly struct PEImportFunctionEntry /// The name of the import. public PEImportFunctionEntry(PEAsciiStringLink name) { - _peSectionData = name.StreamSectionData; + _peSectionData = name.Container; _offset = name.RVO; } diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs index 28b2e59..b22928c 100644 --- a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs @@ -20,8 +20,308 @@ public unsafe PELoadConfigDirectory32() : base(sizeof(PELoadConfigDirectoryData3 SetRawDataSize(sizeof(PELoadConfigDirectoryData32)); } + public uint ConfigSize => Data.Size; + + public uint TimeDateStamp + { + get => Data.TimeDateStamp; + set => Data.TimeDateStamp = value; + } + + public ushort MajorVersion + { + get => Data.MajorVersion; + set => Data.MajorVersion = value; + } + + public ushort MinorVersion + { + get => Data.MinorVersion; + set => Data.MinorVersion = value; + } + + public uint GlobalFlagsClear + { + get => Data.GlobalFlagsClear; + set => Data.GlobalFlagsClear = value; + } + + public uint GlobalFlagsSet + { + get => Data.GlobalFlagsSet; + set => Data.GlobalFlagsSet = value; + } + + public uint CriticalSectionDefaultTimeout + { + get => Data.CriticalSectionDefaultTimeout; + set => Data.CriticalSectionDefaultTimeout = value; + } + + public uint DeCommitFreeBlockThreshold + { + get => Data.DeCommitFreeBlockThreshold; + set => Data.DeCommitFreeBlockThreshold = value; + } + + public uint DeCommitTotalFreeThreshold + { + get => Data.DeCommitTotalFreeThreshold; + set => Data.DeCommitTotalFreeThreshold = value; + } + + public VA32 LockPrefixTable + { + get => Data.LockPrefixTable; + set => Data.LockPrefixTable = value; + } + + public uint MaximumAllocationSize + { + get => Data.MaximumAllocationSize; + set => Data.MaximumAllocationSize = value; + } + + public uint VirtualMemoryThreshold + { + get => Data.VirtualMemoryThreshold; + set => Data.VirtualMemoryThreshold = value; + } + + public uint ProcessHeapFlags + { + get => Data.ProcessHeapFlags; + set => Data.ProcessHeapFlags = value; + } + + public uint ProcessAffinityMask + { + get => Data.ProcessAffinityMask; + set => Data.ProcessAffinityMask = value; + } + + public ushort CSDVersion + { + get => Data.CSDVersion; + set => Data.CSDVersion = value; + } + + public ushort DependentLoadFlags + { + get => Data.DependentLoadFlags; + set => Data.DependentLoadFlags = value; + } + + public VA32 EditList + { + get => Data.EditList; + set => Data.EditList = value; + } + + public VA32 SecurityCookie + { + get => Data.SecurityCookie; + set => Data.SecurityCookie = value; + } + + public VA32 SEHandlerTable + { + get => Data.SEHandlerTable; + set => Data.SEHandlerTable = value; + } + + public uint SEHandlerCount + { + get => Data.SEHandlerCount; + set => Data.SEHandlerCount = value; + } + + public VA32 GuardCFCheckFunctionPointer + { + get => Data.GuardCFCheckFunctionPointer; + set => Data.GuardCFCheckFunctionPointer = value; + } + + public VA32 GuardCFDispatchFunctionPointer + { + get => Data.GuardCFDispatchFunctionPointer; + set => Data.GuardCFDispatchFunctionPointer = value; + } + + public VA32 GuardCFFunctionTable + { + get => Data.GuardCFFunctionTable; + set => Data.GuardCFFunctionTable = value; + } + + public uint GuardCFFunctionCount + { + get => Data.GuardCFFunctionCount; + set => Data.GuardCFFunctionCount = value; + } + + public PEGuardFlags GuardFlags + { + get => Data.GuardFlags; + set => Data.GuardFlags = value; + } + + public int TableSizeShift + { + get => Data.TableSizeShift; + set => Data.TableSizeShift = value; + } + + public PELoadConfigCodeIntegrity CodeIntegrity + { + get => Data.CodeIntegrity; + set => Data.CodeIntegrity = value; + } + + public VA32 GuardAddressTakenIatEntryTable + { + get => Data.GuardAddressTakenIatEntryTable; + set => Data.GuardAddressTakenIatEntryTable = value; + } + + public uint GuardAddressTakenIatEntryCount + { + get => Data.GuardAddressTakenIatEntryCount; + set => Data.GuardAddressTakenIatEntryCount = value; + } + + public VA32 GuardLongJumpTargetTable + { + get => Data.GuardLongJumpTargetTable; + set => Data.GuardLongJumpTargetTable = value; + } + + public uint GuardLongJumpTargetCount + { + get => Data.GuardLongJumpTargetCount; + set => Data.GuardLongJumpTargetCount = value; + } + + public VA32 DynamicValueRelocTable + { + get => Data.DynamicValueRelocTable; + set => Data.DynamicValueRelocTable = value; + } + + public uint CHPEMetadataPointer + { + get => Data.CHPEMetadataPointer; + set => Data.CHPEMetadataPointer = value; + } + + public VA32 GuardRFFailureRoutine + { + get => Data.GuardRFFailureRoutine; + set => Data.GuardRFFailureRoutine = value; + } + + public VA32 GuardRFFailureRoutineFunctionPointer + { + get => Data.GuardRFFailureRoutineFunctionPointer; + set => Data.GuardRFFailureRoutineFunctionPointer = value; + } + + public uint DynamicValueRelocTableOffset + { + get => Data.DynamicValueRelocTableOffset; + set => Data.DynamicValueRelocTableOffset = value; + } + + public ushort DynamicValueRelocTableSection + { + get => Data.DynamicValueRelocTableSection; + set => Data.DynamicValueRelocTableSection = value; + } + + public ushort Reserved2 + { + get => Data.Reserved2; + set => Data.Reserved2 = value; + } + + public VA32 GuardRFVerifyStackPointerFunctionPointer + { + get => Data.GuardRFVerifyStackPointerFunctionPointer; + set => Data.GuardRFVerifyStackPointerFunctionPointer = value; + } + + public uint HotPatchTableOffset + { + get => Data.HotPatchTableOffset; + set => Data.HotPatchTableOffset = value; + } + + public uint Reserved3 + { + get => Data.Reserved3; + set => Data.Reserved3 = value; + } + + public VA32 EnclaveConfigurationPointer + { + get => Data.EnclaveConfigurationPointer; + set => Data.EnclaveConfigurationPointer = value; + } + + public VA32 VolatileMetadataPointer + { + get => Data.VolatileMetadataPointer; + set => Data.VolatileMetadataPointer = value; + } + + public VA32 GuardEHContinuationTable + { + get => Data.GuardEHContinuationTable; + set => Data.GuardEHContinuationTable = value; + } + + public uint GuardEHContinuationCount + { + get => Data.GuardEHContinuationCount; + set => Data.GuardEHContinuationCount = value; + } + + public VA32 GuardXFGCheckFunctionPointer + { + get => Data.GuardXFGCheckFunctionPointer; + set => Data.GuardXFGCheckFunctionPointer = value; + } + + public VA32 GuardXFGDispatchFunctionPointer + { + get => Data.GuardXFGDispatchFunctionPointer; + set => Data.GuardXFGDispatchFunctionPointer = value; + } + + public VA32 GuardXFGTableDispatchFunctionPointer + { + get => Data.GuardXFGTableDispatchFunctionPointer; + set => Data.GuardXFGTableDispatchFunctionPointer = value; + } + + public VA32 CastGuardOsDeterminedFailureMode + { + get => Data.CastGuardOsDeterminedFailureMode; + set => Data.CastGuardOsDeterminedFailureMode = value; + } + + public VA32 GuardMemcpyFunctionPointer + { + get => Data.GuardMemcpyFunctionPointer; + set => Data.GuardMemcpyFunctionPointer = value; + } + /// /// Gets the 32-bit Load Configuration Directory. /// - public ref PELoadConfigDirectoryData32 Data => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); + public ref PELoadConfigDirectoryData32 Data + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs index b391534..08d0fd5 100644 --- a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs @@ -2,6 +2,8 @@ // 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.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -21,8 +23,308 @@ public unsafe PELoadConfigDirectory64() : base(sizeof(PELoadConfigDirectoryData6 SetRawDataSize(sizeof(PELoadConfigDirectoryData64)); } + public uint ConfigSize => Data.Size; + + public uint TimeDateStamp + { + get => Data.TimeDateStamp; + set => Data.TimeDateStamp = value; + } + + public ushort MajorVersion + { + get => Data.MajorVersion; + set => Data.MajorVersion = value; + } + + public ushort MinorVersion + { + get => Data.MinorVersion; + set => Data.MinorVersion = value; + } + + public uint GlobalFlagsClear + { + get => Data.GlobalFlagsClear; + set => Data.GlobalFlagsClear = value; + } + + public uint GlobalFlagsSet + { + get => Data.GlobalFlagsSet; + set => Data.GlobalFlagsSet = value; + } + + public uint CriticalSectionDefaultTimeout + { + get => Data.CriticalSectionDefaultTimeout; + set => Data.CriticalSectionDefaultTimeout = value; + } + + public ulong DeCommitFreeBlockThreshold + { + get => Data.DeCommitFreeBlockThreshold; + set => Data.DeCommitFreeBlockThreshold = value; + } + + public ulong DeCommitTotalFreeThreshold + { + get => Data.DeCommitTotalFreeThreshold; + set => Data.DeCommitTotalFreeThreshold = value; + } + + public VA64 LockPrefixTable + { + get => Data.LockPrefixTable; + set => Data.LockPrefixTable = value; + } + + public ulong MaximumAllocationSize + { + get => Data.MaximumAllocationSize; + set => Data.MaximumAllocationSize = value; + } + + public ulong VirtualMemoryThreshold + { + get => Data.VirtualMemoryThreshold; + set => Data.VirtualMemoryThreshold = value; + } + + public ulong ProcessAffinityMask + { + get => Data.ProcessAffinityMask; + set => Data.ProcessAffinityMask = value; + } + + public uint ProcessHeapFlags + { + get => Data.ProcessHeapFlags; + set => Data.ProcessHeapFlags = value; + } + + public ushort CSDVersion + { + get => Data.CSDVersion; + set => Data.CSDVersion = value; + } + + public ushort DependentLoadFlags + { + get => Data.DependentLoadFlags; + set => Data.DependentLoadFlags = value; + } + + public VA64 EditList + { + get => Data.EditList; + set => Data.EditList = value; + } + + public VA64 SecurityCookie + { + get => Data.SecurityCookie; + set => Data.SecurityCookie = value; + } + + public VA64 SEHandlerTable + { + get => Data.SEHandlerTable; + set => Data.SEHandlerTable = value; + } + + public ulong SEHandlerCount + { + get => Data.SEHandlerCount; + set => Data.SEHandlerCount = value; + } + + public VA64 GuardCFCheckFunctionPointer + { + get => Data.GuardCFCheckFunctionPointer; + set => Data.GuardCFCheckFunctionPointer = value; + } + + public VA64 GuardCFDispatchFunctionPointer + { + get => Data.GuardCFDispatchFunctionPointer; + set => Data.GuardCFDispatchFunctionPointer = value; + } + + public VA64 GuardCFFunctionTable + { + get => Data.GuardCFFunctionTable; + set => Data.GuardCFFunctionTable = value; + } + + public ulong GuardCFFunctionCount + { + get => Data.GuardCFFunctionCount; + set => Data.GuardCFFunctionCount = value; + } + + public PEGuardFlags GuardFlags + { + get => Data.GuardFlags; + set => Data.GuardFlags = value; + } + + public int TableSizeShift + { + get => Data.TableSizeShift; + set => Data.TableSizeShift = value; + } + + public PELoadConfigCodeIntegrity CodeIntegrity + { + get => Data.CodeIntegrity; + set => Data.CodeIntegrity = value; + } + + public VA64 GuardAddressTakenIatEntryTable + { + get => Data.GuardAddressTakenIatEntryTable; + set => Data.GuardAddressTakenIatEntryTable = value; + } + + public ulong GuardAddressTakenIatEntryCount + { + get => Data.GuardAddressTakenIatEntryCount; + set => Data.GuardAddressTakenIatEntryCount = value; + } + + public VA64 GuardLongJumpTargetTable + { + get => Data.GuardLongJumpTargetTable; + set => Data.GuardLongJumpTargetTable = value; + } + + public ulong GuardLongJumpTargetCount + { + get => Data.GuardLongJumpTargetCount; + set => Data.GuardLongJumpTargetCount = value; + } + + public VA64 DynamicValueRelocTable + { + get => Data.DynamicValueRelocTable; + set => Data.DynamicValueRelocTable = value; + } + + public VA64 CHPEMetadataPointer + { + get => Data.CHPEMetadataPointer; + set => Data.CHPEMetadataPointer = value; + } + + public VA64 GuardRFFailureRoutine + { + get => Data.GuardRFFailureRoutine; + set => Data.GuardRFFailureRoutine = value; + } + + public VA64 GuardRFFailureRoutineFunctionPointer + { + get => Data.GuardRFFailureRoutineFunctionPointer; + set => Data.GuardRFFailureRoutineFunctionPointer = value; + } + + public uint DynamicValueRelocTableOffset + { + get => Data.DynamicValueRelocTableOffset; + set => Data.DynamicValueRelocTableOffset = value; + } + + public ushort DynamicValueRelocTableSection + { + get => Data.DynamicValueRelocTableSection; + set => Data.DynamicValueRelocTableSection = value; + } + + public ushort Reserved2 + { + get => Data.Reserved2; + set => Data.Reserved2 = value; + } + + public VA64 GuardRFVerifyStackPointerFunctionPointer + { + get => Data.GuardRFVerifyStackPointerFunctionPointer; + set => Data.GuardRFVerifyStackPointerFunctionPointer = value; + } + + public uint HotPatchTableOffset + { + get => Data.HotPatchTableOffset; + set => Data.HotPatchTableOffset = value; + } + + public uint Reserved3 + { + get => Data.Reserved3; + set => Data.Reserved3 = value; + } + + public VA64 EnclaveConfigurationPointer + { + get => Data.EnclaveConfigurationPointer; + set => Data.EnclaveConfigurationPointer = value; + } + + public VA64 VolatileMetadataPointer + { + get => Data.VolatileMetadataPointer; + set => Data.VolatileMetadataPointer = value; + } + + public VA64 GuardEHContinuationTable + { + get => Data.GuardEHContinuationTable; + set => Data.GuardEHContinuationTable = value; + } + + public ulong GuardEHContinuationCount + { + get => Data.GuardEHContinuationCount; + set => Data.GuardEHContinuationCount = value; + } + + public VA64 GuardXFGCheckFunctionPointer + { + get => Data.GuardXFGCheckFunctionPointer; + set => Data.GuardXFGCheckFunctionPointer = value; + } + + public VA64 GuardXFGDispatchFunctionPointer + { + get => Data.GuardXFGDispatchFunctionPointer; + set => Data.GuardXFGDispatchFunctionPointer = value; + } + + public VA64 GuardXFGTableDispatchFunctionPointer + { + get => Data.GuardXFGTableDispatchFunctionPointer; + set => Data.GuardXFGTableDispatchFunctionPointer = value; + } + + public VA64 CastGuardOsDeterminedFailureMode + { + get => Data.CastGuardOsDeterminedFailureMode; + set => Data.CastGuardOsDeterminedFailureMode = value; + } + + public VA64 GuardMemcpyFunctionPointer + { + get => Data.GuardMemcpyFunctionPointer; + set => Data.GuardMemcpyFunctionPointer = value; + } + /// /// Gets the 64-bit Load Configuration Directory. /// - public ref PELoadConfigDirectoryData64 Data => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); + public ref PELoadConfigDirectoryData64 Data + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs index 504d4e4..6701528 100644 --- a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs @@ -22,10 +22,85 @@ public unsafe PETlsDirectory32() : base(sizeof(PETlsDirectoryData32)) public override unsafe int RawDataSize => sizeof(PETlsDirectoryData32); + /// + /// The starting address of the TLS template. + /// + /// + /// The template is a block of data that is used to initialize TLS data. + /// The system copies all of this data each time a thread is created, so it must not be corrupted. + /// Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section. + /// + public VA32 StartAddressOfRawData + { + get => Data.StartAddressOfRawData; + set => Data.StartAddressOfRawData = value; + } + + /// + /// Gets or sets the address of the last byte of the TLS, except for the zero fill. + /// + /// + /// As with the Raw Data Start VA field, this is a VA, not an RVA. + /// + public VA32 EndAddressOfRawData + { + get => Data.EndAddressOfRawData; + set => Data.EndAddressOfRawData = value; + } + + /// + /// Gets or sets the location to receive the TLS index, which the loader assigns. + /// + /// + /// This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program. + /// + public VA32 AddressOfIndex + { + get => Data.AddressOfIndex; + set => Data.AddressOfIndex = value; + } + + /// + /// Gets or sets the address of the TLS callback functions array. + /// + /// + /// The array is null-terminated, so the number of functions is the difference between this field and the AddressOfCallBacks field, divided by the size of a pointer. + /// + public VA32 AddressOfCallBacks + { + get => Data.AddressOfCallBacks; + set => Data.AddressOfCallBacks = value; + } + + /// + /// Gets or sets the size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. + /// + /// + /// The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data. + /// + public uint SizeOfZeroFill + { + get => Data.SizeOfZeroFill; + set => Data.SizeOfZeroFill = value; + } + + /// + /// Gets or sets the alignment characteristics of the TLS directory. + /// + public PETlsCharacteristics Characteristics + { + get => Data.Characteristics; + set => Data.Characteristics = value; + } + /// /// Gets the 32-bit Thread Local Storage (TLS) directory. /// - public ref PETlsDirectoryData32 Data => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); + public ref PETlsDirectoryData32 Data + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); + } public override unsafe void SetRawDataSize(int value) { diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs index 2c6d7ca..ab31227 100644 --- a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs @@ -22,10 +22,85 @@ public unsafe PETlsDirectory64() : base(sizeof(PETlsDirectoryData64)) public override unsafe int RawDataSize => sizeof(PETlsDirectoryData64); + /// + /// The starting address of the TLS template. + /// + /// + /// The template is a block of data that is used to initialize TLS data. + /// The system copies all of this data each time a thread is created, so it must not be corrupted. + /// Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section. + /// + public VA64 StartAddressOfRawData + { + get => Data.StartAddressOfRawData; + set => Data.StartAddressOfRawData = value; + } + + /// + /// Gets or sets the address of the last byte of the TLS, except for the zero fill. + /// + /// + /// As with the Raw Data Start VA field, this is a VA, not an RVA. + /// + public VA64 EndAddressOfRawData + { + get => Data.EndAddressOfRawData; + set => Data.EndAddressOfRawData = value; + } + + /// + /// Gets or sets the location to receive the TLS index, which the loader assigns. + /// + /// + /// This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program. + /// + public VA64 AddressOfIndex + { + get => Data.AddressOfIndex; + set => Data.AddressOfIndex = value; + } + + /// + /// Gets or sets the address of the TLS callback functions array. + /// + /// + /// The array is null-terminated, so the number of functions is the difference between this field and the AddressOfCallBacks field, divided by the size of a pointer. + /// + public VA64 AddressOfCallBacks + { + get => Data.AddressOfCallBacks; + set => Data.AddressOfCallBacks = value; + } + + /// + /// Gets or sets the size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. + /// + /// + /// The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data. + /// + public uint SizeOfZeroFill + { + get => Data.SizeOfZeroFill; + set => Data.SizeOfZeroFill = value; + } + + /// + /// Gets or sets the alignment characteristics of the TLS directory. + /// + public PETlsCharacteristics Characteristics + { + get => Data.Characteristics; + set => Data.Characteristics = value; + } + /// /// Gets the 64-bit Thread Local Storage (TLS) directory. /// - public ref PETlsDirectoryData64 Data => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); + public ref PETlsDirectoryData64 Data + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); + } public override unsafe void SetRawDataSize(int value) { diff --git a/src/LibObjectFile/PE/IRVALink.cs b/src/LibObjectFile/PE/IPELink.cs similarity index 57% rename from src/LibObjectFile/PE/IRVALink.cs rename to src/LibObjectFile/PE/IPELink.cs index 8f59912..e410584 100644 --- a/src/LibObjectFile/PE/IRVALink.cs +++ b/src/LibObjectFile/PE/IPELink.cs @@ -5,14 +5,19 @@ namespace LibObjectFile.PE; // ReSharper disable once InconsistentNaming -public interface IRVALink + + +public interface IPELink; + +public interface IPELink : IPELink where TObject: PEObjectBase { - public PEObject? Container { get; } + public TObject? Container { get; } public RVO RVO { get; } } -public interface IRVALink : IRVALink + +public interface IPELink : IPELink where TObject : PEObjectBase { public TData Resolve(); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageDebugDirectory.cs b/src/LibObjectFile/PE/Internal/RawImageDebugDirectory.cs new file mode 100644 index 0000000..f382ced --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageDebugDirectory.cs @@ -0,0 +1,19 @@ +// 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.Internal; + +#pragma warning disable CS0649 + +internal struct RawImageDebugDirectory +{ + public uint Characteristics; + public uint TimeDateStamp; + public ushort MajorVersion; + public ushort MinorVersion; + public PEDebugType Type; + public uint SizeOfData; + public RVA AddressOfRawData; + public uint PointerToRawData; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEAsciiStringLink.cs b/src/LibObjectFile/PE/PEAsciiStringLink.cs index f0bcfa2..a4376ac 100644 --- a/src/LibObjectFile/PE/PEAsciiStringLink.cs +++ b/src/LibObjectFile/PE/PEAsciiStringLink.cs @@ -2,31 +2,22 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using System.Diagnostics; + namespace LibObjectFile.PE; /// /// A link to a null terminated ASCII string in a . /// -public readonly struct PEAsciiStringLink : IRVALink +[DebuggerDisplay("{ToString(),nq}")] +public readonly record struct PEAsciiStringLink(PEStreamSectionData? Container, RVO RVO) : IPELink { - public PEAsciiStringLink(PEStreamSectionData? streamSectionData, RVO rvo) - { - StreamSectionData = streamSectionData; - RVO = rvo; - } - - public readonly PEStreamSectionData? StreamSectionData; - - public PEObject? Container => StreamSectionData; - - public RVO RVO { get; } - /// - public override string ToString() => this.ToDisplayText(); + public override string ToString() => this.ToDisplayTextWithRVA(); /// /// Resolves this link to a string. /// /// The string resolved. - public string? Resolve() => StreamSectionData?.ReadAsciiString(RVO); + public string? Resolve() => Container?.ReadAsciiString(RVO); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFunctionAddressLink.cs b/src/LibObjectFile/PE/PEFunctionAddressLink.cs index 4a02bec..74ba7c8 100644 --- a/src/LibObjectFile/PE/PEFunctionAddressLink.cs +++ b/src/LibObjectFile/PE/PEFunctionAddressLink.cs @@ -8,19 +8,7 @@ namespace LibObjectFile.PE; #pragma warning disable CS0649 [DebuggerDisplay("{ToString(),nq}")] -public struct PEFunctionAddressLink : IRVALink +public readonly record struct PEFunctionAddressLink(PEObject? Container, RVO RVO) : IPELink { - public PEFunctionAddressLink(PEObject? container, RVO rvo) - { - Container = container; - RVO = rvo; - } - - public PEObject? Container { get; } - - public RVO RVO { get; } - - public RVA RVA => Container is not null ? Container.RVA + RVO : 0; - public override string ToString() => Container is not null ? $"{Container}, Offset = {RVO}" : $""; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEImportHintNameLink.cs b/src/LibObjectFile/PE/PEImportHintNameLink.cs index 8cc7ae0..58d1178 100644 --- a/src/LibObjectFile/PE/PEImportHintNameLink.cs +++ b/src/LibObjectFile/PE/PEImportHintNameLink.cs @@ -2,31 +2,22 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using System.Diagnostics; + namespace LibObjectFile.PE; /// /// A link to a PE Import Hint Name in a . /// -public readonly struct PEImportHintNameLink : IRVALink +[DebuggerDisplay("{ToString(),nq}")] +public readonly record struct PEImportHintNameLink(PEStreamSectionData? Container, RVO RVO) : IPELink { - public PEImportHintNameLink(PEStreamSectionData? streamSectionData, RVO rvoInSection) - { - StreamSectionData = streamSectionData; - RVO = rvoInSection; - } - - public readonly PEStreamSectionData? StreamSectionData; - - public PEObject? Container => StreamSectionData; - - public RVO RVO { get; } - /// - public override string ToString() => this.ToDisplayText(); + public override string ToString() => this.ToDisplayTextWithRVA(); /// /// Resolves this link to a PE Import Hint Name. /// /// The PE Import Hint Name resolved. - public PEImportHintName Resolve() => StreamSectionData?.ReadHintName(RVO) ?? default; + public PEImportHintName Resolve() => Container?.ReadHintName(RVO) ?? default; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEModuleHandleLink.cs b/src/LibObjectFile/PE/PEModuleHandleLink.cs index a6ab02f..57f1afc 100644 --- a/src/LibObjectFile/PE/PEModuleHandleLink.cs +++ b/src/LibObjectFile/PE/PEModuleHandleLink.cs @@ -2,25 +2,16 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using System.Diagnostics; + namespace LibObjectFile.PE; /// /// A link to a module handle. /// -public readonly struct PEModuleHandleLink : IRVALink +[DebuggerDisplay("{ToString(),nq}")] +public readonly record struct PEModuleHandleLink(PEStreamSectionData? Container, RVO RVO) : IPELink { - public PEModuleHandleLink(PEStreamSectionData? streamSectionData, RVO rvo) - { - StreamSectionData = streamSectionData; - RVO = rvo; - } - - public readonly PEStreamSectionData? StreamSectionData; - - public PEObject? Container => StreamSectionData; - - public RVO RVO { get; } - /// - public override string ToString() => this.ToDisplayText(); + public override string ToString() => this.ToDisplayTextWithRVA(); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEObjectBase.cs b/src/LibObjectFile/PE/PEObjectBase.cs index 5ca90e5..3fd6f64 100644 --- a/src/LibObjectFile/PE/PEObjectBase.cs +++ b/src/LibObjectFile/PE/PEObjectBase.cs @@ -2,6 +2,7 @@ // 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; namespace LibObjectFile.PE; @@ -20,4 +21,14 @@ public abstract class PEObjectBase : ObjectFileElement public PEFile? GetPEFile() => FindParent(); + + public virtual int ReadAt(uint offset, Span destination) + { + throw new NotSupportedException($"The read operation is not supported for {this.GetType().FullName}"); + } + + public virtual void WriteAt(uint offset, ReadOnlySpan source) + { + throw new NotSupportedException($"The write operation is not supported for {this.GetType().FullName}"); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEObjectBaseExtensions.cs b/src/LibObjectFile/PE/PEObjectBaseExtensions.cs new file mode 100644 index 0000000..34d37cb --- /dev/null +++ b/src/LibObjectFile/PE/PEObjectBaseExtensions.cs @@ -0,0 +1,5 @@ +// 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; diff --git a/src/LibObjectFile/PE/PEObjectExtensions.cs b/src/LibObjectFile/PE/PEObjectExtensions.cs index 0024442..a19a0b2 100644 --- a/src/LibObjectFile/PE/PEObjectExtensions.cs +++ b/src/LibObjectFile/PE/PEObjectExtensions.cs @@ -12,6 +12,15 @@ namespace LibObjectFile.PE; public static class PEObjectExtensions { + public static bool IsNull(this TRVALink link) where TRVALink : IPELink => link.Container is null; + + public static string ToDisplayText(this TRVALink link) where TRVALink : IPELink => link.Container is not null ? $"{link.Container}, Offset = {link.RVO}" : $""; + + public static RVA RVA(this TRVALink link) where TRVALink : IPELink => link.Container is not null ? link.Container.RVA + link.RVO : 0; + + public static string ToDisplayTextWithRVA(this TRVALink link) where TRVALink : IPELink => link.Container is not null ? $"RVA = {RVA(link)}, {link.Container}, Offset = {link.RVO}" : $""; + + /// /// Tries to find a virtual object by its virtual address. /// @@ -52,8 +61,7 @@ public static bool TryFindByRVA(this ObjectList list, RVA item = null; return false; } - - + /// /// Tries to find a virtual object by its virtual address. /// diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index 0728d84..df955f0 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -281,16 +281,16 @@ private static void Print(PEFile file, PEBaseRelocationDirectory data, TextWrite foreach (var reloc in block.Relocations) { - var relocRVA = reloc.Link.RVA(); + var relocRVA = reloc.RVA(); var offsetInPage = relocRVA - pageRVA; if (reloc.Type == PEBaseRelocationType.Dir64) { - writer.WriteLine($" {reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA} (0x{reloc.ReadAddress(file):X16}), SectionData = {{ {PEDescribe(reloc.Link.SectionData)} }}"); + writer.WriteLine($" {reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA} (0x{reloc.ReadAddress(file):X16}), SectionData = {{ {PEDescribe(reloc.Container)} }}"); } else { - writer.WriteLine($" {reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA}, SectionData = {{ {PEDescribe(reloc.Link.SectionData)} }}"); + writer.WriteLine($" {reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA}, SectionData = {{ {PEDescribe(reloc.Container)} }}"); } } } diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index 408dd59..e3d0b6b 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -16,16 +16,6 @@ public abstract class PESectionData : PEObject protected PESectionData(bool isContainer) : base(isContainer) { } - - public virtual int ReadAt(uint offset, Span destination) - { - throw new NotSupportedException($"The read operation is not supported for {this.GetType().FullName}"); - } - - public virtual void WriteAt(uint offset, ReadOnlySpan source) - { - throw new NotSupportedException($"The write operation is not supported for {this.GetType().FullName}"); - } protected override void ValidateParent(ObjectElement parent) { diff --git a/src/LibObjectFile/PE/PESectionDataLink.cs b/src/LibObjectFile/PE/PESectionDataLink.cs index e86651b..6193770 100644 --- a/src/LibObjectFile/PE/PESectionDataLink.cs +++ b/src/LibObjectFile/PE/PESectionDataLink.cs @@ -10,19 +10,7 @@ namespace LibObjectFile.PE; /// A link to a section data. /// [DebuggerDisplay("{ToString(),nq}")] -public readonly struct PESectionDataLink : IRVALink +public readonly record struct PESectionDataLink(PESectionData? Container, RVO RVO) : IPELink { - public PESectionDataLink(PESectionData? sectionData, RVO rvo) - { - SectionData = sectionData; - RVO = rvo; - } - - public PESectionData? SectionData { get; } - - public PEObject? Container => SectionData; - - public RVO RVO { get; } - - public override string ToString() => this.ToDisplayText(); + public override string ToString() => this.ToDisplayTextWithRVA(); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PESectionLink.cs b/src/LibObjectFile/PE/PESectionLink.cs index b619403..de985c0 100644 --- a/src/LibObjectFile/PE/PESectionLink.cs +++ b/src/LibObjectFile/PE/PESectionLink.cs @@ -10,19 +10,7 @@ namespace LibObjectFile.PE; /// A link to a section ///
[DebuggerDisplay("{ToString(),nq}")] -public readonly struct PESectionLink : IRVALink +public readonly record struct PESectionLink(PESection? Container, RVO RVO) : IPELink { - public PESectionLink(PESection? section, uint rvo) - { - Section = section; - RVO = rvo; - } - - public PESection? Section { get; } - - public PEObject? Container => Section; - - public RVO RVO { get; } - - public override string ToString() => this.ToDisplayText(); + public override string ToString() => this.ToDisplayTextWithRVA(); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/RVALinkExtensions.cs b/src/LibObjectFile/PE/RVALinkExtensions.cs deleted file mode 100644 index 12b8d66..0000000 --- a/src/LibObjectFile/PE/RVALinkExtensions.cs +++ /dev/null @@ -1,14 +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; - -public static class RVALinkExtensions -{ - public static bool IsNull(this TRVALink link) where TRVALink : IRVALink => link.Container is null; - - public static RVA RVA(this TRVALink link) where TRVALink : IRVALink => link.Container is not null ? link.Container.RVA + link.RVO : 0; - - public static string ToDisplayText(this TRVALink link) where TRVALink : IRVALink => link.Container is not null ? $"RVA = {RVA(link)}, {link.Container}, Offset = {link.RVO}" : $""; -} \ No newline at end of file From b51129b861599dc3db8b148318e28bf86bf27310 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 25 Sep 2024 09:26:42 +0200 Subject: [PATCH 51/87] Improve support for debug directory --- src/LibObjectFile.sln.DotSettings | 1 + src/LibObjectFile/Diagnostics/DiagnosticId.cs | 6 +- .../PE/DataDirectory/PEDataDirectory.cs | 6 +- .../PE/DataDirectory/PEDebugDataRSDS.cs | 42 ------ .../PE/DataDirectory/PEDebugDirectory.cs | 126 ++++++++---------- .../PE/DataDirectory/PEDebugDirectoryEntry.cs | 74 +++------- .../PE/DataDirectory/PEDebugSectionData.cs | 14 ++ .../DataDirectory/PEDebugSectionDataRSDS.cs | 95 +++++++++++++ .../DataDirectory/PEDebugStreamExtraData.cs | 12 ++ .../DataDirectory/PEDebugStreamSectionData.cs | 28 ++++ .../DataDirectory/PEDelayImportDirectory.cs | 2 +- .../PE/DataDirectory/PEExportAddressTable.cs | 8 +- .../PE/DataDirectory/PEExportDirectory.cs | 2 +- .../PE/DataDirectory/PEExportNameTable.cs | 8 +- .../PE/DataDirectory/PEExportOrdinalTable.cs | 10 +- .../PE/DataDirectory/PEImportAddressTable.cs | 4 +- .../PE/DataDirectory/PEImportDirectory.cs | 2 +- .../PE/DataDirectory/PEImportLookupTable.cs | 5 +- .../PE/DataDirectory/PEThunkAddressTable.cs | 4 +- src/LibObjectFile/PE/PEFile.Read.cs | 102 +++++++------- src/LibObjectFile/PE/PEObject.cs | 9 +- src/LibObjectFile/PE/PESection.cs | 4 +- src/LibObjectFile/PE/PESectionData.cs | 2 +- src/LibObjectFile/PE/PEStreamExtraData.cs | 28 ++-- src/LibObjectFile/PE/PEStreamSectionData.cs | 22 ++- 25 files changed, 358 insertions(+), 258 deletions(-) delete mode 100644 src/LibObjectFile/PE/DataDirectory/PEDebugDataRSDS.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDebugSectionData.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDebugStreamExtraData.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEDebugStreamSectionData.cs diff --git a/src/LibObjectFile.sln.DotSettings b/src/LibObjectFile.sln.DotSettings index 287d885..7f73775 100644 --- a/src/LibObjectFile.sln.DotSettings +++ b/src/LibObjectFile.sln.DotSettings @@ -17,6 +17,7 @@ See the license.txt file in the project root for more information. True True True + True True True True diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 1fd7856..57fc0d2 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -124,7 +124,8 @@ public enum DiagnosticId PE_ERR_InvalidOptionalHeaderMagic = 3006, PE_ERR_InvalidSectionHeadersSize = 3007, PE_ERR_InvalidParent = 3008, - + PE_ERR_InvalidExtraData = 3009, + // PE BaseRelocation PE_ERR_BaseRelocationDirectoryInvalidEndOfStream = 3020, PE_ERR_BaseRelocationDirectoryInvalidSection = 3021, @@ -189,4 +190,7 @@ public enum DiagnosticId PE_ERR_DebugDirectorySize = 3600, PE_ERR_DebugDirectorySectionNotFound = 3601, PE_ERR_DebugDirectoryContainerNotFound = 3602, + PE_ERR_InvalidDebugDataRSDSSignature = 3603, + PE_ERR_InvalidDebugDataRSDSPdbPath = 3604, + PE_ERR_DebugDirectoryExtraData = 3605, } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index 21a9c47..f8107ff 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -13,7 +13,7 @@ namespace LibObjectFile.PE; public abstract class PEDataDirectory : PESectionData { - protected PEDataDirectory(PEDataDirectoryKind kind) : base(true) + protected PEDataDirectory(PEDataDirectoryKind kind) { Kind = kind; Content = CreateObjectList(this); @@ -21,6 +21,8 @@ protected PEDataDirectory(PEDataDirectoryKind kind) : base(true) public PEDataDirectoryKind Kind { get; } + public override bool HasChildren => true; + internal uint HeaderSize { get; private protected set; } /// @@ -62,7 +64,7 @@ public sealed override void UpdateLayout(PELayoutContext context) Size = size; } - internal virtual IEnumerable CollectImplicitSectionDataList() => Enumerable.Empty(); + internal virtual IEnumerable CollectImplicitSectionDataList() => Enumerable.Empty(); internal virtual void Bind(PEImageReader reader) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDataRSDS.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDataRSDS.cs deleted file mode 100644 index d0fc685..0000000 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugDataRSDS.cs +++ /dev/null @@ -1,42 +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. - -using System; -using System.Diagnostics; - -namespace LibObjectFile.PE; - -/// -/// Represents a RSDS debug data. -/// -[DebuggerDisplay("{ToString(),nq}")] -public sealed class PEDebugDataRSDS -{ - /// - /// Initializes a new instance of the class. - /// - public PEDebugDataRSDS() - { - Guid = Guid.Empty; - Age = 0; - PdbPath = string.Empty; - } - - /// - /// Gets or sets the GUID of the PDB. - /// - public Guid Guid { get; set; } - - /// - /// Gets or sets the age of the PDB. - /// - public uint Age { get; set; } - - /// - /// Gets or sets the path of the PDB. - /// - public string PdbPath { get; set; } - - public override string ToString() => $"{nameof(PEDebugDataRSDS)} {Guid} {Age} {PdbPath}"; -} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs index 7f3a0a8..1a9b23b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs @@ -27,6 +27,10 @@ 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 { @@ -43,38 +47,57 @@ public override unsafe void Read(PEImageReader reader) for (int i = 0; i < entryCount; i++) { - var entry = entries[i]; - var debugEntry = new PEDebugDirectoryEntry + var rawEntry = entries[i]; + var entry = new PEDebugDirectoryEntry { - Characteristics = entry.Characteristics, - MajorVersion = entry.MajorVersion, - MinorVersion = entry.MinorVersion, - Type = entry.Type, + Characteristics = rawEntry.Characteristics, + MajorVersion = rawEntry.MajorVersion, + MinorVersion = rawEntry.MinorVersion, + Type = rawEntry.Type, }; - if (entry.AddressOfRawData != 0) + if (rawEntry.AddressOfRawData != 0) { - if (!reader.File.TryFindSection(entry.AddressOfRawData, out var section)) + if (!reader.File.TryFindSection(rawEntry.AddressOfRawData, out var section)) { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_DebugDirectorySectionNotFound, $"Unable to find the section for the debug directory entry at {entry.AddressOfRawData}"); + reader.Diagnostics.Error(DiagnosticId.PE_ERR_DebugDirectorySectionNotFound, $"Unable to find the section for the debug directory entry at {rawEntry.AddressOfRawData}"); continue; } - var dataLink = new PEBlobDataLink(section, (RVO)(uint)entry.AddressOfRawData, entry.SizeOfData); - debugEntry.DataLink = dataLink; + PESectionData debugSectionData; + + if (rawEntry.Type == PEDebugKnownType.CodeView) + { + debugSectionData = new PEDebugSectionDataRSDS(); + } + else + { + debugSectionData = new PEDebugStreamSectionData(); + } + + debugSectionData.Position = section.Position + (RVA)(uint)rawEntry.AddressOfRawData - section.RVA; + debugSectionData.Size = rawEntry.SizeOfData; + + entry.SectionData = debugSectionData; } - else if (entry.PointerToRawData != 0) + else if (rawEntry.PointerToRawData != 0) { - var dataLink = new PEBlobDataLink(reader.File, entry.PointerToRawData, entry.SizeOfData); - debugEntry.DataLink = dataLink; + + var extraData = new PEDebugStreamExtraData + { + Position = (RVA)(uint)rawEntry.PointerToRawData, + Size = rawEntry.SizeOfData + }; + + entry.ExtraData = extraData; } else { - Debug.Assert(entry.SizeOfData == 0); + Debug.Assert(rawEntry.SizeOfData == 0); } - Entries.Add(debugEntry); + Entries.Add(entry); } } finally @@ -82,68 +105,37 @@ public override unsafe void Read(PEImageReader reader) ArrayPool.Shared.Return(buffer); } - HeaderSize = ComputeHeaderSize(reader); - } - - internal override void Bind(PEImageReader reader) - { - var entries = CollectionsMarshal.AsSpan(Entries); - var peFile = reader.File; - foreach (var entry in entries) + // Read the data associated with the debug directory entries + foreach (var entry in Entries) { - - if (entry.DataLink.Container is PEFile) + if (entry.SectionData is not null) { - PEObjectBase? container = null; - - foreach (var extraData in peFile.ExtraDataBeforeSections) - { - if (peFile.Contains(entry.DataLink.RVO)) - { - container = extraData; - break; - } - } - - if (container is null) - { - foreach (var section in peFile.ExtraDataAfterSections) - { - if (section.Contains(entry.DataLink.RVO)) - { - container = section; - break; - } - } - } - - if (container is null) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_DebugDirectoryContainerNotFound, $"Unable to find the container for the debug directory entry at {entry.DataLink.RVO}"); - continue; - } - - entry.DataLink = new(container, entry.DataLink.RVO - (uint)container.Position, entry.DataLink.Size); + entry.SectionData.Read(reader); } - else if (entry.DataLink.Container is PESection) + else if (entry.ExtraData is not null) { - var section = (PESection)entry.DataLink.Container!; + entry.ExtraData.Read(reader); + } + } - if (!section.TryFindSectionData((RVA)(uint)entry.DataLink.RVO, out var container)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_DebugDirectoryContainerNotFound, $"Unable to find the container for the debug directory entry at {entry.DataLink.RVO}"); - continue; - } + HeaderSize = ComputeHeaderSize(reader); + } - entry.DataLink = new(container, entry.DataLink.RVO - container.RVA, entry.DataLink.Size); + internal override IEnumerable CollectImplicitSectionDataList() + { + foreach (var entry in Entries) + { + if (entry.SectionData is not null) + { + yield return entry.SectionData; } - else + else if (entry.ExtraData is not null) { - // Ignore, there are no links + yield return entry.ExtraData; } } } - + public override void Write(PEImageWriter writer) { throw new NotImplementedException(); diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs index 950b716..1373d77 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs @@ -16,6 +16,8 @@ namespace LibObjectFile.PE; [DebuggerDisplay("{ToString(),nq}")] public sealed class PEDebugDirectoryEntry { + private PEObjectBase? _data; + /// /// Gets or sets the characteristics of the debug directory entry. /// @@ -42,70 +44,28 @@ public sealed class PEDebugDirectoryEntry public PEDebugType Type { get; set; } /// - /// Gets or sets the data link of the debug directory entry. + /// Gets or sets the associated section data to this debug entry. /// - public PEBlobDataLink DataLink { get; set; } + /// + /// This is set when the data is located inside a section. Otherwise might be set. + /// + public PESectionData? SectionData + { + get => _data as PESectionData; + set => _data = value; + } /// - /// Try to get the CodeView (RSDS) information from this debug directory entry. + /// Gets or sets the associated blob data outside a section (e.g. in or ). /// - /// The CodeView (RSDS) information if the entry is of type . - /// true if the CodeView (RSDS) information has been successfully retrieved. /// - /// The entry must be of type to be able to get the CodeView information. The data behind the must be a CodeView (RSDS) format. + /// This is set when the data is located outside a section. Otherwise might be set. /// - public unsafe bool TryGetDebugRSDS([NotNullWhen(true)] out PEDebugDataRSDS? debugRSDS) + public PEExtraData? ExtraData { - debugRSDS = null; - if (Type.Value != (uint)PEDebugKnownType.CodeView) - { - return false; - } - - var dataLink = DataLink; - if (dataLink.Container == null) - { - return false; - } - - var container = dataLink.Container; - var buffer = ArrayPool.Shared.Rent((int)dataLink.Size); - var span = buffer.AsSpan(0, (int)dataLink.Size); - try - { - var read = container.ReadAt(DataLink.RVO, span); - if (read != span.Length) - { - return false; - } - - var signature = MemoryMarshal.Read(span); - if (signature != 0x53445352) - { - return false; - } - - var pdbPath = span.Slice(sizeof(uint) + sizeof(Guid) + sizeof(uint)); - var indexOfZero = pdbPath.IndexOf((byte)0); - if (indexOfZero < 0) - { - return false; - } - - debugRSDS = new PEDebugDataRSDS - { - Guid = MemoryMarshal.Read(span.Slice(4)), - Age = MemoryMarshal.Read(span.Slice(sizeof(uint) + sizeof(Guid))), - PdbPath = System.Text.Encoding.UTF8.GetString(pdbPath.Slice(0, indexOfZero)) - }; - - return true; - } - finally - { - ArrayPool.Shared.Return(buffer); - } + get => _data as PEExtraData; + set => _data = value; } - public override string ToString() => $"{nameof(PEDebugDirectoryEntry)} {Type} {DataLink}"; + public override string ToString() => $"{nameof(PEDebugDirectoryEntry)} {Type} {_data}"; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionData.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionData.cs new file mode 100644 index 0000000..97adf2b --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionData.cs @@ -0,0 +1,14 @@ +// 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; + +public abstract class PEDebugSectionData : PESectionData +{ + protected PEDebugSectionData() + { + } + + public override bool HasChildren => false; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs new file mode 100644 index 0000000..7372f62 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs @@ -0,0 +1,95 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using LibObjectFile.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// Represents a RSDS debug data. +/// +[DebuggerDisplay("{ToString(),nq}")] +public sealed class PEDebugSectionDataRSDS : PEDebugSectionData +{ + /// + /// Initializes a new instance of the class. + /// + public PEDebugSectionDataRSDS() + { + Guid = Guid.Empty; + Age = 0; + PdbPath = string.Empty; + } + + /// + /// Gets or sets the GUID of the PDB. + /// + public Guid Guid { get; set; } + + /// + /// Gets or sets the age of the PDB. + /// + public uint Age { get; set; } + + /// + /// Gets or sets the path of the PDB. + /// + public string PdbPath { get; set; } + + public override unsafe void Read(PEImageReader reader) + { + reader.Position = Position; + var size = (int)Size; + var buffer = ArrayPool.Shared.Rent((int)Size); + var span = buffer.AsSpan(0, size); + try + { + var read = reader.Read(span); + if (read != span.Length) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unable to read PEDebugDataRSDS"); + return; + } + + var signature = MemoryMarshal.Read(span); + if (signature != 0x53445352) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidDebugDataRSDSSignature, $"Invalid signature for PEDebugDataRSDS"); + return; + } + + var pdbPath = span.Slice(sizeof(uint) + sizeof(Guid) + sizeof(uint)); + var indexOfZero = pdbPath.IndexOf((byte)0); + if (indexOfZero < 0) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidDebugDataRSDSPdbPath, $"Invalid PDB path for PEDebugDataRSDS"); + return; + } + + Guid = MemoryMarshal.Read(span.Slice(4)); + Age = MemoryMarshal.Read(span.Slice(sizeof(uint) + sizeof(Guid))); + PdbPath = System.Text.Encoding.UTF8.GetString(pdbPath.Slice(0, indexOfZero)); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + /// + protected override bool PrintMembers(StringBuilder builder) + { + if (base.PrintMembers(builder)) + { + builder.Append(", "); + } + builder.Append($"Guid: {Guid}, Age: {Age}, PdbPath: {PdbPath}"); + return true; + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugStreamExtraData.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugStreamExtraData.cs new file mode 100644 index 0000000..1221a72 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugStreamExtraData.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 debug directory entry in a Portable Executable (PE) file. +/// +public class PEDebugStreamExtraData : PEStreamExtraData +{ +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugStreamSectionData.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugStreamSectionData.cs new file mode 100644 index 0000000..dd30c7f --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugStreamSectionData.cs @@ -0,0 +1,28 @@ +// 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.IO; + +namespace LibObjectFile.PE; + +/// +/// A debug stream section data in a Portable Executable (PE) image. +/// +public class PEDebugStreamSectionData : PEStreamSectionData +{ + /// + /// Initializes a new instance of the class. + /// + public PEDebugStreamSectionData() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream containing the data of this section data. + public PEDebugStreamSectionData(Stream stream) : base(stream) + { + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs index b1be563..ed253ba 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs @@ -139,7 +139,7 @@ public override void Read(PEImageReader reader) } } } - internal override IEnumerable CollectImplicitSectionDataList() + internal override IEnumerable CollectImplicitSectionDataList() { foreach (var entry in Entries) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index 7b36c72..55e3d84 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -11,17 +11,19 @@ namespace LibObjectFile.PE; -public class PEExportAddressTable : PESectionData +public sealed class PEExportAddressTable : PESectionData { - public PEExportAddressTable() : base(false) + public PEExportAddressTable() { } - public PEExportAddressTable(int count) : base(false) + public PEExportAddressTable(int count) { CollectionsMarshal.SetCount(Values, count); } + public override bool HasChildren => false; + public List Values { get; } = new(); public override unsafe void UpdateLayout(PELayoutContext context) diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs index dad95bb..ca51cb0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs @@ -37,7 +37,7 @@ protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) return (uint)sizeof(RawImageExportDirectory); } - internal override IEnumerable CollectImplicitSectionDataList() + internal override IEnumerable CollectImplicitSectionDataList() { if (ExportFunctionAddressTable is not null) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs index 1964001..a9976d0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs @@ -10,17 +10,19 @@ namespace LibObjectFile.PE; -public class PEExportNameTable : PESectionData +public sealed class PEExportNameTable : PESectionData { - public PEExportNameTable() : base(false) + public PEExportNameTable() { } - public PEExportNameTable(int count) : base(false) + public PEExportNameTable(int count) { CollectionsMarshal.SetCount(Values, count); } + public override bool HasChildren => false; + public List Values { get; } = new(); public override unsafe void UpdateLayout(PELayoutContext context) diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs index 0b15903..f2fe97d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs @@ -8,17 +8,19 @@ namespace LibObjectFile.PE; -public class PEExportOrdinalTable : PESectionData +public sealed class PEExportOrdinalTable : PESectionData { - public PEExportOrdinalTable() : base(false) + public PEExportOrdinalTable() { } - public PEExportOrdinalTable(int count) : base(false) + public PEExportOrdinalTable(int count) { CollectionsMarshal.SetCount(Values, count); } - + + public override bool HasChildren => false; + public List Values { get; } = new(); diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs index b79d88b..0bca02e 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs @@ -10,11 +10,13 @@ public class PEImportAddressTable : PESectionData { internal readonly PEImportFunctionTable FunctionTable; - public PEImportAddressTable() : base(false) + public PEImportAddressTable() { FunctionTable = new PEImportFunctionTable(); } + public override bool HasChildren => false; + public List Entries => FunctionTable.Entries; public override void UpdateLayout(PELayoutContext context) diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 0b44d82..96ca935 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -103,7 +103,7 @@ public override void Read(PEImageReader reader) protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) => CalculateSize(); - internal override IEnumerable CollectImplicitSectionDataList() + internal override IEnumerable CollectImplicitSectionDataList() { foreach (var entry in _entries) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs index 3fe75aa..782769e 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -12,11 +12,13 @@ public sealed class PEImportLookupTable : PESectionData { internal readonly PEImportFunctionTable FunctionTable; - public PEImportLookupTable() : base(false) + public PEImportLookupTable() { FunctionTable = new PEImportFunctionTable(); } + public override bool HasChildren => false; + public List Entries => FunctionTable.Entries; public override void UpdateLayout(PELayoutContext context) @@ -31,4 +33,5 @@ public override void Read(PEImageReader reader) } public override void Write(PEImageWriter writer) => FunctionTable.Write(writer); + } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs index c065ecb..aa8c67d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs @@ -10,11 +10,13 @@ namespace LibObjectFile.PE; public abstract class PEThunkAddressTable : PESectionData { - protected PEThunkAddressTable(bool is32Bits) : base(false) + protected PEThunkAddressTable(bool is32Bits) { Is32Bits = is32Bits; } + public override bool HasChildren => false; + public bool Is32Bits { get; } public abstract int Count { get; } diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index f88048e..3ce126c 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -200,13 +200,7 @@ public override void Read(PEImageReader reader) } // Read all sections and directories - if (TryInitializeSections(reader, sectionHeaders, out var positionAfterLastSection)) - { - // Read the remaining of the file - var sizeToRead = reader.Length - positionAfterLastSection; - - FillExtraDataWithMissingStreams(reader, positionAfterLastSection, sizeToRead); - } + ReadSectionsAndDirectories(reader, sectionHeaders); } finally { @@ -214,29 +208,21 @@ public override void Read(PEImageReader reader) } } - private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan headers, out ulong positionAfterLastSection) + private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan headers) { _sections.Clear(); - positionAfterLastSection = 0; + uint positionAfterHeaders = (uint)reader.Position; + uint positionFirstSection = positionAfterHeaders; // Load any data stored before the sections if (headers.Length > 0) { - var firstSectionPosition = headers[0].PointerToRawData; - - var position = reader.Position; - var lengthBeforeFirstSection = firstSectionPosition - position; - if (lengthBeforeFirstSection > 0) - { - var extraData = new PEStreamExtraData(reader.ReadAsStream((ulong)lengthBeforeFirstSection)) - { - Position = position, - }; - ExtraDataBeforeSections.Add(extraData); - } + positionFirstSection = headers[0].PointerToRawData; } + uint positionAfterLastSection = positionFirstSection; + // Create sections foreach (var section in headers) { @@ -259,6 +245,7 @@ private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan(); + var extraDataList = new List(); // Create directories and find the section for each directory var maxNumberOfDirectory = (int)Math.Min(OptionalHeader.NumberOfRvaAndSizes, 15); @@ -289,7 +276,7 @@ private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan= positionAfterLastSection) + { + ExtraDataAfterSections.Add(extraData); + } + else + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExtraData, $"Extra data found in the middle of the sections at {extraData.Position}"); + } } // Attach all the section data we have created (from e.g. directories, import tables) to the sections @@ -340,7 +354,7 @@ private bool TryInitializeSections(PEImageReader reader, ReadOnlySpan @@ -499,21 +510,18 @@ private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data position {currentPosition} > {startPosition + totalSize} in {container}"); } } - - - private void FillExtraDataWithMissingStreams(PEImageReader imageReader, ulong extraPosition, ulong extraTotalSize) + + private static void FillExtraDataWithMissingStreams(PEImageReader imageReader, ObjectList list, ulong extraPosition, ulong extraTotalSize) { var currentPosition = extraPosition; imageReader.Position = extraPosition; // We are working on position, while the list is ordered by VirtualAddress - var listOrderedByPosition = new List(); - listOrderedByPosition.AddRange(ExtraDataAfterSections.UnsafeList); - listOrderedByPosition.Sort((a, b) => a.Position.CompareTo(b.Position)); - - for (var i = 0; i < listOrderedByPosition.Count; i++) + list.UnsafeList.Sort((a, b) => a.Position.CompareTo(b.Position)); + + for (var i = 0; i < list.Count; i++) { - var data = listOrderedByPosition[i]; + var data = list[i]; if (currentPosition < data.Position) { var size = data.Position - currentPosition; @@ -525,7 +533,7 @@ private void FillExtraDataWithMissingStreams(PEImageReader imageReader, ulong ex Size = size, }; - ExtraDataAfterSections.Insert(data.Index, sectionData); + list.Insert(data.Index, sectionData); currentPosition = data.Position; } else if (currentPosition > data.Position) @@ -547,7 +555,7 @@ private void FillExtraDataWithMissingStreams(PEImageReader imageReader, ulong ex Size = size, }; - ExtraDataAfterSections.Add(sectionData); + list.Add(sectionData); } else if (currentPosition > extraPosition + extraTotalSize) { diff --git a/src/LibObjectFile/PE/PEObject.cs b/src/LibObjectFile/PE/PEObject.cs index 7098bad..32f873f 100644 --- a/src/LibObjectFile/PE/PEObject.cs +++ b/src/LibObjectFile/PE/PEObject.cs @@ -15,15 +15,14 @@ namespace LibObjectFile.PE; /// public abstract class PEObject : PEObjectBase { - protected PEObject(bool isContainer) + protected PEObject() { - IsContainer = isContainer; } /// /// Gets a value indicating whether this object has children. /// - public bool IsContainer { get; } + public abstract bool HasChildren { get; } /// /// The address of the first byte of the section when loaded into memory, relative to the image base. @@ -63,7 +62,7 @@ public bool TryFindByRVA(RVA rva, out PEObject? result) { if (ContainsVirtual(rva)) { - if (IsContainer && TryFindByRVAInChildren(rva, out result)) + if (HasChildren && TryFindByRVAInChildren(rva, out result)) { return true; } @@ -91,7 +90,7 @@ protected virtual bool TryFindByRVAInChildren(RVA rva, out PEObject? result) internal void UpdateRVA(RVA rva) { RVA = rva; - if (IsContainer) + if (HasChildren) { UpdateRVAInChildren(); } diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index 0d0daa1..ffc483d 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -20,7 +20,7 @@ public sealed class PESection : PEObject { private readonly ObjectList _content; - public PESection(PESectionName name, RVA rva, RVA virtualSize) : base(true) + public PESection(PESectionName name, RVA rva, RVA virtualSize) { Name = name; RVA = rva; @@ -35,6 +35,8 @@ public PESection(PESectionName name, RVA rva, RVA virtualSize) : base(true) /// public PESectionName Name { get; } + public override bool HasChildren => true; + /// /// The total size of the section when loaded into memory. /// If this value is greater than , the section is zero-padded. diff --git a/src/LibObjectFile/PE/PESectionData.cs b/src/LibObjectFile/PE/PESectionData.cs index e3d0b6b..0af00ff 100644 --- a/src/LibObjectFile/PE/PESectionData.cs +++ b/src/LibObjectFile/PE/PESectionData.cs @@ -13,7 +13,7 @@ namespace LibObjectFile.PE; /// public abstract class PESectionData : PEObject { - protected PESectionData(bool isContainer) : base(isContainer) + protected PESectionData() { } diff --git a/src/LibObjectFile/PE/PEStreamExtraData.cs b/src/LibObjectFile/PE/PEStreamExtraData.cs index 7522c6d..0b270a3 100644 --- a/src/LibObjectFile/PE/PEStreamExtraData.cs +++ b/src/LibObjectFile/PE/PEStreamExtraData.cs @@ -10,56 +10,56 @@ namespace LibObjectFile.PE; /// /// Defines a stream extra data in the PE file . /// -public sealed class PEStreamExtraData : PEExtraData +public class PEStreamExtraData : PEExtraData { - private Stream _data; + private Stream _stream; /// /// Initializes a new instance of the class. /// public PEStreamExtraData() { - _data = Stream.Null; + _stream = Stream.Null; } /// /// Initializes a new instance of the class. /// - /// The data stream. - public PEStreamExtraData(Stream data) + /// The data stream. + public PEStreamExtraData(Stream stream) { - ArgumentNullException.ThrowIfNull(data); - _data = data; - Size = (uint)data.Length; + ArgumentNullException.ThrowIfNull(stream); + _stream = stream; + Size = (uint)stream.Length; } /// /// Gets or sets the data stream. /// - public Stream Data + public Stream Stream { - get => _data; + get => _stream; set { ArgumentNullException.ThrowIfNull(value); - _data = value; + _stream = value; Size = (uint)value.Length; } } public override void UpdateLayout(PELayoutContext layoutContext) { - Size = (uint)Data.Length; + Size = (uint)Stream.Length; } public override void Read(PEImageReader reader) { reader.Position = Position; - Data = reader.ReadAsStream(Size); + Stream = reader.ReadAsStream(Size); } public override void Write(PEImageWriter writer) { - Data.CopyTo(writer.Stream); + Stream.CopyTo(writer.Stream); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEStreamSectionData.cs b/src/LibObjectFile/PE/PEStreamSectionData.cs index 9c6020f..2b7b491 100644 --- a/src/LibObjectFile/PE/PEStreamSectionData.cs +++ b/src/LibObjectFile/PE/PEStreamSectionData.cs @@ -12,14 +12,14 @@ namespace LibObjectFile.PE; ///
public class PEStreamSectionData : PESectionData { - private readonly Stream _stream; + private Stream _stream; internal static PEStreamSectionData Empty = new(); /// /// Initializes a new instance of the class. /// - private PEStreamSectionData() : base(false) + public PEStreamSectionData() { _stream = Stream.Null; Size = 0; @@ -29,17 +29,28 @@ private PEStreamSectionData() : base(false) /// Initializes a new instance of the class. ///
/// The stream containing the data of this section data. - public PEStreamSectionData(Stream stream) : base(false) + public PEStreamSectionData(Stream stream) { ArgumentNullException.ThrowIfNull(stream); _stream = stream; Size = (ulong)stream.Length; } + public override bool HasChildren => false; + /// /// Gets the stream containing the data of this section data. /// - public Stream Stream => _stream; + public Stream Stream + { + get => _stream; + set + { + ArgumentNullException.ThrowIfNull(value); + _stream = value; + Size = (ulong)value.Length; + } + } public override void UpdateLayout(PELayoutContext layoutContext) { @@ -48,7 +59,8 @@ public override void UpdateLayout(PELayoutContext layoutContext) public override void Read(PEImageReader reader) { - // No need to read, as the data is already provided via a stream + reader.Position = Position; + Stream = reader.ReadAsStream(Size); } public override void Write(PEImageWriter writer) From a77d364649ef93bb7aa844af939606beba0165a1 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 25 Sep 2024 09:27:01 +0200 Subject: [PATCH 52/87] Add TextWriterIndenter and update PEPrinter to use it --- src/LibObjectFile/IO/TextWriterIndenter.cs | 166 +++++ src/LibObjectFile/PE/PEPrinter.cs | 683 +++++++++------------ 2 files changed, 460 insertions(+), 389 deletions(-) create mode 100644 src/LibObjectFile/IO/TextWriterIndenter.cs diff --git a/src/LibObjectFile/IO/TextWriterIndenter.cs b/src/LibObjectFile/IO/TextWriterIndenter.cs new file mode 100644 index 0000000..dbc5f2f --- /dev/null +++ b/src/LibObjectFile/IO/TextWriterIndenter.cs @@ -0,0 +1,166 @@ +// 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.Runtime.CompilerServices; + +namespace LibObjectFile.IO; + +/// +/// Provides indentation functionality for writing text to a . +/// +public struct TextWriterIndenter +{ + private readonly TextWriter _writer; + private int _indentLevel; + private readonly int _indentSize; + private bool _previousCharWasNewLine; + + /// + /// Initializes a new instance of the struct with the specified . + /// + /// The to write the indented text to. + public TextWriterIndenter(TextWriter writer) + { + _writer = writer; + _indentSize = 4; + _indentLevel = 0; + _previousCharWasNewLine = true; + } + + /// + /// Gets or sets the size of the indentation. + /// + /// The size of the indentation. The default value is 4. + /// Thrown when the specified value is less than 0 or greater than 8. + public int IndentSize + { + get => _indentSize; + init + { + if (value < 0 || value > 8) + { + throw new ArgumentOutOfRangeException(nameof(value), "IndentSize must be between 0 and 8"); + } + + _indentSize = value; + } + } + + /// + /// Writes the specified string to the underlying without indenting. + /// + /// The string to write. + public void Write(string value) + { + WriteInternal(value); + } + + /// + /// Writes the specified span of characters to the underlying without indenting. + /// + /// The span of characters to write. + public void Write(ReadOnlySpan value) + { + WriteInternal(value); + } + + /// + /// Writes a new line to the underlying with the current indentation level. + /// + public void WriteLine() + { + WriteIndent(); + _writer.WriteLine(); + _previousCharWasNewLine = true; + } + + /// + /// Writes the specified string to the underlying with the current indentation level. + /// + /// The string to write. + public void WriteLine(string value) + { + WriteInternal(value); + _writer.WriteLine(); + _previousCharWasNewLine = true; + } + + /// + /// Writes the specified span of characters to the underlying with the current indentation level. + /// + /// The span of characters to write. + public void WriteLine(ReadOnlySpan value) + { + WriteInternal(value); + _writer.WriteLine(); + _previousCharWasNewLine = true; + } + + /// + /// Increases the indentation level by one. + /// + public void Indent() + { + _indentLevel++; + } + + /// + /// Decreases the indentation level by one. + /// + /// Thrown when trying to unindent below 0. + public void Unindent() + { + if (_indentLevel == 0) + { + throw new InvalidOperationException("Cannot unindent below 0"); + } + _indentLevel--; + } + + private void WriteInternal(ReadOnlySpan data) + { + while (data.Length > 0) + { + WriteIndent(); + + var nextEndOfLine = data.IndexOfAny('\r', '\n'); + if (nextEndOfLine < 0) + { + _writer.Write(data); + break; + } + + // Write the current line + _writer.WriteLine(data.Slice(0, nextEndOfLine)); + _previousCharWasNewLine = true; + + // Move to the next line + data = data.Slice(nextEndOfLine + 1); + if (data.Length > 0 && data[0] == '\n') + { + data = data.Slice(1); + } + } + } + + [SkipLocalsInit] + private void WriteIndent() + { + if (_previousCharWasNewLine) + { + var indentSize = _indentLevel * IndentSize; + if (indentSize > 0) + { + // Could be more optimized without requiring a stackalloc + Span buffer = stackalloc char[_indentLevel * IndentSize]; + buffer.Fill(' '); + _writer.Write(buffer); + } + + _previousCharWasNewLine = false; + } + } +} diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index df955f0..78dcb4e 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using LibObjectFile.IO; namespace LibObjectFile.PE; @@ -13,272 +14,270 @@ public static void Print(this PEFile file, TextWriter writer) { ArgumentNullException.ThrowIfNull(file); ArgumentNullException.ThrowIfNull(writer); + + var indenter = new TextWriterIndenter(writer); - PrintHeaders(file, writer); - PrintDataDirectories(file, writer); - PrintSections(file, writer); + PrintHeaders(file, ref indenter); + PrintDataDirectories(file, ref indenter); + PrintSections(file, ref indenter); } - public static void PrintHeaders(PEFile file, TextWriter writer) + private static void PrintHeaders(PEFile file, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(writer); - - PrintDosHeader(file, writer); - PrintDosStub(file, writer); - PrintCoffHeader(file, writer); - PrintOptionalHeader(file, writer); + PrintDosHeader(file, ref writer); + PrintDosStub(file, ref writer); + PrintCoffHeader(file, ref writer); + PrintOptionalHeader(file, ref writer); } - public static unsafe void PrintDosHeader(PEFile file, TextWriter writer) + private static unsafe void PrintDosHeader(PEFile file, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(writer); - const int indent = -26; writer.WriteLine("DOS Header"); - writer.WriteLine($" {nameof(ImageDosHeader.Magic),indent} = {file.DosHeader.Magic}"); - writer.WriteLine($" {nameof(ImageDosHeader.ByteCountOnLastPage),indent} = 0x{file.DosHeader.ByteCountOnLastPage:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.PageCount),indent} = 0x{file.DosHeader.PageCount:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.RelocationCount),indent} = 0x{file.DosHeader.RelocationCount:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.SizeOfParagraphsHeader),indent} = 0x{file.DosHeader.SizeOfParagraphsHeader:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.MinExtraParagraphs),indent} = 0x{file.DosHeader.MinExtraParagraphs:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.MaxExtraParagraphs),indent} = 0x{file.DosHeader.MaxExtraParagraphs:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.InitialSSValue),indent} = 0x{file.DosHeader.InitialSSValue:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.InitialSPValue),indent} = 0x{file.DosHeader.InitialSPValue:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.Checksum),indent} = 0x{file.DosHeader.Checksum:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.InitialIPValue),indent} = 0x{file.DosHeader.InitialIPValue:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.InitialCSValue),indent} = 0x{file.DosHeader.InitialCSValue:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.FileAddressRelocationTable),indent} = 0x{file.DosHeader.FileAddressRelocationTable:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.OverlayNumber),indent} = 0x{file.DosHeader.OverlayNumber:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.Reserved),indent} = 0x{file.DosHeader.Reserved[0]:X}, 0x{file.DosHeader.Reserved[1]:X}, 0x{file.DosHeader.Reserved[2]:X}, 0x{file.DosHeader.Reserved[3]:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.OEMIdentifier),indent} = 0x{file.DosHeader.OEMIdentifier:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.OEMInformation),indent} = 0x{file.DosHeader.OEMInformation:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.Reserved2),indent} = 0x{file.DosHeader.Reserved2[0]:X}, 0x{file.DosHeader.Reserved2[1]:X}, 0x{file.DosHeader.Reserved2[2]:X}, 0x{file.DosHeader.Reserved2[3]:X}, 0x{file.DosHeader.Reserved2[4]:X}, 0x{file.DosHeader.Reserved2[5]:X}, 0x{file.DosHeader.Reserved2[6]:X}, 0x{file.DosHeader.Reserved2[7]:X}, 0x{file.DosHeader.Reserved2[8]:X}, 0x{file.DosHeader.Reserved2[9]:X}"); - writer.WriteLine($" {nameof(ImageDosHeader.FileAddressPEHeader),indent} = 0x{file.DosHeader.FileAddressPEHeader:X}"); + writer.Indent(); + { + writer.WriteLine($"{nameof(ImageDosHeader.Magic),indent} = {file.DosHeader.Magic}"); + writer.WriteLine($"{nameof(ImageDosHeader.ByteCountOnLastPage),indent} = 0x{file.DosHeader.ByteCountOnLastPage:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.PageCount),indent} = 0x{file.DosHeader.PageCount:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.RelocationCount),indent} = 0x{file.DosHeader.RelocationCount:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.SizeOfParagraphsHeader),indent} = 0x{file.DosHeader.SizeOfParagraphsHeader:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.MinExtraParagraphs),indent} = 0x{file.DosHeader.MinExtraParagraphs:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.MaxExtraParagraphs),indent} = 0x{file.DosHeader.MaxExtraParagraphs:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.InitialSSValue),indent} = 0x{file.DosHeader.InitialSSValue:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.InitialSPValue),indent} = 0x{file.DosHeader.InitialSPValue:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.Checksum),indent} = 0x{file.DosHeader.Checksum:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.InitialIPValue),indent} = 0x{file.DosHeader.InitialIPValue:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.InitialCSValue),indent} = 0x{file.DosHeader.InitialCSValue:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.FileAddressRelocationTable),indent} = 0x{file.DosHeader.FileAddressRelocationTable:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.OverlayNumber),indent} = 0x{file.DosHeader.OverlayNumber:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.Reserved),indent} = 0x{file.DosHeader.Reserved[0]:X}, 0x{file.DosHeader.Reserved[1]:X}, 0x{file.DosHeader.Reserved[2]:X}, 0x{file.DosHeader.Reserved[3]:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.OEMIdentifier),indent} = 0x{file.DosHeader.OEMIdentifier:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.OEMInformation),indent} = 0x{file.DosHeader.OEMInformation:X}"); + writer.WriteLine( + $"{nameof(ImageDosHeader.Reserved2),indent} = 0x{file.DosHeader.Reserved2[0]:X}, 0x{file.DosHeader.Reserved2[1]:X}, 0x{file.DosHeader.Reserved2[2]:X}, 0x{file.DosHeader.Reserved2[3]:X}, 0x{file.DosHeader.Reserved2[4]:X}, 0x{file.DosHeader.Reserved2[5]:X}, 0x{file.DosHeader.Reserved2[6]:X}, 0x{file.DosHeader.Reserved2[7]:X}, 0x{file.DosHeader.Reserved2[8]:X}, 0x{file.DosHeader.Reserved2[9]:X}"); + writer.WriteLine($"{nameof(ImageDosHeader.FileAddressPEHeader),indent} = 0x{file.DosHeader.FileAddressPEHeader:X}"); + } + writer.Unindent(); writer.WriteLine(); } - public static void PrintDosStub(PEFile file, TextWriter writer) + private static void PrintDosStub(PEFile file, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(writer); const int indent = -26; writer.WriteLine("DOS Stub"); - writer.WriteLine($" {nameof(file.DosStub),indent} = {file.DosStub.Length} bytes"); + writer.Indent(); + { + writer.WriteLine($"{nameof(file.DosStub),indent} = {file.DosStub.Length} bytes"); + } + writer.Unindent(); writer.WriteLine(); } - public static void PrintCoffHeader(PEFile file, TextWriter writer) + private static void PrintCoffHeader(PEFile file, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(writer); - const int indent = -26; writer.WriteLine("COFF Header"); - writer.WriteLine($" {nameof(ImageCoffHeader.Machine),indent} = {file.CoffHeader.Machine}"); - writer.WriteLine($" {nameof(ImageCoffHeader.NumberOfSections),indent} = {file.CoffHeader.NumberOfSections}"); - writer.WriteLine($" {nameof(ImageCoffHeader.TimeDateStamp),indent} = {file.CoffHeader.TimeDateStamp}"); - writer.WriteLine($" {nameof(ImageCoffHeader.PointerToSymbolTable),indent} = 0x{file.CoffHeader.PointerToSymbolTable:X}"); - writer.WriteLine($" {nameof(ImageCoffHeader.NumberOfSymbols),indent} = {file.CoffHeader.NumberOfSymbols}"); - writer.WriteLine($" {nameof(ImageCoffHeader.SizeOfOptionalHeader),indent} = {file.CoffHeader.SizeOfOptionalHeader}"); - writer.WriteLine($" {nameof(ImageCoffHeader.Characteristics),indent} = {file.CoffHeader.Characteristics}"); + writer.Indent(); + { + writer.WriteLine($"{nameof(ImageCoffHeader.Machine),indent} = {file.CoffHeader.Machine}"); + writer.WriteLine($"{nameof(ImageCoffHeader.NumberOfSections),indent} = {file.CoffHeader.NumberOfSections}"); + writer.WriteLine($"{nameof(ImageCoffHeader.TimeDateStamp),indent} = {file.CoffHeader.TimeDateStamp}"); + writer.WriteLine($"{nameof(ImageCoffHeader.PointerToSymbolTable),indent} = 0x{file.CoffHeader.PointerToSymbolTable:X}"); + writer.WriteLine($"{nameof(ImageCoffHeader.NumberOfSymbols),indent} = {file.CoffHeader.NumberOfSymbols}"); + writer.WriteLine($"{nameof(ImageCoffHeader.SizeOfOptionalHeader),indent} = {file.CoffHeader.SizeOfOptionalHeader}"); + writer.WriteLine($"{nameof(ImageCoffHeader.Characteristics),indent} = {file.CoffHeader.Characteristics}"); + } + writer.Unindent(); writer.WriteLine(); } - public static void PrintOptionalHeader(PEFile file, TextWriter writer) + private static void PrintOptionalHeader(PEFile file, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(writer); - const int indent = -26; writer.WriteLine("Optional Header"); - writer.WriteLine($" {nameof(ImageOptionalHeader.Magic),indent} = {file.OptionalHeader.Magic}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.MajorLinkerVersion),indent} = {file.OptionalHeader.MajorLinkerVersion}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.MinorLinkerVersion),indent} = {file.OptionalHeader.MinorLinkerVersion}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfCode),indent} = 0x{file.OptionalHeader.SizeOfCode:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfInitializedData),indent} = 0x{file.OptionalHeader.SizeOfInitializedData:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfUninitializedData),indent} = 0x{file.OptionalHeader.SizeOfUninitializedData:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.AddressOfEntryPoint),indent} = 0x{file.OptionalHeader.AddressOfEntryPoint:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.BaseOfCode),indent} = 0x{file.OptionalHeader.BaseOfCode:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.BaseOfData),indent} = 0x{file.OptionalHeader.BaseOfData:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.ImageBase),indent} = 0x{file.OptionalHeader.ImageBase:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.SectionAlignment),indent} = 0x{file.OptionalHeader.SectionAlignment:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.FileAlignment),indent} = 0x{file.OptionalHeader.FileAlignment:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.MajorOperatingSystemVersion),indent} = {file.OptionalHeader.MajorOperatingSystemVersion}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.MinorOperatingSystemVersion),indent} = {file.OptionalHeader.MinorOperatingSystemVersion}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.MajorImageVersion),indent} = {file.OptionalHeader.MajorImageVersion}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.MinorImageVersion),indent} = {file.OptionalHeader.MinorImageVersion}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.MajorSubsystemVersion),indent} = {file.OptionalHeader.MajorSubsystemVersion}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.MinorSubsystemVersion),indent} = {file.OptionalHeader.MinorSubsystemVersion}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.Win32VersionValue),indent} = 0x{file.OptionalHeader.Win32VersionValue:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfImage),indent} = 0x{file.OptionalHeader.SizeOfImage:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfHeaders),indent} = 0x{file.OptionalHeader.SizeOfHeaders:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.CheckSum),indent} = 0x{file.OptionalHeader.CheckSum:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.Subsystem),indent} = {file.OptionalHeader.Subsystem}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.DllCharacteristics),indent} = {file.OptionalHeader.DllCharacteristics}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfStackReserve),indent} = 0x{file.OptionalHeader.SizeOfStackReserve:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfStackCommit),indent} = 0x{file.OptionalHeader.SizeOfStackCommit:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfHeapReserve),indent} = 0x{file.OptionalHeader.SizeOfHeapReserve:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.SizeOfHeapCommit),indent} = 0x{file.OptionalHeader.SizeOfHeapCommit:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.LoaderFlags),indent} = 0x{file.OptionalHeader.LoaderFlags:X}"); - writer.WriteLine($" {nameof(ImageOptionalHeader.NumberOfRvaAndSizes),indent} = 0x{file.OptionalHeader.NumberOfRvaAndSizes:X}"); + writer.Indent(); + { + writer.WriteLine($"{nameof(ImageOptionalHeader.Magic),indent} = {file.OptionalHeader.Magic}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.MajorLinkerVersion),indent} = {file.OptionalHeader.MajorLinkerVersion}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.MinorLinkerVersion),indent} = {file.OptionalHeader.MinorLinkerVersion}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfCode),indent} = 0x{file.OptionalHeader.SizeOfCode:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfInitializedData),indent} = 0x{file.OptionalHeader.SizeOfInitializedData:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfUninitializedData),indent} = 0x{file.OptionalHeader.SizeOfUninitializedData:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.AddressOfEntryPoint),indent} = 0x{file.OptionalHeader.AddressOfEntryPoint:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.BaseOfCode),indent} = 0x{file.OptionalHeader.BaseOfCode:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.BaseOfData),indent} = 0x{file.OptionalHeader.BaseOfData:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.ImageBase),indent} = 0x{file.OptionalHeader.ImageBase:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.SectionAlignment),indent} = 0x{file.OptionalHeader.SectionAlignment:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.FileAlignment),indent} = 0x{file.OptionalHeader.FileAlignment:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.MajorOperatingSystemVersion),indent} = {file.OptionalHeader.MajorOperatingSystemVersion}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.MinorOperatingSystemVersion),indent} = {file.OptionalHeader.MinorOperatingSystemVersion}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.MajorImageVersion),indent} = {file.OptionalHeader.MajorImageVersion}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.MinorImageVersion),indent} = {file.OptionalHeader.MinorImageVersion}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.MajorSubsystemVersion),indent} = {file.OptionalHeader.MajorSubsystemVersion}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.MinorSubsystemVersion),indent} = {file.OptionalHeader.MinorSubsystemVersion}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.Win32VersionValue),indent} = 0x{file.OptionalHeader.Win32VersionValue:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfImage),indent} = 0x{file.OptionalHeader.SizeOfImage:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfHeaders),indent} = 0x{file.OptionalHeader.SizeOfHeaders:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.CheckSum),indent} = 0x{file.OptionalHeader.CheckSum:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.Subsystem),indent} = {file.OptionalHeader.Subsystem}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.DllCharacteristics),indent} = {file.OptionalHeader.DllCharacteristics}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfStackReserve),indent} = 0x{file.OptionalHeader.SizeOfStackReserve:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfStackCommit),indent} = 0x{file.OptionalHeader.SizeOfStackCommit:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfHeapReserve),indent} = 0x{file.OptionalHeader.SizeOfHeapReserve:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfHeapCommit),indent} = 0x{file.OptionalHeader.SizeOfHeapCommit:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.LoaderFlags),indent} = 0x{file.OptionalHeader.LoaderFlags:X}"); + writer.WriteLine($"{nameof(ImageOptionalHeader.NumberOfRvaAndSizes),indent} = 0x{file.OptionalHeader.NumberOfRvaAndSizes:X}"); + } + writer.Unindent(); writer.WriteLine(); } - public static void PrintDataDirectories(PEFile file, TextWriter writer) + private static void PrintDataDirectories(PEFile file, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(writer); - writer.WriteLine("Data Directories"); + writer.Indent(); for(int i = (int)PEDataDirectoryKind.Export; i <= (int)PEDataDirectoryKind.ClrMetadata; i++) { var kind = (PEDataDirectoryKind)i; var directory = file.Directories[kind]; writer.WriteLine(directory is null - ? $" [{i,3}] = null" - : $" [{i,3}] = {PEDescribe(directory)}"); + ? $"[{i:00}] = null" + : $"[{i:00}] = {PEDescribe(directory)}"); } + writer.Unindent(); writer.WriteLine(); } - public static void PrintSections(PEFile file, TextWriter writer) + private static void PrintSections(PEFile file, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(writer); - writer.WriteLine("Sections"); - foreach (var section in file.Sections) + for (var i = 0; i < file.Sections.Count; i++) { - writer.WriteLine($" {section.Name,8} {PEDescribe(section)}, Characteristics = 0x{(uint)section.Characteristics:X8} ({section.Characteristics})"); + var section = file.Sections[i]; + writer.Indent(); + writer.WriteLine($"[{i:00}] {section.Name,8} {PEDescribe(section)}, Characteristics = 0x{(uint)section.Characteristics:X8} ({section.Characteristics})"); writer.WriteLine(); foreach (var data in section.Content) { - PrintSectionData(file, data, writer); + writer.Indent(); + PrintSectionData(file, data, ref writer); + writer.Unindent(); } + + writer.Unindent(); writer.WriteLine(); } } - public static void PrintSection(PEFile file, PESection section, TextWriter writer) + private static void PrintSectionData(PEFile file, PESectionData data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(section); - ArgumentNullException.ThrowIfNull(writer); - - writer.WriteLine($" {section.Name,-8} {PEDescribe(section)}"); - foreach (var data in section.Content) - { - PrintSectionData(file, data, writer); - } - } - - public static void PrintSectionData(PEFile file, PESectionData data, TextWriter writer) - { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - - writer.WriteLine($" [{data.Index,3}] {PEDescribe(data)}"); + writer.WriteLine($"Section Data [{data.Index:00}] {PEDescribe(data)}"); + writer.Indent(); switch (data) { case PEBaseRelocationDirectory peBaseRelocationDirectory: - Print(file, peBaseRelocationDirectory, writer); + Print(file, peBaseRelocationDirectory, ref writer); break; case PEBoundImportDirectory peBoundImportDirectory: - Print(file, peBoundImportDirectory, writer); + Print(file, peBoundImportDirectory, ref writer); break; case PEClrMetadata peClrMetadata: - Print(file, peClrMetadata, writer); + Print(file, peClrMetadata, ref writer); break; case PEArchitectureDirectory peArchitectureDirectory: - Print(file, peArchitectureDirectory, writer); + Print(file, peArchitectureDirectory, ref writer); break; case PEDebugDirectory peDebugDirectory: - Print(file, peDebugDirectory, writer); + Print(file, peDebugDirectory, ref writer); break; case PEDelayImportDirectory peDelayImportDirectory: - Print(file, peDelayImportDirectory, writer); + Print(file, peDelayImportDirectory, ref writer); break; case PEExceptionDirectory peExceptionDirectory: - Print(file, peExceptionDirectory, writer); + Print(file, peExceptionDirectory, ref writer); break; case PEExportDirectory peExportDirectory: - Print(file, peExportDirectory, writer); + Print(file, peExportDirectory, ref writer); break; case PEGlobalPointerDirectory peGlobalPointerDirectory: - Print(file, peGlobalPointerDirectory, writer); + Print(file, peGlobalPointerDirectory, ref writer); break; case PEImportAddressTableDirectory peImportAddressTableDirectory: - Print(file, peImportAddressTableDirectory, writer); + Print(file, peImportAddressTableDirectory, ref writer); break; case PEImportDirectory peImportDirectory: - Print(file, peImportDirectory, writer); + Print(file, peImportDirectory, ref writer); break; case PELoadConfigDirectory32 peLoadConfigDirectory: - Print(file, peLoadConfigDirectory, writer); + Print(file, peLoadConfigDirectory, ref writer); break; case PELoadConfigDirectory64 peLoadConfigDirectory: - Print(file, peLoadConfigDirectory, writer); + Print(file, peLoadConfigDirectory, ref writer); break; case PEResourceDirectory peResourceDirectory: - Print(file, peResourceDirectory, writer); + Print(file, peResourceDirectory, ref writer); break; case PETlsDirectory32 peTlsDirectory32: - Print(file, peTlsDirectory32, writer); + Print(file, peTlsDirectory32, ref writer); break; case PETlsDirectory64 peTlsDirectory64: - Print(file, peTlsDirectory64, writer); + Print(file, peTlsDirectory64, ref writer); break; case PEDataDirectory peDataDirectory: - Print(file, peDataDirectory, writer); + Print(file, peDataDirectory, ref writer); break; case PEBoundImportAddressTable32 peBoundImportAddressTable32: - Print(file, peBoundImportAddressTable32, writer); + Print(file, peBoundImportAddressTable32, ref writer); break; case PEBoundImportAddressTable64 peBoundImportAddressTable64: - Print(file, peBoundImportAddressTable64, writer); + Print(file, peBoundImportAddressTable64, ref writer); break; case PEDelayImportAddressTable peDelayImportAddressTable: - Print(file, peDelayImportAddressTable, writer); + Print(file, peDelayImportAddressTable, ref writer); break; case PEExportAddressTable peExportAddressTable: - Print(file, peExportAddressTable, writer); + Print(file, peExportAddressTable, ref writer); break; case PEExportNameTable peExportNameTable: - Print(file, peExportNameTable, writer); + Print(file, peExportNameTable, ref writer); break; case PEExportOrdinalTable peExportOrdinalTable: - Print(file, peExportOrdinalTable, writer); + Print(file, peExportOrdinalTable, ref writer); break; case PEImportAddressTable peImportAddressTable: - Print(file, peImportAddressTable, writer); + Print(file, peImportAddressTable, ref writer); break; case PEImportLookupTable peImportLookupTable: - Print(file, peImportLookupTable, writer); + Print(file, peImportLookupTable, ref writer); break; case PEStreamSectionData peStreamSectionData: - Print(file, peStreamSectionData, writer); + Print(file, peStreamSectionData, ref writer); + break; + case PEDebugSectionDataRSDS peDebugSectionDataRSDS: + Print(file, peDebugSectionDataRSDS, ref writer); break; default: - throw new ArgumentOutOfRangeException(nameof(data)); + writer.WriteLine($"Unsupported section data {data}"); + break; } + writer.Unindent(); writer.WriteLine(); } - private static void Print(PEFile file, PEBaseRelocationDirectory data, TextWriter writer) + private static void Print(PEFile file, PEDebugSectionDataRSDS data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); + } + + private static void Print(PEFile file, PEBaseRelocationDirectory data, ref TextWriterIndenter writer) + { foreach (var block in data.Blocks) { var pageRVA = block.SectionLink.RVA(); - writer.WriteLine($" Block {pageRVA} Relocations[{block.Relocations.Count}]"); + writer.WriteLine($"Block {pageRVA} Relocations[{block.Relocations.Count}]"); + writer.Indent(); foreach (var reloc in block.Relocations) { var relocRVA = reloc.RVA(); @@ -286,339 +285,245 @@ private static void Print(PEFile file, PEBaseRelocationDirectory data, TextWrite if (reloc.Type == PEBaseRelocationType.Dir64) { - writer.WriteLine($" {reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA} (0x{reloc.ReadAddress(file):X16}), SectionData = {{ {PEDescribe(reloc.Container)} }}"); + writer.WriteLine($"{reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA} (0x{reloc.ReadAddress(file):X16}), SectionData = {{ {PEDescribe(reloc.Container)} }}"); } else { - writer.WriteLine($" {reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA}, SectionData = {{ {PEDescribe(reloc.Container)} }}"); + writer.WriteLine($"{reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA}, SectionData = {{ {PEDescribe(reloc.Container)} }}"); } } + writer.Unindent(); } } - private static void Print(PEFile file, PEBoundImportDirectory data, TextWriter writer) + private static void Print(PEFile file, PEBoundImportDirectory data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - foreach (var entry in data.Entries) { - writer.WriteLine($" ModuleName = {entry.ModuleName.Resolve()} ({entry.ModuleName}), ForwarderRefs[{entry.ForwarderRefs.Count}]"); + writer.WriteLine($"ModuleName = {entry.ModuleName.Resolve()} ({entry.ModuleName}), ForwarderRefs[{entry.ForwarderRefs.Count}]"); + writer.Indent(); foreach (var forwarderRef in entry.ForwarderRefs) { - writer.WriteLine($" ForwarderRef = {forwarderRef.ModuleName.Resolve()} ({forwarderRef.ModuleName})"); + writer.WriteLine($"ForwarderRef = {forwarderRef.ModuleName.Resolve()} ({forwarderRef.ModuleName})"); } + writer.Unindent(); } } - private static void Print(PEFile file, PEClrMetadata data, TextWriter writer) + private static void Print(PEFile file, PEClrMetadata data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEArchitectureDirectory data, TextWriter writer) + private static void Print(PEFile file, PEArchitectureDirectory data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEDebugDirectory data, TextWriter writer) + private static void Print(PEFile file, PEDebugDirectory data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEDelayImportDirectory data, TextWriter writer) + private static void Print(PEFile file, PEDelayImportDirectory data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - foreach (var dirEntry in data.Entries) { - writer.WriteLine($" DllName = {dirEntry.DllName.Resolve()}, RVA = {dirEntry.DllName.RVA()}"); - writer.WriteLine($" Attributes = {dirEntry.Attributes}"); - writer.WriteLine($" DelayImportAddressTable RVA = {dirEntry.DelayImportAddressTable.RVA}"); - writer.WriteLine($" DelayImportNameTable RVA = {dirEntry.DelayImportNameTable.RVA}"); - writer.WriteLine($" BoundImportAddressTable RVA = {(dirEntry.BoundImportAddressTable?.RVA ?? (RVA)0)}"); - writer.WriteLine($" UnloadDelayInformationTable RVA = {(dirEntry.UnloadDelayInformationTable?.RVA ?? (RVA)0)}"); + writer.WriteLine($"DllName = {dirEntry.DllName.Resolve()}, RVA = {dirEntry.DllName.RVA()}"); + writer.WriteLine($"Attributes = {dirEntry.Attributes}"); + writer.WriteLine($"DelayImportAddressTable RVA = {dirEntry.DelayImportAddressTable.RVA}"); + writer.WriteLine($"DelayImportNameTable RVA = {dirEntry.DelayImportNameTable.RVA}"); + writer.WriteLine($"BoundImportAddressTable RVA = {(dirEntry.BoundImportAddressTable?.RVA ?? (RVA)0)}"); + writer.WriteLine($"UnloadDelayInformationTable RVA = {(dirEntry.UnloadDelayInformationTable?.RVA ?? (RVA)0)}"); } } - private static void Print(PEFile file, PEExceptionDirectory data, TextWriter writer) + private static void Print(PEFile file, PEExceptionDirectory data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEExportDirectory data, TextWriter writer) + private static void Print(PEFile file, PEExportDirectory data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEGlobalPointerDirectory data, TextWriter writer) + private static void Print(PEFile file, PEGlobalPointerDirectory data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEImportAddressTableDirectory data, TextWriter writer) + private static void Print(PEFile file, PEImportAddressTableDirectory data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEImportDirectory data, TextWriter writer) + private static void Print(PEFile file, PEImportDirectory data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PELoadConfigDirectory32 data, TextWriter writer) + private static void Print(PEFile file, PELoadConfigDirectory32 data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - const int indent = -32; - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.Size),indent} = 0x{data.Data.Size:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.TimeDateStamp),indent} = 0x{data.Data.TimeDateStamp:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.MajorVersion),indent} = {data.Data.MajorVersion}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.MinorVersion),indent} = {data.Data.MinorVersion}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GlobalFlagsClear),indent} = 0x{data.Data.GlobalFlagsClear:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GlobalFlagsSet),indent} = 0x{data.Data.GlobalFlagsSet:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.CriticalSectionDefaultTimeout),indent} = 0x{data.Data.CriticalSectionDefaultTimeout:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.DeCommitFreeBlockThreshold),indent} = 0x{data.Data.DeCommitFreeBlockThreshold:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.DeCommitTotalFreeThreshold),indent} = 0x{data.Data.DeCommitTotalFreeThreshold:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.LockPrefixTable),indent} = 0x{data.Data.LockPrefixTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.MaximumAllocationSize),indent} = 0x{data.Data.MaximumAllocationSize:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.VirtualMemoryThreshold),indent} = 0x{data.Data.VirtualMemoryThreshold:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.ProcessAffinityMask),indent} = 0x{data.Data.ProcessAffinityMask:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.ProcessHeapFlags),indent} = 0x{data.Data.ProcessHeapFlags:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.CSDVersion),indent} = {data.Data.CSDVersion}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.DependentLoadFlags),indent} = 0x{data.Data.DependentLoadFlags:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.EditList),indent} = {data.Data.EditList}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.SecurityCookie),indent} = {data.Data.SecurityCookie}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.SEHandlerTable),indent} = {data.Data.SEHandlerTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.SEHandlerCount),indent} = 0x{data.Data.SEHandlerCount:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardCFCheckFunctionPointer),indent} = {data.Data.GuardCFCheckFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardCFDispatchFunctionPointer),indent} = {data.Data.GuardCFDispatchFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardCFFunctionTable),indent} = {data.Data.GuardCFFunctionTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardCFFunctionCount),indent} = 0x{data.Data.GuardCFFunctionCount:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardFlags),indent} = {data.Data.GuardFlags}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.TableSizeShift),indent} = 0x{data.Data.TableSizeShift:X}"); - writer.WriteLine($" {$"{nameof(PELoadConfigDirectoryData32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Flags)}",indent} = 0x{data.Data.CodeIntegrity.Flags:X}"); - writer.WriteLine($" {$"{nameof(PELoadConfigDirectoryData32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Catalog)}",indent} = 0x{data.Data.CodeIntegrity.Catalog:X}"); - writer.WriteLine($" {$"{nameof(PELoadConfigDirectoryData32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.CatalogOffset)}",indent} = 0x{data.Data.CodeIntegrity.CatalogOffset:X}"); - writer.WriteLine($" {$"{nameof(PELoadConfigDirectoryData32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Reserved)}",indent} = 0x{data.Data.CodeIntegrity.Reserved:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardAddressTakenIatEntryTable),indent} = {data.Data.GuardAddressTakenIatEntryTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardAddressTakenIatEntryCount),indent} = 0x{data.Data.GuardAddressTakenIatEntryCount:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardLongJumpTargetTable),indent} = {data.Data.GuardLongJumpTargetTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardLongJumpTargetCount),indent} = 0x{data.Data.GuardLongJumpTargetCount:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.DynamicValueRelocTable),indent} = {data.Data.DynamicValueRelocTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.CHPEMetadataPointer),indent} = {data.Data.CHPEMetadataPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardRFFailureRoutine),indent} = {data.Data.GuardRFFailureRoutine}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardRFFailureRoutineFunctionPointer),indent} = {data.Data.GuardRFFailureRoutineFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.DynamicValueRelocTableOffset),indent} = 0x{data.Data.DynamicValueRelocTableOffset:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.DynamicValueRelocTableSection),indent} = {data.Data.DynamicValueRelocTableSection}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.Reserved2),indent} = {data.Data.Reserved2}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardRFVerifyStackPointerFunctionPointer),indent} = {data.Data.GuardRFVerifyStackPointerFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.HotPatchTableOffset),indent} = 0x{data.Data.HotPatchTableOffset:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.Reserved3),indent} = 0x{data.Data.Reserved3:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.EnclaveConfigurationPointer),indent} = {data.Data.EnclaveConfigurationPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.VolatileMetadataPointer),indent} = {data.Data.VolatileMetadataPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardEHContinuationTable),indent} = {data.Data.GuardEHContinuationTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardEHContinuationCount),indent} = 0x{data.Data.GuardEHContinuationCount}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardXFGCheckFunctionPointer),indent} = {data.Data.GuardXFGCheckFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardXFGDispatchFunctionPointer),indent} = {data.Data.GuardXFGDispatchFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardXFGTableDispatchFunctionPointer),indent} = {data.Data.GuardXFGTableDispatchFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.CastGuardOsDeterminedFailureMode),indent} = {data.Data.CastGuardOsDeterminedFailureMode}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData32.GuardMemcpyFunctionPointer),indent} = {data.Data.GuardMemcpyFunctionPointer}"); - } - - private static void Print(PEFile file, PELoadConfigDirectory64 data, TextWriter writer) + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.Size),indent} = 0x{data.Data.Size:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.TimeDateStamp),indent} = 0x{data.Data.TimeDateStamp:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.MajorVersion),indent} = {data.Data.MajorVersion}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.MinorVersion),indent} = {data.Data.MinorVersion}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GlobalFlagsClear),indent} = 0x{data.Data.GlobalFlagsClear:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GlobalFlagsSet),indent} = 0x{data.Data.GlobalFlagsSet:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.CriticalSectionDefaultTimeout),indent} = 0x{data.Data.CriticalSectionDefaultTimeout:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.DeCommitFreeBlockThreshold),indent} = 0x{data.Data.DeCommitFreeBlockThreshold:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.DeCommitTotalFreeThreshold),indent} = 0x{data.Data.DeCommitTotalFreeThreshold:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.LockPrefixTable),indent} = 0x{data.Data.LockPrefixTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.MaximumAllocationSize),indent} = 0x{data.Data.MaximumAllocationSize:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.VirtualMemoryThreshold),indent} = 0x{data.Data.VirtualMemoryThreshold:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.ProcessAffinityMask),indent} = 0x{data.Data.ProcessAffinityMask:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.ProcessHeapFlags),indent} = 0x{data.Data.ProcessHeapFlags:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.CSDVersion),indent} = {data.Data.CSDVersion}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.DependentLoadFlags),indent} = 0x{data.Data.DependentLoadFlags:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.EditList),indent} = {data.Data.EditList}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.SecurityCookie),indent} = {data.Data.SecurityCookie}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.SEHandlerTable),indent} = {data.Data.SEHandlerTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.SEHandlerCount),indent} = 0x{data.Data.SEHandlerCount:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardCFCheckFunctionPointer),indent} = {data.Data.GuardCFCheckFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardCFDispatchFunctionPointer),indent} = {data.Data.GuardCFDispatchFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardCFFunctionTable),indent} = {data.Data.GuardCFFunctionTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardCFFunctionCount),indent} = 0x{data.Data.GuardCFFunctionCount:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardFlags),indent} = {data.Data.GuardFlags}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.TableSizeShift),indent} = 0x{data.Data.TableSizeShift:X}"); + writer.WriteLine($"{$"{nameof(PELoadConfigDirectoryData32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Flags)}",indent} = 0x{data.Data.CodeIntegrity.Flags:X}"); + writer.WriteLine($"{$"{nameof(PELoadConfigDirectoryData32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Catalog)}",indent} = 0x{data.Data.CodeIntegrity.Catalog:X}"); + writer.WriteLine($"{$"{nameof(PELoadConfigDirectoryData32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.CatalogOffset)}",indent} = 0x{data.Data.CodeIntegrity.CatalogOffset:X}"); + writer.WriteLine($"{$"{nameof(PELoadConfigDirectoryData32.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Reserved)}",indent} = 0x{data.Data.CodeIntegrity.Reserved:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardAddressTakenIatEntryTable),indent} = {data.Data.GuardAddressTakenIatEntryTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardAddressTakenIatEntryCount),indent} = 0x{data.Data.GuardAddressTakenIatEntryCount:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardLongJumpTargetTable),indent} = {data.Data.GuardLongJumpTargetTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardLongJumpTargetCount),indent} = 0x{data.Data.GuardLongJumpTargetCount:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.DynamicValueRelocTable),indent} = {data.Data.DynamicValueRelocTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.CHPEMetadataPointer),indent} = {data.Data.CHPEMetadataPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardRFFailureRoutine),indent} = {data.Data.GuardRFFailureRoutine}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardRFFailureRoutineFunctionPointer),indent} = {data.Data.GuardRFFailureRoutineFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.DynamicValueRelocTableOffset),indent} = 0x{data.Data.DynamicValueRelocTableOffset:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.DynamicValueRelocTableSection),indent} = {data.Data.DynamicValueRelocTableSection}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.Reserved2),indent} = {data.Data.Reserved2}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardRFVerifyStackPointerFunctionPointer),indent} = {data.Data.GuardRFVerifyStackPointerFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.HotPatchTableOffset),indent} = 0x{data.Data.HotPatchTableOffset:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.Reserved3),indent} = 0x{data.Data.Reserved3:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.EnclaveConfigurationPointer),indent} = {data.Data.EnclaveConfigurationPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.VolatileMetadataPointer),indent} = {data.Data.VolatileMetadataPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardEHContinuationTable),indent} = {data.Data.GuardEHContinuationTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardEHContinuationCount),indent} = 0x{data.Data.GuardEHContinuationCount}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardXFGCheckFunctionPointer),indent} = {data.Data.GuardXFGCheckFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardXFGDispatchFunctionPointer),indent} = {data.Data.GuardXFGDispatchFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardXFGTableDispatchFunctionPointer),indent} = {data.Data.GuardXFGTableDispatchFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.CastGuardOsDeterminedFailureMode),indent} = {data.Data.CastGuardOsDeterminedFailureMode}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardMemcpyFunctionPointer),indent} = {data.Data.GuardMemcpyFunctionPointer}"); + } + + private static void Print(PEFile file, PELoadConfigDirectory64 data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - const int indent = -32; - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.Size),indent} = 0x{data.Data.Size:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.TimeDateStamp),indent} = 0x{data.Data.TimeDateStamp:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.MajorVersion),indent} = {data.Data.MajorVersion}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.MinorVersion),indent} = {data.Data.MinorVersion}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GlobalFlagsClear),indent} = 0x{data.Data.GlobalFlagsClear:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GlobalFlagsSet),indent} = 0x{data.Data.GlobalFlagsSet:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.CriticalSectionDefaultTimeout),indent} = 0x{data.Data.CriticalSectionDefaultTimeout:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.DeCommitFreeBlockThreshold),indent} = 0x{data.Data.DeCommitFreeBlockThreshold:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.DeCommitTotalFreeThreshold),indent} = 0x{data.Data.DeCommitTotalFreeThreshold:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.LockPrefixTable),indent} = 0x{data.Data.LockPrefixTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.MaximumAllocationSize),indent} = 0x{data.Data.MaximumAllocationSize:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.VirtualMemoryThreshold),indent} = 0x{data.Data.VirtualMemoryThreshold:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.ProcessAffinityMask),indent} = 0x{data.Data.ProcessAffinityMask:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.ProcessHeapFlags),indent} = 0x{data.Data.ProcessHeapFlags:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.CSDVersion),indent} = {data.Data.CSDVersion}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.DependentLoadFlags),indent} = 0x{data.Data.DependentLoadFlags:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.EditList),indent} = {data.Data.EditList}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.SecurityCookie),indent} = {data.Data.SecurityCookie}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.SEHandlerTable),indent} = {data.Data.SEHandlerTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.SEHandlerCount),indent} = 0x{data.Data.SEHandlerCount:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardCFCheckFunctionPointer),indent} = {data.Data.GuardCFCheckFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardCFDispatchFunctionPointer),indent} = {data.Data.GuardCFDispatchFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardCFFunctionTable),indent} = {data.Data.GuardCFFunctionTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardCFFunctionCount),indent} = 0x{data.Data.GuardCFFunctionCount:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardFlags),indent} = {data.Data.GuardFlags}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.TableSizeShift),indent} = 0x{data.Data.TableSizeShift:X}"); - writer.WriteLine($" {$"{nameof(PELoadConfigDirectoryData64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Flags)}",indent} = 0x{data.Data.CodeIntegrity.Flags:X}"); - writer.WriteLine($" {$"{nameof(PELoadConfigDirectoryData64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Catalog)}",indent} = 0x{data.Data.CodeIntegrity.Catalog:X}"); - writer.WriteLine($" {$"{nameof(PELoadConfigDirectoryData64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.CatalogOffset)}",indent} = 0x{data.Data.CodeIntegrity.CatalogOffset:X}"); - writer.WriteLine($" {$"{nameof(PELoadConfigDirectoryData64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Reserved)}",indent} = 0x{data.Data.CodeIntegrity.Reserved:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardAddressTakenIatEntryTable),indent} = {data.Data.GuardAddressTakenIatEntryTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardAddressTakenIatEntryCount),indent} = 0x{data.Data.GuardAddressTakenIatEntryCount:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardLongJumpTargetTable),indent} = {data.Data.GuardLongJumpTargetTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardLongJumpTargetCount),indent} = 0x{data.Data.GuardLongJumpTargetCount:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.DynamicValueRelocTable),indent} = {data.Data.DynamicValueRelocTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.CHPEMetadataPointer),indent} = {data.Data.CHPEMetadataPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardRFFailureRoutine),indent} = {data.Data.GuardRFFailureRoutine}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardRFFailureRoutineFunctionPointer),indent} = {data.Data.GuardRFFailureRoutineFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.DynamicValueRelocTableOffset),indent} = 0x{data.Data.DynamicValueRelocTableOffset:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.DynamicValueRelocTableSection),indent} = {data.Data.DynamicValueRelocTableSection}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.Reserved2),indent} = {data.Data.Reserved2}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardRFVerifyStackPointerFunctionPointer),indent} = {data.Data.GuardRFVerifyStackPointerFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.HotPatchTableOffset),indent} = 0x{data.Data.HotPatchTableOffset:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.Reserved3),indent} = 0x{data.Data.Reserved3:X}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.EnclaveConfigurationPointer),indent} = {data.Data.EnclaveConfigurationPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.VolatileMetadataPointer),indent} = {data.Data.VolatileMetadataPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardEHContinuationTable),indent} = {data.Data.GuardEHContinuationTable}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardEHContinuationCount),indent} = 0x{data.Data.GuardEHContinuationCount}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardXFGCheckFunctionPointer),indent} = {data.Data.GuardXFGCheckFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardXFGDispatchFunctionPointer),indent} = {data.Data.GuardXFGDispatchFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardXFGTableDispatchFunctionPointer),indent} = {data.Data.GuardXFGTableDispatchFunctionPointer}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.CastGuardOsDeterminedFailureMode),indent} = {data.Data.CastGuardOsDeterminedFailureMode}"); - writer.WriteLine($" {nameof(PELoadConfigDirectoryData64.GuardMemcpyFunctionPointer),indent} = {data.Data.GuardMemcpyFunctionPointer}"); - } - - private static void Print(PEFile file, PEResourceDirectory data, TextWriter writer) + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.Size),indent} = 0x{data.Data.Size:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.TimeDateStamp),indent} = 0x{data.Data.TimeDateStamp:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.MajorVersion),indent} = {data.Data.MajorVersion}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.MinorVersion),indent} = {data.Data.MinorVersion}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GlobalFlagsClear),indent} = 0x{data.Data.GlobalFlagsClear:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GlobalFlagsSet),indent} = 0x{data.Data.GlobalFlagsSet:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.CriticalSectionDefaultTimeout),indent} = 0x{data.Data.CriticalSectionDefaultTimeout:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.DeCommitFreeBlockThreshold),indent} = 0x{data.Data.DeCommitFreeBlockThreshold:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.DeCommitTotalFreeThreshold),indent} = 0x{data.Data.DeCommitTotalFreeThreshold:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.LockPrefixTable),indent} = 0x{data.Data.LockPrefixTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.MaximumAllocationSize),indent} = 0x{data.Data.MaximumAllocationSize:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.VirtualMemoryThreshold),indent} = 0x{data.Data.VirtualMemoryThreshold:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.ProcessAffinityMask),indent} = 0x{data.Data.ProcessAffinityMask:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.ProcessHeapFlags),indent} = 0x{data.Data.ProcessHeapFlags:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.CSDVersion),indent} = {data.Data.CSDVersion}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.DependentLoadFlags),indent} = 0x{data.Data.DependentLoadFlags:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.EditList),indent} = {data.Data.EditList}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.SecurityCookie),indent} = {data.Data.SecurityCookie}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.SEHandlerTable),indent} = {data.Data.SEHandlerTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.SEHandlerCount),indent} = 0x{data.Data.SEHandlerCount:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardCFCheckFunctionPointer),indent} = {data.Data.GuardCFCheckFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardCFDispatchFunctionPointer),indent} = {data.Data.GuardCFDispatchFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardCFFunctionTable),indent} = {data.Data.GuardCFFunctionTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardCFFunctionCount),indent} = 0x{data.Data.GuardCFFunctionCount:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardFlags),indent} = {data.Data.GuardFlags}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.TableSizeShift),indent} = 0x{data.Data.TableSizeShift:X}"); + writer.WriteLine($"{$"{nameof(PELoadConfigDirectoryData64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Flags)}",indent} = 0x{data.Data.CodeIntegrity.Flags:X}"); + writer.WriteLine($"{$"{nameof(PELoadConfigDirectoryData64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Catalog)}",indent} = 0x{data.Data.CodeIntegrity.Catalog:X}"); + writer.WriteLine($"{$"{nameof(PELoadConfigDirectoryData64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.CatalogOffset)}",indent} = 0x{data.Data.CodeIntegrity.CatalogOffset:X}"); + writer.WriteLine($"{$"{nameof(PELoadConfigDirectoryData64.CodeIntegrity)}.{nameof(PELoadConfigCodeIntegrity.Reserved)}",indent} = 0x{data.Data.CodeIntegrity.Reserved:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardAddressTakenIatEntryTable),indent} = {data.Data.GuardAddressTakenIatEntryTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardAddressTakenIatEntryCount),indent} = 0x{data.Data.GuardAddressTakenIatEntryCount:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardLongJumpTargetTable),indent} = {data.Data.GuardLongJumpTargetTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardLongJumpTargetCount),indent} = 0x{data.Data.GuardLongJumpTargetCount:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.DynamicValueRelocTable),indent} = {data.Data.DynamicValueRelocTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.CHPEMetadataPointer),indent} = {data.Data.CHPEMetadataPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardRFFailureRoutine),indent} = {data.Data.GuardRFFailureRoutine}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardRFFailureRoutineFunctionPointer),indent} = {data.Data.GuardRFFailureRoutineFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.DynamicValueRelocTableOffset),indent} = 0x{data.Data.DynamicValueRelocTableOffset:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.DynamicValueRelocTableSection),indent} = {data.Data.DynamicValueRelocTableSection}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.Reserved2),indent} = {data.Data.Reserved2}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardRFVerifyStackPointerFunctionPointer),indent} = {data.Data.GuardRFVerifyStackPointerFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.HotPatchTableOffset),indent} = 0x{data.Data.HotPatchTableOffset:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.Reserved3),indent} = 0x{data.Data.Reserved3:X}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.EnclaveConfigurationPointer),indent} = {data.Data.EnclaveConfigurationPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.VolatileMetadataPointer),indent} = {data.Data.VolatileMetadataPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardEHContinuationTable),indent} = {data.Data.GuardEHContinuationTable}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardEHContinuationCount),indent} = 0x{data.Data.GuardEHContinuationCount}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardXFGCheckFunctionPointer),indent} = {data.Data.GuardXFGCheckFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardXFGDispatchFunctionPointer),indent} = {data.Data.GuardXFGDispatchFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardXFGTableDispatchFunctionPointer),indent} = {data.Data.GuardXFGTableDispatchFunctionPointer}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.CastGuardOsDeterminedFailureMode),indent} = {data.Data.CastGuardOsDeterminedFailureMode}"); + writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardMemcpyFunctionPointer),indent} = {data.Data.GuardMemcpyFunctionPointer}"); + } + + private static void Print(PEFile file, PEResourceDirectory data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); } - private static void Print(PEFile file, PETlsDirectory32 data, TextWriter writer) + private static void Print(PEFile file, PETlsDirectory32 data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); } - private static void Print(PEFile file, PETlsDirectory64 data, TextWriter writer) + private static void Print(PEFile file, PETlsDirectory64 data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); } - private static void Print(PEFile file, PEDataDirectory data, TextWriter writer) + private static void Print(PEFile file, PEDataDirectory data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEBoundImportAddressTable32 data, TextWriter writer) + private static void Print(PEFile file, PEBoundImportAddressTable32 data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEBoundImportAddressTable64 data, TextWriter writer) + private static void Print(PEFile file, PEBoundImportAddressTable64 data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEDelayImportAddressTable data, TextWriter writer) + private static void Print(PEFile file, PEDelayImportAddressTable data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEExportAddressTable data, TextWriter writer) + private static void Print(PEFile file, PEExportAddressTable data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEExportNameTable data, TextWriter writer) + private static void Print(PEFile file, PEExportNameTable data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEExportOrdinalTable data, TextWriter writer) + private static void Print(PEFile file, PEExportOrdinalTable data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEImportAddressTable data, TextWriter writer) + private static void Print(PEFile file, PEImportAddressTable data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEImportLookupTable data, TextWriter writer) + private static void Print(PEFile file, PEImportLookupTable data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } - private static void Print(PEFile file, PEStreamSectionData data, TextWriter writer) + private static void Print(PEFile file, PEStreamSectionData data, ref TextWriterIndenter writer) { - ArgumentNullException.ThrowIfNull(file); - ArgumentNullException.ThrowIfNull(data); - ArgumentNullException.ThrowIfNull(writer); - } private static string PEDescribe(PEObjectBase? peObjectBase) From 7191b00d69d6c399571c12fa05b32e5671042ee5 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 25 Sep 2024 20:44:34 +0200 Subject: [PATCH 53/87] Improve PEPrinter --- .../PE/DataDirectory/PEDebugDirectory.cs | 1 + src/LibObjectFile/PE/PEPrinter.cs | 306 ++++++++++++++---- 2 files changed, 237 insertions(+), 70 deletions(-) diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs index 1a9b23b..0b75f2a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs @@ -53,6 +53,7 @@ public override unsafe void Read(PEImageReader reader) Characteristics = rawEntry.Characteristics, MajorVersion = rawEntry.MajorVersion, MinorVersion = rawEntry.MinorVersion, + TimeDateStamp = rawEntry.TimeDateStamp, Type = rawEntry.Type, }; diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index 78dcb4e..a0c2f9e 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -177,101 +177,116 @@ private static void PrintSectionData(PEFile file, PESectionData data, ref TextWr switch (data) { case PEBaseRelocationDirectory peBaseRelocationDirectory: - Print(file, peBaseRelocationDirectory, ref writer); + Print(peBaseRelocationDirectory, ref writer); break; case PEBoundImportDirectory peBoundImportDirectory: - Print(file, peBoundImportDirectory, ref writer); + Print(peBoundImportDirectory, ref writer); break; case PEClrMetadata peClrMetadata: - Print(file, peClrMetadata, ref writer); + Print(peClrMetadata, ref writer); break; case PEArchitectureDirectory peArchitectureDirectory: - Print(file, peArchitectureDirectory, ref writer); + Print(peArchitectureDirectory, ref writer); break; case PEDebugDirectory peDebugDirectory: - Print(file, peDebugDirectory, ref writer); + Print(peDebugDirectory, ref writer); break; case PEDelayImportDirectory peDelayImportDirectory: - Print(file, peDelayImportDirectory, ref writer); + Print(peDelayImportDirectory, ref writer); break; case PEExceptionDirectory peExceptionDirectory: - Print(file, peExceptionDirectory, ref writer); + Print(peExceptionDirectory, ref writer); break; case PEExportDirectory peExportDirectory: - Print(file, peExportDirectory, ref writer); + Print(peExportDirectory, ref writer); break; case PEGlobalPointerDirectory peGlobalPointerDirectory: - Print(file, peGlobalPointerDirectory, ref writer); + Print(peGlobalPointerDirectory, ref writer); break; case PEImportAddressTableDirectory peImportAddressTableDirectory: - Print(file, peImportAddressTableDirectory, ref writer); + Print(peImportAddressTableDirectory, ref writer); break; case PEImportDirectory peImportDirectory: - Print(file, peImportDirectory, ref writer); + Print(peImportDirectory, ref writer); break; case PELoadConfigDirectory32 peLoadConfigDirectory: - Print(file, peLoadConfigDirectory, ref writer); + Print(peLoadConfigDirectory, ref writer); break; case PELoadConfigDirectory64 peLoadConfigDirectory: - Print(file, peLoadConfigDirectory, ref writer); + Print(peLoadConfigDirectory, ref writer); break; case PEResourceDirectory peResourceDirectory: - Print(file, peResourceDirectory, ref writer); + Print(peResourceDirectory, ref writer); break; case PETlsDirectory32 peTlsDirectory32: - Print(file, peTlsDirectory32, ref writer); + Print(peTlsDirectory32, ref writer); break; case PETlsDirectory64 peTlsDirectory64: - Print(file, peTlsDirectory64, ref writer); - break; - case PEDataDirectory peDataDirectory: - Print(file, peDataDirectory, ref writer); + Print(peTlsDirectory64, ref writer); break; case PEBoundImportAddressTable32 peBoundImportAddressTable32: - Print(file, peBoundImportAddressTable32, ref writer); + Print(peBoundImportAddressTable32, ref writer); break; case PEBoundImportAddressTable64 peBoundImportAddressTable64: - Print(file, peBoundImportAddressTable64, ref writer); + Print(peBoundImportAddressTable64, ref writer); break; case PEDelayImportAddressTable peDelayImportAddressTable: - Print(file, peDelayImportAddressTable, ref writer); + Print(peDelayImportAddressTable, ref writer); break; case PEExportAddressTable peExportAddressTable: - Print(file, peExportAddressTable, ref writer); + Print(peExportAddressTable, ref writer); break; case PEExportNameTable peExportNameTable: - Print(file, peExportNameTable, ref writer); + Print(peExportNameTable, ref writer); break; case PEExportOrdinalTable peExportOrdinalTable: - Print(file, peExportOrdinalTable, ref writer); + Print(peExportOrdinalTable, ref writer); break; case PEImportAddressTable peImportAddressTable: - Print(file, peImportAddressTable, ref writer); + Print(peImportAddressTable, ref writer); break; case PEImportLookupTable peImportLookupTable: - Print(file, peImportLookupTable, ref writer); + Print(peImportLookupTable, ref writer); break; case PEStreamSectionData peStreamSectionData: - Print(file, peStreamSectionData, ref writer); + Print(peStreamSectionData, ref writer); break; case PEDebugSectionDataRSDS peDebugSectionDataRSDS: - Print(file, peDebugSectionDataRSDS, ref writer); + Print(peDebugSectionDataRSDS, ref writer); break; default: writer.WriteLine($"Unsupported section data {data}"); break; } + + if (data is PEDataDirectory directory) + { + foreach (var content in directory.Content) + { + writer.Indent(); + PrintSectionData(file, content, ref writer); + writer.Unindent(); + } + } + writer.Unindent(); writer.WriteLine(); } - private static void Print(PEFile file, PEDebugSectionDataRSDS data, ref TextWriterIndenter writer) + private static void Print(PEDebugSectionDataRSDS data, ref TextWriterIndenter writer) { - + const int indent = -26; + writer.WriteLine("Debug Section Data (RSDS)"); + writer.Indent(); + writer.WriteLine($"{nameof(PEDebugSectionDataRSDS.Guid),indent} = {data.Guid}"); + writer.WriteLine($"{nameof(PEDebugSectionDataRSDS.Age),indent} = {data.Age}"); + writer.WriteLine($"{nameof(PEDebugSectionDataRSDS.PdbPath),indent} = {data.PdbPath}"); + writer.Unindent(); } - private static void Print(PEFile file, PEBaseRelocationDirectory data, ref TextWriterIndenter writer) + private static void Print(PEBaseRelocationDirectory data, ref TextWriterIndenter writer) { + var peFile = data.GetPEFile()!; foreach (var block in data.Blocks) { var pageRVA = block.SectionLink.RVA(); @@ -285,18 +300,18 @@ private static void Print(PEFile file, PEBaseRelocationDirectory data, ref TextW if (reloc.Type == PEBaseRelocationType.Dir64) { - writer.WriteLine($"{reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA} (0x{reloc.ReadAddress(file):X16}), SectionData = {{ {PEDescribe(reloc.Container)} }}"); + writer.WriteLine($"{reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA} (0x{reloc.ReadAddress(peFile):X16}), SectionData = {{ {PELink(reloc.Container)} }}"); } else { - writer.WriteLine($"{reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA}, SectionData = {{ {PEDescribe(reloc.Container)} }}"); + writer.WriteLine($"{reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA}, SectionData = {{ {PELink(reloc.Container)} }}"); } } writer.Unindent(); } } - private static void Print(PEFile file, PEBoundImportDirectory data, ref TextWriterIndenter writer) + private static void Print(PEBoundImportDirectory data, ref TextWriterIndenter writer) { foreach (var entry in data.Entries) { @@ -311,52 +326,95 @@ private static void Print(PEFile file, PEBoundImportDirectory data, ref TextWrit } } - private static void Print(PEFile file, PEClrMetadata data, ref TextWriterIndenter writer) + private static void Print(PEClrMetadata data, ref TextWriterIndenter writer) { } - private static void Print(PEFile file, PEArchitectureDirectory data, ref TextWriterIndenter writer) + private static void Print(PEArchitectureDirectory data, ref TextWriterIndenter writer) { } - private static void Print(PEFile file, PEDebugDirectory data, ref TextWriterIndenter writer) + private static void Print(PEDebugDirectory data, ref TextWriterIndenter writer) { + for (var i = 0; i < data.Entries.Count; i++) + { + var entry = data.Entries[i]; + writer.WriteLine( + $"[{i}] Type = {entry.Type}, Characteristics = 0x{entry.Characteristics:X}, Version = {entry.MajorVersion}.{entry.MinorVersion}, TimeStamp = 0x{entry.TimeDateStamp:X}, Data = {PELink((PEObjectBase?)entry.SectionData ?? entry.ExtraData)}"); + } } - private static void Print(PEFile file, PEDelayImportDirectory data, ref TextWriterIndenter writer) + private static void Print(PEDelayImportDirectory data, ref TextWriterIndenter writer) { - foreach (var dirEntry in data.Entries) + for (var i = 0; i < data.Entries.Count; i++) { - writer.WriteLine($"DllName = {dirEntry.DllName.Resolve()}, RVA = {dirEntry.DllName.RVA()}"); - writer.WriteLine($"Attributes = {dirEntry.Attributes}"); - writer.WriteLine($"DelayImportAddressTable RVA = {dirEntry.DelayImportAddressTable.RVA}"); - writer.WriteLine($"DelayImportNameTable RVA = {dirEntry.DelayImportNameTable.RVA}"); - writer.WriteLine($"BoundImportAddressTable RVA = {(dirEntry.BoundImportAddressTable?.RVA ?? (RVA)0)}"); - writer.WriteLine($"UnloadDelayInformationTable RVA = {(dirEntry.UnloadDelayInformationTable?.RVA ?? (RVA)0)}"); + var dirEntry = data.Entries[i]; + writer.WriteLine($"[{i}] DllName = {dirEntry.DllName.Resolve()}, RVA = {dirEntry.DllName.RVA()}"); + writer.WriteLine($"[{i}] Attributes = {dirEntry.Attributes}"); + writer.WriteLine($"[{i}] DelayImportAddressTable {PELink(dirEntry.DelayImportAddressTable)}"); + writer.WriteLine($"[{i}] DelayImportNameTable {PELink(dirEntry.DelayImportNameTable)}"); + writer.WriteLine($"[{i}] BoundImportAddressTable {PELink(dirEntry.BoundImportAddressTable)}"); + writer.WriteLine($"[{i}] UnloadDelayInformationTable {PELink(dirEntry.UnloadDelayInformationTable)}"); + writer.WriteLine(); } } - private static void Print(PEFile file, PEExceptionDirectory data, ref TextWriterIndenter writer) + private static void Print(PEExceptionDirectory data, ref TextWriterIndenter writer) { + for (var i = 0; i < data.Entries.Count; i++) + { + var entry = data.Entries[i]; + switch (entry) + { + case PEExceptionFunctionEntryArm entryArm: + writer.WriteLine($"[{i}] Begin = {entry.BeginAddress.RVA()}"); + writer.WriteLine($"[{i}] UnwindData = 0x{entryArm.UnwindData:X}"); + break; + case PEExceptionFunctionEntryX86 entryX86: + writer.WriteLine($"[{i}] Begin = {entry.BeginAddress}"); + writer.WriteLine($"[{i}] End = {entryX86.EndAddress}"); + writer.WriteLine($"[{i}] UnwindInfoAddress = {entryX86.UnwindInfoAddress}"); + break; + default: + throw new ArgumentOutOfRangeException(nameof(entry)); + } + writer.WriteLine(); + } } - private static void Print(PEFile file, PEExportDirectory data, ref TextWriterIndenter writer) + private static void Print(PEExportDirectory data, ref TextWriterIndenter writer) { + writer.WriteLine($"{nameof(PEExportDirectory.TimeStamp)} = {data.TimeStamp}"); + writer.WriteLine($"{nameof(PEExportDirectory.MajorVersion)} = {data.MajorVersion}"); + writer.WriteLine($"{nameof(PEExportDirectory.MinorVersion)} = {data.MinorVersion}"); + writer.WriteLine($"{nameof(PEExportDirectory.OrdinalBase)} = 0x{data.OrdinalBase:X}"); + writer.WriteLine($"{nameof(PEExportDirectory.NameLink)} = {data.NameLink.Resolve()} ({data.NameLink})"); + writer.WriteLine($"{nameof(PEExportDirectory.ExportFunctionAddressTable)} = {PELink(data.ExportFunctionAddressTable)}"); + writer.WriteLine($"{nameof(PEExportDirectory.ExportNameTable)} = {PELink(data.ExportNameTable)}"); + writer.WriteLine($"{nameof(PEExportDirectory.ExportOrdinalTable)} = {PELink(data.ExportOrdinalTable)}"); } - private static void Print(PEFile file, PEGlobalPointerDirectory data, ref TextWriterIndenter writer) + private static void Print(PEGlobalPointerDirectory data, ref TextWriterIndenter writer) { } - private static void Print(PEFile file, PEImportAddressTableDirectory data, ref TextWriterIndenter writer) + private static void Print(PEImportAddressTableDirectory data, ref TextWriterIndenter writer) { } - private static void Print(PEFile file, PEImportDirectory data, ref TextWriterIndenter writer) + private static void Print(PEImportDirectory data, ref TextWriterIndenter writer) { + for (var i = 0; i < data.Entries.Count; i++) + { + var entry = data.Entries[i]; + writer.WriteLine($"[{i}] ImportDllNameLink = {entry.ImportDllNameLink.Resolve()} ({entry.ImportDllNameLink})"); + writer.WriteLine($"[{i}] ImportAddressTable = {PELink(entry.ImportAddressTable)}"); + writer.WriteLine($"[{i}] ImportLookupTable = {PELink(entry.ImportLookupTable)}"); + writer.WriteLine(); + } } - private static void Print(PEFile file, PELoadConfigDirectory32 data, ref TextWriterIndenter writer) + private static void Print(PELoadConfigDirectory32 data, ref TextWriterIndenter writer) { const int indent = -32; writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.Size),indent} = 0x{data.Data.Size:X}"); @@ -414,7 +472,7 @@ private static void Print(PEFile file, PELoadConfigDirectory32 data, ref TextWri writer.WriteLine($"{nameof(PELoadConfigDirectoryData32.GuardMemcpyFunctionPointer),indent} = {data.Data.GuardMemcpyFunctionPointer}"); } - private static void Print(PEFile file, PELoadConfigDirectory64 data, ref TextWriterIndenter writer) + private static void Print(PELoadConfigDirectory64 data, ref TextWriterIndenter writer) { const int indent = -32; @@ -473,56 +531,138 @@ private static void Print(PEFile file, PELoadConfigDirectory64 data, ref TextWri writer.WriteLine($"{nameof(PELoadConfigDirectoryData64.GuardMemcpyFunctionPointer),indent} = {data.Data.GuardMemcpyFunctionPointer}"); } - private static void Print(PEFile file, PEResourceDirectory data, ref TextWriterIndenter writer) + private static void Print(PEResourceDirectory data, ref TextWriterIndenter writer) { - + Print(data.Root, ref writer); } - private static void Print(PEFile file, PETlsDirectory32 data, ref TextWriterIndenter writer) - { - } - - private static void Print(PEFile file, PETlsDirectory64 data, ref TextWriterIndenter writer) + private static void Print(PEResourceEntry data, ref TextWriterIndenter writer) { + switch (data) + { + case PEResourceDataEntry resourceFile: + writer.WriteLine($"> {resourceFile}"); + break; + case PEResourceDirectoryEntry dir: + writer.WriteLine($"> {dir}"); + writer.Indent(); + foreach (var entry in dir.Entries) + { + writer.Indent(); + Print(entry, ref writer); + writer.Unindent(); + } + break; + default: + throw new ArgumentOutOfRangeException(nameof(data)); + } } - private static void Print(PEFile file, PEDataDirectory data, ref TextWriterIndenter writer) + private static void Print(PETlsDirectory32 data, ref TextWriterIndenter writer) { + writer.WriteLine($"{nameof(PETlsDirectory32.StartAddressOfRawData)} = {data.StartAddressOfRawData}"); + writer.WriteLine($"{nameof(PETlsDirectory32.EndAddressOfRawData)} = {data.EndAddressOfRawData}"); + writer.WriteLine($"{nameof(PETlsDirectory32.AddressOfIndex)} = {data.AddressOfIndex}"); + writer.WriteLine($"{nameof(PETlsDirectory32.AddressOfCallBacks)} = {data.AddressOfCallBacks}"); + writer.WriteLine($"{nameof(PETlsDirectory32.SizeOfZeroFill)} = 0x{data.SizeOfZeroFill:X}"); + writer.WriteLine($"{nameof(PETlsDirectory32.Characteristics)} = {data.Characteristics}"); } - private static void Print(PEFile file, PEBoundImportAddressTable32 data, ref TextWriterIndenter writer) + private static void Print(PETlsDirectory64 data, ref TextWriterIndenter writer) { + writer.WriteLine($"{nameof(PETlsDirectory64.StartAddressOfRawData)} = {data.StartAddressOfRawData}"); + writer.WriteLine($"{nameof(PETlsDirectory64.EndAddressOfRawData)} = {data.EndAddressOfRawData}"); + writer.WriteLine($"{nameof(PETlsDirectory64.AddressOfIndex)} = {data.AddressOfIndex}"); + writer.WriteLine($"{nameof(PETlsDirectory64.AddressOfCallBacks)} = {data.AddressOfCallBacks}"); + writer.WriteLine($"{nameof(PETlsDirectory64.SizeOfZeroFill)} = 0x{data.SizeOfZeroFill:X}"); + writer.WriteLine($"{nameof(PETlsDirectory64.Characteristics)} = {data.Characteristics}"); } - private static void Print(PEFile file, PEBoundImportAddressTable64 data, ref TextWriterIndenter writer) + private static void Print(PEBoundImportAddressTable32 data, ref TextWriterIndenter writer) { + for (var i = 0; i < data.Entries.Count; i++) + { + var entry = data.Entries[i]; + writer.WriteLine($"[{i}] VA = {entry}"); + } } - private static void Print(PEFile file, PEDelayImportAddressTable data, ref TextWriterIndenter writer) + private static void Print(PEBoundImportAddressTable64 data, ref TextWriterIndenter writer) { + for (var i = 0; i < data.Entries.Count; i++) + { + var entry = data.Entries[i]; + writer.WriteLine($"[{i}] VA = {entry}"); + } } - private static void Print(PEFile file, PEExportAddressTable data, ref TextWriterIndenter writer) + private static void Print(PEExportAddressTable data, ref TextWriterIndenter writer) { + for (var i = 0; i < data.Values.Count; i++) + { + var entry = data.Values[i]; + if (entry.IsForwarderRVA) + { + writer.WriteLine($"[{i}] Forwarder RVA = {entry.ForwarderRVA}"); + } + else + { + writer.WriteLine($"[{i}] Exported RVA = {entry.ExportRVA}"); + } + } } - private static void Print(PEFile file, PEExportNameTable data, ref TextWriterIndenter writer) + private static void Print(PEExportNameTable data, ref TextWriterIndenter writer) { + for (var i = 0; i < data.Values.Count; i++) + { + var entry = data.Values[i]; + writer.WriteLine($"[{i}] {entry.Resolve()} ({entry})"); + } } - private static void Print(PEFile file, PEExportOrdinalTable data, ref TextWriterIndenter writer) + private static void Print(PEExportOrdinalTable data, ref TextWriterIndenter writer) { + for (var i = 0; i < data.Values.Count; i++) + { + var entry = data.Values[i]; + writer.WriteLine($"[{i}] Ordinal = {entry}"); + } } - private static void Print(PEFile file, PEImportAddressTable data, ref TextWriterIndenter writer) + private static void Print(PEImportAddressTable data, ref TextWriterIndenter writer) { + for (var i = 0; i < data.Entries.Count; i++) + { + var entry = data.Entries[i]; + if (entry.IsImportByOrdinal) + { + writer.WriteLine($"[{i}] Ordinal = {entry.Ordinal}"); + } + else + { + writer.WriteLine($"[{i}] {entry.HintName.Resolve()} ({entry.HintName})"); + } + } } - private static void Print(PEFile file, PEImportLookupTable data, ref TextWriterIndenter writer) + private static void Print(PEImportLookupTable data, ref TextWriterIndenter writer) { + for (var i = 0; i < data.Entries.Count; i++) + { + var entry = data.Entries[i]; + if (entry.IsImportByOrdinal) + { + writer.WriteLine($"[{i}] Ordinal = {entry.Ordinal}"); + } + else + { + writer.WriteLine($"[{i}] {entry.HintName.Resolve()} ({entry.HintName})"); + } + } } - private static void Print(PEFile file, PEStreamSectionData data, ref TextWriterIndenter writer) + private static void Print(PEStreamSectionData data, ref TextWriterIndenter writer) { } @@ -542,4 +682,30 @@ private static string PEDescribe(PEObjectBase? peObjectBase) return "null"; } } + + private static string PELink(PEObjectBase? peObjectBase) + { + if (peObjectBase is PEObject peObject) + { + return $"RVA = 0x{peObject.RVA.Value:X8} ({peObject.GetType().Name}[{peObject.Index}]{PEParent((PEObjectBase?)peObject.Parent)}"; + } + else if (peObjectBase is not null) + { + return $"({peObjectBase.GetType().Name}[{peObjectBase.Index}]{PEParent((PEObjectBase?)peObjectBase.Parent)}"; + } + else + { + return "null"; + } + + static string PEParent(PEObjectBase? obj) + { + if (obj is PESection section) + { + return $" {section.Name}"; + } + + return ""; + } + } } \ No newline at end of file From a84775bd368cd22e40e5ad76f0034c5f0fe66df7 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 25 Sep 2024 20:45:45 +0200 Subject: [PATCH 54/87] Add ResolveOriginalStream extension method --- src/LibObjectFile/IO/StreamExtensions.cs | 19 +++++++++++++++++++ src/LibObjectFile/IO/SubStream.cs | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/src/LibObjectFile/IO/StreamExtensions.cs b/src/LibObjectFile/IO/StreamExtensions.cs index 8ed24f0..b5ca792 100644 --- a/src/LibObjectFile/IO/StreamExtensions.cs +++ b/src/LibObjectFile/IO/StreamExtensions.cs @@ -16,6 +16,25 @@ namespace LibObjectFile.IO; public static class StreamExtensions { + /// + /// Resolves the original stream from a and returns the position of the stream. + /// + /// The stream to resolve the underlying stream from. + /// The position resolved within the returned stream. + /// The original stream. + public static Stream ResolveOriginalStream(this Stream stream, out long position) + { + if (stream is SubStream subStream) + { + var nextStream = ResolveOriginalStream(subStream.BaseStream, out var subPosition); + position = subPosition + subStream.BasePosition; + return nextStream; + } + + position = 0; + return stream; + } + public static byte ReadU8(this Stream stream) { int nextValue = stream.ReadByte(); diff --git a/src/LibObjectFile/IO/SubStream.cs b/src/LibObjectFile/IO/SubStream.cs index d071431..b73fc32 100644 --- a/src/LibObjectFile/IO/SubStream.cs +++ b/src/LibObjectFile/IO/SubStream.cs @@ -32,6 +32,11 @@ public SubStream(Stream baseStream, long position, long length) _length = length; _basePosition = position; } + + public Stream BaseStream => _baseStream; + + public long BasePosition => _basePosition; + public override int Read(byte[] buffer, int offset, int count) => Read(new Span(buffer, offset, count)); public override int Read(Span buffer) From 90c901dafc3271170adbcc24fd781e4703d11fcf Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 25 Sep 2024 20:50:33 +0200 Subject: [PATCH 55/87] Rename PESecurityDirectory to PESecurityCertificateDirectory --- src/LibObjectFile/PE/DataDirectory/PEDataDirectoryKind.cs | 4 ++-- src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs | 8 ++++---- .../PE/DataDirectory/PESecurityCertificate.cs | 2 +- ...rityDirectory.cs => PESecurityCertificateDirectory.cs} | 6 +++--- src/LibObjectFile/PE/PEFile.Read.cs | 6 +++--- src/LibObjectFile/PE/PEFile.cs | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) rename src/LibObjectFile/PE/DataDirectory/{PESecurityDirectory.cs => PESecurityCertificateDirectory.cs} (95%) diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectoryKind.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectoryKind.cs index 931f913..869a38e 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectoryKind.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectoryKind.cs @@ -26,9 +26,9 @@ public enum PEDataDirectoryKind : ushort ///
Exception = 3, /// - /// Security Directory + /// Security/Certificate Directory /// - Security = 4, + SecurityCertificate = 4, /// /// Base Relocation Table /// diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs index f35b202..d352e3e 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -52,9 +52,9 @@ internal PEDirectoryTable() public PEExceptionDirectory? Exception => (PEExceptionDirectory?)this[PEDataDirectoryKind.Exception]; /// - /// Gets the security directory information from the PE file. + /// Gets the certificate/security directory information from the PE file. /// - public PESecurityDirectory? Security => (PESecurityDirectory?)this[PEDataDirectoryKind.Security]; + public PESecurityCertificateDirectory? Certificate => (PESecurityCertificateDirectory?)this[PEDataDirectoryKind.SecurityCertificate]; /// /// Gets the base relocation directory information from the PE file. @@ -112,9 +112,9 @@ internal PEDirectoryTable() [EditorBrowsable(EditorBrowsableState.Never)] public Enumerator GetEnumerator() => new(this); - internal void Set(PESecurityDirectory? directory) + internal void Set(PESecurityCertificateDirectory? directory) { - var kind = PEDataDirectoryKind.Security; + var kind = PEDataDirectoryKind.SecurityCertificate; ref var entry = ref _entries[(int)kind]; var previousEntry = entry; entry = directory; diff --git a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs index 2647f1d..3e18d7d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs +++ b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs @@ -8,7 +8,7 @@ namespace LibObjectFile.PE; /// -/// Represents a security certificate used in a Portable Executable (PE) file available in . +/// Represents a security certificate used in a Portable Executable (PE) file available in . /// public sealed class PESecurityCertificate { diff --git a/src/LibObjectFile/PE/DataDirectory/PESecurityDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs similarity index 95% rename from src/LibObjectFile/PE/DataDirectory/PESecurityDirectory.cs rename to src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs index 1b1f395..3f5cb3f 100644 --- a/src/LibObjectFile/PE/DataDirectory/PESecurityDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs @@ -13,12 +13,12 @@ namespace LibObjectFile.PE; /// /// Represents the security directory in a PE file. /// -public sealed class PESecurityDirectory : PEExtraData +public sealed class PESecurityCertificateDirectory : PEExtraData { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public PESecurityDirectory() + public PESecurityCertificateDirectory() { Certificates = new(); } diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 3ce126c..288eb9f 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -265,9 +265,9 @@ private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan Sections => _sections; /// - /// Gets the data present after the sections in the file (e.g ) + /// Gets the data present after the sections in the file (e.g ) /// public ObjectList ExtraDataAfterSections { get; } From 37586de8bc2ad4115f6b42295b699236f9563267 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 25 Sep 2024 20:57:23 +0200 Subject: [PATCH 56/87] Add Win64 native projects for PE tests --- .../NativeConsole2Win64.cpp | 26 +++ .../NativeConsole2Win64.vcxproj | 144 ++++++++++++++++ .../NativeConsole2Win64.vcxproj.filters | 22 +++ .../NativeConsoleWin64/NativeConsoleWin64.cpp | 20 +++ .../NativeConsoleWin64.vcxproj | 143 ++++++++++++++++ .../NativeConsoleWin64.vcxproj.filters | 22 +++ .../NativeLibraryWin64.vcxproj | 157 ++++++++++++++++++ .../NativeLibraryWin64.vcxproj.filters | 33 ++++ .../NativeLibraryWin64/dllmain.cpp | 33 ++++ .../NativeLibraryWin64/framework.h | 5 + .../NativeProjects/NativeLibraryWin64/pch.cpp | 5 + .../NativeProjects/NativeLibraryWin64/pch.h | 13 ++ .../Win64/NativeProjects/NativeProjects.sln | 51 ++++++ 13 files changed, 674 insertions(+) create mode 100644 src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.cpp create mode 100644 src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj create mode 100644 src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj.filters create mode 100644 src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.cpp create mode 100644 src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.vcxproj create mode 100644 src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.vcxproj.filters create mode 100644 src/native/Win64/NativeProjects/NativeLibraryWin64/NativeLibraryWin64.vcxproj create mode 100644 src/native/Win64/NativeProjects/NativeLibraryWin64/NativeLibraryWin64.vcxproj.filters create mode 100644 src/native/Win64/NativeProjects/NativeLibraryWin64/dllmain.cpp create mode 100644 src/native/Win64/NativeProjects/NativeLibraryWin64/framework.h create mode 100644 src/native/Win64/NativeProjects/NativeLibraryWin64/pch.cpp create mode 100644 src/native/Win64/NativeProjects/NativeLibraryWin64/pch.h create mode 100644 src/native/Win64/NativeProjects/NativeProjects.sln diff --git a/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.cpp b/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.cpp new file mode 100644 index 0000000..43e88b6 --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.cpp @@ -0,0 +1,26 @@ +// NativeConsole2Win64.cpp : This file contains the 'main' function. Program execution begins and ends there. +// + +#include + +extern "C" +{ + __declspec(dllimport) int __cdecl HelloWorld(int a, int b); + __declspec(dllexport) int __cdecl AnotherFunction(); +} + +int main() +{ + std::cout << "HelloWorld " << HelloWorld(1 + AnotherFunction(), 2) << std::endl; +} + +// Run program: Ctrl + F5 or Debug > Start Without Debugging menu +// Debug program: F5 or Debug > Start Debugging menu + +// Tips for Getting Started: +// 1. Use the Solution Explorer window to add/manage files +// 2. Use the Team Explorer window to connect to source control +// 3. Use the Output window to see build output and other messages +// 4. Use the Error List window to view errors +// 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project +// 6. In the future, to open this project again, go to File > Open > Project and select the .sln file diff --git a/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj b/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj new file mode 100644 index 0000000..9faca16 --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj @@ -0,0 +1,144 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {dd0edd92-0668-410f-9e74-d9802f9c2f49} + NativeConsole2Win64 + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + NativeLibraryWin64.dll + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + NativeLibraryWin64.dll + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + NativeLibraryWin64.dll + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + NativeLibraryWin64.dll + + + + + + + + {6fcdb0a8-ed32-4f64-91af-6a41fe4c30c5} + + + + + + \ No newline at end of file diff --git a/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj.filters b/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj.filters new file mode 100644 index 0000000..559cb0b --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.cpp b/src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.cpp new file mode 100644 index 0000000..0debfb2 --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.cpp @@ -0,0 +1,20 @@ +// NativeConsoleWin64.cpp : This file contains the 'main' function. Program execution begins and ends there. +// + +#include + +int main() +{ + std::cout << "Hello World!\n"; +} + +// Run program: Ctrl + F5 or Debug > Start Without Debugging menu +// Debug program: F5 or Debug > Start Debugging menu + +// Tips for Getting Started: +// 1. Use the Solution Explorer window to add/manage files +// 2. Use the Team Explorer window to connect to source control +// 3. Use the Output window to see build output and other messages +// 4. Use the Error List window to view errors +// 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project +// 6. In the future, to open this project again, go to File > Open > Project and select the .sln file diff --git a/src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.vcxproj b/src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.vcxproj new file mode 100644 index 0000000..8a6eabf --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.vcxproj @@ -0,0 +1,143 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {b2740032-c0c6-4eec-82a3-ebfe97ebc445} + NativeConsoleWin64 + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + \ No newline at end of file diff --git a/src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.vcxproj.filters b/src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.vcxproj.filters new file mode 100644 index 0000000..68cf5fa --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeConsoleWin64/NativeConsoleWin64.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/src/native/Win64/NativeProjects/NativeLibraryWin64/NativeLibraryWin64.vcxproj b/src/native/Win64/NativeProjects/NativeLibraryWin64/NativeLibraryWin64.vcxproj new file mode 100644 index 0000000..7ca6b1c --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeLibraryWin64/NativeLibraryWin64.vcxproj @@ -0,0 +1,157 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {6fcdb0a8-ed32-4f64-91af-6a41fe4c30c5} + NativeLibraryWin64 + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;NATIVELIBRARYWIN64_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;NATIVELIBRARYWIN64_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + Level3 + true + _DEBUG;NATIVELIBRARYWIN64_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + NDEBUG;NATIVELIBRARYWIN64_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/src/native/Win64/NativeProjects/NativeLibraryWin64/NativeLibraryWin64.vcxproj.filters b/src/native/Win64/NativeProjects/NativeLibraryWin64/NativeLibraryWin64.vcxproj.filters new file mode 100644 index 0000000..1e57c7b --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeLibraryWin64/NativeLibraryWin64.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/src/native/Win64/NativeProjects/NativeLibraryWin64/dllmain.cpp b/src/native/Win64/NativeProjects/NativeLibraryWin64/dllmain.cpp new file mode 100644 index 0000000..db5fe0e --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeLibraryWin64/dllmain.cpp @@ -0,0 +1,33 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + + +extern "C" +{ + __declspec(dllexport) int __cdecl HelloWorld(int a, int b) + { + return a + b; + } + + __declspec(dllexport) int __cdecl AnotherFunction() + { + return 123; + } + +} diff --git a/src/native/Win64/NativeProjects/NativeLibraryWin64/framework.h b/src/native/Win64/NativeProjects/NativeLibraryWin64/framework.h new file mode 100644 index 0000000..54b83e9 --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeLibraryWin64/framework.h @@ -0,0 +1,5 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include diff --git a/src/native/Win64/NativeProjects/NativeLibraryWin64/pch.cpp b/src/native/Win64/NativeProjects/NativeLibraryWin64/pch.cpp new file mode 100644 index 0000000..64b7eef --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeLibraryWin64/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/native/Win64/NativeProjects/NativeLibraryWin64/pch.h b/src/native/Win64/NativeProjects/NativeLibraryWin64/pch.h new file mode 100644 index 0000000..885d5d6 --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeLibraryWin64/pch.h @@ -0,0 +1,13 @@ +// pch.h: This is a precompiled header file. +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef PCH_H +#define PCH_H + +// add headers that you want to pre-compile here +#include "framework.h" + +#endif //PCH_H diff --git a/src/native/Win64/NativeProjects/NativeProjects.sln b/src/native/Win64/NativeProjects/NativeProjects.sln new file mode 100644 index 0000000..4c5e0a1 --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeProjects.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35222.181 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NativeConsoleWin64", "NativeConsoleWin64\NativeConsoleWin64.vcxproj", "{B2740032-C0C6-4EEC-82A3-EBFE97EBC445}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NativeConsole2Win64", "NativeConsole2Win64\NativeConsole2Win64.vcxproj", "{DD0EDD92-0668-410F-9E74-D9802F9C2F49}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NativeLibraryWin64", "NativeLibraryWin64\NativeLibraryWin64.vcxproj", "{6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B2740032-C0C6-4EEC-82A3-EBFE97EBC445}.Debug|x64.ActiveCfg = Debug|x64 + {B2740032-C0C6-4EEC-82A3-EBFE97EBC445}.Debug|x64.Build.0 = Debug|x64 + {B2740032-C0C6-4EEC-82A3-EBFE97EBC445}.Debug|x86.ActiveCfg = Debug|Win32 + {B2740032-C0C6-4EEC-82A3-EBFE97EBC445}.Debug|x86.Build.0 = Debug|Win32 + {B2740032-C0C6-4EEC-82A3-EBFE97EBC445}.Release|x64.ActiveCfg = Release|x64 + {B2740032-C0C6-4EEC-82A3-EBFE97EBC445}.Release|x64.Build.0 = Release|x64 + {B2740032-C0C6-4EEC-82A3-EBFE97EBC445}.Release|x86.ActiveCfg = Release|Win32 + {B2740032-C0C6-4EEC-82A3-EBFE97EBC445}.Release|x86.Build.0 = Release|Win32 + {DD0EDD92-0668-410F-9E74-D9802F9C2F49}.Debug|x64.ActiveCfg = Debug|x64 + {DD0EDD92-0668-410F-9E74-D9802F9C2F49}.Debug|x64.Build.0 = Debug|x64 + {DD0EDD92-0668-410F-9E74-D9802F9C2F49}.Debug|x86.ActiveCfg = Debug|Win32 + {DD0EDD92-0668-410F-9E74-D9802F9C2F49}.Debug|x86.Build.0 = Debug|Win32 + {DD0EDD92-0668-410F-9E74-D9802F9C2F49}.Release|x64.ActiveCfg = Release|x64 + {DD0EDD92-0668-410F-9E74-D9802F9C2F49}.Release|x64.Build.0 = Release|x64 + {DD0EDD92-0668-410F-9E74-D9802F9C2F49}.Release|x86.ActiveCfg = Release|Win32 + {DD0EDD92-0668-410F-9E74-D9802F9C2F49}.Release|x86.Build.0 = Release|Win32 + {6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}.Debug|x64.ActiveCfg = Debug|x64 + {6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}.Debug|x64.Build.0 = Debug|x64 + {6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}.Debug|x86.ActiveCfg = Debug|Win32 + {6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}.Debug|x86.Build.0 = Debug|Win32 + {6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}.Release|x64.ActiveCfg = Release|x64 + {6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}.Release|x64.Build.0 = Release|x64 + {6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}.Release|x86.ActiveCfg = Release|Win32 + {6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {46F09478-29CA-4093-831B-DCAF8ECB4A49} + EndGlobalSection +EndGlobal From 0c340eed6186949115e3ababbb458624144563be Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Wed, 25 Sep 2024 21:07:34 +0200 Subject: [PATCH 57/87] Add pseudo printer tests --- .../LibObjectFile.Tests.csproj | 12 +++++++++ .../PE/NativeConsole2Win64.exe | Bin 0 -> 16896 bytes .../PE/NativeConsoleWin64.exe | Bin 0 -> 12800 bytes .../PE/NativeLibraryWin64.dll | Bin 0 -> 10752 bytes src/LibObjectFile.Tests/PE/PEReaderTests.cs | 25 ++++++++++++++++++ .../PE/DataDirectory/PEExportDirectory.cs | 8 +++--- src/LibObjectFile/PE/PEPrinter.cs | 4 +-- 7 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 src/LibObjectFile.Tests/PE/NativeConsole2Win64.exe create mode 100644 src/LibObjectFile.Tests/PE/NativeConsoleWin64.exe create mode 100644 src/LibObjectFile.Tests/PE/NativeLibraryWin64.dll create mode 100644 src/LibObjectFile.Tests/PE/PEReaderTests.cs diff --git a/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj index 8de8309..0a654eb 100644 --- a/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj +++ b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj @@ -12,6 +12,9 @@ + + + @@ -28,6 +31,15 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/src/LibObjectFile.Tests/PE/NativeConsole2Win64.exe b/src/LibObjectFile.Tests/PE/NativeConsole2Win64.exe new file mode 100644 index 0000000000000000000000000000000000000000..237ba87985ee97eebba8cd866c87468f3e210176 GIT binary patch literal 16896 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%0Ap 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 literal 0 HcmV?d00001 diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs new file mode 100644 index 0000000..ae31bc0 --- /dev/null +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -0,0 +1,25 @@ +// 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.Linq; +using System.Runtime.InteropServices; +using LibObjectFile.PE; + +namespace LibObjectFile.Tests.PE; + +public class PEReaderTests +{ + [TestCase("NativeConsoleWin64.exe")] + [TestCase("NativeConsole2Win64.exe")] + [TestCase("NativeLibraryWin64.dll")] + + public void TestPrinter(string name) + { + var stream = File.OpenRead(Path.Combine(AppContext.BaseDirectory, "PE", name)); + var peImage = PEFile.Read(stream); + peImage.Print(Console.Out); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs index ca51cb0..60597d2 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs @@ -124,20 +124,20 @@ internal override void Bind(PEImageReader reader) var peFile = reader.File; var diagnostics = reader.Diagnostics; - if (!peFile.TryFindContainerByRVA(NameLink.RVA(), out var container)) + if (!peFile.TryFindContainerByRVA((RVA)(uint)NameLink.RVO, out var container)) { - diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidName, $"Unable to find the section data for Name {NameLink.RVA()}"); + diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidName, $"Unable to find the section data for Name {(RVA)(uint)NameLink.RVO}"); return; } var streamSectionData = container as PEStreamSectionData; if (streamSectionData is null) { - diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidName, $"The section data for Name {NameLink.RVA()} is not a stream section data"); + diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidName, $"The section data for Name {(RVA)(uint)NameLink.RVO} is not a stream section data"); return; } - NameLink = new PEAsciiStringLink(streamSectionData, NameLink.RVO); + NameLink = new PEAsciiStringLink(streamSectionData, NameLink.RVO - streamSectionData.RVA); if (ExportFunctionAddressTable is not null) { diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index a0c2f9e..5b38b00 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -603,11 +603,11 @@ private static void Print(PEExportAddressTable data, ref TextWriterIndenter writ var entry = data.Values[i]; if (entry.IsForwarderRVA) { - writer.WriteLine($"[{i}] Forwarder RVA = {entry.ForwarderRVA}"); + writer.WriteLine($"[{i}] Forwarder RVA = {entry.ForwarderRVA.RVA()} ({PELink(entry.ForwarderRVA.Container)})"); } else { - writer.WriteLine($"[{i}] Exported RVA = {entry.ExportRVA}"); + writer.WriteLine($"[{i}] Exported RVA = {entry.ExportRVA.RVA()} ({PELink(entry.ExportRVA.Container)})"); } } } From 643015776d5bae1ab929b572c981e733b9bf182d Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 26 Sep 2024 08:06:45 +0200 Subject: [PATCH 58/87] Migrate tests to MSTest --- .editorconfig | 145 ++++++++++++++++++ .gitattributes | 6 + src/Directory.Packages.props | 16 ++ .../LibObjectFile.CodeGen.csproj | 4 +- src/LibObjectFile.Tests/Ar/ArTestBase.cs | 2 +- src/LibObjectFile.Tests/Ar/ArTests.cs | 61 ++++---- src/LibObjectFile.Tests/Dwarf/DwarfTests.cs | 69 +++++---- src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs | 55 +++---- src/LibObjectFile.Tests/Elf/ElfTestBase.cs | 2 +- .../LibObjectFile.Tests.csproj | 9 +- src/LibObjectFile.Tests/PE/PEReaderTests.cs | 10 +- src/LibObjectFile.Tests/Usings.cs | 10 -- src/LibObjectFile.sln | 3 + src/LibObjectFile/LibObjectFile.csproj | 6 +- src/objdasm/objdasm.csproj | 6 +- 15 files changed, 286 insertions(+), 118 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 src/Directory.Packages.props delete mode 100644 src/LibObjectFile.Tests/Usings.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f7ffab2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,145 @@ +# EditorConfig is awesome:http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# All Files +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +insert_final_newline = false +trim_trailing_whitespace = true +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_indent_labels = one_less_than_current +csharp_style_deconstructed_variable_declaration = true:suggestion +dotnet_diagnostic.NUnit2006.severity = silent +dotnet_diagnostic.NUnit2005.severity = silent +dotnet_diagnostic.NUnit2004.severity = silent +dotnet_diagnostic.NUnit2003.severity = silent +dotnet_diagnostic.NUnit2002.severity = silent +dotnet_diagnostic.NUnit2001.severity = silent + +# Solution Files +[*.sln] +indent_style = tab + +# XML Project Files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# Configuration Files +[*.{json,xml,yml,config,props,targets,nuspec,resx,ruleset}] +indent_size = 2 + +# Txt/Markdown Files +[*.{md,txt}] +trim_trailing_whitespace = false + +# Web Files +[*.{htm,html,js,ts,css,scss,less}] +indent_size = 2 +insert_final_newline = true + +# Bash Files +[*.sh] +end_of_line = lf + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 + + +# Verify settings +[*.{received,verified}.{txt,xml,json}] +charset = "utf-8-bom" +end_of_line = lf +indent_size = unset +indent_style = unset +insert_final_newline = false +tab_width = unset +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..811523a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto +*.sh text eol=lf +*.verified.txt text eol=lf working-tree-encoding=UTF-8 +*.verified.xml text eol=lf working-tree-encoding=UTF-8 +*.verified.json text eol=lf working-tree-encoding=UTF-8 \ No newline at end of file diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 0000000..e1c43c9 --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,16 @@ + + + true + false + + + + + + + + + + + + \ No newline at end of file diff --git a/src/LibObjectFile.CodeGen/LibObjectFile.CodeGen.csproj b/src/LibObjectFile.CodeGen/LibObjectFile.CodeGen.csproj index 77c6fbd..f5e76dc 100644 --- a/src/LibObjectFile.CodeGen/LibObjectFile.CodeGen.csproj +++ b/src/LibObjectFile.CodeGen/LibObjectFile.CodeGen.csproj @@ -1,4 +1,4 @@ - + net8.0 Exe @@ -12,7 +12,7 @@ - + PreserveNewest diff --git a/src/LibObjectFile.Tests/Ar/ArTestBase.cs b/src/LibObjectFile.Tests/Ar/ArTestBase.cs index 3e55e7d..2a4ce68 100644 --- a/src/LibObjectFile.Tests/Ar/ArTestBase.cs +++ b/src/LibObjectFile.Tests/Ar/ArTestBase.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.Tests/Ar/ArTests.cs b/src/LibObjectFile.Tests/Ar/ArTests.cs index 52aea87..85f346e 100644 --- a/src/LibObjectFile.Tests/Ar/ArTests.cs +++ b/src/LibObjectFile.Tests/Ar/ArTests.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. @@ -11,15 +11,16 @@ namespace LibObjectFile.Tests.Ar; +[TestClass] public class ArTests : ArTestBase { - [Test] + [TestMethod] public void CheckInvalidHeader() { // Incorrect magic length { var stream = new MemoryStream(new byte[] { 1, 2, 3, 4 }); - Assert.False(ArArchiveFile.IsAr(stream, out var diagnostics)); + Assert.IsFalse(ArArchiveFile.IsAr(stream, out var diagnostics)); ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidMagicLength); } @@ -36,7 +37,7 @@ public void CheckInvalidHeader() (byte)'>', (byte)'?', }); - Assert.False(ArArchiveFile.IsAr(stream, out var diagnostics)); + Assert.IsFalse(ArArchiveFile.IsAr(stream, out var diagnostics)); ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_MagicNotFound); } @@ -53,12 +54,12 @@ public void CheckInvalidHeader() (byte)'>', (byte)'\n', }); - Assert.True(ArArchiveFile.IsAr(stream, out var diagnostics)); + Assert.IsTrue(ArArchiveFile.IsAr(stream, out var diagnostics)); ExpectNoDiagnostics(diagnostics); } } - [Test] + [TestMethod] public void CheckInvalidFileEntry() { // Incorrect entry length @@ -68,7 +69,7 @@ public void CheckInvalidFileEntry() stream.Write(new byte[] {(byte)'a', (byte)'b'}); stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); + Assert.IsFalse(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidFileEntryLength); } @@ -95,7 +96,7 @@ public void CheckInvalidFileEntry() stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); + Assert.IsFalse(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry); } } @@ -118,7 +119,7 @@ public void CheckInvalidFileEntry() stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); + Assert.IsFalse(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidCharacterInFileEntryName); } @@ -139,7 +140,7 @@ public void CheckInvalidFileEntry() stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); + Assert.IsFalse(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); ExpectDiagnostics(diagnostics, DiagnosticId.CMN_ERR_UnexpectedEndOfFile, DiagnosticId.CMN_ERR_UnexpectedEndOfFile); stream.Position = continuePosition; @@ -153,11 +154,11 @@ public void CheckInvalidFileEntry() var result = ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out var arFile, out diagnostics); ExpectNoDiagnostics(diagnostics); - Assert.True(result, $"Error while reading file: {diagnostics}"); + Assert.IsTrue(result, $"Error while reading file: {diagnostics}"); Assert.AreEqual(1, arFile.Files.Count, "Invalid number of file entries found"); Assert.AreEqual("a", arFile.Files[0].Name, "Invalid name of file entry[0] found"); - Assert.AreEqual(2, arFile.Files[0].Size, "Invalid size of file entry[0] found"); - Assert.IsInstanceOf(arFile.Files[0], "Invalid instance of of file entry[0] "); + Assert.AreEqual(2U, arFile.Files[0].Size, "Invalid size of file entry[0] found"); + Assert.IsInstanceOfType(arFile.Files[0], "Invalid instance of of file entry[0] "); var fileStream = ((ArBinaryFile) arFile.Files[0]).Stream; var read = new byte[] @@ -165,9 +166,9 @@ public void CheckInvalidFileEntry() (byte)fileStream.ReadByte(), (byte)fileStream.ReadByte() }; - Assert.AreEqual(new byte[] { 0, 1}, read, "Invalid content of of file entry[0] "); + CollectionAssert.AreEqual(new byte[] { 0, 1}, read, "Invalid content of of file entry[0] "); - Assert.Null(arFile.SymbolTable, "Invalid non-null symbol table found"); + Assert.IsNull(arFile.SymbolTable, "Invalid non-null symbol table found"); } @@ -188,26 +189,26 @@ public void CheckInvalidFileEntry() var continuePosition = stream.Position; stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); + Assert.IsFalse(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); ExpectDiagnostics(diagnostics, DiagnosticId.CMN_ERR_UnexpectedEndOfFile); stream.Position = continuePosition; stream.WriteByte(0); stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out diagnostics)); + Assert.IsFalse(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out diagnostics)); ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_ExpectingNewLineCharacter); stream.Position = continuePosition; stream.WriteByte((byte)'\n'); stream.Position = 0; - Assert.True(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out diagnostics)); + Assert.IsTrue(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out diagnostics)); ExpectNoDiagnostics(diagnostics); } } - [Test] + [TestMethod] public void CheckInvalidBSDFileEntry() { // Input invalid BSD Length @@ -225,7 +226,7 @@ public void CheckInvalidBSDFileEntry() stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.BSD, out _, out var diagnostics)); + Assert.IsFalse(ArArchiveFile.TryRead(stream, ArArchiveKind.BSD, out _, out var diagnostics)); ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidCharacterFoundInFileEntry); } @@ -249,7 +250,7 @@ public void CheckInvalidBSDFileEntry() stream.Position = 0; - Assert.False(ArArchiveFile.TryRead(stream, ArArchiveKind.BSD, out _, out var diagnostics)); + Assert.IsFalse(ArArchiveFile.TryRead(stream, ArArchiveKind.BSD, out _, out var diagnostics)); ExpectDiagnostics(diagnostics, DiagnosticId.CMN_ERR_UnexpectedEndOfFile); // Check validity of BSD name @@ -261,14 +262,14 @@ public void CheckInvalidBSDFileEntry() stream.Position = 0; var result = ArArchiveFile.TryRead(stream, ArArchiveKind.BSD, out var arFile, out diagnostics); - Assert.True(result, $"Error while reading file: {diagnostics}"); + Assert.IsTrue(result, $"Error while reading file: {diagnostics}"); Assert.AreEqual(1, arFile.Files.Count, "Invalid number of file entries found"); Assert.AreEqual("ab", arFile.Files[0].Name, "Invalid name of file entry[0] found"); - Assert.Null(arFile.SymbolTable, "Invalid non-null symbol table found"); + Assert.IsNull(arFile.SymbolTable, "Invalid non-null symbol table found"); } } - [Test] + [TestMethod] public void CheckLibraryWithELF() { var cppName = "helloworld"; @@ -281,15 +282,15 @@ public void CheckLibraryWithELF() using (var stream = new FileStream(cppLib, FileMode.Open, FileAccess.Read)) { - Assert.True(ArArchiveFile.IsAr(stream)); + Assert.IsTrue(ArArchiveFile.IsAr(stream)); var arFile = ArArchiveFile.Read(stream, new ArArchiveFileReaderOptions(ArArchiveKind.GNU) {ProcessObjectFiles = false}); var elfFile = arFile.Files.FirstOrDefault(x => x.Name == cppObj); - Assert.NotNull(elfFile, $"Unable to find {cppObj} file in {cppLib}"); + Assert.IsNotNull(elfFile, $"Unable to find {cppObj} file in {cppLib}"); - Assert.NotNull(arFile.SymbolTable, $"Unable to find symbol table in {cppLib}"); + Assert.IsNotNull(arFile.SymbolTable, $"Unable to find symbol table in {cppLib}"); Assert.AreEqual(1, arFile.SymbolTable.Symbols.Count, "Invalid number of symbols in Symbol table"); Assert.AreEqual("main", arFile.SymbolTable.Symbols[0].Name, "Invalid symbol found"); @@ -311,11 +312,11 @@ public void CheckLibraryWithELF() stream.CopyTo(originalStream); var originalArray = originalStream.ToArray(); - Assert.AreEqual(originalArray, newArray, $"Non binary matching between file {cppLib} and {cppLibCopy}"); + CollectionAssert.AreEqual(originalArray, newArray, $"Non binary matching between file {cppLib} and {cppLibCopy}"); } } - [Test] + [TestMethod] public void CheckCreateArLibrary() { var libName = "libcustom.a"; @@ -378,7 +379,7 @@ public void CheckCreateArLibrary() file2 = ArArchiveFile.Read(stream, ArArchiveKind.GNU); } - Assert.NotNull(file2.SymbolTable); + Assert.IsNotNull(file2.SymbolTable); CompareArFiles(file, file2); var libNameBsd = "libcustom_bsd.a"; diff --git a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs index 3696aa5..c68ad32 100644 --- a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs +++ b/src/LibObjectFile.Tests/Dwarf/DwarfTests.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. @@ -10,18 +10,20 @@ namespace LibObjectFile.Tests.Dwarf; +[TestClass] public class DwarfTests { - [TestCase(0UL)] - [TestCase(1UL)] - [TestCase(50UL)] - [TestCase(0x7fUL)] - [TestCase(0x80UL)] - [TestCase(0x81UL)] - [TestCase(0x12345UL)] - [TestCase(2147483647UL)] // int.MaxValue - [TestCase(4294967295UL)] // uint.MaxValue - [TestCase(ulong.MaxValue)] + [TestMethod] + [DataRow(0UL)] + [DataRow(1UL)] + [DataRow(50UL)] + [DataRow(0x7fUL)] + [DataRow(0x80UL)] + [DataRow(0x81UL)] + [DataRow(0x12345UL)] + [DataRow(2147483647UL)] // int.MaxValue + [DataRow(4294967295UL)] // uint.MaxValue + [DataRow(ulong.MaxValue)] public void TestLEB128(ulong value) { var stream = new MemoryStream(); @@ -36,17 +38,18 @@ public void TestLEB128(ulong value) Assert.AreEqual(value, readbackValue); } - [TestCase(0L)] - [TestCase(1L)] - [TestCase(50L)] - [TestCase(0x7fL)] - [TestCase(0x80L)] - [TestCase(0x81L)] - [TestCase(0x12345L)] - [TestCase(2147483647L)] // int.MaxValue - [TestCase(4294967295L)] // uint.MaxValue - [TestCase(long.MinValue)] - [TestCase(long.MaxValue)] + [TestMethod] + [DataRow(0L)] + [DataRow(1L)] + [DataRow(50L)] + [DataRow(0x7fL)] + [DataRow(0x80L)] + [DataRow(0x81L)] + [DataRow(0x12345L)] + [DataRow(2147483647L)] // int.MaxValue + [DataRow(4294967295L)] // uint.MaxValue + [DataRow(long.MinValue)] + [DataRow(long.MaxValue)] public void TestSignedLEB128(long value) { var stream = new MemoryStream(); @@ -75,7 +78,7 @@ public void TestSignedLEB128(long value) } - [Test] + [TestMethod] public void TestDebugLineHelloWorld() { var cppName = "helloworld"; @@ -131,10 +134,10 @@ public void TestDebugLineHelloWorld() var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + CollectionAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); } - [Test] + [TestMethod] public void TestDebugLineLibMultipleObjs() { var cppName = "lib"; @@ -190,10 +193,10 @@ public void TestDebugLineLibMultipleObjs() var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + CollectionAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); } - [Test] + [TestMethod] public void TestDebugLineSmall() { var cppName = "small"; @@ -246,12 +249,12 @@ public void TestDebugLineSmall() var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + CollectionAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); } - [Test] + [TestMethod] public void TestDebugLineMultipleFunctions() { var cppName = "multiple_functions"; @@ -305,11 +308,11 @@ public void TestDebugLineMultipleFunctions() var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - Assert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + CollectionAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); } - [Test] + [TestMethod] public void TestDebugInfoSmall() { var cppName = "small"; @@ -368,7 +371,7 @@ public void TestDebugInfoSmall() } - [Test] + [TestMethod] public void CreateDwarf() { // Create ELF object @@ -383,7 +386,7 @@ public void CreateDwarf() var elfDiagnostics = new DiagnosticBag(); elf.UpdateLayout(elfDiagnostics); - Assert.False(elfDiagnostics.HasErrors); + Assert.IsFalse(elfDiagnostics.HasErrors); // Create DWARF Object var dwarfFile = new DwarfFile(); diff --git a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs index a4f3376..a4b801a 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -10,16 +10,17 @@ namespace LibObjectFile.Tests.Elf; +[TestClass] public class ElfSimpleTests : ElfTestBase { - [Test] + [TestMethod] public void TryReadThrows() { static void CheckInvalidLib(bool isReadOnly) { using var stream = File.OpenRead("TestFiles/cmnlib.b00"); - Assert.False(ElfObjectFile.TryRead(stream, out var elf, out var diagnostics, new ElfReaderOptions() { ReadOnly = isReadOnly })); - Assert.NotNull(elf); + Assert.IsFalse(ElfObjectFile.TryRead(stream, out var elf, out var diagnostics, new ElfReaderOptions() { ReadOnly = isReadOnly })); + Assert.IsNotNull(elf); Assert.AreEqual(4, diagnostics.Messages.Count, "Invalid number of error messages found"); Assert.AreEqual(DiagnosticId.ELF_ERR_IncompleteProgramHeader32Size, diagnostics.Messages[0].Id); for (int i = 1; i < diagnostics.Messages.Count; i++) @@ -32,26 +33,26 @@ static void CheckInvalidLib(bool isReadOnly) CheckInvalidLib(true); } - [Test] + [TestMethod] public void TryReadFailed() { using var stream = File.OpenRead(typeof(ElfSimpleTests).Assembly.Location); - Assert.False(ElfObjectFile.TryRead(stream, out var elfObjectFile, out var diagnostics)); - Assert.True(diagnostics.HasErrors); + Assert.IsFalse(ElfObjectFile.TryRead(stream, out var elfObjectFile, out var diagnostics)); + Assert.IsTrue(diagnostics.HasErrors); Assert.AreEqual(1, diagnostics.Messages.Count); Assert.AreEqual(DiagnosticId.ELF_ERR_InvalidHeaderMagic, diagnostics.Messages[0].Id); } - [Test] + [TestMethod] public void SimpleEmptyWithDefaultSections() { var elf = new ElfObjectFile(ElfArch.X86_64); AssertReadElf(elf, "empty_default.elf"); } - [Test] + [TestMethod] public void SimpleEmpty() { var elf = new ElfObjectFile(ElfArch.X86_64); @@ -62,7 +63,7 @@ public void SimpleEmpty() AssertReadElf(elf, "empty.elf"); } - [Test] + [TestMethod] public void SimpleCodeSection() { var elf = new ElfObjectFile(ElfArch.X86_64); @@ -78,7 +79,7 @@ public void SimpleCodeSection() AssertReadElf(elf, "test.elf"); } - [Test] + [TestMethod] public void TestBss() { var elf = new ElfObjectFile(ElfArch.X86_64); @@ -98,14 +99,14 @@ public void TestBss() var diagnostics = new DiagnosticBag(); elf.UpdateLayout(diagnostics); - Assert.False(diagnostics.HasErrors); + Assert.IsFalse(diagnostics.HasErrors); - Assert.AreEqual(1024, bssSection.Position); + Assert.AreEqual(1024U, bssSection.Position); AssertReadElf(elf, "test_bss.elf"); } - [Test] + [TestMethod] public void SimpleCodeSectionAndSymbolSection() { var elf = new ElfObjectFile(ElfArch.X86_64); @@ -153,7 +154,7 @@ public void SimpleCodeSectionAndSymbolSection() AssertReadElf(elf, "test2.elf"); } - [Test] + [TestMethod] public void SimpleProgramHeaderAndCodeSectionAndSymbolSection() { var elf = new ElfObjectFile(ElfArch.X86_64); @@ -241,7 +242,7 @@ public void SimpleProgramHeaderAndCodeSectionAndSymbolSection() } - [Test] + [TestMethod] public void SimpleProgramHeaderAndCodeSectionAndSymbolSectionAndRelocation() { var elf = new ElfObjectFile(ElfArch.X86_64); @@ -359,7 +360,7 @@ public void SimpleProgramHeaderAndCodeSectionAndSymbolSectionAndRelocation() } - [Test] + [TestMethod] public void TestHelloWorld() { var cppName = "helloworld"; @@ -393,7 +394,7 @@ public void TestHelloWorld() } } - [Test] + [TestMethod] public void TestAlignedSection() { var elf = new ElfObjectFile(ElfArch.X86_64); @@ -412,17 +413,17 @@ public void TestAlignedSection() elf.AddSection(new ElfSectionHeaderStringTable()); var diagnostics = elf.Verify(); - Assert.False(diagnostics.HasErrors); + Assert.IsFalse(diagnostics.HasErrors); elf.UpdateLayout(diagnostics); - Assert.False(diagnostics.HasErrors); + Assert.IsFalse(diagnostics.HasErrors); elf.Print(Console.Out); Assert.AreEqual(alignedSection.UpperAlignment, codeSection.Position, "Invalid alignment"); } - [Test] + [TestMethod] public void TestManySections() { var elf = new ElfObjectFile(ElfArch.X86_64); @@ -441,12 +442,12 @@ public void TestManySections() elf.AddSection(new ElfSectionHeaderStringTable()); var diagnostics = elf.Verify(); - Assert.True(diagnostics.HasErrors); + Assert.IsTrue(diagnostics.HasErrors); Assert.AreEqual(DiagnosticId.ELF_ERR_MissingSectionHeaderIndices, diagnostics.Messages[0].Id); elf.AddSection(new ElfSymbolTableSectionHeaderIndices { Link = symbolTable }); diagnostics = elf.Verify(); - Assert.False(diagnostics.HasErrors); + Assert.IsFalse(diagnostics.HasErrors); uint visibleSectionCount = elf.VisibleSectionCount; @@ -462,16 +463,16 @@ public void TestManySections() } Assert.AreEqual(visibleSectionCount, elf.VisibleSectionCount); - Assert.True(elf.Sections[0] is ElfNullSection); - Assert.True(elf.Sections[1] is ElfProgramHeaderTable); + Assert.IsTrue(elf.Sections[0] is ElfNullSection); + Assert.IsTrue(elf.Sections[1] is ElfProgramHeaderTable); for (int i = 0; i < ushort.MaxValue; i++) { - Assert.True(elf.Sections[i + 2] is ElfBinarySection); + Assert.IsTrue(elf.Sections[i + 2] is ElfBinarySection); Assert.AreEqual($".section{i}", elf.Sections[i + 2].Name.Value); } - Assert.True(elf.Sections[ushort.MaxValue + 3] is ElfSymbolTable); + Assert.IsTrue(elf.Sections[ushort.MaxValue + 3] is ElfSymbolTable); symbolTable = (ElfSymbolTable)elf.Sections[ushort.MaxValue + 3]; for (int i = 0; i < ushort.MaxValue; i++) { @@ -479,7 +480,7 @@ public void TestManySections() } } - [Test] + [TestMethod] public void TestReadLibStdc() { ElfObjectFile elf; diff --git a/src/LibObjectFile.Tests/Elf/ElfTestBase.cs b/src/LibObjectFile.Tests/Elf/ElfTestBase.cs index 80e5a1b..63c4bf7 100644 --- a/src/LibObjectFile.Tests/Elf/ElfTestBase.cs +++ b/src/LibObjectFile.Tests/Elf/ElfTestBase.cs @@ -76,7 +76,7 @@ protected static void AssertReadBack(ElfObjectFile elf, string fileName, bool re newObjectFile.Write(memoryStream); var newBuffer = memoryStream.ToArray(); - Assert.AreEqual(originalBuffer, newBuffer, "Invalid binary diff between write -> (original) -> read -> write -> (new)"); + CollectionAssert.AreEqual(originalBuffer, newBuffer, "Invalid binary diff between write -> (original) -> read -> write -> (new)"); } } diff --git a/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj index 0a654eb..faac293 100644 --- a/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj +++ b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -53,9 +53,10 @@ - - - + + + + diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index ae31bc0..e9e04b4 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.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. @@ -10,11 +10,13 @@ namespace LibObjectFile.Tests.PE; +[TestClass] public class PEReaderTests { - [TestCase("NativeConsoleWin64.exe")] - [TestCase("NativeConsole2Win64.exe")] - [TestCase("NativeLibraryWin64.dll")] + [TestMethod] + [DataRow("NativeConsoleWin64.exe")] + [DataRow("NativeConsole2Win64.exe")] + [DataRow("NativeLibraryWin64.dll")] public void TestPrinter(string name) { diff --git a/src/LibObjectFile.Tests/Usings.cs b/src/LibObjectFile.Tests/Usings.cs deleted file mode 100644 index 501a06f..0000000 --- a/src/LibObjectFile.Tests/Usings.cs +++ /dev/null @@ -1,10 +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. - -global using NUnit.Framework; -global using Assert = NUnit.Framework.Legacy.ClassicAssert; -global using CollectionAssert = NUnit.Framework.Legacy.CollectionAssert; -global using StringAssert = NUnit.Framework.Legacy.StringAssert; -global using DirectoryAssert = NUnit.Framework.Legacy.DirectoryAssert; -global using FileAssert = NUnit.Framework.Legacy.FileAssert; \ No newline at end of file diff --git a/src/LibObjectFile.sln b/src/LibObjectFile.sln index b3d04ef..c6de7eb 100644 --- a/src/LibObjectFile.sln +++ b/src/LibObjectFile.sln @@ -11,10 +11,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibObjectFile.CodeGen", "Li EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E0D117A3-A530-46EE-A873-4FE18DCA6841}" ProjectSection(SolutionItems) = preProject + ..\.editorconfig = ..\.editorconfig + ..\.gitattributes = ..\.gitattributes ..\.gitignore = ..\.gitignore ..\changelog.md = ..\changelog.md ..\.github\workflows\ci.yml = ..\.github\workflows\ci.yml Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props dotnet-releaser.toml = dotnet-releaser.toml global.json = global.json ..\license.txt = ..\license.txt diff --git a/src/LibObjectFile/LibObjectFile.csproj b/src/LibObjectFile/LibObjectFile.csproj index 2ec2aa8..e706b4d 100644 --- a/src/LibObjectFile/LibObjectFile.csproj +++ b/src/LibObjectFile/LibObjectFile.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -31,12 +31,12 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/objdasm/objdasm.csproj b/src/objdasm/objdasm.csproj index 60b2fc3..395c907 100644 --- a/src/objdasm/objdasm.csproj +++ b/src/objdasm/objdasm.csproj @@ -1,4 +1,4 @@ - + Exe @@ -8,8 +8,8 @@ - - + + From 81e7cd8c11c4e8cb2a7ff44e3cedecd53ef71259 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 26 Sep 2024 08:20:00 +0200 Subject: [PATCH 59/87] Add Verify for testing PE --- src/LibObjectFile.Tests/Dwarf/DwarfTests.cs | 4 +- .../LibObjectFile.Tests.csproj | 5 + src/LibObjectFile.Tests/PE/PEReaderTests.cs | 13 +- src/LibObjectFile.Tests/TestsInitializer.cs | 19 + ..._name=NativeConsole2Win64.exe.verified.txt | 643 ++++++++++++++++++ ...r_name=NativeConsoleWin64.exe.verified.txt | 547 +++++++++++++++ ...r_name=NativeLibraryWin64.dll.verified.txt | 468 +++++++++++++ 7 files changed, 1693 insertions(+), 6 deletions(-) create mode 100644 src/LibObjectFile.Tests/TestsInitializer.cs create mode 100644 src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsole2Win64.exe.verified.txt create mode 100644 src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsoleWin64.exe.verified.txt create mode 100644 src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeLibraryWin64.dll.verified.txt diff --git a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs index c68ad32..6b036bc 100644 --- a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs +++ b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs @@ -13,7 +13,7 @@ namespace LibObjectFile.Tests.Dwarf; [TestClass] public class DwarfTests { - [TestMethod] + [DataTestMethod] [DataRow(0UL)] [DataRow(1UL)] [DataRow(50UL)] @@ -38,7 +38,7 @@ public void TestLEB128(ulong value) Assert.AreEqual(value, readbackValue); } - [TestMethod] + [DataTestMethod] [DataRow(0L)] [DataRow(1L)] [DataRow(50L)] diff --git a/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj index faac293..9dafa08 100644 --- a/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj +++ b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj @@ -4,6 +4,7 @@ net8.0 true false + true @@ -63,4 +64,8 @@ + + + + diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index e9e04b4..20363e9 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -6,22 +6,27 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Threading.Tasks; using LibObjectFile.PE; +using VerifyMSTest; namespace LibObjectFile.Tests.PE; [TestClass] -public class PEReaderTests +[UsesVerify] +public partial class PEReaderTests { - [TestMethod] + [DataTestMethod] [DataRow("NativeConsoleWin64.exe")] [DataRow("NativeConsole2Win64.exe")] [DataRow("NativeLibraryWin64.dll")] - public void TestPrinter(string name) + public async Task TestPrinter(string name) { var stream = File.OpenRead(Path.Combine(AppContext.BaseDirectory, "PE", name)); var peImage = PEFile.Read(stream); - peImage.Print(Console.Out); + var text = new StringWriter(); + peImage.Print(text); + await Verifier.Verify(text).UseParameters(name); } } \ No newline at end of file diff --git a/src/LibObjectFile.Tests/TestsInitializer.cs b/src/LibObjectFile.Tests/TestsInitializer.cs new file mode 100644 index 0000000..04f353d --- /dev/null +++ b/src/LibObjectFile.Tests/TestsInitializer.cs @@ -0,0 +1,19 @@ +using System.Globalization; +using System.Runtime.CompilerServices; +using VerifyMSTest; +using VerifyTests; +using VerifyTests.DiffPlex; + +namespace LibObjectFile.Tests; + +internal static class TestsInitializer +{ + [ModuleInitializer] + public static void Initialize() + { + VerifyDiffPlex.Initialize(OutputType.Compact); + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + Verifier.UseProjectRelativeDirectory("Verified"); + DiffEngine.DiffRunner.Disabled = true; + } +} \ No newline at end of file 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 new file mode 100644 index 0000000..7bf5439 --- /dev/null +++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsole2Win64.exe.verified.txt @@ -0,0 +1,643 @@ +DOS Header + Magic = DOS + ByteCountOnLastPage = 0x90 + PageCount = 0x3 + RelocationCount = 0x0 + SizeOfParagraphsHeader = 0x4 + MinExtraParagraphs = 0x0 + MaxExtraParagraphs = 0xFFFF + InitialSSValue = 0x0 + InitialSPValue = 0xB8 + Checksum = 0x0 + InitialIPValue = 0x0 + InitialCSValue = 0x0 + FileAddressRelocationTable = 0x40 + OverlayNumber = 0x0 + Reserved = 0x0, 0x0, 0x0, 0x0 + OEMIdentifier = 0x0 + OEMInformation = 0x0 + Reserved2 = 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + FileAddressPEHeader = 0xF8 + +DOS Stub + DosStub = 64 bytes + +COFF Header + Machine = Amd64 + NumberOfSections = 6 + TimeDateStamp = 1727110447 + PointerToSymbolTable = 0x0 + NumberOfSymbols = 0 + SizeOfOptionalHeader = 240 + Characteristics = ExecutableImage, LargeAddressAware + +Optional Header + Magic = PE32Plus + MajorLinkerVersion = 14 + MinorLinkerVersion = 41 + SizeOfCode = 0x1A00 + SizeOfInitializedData = 0x2A00 + SizeOfUninitializedData = 0x0 + AddressOfEntryPoint = 0x15E0 + BaseOfCode = 0x1000 + BaseOfData = 0x0 + ImageBase = 0x140000000 + SectionAlignment = 0x1000 + FileAlignment = 0x200 + MajorOperatingSystemVersion = 6 + MinorOperatingSystemVersion = 0 + MajorImageVersion = 0 + MinorImageVersion = 0 + MajorSubsystemVersion = 6 + MinorSubsystemVersion = 0 + Win32VersionValue = 0x0 + SizeOfImage = 0x9000 + SizeOfHeaders = 0x400 + CheckSum = 0x0 + Subsystem = WindowsCui + DllCharacteristics = HighEntropyVirtualAddressSpace, DynamicBase, NxCompatible, TerminalServerAware + SizeOfStackReserve = 0x100000 + SizeOfStackCommit = 0x1000 + SizeOfHeapReserve = 0x100000 + SizeOfHeapCommit = 0x1000 + LoaderFlags = 0x0 + NumberOfRvaAndSizes = 0x10 + +Data Directories + [00] = null + [01] = PEImportDirectory Position = 0x00002B3C, Size = 0x000000C8, RVA = 0x00003D3C, VirtualSize = 0x000000C8 + [02] = PEResourceDirectory Position = 0x00003E00, Size = 0x000001E0, RVA = 0x00007000, VirtualSize = 0x000001E0 + [03] = PEExceptionDirectory Position = 0x00003A00, Size = 0x00000210, RVA = 0x00006000, VirtualSize = 0x00000210 + [04] = null + [05] = PEBaseRelocationDirectory Position = 0x00004000, Size = 0x0000003C, RVA = 0x00008000, VirtualSize = 0x0000003C + [06] = PEDebugDirectory Position = 0x000022E0, Size = 0x00000070, RVA = 0x000034E0, VirtualSize = 0x00000070 + [07] = null + [08] = null + [09] = null + [10] = PELoadConfigDirectory64 Position = 0x00002190, Size = 0x00000140, RVA = 0x00003390, VirtualSize = 0x00000140 + [11] = null + [12] = PEImportAddressTableDirectory Position = 0x00001E00, Size = 0x00000250, RVA = 0x00003000, VirtualSize = 0x00000250 + [13] = PEDelayImportDirectory Position = 0x000029A8, Size = 0x00000040, RVA = 0x00003BA8, VirtualSize = 0x00000040 + [14] = null + +Sections + [00] .text PESection Position = 0x00000400, Size = 0x00001A00, RVA = 0x00001000, VirtualSize = 0x000019E9, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) + + Section Data [00] PEStreamSectionData Position = 0x00000400, Size = 0x00001A00, RVA = 0x00001000, VirtualSize = 0x00001A00 + + + [01] .rdata PESection Position = 0x00001E00, Size = 0x00001A00, RVA = 0x00003000, VirtualSize = 0x0000183C, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + Section Data [00] PEImportAddressTableDirectory Position = 0x00001E00, Size = 0x00000250, RVA = 0x00003000, VirtualSize = 0x00000250 + Section Data [00] PEImportAddressTable Position = 0x00001EC0, Size = 0x00000068, RVA = 0x00003000, VirtualSize = 0x00000068 + [0] PEImportHintName { Hint = 255, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z } (RVA = 0x42D6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x27E) + [1] PEImportHintName { Hint = 262, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@P6AAEAV01@AEAV01@@Z@Z } (RVA = 0x4282, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x22A) + [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x4242, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x1EA) + [3] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x41FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x1A6) + [4] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x41C0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x168) + [5] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x417E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x126) + [6] PEImportHintName { Hint = 1121, Name = ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@D@Z } (RVA = 0x413A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0xE2) + [7] PEImportHintName { Hint = 1332, Name = ?widen@?$basic_ios@DU?$char_traits@D@std@@@std@@QEBADD@Z } (RVA = 0x40FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0xA6) + [8] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x40B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5E) + [9] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x4094, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3C) + [10] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x4318, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x2C0) + [11] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x4058, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x0) + + Section Data [01] PEImportAddressTable Position = 0x00001F60, Size = 0x00000010, RVA = 0x00003068, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x4346, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x2EE) + + Section Data [02] PEImportAddressTable Position = 0x00001F28, Size = 0x00000038, RVA = 0x00003078, VirtualSize = 0x00000038 + [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x47AC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x754) + [1] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x439C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x344) + [2] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x43BA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x362) + [3] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x436E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x316) + [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x4386, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x32E) + [5] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x435C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x304) + + Section Data [03] PEImportAddressTable Position = 0x00001FA0, Size = 0x00000098, RVA = 0x000030B0, VirtualSize = 0x00000098 + [0] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x44C4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x46C) + [1] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4566, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x50E) + [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x454A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4F2) + [3] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x44A0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x448) + [4] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x448A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x432) + [5] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x447E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x426) + [6] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x445C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x404) + [7] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x443A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3E2) + [8] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x4420, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3C8) + [9] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x44D2, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x47A) + [10] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x43FC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3A4) + [11] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x43EA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x392) + [12] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4590, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x538) + [13] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x4498, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x440) + [14] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4582, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x52A) + [15] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x44B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x45E) + [16] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x44E6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x48E) + [17] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x44DC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x484) + + Section Data [04] PEImportAddressTable Position = 0x00001F90, Size = 0x00000010, RVA = 0x00003148, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x440C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3B4) + + Section Data [05] PEImportAddressTable Position = 0x00002038, Size = 0x00000018, RVA = 0x00003158, VirtualSize = 0x00000018 + [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x453A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4E2) + [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x44A8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x450) + + Section Data [06] PEImportAddressTable Position = 0x00001F80, Size = 0x00000010, RVA = 0x00003170, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x4514, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4BC) + + Section Data [07] PEImportAddressTable Position = 0x00001F70, Size = 0x00000010, RVA = 0x00003180, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x452A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4D2) + + Section Data [08] PEImportAddressTable Position = 0x00001E00, Size = 0x000000C0, RVA = 0x00003190, VirtualSize = 0x000000C0 + [0] PEImportHintName { Hint = 997, Name = LoadLibraryExA } (RVA = 0x482A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7D2) + [1] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4654, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5FC) + [2] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x466E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x616) + [3] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4682, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x62A) + [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x469E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x646) + [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x46BC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x664) + [6] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x46D0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x678) + [7] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x46E4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x68C) + [8] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x4700, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6A8) + [9] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x471A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6C2) + [10] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4730, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6D8) + [11] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4746, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6EE) + [12] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4760, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x708) + [13] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4776, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x71E) + [14] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x478A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x732) + [15] PEImportHintName { Hint = 1159, Name = RaiseException } (RVA = 0x47B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x75E) + [16] PEImportHintName { Hint = 637, Name = GetLastError } (RVA = 0x47C8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x770) + [17] PEImportHintName { Hint = 717, Name = GetProcAddress } (RVA = 0x4818, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7C0) + [18] PEImportHintName { Hint = 453, Name = FreeLibrary } (RVA = 0x480A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7B2) + [19] PEImportHintName { Hint = 1543, Name = VirtualQuery } (RVA = 0x47FA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7A2) + [20] PEImportHintName { Hint = 1541, Name = VirtualProtect } (RVA = 0x47E8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x790) + [21] PEImportHintName { Hint = 772, Name = GetSystemInfo } (RVA = 0x47D8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x780) + [22] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4640, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5E8) + + + Section Data [01] PEStreamSectionData Position = 0x00002050, Size = 0x00000140, RVA = 0x00003250, VirtualSize = 0x00000140 + + Section Data [02] PELoadConfigDirectory64 Position = 0x00002190, Size = 0x00000140, RVA = 0x00003390, VirtualSize = 0x00000140 + Size = 0x140 + TimeDateStamp = 0x0 + MajorVersion = 0 + MinorVersion = 0 + GlobalFlagsClear = 0x0 + GlobalFlagsSet = 0x0 + CriticalSectionDefaultTimeout = 0x0 + DeCommitFreeBlockThreshold = 0x0 + DeCommitTotalFreeThreshold = 0x0 + LockPrefixTable = 0x0x0 + MaximumAllocationSize = 0x0 + VirtualMemoryThreshold = 0x0 + ProcessAffinityMask = 0x0 + ProcessHeapFlags = 0x0 + CSDVersion = 0 + DependentLoadFlags = 0x0 + EditList = 0x0 + SecurityCookie = 0x140005000 + SEHandlerTable = 0x0 + SEHandlerCount = 0x0 + GuardCFCheckFunctionPointer = 0x140003250 + GuardCFDispatchFunctionPointer = 0x140003260 + GuardCFFunctionTable = 0x0 + GuardCFFunctionCount = 0x0 + GuardFlags = Instrumented + TableSizeShift = 0x0 + CodeIntegrity.Flags = 0x0 + CodeIntegrity.Catalog = 0x0 + CodeIntegrity.CatalogOffset = 0x0 + CodeIntegrity.Reserved = 0x0 + GuardAddressTakenIatEntryTable = 0x0 + GuardAddressTakenIatEntryCount = 0x0 + GuardLongJumpTargetTable = 0x0 + GuardLongJumpTargetCount = 0x0 + DynamicValueRelocTable = 0x0 + CHPEMetadataPointer = 0x0 + GuardRFFailureRoutine = 0x0 + GuardRFFailureRoutineFunctionPointer = 0x0 + DynamicValueRelocTableOffset = 0x0 + DynamicValueRelocTableSection = 0 + Reserved2 = 0 + GuardRFVerifyStackPointerFunctionPointer = 0x0 + HotPatchTableOffset = 0x0 + Reserved3 = 0x0 + EnclaveConfigurationPointer = 0x0 + VolatileMetadataPointer = 0x140003580 + GuardEHContinuationTable = 0x0 + GuardEHContinuationCount = 0x0 + GuardXFGCheckFunctionPointer = 0x140003258 + GuardXFGDispatchFunctionPointer = 0x140003268 + GuardXFGTableDispatchFunctionPointer = 0x140003270 + CastGuardOsDeterminedFailureMode = 0x140003278 + GuardMemcpyFunctionPointer = 0x140003280 + + Section Data [03] PEStreamSectionData Position = 0x000022D0, Size = 0x00000010, RVA = 0x000034D0, VirtualSize = 0x00000010 + + Section Data [04] PEDebugDirectory Position = 0x000022E0, Size = 0x00000070, RVA = 0x000034E0, VirtualSize = 0x00000070 + [0] Type = CodeView, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x00003618 (PEDebugSectionDataRSDS[6] .rdata + [1] Type = VCFeature, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x0000368C (PEDebugStreamSectionData[8] .rdata + [2] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x000036A0 (PEDebugStreamSectionData[9] .rdata + [3] Type = ILTCG, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = null + + Section Data [05] PEStreamSectionData Position = 0x00002350, Size = 0x000000C8, RVA = 0x00003550, VirtualSize = 0x000000C8 + + Section Data [06] PEDebugSectionDataRSDS Position = 0x00002418, Size = 0x00000072, RVA = 0x00003618, VirtualSize = 0x00000072 + Debug Section Data (RSDS) + Guid = ffed6f99-5708-452c-a889-ff343b6ce898 + Age = 6 + PdbPath = {SolutionDirectory}native\Win64\NativeProjects\x64\Release\NativeConsole2Win64.pdb + + Section Data [07] PEStreamSectionData Position = 0x0000248A, Size = 0x00000002, RVA = 0x0000368A, VirtualSize = 0x00000002 + + Section Data [08] PEDebugStreamSectionData Position = 0x0000248C, Size = 0x00000014, RVA = 0x0000368C, VirtualSize = 0x00000014 + + Section Data [09] PEDebugStreamSectionData Position = 0x000024A0, Size = 0x000002FC, RVA = 0x000036A0, VirtualSize = 0x000002FC + + Section Data [10] PEStreamSectionData Position = 0x0000279C, Size = 0x0000020C, RVA = 0x0000399C, VirtualSize = 0x0000020C + + Section Data [11] PEDelayImportDirectory Position = 0x000029A8, Size = 0x00000040, RVA = 0x00003BA8, VirtualSize = 0x00000040 + [0] DllName = NativeLibraryWin64.dll, RVA = 0x32E0 + [0] Attributes = 1 + [0] DelayImportAddressTable RVA = 0x00005078 (PEBoundImportAddressTable64[1] .data + [0] DelayImportNameTable RVA = 0x00003BE8 (PEImportLookupTable[12] .rdata + [0] BoundImportAddressTable RVA = 0x00003C20 (PEBoundImportAddressTable64[14] .rdata + [0] UnloadDelayInformationTable null + + + Section Data [12] PEImportLookupTable Position = 0x000029E8, Size = 0x00000018, RVA = 0x00003BE8, VirtualSize = 0x00000018 + [0] PEImportHintName { Hint = 0, Name = AnotherFunction } (RVA = 0x3C0E, PEStreamSectionData { RVA = 0x3C00, VirtualSize = 0x20, Position = 0x2A00, Size = 0x20 }, Offset = 0xE) + [1] PEImportHintName { Hint = 1, Name = HelloWorld } (RVA = 0x3C00, PEStreamSectionData { RVA = 0x3C00, VirtualSize = 0x20, Position = 0x2A00, Size = 0x20 }, Offset = 0x0) + + Section Data [13] PEStreamSectionData Position = 0x00002A00, Size = 0x00000020, RVA = 0x00003C00, VirtualSize = 0x00000020 + + Section Data [14] PEBoundImportAddressTable64 Position = 0x00002A20, Size = 0x00000018, RVA = 0x00003C20, VirtualSize = 0x00000018 + [0] VA = 0x0 + [1] VA = 0x0 + + Section Data [15] PEStreamSectionData Position = 0x00002A38, Size = 0x00000104, RVA = 0x00003C38, VirtualSize = 0x00000104 + + Section Data [16] PEImportDirectory Position = 0x00002B3C, Size = 0x000000C8, RVA = 0x00003D3C, VirtualSize = 0x000000C8 + [0] ImportDllNameLink = MSVCP140.dll (RVA = 0x4338, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x2E0) + [0] ImportAddressTable = RVA = 0x00003000 (PEImportAddressTable[0] + [0] ImportLookupTable = RVA = 0x00003EC8 (PEImportLookupTable[19] .rdata + + [1] ImportDllNameLink = VCRUNTIME140_1.dll (RVA = 0x43C4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x36C) + [1] ImportAddressTable = RVA = 0x00003068 (PEImportAddressTable[1] + [1] ImportLookupTable = RVA = 0x00003F68 (PEImportLookupTable[21] .rdata + + [2] ImportDllNameLink = VCRUNTIME140.dll (RVA = 0x43D8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x380) + [2] ImportAddressTable = RVA = 0x00003078 (PEImportAddressTable[2] + [2] ImportLookupTable = RVA = 0x00003F30 (PEImportLookupTable[20] .rdata + + [3] ImportDllNameLink = api-ms-win-crt-runtime-l1-1-0.dll (RVA = 0x459C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x544) + [3] ImportAddressTable = RVA = 0x000030B0 (PEImportAddressTable[3] + [3] ImportLookupTable = RVA = 0x00003FA8 (PEImportLookupTable[25] .rdata + + [4] ImportDllNameLink = api-ms-win-crt-math-l1-1-0.dll (RVA = 0x45BE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x566) + [4] ImportAddressTable = RVA = 0x00003148 (PEImportAddressTable[4] + [4] ImportLookupTable = RVA = 0x00003F98 (PEImportLookupTable[24] .rdata + + [5] ImportDllNameLink = api-ms-win-crt-stdio-l1-1-0.dll (RVA = 0x45DE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x586) + [5] ImportAddressTable = RVA = 0x00003158 (PEImportAddressTable[5] + [5] ImportLookupTable = RVA = 0x00004040 (PEImportLookupTable[26] .rdata + + [6] ImportDllNameLink = api-ms-win-crt-locale-l1-1-0.dll (RVA = 0x45FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5A6) + [6] ImportAddressTable = RVA = 0x00003170 (PEImportAddressTable[6] + [6] ImportLookupTable = RVA = 0x00003F88 (PEImportLookupTable[23] .rdata + + [7] ImportDllNameLink = api-ms-win-crt-heap-l1-1-0.dll (RVA = 0x4620, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5C8) + [7] ImportAddressTable = RVA = 0x00003180 (PEImportAddressTable[7] + [7] ImportLookupTable = RVA = 0x00003F78 (PEImportLookupTable[22] .rdata + + [8] ImportDllNameLink = KERNEL32.dll (RVA = 0x479E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x746) + [8] ImportAddressTable = RVA = 0x00003190 (PEImportAddressTable[8] + [8] ImportLookupTable = RVA = 0x00003E08 (PEImportLookupTable[18] .rdata + + + Section Data [17] PEStreamSectionData Position = 0x00002C04, Size = 0x00000004, RVA = 0x00003E04, VirtualSize = 0x00000004 + + Section Data [18] PEImportLookupTable Position = 0x00002C08, Size = 0x000000C0, RVA = 0x00003E08, VirtualSize = 0x000000C0 + [0] PEImportHintName { Hint = 997, Name = LoadLibraryExA } (RVA = 0x482A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7D2) + [1] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4654, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5FC) + [2] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x466E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x616) + [3] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4682, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x62A) + [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x469E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x646) + [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x46BC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x664) + [6] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x46D0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x678) + [7] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x46E4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x68C) + [8] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x4700, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6A8) + [9] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x471A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6C2) + [10] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4730, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6D8) + [11] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4746, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6EE) + [12] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4760, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x708) + [13] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4776, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x71E) + [14] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x478A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x732) + [15] PEImportHintName { Hint = 1159, Name = RaiseException } (RVA = 0x47B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x75E) + [16] PEImportHintName { Hint = 637, Name = GetLastError } (RVA = 0x47C8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x770) + [17] PEImportHintName { Hint = 717, Name = GetProcAddress } (RVA = 0x4818, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7C0) + [18] PEImportHintName { Hint = 453, Name = FreeLibrary } (RVA = 0x480A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7B2) + [19] PEImportHintName { Hint = 1543, Name = VirtualQuery } (RVA = 0x47FA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7A2) + [20] PEImportHintName { Hint = 1541, Name = VirtualProtect } (RVA = 0x47E8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x790) + [21] PEImportHintName { Hint = 772, Name = GetSystemInfo } (RVA = 0x47D8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x780) + [22] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4640, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5E8) + + Section Data [19] PEImportLookupTable Position = 0x00002CC8, Size = 0x00000068, RVA = 0x00003EC8, VirtualSize = 0x00000068 + [0] PEImportHintName { Hint = 255, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z } (RVA = 0x42D6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x27E) + [1] PEImportHintName { Hint = 262, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@P6AAEAV01@AEAV01@@Z@Z } (RVA = 0x4282, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x22A) + [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x4242, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x1EA) + [3] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x41FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x1A6) + [4] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x41C0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x168) + [5] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x417E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x126) + [6] PEImportHintName { Hint = 1121, Name = ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@D@Z } (RVA = 0x413A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0xE2) + [7] PEImportHintName { Hint = 1332, Name = ?widen@?$basic_ios@DU?$char_traits@D@std@@@std@@QEBADD@Z } (RVA = 0x40FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0xA6) + [8] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x40B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5E) + [9] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x4094, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3C) + [10] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x4318, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x2C0) + [11] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x4058, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x0) + + Section Data [20] PEImportLookupTable Position = 0x00002D30, Size = 0x00000038, RVA = 0x00003F30, VirtualSize = 0x00000038 + [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x47AC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x754) + [1] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x439C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x344) + [2] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x43BA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x362) + [3] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x436E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x316) + [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x4386, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x32E) + [5] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x435C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x304) + + Section Data [21] PEImportLookupTable Position = 0x00002D68, Size = 0x00000010, RVA = 0x00003F68, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x4346, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x2EE) + + Section Data [22] PEImportLookupTable Position = 0x00002D78, Size = 0x00000010, RVA = 0x00003F78, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x452A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4D2) + + Section Data [23] PEImportLookupTable Position = 0x00002D88, Size = 0x00000010, RVA = 0x00003F88, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x4514, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4BC) + + Section Data [24] PEImportLookupTable Position = 0x00002D98, Size = 0x00000010, RVA = 0x00003F98, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x440C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3B4) + + Section Data [25] PEImportLookupTable Position = 0x00002DA8, Size = 0x00000098, RVA = 0x00003FA8, VirtualSize = 0x00000098 + [0] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x44C4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x46C) + [1] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4566, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x50E) + [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x454A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4F2) + [3] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x44A0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x448) + [4] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x448A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x432) + [5] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x447E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x426) + [6] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x445C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x404) + [7] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x443A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3E2) + [8] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x4420, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3C8) + [9] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x44D2, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x47A) + [10] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x43FC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3A4) + [11] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x43EA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x392) + [12] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4590, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x538) + [13] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x4498, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x440) + [14] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4582, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x52A) + [15] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x44B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x45E) + [16] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x44E6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x48E) + [17] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x44DC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x484) + + Section Data [26] PEImportLookupTable Position = 0x00002E40, Size = 0x00000018, RVA = 0x00004040, VirtualSize = 0x00000018 + [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x453A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4E2) + [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x44A8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x450) + + Section Data [27] PEStreamSectionData Position = 0x00002E58, Size = 0x000009A8, RVA = 0x00004058, VirtualSize = 0x000009A8 + + + [02] .data PESection Position = 0x00003800, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x000006D0, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) + + Section Data [00] PEStreamSectionData Position = 0x00003800, Size = 0x00000078, RVA = 0x00005000, VirtualSize = 0x00000078 + + Section Data [01] PEBoundImportAddressTable64 Position = 0x00003878, Size = 0x00000018, RVA = 0x00005078, VirtualSize = 0x00000018 + [0] VA = 0x14000133E + [1] VA = 0x140001332 + + Section Data [02] PEStreamSectionData Position = 0x00003890, Size = 0x00000170, RVA = 0x00005090, VirtualSize = 0x00000170 + + + [03] .pdata PESection Position = 0x00003A00, Size = 0x00000400, RVA = 0x00006000, VirtualSize = 0x00000210, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + Section Data [00] PEExceptionDirectory Position = 0x00003A00, Size = 0x00000210, RVA = 0x00006000, VirtualSize = 0x00000210 + [0] Begin = RVA = 0x1000, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x0 + [0] End = RVA = 0x104B, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x4B + [0] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 + + [1] Begin = RVA = 0x1050, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x50 + [1] End = RVA = 0x108E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x8E + [1] UnwindInfoAddress = RVA = 0x39C8, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x2C + + [2] Begin = RVA = 0x1090, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x90 + [2] End = RVA = 0x10B4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xB4 + [2] UnwindInfoAddress = RVA = 0x39EC, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x50 + + [3] Begin = RVA = 0x10C0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xC0 + [3] End = RVA = 0x1272, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x272 + [3] UnwindInfoAddress = RVA = 0x3A04, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x68 + + [4] Begin = RVA = 0x1280, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x280 + [4] End = RVA = 0x12B9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x2B9 + [4] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 + + [5] Begin = RVA = 0x12B9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x2B9 + [5] End = RVA = 0x1330, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x330 + [5] UnwindInfoAddress = RVA = 0x3A64, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xC8 + + [6] Begin = RVA = 0x1360, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x360 + [6] End = RVA = 0x137E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x37E + [6] UnwindInfoAddress = RVA = 0x3A70, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD4 + + [7] Begin = RVA = 0x1380, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x380 + [7] End = RVA = 0x1436, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x436 + [7] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 + + [8] Begin = RVA = 0x1438, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x438 + [8] End = RVA = 0x1448, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x448 + [8] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 + + [9] Begin = RVA = 0x1448, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x448 + [9] End = RVA = 0x1461, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x461 + [9] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 + + [10] Begin = RVA = 0x1464, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x464 + [10] End = RVA = 0x15E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x5E0 + [10] UnwindInfoAddress = RVA = 0x3A7C, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xE0 + + [11] Begin = RVA = 0x15E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x5E0 + [11] End = RVA = 0x15F2, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x5F2 + [11] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 + + [12] Begin = RVA = 0x15F4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x5F4 + [12] End = RVA = 0x1628, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x628 + [12] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 + + [13] Begin = RVA = 0x1628, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x628 + [13] End = RVA = 0x16FB, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x6FB + [13] UnwindInfoAddress = RVA = 0x3ABC, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x120 + + [14] Begin = RVA = 0x16FC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x6FC + [14] End = RVA = 0x176D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x76D + [14] UnwindInfoAddress = RVA = 0x3AC4, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x128 + + [15] Begin = RVA = 0x1770, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x770 + [15] End = RVA = 0x17A9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x7A9 + [15] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 + + [16] Begin = RVA = 0x17AC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x7AC + [16] End = RVA = 0x17E6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x7E6 + [16] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 + + [17] Begin = RVA = 0x17E8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x7E8 + [17] End = RVA = 0x1873, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x873 + [17] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 + + [18] Begin = RVA = 0x1874, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x874 + [18] End = RVA = 0x190C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x90C + [18] UnwindInfoAddress = RVA = 0x3AD0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x134 + + [19] Begin = RVA = 0x190C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x90C + [19] End = RVA = 0x1930, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x930 + [19] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 + + [20] Begin = RVA = 0x1930, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x930 + [20] End = RVA = 0x1959, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x959 + [20] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 + + [21] Begin = RVA = 0x195C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x95C + [21] End = RVA = 0x1996, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x996 + [21] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 + + [22] Begin = RVA = 0x1998, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x998 + [22] End = RVA = 0x19AF, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x9AF + [22] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 + + [23] Begin = RVA = 0x19B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x9B0 + [23] End = RVA = 0x1A5C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xA5C + [23] UnwindInfoAddress = RVA = 0x3AF8, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x15C + + [24] Begin = RVA = 0x1A98, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xA98 + [24] End = RVA = 0x1AB3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xAB3 + [24] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 + + [25] Begin = RVA = 0x1AD8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xAD8 + [25] End = RVA = 0x1C20, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xC20 + [25] UnwindInfoAddress = RVA = 0x3B04, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x168 + + [26] Begin = RVA = 0x1C28, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xC28 + [26] End = RVA = 0x1C79, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xC79 + [26] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 + + [27] Begin = RVA = 0x1C8C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xC8C + [27] End = RVA = 0x1CE7, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xCE7 + [27] UnwindInfoAddress = RVA = 0x3B14, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x178 + + [28] Begin = RVA = 0x1CE8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xCE8 + [28] End = RVA = 0x1D24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xD24 + [28] UnwindInfoAddress = RVA = 0x3B14, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x178 + + [29] Begin = RVA = 0x1D24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xD24 + [29] End = RVA = 0x1D60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xD60 + [29] UnwindInfoAddress = RVA = 0x3B14, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x178 + + [30] Begin = RVA = 0x1D60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xD60 + [30] End = RVA = 0x202A, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x102A + [30] UnwindInfoAddress = RVA = 0x3B20, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x184 + + [31] Begin = RVA = 0x20F0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x10F0 + [31] End = RVA = 0x21DB, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x11DB + [31] UnwindInfoAddress = RVA = 0x3B94, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1F8 + + [32] Begin = RVA = 0x21DC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x11DC + [32] End = RVA = 0x2296, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1296 + [32] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 + + [33] Begin = RVA = 0x2298, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1298 + [33] End = RVA = 0x2336, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1336 + [33] UnwindInfoAddress = RVA = 0x3B14, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x178 + + [34] Begin = RVA = 0x2338, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1338 + [34] End = RVA = 0x23D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x13D0 + [34] UnwindInfoAddress = RVA = 0x3B54, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1B8 + + [35] Begin = RVA = 0x23D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x13D0 + [35] End = RVA = 0x246A, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x146A + [35] UnwindInfoAddress = RVA = 0x3B44, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1A8 + + [36] Begin = RVA = 0x246C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x146C + [36] End = RVA = 0x2563, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1563 + [36] UnwindInfoAddress = RVA = 0x3B60, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1C4 + + [37] Begin = RVA = 0x2564, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1564 + [37] End = RVA = 0x260D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x160D + [37] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 + + [38] Begin = RVA = 0x2610, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1610 + [38] End = RVA = 0x290C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x190C + [38] UnwindInfoAddress = RVA = 0x3B78, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1DC + + [39] Begin = RVA = 0x2930, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1930 + [39] End = RVA = 0x2932, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1932 + [39] UnwindInfoAddress = RVA = 0x3B38, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x19C + + [40] Begin = RVA = 0x2950, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1950 + [40] End = RVA = 0x2956, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1956 + [40] UnwindInfoAddress = RVA = 0x3B40, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1A4 + + [41] Begin = RVA = 0x2978, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1978 + [41] End = RVA = 0x29B3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x19B3 + [41] UnwindInfoAddress = RVA = 0x3A5C, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xC0 + + [42] Begin = RVA = 0x29B3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x19B3 + [42] End = RVA = 0x29D1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x19D1 + [42] UnwindInfoAddress = RVA = 0x3AB4, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x118 + + [43] Begin = RVA = 0x29D1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x19D1 + [43] End = RVA = 0x29E9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x19E9 + [43] UnwindInfoAddress = RVA = 0x3AF0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x154 + + + Section Data [01] PEStreamSectionData Position = 0x00003C10, Size = 0x000001F0, RVA = 0x00006210, VirtualSize = 0x000001F0 + + + [04] .rsrc PESection Position = 0x00003E00, Size = 0x00000200, RVA = 0x00007000, VirtualSize = 0x000001E0, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + Section Data [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) } + Section Data [00] PEStreamSectionData Position = 0x00003FD8, Size = 0x00000008, RVA = 0x000071D8, VirtualSize = 0x00000008 + + + Section Data [01] PEStreamSectionData Position = 0x00003FE0, Size = 0x00000020, RVA = 0x000071E0, VirtualSize = 0x00000020 + + + [05] .reloc PESection Position = 0x00004000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x0000003C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) + + Section Data [00] PEBaseRelocationDirectory Position = 0x00004000, Size = 0x0000003C, RVA = 0x00008000, VirtualSize = 0x0000003C + Block 0x3000 Relocations[20] + Dir64 Offset = 0x0250, RVA = 0x3250 (0x0000000140001A84), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0258, RVA = 0x3258 (0x0000000140001A84), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0260, RVA = 0x3260 (0x0000000140002930), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0268, RVA = 0x3268 (0x0000000140002950), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0270, RVA = 0x3270 (0x0000000140002950), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0280, RVA = 0x3280 (0x000000014000290C), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0290, RVA = 0x3290 (0x0000000140001448), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x02A8, RVA = 0x32A8 (0x0000000140001380), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x02B0, RVA = 0x32B0 (0x0000000140001438), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x02F8, RVA = 0x32F8 (0x00000001400050A0), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0300, RVA = 0x3300 (0x0000000140005140), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x03E8, RVA = 0x33E8 (0x0000000140005000), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x0400, RVA = 0x3400 (0x0000000140003250), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x0408, RVA = 0x3408 (0x0000000140003260), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x0490, RVA = 0x3490 (0x0000000140003580), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x04A8, RVA = 0x34A8 (0x0000000140003258), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x04B0, RVA = 0x34B0 (0x0000000140003268), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x04B8, RVA = 0x34B8 (0x0000000140003270), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x04C0, RVA = 0x34C0 (0x0000000140003278), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x04C8, RVA = 0x34C8 (0x0000000140003280), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } + Block 0x5000 Relocations[2] + Dir64 Offset = 0x0078, RVA = 0x5078 (0x000000014000133E), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] .data } + Dir64 Offset = 0x0080, RVA = 0x5080 (0x0000000140001332), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] .data } + Section Data [00] PEStreamSectionData Position = 0x0000402C, Size = 0x00000010, RVA = 0x0000802C, VirtualSize = 0x00000010 + + + Section Data [01] PEStreamSectionData Position = 0x0000403C, Size = 0x000001C4, RVA = 0x0000803C, VirtualSize = 0x000001C4 + + 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 new file mode 100644 index 0000000..65fa9f4 --- /dev/null +++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsoleWin64.exe.verified.txt @@ -0,0 +1,547 @@ +DOS Header + Magic = DOS + ByteCountOnLastPage = 0x90 + PageCount = 0x3 + RelocationCount = 0x0 + SizeOfParagraphsHeader = 0x4 + MinExtraParagraphs = 0x0 + MaxExtraParagraphs = 0xFFFF + InitialSSValue = 0x0 + InitialSPValue = 0xB8 + Checksum = 0x0 + InitialIPValue = 0x0 + InitialCSValue = 0x0 + FileAddressRelocationTable = 0x40 + OverlayNumber = 0x0 + Reserved = 0x0, 0x0, 0x0, 0x0 + OEMIdentifier = 0x0 + OEMInformation = 0x0 + Reserved2 = 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + FileAddressPEHeader = 0xF8 + +DOS Stub + DosStub = 64 bytes + +COFF Header + Machine = Amd64 + NumberOfSections = 6 + TimeDateStamp = 1727077439 + PointerToSymbolTable = 0x0 + NumberOfSymbols = 0 + SizeOfOptionalHeader = 240 + Characteristics = ExecutableImage, LargeAddressAware + +Optional Header + Magic = PE32Plus + MajorLinkerVersion = 14 + MinorLinkerVersion = 41 + SizeOfCode = 0x1200 + SizeOfInitializedData = 0x2200 + SizeOfUninitializedData = 0x0 + AddressOfEntryPoint = 0x14E0 + BaseOfCode = 0x1000 + BaseOfData = 0x0 + ImageBase = 0x140000000 + SectionAlignment = 0x1000 + FileAlignment = 0x200 + MajorOperatingSystemVersion = 6 + MinorOperatingSystemVersion = 0 + MajorImageVersion = 0 + MinorImageVersion = 0 + MajorSubsystemVersion = 6 + MinorSubsystemVersion = 0 + Win32VersionValue = 0x0 + SizeOfImage = 0x9000 + SizeOfHeaders = 0x400 + CheckSum = 0x0 + Subsystem = WindowsCui + DllCharacteristics = HighEntropyVirtualAddressSpace, DynamicBase, NxCompatible, TerminalServerAware + SizeOfStackReserve = 0x100000 + SizeOfStackCommit = 0x1000 + SizeOfHeapReserve = 0x100000 + SizeOfHeapCommit = 0x1000 + LoaderFlags = 0x0 + NumberOfRvaAndSizes = 0x10 + +Data Directories + [00] = null + [01] = PEImportDirectory Position = 0x00001F84, Size = 0x000000C8, RVA = 0x00003984, VirtualSize = 0x000000C8 + [02] = PEResourceDirectory Position = 0x00002E00, Size = 0x000001E0, RVA = 0x00007000, VirtualSize = 0x000001E0 + [03] = PEExceptionDirectory Position = 0x00002C00, Size = 0x00000198, RVA = 0x00006000, VirtualSize = 0x00000198 + [04] = null + [05] = PEBaseRelocationDirectory Position = 0x00003000, Size = 0x00000030, RVA = 0x00008000, VirtualSize = 0x00000030 + [06] = PEDebugDirectory Position = 0x000019F0, Size = 0x00000070, RVA = 0x000033F0, VirtualSize = 0x00000070 + [07] = null + [08] = null + [09] = null + [10] = PELoadConfigDirectory64 Position = 0x000018B0, Size = 0x00000140, RVA = 0x000032B0, VirtualSize = 0x00000140 + [11] = null + [12] = PEImportAddressTableDirectory Position = 0x00001600, Size = 0x000001F0, RVA = 0x00003000, VirtualSize = 0x000001F0 + [13] = null + [14] = null + +Sections + [00] .text PESection Position = 0x00000400, Size = 0x00001200, RVA = 0x00001000, VirtualSize = 0x000010C9, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) + + Section Data [00] PEStreamSectionData Position = 0x00000400, Size = 0x00001200, RVA = 0x00001000, VirtualSize = 0x00001200 + + + [01] .rdata PESection Position = 0x00001600, Size = 0x00001400, RVA = 0x00003000, VirtualSize = 0x00001288, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + Section Data [00] PEImportAddressTableDirectory Position = 0x00001600, Size = 0x000001F0, RVA = 0x00003000, VirtualSize = 0x000001F0 + Section Data [00] PEImportAddressTable Position = 0x00001680, Size = 0x00000048, RVA = 0x00003000, VirtualSize = 0x00000048 + [0] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x3C40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x0) + [1] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x3DA8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x168) + [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x3D68, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x128) + [3] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x3D20, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0xE0) + [4] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x3CDC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x9C) + [5] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x3C9E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5E) + [6] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x3C7C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3C) + [7] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x3DEA, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1AA) + + Section Data [01] PEImportAddressTable Position = 0x00001700, Size = 0x00000010, RVA = 0x00003048, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x3E18, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1D8) + + Section Data [02] PEImportAddressTable Position = 0x000016C8, Size = 0x00000038, RVA = 0x00003058, VirtualSize = 0x00000038 + [0] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x3E40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x200) + [1] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x3E2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1EE) + [2] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x427E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x63E) + [3] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x3E6E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x22E) + [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x3E58, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x218) + [5] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x3E8C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x24C) + + Section Data [03] PEImportAddressTable Position = 0x00001740, Size = 0x00000098, RVA = 0x00003090, VirtualSize = 0x00000098 + [0] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4062, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x422) + [1] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4054, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x414) + [2] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4038, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3F8) + [3] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x401C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3DC) + [4] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x3FAE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x36E) + [5] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x3FA4, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x364) + [6] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x3F88, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x348) + [7] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x3EBC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x27C) + [8] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x3F72, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x332) + [9] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x3F6A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x32A) + [10] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x3F5C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x31C) + [11] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x3F50, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x310) + [12] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x3F2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2EE) + [13] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x3F0C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2CC) + [14] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x3EF2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2B2) + [15] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x3FB8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x378) + [16] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x3ECE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x28E) + [17] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x3F96, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x356) + + Section Data [04] PEImportAddressTable Position = 0x00001730, Size = 0x00000010, RVA = 0x00003128, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x3EDE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x29E) + + Section Data [05] PEImportAddressTable Position = 0x000017D8, Size = 0x00000018, RVA = 0x00003138, VirtualSize = 0x00000018 + [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x400C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3CC) + [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x3F7A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x33A) + + Section Data [06] PEImportAddressTable Position = 0x00001720, Size = 0x00000010, RVA = 0x00003150, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x3FE6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3A6) + + Section Data [07] PEImportAddressTable Position = 0x00001710, Size = 0x00000010, RVA = 0x00003160, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x3FFC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3BC) + + Section Data [08] PEImportAddressTable Position = 0x00001600, Size = 0x00000080, RVA = 0x00003170, VirtualSize = 0x00000080 + [0] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4112, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x4D2) + [1] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x41D2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x592) + [2] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x41EC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5AC) + [3] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4202, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5C2) + [4] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4218, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5D8) + [5] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4232, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5F2) + [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4248, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x608) + [7] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x425C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x61C) + [8] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x41A2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x562) + [9] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x418E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x54E) + [10] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x4170, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x530) + [11] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4154, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x514) + [12] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x4140, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x500) + [13] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4126, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x4E6) + [14] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x41B6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x576) + + + Section Data [01] PEStreamSectionData Position = 0x000017F0, Size = 0x000000C0, RVA = 0x000031F0, VirtualSize = 0x000000C0 + + Section Data [02] PELoadConfigDirectory64 Position = 0x000018B0, Size = 0x00000140, RVA = 0x000032B0, VirtualSize = 0x00000140 + Size = 0x140 + TimeDateStamp = 0x0 + MajorVersion = 0 + MinorVersion = 0 + GlobalFlagsClear = 0x0 + GlobalFlagsSet = 0x0 + CriticalSectionDefaultTimeout = 0x0 + DeCommitFreeBlockThreshold = 0x0 + DeCommitTotalFreeThreshold = 0x0 + LockPrefixTable = 0x0x0 + MaximumAllocationSize = 0x0 + VirtualMemoryThreshold = 0x0 + ProcessAffinityMask = 0x0 + ProcessHeapFlags = 0x0 + CSDVersion = 0 + DependentLoadFlags = 0x0 + EditList = 0x0 + SecurityCookie = 0x140005000 + SEHandlerTable = 0x0 + SEHandlerCount = 0x0 + GuardCFCheckFunctionPointer = 0x1400031F0 + GuardCFDispatchFunctionPointer = 0x140003200 + GuardCFFunctionTable = 0x0 + GuardCFFunctionCount = 0x0 + GuardFlags = Instrumented + TableSizeShift = 0x0 + CodeIntegrity.Flags = 0x0 + CodeIntegrity.Catalog = 0x0 + CodeIntegrity.CatalogOffset = 0x0 + CodeIntegrity.Reserved = 0x0 + GuardAddressTakenIatEntryTable = 0x0 + GuardAddressTakenIatEntryCount = 0x0 + GuardLongJumpTargetTable = 0x0 + GuardLongJumpTargetCount = 0x0 + DynamicValueRelocTable = 0x0 + CHPEMetadataPointer = 0x0 + GuardRFFailureRoutine = 0x0 + GuardRFFailureRoutineFunctionPointer = 0x0 + DynamicValueRelocTableOffset = 0x0 + DynamicValueRelocTableSection = 0 + Reserved2 = 0 + GuardRFVerifyStackPointerFunctionPointer = 0x0 + HotPatchTableOffset = 0x0 + Reserved3 = 0x0 + EnclaveConfigurationPointer = 0x0 + VolatileMetadataPointer = 0x140003480 + GuardEHContinuationTable = 0x0 + GuardEHContinuationCount = 0x0 + GuardXFGCheckFunctionPointer = 0x1400031F8 + GuardXFGDispatchFunctionPointer = 0x140003208 + GuardXFGTableDispatchFunctionPointer = 0x140003210 + CastGuardOsDeterminedFailureMode = 0x140003218 + GuardMemcpyFunctionPointer = 0x140003220 + + Section Data [03] PEDebugDirectory Position = 0x000019F0, Size = 0x00000070, RVA = 0x000033F0, VirtualSize = 0x00000070 + [0] Type = CodeView, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F11C3F, Data = RVA = 0x000034DC (PEDebugSectionDataRSDS[5] .rdata + [1] Type = VCFeature, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F11C3F, Data = RVA = 0x00003550 (PEDebugStreamSectionData[7] .rdata + [2] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F11C3F, Data = RVA = 0x00003564 (PEDebugStreamSectionData[8] .rdata + [3] Type = ILTCG, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F11C3F, Data = null + + Section Data [04] PEStreamSectionData Position = 0x00001A60, Size = 0x0000007C, RVA = 0x00003460, VirtualSize = 0x0000007C + + Section Data [05] PEDebugSectionDataRSDS Position = 0x00001ADC, Size = 0x00000071, RVA = 0x000034DC, VirtualSize = 0x00000071 + Debug Section Data (RSDS) + Guid = a28d7ba2-048a-4315-9bbe-0edbca526f59 + Age = 2 + PdbPath = {SolutionDirectory}native\Win64\NativeProjects\x64\Release\NativeConsoleWin64.pdb + + Section Data [06] PEStreamSectionData Position = 0x00001B4D, Size = 0x00000003, RVA = 0x0000354D, VirtualSize = 0x00000003 + + Section Data [07] PEDebugStreamSectionData Position = 0x00001B50, Size = 0x00000014, RVA = 0x00003550, VirtualSize = 0x00000014 + + Section Data [08] PEDebugStreamSectionData Position = 0x00001B64, Size = 0x00000284, RVA = 0x00003564, VirtualSize = 0x00000284 + + Section Data [09] PEStreamSectionData Position = 0x00001DE8, Size = 0x0000019C, RVA = 0x000037E8, VirtualSize = 0x0000019C + + Section Data [10] PEImportDirectory Position = 0x00001F84, Size = 0x000000C8, RVA = 0x00003984, VirtualSize = 0x000000C8 + [0] ImportDllNameLink = MSVCP140.dll (RVA = 0x3E0A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1CA) + [0] ImportAddressTable = RVA = 0x00003000 (PEImportAddressTable[0] + [0] ImportLookupTable = RVA = 0x00003AD0 (PEImportLookupTable[13] .rdata + + [1] ImportDllNameLink = VCRUNTIME140_1.dll (RVA = 0x3E96, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x256) + [1] ImportAddressTable = RVA = 0x00003048 (PEImportAddressTable[1] + [1] ImportLookupTable = RVA = 0x00003B50 (PEImportLookupTable[15] .rdata + + [2] ImportDllNameLink = VCRUNTIME140.dll (RVA = 0x3EAA, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x26A) + [2] ImportAddressTable = RVA = 0x00003058 (PEImportAddressTable[2] + [2] ImportLookupTable = RVA = 0x00003B18 (PEImportLookupTable[14] .rdata + + [3] ImportDllNameLink = api-ms-win-crt-runtime-l1-1-0.dll (RVA = 0x406E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x42E) + [3] ImportAddressTable = RVA = 0x00003090 (PEImportAddressTable[3] + [3] ImportLookupTable = RVA = 0x00003B90 (PEImportLookupTable[19] .rdata + + [4] ImportDllNameLink = api-ms-win-crt-math-l1-1-0.dll (RVA = 0x4090, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x450) + [4] ImportAddressTable = RVA = 0x00003128 (PEImportAddressTable[4] + [4] ImportLookupTable = RVA = 0x00003B80 (PEImportLookupTable[18] .rdata + + [5] ImportDllNameLink = api-ms-win-crt-stdio-l1-1-0.dll (RVA = 0x40B0, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x470) + [5] ImportAddressTable = RVA = 0x00003138 (PEImportAddressTable[5] + [5] ImportLookupTable = RVA = 0x00003C28 (PEImportLookupTable[20] .rdata + + [6] ImportDllNameLink = api-ms-win-crt-locale-l1-1-0.dll (RVA = 0x40D0, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x490) + [6] ImportAddressTable = RVA = 0x00003150 (PEImportAddressTable[6] + [6] ImportLookupTable = RVA = 0x00003B70 (PEImportLookupTable[17] .rdata + + [7] ImportDllNameLink = api-ms-win-crt-heap-l1-1-0.dll (RVA = 0x40F2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x4B2) + [7] ImportAddressTable = RVA = 0x00003160 (PEImportAddressTable[7] + [7] ImportLookupTable = RVA = 0x00003B60 (PEImportLookupTable[16] .rdata + + [8] ImportDllNameLink = KERNEL32.dll (RVA = 0x4270, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x630) + [8] ImportAddressTable = RVA = 0x00003170 (PEImportAddressTable[8] + [8] ImportLookupTable = RVA = 0x00003A50 (PEImportLookupTable[12] .rdata + + + Section Data [11] PEStreamSectionData Position = 0x0000204C, Size = 0x00000004, RVA = 0x00003A4C, VirtualSize = 0x00000004 + + Section Data [12] PEImportLookupTable Position = 0x00002050, Size = 0x00000080, RVA = 0x00003A50, VirtualSize = 0x00000080 + [0] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4112, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x4D2) + [1] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x41D2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x592) + [2] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x41EC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5AC) + [3] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4202, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5C2) + [4] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4218, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5D8) + [5] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4232, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5F2) + [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4248, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x608) + [7] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x425C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x61C) + [8] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x41A2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x562) + [9] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x418E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x54E) + [10] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x4170, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x530) + [11] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4154, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x514) + [12] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x4140, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x500) + [13] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4126, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x4E6) + [14] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x41B6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x576) + + Section Data [13] PEImportLookupTable Position = 0x000020D0, Size = 0x00000048, RVA = 0x00003AD0, VirtualSize = 0x00000048 + [0] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x3C40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x0) + [1] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x3DA8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x168) + [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x3D68, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x128) + [3] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x3D20, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0xE0) + [4] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x3CDC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x9C) + [5] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x3C9E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5E) + [6] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x3C7C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3C) + [7] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x3DEA, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1AA) + + Section Data [14] PEImportLookupTable Position = 0x00002118, Size = 0x00000038, RVA = 0x00003B18, VirtualSize = 0x00000038 + [0] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x3E40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x200) + [1] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x3E2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1EE) + [2] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x427E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x63E) + [3] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x3E6E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x22E) + [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x3E58, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x218) + [5] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x3E8C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x24C) + + Section Data [15] PEImportLookupTable Position = 0x00002150, Size = 0x00000010, RVA = 0x00003B50, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x3E18, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1D8) + + Section Data [16] PEImportLookupTable Position = 0x00002160, Size = 0x00000010, RVA = 0x00003B60, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x3FFC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3BC) + + Section Data [17] PEImportLookupTable Position = 0x00002170, Size = 0x00000010, RVA = 0x00003B70, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x3FE6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3A6) + + Section Data [18] PEImportLookupTable Position = 0x00002180, Size = 0x00000010, RVA = 0x00003B80, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x3EDE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x29E) + + Section Data [19] PEImportLookupTable Position = 0x00002190, Size = 0x00000098, RVA = 0x00003B90, VirtualSize = 0x00000098 + [0] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4062, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x422) + [1] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4054, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x414) + [2] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4038, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3F8) + [3] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x401C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3DC) + [4] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x3FAE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x36E) + [5] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x3FA4, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x364) + [6] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x3F88, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x348) + [7] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x3EBC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x27C) + [8] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x3F72, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x332) + [9] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x3F6A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x32A) + [10] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x3F5C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x31C) + [11] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x3F50, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x310) + [12] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x3F2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2EE) + [13] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x3F0C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2CC) + [14] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x3EF2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2B2) + [15] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x3FB8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x378) + [16] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x3ECE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x28E) + [17] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x3F96, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x356) + + Section Data [20] PEImportLookupTable Position = 0x00002228, Size = 0x00000018, RVA = 0x00003C28, VirtualSize = 0x00000018 + [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x400C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3CC) + [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x3F7A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x33A) + + Section Data [21] PEStreamSectionData Position = 0x00002240, Size = 0x000007C0, RVA = 0x00003C40, VirtualSize = 0x000007C0 + + + [02] .data PESection Position = 0x00002A00, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x00000680, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) + + Section Data [00] PEStreamSectionData Position = 0x00002A00, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x00000200 + + + [03] .pdata PESection Position = 0x00002C00, Size = 0x00000200, RVA = 0x00006000, VirtualSize = 0x00000198, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + Section Data [00] PEExceptionDirectory Position = 0x00002C00, Size = 0x00000198, RVA = 0x00006000, VirtualSize = 0x00000198 + [0] Begin = RVA = 0x1000, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x0 + [0] End = RVA = 0x1017, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x17 + [0] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 + + [1] Begin = RVA = 0x1020, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x20 + [1] End = RVA = 0x11D2, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1D2 + [1] UnwindInfoAddress = RVA = 0x3810, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x28 + + [2] Begin = RVA = 0x11E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1E0 + [2] End = RVA = 0x121E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x21E + [2] UnwindInfoAddress = RVA = 0x3870, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x88 + + [3] Begin = RVA = 0x1220, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x220 + [3] End = RVA = 0x1244, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x244 + [3] UnwindInfoAddress = RVA = 0x3894, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xAC + + [4] Begin = RVA = 0x1260, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x260 + [4] End = RVA = 0x127E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x27E + [4] UnwindInfoAddress = RVA = 0x38B0, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xC8 + + [5] Begin = RVA = 0x1280, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x280 + [5] End = RVA = 0x1336, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x336 + [5] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC + + [6] Begin = RVA = 0x1338, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x338 + [6] End = RVA = 0x1348, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x348 + [6] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 + + [7] Begin = RVA = 0x1348, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x348 + [7] End = RVA = 0x1361, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x361 + [7] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 + + [8] Begin = RVA = 0x1364, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x364 + [8] End = RVA = 0x14E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x4E0 + [8] UnwindInfoAddress = RVA = 0x38BC, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xD4 + + [9] Begin = RVA = 0x14E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x4E0 + [9] End = RVA = 0x14F2, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x4F2 + [9] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 + + [10] Begin = RVA = 0x14F4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x4F4 + [10] End = RVA = 0x1528, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x528 + [10] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC + + [11] Begin = RVA = 0x1528, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x528 + [11] End = RVA = 0x15FB, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x5FB + [11] UnwindInfoAddress = RVA = 0x38FC, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x114 + + [12] Begin = RVA = 0x15FC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x5FC + [12] End = RVA = 0x166D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x66D + [12] UnwindInfoAddress = RVA = 0x3904, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x11C + + [13] Begin = RVA = 0x1670, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x670 + [13] End = RVA = 0x16A9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x6A9 + [13] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 + + [14] Begin = RVA = 0x16AC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x6AC + [14] End = RVA = 0x16E6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x6E6 + [14] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 + + [15] Begin = RVA = 0x16E8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x6E8 + [15] End = RVA = 0x1773, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x773 + [15] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC + + [16] Begin = RVA = 0x1774, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x774 + [16] End = RVA = 0x180C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x80C + [16] UnwindInfoAddress = RVA = 0x3910, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x128 + + [17] Begin = RVA = 0x180C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x80C + [17] End = RVA = 0x1830, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x830 + [17] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC + + [18] Begin = RVA = 0x1830, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x830 + [18] End = RVA = 0x1859, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x859 + [18] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC + + [19] Begin = RVA = 0x185C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x85C + [19] End = RVA = 0x1896, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x896 + [19] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC + + [20] Begin = RVA = 0x1898, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x898 + [20] End = RVA = 0x18AF, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x8AF + [20] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 + + [21] Begin = RVA = 0x18B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x8B0 + [21] End = RVA = 0x195C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x95C + [21] UnwindInfoAddress = RVA = 0x3938, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x150 + + [22] Begin = RVA = 0x1998, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x998 + [22] End = RVA = 0x19B3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x9B3 + [22] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 + + [23] Begin = RVA = 0x19D8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x9D8 + [23] End = RVA = 0x1B20, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xB20 + [23] UnwindInfoAddress = RVA = 0x3944, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x15C + + [24] Begin = RVA = 0x1B28, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xB28 + [24] End = RVA = 0x1B79, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xB79 + [24] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 + + [25] Begin = RVA = 0x1B8C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xB8C + [25] End = RVA = 0x1BE7, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xBE7 + [25] UnwindInfoAddress = RVA = 0x3954, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x16C + + [26] Begin = RVA = 0x1BE8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xBE8 + [26] End = RVA = 0x1C24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xC24 + [26] UnwindInfoAddress = RVA = 0x3954, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x16C + + [27] Begin = RVA = 0x1C24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xC24 + [27] End = RVA = 0x1C60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xC60 + [27] UnwindInfoAddress = RVA = 0x3954, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x16C + + [28] Begin = RVA = 0x1C60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xC60 + [28] End = RVA = 0x1F2A, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xF2A + [28] UnwindInfoAddress = RVA = 0x3960, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x178 + + [29] Begin = RVA = 0x2010, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1010 + [29] End = RVA = 0x2012, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1012 + [29] UnwindInfoAddress = RVA = 0x3978, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x190 + + [30] Begin = RVA = 0x2030, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1030 + [30] End = RVA = 0x2036, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1036 + [30] UnwindInfoAddress = RVA = 0x3980, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x198 + + [31] Begin = RVA = 0x2058, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1058 + [31] End = RVA = 0x2093, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1093 + [31] UnwindInfoAddress = RVA = 0x3868, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x80 + + [32] Begin = RVA = 0x2093, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1093 + [32] End = RVA = 0x20B1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x10B1 + [32] UnwindInfoAddress = RVA = 0x38F4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x10C + + [33] Begin = RVA = 0x20B1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x10B1 + [33] End = RVA = 0x20C9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x10C9 + [33] UnwindInfoAddress = RVA = 0x3930, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x148 + + + Section Data [01] PEStreamSectionData Position = 0x00002D98, Size = 0x00000068, RVA = 0x00006198, VirtualSize = 0x00000068 + + + [04] .rsrc PESection Position = 0x00002E00, Size = 0x00000200, RVA = 0x00007000, VirtualSize = 0x000001E0, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + Section Data [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) } + Section Data [00] PEStreamSectionData Position = 0x00002FD8, Size = 0x00000008, RVA = 0x000071D8, VirtualSize = 0x00000008 + + + Section Data [01] PEStreamSectionData Position = 0x00002FE0, Size = 0x00000020, RVA = 0x000071E0, VirtualSize = 0x00000020 + + + [05] .reloc PESection Position = 0x00003000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x00000030, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) + + Section Data [00] PEBaseRelocationDirectory Position = 0x00003000, Size = 0x00000030, RVA = 0x00008000, VirtualSize = 0x00000030 + Block 0x3000 Relocations[20] + Dir64 Offset = 0x01F0, RVA = 0x31F0 (0x0000000140001984), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x01F8, RVA = 0x31F8 (0x0000000140001984), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0200, RVA = 0x3200 (0x0000000140002010), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0208, RVA = 0x3208 (0x0000000140002030), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0210, RVA = 0x3210 (0x0000000140002030), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0220, RVA = 0x3220 (0x0000000140001FEE), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0230, RVA = 0x3230 (0x0000000140001348), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0248, RVA = 0x3248 (0x0000000140001280), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0250, RVA = 0x3250 (0x0000000140001338), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0280, RVA = 0x3280 (0x0000000140005080), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0288, RVA = 0x3288 (0x0000000140005120), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0308, RVA = 0x3308 (0x0000000140005000), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x0320, RVA = 0x3320 (0x00000001400031F0), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x0328, RVA = 0x3328 (0x0000000140003200), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x03B0, RVA = 0x33B0 (0x0000000140003480), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x03C8, RVA = 0x33C8 (0x00000001400031F8), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x03D0, RVA = 0x33D0 (0x0000000140003208), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x03D8, RVA = 0x33D8 (0x0000000140003210), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x03E0, RVA = 0x33E0 (0x0000000140003218), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x03E8, RVA = 0x33E8 (0x0000000140003220), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } + Section Data [00] PEStreamSectionData Position = 0x00003028, Size = 0x00000008, RVA = 0x00008028, VirtualSize = 0x00000008 + + + Section Data [01] PEStreamSectionData Position = 0x00003030, Size = 0x000001D0, RVA = 0x00008030, VirtualSize = 0x000001D0 + + 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 new file mode 100644 index 0000000..bdde20e --- /dev/null +++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeLibraryWin64.dll.verified.txt @@ -0,0 +1,468 @@ +DOS Header + Magic = DOS + ByteCountOnLastPage = 0x90 + PageCount = 0x3 + RelocationCount = 0x0 + SizeOfParagraphsHeader = 0x4 + MinExtraParagraphs = 0x0 + MaxExtraParagraphs = 0xFFFF + InitialSSValue = 0x0 + InitialSPValue = 0xB8 + Checksum = 0x0 + InitialIPValue = 0x0 + InitialCSValue = 0x0 + FileAddressRelocationTable = 0x40 + OverlayNumber = 0x0 + Reserved = 0x0, 0x0, 0x0, 0x0 + OEMIdentifier = 0x0 + OEMInformation = 0x0 + Reserved2 = 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + FileAddressPEHeader = 0xF8 + +DOS Stub + DosStub = 64 bytes + +COFF Header + Machine = Amd64 + NumberOfSections = 6 + TimeDateStamp = 1727110446 + PointerToSymbolTable = 0x0 + NumberOfSymbols = 0 + SizeOfOptionalHeader = 240 + Characteristics = ExecutableImage, LargeAddressAware, Dll + +Optional Header + Magic = PE32Plus + MajorLinkerVersion = 14 + MinorLinkerVersion = 41 + SizeOfCode = 0x1000 + SizeOfInitializedData = 0x1C00 + SizeOfUninitializedData = 0x0 + AddressOfEntryPoint = 0x1370 + BaseOfCode = 0x1000 + BaseOfData = 0x0 + ImageBase = 0x180000000 + SectionAlignment = 0x1000 + FileAlignment = 0x200 + MajorOperatingSystemVersion = 6 + MinorOperatingSystemVersion = 0 + MajorImageVersion = 0 + MinorImageVersion = 0 + MajorSubsystemVersion = 6 + MinorSubsystemVersion = 0 + Win32VersionValue = 0x0 + SizeOfImage = 0x7000 + SizeOfHeaders = 0x400 + CheckSum = 0x0 + Subsystem = WindowsGui + DllCharacteristics = HighEntropyVirtualAddressSpace, DynamicBase, NxCompatible + SizeOfStackReserve = 0x100000 + SizeOfStackCommit = 0x1000 + SizeOfHeapReserve = 0x100000 + SizeOfHeapCommit = 0x1000 + LoaderFlags = 0x0 + NumberOfRvaAndSizes = 0x10 + +Data Directories + [00] = PEExportDirectory Position = 0x00001C70, Size = 0x00000070, RVA = 0x00002870, VirtualSize = 0x00000070 + [01] = PEImportDirectory Position = 0x00001CE0, Size = 0x00000050, RVA = 0x000028E0, VirtualSize = 0x00000050 + [02] = PEResourceDirectory Position = 0x00002600, Size = 0x000000F8, RVA = 0x00005000, VirtualSize = 0x000000F8 + [03] = PEExceptionDirectory Position = 0x00002400, Size = 0x000001A4, RVA = 0x00004000, VirtualSize = 0x000001A4 + [04] = null + [05] = PEBaseRelocationDirectory Position = 0x00002800, Size = 0x0000002C, RVA = 0x00006000, VirtualSize = 0x0000002C + [06] = PEDebugDirectory Position = 0x000016D0, Size = 0x00000070, RVA = 0x000022D0, VirtualSize = 0x00000070 + [07] = null + [08] = null + [09] = null + [10] = PELoadConfigDirectory64 Position = 0x00001590, Size = 0x00000140, RVA = 0x00002190, VirtualSize = 0x00000140 + [11] = null + [12] = PEImportAddressTableDirectory Position = 0x00001400, Size = 0x000000E8, RVA = 0x00002000, VirtualSize = 0x000000E8 + [13] = null + [14] = null + +Sections + [00] .text PESection Position = 0x00000400, Size = 0x00001000, RVA = 0x00001000, VirtualSize = 0x00000F18, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) + + Section Data [00] PEStreamSectionData Position = 0x00000400, Size = 0x00001000, RVA = 0x00001000, VirtualSize = 0x00001000 + + + [01] .rdata PESection Position = 0x00001400, Size = 0x00000E00, RVA = 0x00002000, VirtualSize = 0x00000C96, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + Section Data [00] PEImportAddressTableDirectory Position = 0x00001400, Size = 0x000000E8, RVA = 0x00002000, VirtualSize = 0x000000E8 + Section Data [00] PEImportAddressTable Position = 0x00001478, Size = 0x00000028, RVA = 0x00002000, VirtualSize = 0x00000028 + [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x2C8C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x274) + [1] PEImportHintName { Hint = 37, Name = __std_type_info_destroy_list } (RVA = 0x2A30, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x18) + [2] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x2A18, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x0) + [3] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x2A50, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x38) + + Section Data [01] PEImportAddressTable Position = 0x000014A0, Size = 0x00000048, RVA = 0x00002028, VirtualSize = 0x00000048 + [0] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x2B08, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xF0) + [1] PEImportHintName { Hint = 34, Name = _execute_onexit_table } (RVA = 0x2AF0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xD8) + [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x2AD4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xBC) + [3] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x2AB2, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x9A) + [4] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x2A98, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x80) + [5] PEImportHintName { Hint = 63, Name = _seh_filter_dll } (RVA = 0x2A86, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x6E) + [6] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x2A78, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x60) + [7] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x2A6C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x54) + + Section Data [02] PEImportAddressTable Position = 0x00001400, Size = 0x00000078, RVA = 0x00002070, VirtualSize = 0x00000078 + [0] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x2C24, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x20C) + [1] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x2B62, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x14A) + [2] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x2B48, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x130) + [3] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x2B34, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x11C) + [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x2B92, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x17A) + [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x2BB0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x198) + [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x2C6A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x252) + [7] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x2C54, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x23C) + [8] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x2C3A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x222) + [9] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x2B76, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x15E) + [10] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x2C0E, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1F6) + [11] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x2BF4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1DC) + [12] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x2BD8, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1C0) + [13] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x2BC4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1AC) + + + Section Data [01] PEStreamSectionData Position = 0x000014E8, Size = 0x000000A8, RVA = 0x000020E8, VirtualSize = 0x000000A8 + + Section Data [02] PELoadConfigDirectory64 Position = 0x00001590, Size = 0x00000140, RVA = 0x00002190, VirtualSize = 0x00000140 + Size = 0x140 + TimeDateStamp = 0x0 + MajorVersion = 0 + MinorVersion = 0 + GlobalFlagsClear = 0x0 + GlobalFlagsSet = 0x0 + CriticalSectionDefaultTimeout = 0x0 + DeCommitFreeBlockThreshold = 0x0 + DeCommitTotalFreeThreshold = 0x0 + LockPrefixTable = 0x0x0 + MaximumAllocationSize = 0x0 + VirtualMemoryThreshold = 0x0 + ProcessAffinityMask = 0x0 + ProcessHeapFlags = 0x0 + CSDVersion = 0 + DependentLoadFlags = 0x0 + EditList = 0x0 + SecurityCookie = 0x180003000 + SEHandlerTable = 0x0 + SEHandlerCount = 0x0 + GuardCFCheckFunctionPointer = 0x1800020E8 + GuardCFDispatchFunctionPointer = 0x1800020F8 + GuardCFFunctionTable = 0x0 + GuardCFFunctionCount = 0x0 + GuardFlags = Instrumented + TableSizeShift = 0x0 + CodeIntegrity.Flags = 0x0 + CodeIntegrity.Catalog = 0x0 + CodeIntegrity.CatalogOffset = 0x0 + CodeIntegrity.Reserved = 0x0 + GuardAddressTakenIatEntryTable = 0x0 + GuardAddressTakenIatEntryCount = 0x0 + GuardLongJumpTargetTable = 0x0 + GuardLongJumpTargetCount = 0x0 + DynamicValueRelocTable = 0x0 + CHPEMetadataPointer = 0x0 + GuardRFFailureRoutine = 0x0 + GuardRFFailureRoutineFunctionPointer = 0x0 + DynamicValueRelocTableOffset = 0x0 + DynamicValueRelocTableSection = 0 + Reserved2 = 0 + GuardRFVerifyStackPointerFunctionPointer = 0x0 + HotPatchTableOffset = 0x0 + Reserved3 = 0x0 + EnclaveConfigurationPointer = 0x0 + VolatileMetadataPointer = 0x180002380 + GuardEHContinuationTable = 0x0 + GuardEHContinuationCount = 0x0 + GuardXFGCheckFunctionPointer = 0x1800020F0 + GuardXFGDispatchFunctionPointer = 0x180002100 + GuardXFGTableDispatchFunctionPointer = 0x180002108 + CastGuardOsDeterminedFailureMode = 0x180002110 + GuardMemcpyFunctionPointer = 0x180002118 + + Section Data [03] PEDebugDirectory Position = 0x000016D0, Size = 0x00000070, RVA = 0x000022D0, VirtualSize = 0x00000070 + [0] Type = CodeView, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2E, Data = RVA = 0x000023E4 (PEDebugSectionDataRSDS[5] .rdata + [1] Type = VCFeature, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2E, Data = RVA = 0x00002458 (PEDebugStreamSectionData[7] .rdata + [2] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2E, Data = RVA = 0x0000246C (PEDebugStreamSectionData[8] .rdata + [3] Type = ILTCG, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2E, Data = null + + Section Data [04] PEStreamSectionData Position = 0x00001740, Size = 0x000000A4, RVA = 0x00002340, VirtualSize = 0x000000A4 + + Section Data [05] PEDebugSectionDataRSDS Position = 0x000017E4, Size = 0x00000071, RVA = 0x000023E4, VirtualSize = 0x00000071 + Debug Section Data (RSDS) + Guid = 9e24e83e-5203-490d-b4fe-ac81f739897a + Age = 2 + PdbPath = {SolutionDirectory}native\Win64\NativeProjects\x64\Release\NativeLibraryWin64.pdb + + Section Data [06] PEStreamSectionData Position = 0x00001855, Size = 0x00000003, RVA = 0x00002455, VirtualSize = 0x00000003 + + Section Data [07] PEDebugStreamSectionData Position = 0x00001858, Size = 0x00000014, RVA = 0x00002458, VirtualSize = 0x00000014 + + Section Data [08] PEDebugStreamSectionData Position = 0x0000186C, Size = 0x00000258, RVA = 0x0000246C, VirtualSize = 0x00000258 + + Section Data [09] PEStreamSectionData Position = 0x00001AC4, Size = 0x000001AC, RVA = 0x000026C4, VirtualSize = 0x000001AC + + Section Data [10] PEExportDirectory Position = 0x00001C70, Size = 0x00000070, RVA = 0x00002870, VirtualSize = 0x00000070 + TimeStamp = 02/07/2106 06:28:15 + MajorVersion = 0 + MinorVersion = 0 + OrdinalBase = 0x1 + NameLink = NativeLibraryWin64.dll (RVA = 0x28AC, PEStreamSectionData { RVA = 0x28AC, VirtualSize = 0x34, Position = 0x1CAC, Size = 0x34 }, Offset = 0x0) + ExportFunctionAddressTable = RVA = 0x00002898 (PEExportAddressTable[0] + ExportNameTable = RVA = 0x000028A0 (PEExportNameTable[1] + ExportOrdinalTable = RVA = 0x000028A8 (PEExportOrdinalTable[2] + Section Data [00] PEExportAddressTable Position = 0x00001C98, Size = 0x00000008, RVA = 0x00002898, VirtualSize = 0x00000008 + [0] Exported RVA = 0x1020 (RVA = 0x00001000 (PEStreamSectionData[0] .text) + [1] Exported RVA = 0x1010 (RVA = 0x00001000 (PEStreamSectionData[0] .text) + + Section Data [01] PEExportNameTable Position = 0x00001CA0, Size = 0x00000008, RVA = 0x000028A0, VirtualSize = 0x00000008 + [0] AnotherFunction (RVA = 0x28C3, PEStreamSectionData { RVA = 0x28AC, VirtualSize = 0x34, Position = 0x1CAC, Size = 0x34 }, Offset = 0x17) + [1] HelloWorld (RVA = 0x28D3, PEStreamSectionData { RVA = 0x28AC, VirtualSize = 0x34, Position = 0x1CAC, Size = 0x34 }, Offset = 0x27) + + Section Data [02] PEExportOrdinalTable Position = 0x00001CA8, Size = 0x00000004, RVA = 0x000028A8, VirtualSize = 0x00000004 + [0] Ordinal = 0 + [1] Ordinal = 1 + + Section Data [03] PEStreamSectionData Position = 0x00001CAC, Size = 0x00000034, RVA = 0x000028AC, VirtualSize = 0x00000034 + + + Section Data [11] PEImportDirectory Position = 0x00001CE0, Size = 0x00000050, RVA = 0x000028E0, VirtualSize = 0x00000050 + [0] ImportDllNameLink = VCRUNTIME140.dll (RVA = 0x2A5A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x42) + [0] ImportAddressTable = RVA = 0x00002000 (PEImportAddressTable[0] + [0] ImportLookupTable = RVA = 0x000029A8 (PEImportLookupTable[13] .rdata + + [1] ImportDllNameLink = api-ms-win-crt-runtime-l1-1-0.dll (RVA = 0x2B12, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xFA) + [1] ImportAddressTable = RVA = 0x00002028 (PEImportAddressTable[1] + [1] ImportLookupTable = RVA = 0x000029D0 (PEImportLookupTable[14] .rdata + + [2] ImportDllNameLink = KERNEL32.dll (RVA = 0x2C7E, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x266) + [2] ImportAddressTable = RVA = 0x00002070 (PEImportAddressTable[2] + [2] ImportLookupTable = RVA = 0x00002930 (PEImportLookupTable[12] .rdata + + + Section Data [12] PEImportLookupTable Position = 0x00001D30, Size = 0x00000078, RVA = 0x00002930, VirtualSize = 0x00000078 + [0] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x2C24, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x20C) + [1] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x2B62, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x14A) + [2] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x2B48, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x130) + [3] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x2B34, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x11C) + [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x2B92, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x17A) + [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x2BB0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x198) + [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x2C6A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x252) + [7] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x2C54, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x23C) + [8] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x2C3A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x222) + [9] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x2B76, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x15E) + [10] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x2C0E, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1F6) + [11] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x2BF4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1DC) + [12] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x2BD8, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1C0) + [13] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x2BC4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1AC) + + Section Data [13] PEImportLookupTable Position = 0x00001DA8, Size = 0x00000028, RVA = 0x000029A8, VirtualSize = 0x00000028 + [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x2C8C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x274) + [1] PEImportHintName { Hint = 37, Name = __std_type_info_destroy_list } (RVA = 0x2A30, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x18) + [2] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x2A18, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x0) + [3] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x2A50, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x38) + + Section Data [14] PEImportLookupTable Position = 0x00001DD0, Size = 0x00000048, RVA = 0x000029D0, VirtualSize = 0x00000048 + [0] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x2B08, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xF0) + [1] PEImportHintName { Hint = 34, Name = _execute_onexit_table } (RVA = 0x2AF0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xD8) + [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x2AD4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xBC) + [3] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x2AB2, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x9A) + [4] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x2A98, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x80) + [5] PEImportHintName { Hint = 63, Name = _seh_filter_dll } (RVA = 0x2A86, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x6E) + [6] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x2A78, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x60) + [7] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x2A6C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x54) + + Section Data [15] PEStreamSectionData Position = 0x00001E18, Size = 0x000003E8, RVA = 0x00002A18, VirtualSize = 0x000003E8 + + + [02] .data PESection Position = 0x00002200, Size = 0x00000200, RVA = 0x00003000, VirtualSize = 0x00000680, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) + + Section Data [00] PEStreamSectionData Position = 0x00002200, Size = 0x00000200, RVA = 0x00003000, VirtualSize = 0x00000200 + + + [03] .pdata PESection Position = 0x00002400, Size = 0x00000200, RVA = 0x00004000, VirtualSize = 0x000001A4, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + Section Data [00] PEExceptionDirectory Position = 0x00002400, Size = 0x000001A4, RVA = 0x00004000, VirtualSize = 0x000001A4 + [0] Begin = RVA = 0x1040, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x40 + [0] End = RVA = 0x105E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x5E + [0] UnwindInfoAddress = RVA = 0x26E8, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x24 + + [1] Begin = RVA = 0x1060, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x60 + [1] End = RVA = 0x10B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xB0 + [1] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 + + [2] Begin = RVA = 0x10B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xB0 + [2] End = RVA = 0x11C6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x1C6 + [2] UnwindInfoAddress = RVA = 0x26EC, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x28 + + [3] Begin = RVA = 0x11C8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x1C8 + [3] End = RVA = 0x1248, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x248 + [3] UnwindInfoAddress = RVA = 0x2730, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x6C + + [4] Begin = RVA = 0x1248, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x248 + [4] End = RVA = 0x1370, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x370 + [4] UnwindInfoAddress = RVA = 0x278C, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC8 + + [5] Begin = RVA = 0x1370, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x370 + [5] End = RVA = 0x13AD, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x3AD + [5] UnwindInfoAddress = RVA = 0x27BC, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xF8 + + [6] Begin = RVA = 0x13B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x3B0 + [6] End = RVA = 0x13E4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x3E4 + [6] UnwindInfoAddress = RVA = 0x27E0, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x11C + + [7] Begin = RVA = 0x13E4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x3E4 + [7] End = RVA = 0x14B7, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x4B7 + [7] UnwindInfoAddress = RVA = 0x27CC, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x108 + + [8] Begin = RVA = 0x14B8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x4B8 + [8] End = RVA = 0x1529, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x529 + [8] UnwindInfoAddress = RVA = 0x27D4, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x110 + + [9] Begin = RVA = 0x152C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x52C + [9] End = RVA = 0x15D8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x5D8 + [9] UnwindInfoAddress = RVA = 0x27E8, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x124 + + [10] Begin = RVA = 0x1604, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x604 + [10] End = RVA = 0x161F, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x61F + [10] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 + + [11] Begin = RVA = 0x1620, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x620 + [11] End = RVA = 0x1659, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x659 + [11] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 + + [12] Begin = RVA = 0x165C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x65C + [12] End = RVA = 0x1690, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x690 + [12] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 + + [13] Begin = RVA = 0x1690, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x690 + [13] End = RVA = 0x16A5, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6A5 + [13] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 + + [14] Begin = RVA = 0x16A8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6A8 + [14] End = RVA = 0x16D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6D0 + [14] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 + + [15] Begin = RVA = 0x16D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6D0 + [15] End = RVA = 0x16E5, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6E5 + [15] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 + + [16] Begin = RVA = 0x16E8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6E8 + [16] End = RVA = 0x1748, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x748 + [16] UnwindInfoAddress = RVA = 0x281C, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x158 + + [17] Begin = RVA = 0x1748, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x748 + [17] End = RVA = 0x1778, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x778 + [17] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 + + [18] Begin = RVA = 0x1778, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x778 + [18] End = RVA = 0x178C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x78C + [18] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 + + [19] Begin = RVA = 0x178C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x78C + [19] End = RVA = 0x17C6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x7C6 + [19] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 + + [20] Begin = RVA = 0x17C8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x7C8 + [20] End = RVA = 0x1853, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x853 + [20] UnwindInfoAddress = RVA = 0x27E0, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x11C + + [21] Begin = RVA = 0x1854, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x854 + [21] End = RVA = 0x18EC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x8EC + [21] UnwindInfoAddress = RVA = 0x27F4, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x130 + + [22] Begin = RVA = 0x18EC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x8EC + [22] End = RVA = 0x1910, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x910 + [22] UnwindInfoAddress = RVA = 0x27E0, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x11C + + [23] Begin = RVA = 0x1910, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x910 + [23] End = RVA = 0x1939, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x939 + [23] UnwindInfoAddress = RVA = 0x27E0, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x11C + + [24] Begin = RVA = 0x194C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x94C + [24] End = RVA = 0x1A94, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xA94 + [24] UnwindInfoAddress = RVA = 0x2830, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x16C + + [25] Begin = RVA = 0x1A94, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xA94 + [25] End = RVA = 0x1AD0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xAD0 + [25] UnwindInfoAddress = RVA = 0x2840, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x17C + + [26] Begin = RVA = 0x1AD0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xAD0 + [26] End = RVA = 0x1B0C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xB0C + [26] UnwindInfoAddress = RVA = 0x2840, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x17C + + [27] Begin = RVA = 0x1B10, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xB10 + [27] End = RVA = 0x1DDA, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xDDA + [27] UnwindInfoAddress = RVA = 0x284C, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x188 + + [28] Begin = RVA = 0x1E60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE60 + [28] End = RVA = 0x1E62, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE62 + [28] UnwindInfoAddress = RVA = 0x2860, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x19C + + [29] Begin = RVA = 0x1E80, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE80 + [29] End = RVA = 0x1E86, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE86 + [29] UnwindInfoAddress = RVA = 0x2868, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x1A4 + + [30] Begin = RVA = 0x1E86, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE86 + [30] End = RVA = 0x1E9D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE9D + [30] UnwindInfoAddress = RVA = 0x2728, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x64 + + [31] Begin = RVA = 0x1E9D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE9D + [31] End = RVA = 0x1EB6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xEB6 + [31] UnwindInfoAddress = RVA = 0x2728, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x64 + + [32] Begin = RVA = 0x1EB6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xEB6 + [32] End = RVA = 0x1ECA, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xECA + [32] UnwindInfoAddress = RVA = 0x2728, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x64 + + [33] Begin = RVA = 0x1ECA, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xECA + [33] End = RVA = 0x1F00, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xF00 + [33] UnwindInfoAddress = RVA = 0x27B4, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xF0 + + [34] Begin = RVA = 0x1F00, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xF00 + [34] End = RVA = 0x1F18, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xF18 + [34] UnwindInfoAddress = RVA = 0x2814, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x150 + + + Section Data [01] PEStreamSectionData Position = 0x000025A4, Size = 0x0000005C, RVA = 0x000041A4, VirtualSize = 0x0000005C + + + [04] .rsrc PESection Position = 0x00002600, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x000000F8, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + Section Data [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) } + Section Data [00] PEStreamSectionData Position = 0x000026EC, Size = 0x0000000C, RVA = 0x000050EC, VirtualSize = 0x0000000C + + + Section Data [01] PEStreamSectionData Position = 0x000026F8, Size = 0x00000108, RVA = 0x000050F8, VirtualSize = 0x00000108 + + + [05] .reloc PESection Position = 0x00002800, Size = 0x00000200, RVA = 0x00006000, VirtualSize = 0x0000002C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) + + Section Data [00] PEBaseRelocationDirectory Position = 0x00002800, Size = 0x0000002C, RVA = 0x00006000, VirtualSize = 0x0000002C + Block 0x2000 Relocations[17] + Dir64 Offset = 0x00E8, RVA = 0x20E8 (0x0000000180001B0C), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x00F0, RVA = 0x20F0 (0x0000000180001B0C), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x00F8, RVA = 0x20F8 (0x0000000180001E60), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0100, RVA = 0x2100 (0x0000000180001E80), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0108, RVA = 0x2108 (0x0000000180001E80), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0118, RVA = 0x2118 (0x0000000180001E3B), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0168, RVA = 0x2168 (0x0000000180003090), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x0170, RVA = 0x2170 (0x0000000180003130), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } + Dir64 Offset = 0x01E8, RVA = 0x21E8 (0x0000000180003000), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x0200, RVA = 0x2200 (0x00000001800020E8), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x0208, RVA = 0x2208 (0x00000001800020F8), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x0290, RVA = 0x2290 (0x0000000180002380), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x02A8, RVA = 0x22A8 (0x00000001800020F0), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x02B0, RVA = 0x22B0 (0x0000000180002100), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x02B8, RVA = 0x22B8 (0x0000000180002108), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x02C0, RVA = 0x22C0 (0x0000000180002110), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } + Dir64 Offset = 0x02C8, RVA = 0x22C8 (0x0000000180002118), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } + Section Data [00] PEStreamSectionData Position = 0x00002824, Size = 0x00000008, RVA = 0x00006024, VirtualSize = 0x00000008 + + + Section Data [01] PEStreamSectionData Position = 0x0000282C, Size = 0x000001D4, RVA = 0x0000602C, VirtualSize = 0x000001D4 + + From bfd0d63abc6bcde44147b68e384b7fd509a3a8b9 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 26 Sep 2024 08:47:08 +0200 Subject: [PATCH 60/87] Add PE diagram --- src/LibObjectFile/PE/PE.cd | 417 +++++++++++++++++++++++++++++++++ src/LibObjectFile/PE/PEFile.cs | 4 +- 2 files changed, 419 insertions(+), 2 deletions(-) create mode 100644 src/LibObjectFile/PE/PE.cd diff --git a/src/LibObjectFile/PE/PE.cd b/src/LibObjectFile/PE/PE.cd new file mode 100644 index 0000000..4ec7bde --- /dev/null +++ b/src/LibObjectFile/PE/PE.cd @@ -0,0 +1,417 @@ + + + + + + AAAAAAAAAAAAAAABAAAAAAAAAAEAABAAAAAAAAAAAAA= + PE\PEObjectBase.cs + + + + + + + + + + + + + + + + + AAABCAAgAAgAAAAAAAAAAgACAAAAAAQAEABAAAAQAAA= + PE\PEObject.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + oAA0CCAAAACEICOEAAGGAgAgAIAARogAAMAgAAEAEAA= + PE\PEFile.cs + + + + + + + + + + + + + + + + + AAABCCAAAggAAAABCAAAAgQgiAAAAAQAEAAAAAEAAAA= + PE\PESection.cs + + + + + + + + + AAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + PE\PESectionData.cs + + + + + + + + + + + + + AAABiCAACghAAAAIAAAAAAAAAAAAAEQAAAAAAAAEAAA= + PE\DataDirectory\PEDataDirectory.cs + + + + + + + + + AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + PE\DataDirectory\PEDebugSectionData.cs + + + + + + AAAAACAAAAgAAAAAAAAAAAAgAAAAAAAAAAAgAAEAAAA= + PE\DataDirectory\PEExportAddressTable.cs + + + + + + AAAAACAAAAgAAAAAAAAAAAAgAAAAAAAAAAAgAAEAAAA= + PE\DataDirectory\PEExportNameTable.cs + + + + + + AAAAACAAAAgAAAAAAAAAAAAgAAAAAAAAAAAgAAAAAAA= + PE\DataDirectory\PEExportOrdinalTable.cs + + + + + + AAAAACAAAAgAACAAAAAAAAAgAAAAAAAAAAAAAAEACAA= + PE\DataDirectory\PEImportAddressTable.cs + + + + + + AAAAACAAAAgAACAAAAAAAAAgAAAAAAAAAAAAAAEACAA= + PE\DataDirectory\PEImportLookupTable.cs + + + + + + AAAAACAAAIgAAAAAAEAAAAQAAAAAAAAAACAAAAAAFEA= + PE\DataDirectory\PEThunkAddressTable.cs + + + + + + AAAAACAAAAgAAAAAAAIAAAAgAAEIABAAAAAAAAEAABA= + PE\PEStreamSectionData.cs + + + + + + + + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + PE\PEExtraData.cs + + + + + + AAAAAAAACAAAAAAAAAAAAAAgAAAAAAAAAAAAAEEAAAA= + PE\DataDirectory\PESecurityCertificateDirectory.cs + + + + + + + + + AAAAACAAAAAAAAAAAAAAAAAgAAAIAAAAAAAAAAEAABA= + PE\PEStreamExtraData.cs + + + + + + AAAAAAAACAAAAAAAAAAAAAAgAAAAAAAAAAAAAAEAAAA= + PE\DataDirectory\PEArchitectureDirectory.cs + + + + + + AAAEgAAACAAAAAAAAAAAAgAgAAAAAAAAAAAAAAEAAAA= + PE\DataDirectory\PEBaseRelocationDirectory.cs + + + + + + AAAAgAAACAAAACAAAAAAAAAgAAAAAAAAAAAAAAEAAAA= + PE\DataDirectory\PEBoundImportDirectory.cs + + + + + + AAAAAAAACAAAAAAAAAAAAAAgAAAAAAAAAAAAAAEAAAA= + PE\DataDirectory\PEClrMetadata.cs + + + + + + AAAAAAAACAAAACAAAAAAAAAgAAAAAAAAAAAAAAEEAAA= + PE\DataDirectory\PEDebugDirectory.cs + + + + + + AAAAgAAACAAAACQAAAAAAAAgAAAAAAAAAAAAAAEEAAA= + PE\DataDirectory\PEDelayImportDirectory.cs + + + + + + AAAAgAAQCAAAACAAAAAAAAAgAAAAAAAgAAAAAAEAAAA= + PE\DataDirectory\PEExceptionDirectory.cs + + + + + + AAAAgCCACEQAAAAAAAAAAACgBEAAAAAAAAAAAAEUAAA= + PE\DataDirectory\PEExportDirectory.cs + + + + + + AAAAAAAACAAAAAAAAAAAAAAgAAAAAAAAAAAAAAEAAAA= + PE\DataDirectory\PEGlobalPointerDirectory.cs + + + + + + AAAAAAAACAAAAAAAAAAAAAAgAAAAAAAAAAAAAAEAAAA= + PE\DataDirectory\PEImportAddressTableDirectory.cs + + + + + + AAAAgAAACAAAACUAAAAAAAAgAAAAAAAAAAAAAAEEAAA= + PE\DataDirectory\PEImportDirectory.cs + + + + + + AAAAAAAICAAAAAAAAAAAAAAggAEAIBAAgAAAAAEAAAA= + PE\DataDirectory\PERawDataDirectory.cs + + + + + + + + + + + + + AAAAAAAACABAADAAAAAAAIAgAAAAAAAEQAAEAAEAAAA= + PE\DataDirectory\PEResourceDirectory.cs + + + + + + + + + + + + + + + + + + + + + + + + + AAJAAAAAAARAADQIAAAAAgQAAEABAAAEQAAAAAAQAAA= + PE\DataDirectory\PEResourceDirectoryEntry.cs + + + + + + + + + + AABCgAAAAAAAAAAAAAAAAgQBAAAAAAAAAQAAAAAAAAA= + PE\DataDirectory\PEResourceEntry.cs + + + + + + + + + + + + + + AABAAAAABAAAAAAAAAAAAgAAAAAAABAAAAAAgAAAAAA= + PE\DataDirectory\PEResourceDataEntry.cs + + + + + + AAAAAAAAAAAAAAAEAAAAAAAAAEAAABAAAQAAAAAAAAA= + PE\DataDirectory\PESecurityCertificate.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + PE\DataDirectory\PEDebugStreamExtraData.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAgAAAAAAAAAA= + PE\DataDirectory\PELoadConfigDirectory.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + PE\DataDirectory\PETlsDirectory.cs + + + + + + AA2AEgDBJAwRYAgBQAAgMgAEAkQRAbMAGgwAIgAAAgE= + PE\DataDirectory\PELoadConfigDirectory32.cs + + + + + + + + + + + + + + AA2AEgDBJAwRYAgBQAAgMgAEAkQRAbMAGgwAIgAAAgE= + PE\DataDirectory\PELoadConfigDirectory64.cs + + + + + + AAAABAAAAAAAAAAECAAAAAAAgAAAARAAgAAAIAAAAQA= + PE\DataDirectory\PETlsDirectory32.cs + + + + + + AAAABAAAAAAAAAAECAAAAAAAgAAAARAAgAAAIAAAAQA= + PE\DataDirectory\PETlsDirectory64.cs + + + + + + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + PE\DataDirectory\PEDebugStreamSectionData.cs + + + + \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 6fcced8..94bb9e3 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.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. @@ -17,7 +17,7 @@ namespace LibObjectFile.PE; /// /// A Portable Executable file that can be read, modified and written. /// -public partial class PEFile : PEObjectBase +public sealed partial class PEFile : PEObjectBase { private byte[] _dosStub = []; private Stream? _dosStubExtra; From 60988b63b13cc40ca1376ae6cd6db1832963424e Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 26 Sep 2024 09:06:40 +0200 Subject: [PATCH 61/87] Add basic documentation --- doc/PE.png | Bin 0 -> 420230 bytes doc/readme.md | 38 ++++++++++++ src/LibObjectFile/PE/PE.cd | 121 ++++++++++++++++++------------------- 3 files changed, 97 insertions(+), 62 deletions(-) create mode 100644 doc/PE.png diff --git a/doc/PE.png b/doc/PE.png new file mode 100644 index 0000000000000000000000000000000000000000..02a8b24ce167a5283872b9f95cd62a44fd54fc4a GIT binary patch literal 420230 zcmeFZ2T+q+_cw}wG%1OSic&&Ukfs!o4iRFZ*U*b7MLg3_BH zN|mDY7Ntlhv=Dl}9l-OR@4V-KXYSm&GxyGX%rKDhJkQ>1uf5i9{nlFhiH6z@2D;O9 zWMpIviZ_v3WMni*@Oz7f3cMpg?)4u0Ipm^sL!KlVR^xW~^{~}>vLhchb4=ZaKTBro zC7JGcIxrE8jeOV)Jfv0^7u6t zizr13VQP8UInEblN4IOXJk~}~Ubulsopg-W55k8{?70eFDb$m!9r_y2MCn6dXn%eh zg(=~G{s(^%J)Drge_Y5IK=ZecU$~Ma@Yj!jjslGQuUBMb7Hs6uzrBRbLePJENoEj& zp#0nGM#=vd9_^o&m6sQ_vqhKE`K4Ay-+; zo*aX`u_Gt=I~o!ibnHhe`w2rcPF$lyGtwhtGjGbjd9=!wNJg)e`Z!FsE28)(b<^G3 za}z^GWlB6pd?$6~GLZ2`PVCyuEx10}5(41`+Qsd++nk#Neq|-mYoyX+lXbQyH-b&- z;nddNr2X@*?F;LLhJb*;axFDSV@I;W#O7>%bhXcxl95rI^u~CEu=o1nE#{Lam0^(k zY}zP8Lrja8@vxrH^Oe4-&Q$yoi-=WeSbLHjmN0bY%DqJyLQ@3yF=q!?cAS%|%J6S# zt0bd@7LOs>lJa2>=_r@%WvLQRb}-!bLEO;HK@>vw*YaE_okEUjm7)6X8V+lw z&X3#KRL;s5h>krb#a5(j%loh?=GOdLmW=c7v=7zaQM0+)W?MR06}-w5_a;o$=Z|#T zR8%{4m;KmMK&8+4tFzIg@K>|4WyiHc6i}%%H}$)FSsA)nP?oZ;{5nMqX~}D)8O_74 zy4|!%7#iI%i3+vNE*_hl72Wjg*cmlt?Wz)tG)h2~VZqiRbE%8P#^Hgky?)W<#Kx7joNgEqR)Z4O=jML{jMsu(06=CQyZI^XER2ZU)rfiSsBJ}LuUZdvYsriyo ziQn+!&DglOKtALBH5)(J4rn72lLTqPkjwJ#IL8^F>NNSc#iBt>P6Eo2^c)jeuI8%^=Ozr|lTq2@;{hn=nNK%#efTpNF@|h8&4EcbCpb|_ zhDFzTnet!$2$6H?nsnYoV~WZ#VL1_)R7{UiQEg5kgOyxdZ2mxaSAhaz13N>Ej~}A~Ds)7@fAn zE3-q>Rl4&LE+|ZOCrYo#YzBj3yWa&COX31vG$cooA8)P0MyvZdCiS~UHl`JG=llJ& zH{KF++p+#T0#Y-ZDKW8K2~~{dhUy zrGK8`huC(>nfQ+wTTPTTs_G-vOu9hFEmW#OE++Otf}?%Ziao|KhpusB<^61(b7#Vl2T(K& zaut@+t!e+gr-wJmXDw!RrdxTTY?58Vb2Ss+yKkM2KG`=@Z+~=E3~sH>^SNPtO6CNH zQv)e)xK*CL;f8^OyF5Y!nZZT}9Z?CipyOW5f4#){{W=RG~T289++K7RbD?}ysI4tj8%WO`Z}a>RQ@6^QHA(;W^& za~g6eC7cF{JcAaTHR><2i#GP#!FwKI*t_n9_<>x8^=*p^6Q{=H94- zaZ(1L)hOYi#2d(TK{@UDgtR__spq=Ys~I+dbjuqRhWG4Q6V6rjd0>R6B}9szk~C`ZU<8A%_>3u zU^PE5xn4*~mym{Zp1#|22rXjW))>O!n~C#Vozhgl zP$vgTj=wONU6Qi#i^=a4Juvr}kI^^3g^zv`a(Ofn*Hc`#ZN>z`V-n6!s_5gswTMQHlFZ7l1U(ijI2J4U6k|}#&!7cVQ0dyo3iW1S|~m89*bLA^UdUT#NPW6eQoaEeGzrS<;e5jzpt|e zuLCzQUeLfEA%&~dHuc@Gl(_%XpCyszL8Uy>b%SvlTT`<=da{OwigRvr;*wt$Zh!q! zZXB#%#AKqES}+sGMU-WnBnR4kti=3Sb$K2e|CxjUw3ych_7YAjX0<>HQZT*IQnwII z+#Y(5sNZ5TEOGPGDMr#Gk2k-bom&~=yhcldyq9!8-6_w@B1QyKuxL8#}zXl6Bvh4=WPxLaXsTa2SUed$!Xc29<3w)sgJk za-=Bty1jj_EV~^n<>N=4i{(@BBqICrIGAP+VPDprf4r0d41zuj5mGQM{~9}9_3*{L zkHxL}tBAX!ZaFL%I`N+ z`H%w2&6M*>n_9MzHxW?}=b18>i8}P-{71{LTf*J4BV#Q6=JqiK_ujhl9KIx&CJ#5A2%E$l8b-55wQ-F zt#-qi+SuB@>d7o_?YsEp%a`xDMq;xyhujM6N%$JT=s&Z15)09dC{AEPhQ1Eyp>z!I zywoDZ2XVURaMz1o1R}(B>fw~oZxj|$|E?gJ`5aZ2AkyXn#OXFpoaNqd6yc}42xMt( zzIKc0Vmzc=2Im}8_!Wx2Q2ANX_wjLWD{44b%jepyIQ$~HHSBisamtc@!E9?{MS8}V zOGPlhP5?SaxPG&8Qd!ycR7!bHBlMNK?n7~1w7%?xRebVhqzd*p9g?48uFPj~HVjdJ zM!3}@PV;lrQS_8J#5`|#aiId&{DMwh6q}NoZm+@c_NG~HRWA|T=H+}bn0{a7+g`Rk|guXiT z{d4S3HgUbjoI20xg3moNvdTHlTA7snXwb6trQ#ah(czG0F6Q9-hv6eM_Tim7(Eh@F z{o>G-q`N)cJ3{@ohvCIxABtNVD-@n~(LrndgG%yVJSs|3niy#CdBmirRQt@$D=elV zBhTy-YBp(&L3!%*dZ7--U|P1-*azo8tj~ktOD(y~@VYw4^!Qj*AV|j&PTRP!2Q{9}H zaDfP$wWm-ILX)#yOXyEq=tg_fMK-j%KhR*-*X#WScS0DiNiTk)SnkqRpGLuT^Nh0snRw}i{C2(Bf;8}i8lnj=AK$lm zVZ-@(;R8s}5&8G|o0}1zn0i^#Fb8WCO&ywDFJfx>njaPH7_+nlX)39{Lfq*6OaaXf zdF;R;rpLSX+>P>_X_q+-D!Cgrhp6|Cxt-6y)RFd3H$-rzyuottQAJLB{_97scC6+! zA`s2XpUNG9xoSC^jHoaCxnw=wQpoT-iscBds4bFPhWlc~%r(McIgaw1_6f=4P!39k zqhczCoYvdA=td5nig^UGzMC_&nM1&aQ-o0&Pr+aatC*zeWg!@AzR%U zwEnrnYjjK$@03x3kfmxB2KFQ#)I*?pBDMCHbzrv}>dg!$b+Z-p1JKX?7#xy??iJ7} zya_;ya90jmt-fStma-s>+p#vtyMBJnl)g6Zz()^|CxAe9%2k)e#HQv&K=^d&7?;Wm zBZsnUjxj>cb^xc;$lk>U4DMyf$Xp{Z@@41?`2X}?E zfk`~FXAauWm7lAz?h*wc*Z|XW zopX+<{0cw;Z60H?{L%BC6`>K2s{#pp>#Z#4QeX%kvgo4Nx9g9g@8>|479zej@Ir)C zgY_qI66OWKyZmtSwh@E?`|qLB-El3BH2`ha^zk__1Zv18&lXqR1dyDOWqiN%cRCg0 zvp-72DWE= zDV>+_Zev zwSj%&>K zTLL5Uz2&}Nd!q1X640~PrxXXcq3#Pb0Nvu z4`S@nX+B(xXv&U`GpFH$XcjnD(mx!&(LEt-`J7I^PgL&XxHFhbF4$zg+8yVo6f&Bx~r!yDL>om#bCB-;bQ#ZJXK9dI{|K-7YSZ7Pjo`LxM|z z8ta@yiT-p7=m*O;u3ni7(ahWrZ|2e=b>>zXuD!RsZcPDY_}ah#Omk+**S-q3{JDW5 zWwm%gb~Ei%N^(`*Qy|nJEe=`M^*AjC`<7eqD47t>Nn3zU92lhqA4pCklwn zA%YB&1w_>5Q06p_u#`UDJ!2b;iNFGlITo1wWcwuDH^HF1@QJRgs9#1_Q;48E(XZ|G z1HxzZlUk{m<%|j?hTow~OT_W1Fq01bMz641wPAyv>eP+T*W4*U0{ipuiAPq!tPoF*e z0CokPygt4IDdFKy3M|4Xp!TGo_BFQc0Q5OZIC+4-Bc$%)zdjl65kOPiy7$q4hF=%Ovj6=3_m6cP2tNqi zaPRqh?>AEZF?iD$M12bN@;O|9kOVVY|4N5@8-*Dcm{*+`{XzWmml(O8&?EV0vbj9` z_?|g)L4-_#gH)Zdd;oPr(1Mu=q>W9M@q^B5iNx#j|FA3{5HN@zZ1i)$_p`X8(@yq@ z^zm^)i26al7nPNAjjt@I;U|VHy^|`^G6NCyYqWYZwDX)thWU{EXYNO;4DZLy`;_U? z=YPZu5V4lAF{>MJA5+Tb@^KgXGj~boy!AJt-plO_p-k&to^=a7Idq;H-Y2|=bNuU* z4;Dl5c5&+)o=ek_oF|$JhEpwWjhq*DLV+R0)wrq9m;od4kPD@7cp_5Iq=liK#EMpJHZ5YeamiMZuq1jP)r$zr%YRV2Ab= z*x1@t$9_I!<;94tm;m%mK4jljbnW8>W`Ij)1+;sD0MOoi;Z!pl+;W8vQUIKFXnut} zootC7S|4_Jha=j)GY3+zt2iCwmVP|P?2HS^GjCZ8eC?ovwotQ_5Z1Hjx&qMs!maKX zKc5%`!C1>`EpX#u9)Q`QgP#9tPg21>MSEfN*SgAdJr=+w(I3IkQKkwmDW${uB z;xvD&e`-K@Of=?!ed2g{!kt`3rz{miww)*zNl538X8-rjD3-^zqG+kKn;@!a`t0Gc zyeO40Bz!guf$WQ7dA4ieeW^&PUN@^VTVY%2o#S%9@ST;LW5sqBn`)SG`OfW0K``kA2zs zdJRO_i`TLiX_5EX+{)}mB~GE&?**-PWh(#!QQ$I~FCE@OhkWfQsu!x!Kvngd5qYZU z_IuaCpJITn(SX4rG~&L!|Ma#ZqBev5i27j7=ykQ#87)5fdI-7$wOOt*&WD>w>PWRFvFWmnq!=c zB%shus$AZ^2U5f(+Sir;inr27-X4l$Pq3yUtqewt4@Ykyo zJeCy+VDA+de=F7D%Co%$S*oOO{!rXXLj}(f5E&`|w!Yb7u{?8%0{Xb>`4&DZy$LK} z`Z%C0+VwBp^h>AfPKbO3tGvdob4_R39`D<0|GkWM|__ws*0wlmEikUa_nG8f0~ z(M$h=p<)}D153bjoWGPG&F`E8F{eJ;s%4IbyI6vGOu-bUiU%zev*-W@rb&&OB72OH zYutjCv|f0bJ9g16KBI)+6`+@yJN;KoAH(Oi#ueL_aIt(y)=izKn}ZFwS8x!q8=9D@ zm40_+G@-D%-Y0w~V(!g4UxN=WVxo;1eno~`OuYS02xkr=XscMZFr|)B;XlsG;g7RY zH0DFjdA9Qs5kZD5b$Mf6Bhh1KSMAUQ=@h_z;RdiaOh5!ofW{3rnl^Ir zbAjd8U6k|kCtf~!b1JwRTj+bsugU7UwvLZUQBZen6pK}sU=Ryh12r}zJhpx!+q<|` zJh_<;d1_)nSm9&Ow~YQ5H~c?2OU`|Ne-K3pANsSv`~LoA zB?9T>RPbQ7=i9&dPT;OQ|28gC_yG(0XZX=y<5%-K3h*3zLj;K7|FM7npN4#t4c%8$ zq_Jx%uJ(0z;;Mlp(L-W2-Dzv#OA{>;#53a)!xwRG$4Qhrqn@v|7zk1plRmq;&F!zfJp7hq3P-A&YjmzvecYBNLtoE`{S>-2)N#eVsOE0 z-=(P5_{B_PzhTSo#9brT3%9XRZntTWp%GU{o{TMgwr~L$sz>guOjY=BX5z7DN5KwU z7Avcr+!j;W5G0U8y)v5eKl`2#Ys}sPfs&7i_d*1>Sk)%;pM`<^Sr{cHrL@vgaq|Fl ze$5+hZL?Ec(c4MSCWuvYauZpPjxm?8_dEtKjA3(E@DEwc@?y?vCvx!C zR99EfpShy*HGC^hdS%kLaI}in*}b8zF#yvL8v4*pXdLXd(|v&3-|Ddd%DL8Louw0^`%Sp1SYRn7cKw7VwON?mv|+^dBR-C<+} z2;>n&{iNSRN2ku3Puvh86g9^qLQ{nn9oNwO`cMw%kyIMwlz_-W#KinzV0-J%`Y>%(pYScj+7A=Pcw^BmyL#VvedYp?Z`r09_GFT>eI+75_v{~YAs z!lshp?`^jW zRI6-()+Z_P(nY#FNP$n?u-5zP2tMS4))HJbt*t`b>(ekU;P+*f9}taq1qxKquq^PTySrCB`yGWV<^?8u+k z>RciM*|{xLcz5Au>Mqx_k?lBsUzgXFJIx}(-iry&E2=W2RLb(gBYBV8^|xX~rxiDD zcV`0>71eUI=qXYkHcH|@ssKIb6s>GZXt1!83P7jqdVF`;CMN)%YvJ;OTl8^*02yvBC=Zj4#DhWNk67l<;^tK9pGV+>XgfGz}$r zA`bV@a4Tz%PGH38!$PXP8!n8Qjg5Vl8i^d9{oUMkGPp@p&xE&RXV-1H^_W8Xt?m08 zmG19Yhl|9)3YN(ucUN3|idz8(qIP2KN*Ez39SUI6Z(E4$u`Uk}y_>a$K;JWQ#|+uv z0|=xwK;Y(iRM0hd9THk5QBwDs0a9Ttkhg z+#V~YpSyd19@2kY2qL6LH967rvqbM4KLlDVU(bo+rH9fX={|OK2Mvyj4JCnd+OG2CNQ57r0+y$5_#VC9RET)*P0}q1R)w z6^=LEACu)37pw&MU~l`Bh5d1EPZ*J`1JwvwBOwzyYdJoMHeyA!M~Q}xcJ z?udG;3-IG;{S@8s#tId#d{cAp1qS4)5?7Fp>kFW%l0+ubU*s4141tnEGe$HUQumc{QI-mz0SS=#uRdPQeCbaZkgV?b>tjx`yc|XW=)ldDhVeVV^?L>_VS7~b z8#xbLeA?kZjL11j;O%59$7Cc*eF9iVY=Svc7?4wjGCKLkf5nO~$f{hKwHE=)R?@Qd zSXZ!)SA@RvDs1#E-L;|V&GO1hsr1GIdE~?|QD@KL2+8BnO7(DV%L-uIa&@ZQ)EC!Z zJO;_i*N#Vm;qtCE{BF6BUzOi5+nt4jmmt4rt_5L|a?mc1(VMZKYc-IWR5uk>_>l20 z-i_AQ=KIZ&oWy9q@aK(Fxtn+uiH>UlG(~LRKt^qDcQ7vgU<>rFvsdqYfJ_|5f3;LsFFe8$(qy;E#fi5O2 zb6yziLe}A1x&}drAWhMR9KgE?=t_`7%i;H8G?SHb8KLHnX;Rx}1cS~(G_!aCbyKc$ zst$_I9Ju7#jRDAJCQwVyrGpd;phYm?qU6~(mrKc^6IKZ>l<+n%ih)7lR1%8@_)2C@ z2$Y}53@^Xk68b;E;wylGGioqVBS8iwa%j#0MfF$gb*|IMD$NXbJB7YYd+=^fVI71N z$UiWF$?w>kJnXlu2I-D{zwsAg-ZX%yk8L=IjeXF@p?86sX`d9^6R5w+UzFvX8K-CsKJ-_l`EP3Y0Q&#mW$gY?8MOaBWF=+giIis-ml+f?|2dkzf;*OtFF3V)y!j4@ zKL~n#git+sCRpR2*7xv{6F1Gxle={0NX#2p9CF~q9Wm-jvdbTmc3S^Cny|9Bw|8D! zJ+v>;a{y%SzVpP-oR`~O3itU8`Dc2s&|z4zYksGDd21Th#kgI0~lFvt9yN1ZsHo)-Z1KzZuMXAbkIxq0W(&)u+0@ zvXqqYt}B-V5QyFBq}?Vl8uOET8h~a$0O_rB_9Dgh!|;jpmV$H<-@|Gd%B+tc0I(W`U7ug+t4 zzlm-C)Z3E)?BQzW9#=!FZDNvJLZ*T9))NKEM_NkqLf2VcJyJ2qGP@o@=Hc9%S$)6- z%jWI6g4fR`hHUY4xH7kep=qy$Kx(GV#I5nB2wEvpHKlQaQ9^@UEd2Ar4ANAm#8Cu#6>-MRt2|*@Qh}7J{F9kE4d4RYJ_E(`7!C|^H zHDr6vS}p?TOQ@)>rLNhRV1h#aUS8|P{Tt!zk>{188Ugn;j>*oWqdGU|x!QTd)gxRE z6bF#hwYOl@NyLpL9}1>pM9=`I1 z_wO~YUJJwxIbw$If^B*jaTiOhn61Wi{cU-ivL}54+Q$}J%cQoXij{|ZcV)zrDm+`4 z61N_yfkkUa_zcC6N|xB z*;(@2awL8uRQK!7cwi>86WuP#x#27HI0myN(!6$NYj(5oz55z6dLY)0iQV-Exn`2B zz|#IgMj5iab;FG;r*iDv)T{B`akfgQ#7lRtOq3CC62)FDWFw7z@ZCLT)O-8l=J5l| zkki%C0lQSQU1-YrDe;sZA64brfE}obv>N7<%9_kb%sk;1*%+%2CUHn4o zz`GQFpXtuVD;-};D>eBX%n&g@SYkwIR-4(#nrw1mirjR(x+AtHKQd=vj7irbdX|jX z>hkWH@zgp#rc9K|M(*G7*`GVAr2#g(`FG?|cab^J0nbd}c#_VhDD{E*}g% zo2aXQes5X*=dSNp$-Q81ZfUUu1>@ED-7Q=TC@q}wio*c&XJFl=GA}~_wN3p|M?+$o$*JNXgq*&(}yfV}G5q zkVCLB!MEgOi0#ruax#C5)GCs+VBPy;$^=ylq%o1f`tLe|_sMRG?v058jeiva=^Z3F@W`*mpm_;*XP&ojWtX2VAp&6-InD zM;v~c83}7kl;OJ(#`-e#M)(Y!{Z?1863j zA^ZE%#9lGxYr<-^l{;}(!&mOP#$@sE`~5&-r%Lxc08C^ zS-#-b-`#$a=GVU(xJyL>!G_Q}Kp1*>ly&unyc&kw%4Ah706RBslB1T z5+3>a@%isf`oT5m@NbOp&`1%{o@D*0m50lA35>0xi7S0;e!erZve<0j^riJjc@Te=bKJTkTK2%bsLT5#_ESak5uOfN&8;d zlH49EmCny}U9`B*=0{e^2aU|=_sY3+<{_M2HLk9%lE_XT&0xmL2!f}uvFa+vdk42j zHZno(Y1FOS${Hu5`{cv1pWJ*C+?=aY8-AZ8f6tMhDjz_bgzN8|hjMKXo0HYp!?&xW zmt6XD$d8VUEeKv)w6=^CTZGbUPrbi3H9MdGjCbhhwqZ6rNMx1ZJfbdRos{G><;HH) zLhlt9p>eG|+;e*&>8ktNbK8VD7mjh(fAvlGwQ7SwuR|$0Q=4GW2e7_h&_IJ{v!#+h zcLdZ{GzYZzmQs*k5jlRp^8UT`@e(IBf{!9~*r{4gc3o(~XT1;|XCkXq#gR)06(jaM^FZ`;dt(mk*YGLUS z49lfY=q0*2B(~sPKKfGU-RW6z_EVpAa$lI;VcXhKA&;93Cd$5a`FB-$R|lDFOZdU3 zz$)i!Y-?-#VDV}$&99Te8U&kr_dZsaN6bB$e(#)b_%nWy5ZCGm>Xk>vE4BM1avDx3 z!{Em5?zPhS>ae;H+{*ISCQWS!AlIOY`7F2Y2g?rlwf)}d_VKO`F}rnjNm>m@SRLE? zKr2;)$0GX11cP?&Z9ih6!nI%XGuaAfD0}j+F8&@KkC&X%(A9k>r)wUvyzI<|7IzvA zVwHNRf_*2e3yLAP>II&51T@9x#leu*D)iy9l!l)kcqhjLArOuEWa z)gS^DD#DvOMhJ5k?%w(_xwvf%4~9rjE)sWt$=A{}NSu{sC$yK3VdIXXU@61cWP-KT zj@XHZW<;0kD*M@imBxl>iQ{HpxAu3BqeR-E75k z$m7X@FqxPIKkOPg)SUB>!u`S#p=tILG*gQO9^4DL?IvbpB4zpQXV~e`GeQOtD^?w! z@}&cUPmx1^U8T47AO})?-pI(LPhgU5pt0N?s=RvGZ_}9VFdTkZ@G>aEkYghHY{{^% zyxAx{o4`>P{IAj^buWS%+HDOzJ#N6_0P8&g472m*s?Dsa;!#&vUF_?Nt5lRKyAfl6 zyM|U<>#x5B+pA1Qy7DcO1&v-4DiW*3ojmn~6Y`5FLVr>lR`(X?n4~9{xQI=PYj1B2 zA&%jLP7ds_c?L56p8Q3?kF$x1sDDrmF7AURl zNdJ4sYFL}Sp4X%+e$Fn*{+;S6mv|JZ2-)JMMD_drfk-I z;AX3Y>jAlz`WXUfyv>5l_HH_;Vz1`Zr=zhtgd~1K5^MN*hh z41VV>NY%U;As63A=jM zZMkh@GI?PhTeWLtRXew~hT4)MF#2t|N(Jqz#9{vP(3(3yOi4+Eq3Q2_qzL%?p#T~f zoh{EUlk7Son2`-bodf#&vboKZyq6VOWWC+r17a61)eRbgBcq~bNE8vuY)tx!=jvS_ zZ%~PqH?}g_PLLr0O2@lBmn-;~)BoIf>zud+T0T1EyztV1pGhpimUXvhrMe7ukCb&g zO$wn3w!`LKy!@D+h`U+)Hjo;M<#1&U;T-vTRvL0%UdvXW{11Vyuq1sy9+R>4rLjWL zE>oo%yeXBFn;Yq~wWjFd;W1kL<*xeWW8))F>}v9t5?aGAYqIgQ{)`C5oQ0cpH{J#HB}Ff*!?&#O2D4QX zL=$WHhHmXh8#jdF3cfFu;apZW`VDJN_6lK>(l?5JfuhwmoA`Fuq?^Qqb5F{HF^3F9 z`|WBTYD{aEV0|mL*Gg*eRnL`7vTB$`p((o$K_^ctaY1&Ww*jc!JT+)MUJJY`@C0fnZn4G(a=W=cNxWnJ-o)0rk(+6BsO zly)WR^DjlX6Y!CFhK>oF0~t!gC<9(=K7yyMS*F?`{&{v=!UzY37e(zob1M_Wq-28U zOp;{9oJQTK!&Lvio7tAhyV51(*3BLfJTR8X>(*<4;`YYC^`B49Nlt5Lq!qLoMdsOJ zqy$v)Bba5KqRXPC*=5OJwXM_2It_}+PA7tbE%NTEfFw~zVdjcec@=;1UzP@7k|;)eZR zgisIh{V?;a(^PL>eWKnRdP`(uZpAU?lX0n**u>1<{TbBe3wfy?|Zwn zY}1SDMw#z0rzf;i6-4bnRtnpPsy}Dwet>EZ>CXPq@YpsObJE+^JJTvGx?jX>wTsg2 zn->0+x+;B--K9a%rVOqYyL@-Mr`f+NwO}r$_#Pc~)d-0X!}!g)>BLyh{F>i6G3NNM zb|1!{OSF0RXPCYeGiB-?>NUe>^$Qai9IJj~#8BT*q7B;l6|<@+$EnzpIc4RhaXT34 zwZ1#wOB^|{H4G?Qc(|JjIpQ9C4cp$~7hXeLnEw33uVi!f39g2EYmp6)4fI)GovR_i z{%JsLKmuaxJ%EKD2WF~^t?~a~jo_GK@&C^^f>UJ|ZNwW0U}kbK(LIQ&kB3j^*uE~- zt@yw01P=_e9ThQ9_R&M3rWXUjK5{DueI&Jk)Dz&?giRr`o0n`QuwDyg4WhN#GsRde zJYU5_sW{)``K(P{UPhZ8Z|@HaQ+0|?Q}w{!uCV-!8{w=`hjGW1K#T3?UDGZhd+(Ufxx$N47~T?Q&*!9;~`e)xJ!XIyrTl zoLiHEckLD(1#c6(hT?S?T<5RmbJO2Os{WW4^6;!~jxbgUIJa7S2hK{<91Ys2XK_Ee zx-5o|Aj&l;vPT5#9er2qVq||g^_Of20|nHif8Xyh1kij^P#w*bxsI#ea_BcIzrloL9bO7!9nN}6%SsN7 zk$C$GbcU5+F_QTTbUvG_oh@BlujphlyE%0r@;lEl_k%FO!Bn&CWZxefVg@Qyzvh+< z@pA|ywBj$@^lEcmDCC5go2i%F>(Al75bvj8o1(sBe~4@-WufL6`fU{`(eEC)X|-?M z#XxYx0hrD1Kpxwlqf=q>T=!pHd3&4bogf&)p~yT?7A5CrdZ{+6_@jQIg<>6-<7#0E zHPZ|;ysJWwkxjDxdm7Icu>!9m+&_4@r2djlBt8~{aiUXSX|JbeRj7+#Nx?EX^> z5hRe;r~=UM*RB_C;z*FI9PpmvuV25OlDT0N8<%#Q7}9f#?wEMf8I1yJ?Nb_xYyL`l zhvBW8x}+XVZpSVQwUn-|uA3m#jhFV&0&#V2D%p2MSJxubyn)0|JKt1mqEO1*PBNB{ z;C6W#Cd_D!B9Q0l#YSxE?>s+5rXqyL(jq%XV31#fBR6&TMZgV2@Jy*LTP7BqL12)@ zJIH_IVXLDb;xTPJC4FXJAP;@!IWI&y9|iO)k%b_UPmJoKrWS^55D+6m3dR=uT)5_Q!WWH-%etca7Na|;XxZ#RAr^~ z#yDCS&R1qHd_ z{jsrtu@!paZTrhzv)R{gk!NI6>CDw0r_y;P^LCWse?NsloLYN`g4f>un?8&|T<#m=} zU%Mp%3w;U@U6ugXYMFV}v8tGpXFU7cxWUl`Zm%=lqDbwRp!1YR%59-5kXPW_+AXEK zh;-489BPfWc!{$dzU#@=#LmjqitTt`BxJVS=; zoAVQoQ`4vWOlR+I4b0e0tcEzw}h-Z(1F`|L&}ZP$ywO=)gn2|_=N3|aZc;QafUrSshLj60^PZo6671 zD6L&wQrT6&p+NtA{sMF2)H)ki_k>iMA91&KqACI3Vz#S=%@DatGcnXA>b#v>GXv~- z?xEQW{yKMUY@Aek)Zzr;o3u|amnK#d-{J?lK*LH(e#VN0v)dLPQ^Ifg=+Ugrdo354 zgw^fTyqba|mnsG4083U@H|*D5iKvfqiZ;ss1Zw$eu!poQJ`h23u5A%=`NwALWj%e9z$lOYh3$0hE%ou znObMy2=ZQcpn$C~cfc`m-0-}Iaq{Ux!Kg#bUV`T@zb&-;o*%k0bg6|!LvhOgsR$%_ z4Te&7z+8<~m)p^)X+bhvP>UDW1izesP1!64gzZk-QPDTfpr^bA5WW-ZPGcUYN*+^b zOPmEhhTW%$BiS3fZ1T%p?o~%Sq=_; zFwbYM^}O}b?h)zGhaFr72D?&A`&6|(q-K{gd+ThW2iBd1Rr+C>im3t&2=T$O<0c+5 zD9*E!*f(IXbY#v=E_u6N+!W9!b2UZ*G&NUlfJ)2Ho45s-RT=`BtWwq{+s>x-+JEgU z=qEcp2^vs4M;dF_uDj^WKVRF#kxFFzRj%2c5G0WcZkyorA5m&;Nzh*Jc9a`*X3X3r zp9IHHGlkeTx$-G%W*MMayX5^s-! z^IZ0iph)@Y$H7Y@RDsP;Utby=@2+9!B|Hl@R7|!H*I|6$QlnX?0QX-lf)Dq9Tsub zmr&4R>-ffpAf{j+O3hsgB!)oV)6?Ub6FY2L>>_9k z8F|IC=D{ADNv(5KB;dnMI#2ycR3J|+ZG4O{lI??1?(xnglI=;ceXP<_QtjJuk4opL z^xIK-ByiU)(U+q2%zRLwhJXCorRo=;;zFKO;}pTA`n`d}XH^p%I1+AZr7h4PVco{+ z;|)ON_pY?H3(u{ZzRiQqK>p!!SC_+_4x8;9(^%KbEe;qIOwDioovC69$+BMXfAA_4 zQU9rI?PyP!U52L2*UF<;t_n?kYgE<;T}px2ur*K6hdFE0u%~(R7a+C5J!HgVvG4i$w;DcXt zHG{-?$!B|sO<{nlKE0KEjrq~LfvCU9a@Q5&73vGL%7DM&cW_kiWon(Tl+?q zp~&q?GXgEL)m7#D&_$flG4Wcr5=_3=d$;>9z8C}^(QC@8lLBWAym;hx{iZs8$<;5) zzQm#ufDMeW2EPcQ}wqhMWJP`p+D{0qG=6fQShNZwuAHq;n5w=lCk1^;=Edrbaq_w>t|jSqOyJ4Gz`( z`r%m5^!Sb22K5;I?N_Wm8&C;+tqOLK=c!V-InExx>!4=d_H-()t%r zKwUG>renaveZ-4ejtMlBPkca;jzp+)oRzz#=Cf;!4LJH?AqwtAZao}PyT#zqE)_7Q zf4+_pdC&b_!pg2+8?%BN z%a3K3mqgqeOQp&y$f1~REF z{qPj}&D5Jp!P#GKG|2H$%4>S2duu@MZwv(`{k?S=(du?_JB@3qKZCd&mm6e` z#=ij(-_KWZwHiU58Bb2@2w^)7S6{fwrpg}^Kf;PTSEl_mhc+G@G}0@>Gnh*oKYz*8 zW*Dc$qmR{urQCcspzQK{n6D-0P~^m#EmUTgS8D!H6JXhHaL}%qGLeHvHC2=bX;@e9rg# z{Qh}f&vX9jnC^SO$F;6?UDx`gqG%ee!5?=F2?>2CDUn0#YXmTlaa~qsS8bDjHZ)&S z2Bo&0xmL^kUYL}w?Mb1KBSI^{K)qcWWbHLQw6XLv4=uMsN!J{~IXtY8%q2@um7D8E9o~zaiVP1aIRX)NPjix3gTmRWL<3inv$c4wT|H zR8`v&Bmjw1kxC3MZ`sw4;#B70gS*UrthcLTG&^pJ7hjBkjLIcwir9Fh)Rw(2 ze!DiHQ%3|e5WAXu>q=wchPXF( z_{+pc4BUlGRgbBhyk2g$d(5-Jf>!%OI~B7@gQB)>d}ap+Y?9y>(vmla${QiBW=ETaqtyz`%T15%7p~Vkxd+vw%UYMk_wpOi63Wh;Yh}c$13!WG`)p0aVT^*w zR@DY0DzeMg36WxW7s&ETyB&{_Xmu(381N zXA-^>s2TWZ=??3Z8IbnYVrD}>Mgjv{b}p7@b$B!ggy+tY`W$=WSNEXW@5Wf{<^k5- zssIBFyoH_Oqd0~GlH7KwLB$`xl$_`w<^_$1rm$o3VHMp`@++Wuw=N`8H++)Vi5(ES zeZdaRwI5R|pK7ZGKiI*sn12Fxgk%E!@okHkPqwH3i$%<2?f#nnvyv+KAEQnLqd)px-;PNS#)aPnX&EJ6HD%5x)*o7ynMIh^Bi0R zM=%)6PmidH=`8plTr86~_hfqNI1onc_AU(?Q^_4I*qYTDYE1+4oHblbtnX*9;o#-> zl<6)6r>U+V!mhzgm@ayKR@s1~m3j^aHjBOBV<&kpQ&qf|IdQYnH~`}uU${2krtj&} z!(7%qfZ?KTRsEGDAqNu3l`HRLcATt~LlpfZyCTY}^kWsweuR29v8Y5l$KYl%e#U#B zzxvIeRI9y0=>cTmL%!s%+fABzyVveXb=?ix&q}@r^#Y8ho*C18b)KoZ*qgpfK(zg%j_N(sB0$?UOCp8+M-_ zsW(rho81F8w#^llqYDsx?k1A`evJe5WW(7YL=&}sIe^2#-8jH%-pYL8a4gqmL8{3rEbX;S&VF~nUaAYy^PNvqrK57Rq$w8zPU&YB>6i=IR$hlbl-d% z@HiW1e*jw%O92nN^W@1t5Mtvd+n0@vPa-{-NjYt}5)|wkXFYDg$j+43UB(goA1YZp4 z@GvP@d$lWC&h}Sm18B&_qkJedH(hnNFSn%-SD7z)jL0Fnw4A(<;;Va(@gBI^Zq(*h z{8553A#+5I{pOb3#jY5fupz+@A1!-k_M*D%# zr|J5j9%jBiPyBTQ0rk!_r$B>+^Lpm`V*171yOiQp{Hsg@Z7lgqKY8{(dQ-3Hvd;tEL@u#< z9=mp*2N1TZg^P`b#W=B}PQji3DGwy@c^{uw=5yPrZH8#M3kB?oFri5Up{P*irEAwd zSIKw0xx8(xBN}zHrsC)_#S0fw4Gp0rrv;g;mZaqB)NRCzsQhvUG2M`341aOXtNVN9 z&J5-=8A?&ejl5kdOs077ZK+T^f9D*ezsiob&{7KnpGkCCB(y0l`+rsPbplC|wydQ8F=n1n)=Uh#ksK*b7o?wmwaCAx4nDYDmP zaU0wx-!|p)-CsduQZ2U!Rf@pzv)DEr?ql?>Qd~fj<$zQcd|B$W65Y&EGgLjy6qbMg z!+B}SM|?3kY=6Pdyu@_;WfUedNZufH4&l|7tEFwlvHD_CXaWpd78;sfRROFIoTF`U ztWh53kr-4^7U*|uN;lA45Msbhew4wNocmZ*0>afb;EE~v4Dh=PA=K8&R6qK;0 zzG#vI!VRgT-rr{jrTG`yb;s3c1CDCd8BqCEfaAFlo{nG`0ARR#V8BB!E$ z*|mp#;rFNaZ8#&~t+}m3J965d+dgc&F_HY&*=e6-ASlw#Dt#$TTN@UGUjBdZ=>KJ0 z15SvTL+XXehSCN?e=ZC8kB0$&`mquTDk=d+2~9%e0OVri72kFO#0k7$AN`B4xEwUu zZ?+Q4ll=rsBT+bk66Rl!RkPgq`-G}<1D3NWV(7jW{1E51)@=u7H{e`(U~s9>+-F{g zRB8Jp4%*o-Up0{tEEFq9`Tf-tz%)RlHvh-yT@e$)N3+5`a``!FpTO0d4w&$U zry%%>ws>+&gwD~;EP-^5$p5G? zjJ1Jg;f6C@bg*jv+%$P4yM0%{!i4%Mo9i=$P|KMQF(fo;l^bNq#A{@_NR?-gGt=Oh z!12lJXj6hepJR|qqb#1JxnLI(uOWPN;8Bk}fQ@zFS~&gQSX|eOUOrA@P~saU8Vy4# z9G7rx(i;E+kz5E~qY^L({;`{=Pj&-;W;fm9pran=8f@lmNFQeBakTk|-GO0Y^v4AR zLO_UqW05Ni>-^M8W6Mxd;qt7-um_Wb2R2)95cxKlttF0jW@Cq!<2 zR77IDPu3K!Af3s*_(wV?EQXih1l_G%Reg<8XYD z_#>yJz2+_~7!A8@mbyazM#3~*zQtP5E}8JNnKh(0xP11`@p0UWKGvY~>p=6ahzn|8 zIIp@X>+}tgD6jZ@evYB*vu-Y=`*Q>f>qnNUil9;q#8+wRiGHg z9u4z7e$wI=SSAM>3piGV>~m`qKLXf~E<@g`pn|fq9vf>OPdDyu);m$;^EJy2=hx4ASC3x^j?P1u@yfs-SFU?aCm7qYN zcwml97-SnK^=^Ifezg_1JxMS{JC~9y;vLmF&XQ4ev5A7E>fi)%avhn529NNHKsc)V zjMkOOv~!H22{)C$gR6vB(1$7EvUorxCs?_c(I3Cb;w6=Sxke4#C|Kke_QFy<1AOUY z@xgYr0no3Hg;2cM4E9f|UUmew+C`PC!?>v9zke$n+JPO7w~P zM`OZP`BJ{>{B2~t#Ju*Odk0E*>@Gj*J%pqO+Xv_6_DCjE+dhlx)B2Jsr{&4zz_;13 z0NfG2CkLej&5Qbtg00mRW82p`>GDk2ukS2mWv?j9fst|N4NULS(EzW8(dQSJ-Ud=$ zh{CEf29BRl^n=xIenkg<#$(e=gg;-a%2K2l{q`Bd?347V^Vl}i$z`^dMru|}R0_}0 zK!F0DW04;UeVy3rUSvUD6PKXF(MAT@o&R5h&4H{^m@@J)+Q1{^0K!i4BRII|QW z1W7!Mo{;?dyz-=ljbQTYF2w_`WEzM)vw@;bHFtPUQ}}wmeU|execo^_7=%RU!eYsU zuiJK4VKD)d{YRY@p1@!orD1064jQ~uoh%y2B{qCWMHO6fj9cR$;Zg=n3p6bjD-^xB z+Qe3szFD!|?sE!O8muEhnoCS@-|!_j7`;rlCMtWvXogbm(Pu9=*?5JtC z@;Srd{7g+$`y_44p|PsVsWyvcaW)=WMl!FgO(>?6V*WTZWa;VPd1Qkx{l*1LhwT`^pDzu zOC%xA=yz3bOL#5OKVH*~nlz22GpTfo&uhmum{O&I@z&s-q!22%CaHv*?GI`@t^EeC zqq+Qc)A^#`%vVqg5=9H<`(gsXd1}=0R7;uvQbw(tD0vMwtkN>5ksBoKmulSGT;0L* zWAb9(Cw<9GLUMs)lkxkRXB7ka(U2yCS?$lW2l${IuilaGo9ll?#Xl$hHHDnX7m^Ew zu*5K>3!Q_Fg|#J9ZtIq; z8V@HI9Z&CgV6$E@fSSR{eQ8lRb58?q$C7Hpt37We5^m1O^nj`GB%Yc9KAp#O&%)Wj zT@jpZi)>^0xN{aWGKc#X`d-V*+3hw9P-+pIeKjlTqHKqL_u`H2MCl9_97rt4CI7qi z7kX4tnH_daB|k&1s3G4p953uBj{tIln}wi(bs%Sw4u>omV)aXIc-4_z*7_OUdG_z$ z_HPI@LpB@?E)NDqdJAr6PZ;%J)yF95%G-+y6~KxBdGuVw7s=C0UIRxOPZ5gGAtUq# z8KE{SYB|8M(zf>a==j5tPKFwRFE?MWQ|{nSV6f-YC78OY1dGOatg1FQ0c$fBsI8vD(n{Y%=pw$6vKtw6NJ^&5LM zNfmr{m0AMKsmH`Xz2h`fh-HQVRJn8v^t^x6_!r^fB1NT6-G*4SxhCJLN4TC^o(Ns| zU611)lx7Gx`Bv|i#^t|GHJjIOrF@ggG?puq2D^KVKl3H-VoWCP zHvXJh3m??&e$!w~xY!YzC+94#zY|Tk8^fPJc%#5AYE`+jgLPk;|CMh_rv~rPv#6{p z6j#{=%$HqrYGYH;-EsN@V18zJRMH45&!!^oX_CKx+&R(kxH`515I3q)*x_@q(f+VM zgSdL&&P|#$uq_O{a&3r7r3}(xiWg0srd=Zual0a_3+_?802q{9jz}T(Eja9bd{fOC z?9Ga8qam9(Axzp_1SWEZih>dMkAQ_PbHlnJx0k-j%#To2twwrB zR;nC&@DD3~RAIoKtBUL5f09Cz-rI3BfE5EM4>;3CkPSxMo5~_&Ig+6stQj5OGxd2Y z9d`2OanE3uf-lu^&8M^=B>Mxa9DnXp%f-7NHtf|S?al;%hP4P>dU8bS?) zE(pBjzJvCJmd|-h>OGod!+8xS-t~J(3AVjDX#-_$O+?+_TrXydClu(M4pAjU;lnP3 zI7f96^W93X-EZGw?~dYe&@3eVj|YjBI?q)PJ`?)NSPJjKOqs6vrB|?_FQ>=(bjmpN!bD`cT8M3^NTSc z(q!#o#9Mos>|r>gN|Ux73RCQrTd9R*-KXn${?rs`wAAq51 z8NYP@%S?!~m5oQIeekKA%w}h+zl~I;3lb6zPt-75)2!i`5Q@Q{@%P&zSvh~M>X}`P zVEg6@dm`%=OFL%#WaKRK0!qGU-Kxi&iyT8y776PADw~os+ui~HEuo^tD0KWq5m96M)4&#{;Bq9ex2LKC z^sYT>u=ZHb_R=Q`gO(x;`;}B^G2{G@$lknwu*GJAyLy=ONld<6I7o z+nMS(C0wunnd>I;J?qopJNH7$T?-)xf-yjcrj{1BsHmvKv0d!q{}{qqmKwy@C^V2@$58kx2-PQu5bCS9C9s;A`-|z;=`pj)?oU->NO6O!{9o@If0en7o z(2JzkaI|8ec>KTq4SI}S&b~x$bG>WVe%HDQcU%%zUwOYaI9}Ff|MKDX;&XcO z9PaO)89#j-<27!N<+BcYd@WZ$_;4;tV#_5<=X!a1cb3>8Iu(~7h^NDaot0qmYb7|o5`_BGSHXd>Xa=^3soV;swL$Mp zHjVC*AFH1;kr#pD4$aK1WhzrT*m9RJMCs&{DRBw)Rw>fBBW{W39(KNxb`p$fVMx zv}HqucA=z;W7X1mW+lb%=d>`lVBOfl8IO%?97CzfY$<0LP2la%9yinC=A^x`IZzj8 zNsS<3?@ynrT{^Y!dv+|fxieSY1HT`>JILCdy4`d}-Y2i7RtqT!tjZkxwB_FI{ZYN1 z@G&Dr-DGgo`E!a`FSAOS6UK_Nx_-nly03rSVe%YfgU@CBMHBnC&QYJkk55|u1g~x| zcN9AQ^f;EOA(xq>_a+3B<6$v~6J)PvK_4?_eE!AszUq3D;(a0#Jz`1%x6oYYw$TrV zcYq-8eFWl7ZT2K^aKHK$Di7m?E%!_ZqjO^APl+vh)7f-cW5-}a=hK|JR3|WYf z3^uDcNl;%^3U<@!Urnxxli)s98ppkagiuHeC`?4f7Mu6ZiDG5@k6p>}AD)VLWBxZ! z#Y3bT(vKN3gDzLMPOz*yIS}*ey*IFNP;>vJHpnHmHw8_VarV^lCcsHdurDo-3VOdp zNIo3Iyxm~FoGFvm!45@VvtzP**xSc~XpVGl0j;;^R^@*^}h|ucw+LbO+jmr%mt|zxK^#i!!IkBo0m!B{KK9ZgM15gmDhDNpqpO10( zELn|WctVj%XewC8a(oA>*xm6j9NO%40!6(3(6?)=v!jEFrp@Eg$%n!Yb}m1Zr+kj* zxn@Q6vUK%omwq}s2nUoo>4DD#=8`?yszSE*p0UmW0o3)oJH=lZnaLPl4A{*eJ@`7g zH`z_86YJ_Fe|gST@5esdC=N>JvS!OY2V@!9zJD3h4n4V2*^i(1qQMUrdpQq`9XdxC zD_B`y@7P$6vz!}ay#48sPMo(LGh?QQ*TEa1wPVu7K({N6d)`qtmHs^K8?xX;kSh@h zbqd8kCqWX6irD5LCS)R~;yek;rXr<6e{0KNGGi*IOl}h=xOt>(`FZA#?2>`7uU~Uh z>^9Fbot!@B>iQJ})3PY3gi!O652@%bq}?%cPj4b8=8X@294F7SJS5xT^I)=TIiB7l z=tJ?m_MwLMga=!Y@kHG%+24&5(%pJ0{YpHo;2hFv;0^)Ge|{U(;izbdIl%(X9!AuG zLEFADV2g+sl1=1%z%$1O0UUEPZ$9eh#Sb=82|okbteA@10o~z^p0_hc&anv|zG>$u zD{n)Z>a%K3C1E|YU{wC0OZ{G4R@S{XsXQ*^aD7f$hu|4p9qJrFB619bsabsF2~2qB zBd@s&W5e4)G-)3ux#&5i?Z(}deaR6kzUaEa^G$LgN_L2L%IBSj)sT!B7LS=@sju$5 z9>iByU9z?s7;t#nE@;^KJN|WENC2zz{Tcn7F*BCE{SE7`fu2V;9B0Q4w=IZ;l(B(K zu$wS=-;4hewVa()@R-}-A=ViEt8HHQP(3?E9pHg(b6f=wtLr~+E|{YSqoGeO*k3%n zB&NGKI%qNtR7>$c02ww>tF@P+jU(rT@ z`Ffw(8>Qjl==U=gc3kgIh`eZNNMq(s-?#|%#0-5vfCd{%jf`{?2MvTT+cZ{arP?m( zfyJil+fwWIr}1Gie1V4s%^G{fMH1i!m1CzRCu~UYvGMM^GutN)+Ck{>k&>D!Pj;mJ z0j<{!aMXPNULMrLAD*8*%@Yy(|o5RMc>vdBXOCl4NwGdbpad zZnyFfL%*TcF;0GeTDI$R3==0^y$xsC132o8#dzlGd2j}J%Q06;Z<+_L((z;88H80T zjco7cQ{l~m^I*$ZjW6|46z!0+l;@Ob&Z|~~tE>$Bo5zgzBdGaA z2%5x4=iM(R^}${w_zT-nPN)Sy@TVGE4Lv&kZ%s8zH+C^zH4}keJU`wJ&;UF9PEGv? z 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

mlP48$0|(c{-MM385E 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

dA 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 ZFMW~``?Gd5nW@*096F(qsdnbde*jAY9!LNH literal 0 HcmV?d00001 diff --git a/doc/readme.md b/doc/readme.md index 9a2240a..804b6ab 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -240,3 +240,41 @@ symbolTable.Symbols.Add(new ArSymbol("my_symbol", elf)); ### Links - [Archive ar file format (Wikipedia)](https://en.wikipedia.org/wiki/Ar_(Unix)) + +## PE Object File Format + +### Overview + +The main entry-point for reading/writing PE file is the [`PEFile`](https://github.com/xoofx/LibObjectFile/blob/master/src/LibObjectFile/PE/PEFile.cs) class. + +![PE class diagram](PE.png) + +## Sections and Directories + +In `LibObjectFile` all the section data `PESectionData` - e.g code, data but also including PE directories - are part of either: + +- a `PESection` +- some raw data before the first section via `PEFile.ExtraDataBeforeSections` +- raw data after the last section via `PEFile.ExtraDataAfterSections` + +Most of the conventional data is stored in sections, including PE directories. + +A PE Directory itself can contain also a collection of `PESectionData`. + +If the size of a section data is modified (e.g adding elements to a directory table or modifying a stream in a `PEStreamSectionData`), it is important to call `PEFile.UpdateLayout` to update the layout of the PE file. + +## VA, RVA, RVO + +In the PE file format, there are different types of addresses: + +- `VA` (Virtual Address) is the address of a section in memory including the base address of the image `PEFile.OptionalHeader.ImageBase` +- `RVA` (Relative Virtual Address) is the address of a section relative to the base address of the image + - A `RVA` can be converted to a `VA` by adding the `PEFile.OptionalHeader.ImageBase`. +- `RVO` (Relative Virtual Offset) is an offset relative to an RVA provided by section data or a section. + - A `RVO` can be converted to a `RVA` by adding the RVA of the section data or the section. + +In `LibObjectFile` links to RVA between section and section datas are done through a `IPELink` that is combining a reference to a `PEObjectBase` and a `RVO`. It means that RVA are always up to date and linked to the right section data. + +### Links + +- [PE and COFF Specification](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format) diff --git a/src/LibObjectFile/PE/PE.cd b/src/LibObjectFile/PE/PE.cd index 4ec7bde..8e5f927 100644 --- a/src/LibObjectFile/PE/PE.cd +++ b/src/LibObjectFile/PE/PE.cd @@ -26,26 +26,24 @@ - - + + - - - + + - + - + - + - - - + + @@ -59,7 +57,7 @@ - + @@ -82,10 +80,10 @@ - + - + @@ -155,13 +153,13 @@ - + - - + + @@ -170,7 +168,7 @@ - + AAAAAAAACAAAAAAAAAAAAAAgAAAAAAAAAAAAAEEAAAA= PE\DataDirectory\PESecurityCertificateDirectory.cs @@ -180,103 +178,102 @@ - + AAAAACAAAAAAAAAAAAAAAAAgAAAIAAAAAAAAAAEAABA= PE\PEStreamExtraData.cs - + AAAAAAAACAAAAAAAAAAAAAAgAAAAAAAAAAAAAAEAAAA= PE\DataDirectory\PEArchitectureDirectory.cs - + AAAEgAAACAAAAAAAAAAAAgAgAAAAAAAAAAAAAAEAAAA= PE\DataDirectory\PEBaseRelocationDirectory.cs - + AAAAgAAACAAAACAAAAAAAAAgAAAAAAAAAAAAAAEAAAA= PE\DataDirectory\PEBoundImportDirectory.cs - + AAAAAAAACAAAAAAAAAAAAAAgAAAAAAAAAAAAAAEAAAA= PE\DataDirectory\PEClrMetadata.cs - + AAAAAAAACAAAACAAAAAAAAAgAAAAAAAAAAAAAAEEAAA= PE\DataDirectory\PEDebugDirectory.cs - + AAAAgAAACAAAACQAAAAAAAAgAAAAAAAAAAAAAAEEAAA= PE\DataDirectory\PEDelayImportDirectory.cs - + AAAAgAAQCAAAACAAAAAAAAAgAAAAAAAgAAAAAAEAAAA= PE\DataDirectory\PEExceptionDirectory.cs - + AAAAgCCACEQAAAAAAAAAAACgBEAAAAAAAAAAAAEUAAA= PE\DataDirectory\PEExportDirectory.cs - + AAAAAAAACAAAAAAAAAAAAAAgAAAAAAAAAAAAAAEAAAA= PE\DataDirectory\PEGlobalPointerDirectory.cs - + AAAAAAAACAAAAAAAAAAAAAAgAAAAAAAAAAAAAAEAAAA= PE\DataDirectory\PEImportAddressTableDirectory.cs - + AAAAgAAACAAAACUAAAAAAAAgAAAAAAAAAAAAAAEEAAA= PE\DataDirectory\PEImportDirectory.cs - + AAAAAAAICAAAAAAAAAAAAAAggAEAIBAAgAAAAAEAAAA= PE\DataDirectory\PERawDataDirectory.cs - - + + - - - + + @@ -289,20 +286,20 @@ - + - - - - + + + + - - - + + + @@ -315,20 +312,20 @@ - + AABCgAAAAAAAAAAAAAAAAgQBAAAAAAAAAQAAAAAAAAA= PE\DataDirectory\PEResourceEntry.cs - + - - - - + + + + @@ -337,48 +334,48 @@ - + AAAAAAAAAAAAAAAEAAAAAAAAAEAAABAAAQAAAAAAAAA= PE\DataDirectory\PESecurityCertificate.cs - + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= PE\DataDirectory\PEDebugStreamExtraData.cs - + AAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAgAAAAAAAAAA= PE\DataDirectory\PELoadConfigDirectory.cs - + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= PE\DataDirectory\PETlsDirectory.cs - + AA2AEgDBJAwRYAgBQAAgMgAEAkQRAbMAGgwAIgAAAgE= PE\DataDirectory\PELoadConfigDirectory32.cs - + - - - - + + + + @@ -387,14 +384,14 @@ - + AAAABAAAAAAAAAAECAAAAAAAgAAAARAAgAAAIAAAAQA= PE\DataDirectory\PETlsDirectory32.cs - + AAAABAAAAAAAAAAECAAAAAAAgAAAARAAgAAAIAAAAQA= PE\DataDirectory\PETlsDirectory64.cs From 7aafb96b0bd94457b4b9da060caebc17e6418965 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 26 Sep 2024 09:13:06 +0200 Subject: [PATCH 62/87] Update logo with colors --- img/banner.png | Bin 0 -> 4353 bytes img/libobjectfile.png | Bin 6489 -> 3691 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/banner.png diff --git a/img/banner.png b/img/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..b24db17b05a25d8ebab51ebd887de290547bb7e4 GIT binary patch literal 4353 zcmb7H2{=^i|9|Hg84AOwq%cyUC{dI!LkYJUp<9S-iLPWW4N5o@75CbTq!_YYTsx&K zF_uD;vDHYDr7PP^*_X_W|4Dv#d+z`D{O&x@JkNR0dC&J<&S!hWj+2Zyy}&30w~9>&)9cZA1*4y<5<(52j+J&wxMX0bumRM*(njfS@1M(MMsY@F z%@=q#>qLhWd_v8@wrya9m6aBdsj=N;=s(4Mi*NQ)45LZsZ_e8aqlaq_PWaaMyH1Xi z1swDb1DqygBJ^J6)>JCfJ0Y{1C*UJ=q5OgD01gflCZ2Su_mnYP-GM^ZlnGNa0=DpX zlIObu$WKt}5fb$Ja8SIjUwc;?HTTegdRH{os+RvKkSXnbfP2&kPu41JEA}qpI!4bf50a#Ytc)jR164GDz=0Zi4^K zjX(&(KB?E6SWl1XnVPJ?paT^Fbwte30L@xUz7Ej8Ox%d_%hh~<47Ct)$d)C097F8W zg*NMtVcT&qeZT&%mIMA)nMA|W>n}0g$I2%X8I5gLNjezy2%pdv44y1Ap)EHi)sDR1 z6H?o#pJR!aKw+Z0VtNRpo1#Mj`l9Q!=T+A3bY$2B;b7fdcN1iOUtvT25RMsirX7@7 z0aazQ$?-ERy>$FHK+zMzf#XKFU-&Dk(D-rt!-0OyrQjg$8`S#fq2irg+Z?P`-ey0w z&m{NobOcuw_iX4lbFb)O?QfqtA^-UDs6$Nx^6?L^l=Y{GXctA$9drc~<1^vQ&pN6T zYdh$+DY65a&@!+?-swJS$o}+NMID%-ls>9B=<*oWx}rxje($jieZw=eK}?qfs==Fk z3OPy@>nUO+=LD+bG6~RlK-BmXS3(lySSpEQfi#F(CTZf7kG>th111ZfLYtZ`1Le=8 zdhQNzQ!+&G3j+QPsQ-s$e;)wp_WTY}(tB&Ptxv*IQHf>+=i|~Q6G?<1D>V$E!%J?@ zN(FZd6QvVzd-A%TROc&^le#<a~?(gRoo{#7bp6+O*cJ)aT`Z$=#OPz zGJ5T%BT8*&6YJ&mMn`OFB5_k~UnjMtvRkau-F5TnRkJnYvc=OBDz1UmM>+P^`$O$>qpi{1{ zuG8WlU8Tk!3>K|sDuA%8LB(P9DCRTyHgxwnSy$=%U`yijv>-W}K&0jxGa%WVZp<8n z7thmZb_|!^`eAwVydq9y=~CS$G(N?4ym7&r_C8D1e>7}_>C$4#mFN-a9#0RLu6H(M zxE$1kqWkX%c$Ap+dzM{r(2o=t-*)%f5!Hc=K4!oxTRx9JaxX?M<*}_+>vbOGO3RgX zDcnU%@mRIHJ+g>)NDyn4g%~QviQ6Vlg%Lsr*#WcR&IcJTb!;@g?uUip^1))N&x@+= zNl}H!)gcN1)mY!CC-o;h{eg-N)+MX+7w1=nW=~Xgv*QK4aqya)O~ZC%S*vhov0G0N zv~9>o7oXy{nD)9cTv}Y_8X{!l94}ON=_gO|B zb0s7A%ao4bxng?NyUBP#R|z=8XEbfba;ax;rd;l+{aOBM__o}=UwohQd)XrcVGVe< z=NWyHKl`aXIjy@>=uKK@f8q!F>9`42WP2T|C!;^~EGIK4iqqgf*fOUWv|caTy^Jk~ zSOnU^DOk|KW^**=N)ShW2!ZZp5$R>R)@-m&@hvqUj{4?=wIIB(HK*9)eL)G)7@?=y z(=5veiV${{qg3?!SdU3$?UxY0Y<*Ec5kW3$iQ!QD>H=PH6{2r8<8KMW@gSr|TJWq# zsljmaN=Z1JUbwDXu6{)k)s+=@t2S#1{fHq-TkTgMWR4hlnlwtqrNikgMS6Soe-h<*_j`0!CQK00Y$pjZMX{xBfkXab<~VFdgR z6efL-F?Zp?IsA6yQjrfMsu2Ctynu4U?#?L&;}P6+m|LT}>}XU~xz3TZ1LD%xm($1X zri`?p0dfA3WmD-BLmor`^porJ0*AK%)I~fWHWwzsiuLm98pxC)klRXtDn*LWnGiaQ z2h=^Eema0$^?M@*v>%@S_^xHNV#}K|L3zdDW{lsu0;?WvttPmcE*CdBFMn!p;kqya zob->5_uuw2A8}rHlz{s6fBF5EZIjxX(DQMuM9ieSd3}o3KwC9o@?RR|GX6eTki*KP zY<=Z;{G49BoGI-uW64We?4P_LHsI-*FJa`ydDm%}^gT9<4;ruP(1f#Cuvt>YDdI1= zTvzr#kx7?;urjBe_oQBJzDrqmgdZ#hdnK(NGV;9&LqxlyT;-#f7{)9hZ!h*#l7 zP#Dp?%g}cFZdU<_9P5j)#E#C%(}RlW6$*qMy&njp+|!jCy#stbg7Z4fg%;^Njq{Uj zNA7(SN~rLu7%4D{@!MiuUYG99-cDZ%|Ev>vzvbhKbz^Rv0Ed=RZNhSNPNDGzvrm3c zs`9SF@`HIzSHI(OUN4R1wy^>a`AoLF(YdCJ#_zEpr3V$1^uJc0U%E22d9V7(s1I$g zD3?BTOblg>4fpr6jP&y@A7~=^_WKB`^&J=f(`Q~W<(+P_yh|fqjHRU%CPb5f=@B-@ zHe-XX#{+L1BRD_(5hR>fHL3;E9l_o3)(09|elO}o%M2EJoI&EPKh z7A)~!RO7C9UK+=v;;8_A!*}a2(}Hj2X+GT3PDcb|{6(nZfm5j-S}vD!j2x|v->Zqf zGz3sxndG^jmF*cF4Mb%KEB*Y=yp3I%d@6Q04xno*B(3ar%uPI2R@HunFO)%HLe?p8 zBQMJa&upWoqVd=`7>Y;OkZV#8xvwO?<*W^kac5eB{7<-2naBp*@hX1LvesFvo)w5# z8nWFo5-0ZHvta8L;;(tByalnA8YCpPivC#h?t&#b5ro`e=`O?;@T_Ti_hMX@Qu7qM zrKU|m?USmUj*?j1>=4Dr+%P4`veSGaD5uiDOG{822e-bFQA(NIEB~rXr7F)-=TW=X zj3bR!TX8o98Fk%pQc~0y+afT(!}JC3slDs^qQk9+d>x~YO<2-M7N0&p)v3UAOT8n3 zT8*k*O6f*PHnfrYxm495o=$HpZssiEPI6BJr7HU#tBp~dXe4L#=9j*U5H_xZoBjHp z+12XULA~6mWBbhQ*$tDL7j(*Xr*z&D3jH24wH*_#bzq2A#~Cq}X)a3@s*js2BS^1G z7OF_D2-@^^F+4QNFCcw9d@Jl|TN5~}A}=HPB}7=7XKUvLNmoC|EiN9RRll*F^PRif zj1?;6Bx`wdb@f%ph8uK(iUdou7YF%#o1tCX_;JW7V_9!@>Z&T$H0{c_?@#wFc-K2b z;a~9XwMY5A3NDW1e?QbxTB|ifDy!Z#%Iym2)mb^*zKQaJblr_@7t=c#ytS})$+5QE zh{(GC^@#pAGyE1dzMbSxz8X0A0QG4zpcbY!Se7j%v#enwVIuxvmW0*kxsp0H*rjY8 r*(qOe4P6S+GsWMJ{lC||)BKU}t7*nwt)02k0Qs33kq+e>I$ZldTWv$t literal 0 HcmV?d00001 diff --git a/img/libobjectfile.png b/img/libobjectfile.png index 13a891acfcaf4f2c77f4024fb956f4cd22961b70..c163ba6d6855b61fb9aa55bb88e97db15052d3df 100644 GIT binary patch literal 3691 zcmcJRc{J4jzsEl_W0@HlBiY4}C1j~2S!c#FS+h%ov7~I#)ECucFeq6DaTQrg_CgAg zF!`E7Ojms=!dM1b%bFSEj_>c>bMNimbARXlaX;rg&+GGgpYwWuUZ2$O&P(9+#A5s4C5T-wfN0CXT(g3k8Wks&J6Ig(~5(MT<15MU)RQWrQB(x z56?RVgt(~$RNO>Mm?A7Wj zO{lOc&qr7hEril~cwpJh!j1OvE!0UkYjA3b1N!u~J5g!`nl*KIh?$FPx%5%RzakF6 zd#I)9*abU)%4&zY`KgmUtih$$9mr6k%f4w?+O-Ocq1$kBW@+$2s$VE;(D}A~K2Usb zPB}^Q-ptQ0zj{_!R|hN(cU2~^oeu__dSWiGFBJ_-4}}WD>F?j+qC%{1Q|==yWT}r4 zSaOP|Szu@*8E3vhoA`?Zs_ywtK4HuFfeUfU!nF)DHfLD;wFnUP92vJLyI5+~=~3aH z3g87#nns=d)_}fUs{o?Td9Gf(D==SehMUl#KDNb@MQ6=QL}Wun6!K5=E2OT>#v>U# z#{Af1p^2Q0Uk$iPQea3k^%PanC#951|HOt$ScGV+H5m}=f$VTlv095*Chfp1F zy{gB3-1ffsE@^zav=k{d_7IbtjvTR$<�>|@-LIKO_V|q60M|EAGgZ8li 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 Date: Thu, 26 Sep 2024 09:23:53 +0200 Subject: [PATCH 63/87] Fix tests --- src/LibObjectFile.Tests/TestsInitializer.cs | 1 + ...rTests.TestPrinter_name=NativeConsole2Win64.exe.verified.txt | 2 +- ...erTests.TestPrinter_name=NativeConsoleWin64.exe.verified.txt | 2 +- ...erTests.TestPrinter_name=NativeLibraryWin64.dll.verified.txt | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/LibObjectFile.Tests/TestsInitializer.cs b/src/LibObjectFile.Tests/TestsInitializer.cs index 04f353d..bb8384b 100644 --- a/src/LibObjectFile.Tests/TestsInitializer.cs +++ b/src/LibObjectFile.Tests/TestsInitializer.cs @@ -15,5 +15,6 @@ public static void Initialize() CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; Verifier.UseProjectRelativeDirectory("Verified"); DiffEngine.DiffRunner.Disabled = true; + VerifierSettings.DontScrubSolutionDirectory(); } } \ No newline at end of file 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 7bf5439..a881069 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 @@ -244,7 +244,7 @@ Sections Debug Section Data (RSDS) Guid = ffed6f99-5708-452c-a889-ff343b6ce898 Age = 6 - PdbPath = {SolutionDirectory}native\Win64\NativeProjects\x64\Release\NativeConsole2Win64.pdb + PdbPath = C:\code\LibObjectFile\src\native\Win64\NativeProjects\x64\Release\NativeConsole2Win64.pdb Section Data [07] PEStreamSectionData Position = 0x0000248A, Size = 0x00000002, RVA = 0x0000368A, VirtualSize = 0x00000002 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 65fa9f4..7759071 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 @@ -230,7 +230,7 @@ Sections Debug Section Data (RSDS) Guid = a28d7ba2-048a-4315-9bbe-0edbca526f59 Age = 2 - PdbPath = {SolutionDirectory}native\Win64\NativeProjects\x64\Release\NativeConsoleWin64.pdb + PdbPath = C:\code\LibObjectFile\src\native\Win64\NativeProjects\x64\Release\NativeConsoleWin64.pdb Section Data [06] PEStreamSectionData Position = 0x00001B4D, Size = 0x00000003, RVA = 0x0000354D, VirtualSize = 0x00000003 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 bdde20e..4bc60eb 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 @@ -191,7 +191,7 @@ Sections Debug Section Data (RSDS) Guid = 9e24e83e-5203-490d-b4fe-ac81f739897a Age = 2 - PdbPath = {SolutionDirectory}native\Win64\NativeProjects\x64\Release\NativeLibraryWin64.pdb + PdbPath = C:\code\LibObjectFile\src\native\Win64\NativeProjects\x64\Release\NativeLibraryWin64.pdb Section Data [06] PEStreamSectionData Position = 0x00001855, Size = 0x00000003, RVA = 0x00002455, VirtualSize = 0x00000003 From b7cef7151458f0e3b48d587e9d4aed1a4c886067 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Thu, 26 Sep 2024 09:30:05 +0200 Subject: [PATCH 64/87] Update readme --- readme.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 591ecd3..be144af 100644 --- a/readme.md +++ b/readme.md @@ -2,10 +2,11 @@ -LibObjectFile is a .NET library to read, manipulate and write linker and executable object files (e.g ELF, ar, DWARF, COFF...) +LibObjectFile is a .NET library to read, manipulate and write linker and executable object files (e.g ELF, ar, DWARF, PE...) -> NOTE: Currently LibObjectFile supports only the following file format: +> NOTE: Currently LibObjectFile supports the following file format: > +> - **PE** image file format (Portable Executable / DLL) > - **ELF** object-file format > - **DWARF** debugging format (version 4) > - **Archive `ar`** file format (Common, GNU and BSD variants) @@ -31,8 +32,13 @@ elf.Write(outStream); ``` ## Features -- Full support of Archive `ar` file format including Common, GNU and BSD variants. -- Good support for the ELF file format: +- Full support of **Archive `ar` file format** including Common, GNU and BSD variants. +- Full support for the **PE file format** + - Read and write from/to a `System.IO.Stream` + - All PE Directories are supported + - ImageBase relocation is supported with `PEFile.Relocate(ulong)` method + - `PEFile.Print` to print the content of a PE file to a textual representation +- - Good support for the **ELF file format**: - Read and write from/to a `System.IO.Stream` - Handling of LSB/MSB - Support the following sections: @@ -43,7 +49,7 @@ elf.Write(outStream); - Other sections fallback to `ElfCustomSection` - Program headers with or without sections - Print with `readelf` similar output -- Support for DWARF debugging format: +- Support for **DWARF debugging format**: - Partial support of Version 4 (currently still the default for GCC) - Support for the sections: `.debug_info`, `.debug_line`, `.debug_aranges`, `.debug_abbrev` and `.debug_str` - Support for Dwarf expressions @@ -83,7 +89,6 @@ In a future version I would like to implement the following file format: - [ ] COFF - [ ] Mach-O -- [ ] Portable in Memory file format to easily convert between ELF/COFF/Mach-O file formats. ## Download From f7cb66df332eb84369427c0d7166288b474acb57 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Fri, 27 Sep 2024 08:28:37 +0200 Subject: [PATCH 65/87] Fix UpdateLayout --- src/LibObjectFile.Tests/PE/PEReaderTests.cs | 28 +- ..._name=NativeConsole2Win64.exe.verified.txt | 730 +++++++++--------- ...r_name=NativeConsoleWin64.exe.verified.txt | 568 +++++++------- ...r_name=NativeLibraryWin64.dll.verified.txt | 421 +++++----- src/LibObjectFile/Ar/ArArchiveFile.cs | 4 +- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 75 +- .../PE/DataDirectory/PEBaseRelocation.cs | 9 +- .../PE/DataDirectory/PEBaseRelocationBlock.cs | 48 +- .../PEBaseRelocationDirectory.cs | 6 +- .../PE/DataDirectory/PEDataDirectory.cs | 18 +- .../PE/DataDirectory/PEDirectoryTable.cs | 17 +- .../PE/DataDirectory/PEExportAddressTable.cs | 29 +- .../PE/DataDirectory/PEExportDirectory.cs | 2 +- src/LibObjectFile/PE/ImageDosHeader.cs | 4 +- src/LibObjectFile/PE/PEFile.Read.cs | 81 +- src/LibObjectFile/PE/PEFile.cs | 139 +++- src/LibObjectFile/PE/PEPrinter.cs | 38 +- src/LibObjectFile/PE/PESection.cs | 27 +- 18 files changed, 1236 insertions(+), 1008 deletions(-) diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index 20363e9..84d06f8 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -7,8 +7,10 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; +using LibObjectFile.Diagnostics; using LibObjectFile.PE; using VerifyMSTest; +using VerifyTests; namespace LibObjectFile.Tests.PE; @@ -23,10 +25,28 @@ public partial class PEReaderTests public async Task TestPrinter(string name) { - var stream = File.OpenRead(Path.Combine(AppContext.BaseDirectory, "PE", name)); + + await using var stream = File.OpenRead(Path.Combine(AppContext.BaseDirectory, "PE", name)); var peImage = PEFile.Read(stream); - var text = new StringWriter(); - peImage.Print(text); - await Verifier.Verify(text).UseParameters(name); + var afterReadWriter = new StringWriter(); + peImage.Print(afterReadWriter); + + var afterReadText = afterReadWriter.ToString(); + + await Verifier.Verify(afterReadText).UseParameters(name); + + // Update the layout + var diagnostics = new DiagnosticBag(); + peImage.UpdateLayout(diagnostics); + + var afterUpdateWriter = new StringWriter(); + peImage.Print(afterUpdateWriter); + var afterUpdateText = afterUpdateWriter.ToString(); + + if (!string.Equals(afterReadText, afterUpdateText, StringComparison.Ordinal)) + { + TestContext.WriteLine("Error while verifying UpdateLayout"); + await Verifier.Verify(afterUpdateText).UseParameters(name).DisableRequireUniquePrefix(); + } } } \ No newline at end of file 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 a881069..1909d2c 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 @@ -83,99 +83,99 @@ Data Directories Sections [00] .text PESection Position = 0x00000400, Size = 0x00001A00, RVA = 0x00001000, VirtualSize = 0x000019E9, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) - Section Data [00] PEStreamSectionData Position = 0x00000400, Size = 0x00001A00, RVA = 0x00001000, VirtualSize = 0x00001A00 + [00] PEStreamSectionData Position = 0x00000400, Size = 0x000019E9, RVA = 0x00001000, VirtualSize = 0x000019E9 [01] .rdata PESection Position = 0x00001E00, Size = 0x00001A00, RVA = 0x00003000, VirtualSize = 0x0000183C, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) - Section Data [00] PEImportAddressTableDirectory Position = 0x00001E00, Size = 0x00000250, RVA = 0x00003000, VirtualSize = 0x00000250 - Section Data [00] PEImportAddressTable Position = 0x00001EC0, Size = 0x00000068, RVA = 0x00003000, VirtualSize = 0x00000068 - [0] PEImportHintName { Hint = 255, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z } (RVA = 0x42D6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x27E) - [1] PEImportHintName { Hint = 262, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@P6AAEAV01@AEAV01@@Z@Z } (RVA = 0x4282, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x22A) - [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x4242, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x1EA) - [3] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x41FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x1A6) - [4] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x41C0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x168) - [5] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x417E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x126) - [6] PEImportHintName { Hint = 1121, Name = ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@D@Z } (RVA = 0x413A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0xE2) - [7] PEImportHintName { Hint = 1332, Name = ?widen@?$basic_ios@DU?$char_traits@D@std@@@std@@QEBADD@Z } (RVA = 0x40FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0xA6) - [8] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x40B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5E) - [9] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x4094, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3C) - [10] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x4318, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x2C0) - [11] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x4058, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x0) - - Section Data [01] PEImportAddressTable Position = 0x00001F60, Size = 0x00000010, RVA = 0x00003068, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x4346, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x2EE) - - Section Data [02] PEImportAddressTable Position = 0x00001F28, Size = 0x00000038, RVA = 0x00003078, VirtualSize = 0x00000038 - [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x47AC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x754) - [1] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x439C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x344) - [2] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x43BA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x362) - [3] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x436E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x316) - [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x4386, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x32E) - [5] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x435C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x304) - - Section Data [03] PEImportAddressTable Position = 0x00001FA0, Size = 0x00000098, RVA = 0x000030B0, VirtualSize = 0x00000098 - [0] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x44C4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x46C) - [1] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4566, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x50E) - [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x454A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4F2) - [3] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x44A0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x448) - [4] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x448A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x432) - [5] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x447E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x426) - [6] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x445C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x404) - [7] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x443A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3E2) - [8] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x4420, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3C8) - [9] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x44D2, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x47A) - [10] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x43FC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3A4) - [11] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x43EA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x392) - [12] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4590, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x538) - [13] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x4498, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x440) - [14] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4582, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x52A) - [15] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x44B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x45E) - [16] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x44E6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x48E) - [17] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x44DC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x484) - - Section Data [04] PEImportAddressTable Position = 0x00001F90, Size = 0x00000010, RVA = 0x00003148, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x440C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3B4) - - Section Data [05] PEImportAddressTable Position = 0x00002038, Size = 0x00000018, RVA = 0x00003158, VirtualSize = 0x00000018 - [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x453A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4E2) - [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x44A8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x450) - - Section Data [06] PEImportAddressTable Position = 0x00001F80, Size = 0x00000010, RVA = 0x00003170, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x4514, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4BC) - - Section Data [07] PEImportAddressTable Position = 0x00001F70, Size = 0x00000010, RVA = 0x00003180, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x452A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4D2) - - Section Data [08] PEImportAddressTable Position = 0x00001E00, Size = 0x000000C0, RVA = 0x00003190, VirtualSize = 0x000000C0 - [0] PEImportHintName { Hint = 997, Name = LoadLibraryExA } (RVA = 0x482A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7D2) - [1] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4654, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5FC) - [2] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x466E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x616) - [3] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4682, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x62A) - [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x469E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x646) - [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x46BC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x664) - [6] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x46D0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x678) - [7] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x46E4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x68C) - [8] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x4700, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6A8) - [9] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x471A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6C2) - [10] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4730, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6D8) - [11] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4746, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6EE) - [12] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4760, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x708) - [13] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4776, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x71E) - [14] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x478A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x732) - [15] PEImportHintName { Hint = 1159, Name = RaiseException } (RVA = 0x47B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x75E) - [16] PEImportHintName { Hint = 637, Name = GetLastError } (RVA = 0x47C8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x770) - [17] PEImportHintName { Hint = 717, Name = GetProcAddress } (RVA = 0x4818, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7C0) - [18] PEImportHintName { Hint = 453, Name = FreeLibrary } (RVA = 0x480A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7B2) - [19] PEImportHintName { Hint = 1543, Name = VirtualQuery } (RVA = 0x47FA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7A2) - [20] PEImportHintName { Hint = 1541, Name = VirtualProtect } (RVA = 0x47E8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x790) - [21] PEImportHintName { Hint = 772, Name = GetSystemInfo } (RVA = 0x47D8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x780) - [22] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4640, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5E8) - - - Section Data [01] PEStreamSectionData Position = 0x00002050, Size = 0x00000140, RVA = 0x00003250, VirtualSize = 0x00000140 - - Section Data [02] PELoadConfigDirectory64 Position = 0x00002190, Size = 0x00000140, RVA = 0x00003390, VirtualSize = 0x00000140 + [00] PEImportAddressTableDirectory Position = 0x00001E00, Size = 0x00000250, RVA = 0x00003000, VirtualSize = 0x00000250 + [00] PEImportAddressTable Position = 0x00001E00, Size = 0x000000C0, RVA = 0x00003000, VirtualSize = 0x000000C0 + [0] PEImportHintName { Hint = 997, Name = LoadLibraryExA } (RVA = 0x482A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x7D2) + [1] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4654, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5FC) + [2] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x466E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x616) + [3] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4682, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x62A) + [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x469E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x646) + [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x46BC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x664) + [6] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x46D0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x678) + [7] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x46E4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x68C) + [8] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x4700, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x6A8) + [9] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x471A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x6C2) + [10] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4730, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x6D8) + [11] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4746, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x6EE) + [12] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4760, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x708) + [13] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4776, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x71E) + [14] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x478A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x732) + [15] PEImportHintName { Hint = 1159, Name = RaiseException } (RVA = 0x47B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x75E) + [16] PEImportHintName { Hint = 637, Name = GetLastError } (RVA = 0x47C8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x770) + [17] PEImportHintName { Hint = 717, Name = GetProcAddress } (RVA = 0x4818, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x7C0) + [18] PEImportHintName { Hint = 453, Name = FreeLibrary } (RVA = 0x480A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x7B2) + [19] PEImportHintName { Hint = 1543, Name = VirtualQuery } (RVA = 0x47FA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x7A2) + [20] PEImportHintName { Hint = 1541, Name = VirtualProtect } (RVA = 0x47E8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x790) + [21] PEImportHintName { Hint = 772, Name = GetSystemInfo } (RVA = 0x47D8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x780) + [22] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4640, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5E8) + + [01] PEImportAddressTable Position = 0x00001EC0, Size = 0x00000068, RVA = 0x000030C0, VirtualSize = 0x00000068 + [0] PEImportHintName { Hint = 255, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z } (RVA = 0x42D6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x27E) + [1] PEImportHintName { Hint = 262, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@P6AAEAV01@AEAV01@@Z@Z } (RVA = 0x4282, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x22A) + [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x4242, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x1EA) + [3] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x41FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x1A6) + [4] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x41C0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x168) + [5] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x417E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x126) + [6] PEImportHintName { Hint = 1121, Name = ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@D@Z } (RVA = 0x413A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0xE2) + [7] PEImportHintName { Hint = 1332, Name = ?widen@?$basic_ios@DU?$char_traits@D@std@@@std@@QEBADD@Z } (RVA = 0x40FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0xA6) + [8] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x40B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5E) + [9] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x4094, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x3C) + [10] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x4318, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x2C0) + [11] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x4058, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x0) + + [02] PEImportAddressTable Position = 0x00001F28, Size = 0x00000038, RVA = 0x00003128, VirtualSize = 0x00000038 + [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x47AC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x754) + [1] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x439C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x344) + [2] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x43BA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x362) + [3] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x436E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x316) + [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x4386, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x32E) + [5] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x435C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x304) + + [03] PEImportAddressTable Position = 0x00001F60, Size = 0x00000010, RVA = 0x00003160, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x4346, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x2EE) + + [04] PEImportAddressTable Position = 0x00001F70, Size = 0x00000010, RVA = 0x00003170, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x452A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4D2) + + [05] PEImportAddressTable Position = 0x00001F80, Size = 0x00000010, RVA = 0x00003180, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x4514, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4BC) + + [06] PEImportAddressTable Position = 0x00001F90, Size = 0x00000010, RVA = 0x00003190, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x440C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x3B4) + + [07] PEImportAddressTable Position = 0x00001FA0, Size = 0x00000098, RVA = 0x000031A0, VirtualSize = 0x00000098 + [0] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x44C4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x46C) + [1] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4566, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x50E) + [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x454A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4F2) + [3] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x44A0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x448) + [4] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x448A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x432) + [5] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x447E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x426) + [6] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x445C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x404) + [7] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x443A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x3E2) + [8] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x4420, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x3C8) + [9] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x44D2, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x47A) + [10] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x43FC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x3A4) + [11] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x43EA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x392) + [12] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4590, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x538) + [13] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x4498, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x440) + [14] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4582, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x52A) + [15] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x44B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x45E) + [16] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x44E6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x48E) + [17] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x44DC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x484) + + [08] PEImportAddressTable Position = 0x00002038, Size = 0x00000018, RVA = 0x00003238, VirtualSize = 0x00000018 + [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x453A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4E2) + [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x44A8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x450) + + + [01] PEStreamSectionData Position = 0x00002050, Size = 0x00000140, RVA = 0x00003250, VirtualSize = 0x00000140 + + [02] PELoadConfigDirectory64 Position = 0x00002190, Size = 0x00000140, RVA = 0x00003390, VirtualSize = 0x00000140 Size = 0x140 TimeDateStamp = 0x0 MajorVersion = 0 @@ -230,414 +230,406 @@ Sections CastGuardOsDeterminedFailureMode = 0x140003278 GuardMemcpyFunctionPointer = 0x140003280 - Section Data [03] PEStreamSectionData Position = 0x000022D0, Size = 0x00000010, RVA = 0x000034D0, VirtualSize = 0x00000010 + [03] PEStreamSectionData Position = 0x000022D0, Size = 0x00000010, RVA = 0x000034D0, VirtualSize = 0x00000010 - Section Data [04] PEDebugDirectory Position = 0x000022E0, Size = 0x00000070, RVA = 0x000034E0, VirtualSize = 0x00000070 - [0] Type = CodeView, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x00003618 (PEDebugSectionDataRSDS[6] .rdata - [1] Type = VCFeature, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x0000368C (PEDebugStreamSectionData[8] .rdata - [2] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x000036A0 (PEDebugStreamSectionData[9] .rdata + [04] PEDebugDirectory Position = 0x000022E0, Size = 0x00000070, RVA = 0x000034E0, VirtualSize = 0x00000070 + [0] Type = CodeView, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x00003618 (PEDebugSectionDataRSDS[6] -> .rdata) + [1] Type = VCFeature, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x0000368C (PEDebugStreamSectionData[8] -> .rdata) + [2] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x000036A0 (PEDebugStreamSectionData[9] -> .rdata) [3] Type = ILTCG, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = null - Section Data [05] PEStreamSectionData Position = 0x00002350, Size = 0x000000C8, RVA = 0x00003550, VirtualSize = 0x000000C8 + [05] PEStreamSectionData Position = 0x00002350, Size = 0x000000C8, RVA = 0x00003550, VirtualSize = 0x000000C8 - Section Data [06] PEDebugSectionDataRSDS Position = 0x00002418, Size = 0x00000072, RVA = 0x00003618, VirtualSize = 0x00000072 + [06] PEDebugSectionDataRSDS Position = 0x00002418, Size = 0x00000072, RVA = 0x00003618, VirtualSize = 0x00000072 Debug Section Data (RSDS) Guid = ffed6f99-5708-452c-a889-ff343b6ce898 Age = 6 PdbPath = C:\code\LibObjectFile\src\native\Win64\NativeProjects\x64\Release\NativeConsole2Win64.pdb - Section Data [07] PEStreamSectionData Position = 0x0000248A, Size = 0x00000002, RVA = 0x0000368A, VirtualSize = 0x00000002 + [07] PEStreamSectionData Position = 0x0000248A, Size = 0x00000002, RVA = 0x0000368A, VirtualSize = 0x00000002 - Section Data [08] PEDebugStreamSectionData Position = 0x0000248C, Size = 0x00000014, RVA = 0x0000368C, VirtualSize = 0x00000014 + [08] PEDebugStreamSectionData Position = 0x0000248C, Size = 0x00000014, RVA = 0x0000368C, VirtualSize = 0x00000014 - Section Data [09] PEDebugStreamSectionData Position = 0x000024A0, Size = 0x000002FC, RVA = 0x000036A0, VirtualSize = 0x000002FC + [09] PEDebugStreamSectionData Position = 0x000024A0, Size = 0x000002FC, RVA = 0x000036A0, VirtualSize = 0x000002FC - Section Data [10] PEStreamSectionData Position = 0x0000279C, Size = 0x0000020C, RVA = 0x0000399C, VirtualSize = 0x0000020C + [10] PEStreamSectionData Position = 0x0000279C, Size = 0x0000020C, RVA = 0x0000399C, VirtualSize = 0x0000020C - Section Data [11] PEDelayImportDirectory Position = 0x000029A8, Size = 0x00000040, RVA = 0x00003BA8, VirtualSize = 0x00000040 + [11] PEDelayImportDirectory Position = 0x000029A8, Size = 0x00000040, RVA = 0x00003BA8, VirtualSize = 0x00000040 [0] DllName = NativeLibraryWin64.dll, RVA = 0x32E0 [0] Attributes = 1 - [0] DelayImportAddressTable RVA = 0x00005078 (PEBoundImportAddressTable64[1] .data - [0] DelayImportNameTable RVA = 0x00003BE8 (PEImportLookupTable[12] .rdata - [0] BoundImportAddressTable RVA = 0x00003C20 (PEBoundImportAddressTable64[14] .rdata + [0] DelayImportAddressTable RVA = 0x00005078 (PEBoundImportAddressTable64[1] -> .data) + [0] DelayImportNameTable RVA = 0x00003BE8 (PEImportLookupTable[12] -> .rdata) + [0] BoundImportAddressTable RVA = 0x00003C20 (PEBoundImportAddressTable64[14] -> .rdata) [0] UnloadDelayInformationTable null - Section Data [12] PEImportLookupTable Position = 0x000029E8, Size = 0x00000018, RVA = 0x00003BE8, VirtualSize = 0x00000018 + [12] PEImportLookupTable Position = 0x000029E8, Size = 0x00000018, RVA = 0x00003BE8, VirtualSize = 0x00000018 [0] PEImportHintName { Hint = 0, Name = AnotherFunction } (RVA = 0x3C0E, PEStreamSectionData { RVA = 0x3C00, VirtualSize = 0x20, Position = 0x2A00, Size = 0x20 }, Offset = 0xE) [1] PEImportHintName { Hint = 1, Name = HelloWorld } (RVA = 0x3C00, PEStreamSectionData { RVA = 0x3C00, VirtualSize = 0x20, Position = 0x2A00, Size = 0x20 }, Offset = 0x0) - Section Data [13] PEStreamSectionData Position = 0x00002A00, Size = 0x00000020, RVA = 0x00003C00, VirtualSize = 0x00000020 + [13] PEStreamSectionData Position = 0x00002A00, Size = 0x00000020, RVA = 0x00003C00, VirtualSize = 0x00000020 - Section Data [14] PEBoundImportAddressTable64 Position = 0x00002A20, Size = 0x00000018, RVA = 0x00003C20, VirtualSize = 0x00000018 + [14] PEBoundImportAddressTable64 Position = 0x00002A20, Size = 0x00000018, RVA = 0x00003C20, VirtualSize = 0x00000018 [0] VA = 0x0 [1] VA = 0x0 - Section Data [15] PEStreamSectionData Position = 0x00002A38, Size = 0x00000104, RVA = 0x00003C38, VirtualSize = 0x00000104 - - Section Data [16] PEImportDirectory Position = 0x00002B3C, Size = 0x000000C8, RVA = 0x00003D3C, VirtualSize = 0x000000C8 - [0] ImportDllNameLink = MSVCP140.dll (RVA = 0x4338, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x2E0) - [0] ImportAddressTable = RVA = 0x00003000 (PEImportAddressTable[0] - [0] ImportLookupTable = RVA = 0x00003EC8 (PEImportLookupTable[19] .rdata - - [1] ImportDllNameLink = VCRUNTIME140_1.dll (RVA = 0x43C4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x36C) - [1] ImportAddressTable = RVA = 0x00003068 (PEImportAddressTable[1] - [1] ImportLookupTable = RVA = 0x00003F68 (PEImportLookupTable[21] .rdata - - [2] ImportDllNameLink = VCRUNTIME140.dll (RVA = 0x43D8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x380) - [2] ImportAddressTable = RVA = 0x00003078 (PEImportAddressTable[2] - [2] ImportLookupTable = RVA = 0x00003F30 (PEImportLookupTable[20] .rdata - - [3] ImportDllNameLink = api-ms-win-crt-runtime-l1-1-0.dll (RVA = 0x459C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x544) - [3] ImportAddressTable = RVA = 0x000030B0 (PEImportAddressTable[3] - [3] ImportLookupTable = RVA = 0x00003FA8 (PEImportLookupTable[25] .rdata - - [4] ImportDllNameLink = api-ms-win-crt-math-l1-1-0.dll (RVA = 0x45BE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x566) - [4] ImportAddressTable = RVA = 0x00003148 (PEImportAddressTable[4] - [4] ImportLookupTable = RVA = 0x00003F98 (PEImportLookupTable[24] .rdata - - [5] ImportDllNameLink = api-ms-win-crt-stdio-l1-1-0.dll (RVA = 0x45DE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x586) - [5] ImportAddressTable = RVA = 0x00003158 (PEImportAddressTable[5] - [5] ImportLookupTable = RVA = 0x00004040 (PEImportLookupTable[26] .rdata - - [6] ImportDllNameLink = api-ms-win-crt-locale-l1-1-0.dll (RVA = 0x45FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5A6) - [6] ImportAddressTable = RVA = 0x00003170 (PEImportAddressTable[6] - [6] ImportLookupTable = RVA = 0x00003F88 (PEImportLookupTable[23] .rdata - - [7] ImportDllNameLink = api-ms-win-crt-heap-l1-1-0.dll (RVA = 0x4620, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5C8) - [7] ImportAddressTable = RVA = 0x00003180 (PEImportAddressTable[7] - [7] ImportLookupTable = RVA = 0x00003F78 (PEImportLookupTable[22] .rdata - - [8] ImportDllNameLink = KERNEL32.dll (RVA = 0x479E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x746) - [8] ImportAddressTable = RVA = 0x00003190 (PEImportAddressTable[8] - [8] ImportLookupTable = RVA = 0x00003E08 (PEImportLookupTable[18] .rdata - - - Section Data [17] PEStreamSectionData Position = 0x00002C04, Size = 0x00000004, RVA = 0x00003E04, VirtualSize = 0x00000004 - - Section Data [18] PEImportLookupTable Position = 0x00002C08, Size = 0x000000C0, RVA = 0x00003E08, VirtualSize = 0x000000C0 - [0] PEImportHintName { Hint = 997, Name = LoadLibraryExA } (RVA = 0x482A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7D2) - [1] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4654, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5FC) - [2] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x466E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x616) - [3] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4682, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x62A) - [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x469E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x646) - [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x46BC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x664) - [6] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x46D0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x678) - [7] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x46E4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x68C) - [8] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x4700, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6A8) - [9] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x471A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6C2) - [10] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4730, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6D8) - [11] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4746, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x6EE) - [12] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4760, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x708) - [13] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4776, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x71E) - [14] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x478A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x732) - [15] PEImportHintName { Hint = 1159, Name = RaiseException } (RVA = 0x47B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x75E) - [16] PEImportHintName { Hint = 637, Name = GetLastError } (RVA = 0x47C8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x770) - [17] PEImportHintName { Hint = 717, Name = GetProcAddress } (RVA = 0x4818, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7C0) - [18] PEImportHintName { Hint = 453, Name = FreeLibrary } (RVA = 0x480A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7B2) - [19] PEImportHintName { Hint = 1543, Name = VirtualQuery } (RVA = 0x47FA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x7A2) - [20] PEImportHintName { Hint = 1541, Name = VirtualProtect } (RVA = 0x47E8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x790) - [21] PEImportHintName { Hint = 772, Name = GetSystemInfo } (RVA = 0x47D8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x780) - [22] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4640, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5E8) - - Section Data [19] PEImportLookupTable Position = 0x00002CC8, Size = 0x00000068, RVA = 0x00003EC8, VirtualSize = 0x00000068 - [0] PEImportHintName { Hint = 255, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z } (RVA = 0x42D6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x27E) - [1] PEImportHintName { Hint = 262, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@P6AAEAV01@AEAV01@@Z@Z } (RVA = 0x4282, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x22A) - [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x4242, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x1EA) - [3] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x41FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x1A6) - [4] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x41C0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x168) - [5] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x417E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x126) - [6] PEImportHintName { Hint = 1121, Name = ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@D@Z } (RVA = 0x413A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0xE2) - [7] PEImportHintName { Hint = 1332, Name = ?widen@?$basic_ios@DU?$char_traits@D@std@@@std@@QEBADD@Z } (RVA = 0x40FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0xA6) - [8] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x40B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x5E) - [9] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x4094, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3C) - [10] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x4318, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x2C0) - [11] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x4058, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x0) - - Section Data [20] PEImportLookupTable Position = 0x00002D30, Size = 0x00000038, RVA = 0x00003F30, VirtualSize = 0x00000038 - [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x47AC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x754) - [1] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x439C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x344) - [2] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x43BA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x362) - [3] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x436E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x316) - [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x4386, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x32E) - [5] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x435C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x304) - - Section Data [21] PEImportLookupTable Position = 0x00002D68, Size = 0x00000010, RVA = 0x00003F68, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x4346, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x2EE) - - Section Data [22] PEImportLookupTable Position = 0x00002D78, Size = 0x00000010, RVA = 0x00003F78, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x452A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4D2) - - Section Data [23] PEImportLookupTable Position = 0x00002D88, Size = 0x00000010, RVA = 0x00003F88, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x4514, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4BC) - - Section Data [24] PEImportLookupTable Position = 0x00002D98, Size = 0x00000010, RVA = 0x00003F98, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x440C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3B4) - - Section Data [25] PEImportLookupTable Position = 0x00002DA8, Size = 0x00000098, RVA = 0x00003FA8, VirtualSize = 0x00000098 - [0] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x44C4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x46C) - [1] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4566, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x50E) - [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x454A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4F2) - [3] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x44A0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x448) - [4] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x448A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x432) - [5] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x447E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x426) - [6] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x445C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x404) - [7] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x443A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3E2) - [8] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x4420, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3C8) - [9] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x44D2, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x47A) - [10] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x43FC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x3A4) - [11] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x43EA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x392) - [12] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4590, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x538) - [13] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x4498, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x440) - [14] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4582, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x52A) - [15] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x44B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x45E) - [16] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x44E6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x48E) - [17] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x44DC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x484) - - Section Data [26] PEImportLookupTable Position = 0x00002E40, Size = 0x00000018, RVA = 0x00004040, VirtualSize = 0x00000018 - [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x453A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x4E2) - [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x44A8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x9A8, Position = 0x2E58, Size = 0x9A8 }, Offset = 0x450) - - Section Data [27] PEStreamSectionData Position = 0x00002E58, Size = 0x000009A8, RVA = 0x00004058, VirtualSize = 0x000009A8 + [15] PEStreamSectionData Position = 0x00002A38, Size = 0x00000104, RVA = 0x00003C38, VirtualSize = 0x00000104 + + [16] PEImportDirectory Position = 0x00002B3C, Size = 0x000000C8, RVA = 0x00003D3C, VirtualSize = 0x000000C8 + [0] ImportDllNameLink = MSVCP140.dll (RVA = 0x4338, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x2E0) + [0] ImportAddressTable = RVA = 0x000030C0 (PEImportAddressTable[1] -> PEImportAddressTableDirectory[0] -> .rdata) + [0] ImportLookupTable = RVA = 0x00003EC8 (PEImportLookupTable[19] -> .rdata) + + [1] ImportDllNameLink = VCRUNTIME140_1.dll (RVA = 0x43C4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x36C) + [1] ImportAddressTable = RVA = 0x00003160 (PEImportAddressTable[3] -> PEImportAddressTableDirectory[0] -> .rdata) + [1] ImportLookupTable = RVA = 0x00003F68 (PEImportLookupTable[21] -> .rdata) + + [2] ImportDllNameLink = VCRUNTIME140.dll (RVA = 0x43D8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x380) + [2] ImportAddressTable = RVA = 0x00003128 (PEImportAddressTable[2] -> PEImportAddressTableDirectory[0] -> .rdata) + [2] ImportLookupTable = RVA = 0x00003F30 (PEImportLookupTable[20] -> .rdata) + + [3] ImportDllNameLink = api-ms-win-crt-runtime-l1-1-0.dll (RVA = 0x459C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x544) + [3] ImportAddressTable = RVA = 0x000031A0 (PEImportAddressTable[7] -> PEImportAddressTableDirectory[0] -> .rdata) + [3] ImportLookupTable = RVA = 0x00003FA8 (PEImportLookupTable[25] -> .rdata) + + [4] ImportDllNameLink = api-ms-win-crt-math-l1-1-0.dll (RVA = 0x45BE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x566) + [4] ImportAddressTable = RVA = 0x00003190 (PEImportAddressTable[6] -> PEImportAddressTableDirectory[0] -> .rdata) + [4] ImportLookupTable = RVA = 0x00003F98 (PEImportLookupTable[24] -> .rdata) + + [5] ImportDllNameLink = api-ms-win-crt-stdio-l1-1-0.dll (RVA = 0x45DE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x586) + [5] ImportAddressTable = RVA = 0x00003238 (PEImportAddressTable[8] -> PEImportAddressTableDirectory[0] -> .rdata) + [5] ImportLookupTable = RVA = 0x00004040 (PEImportLookupTable[26] -> .rdata) + + [6] ImportDllNameLink = api-ms-win-crt-locale-l1-1-0.dll (RVA = 0x45FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5A6) + [6] ImportAddressTable = RVA = 0x00003180 (PEImportAddressTable[5] -> PEImportAddressTableDirectory[0] -> .rdata) + [6] ImportLookupTable = RVA = 0x00003F88 (PEImportLookupTable[23] -> .rdata) + + [7] ImportDllNameLink = api-ms-win-crt-heap-l1-1-0.dll (RVA = 0x4620, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5C8) + [7] ImportAddressTable = RVA = 0x00003170 (PEImportAddressTable[4] -> PEImportAddressTableDirectory[0] -> .rdata) + [7] ImportLookupTable = RVA = 0x00003F78 (PEImportLookupTable[22] -> .rdata) + + [8] ImportDllNameLink = KERNEL32.dll (RVA = 0x479E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x746) + [8] ImportAddressTable = RVA = 0x00003000 (PEImportAddressTable[0] -> PEImportAddressTableDirectory[0] -> .rdata) + [8] ImportLookupTable = RVA = 0x00003E08 (PEImportLookupTable[18] -> .rdata) + + + [17] PEStreamSectionData Position = 0x00002C04, Size = 0x00000004, RVA = 0x00003E04, VirtualSize = 0x00000004 + + [18] PEImportLookupTable Position = 0x00002C08, Size = 0x000000C0, RVA = 0x00003E08, VirtualSize = 0x000000C0 + [0] PEImportHintName { Hint = 997, Name = LoadLibraryExA } (RVA = 0x482A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x7D2) + [1] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4654, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5FC) + [2] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x466E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x616) + [3] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4682, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x62A) + [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x469E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x646) + [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x46BC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x664) + [6] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x46D0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x678) + [7] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x46E4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x68C) + [8] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x4700, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x6A8) + [9] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x471A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x6C2) + [10] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4730, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x6D8) + [11] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4746, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x6EE) + [12] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4760, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x708) + [13] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4776, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x71E) + [14] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x478A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x732) + [15] PEImportHintName { Hint = 1159, Name = RaiseException } (RVA = 0x47B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x75E) + [16] PEImportHintName { Hint = 637, Name = GetLastError } (RVA = 0x47C8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x770) + [17] PEImportHintName { Hint = 717, Name = GetProcAddress } (RVA = 0x4818, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x7C0) + [18] PEImportHintName { Hint = 453, Name = FreeLibrary } (RVA = 0x480A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x7B2) + [19] PEImportHintName { Hint = 1543, Name = VirtualQuery } (RVA = 0x47FA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x7A2) + [20] PEImportHintName { Hint = 1541, Name = VirtualProtect } (RVA = 0x47E8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x790) + [21] PEImportHintName { Hint = 772, Name = GetSystemInfo } (RVA = 0x47D8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x780) + [22] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4640, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5E8) + + [19] PEImportLookupTable Position = 0x00002CC8, Size = 0x00000068, RVA = 0x00003EC8, VirtualSize = 0x00000068 + [0] PEImportHintName { Hint = 255, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z } (RVA = 0x42D6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x27E) + [1] PEImportHintName { Hint = 262, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@P6AAEAV01@AEAV01@@Z@Z } (RVA = 0x4282, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x22A) + [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x4242, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x1EA) + [3] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x41FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x1A6) + [4] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x41C0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x168) + [5] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x417E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x126) + [6] PEImportHintName { Hint = 1121, Name = ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@D@Z } (RVA = 0x413A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0xE2) + [7] PEImportHintName { Hint = 1332, Name = ?widen@?$basic_ios@DU?$char_traits@D@std@@@std@@QEBADD@Z } (RVA = 0x40FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0xA6) + [8] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x40B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5E) + [9] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x4094, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x3C) + [10] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x4318, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x2C0) + [11] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x4058, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x0) + + [20] PEImportLookupTable Position = 0x00002D30, Size = 0x00000038, RVA = 0x00003F30, VirtualSize = 0x00000038 + [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x47AC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x754) + [1] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x439C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x344) + [2] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x43BA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x362) + [3] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x436E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x316) + [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x4386, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x32E) + [5] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x435C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x304) + + [21] PEImportLookupTable Position = 0x00002D68, Size = 0x00000010, RVA = 0x00003F68, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x4346, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x2EE) + + [22] PEImportLookupTable Position = 0x00002D78, Size = 0x00000010, RVA = 0x00003F78, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x452A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4D2) + + [23] PEImportLookupTable Position = 0x00002D88, Size = 0x00000010, RVA = 0x00003F88, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x4514, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4BC) + + [24] PEImportLookupTable Position = 0x00002D98, Size = 0x00000010, RVA = 0x00003F98, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x440C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x3B4) + + [25] PEImportLookupTable Position = 0x00002DA8, Size = 0x00000098, RVA = 0x00003FA8, VirtualSize = 0x00000098 + [0] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x44C4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x46C) + [1] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4566, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x50E) + [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x454A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4F2) + [3] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x44A0, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x448) + [4] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x448A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x432) + [5] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x447E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x426) + [6] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x445C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x404) + [7] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x443A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x3E2) + [8] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x4420, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x3C8) + [9] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x44D2, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x47A) + [10] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x43FC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x3A4) + [11] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x43EA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x392) + [12] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4590, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x538) + [13] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x4498, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x440) + [14] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4582, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x52A) + [15] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x44B6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x45E) + [16] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x44E6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x48E) + [17] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x44DC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x484) + + [26] PEImportLookupTable Position = 0x00002E40, Size = 0x00000018, RVA = 0x00004040, VirtualSize = 0x00000018 + [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x453A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4E2) + [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x44A8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x450) + + [27] PEStreamSectionData Position = 0x00002E58, Size = 0x000007E4, RVA = 0x00004058, VirtualSize = 0x000007E4 [02] .data PESection Position = 0x00003800, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x000006D0, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) - Section Data [00] PEStreamSectionData Position = 0x00003800, Size = 0x00000078, RVA = 0x00005000, VirtualSize = 0x00000078 + [00] PEStreamSectionData Position = 0x00003800, Size = 0x00000078, RVA = 0x00005000, VirtualSize = 0x00000078 - Section Data [01] PEBoundImportAddressTable64 Position = 0x00003878, Size = 0x00000018, RVA = 0x00005078, VirtualSize = 0x00000018 + [01] PEBoundImportAddressTable64 Position = 0x00003878, Size = 0x00000018, RVA = 0x00005078, VirtualSize = 0x00000018 [0] VA = 0x14000133E [1] VA = 0x140001332 - Section Data [02] PEStreamSectionData Position = 0x00003890, Size = 0x00000170, RVA = 0x00005090, VirtualSize = 0x00000170 + [02] PEStreamSectionData Position = 0x00003890, Size = 0x00000170, RVA = 0x00005090, VirtualSize = 0x00000170 [03] .pdata PESection Position = 0x00003A00, Size = 0x00000400, RVA = 0x00006000, VirtualSize = 0x00000210, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) - Section Data [00] PEExceptionDirectory Position = 0x00003A00, Size = 0x00000210, RVA = 0x00006000, VirtualSize = 0x00000210 - [0] Begin = RVA = 0x1000, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x0 - [0] End = RVA = 0x104B, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x4B + [00] PEExceptionDirectory Position = 0x00003A00, Size = 0x00000210, RVA = 0x00006000, VirtualSize = 0x00000210 + [0] Begin = RVA = 0x1000, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x0 + [0] End = RVA = 0x104B, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x4B [0] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 - [1] Begin = RVA = 0x1050, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x50 - [1] End = RVA = 0x108E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x8E + [1] Begin = RVA = 0x1050, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x50 + [1] End = RVA = 0x108E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x8E [1] UnwindInfoAddress = RVA = 0x39C8, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x2C - [2] Begin = RVA = 0x1090, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x90 - [2] End = RVA = 0x10B4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xB4 + [2] Begin = RVA = 0x1090, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x90 + [2] End = RVA = 0x10B4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xB4 [2] UnwindInfoAddress = RVA = 0x39EC, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x50 - [3] Begin = RVA = 0x10C0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xC0 - [3] End = RVA = 0x1272, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x272 + [3] Begin = RVA = 0x10C0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xC0 + [3] End = RVA = 0x1272, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x272 [3] UnwindInfoAddress = RVA = 0x3A04, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x68 - [4] Begin = RVA = 0x1280, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x280 - [4] End = RVA = 0x12B9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x2B9 + [4] Begin = RVA = 0x1280, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x280 + [4] End = RVA = 0x12B9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x2B9 [4] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 - [5] Begin = RVA = 0x12B9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x2B9 - [5] End = RVA = 0x1330, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x330 + [5] Begin = RVA = 0x12B9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x2B9 + [5] End = RVA = 0x1330, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x330 [5] UnwindInfoAddress = RVA = 0x3A64, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xC8 - [6] Begin = RVA = 0x1360, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x360 - [6] End = RVA = 0x137E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x37E + [6] Begin = RVA = 0x1360, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x360 + [6] End = RVA = 0x137E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x37E [6] UnwindInfoAddress = RVA = 0x3A70, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD4 - [7] Begin = RVA = 0x1380, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x380 - [7] End = RVA = 0x1436, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x436 + [7] Begin = RVA = 0x1380, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x380 + [7] End = RVA = 0x1436, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x436 [7] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 - [8] Begin = RVA = 0x1438, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x438 - [8] End = RVA = 0x1448, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x448 + [8] Begin = RVA = 0x1438, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x438 + [8] End = RVA = 0x1448, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x448 [8] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 - [9] Begin = RVA = 0x1448, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x448 - [9] End = RVA = 0x1461, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x461 + [9] Begin = RVA = 0x1448, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x448 + [9] End = RVA = 0x1461, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x461 [9] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 - [10] Begin = RVA = 0x1464, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x464 - [10] End = RVA = 0x15E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x5E0 + [10] Begin = RVA = 0x1464, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x464 + [10] End = RVA = 0x15E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x5E0 [10] UnwindInfoAddress = RVA = 0x3A7C, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xE0 - [11] Begin = RVA = 0x15E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x5E0 - [11] End = RVA = 0x15F2, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x5F2 + [11] Begin = RVA = 0x15E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x5E0 + [11] End = RVA = 0x15F2, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x5F2 [11] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 - [12] Begin = RVA = 0x15F4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x5F4 - [12] End = RVA = 0x1628, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x628 + [12] Begin = RVA = 0x15F4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x5F4 + [12] End = RVA = 0x1628, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x628 [12] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 - [13] Begin = RVA = 0x1628, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x628 - [13] End = RVA = 0x16FB, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x6FB + [13] Begin = RVA = 0x1628, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x628 + [13] End = RVA = 0x16FB, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x6FB [13] UnwindInfoAddress = RVA = 0x3ABC, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x120 - [14] Begin = RVA = 0x16FC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x6FC - [14] End = RVA = 0x176D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x76D + [14] Begin = RVA = 0x16FC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x6FC + [14] End = RVA = 0x176D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x76D [14] UnwindInfoAddress = RVA = 0x3AC4, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x128 - [15] Begin = RVA = 0x1770, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x770 - [15] End = RVA = 0x17A9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x7A9 + [15] Begin = RVA = 0x1770, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x770 + [15] End = RVA = 0x17A9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x7A9 [15] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 - [16] Begin = RVA = 0x17AC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x7AC - [16] End = RVA = 0x17E6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x7E6 + [16] Begin = RVA = 0x17AC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x7AC + [16] End = RVA = 0x17E6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x7E6 [16] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 - [17] Begin = RVA = 0x17E8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x7E8 - [17] End = RVA = 0x1873, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x873 + [17] Begin = RVA = 0x17E8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x7E8 + [17] End = RVA = 0x1873, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x873 [17] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 - [18] Begin = RVA = 0x1874, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x874 - [18] End = RVA = 0x190C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x90C + [18] Begin = RVA = 0x1874, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x874 + [18] End = RVA = 0x190C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x90C [18] UnwindInfoAddress = RVA = 0x3AD0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x134 - [19] Begin = RVA = 0x190C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x90C - [19] End = RVA = 0x1930, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x930 + [19] Begin = RVA = 0x190C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x90C + [19] End = RVA = 0x1930, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x930 [19] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 - [20] Begin = RVA = 0x1930, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x930 - [20] End = RVA = 0x1959, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x959 + [20] Begin = RVA = 0x1930, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x930 + [20] End = RVA = 0x1959, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x959 [20] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 - [21] Begin = RVA = 0x195C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x95C - [21] End = RVA = 0x1996, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x996 + [21] Begin = RVA = 0x195C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x95C + [21] End = RVA = 0x1996, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x996 [21] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 - [22] Begin = RVA = 0x1998, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x998 - [22] End = RVA = 0x19AF, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x9AF + [22] Begin = RVA = 0x1998, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x998 + [22] End = RVA = 0x19AF, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x9AF [22] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 - [23] Begin = RVA = 0x19B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x9B0 - [23] End = RVA = 0x1A5C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xA5C + [23] Begin = RVA = 0x19B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x9B0 + [23] End = RVA = 0x1A5C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xA5C [23] UnwindInfoAddress = RVA = 0x3AF8, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x15C - [24] Begin = RVA = 0x1A98, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xA98 - [24] End = RVA = 0x1AB3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xAB3 + [24] Begin = RVA = 0x1A98, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xA98 + [24] End = RVA = 0x1AB3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xAB3 [24] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 - [25] Begin = RVA = 0x1AD8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xAD8 - [25] End = RVA = 0x1C20, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xC20 + [25] Begin = RVA = 0x1AD8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xAD8 + [25] End = RVA = 0x1C20, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xC20 [25] UnwindInfoAddress = RVA = 0x3B04, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x168 - [26] Begin = RVA = 0x1C28, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xC28 - [26] End = RVA = 0x1C79, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xC79 + [26] Begin = RVA = 0x1C28, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xC28 + [26] End = RVA = 0x1C79, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xC79 [26] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 - [27] Begin = RVA = 0x1C8C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xC8C - [27] End = RVA = 0x1CE7, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xCE7 + [27] Begin = RVA = 0x1C8C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xC8C + [27] End = RVA = 0x1CE7, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xCE7 [27] UnwindInfoAddress = RVA = 0x3B14, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x178 - [28] Begin = RVA = 0x1CE8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xCE8 - [28] End = RVA = 0x1D24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xD24 + [28] Begin = RVA = 0x1CE8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xCE8 + [28] End = RVA = 0x1D24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xD24 [28] UnwindInfoAddress = RVA = 0x3B14, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x178 - [29] Begin = RVA = 0x1D24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xD24 - [29] End = RVA = 0x1D60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xD60 + [29] Begin = RVA = 0x1D24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xD24 + [29] End = RVA = 0x1D60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xD60 [29] UnwindInfoAddress = RVA = 0x3B14, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x178 - [30] Begin = RVA = 0x1D60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0xD60 - [30] End = RVA = 0x202A, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x102A + [30] Begin = RVA = 0x1D60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0xD60 + [30] End = RVA = 0x202A, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x102A [30] UnwindInfoAddress = RVA = 0x3B20, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x184 - [31] Begin = RVA = 0x20F0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x10F0 - [31] End = RVA = 0x21DB, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x11DB + [31] Begin = RVA = 0x20F0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x10F0 + [31] End = RVA = 0x21DB, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x11DB [31] UnwindInfoAddress = RVA = 0x3B94, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1F8 - [32] Begin = RVA = 0x21DC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x11DC - [32] End = RVA = 0x2296, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1296 + [32] Begin = RVA = 0x21DC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x11DC + [32] End = RVA = 0x2296, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1296 [32] UnwindInfoAddress = RVA = 0x39C0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x24 - [33] Begin = RVA = 0x2298, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1298 - [33] End = RVA = 0x2336, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1336 + [33] Begin = RVA = 0x2298, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1298 + [33] End = RVA = 0x2336, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1336 [33] UnwindInfoAddress = RVA = 0x3B14, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x178 - [34] Begin = RVA = 0x2338, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1338 - [34] End = RVA = 0x23D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x13D0 + [34] Begin = RVA = 0x2338, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1338 + [34] End = RVA = 0x23D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x13D0 [34] UnwindInfoAddress = RVA = 0x3B54, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1B8 - [35] Begin = RVA = 0x23D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x13D0 - [35] End = RVA = 0x246A, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x146A + [35] Begin = RVA = 0x23D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x13D0 + [35] End = RVA = 0x246A, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x146A [35] UnwindInfoAddress = RVA = 0x3B44, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1A8 - [36] Begin = RVA = 0x246C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x146C - [36] End = RVA = 0x2563, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1563 + [36] Begin = RVA = 0x246C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x146C + [36] End = RVA = 0x2563, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1563 [36] UnwindInfoAddress = RVA = 0x3B60, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1C4 - [37] Begin = RVA = 0x2564, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1564 - [37] End = RVA = 0x260D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x160D + [37] Begin = RVA = 0x2564, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1564 + [37] End = RVA = 0x260D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x160D [37] UnwindInfoAddress = RVA = 0x3A74, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xD8 - [38] Begin = RVA = 0x2610, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1610 - [38] End = RVA = 0x290C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x190C + [38] Begin = RVA = 0x2610, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1610 + [38] End = RVA = 0x290C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x190C [38] UnwindInfoAddress = RVA = 0x3B78, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1DC - [39] Begin = RVA = 0x2930, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1930 - [39] End = RVA = 0x2932, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1932 + [39] Begin = RVA = 0x2930, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1930 + [39] End = RVA = 0x2932, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1932 [39] UnwindInfoAddress = RVA = 0x3B38, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x19C - [40] Begin = RVA = 0x2950, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1950 - [40] End = RVA = 0x2956, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1956 + [40] Begin = RVA = 0x2950, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1950 + [40] End = RVA = 0x2956, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1956 [40] UnwindInfoAddress = RVA = 0x3B40, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x1A4 - [41] Begin = RVA = 0x2978, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x1978 - [41] End = RVA = 0x29B3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x19B3 + [41] Begin = RVA = 0x2978, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x1978 + [41] End = RVA = 0x29B3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x19B3 [41] UnwindInfoAddress = RVA = 0x3A5C, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0xC0 - [42] Begin = RVA = 0x29B3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x19B3 - [42] End = RVA = 0x29D1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x19D1 + [42] Begin = RVA = 0x29B3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x19B3 + [42] End = RVA = 0x29D1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x19D1 [42] UnwindInfoAddress = RVA = 0x3AB4, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x118 - [43] Begin = RVA = 0x29D1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x19D1 - [43] End = RVA = 0x29E9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1A00, Position = 0x400, Size = 0x1A00 }, Offset = 0x19E9 + [43] Begin = RVA = 0x29D1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x19D1 + [43] End = RVA = 0x29E9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x19E9 [43] UnwindInfoAddress = RVA = 0x3AF0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x154 - Section Data [01] PEStreamSectionData Position = 0x00003C10, Size = 0x000001F0, RVA = 0x00006210, VirtualSize = 0x000001F0 - [04] .rsrc PESection Position = 0x00003E00, Size = 0x00000200, RVA = 0x00007000, VirtualSize = 0x000001E0, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) - Section Data [00] PEResourceDirectory Position = 0x00003E00, Size = 0x000001E0, RVA = 0x00007000, VirtualSize = 0x000001E0 - > PEResourceDirectoryEntry { Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0} + [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) } - Section Data [00] PEStreamSectionData Position = 0x00003FD8, Size = 0x00000008, RVA = 0x000071D8, VirtualSize = 0x00000008 - - - Section Data [01] PEStreamSectionData Position = 0x00003FE0, Size = 0x00000020, RVA = 0x000071E0, VirtualSize = 0x00000020 - - - [05] .reloc PESection Position = 0x00004000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x0000003C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) - - Section Data [00] PEBaseRelocationDirectory Position = 0x00004000, Size = 0x0000003C, RVA = 0x00008000, VirtualSize = 0x0000003C - Block 0x3000 Relocations[20] - Dir64 Offset = 0x0250, RVA = 0x3250 (0x0000000140001A84), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0258, RVA = 0x3258 (0x0000000140001A84), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0260, RVA = 0x3260 (0x0000000140002930), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0268, RVA = 0x3268 (0x0000000140002950), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0270, RVA = 0x3270 (0x0000000140002950), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0280, RVA = 0x3280 (0x000000014000290C), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0290, RVA = 0x3290 (0x0000000140001448), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x02A8, RVA = 0x32A8 (0x0000000140001380), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x02B0, RVA = 0x32B0 (0x0000000140001438), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x02F8, RVA = 0x32F8 (0x00000001400050A0), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0300, RVA = 0x3300 (0x0000000140005140), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x03E8, RVA = 0x33E8 (0x0000000140005000), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x0400, RVA = 0x3400 (0x0000000140003250), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x0408, RVA = 0x3408 (0x0000000140003260), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x0490, RVA = 0x3490 (0x0000000140003580), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x04A8, RVA = 0x34A8 (0x0000000140003258), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x04B0, RVA = 0x34B0 (0x0000000140003268), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x04B8, RVA = 0x34B8 (0x0000000140003270), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x04C0, RVA = 0x34C0 (0x0000000140003278), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x04C8, RVA = 0x34C8 (0x0000000140003280), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] .rdata } - Block 0x5000 Relocations[2] - Dir64 Offset = 0x0078, RVA = 0x5078 (0x000000014000133E), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] .data } - Dir64 Offset = 0x0080, RVA = 0x5080 (0x0000000140001332), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] .data } - Section Data [00] PEStreamSectionData Position = 0x0000402C, Size = 0x00000010, RVA = 0x0000802C, VirtualSize = 0x00000010 - - - Section Data [01] PEStreamSectionData Position = 0x0000403C, Size = 0x000001C4, RVA = 0x0000803C, VirtualSize = 0x000001C4 - + > 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 + + + [05] .reloc PESection Position = 0x00004000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x0000003C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) + + [00] PEBaseRelocationDirectory Position = 0x00004000, Size = 0x0000003C, RVA = 0x00008000, VirtualSize = 0x0000003C + Block 0x3000 Relocations[20] + [000] Dir64 Offset = 0x0250, RVA = 0x3250 (0x0000000140001A84), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [001] Dir64 Offset = 0x0258, RVA = 0x3258 (0x0000000140001A84), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [002] Dir64 Offset = 0x0260, RVA = 0x3260 (0x0000000140002930), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [003] Dir64 Offset = 0x0268, RVA = 0x3268 (0x0000000140002950), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [004] Dir64 Offset = 0x0270, RVA = 0x3270 (0x0000000140002950), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [005] Dir64 Offset = 0x0280, RVA = 0x3280 (0x000000014000290C), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [006] Dir64 Offset = 0x0290, RVA = 0x3290 (0x0000000140001448), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [007] Dir64 Offset = 0x02A8, RVA = 0x32A8 (0x0000000140001380), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [008] Dir64 Offset = 0x02B0, RVA = 0x32B0 (0x0000000140001438), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [009] Dir64 Offset = 0x02F8, RVA = 0x32F8 (0x00000001400050A0), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [010] Dir64 Offset = 0x0300, RVA = 0x3300 (0x0000000140005140), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [011] Dir64 Offset = 0x03E8, RVA = 0x33E8 (0x0000000140005000), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [012] Dir64 Offset = 0x0400, RVA = 0x3400 (0x0000000140003250), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [013] Dir64 Offset = 0x0408, RVA = 0x3408 (0x0000000140003260), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [014] Dir64 Offset = 0x0490, RVA = 0x3490 (0x0000000140003580), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [015] Dir64 Offset = 0x04A8, RVA = 0x34A8 (0x0000000140003258), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [016] Dir64 Offset = 0x04B0, RVA = 0x34B0 (0x0000000140003268), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [017] Dir64 Offset = 0x04B8, RVA = 0x34B8 (0x0000000140003270), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [018] Dir64 Offset = 0x04C0, RVA = 0x34C0 (0x0000000140003278), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [019] Dir64 Offset = 0x04C8, RVA = 0x34C8 (0x0000000140003280), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + Block 0x5000 Relocations[2] + [000] Dir64 Offset = 0x0078, RVA = 0x5078 (0x000000014000133E), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] -> .data) } + [001] Dir64 Offset = 0x0080, RVA = 0x5080 (0x0000000140001332), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] -> .data) } + + 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 7759071..9d7b758 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 @@ -83,87 +83,87 @@ Data Directories Sections [00] .text PESection Position = 0x00000400, Size = 0x00001200, RVA = 0x00001000, VirtualSize = 0x000010C9, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) - Section Data [00] PEStreamSectionData Position = 0x00000400, Size = 0x00001200, RVA = 0x00001000, VirtualSize = 0x00001200 + [00] PEStreamSectionData Position = 0x00000400, Size = 0x000010C9, RVA = 0x00001000, VirtualSize = 0x000010C9 [01] .rdata PESection Position = 0x00001600, Size = 0x00001400, RVA = 0x00003000, VirtualSize = 0x00001288, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) - Section Data [00] PEImportAddressTableDirectory Position = 0x00001600, Size = 0x000001F0, RVA = 0x00003000, VirtualSize = 0x000001F0 - Section Data [00] PEImportAddressTable Position = 0x00001680, Size = 0x00000048, RVA = 0x00003000, VirtualSize = 0x00000048 - [0] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x3C40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x0) - [1] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x3DA8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x168) - [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x3D68, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x128) - [3] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x3D20, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0xE0) - [4] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x3CDC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x9C) - [5] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x3C9E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5E) - [6] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x3C7C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3C) - [7] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x3DEA, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1AA) - - Section Data [01] PEImportAddressTable Position = 0x00001700, Size = 0x00000010, RVA = 0x00003048, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x3E18, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1D8) - - Section Data [02] PEImportAddressTable Position = 0x000016C8, Size = 0x00000038, RVA = 0x00003058, VirtualSize = 0x00000038 - [0] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x3E40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x200) - [1] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x3E2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1EE) - [2] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x427E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x63E) - [3] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x3E6E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x22E) - [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x3E58, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x218) - [5] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x3E8C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x24C) - - Section Data [03] PEImportAddressTable Position = 0x00001740, Size = 0x00000098, RVA = 0x00003090, VirtualSize = 0x00000098 - [0] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4062, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x422) - [1] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4054, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x414) - [2] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4038, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3F8) - [3] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x401C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3DC) - [4] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x3FAE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x36E) - [5] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x3FA4, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x364) - [6] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x3F88, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x348) - [7] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x3EBC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x27C) - [8] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x3F72, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x332) - [9] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x3F6A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x32A) - [10] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x3F5C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x31C) - [11] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x3F50, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x310) - [12] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x3F2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2EE) - [13] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x3F0C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2CC) - [14] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x3EF2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2B2) - [15] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x3FB8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x378) - [16] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x3ECE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x28E) - [17] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x3F96, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x356) - - Section Data [04] PEImportAddressTable Position = 0x00001730, Size = 0x00000010, RVA = 0x00003128, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x3EDE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x29E) - - Section Data [05] PEImportAddressTable Position = 0x000017D8, Size = 0x00000018, RVA = 0x00003138, VirtualSize = 0x00000018 - [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x400C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3CC) - [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x3F7A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x33A) - - Section Data [06] PEImportAddressTable Position = 0x00001720, Size = 0x00000010, RVA = 0x00003150, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x3FE6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3A6) - - Section Data [07] PEImportAddressTable Position = 0x00001710, Size = 0x00000010, RVA = 0x00003160, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x3FFC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3BC) - - Section Data [08] PEImportAddressTable Position = 0x00001600, Size = 0x00000080, RVA = 0x00003170, VirtualSize = 0x00000080 - [0] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4112, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x4D2) - [1] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x41D2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x592) - [2] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x41EC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5AC) - [3] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4202, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5C2) - [4] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4218, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5D8) - [5] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4232, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5F2) - [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4248, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x608) - [7] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x425C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x61C) - [8] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x41A2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x562) - [9] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x418E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x54E) - [10] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x4170, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x530) - [11] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4154, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x514) - [12] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x4140, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x500) - [13] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4126, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x4E6) - [14] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x41B6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x576) - + [00] PEImportAddressTableDirectory Position = 0x00001600, Size = 0x000001F0, RVA = 0x00003000, VirtualSize = 0x000001F0 + [00] PEImportAddressTable Position = 0x00001600, Size = 0x00000080, RVA = 0x00003000, VirtualSize = 0x00000080 + [0] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4112, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x4D2) + [1] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x41D2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x592) + [2] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x41EC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x5AC) + [3] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4202, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x5C2) + [4] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4218, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x5D8) + [5] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4232, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x5F2) + [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4248, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x608) + [7] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x425C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x61C) + [8] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x41A2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x562) + [9] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x418E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x54E) + [10] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x4170, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x530) + [11] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4154, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x514) + [12] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x4140, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x500) + [13] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4126, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x4E6) + [14] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x41B6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x576) + + [01] PEImportAddressTable Position = 0x00001680, Size = 0x00000048, RVA = 0x00003080, VirtualSize = 0x00000048 + [0] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x3C40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x0) + [1] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x3DA8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x168) + [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x3D68, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x128) + [3] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x3D20, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0xE0) + [4] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x3CDC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x9C) + [5] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x3C9E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x5E) + [6] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x3C7C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3C) + [7] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x3DEA, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x1AA) + + [02] PEImportAddressTable Position = 0x000016C8, Size = 0x00000038, RVA = 0x000030C8, VirtualSize = 0x00000038 + [0] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x3E40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x200) + [1] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x3E2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x1EE) + [2] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x427E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x63E) + [3] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x3E6E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x22E) + [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x3E58, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x218) + [5] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x3E8C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x24C) + + [03] PEImportAddressTable Position = 0x00001700, Size = 0x00000010, RVA = 0x00003100, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x3E18, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x1D8) + + [04] PEImportAddressTable Position = 0x00001710, Size = 0x00000010, RVA = 0x00003110, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x3FFC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3BC) + + [05] PEImportAddressTable Position = 0x00001720, Size = 0x00000010, RVA = 0x00003120, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x3FE6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3A6) + + [06] PEImportAddressTable Position = 0x00001730, Size = 0x00000010, RVA = 0x00003130, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x3EDE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x29E) + + [07] PEImportAddressTable Position = 0x00001740, Size = 0x00000098, RVA = 0x00003140, VirtualSize = 0x00000098 + [0] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4062, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x422) + [1] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4054, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x414) + [2] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4038, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3F8) + [3] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x401C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3DC) + [4] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x3FAE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x36E) + [5] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x3FA4, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x364) + [6] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x3F88, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x348) + [7] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x3EBC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x27C) + [8] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x3F72, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x332) + [9] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x3F6A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x32A) + [10] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x3F5C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x31C) + [11] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x3F50, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x310) + [12] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x3F2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x2EE) + [13] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x3F0C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x2CC) + [14] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x3EF2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x2B2) + [15] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x3FB8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x378) + [16] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x3ECE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x28E) + [17] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x3F96, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x356) + + [08] PEImportAddressTable Position = 0x000017D8, Size = 0x00000018, RVA = 0x000031D8, VirtualSize = 0x00000018 + [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x400C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3CC) + [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x3F7A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x33A) + - Section Data [01] PEStreamSectionData Position = 0x000017F0, Size = 0x000000C0, RVA = 0x000031F0, VirtualSize = 0x000000C0 + [01] PEStreamSectionData Position = 0x000017F0, Size = 0x000000C0, RVA = 0x000031F0, VirtualSize = 0x000000C0 - Section Data [02] PELoadConfigDirectory64 Position = 0x000018B0, Size = 0x00000140, RVA = 0x000032B0, VirtualSize = 0x00000140 + [02] PELoadConfigDirectory64 Position = 0x000018B0, Size = 0x00000140, RVA = 0x000032B0, VirtualSize = 0x00000140 Size = 0x140 TimeDateStamp = 0x0 MajorVersion = 0 @@ -218,330 +218,322 @@ Sections CastGuardOsDeterminedFailureMode = 0x140003218 GuardMemcpyFunctionPointer = 0x140003220 - Section Data [03] PEDebugDirectory Position = 0x000019F0, Size = 0x00000070, RVA = 0x000033F0, VirtualSize = 0x00000070 - [0] Type = CodeView, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F11C3F, Data = RVA = 0x000034DC (PEDebugSectionDataRSDS[5] .rdata - [1] Type = VCFeature, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F11C3F, Data = RVA = 0x00003550 (PEDebugStreamSectionData[7] .rdata - [2] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F11C3F, Data = RVA = 0x00003564 (PEDebugStreamSectionData[8] .rdata + [03] PEDebugDirectory Position = 0x000019F0, Size = 0x00000070, RVA = 0x000033F0, VirtualSize = 0x00000070 + [0] Type = CodeView, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F11C3F, Data = RVA = 0x000034DC (PEDebugSectionDataRSDS[5] -> .rdata) + [1] Type = VCFeature, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F11C3F, Data = RVA = 0x00003550 (PEDebugStreamSectionData[7] -> .rdata) + [2] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F11C3F, Data = RVA = 0x00003564 (PEDebugStreamSectionData[8] -> .rdata) [3] Type = ILTCG, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F11C3F, Data = null - Section Data [04] PEStreamSectionData Position = 0x00001A60, Size = 0x0000007C, RVA = 0x00003460, VirtualSize = 0x0000007C + [04] PEStreamSectionData Position = 0x00001A60, Size = 0x0000007C, RVA = 0x00003460, VirtualSize = 0x0000007C - Section Data [05] PEDebugSectionDataRSDS Position = 0x00001ADC, Size = 0x00000071, RVA = 0x000034DC, VirtualSize = 0x00000071 + [05] PEDebugSectionDataRSDS Position = 0x00001ADC, Size = 0x00000071, RVA = 0x000034DC, VirtualSize = 0x00000071 Debug Section Data (RSDS) Guid = a28d7ba2-048a-4315-9bbe-0edbca526f59 Age = 2 PdbPath = C:\code\LibObjectFile\src\native\Win64\NativeProjects\x64\Release\NativeConsoleWin64.pdb - Section Data [06] PEStreamSectionData Position = 0x00001B4D, Size = 0x00000003, RVA = 0x0000354D, VirtualSize = 0x00000003 + [06] PEStreamSectionData Position = 0x00001B4D, Size = 0x00000003, RVA = 0x0000354D, VirtualSize = 0x00000003 - Section Data [07] PEDebugStreamSectionData Position = 0x00001B50, Size = 0x00000014, RVA = 0x00003550, VirtualSize = 0x00000014 + [07] PEDebugStreamSectionData Position = 0x00001B50, Size = 0x00000014, RVA = 0x00003550, VirtualSize = 0x00000014 - Section Data [08] PEDebugStreamSectionData Position = 0x00001B64, Size = 0x00000284, RVA = 0x00003564, VirtualSize = 0x00000284 + [08] PEDebugStreamSectionData Position = 0x00001B64, Size = 0x00000284, RVA = 0x00003564, VirtualSize = 0x00000284 - Section Data [09] PEStreamSectionData Position = 0x00001DE8, Size = 0x0000019C, RVA = 0x000037E8, VirtualSize = 0x0000019C + [09] PEStreamSectionData Position = 0x00001DE8, Size = 0x0000019C, RVA = 0x000037E8, VirtualSize = 0x0000019C - Section Data [10] PEImportDirectory Position = 0x00001F84, Size = 0x000000C8, RVA = 0x00003984, VirtualSize = 0x000000C8 - [0] ImportDllNameLink = MSVCP140.dll (RVA = 0x3E0A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1CA) - [0] ImportAddressTable = RVA = 0x00003000 (PEImportAddressTable[0] - [0] ImportLookupTable = RVA = 0x00003AD0 (PEImportLookupTable[13] .rdata + [10] PEImportDirectory Position = 0x00001F84, Size = 0x000000C8, RVA = 0x00003984, VirtualSize = 0x000000C8 + [0] ImportDllNameLink = MSVCP140.dll (RVA = 0x3E0A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x1CA) + [0] ImportAddressTable = RVA = 0x00003080 (PEImportAddressTable[1] -> PEImportAddressTableDirectory[0] -> .rdata) + [0] ImportLookupTable = RVA = 0x00003AD0 (PEImportLookupTable[13] -> .rdata) - [1] ImportDllNameLink = VCRUNTIME140_1.dll (RVA = 0x3E96, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x256) - [1] ImportAddressTable = RVA = 0x00003048 (PEImportAddressTable[1] - [1] ImportLookupTable = RVA = 0x00003B50 (PEImportLookupTable[15] .rdata + [1] ImportDllNameLink = VCRUNTIME140_1.dll (RVA = 0x3E96, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x256) + [1] ImportAddressTable = RVA = 0x00003100 (PEImportAddressTable[3] -> PEImportAddressTableDirectory[0] -> .rdata) + [1] ImportLookupTable = RVA = 0x00003B50 (PEImportLookupTable[15] -> .rdata) - [2] ImportDllNameLink = VCRUNTIME140.dll (RVA = 0x3EAA, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x26A) - [2] ImportAddressTable = RVA = 0x00003058 (PEImportAddressTable[2] - [2] ImportLookupTable = RVA = 0x00003B18 (PEImportLookupTable[14] .rdata + [2] ImportDllNameLink = VCRUNTIME140.dll (RVA = 0x3EAA, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x26A) + [2] ImportAddressTable = RVA = 0x000030C8 (PEImportAddressTable[2] -> PEImportAddressTableDirectory[0] -> .rdata) + [2] ImportLookupTable = RVA = 0x00003B18 (PEImportLookupTable[14] -> .rdata) - [3] ImportDllNameLink = api-ms-win-crt-runtime-l1-1-0.dll (RVA = 0x406E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x42E) - [3] ImportAddressTable = RVA = 0x00003090 (PEImportAddressTable[3] - [3] ImportLookupTable = RVA = 0x00003B90 (PEImportLookupTable[19] .rdata + [3] ImportDllNameLink = api-ms-win-crt-runtime-l1-1-0.dll (RVA = 0x406E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x42E) + [3] ImportAddressTable = RVA = 0x00003140 (PEImportAddressTable[7] -> PEImportAddressTableDirectory[0] -> .rdata) + [3] ImportLookupTable = RVA = 0x00003B90 (PEImportLookupTable[19] -> .rdata) - [4] ImportDllNameLink = api-ms-win-crt-math-l1-1-0.dll (RVA = 0x4090, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x450) - [4] ImportAddressTable = RVA = 0x00003128 (PEImportAddressTable[4] - [4] ImportLookupTable = RVA = 0x00003B80 (PEImportLookupTable[18] .rdata + [4] ImportDllNameLink = api-ms-win-crt-math-l1-1-0.dll (RVA = 0x4090, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x450) + [4] ImportAddressTable = RVA = 0x00003130 (PEImportAddressTable[6] -> PEImportAddressTableDirectory[0] -> .rdata) + [4] ImportLookupTable = RVA = 0x00003B80 (PEImportLookupTable[18] -> .rdata) - [5] ImportDllNameLink = api-ms-win-crt-stdio-l1-1-0.dll (RVA = 0x40B0, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x470) - [5] ImportAddressTable = RVA = 0x00003138 (PEImportAddressTable[5] - [5] ImportLookupTable = RVA = 0x00003C28 (PEImportLookupTable[20] .rdata + [5] ImportDllNameLink = api-ms-win-crt-stdio-l1-1-0.dll (RVA = 0x40B0, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x470) + [5] ImportAddressTable = RVA = 0x000031D8 (PEImportAddressTable[8] -> PEImportAddressTableDirectory[0] -> .rdata) + [5] ImportLookupTable = RVA = 0x00003C28 (PEImportLookupTable[20] -> .rdata) - [6] ImportDllNameLink = api-ms-win-crt-locale-l1-1-0.dll (RVA = 0x40D0, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x490) - [6] ImportAddressTable = RVA = 0x00003150 (PEImportAddressTable[6] - [6] ImportLookupTable = RVA = 0x00003B70 (PEImportLookupTable[17] .rdata + [6] ImportDllNameLink = api-ms-win-crt-locale-l1-1-0.dll (RVA = 0x40D0, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x490) + [6] ImportAddressTable = RVA = 0x00003120 (PEImportAddressTable[5] -> PEImportAddressTableDirectory[0] -> .rdata) + [6] ImportLookupTable = RVA = 0x00003B70 (PEImportLookupTable[17] -> .rdata) - [7] ImportDllNameLink = api-ms-win-crt-heap-l1-1-0.dll (RVA = 0x40F2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x4B2) - [7] ImportAddressTable = RVA = 0x00003160 (PEImportAddressTable[7] - [7] ImportLookupTable = RVA = 0x00003B60 (PEImportLookupTable[16] .rdata + [7] ImportDllNameLink = api-ms-win-crt-heap-l1-1-0.dll (RVA = 0x40F2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x4B2) + [7] ImportAddressTable = RVA = 0x00003110 (PEImportAddressTable[4] -> PEImportAddressTableDirectory[0] -> .rdata) + [7] ImportLookupTable = RVA = 0x00003B60 (PEImportLookupTable[16] -> .rdata) - [8] ImportDllNameLink = KERNEL32.dll (RVA = 0x4270, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x630) - [8] ImportAddressTable = RVA = 0x00003170 (PEImportAddressTable[8] - [8] ImportLookupTable = RVA = 0x00003A50 (PEImportLookupTable[12] .rdata + [8] ImportDllNameLink = KERNEL32.dll (RVA = 0x4270, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x630) + [8] ImportAddressTable = RVA = 0x00003000 (PEImportAddressTable[0] -> PEImportAddressTableDirectory[0] -> .rdata) + [8] ImportLookupTable = RVA = 0x00003A50 (PEImportLookupTable[12] -> .rdata) - Section Data [11] PEStreamSectionData Position = 0x0000204C, Size = 0x00000004, RVA = 0x00003A4C, VirtualSize = 0x00000004 + [11] PEStreamSectionData Position = 0x0000204C, Size = 0x00000004, RVA = 0x00003A4C, VirtualSize = 0x00000004 - Section Data [12] PEImportLookupTable Position = 0x00002050, Size = 0x00000080, RVA = 0x00003A50, VirtualSize = 0x00000080 - [0] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4112, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x4D2) - [1] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x41D2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x592) - [2] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x41EC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5AC) - [3] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4202, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5C2) - [4] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4218, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5D8) - [5] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4232, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5F2) - [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4248, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x608) - [7] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x425C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x61C) - [8] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x41A2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x562) - [9] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x418E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x54E) - [10] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x4170, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x530) - [11] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4154, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x514) - [12] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x4140, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x500) - [13] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4126, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x4E6) - [14] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x41B6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x576) + [12] PEImportLookupTable Position = 0x00002050, Size = 0x00000080, RVA = 0x00003A50, VirtualSize = 0x00000080 + [0] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4112, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x4D2) + [1] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x41D2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x592) + [2] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x41EC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x5AC) + [3] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x4202, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x5C2) + [4] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x4218, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x5D8) + [5] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x4232, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x5F2) + [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x4248, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x608) + [7] PEImportHintName { Hint = 661, Name = GetModuleHandleW } (RVA = 0x425C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x61C) + [8] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x41A2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x562) + [9] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x418E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x54E) + [10] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x4170, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x530) + [11] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x4154, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x514) + [12] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x4140, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x500) + [13] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4126, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x4E6) + [14] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x41B6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x576) - Section Data [13] PEImportLookupTable Position = 0x000020D0, Size = 0x00000048, RVA = 0x00003AD0, VirtualSize = 0x00000048 - [0] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x3C40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x0) - [1] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x3DA8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x168) - [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x3D68, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x128) - [3] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x3D20, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0xE0) - [4] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x3CDC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x9C) - [5] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x3C9E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x5E) - [6] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x3C7C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3C) - [7] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x3DEA, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1AA) + [13] PEImportLookupTable Position = 0x000020D0, Size = 0x00000048, RVA = 0x00003AD0, VirtualSize = 0x00000048 + [0] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x3C40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x0) + [1] PEImportHintName { Hint = 1246, Name = ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAAHD@Z } (RVA = 0x3DA8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x168) + [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x3D68, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x128) + [3] PEImportHintName { Hint = 1249, Name = ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QEAA_JPEBD_J@Z } (RVA = 0x3D20, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0xE0) + [4] PEImportHintName { Hint = 872, Name = ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV12@XZ } (RVA = 0x3CDC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x9C) + [5] PEImportHintName { Hint = 580, Name = ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAXXZ } (RVA = 0x3C9E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x5E) + [6] PEImportHintName { Hint = 1310, Name = ?uncaught_exception@std@@YA_NXZ } (RVA = 0x3C7C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3C) + [7] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x3DEA, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x1AA) - Section Data [14] PEImportLookupTable Position = 0x00002118, Size = 0x00000038, RVA = 0x00003B18, VirtualSize = 0x00000038 - [0] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x3E40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x200) - [1] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x3E2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1EE) - [2] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x427E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x63E) - [3] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x3E6E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x22E) - [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x3E58, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x218) - [5] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x3E8C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x24C) + [14] PEImportLookupTable Position = 0x00002118, Size = 0x00000038, RVA = 0x00003B18, VirtualSize = 0x00000038 + [0] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x3E40, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x200) + [1] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x3E2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x1EE) + [2] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x427E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x63E) + [3] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x3E6E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x22E) + [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x3E58, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x218) + [5] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x3E8C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x24C) - Section Data [15] PEImportLookupTable Position = 0x00002150, Size = 0x00000010, RVA = 0x00003B50, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x3E18, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x1D8) + [15] PEImportLookupTable Position = 0x00002150, Size = 0x00000010, RVA = 0x00003B50, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x3E18, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x1D8) - Section Data [16] PEImportLookupTable Position = 0x00002160, Size = 0x00000010, RVA = 0x00003B60, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x3FFC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3BC) + [16] PEImportLookupTable Position = 0x00002160, Size = 0x00000010, RVA = 0x00003B60, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x3FFC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3BC) - Section Data [17] PEImportLookupTable Position = 0x00002170, Size = 0x00000010, RVA = 0x00003B70, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x3FE6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3A6) + [17] PEImportLookupTable Position = 0x00002170, Size = 0x00000010, RVA = 0x00003B70, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x3FE6, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3A6) - Section Data [18] PEImportLookupTable Position = 0x00002180, Size = 0x00000010, RVA = 0x00003B80, VirtualSize = 0x00000010 - [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x3EDE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x29E) + [18] PEImportLookupTable Position = 0x00002180, Size = 0x00000010, RVA = 0x00003B80, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x3EDE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x29E) - Section Data [19] PEImportLookupTable Position = 0x00002190, Size = 0x00000098, RVA = 0x00003B90, VirtualSize = 0x00000098 - [0] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4062, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x422) - [1] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4054, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x414) - [2] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4038, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3F8) - [3] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x401C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3DC) - [4] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x3FAE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x36E) - [5] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x3FA4, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x364) - [6] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x3F88, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x348) - [7] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x3EBC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x27C) - [8] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x3F72, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x332) - [9] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x3F6A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x32A) - [10] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x3F5C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x31C) - [11] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x3F50, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x310) - [12] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x3F2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2EE) - [13] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x3F0C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2CC) - [14] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x3EF2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x2B2) - [15] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x3FB8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x378) - [16] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x3ECE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x28E) - [17] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x3F96, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x356) + [19] PEImportLookupTable Position = 0x00002190, Size = 0x00000098, RVA = 0x00003B90, VirtualSize = 0x00000098 + [0] PEImportHintName { Hint = 103, Name = terminate } (RVA = 0x4062, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x422) + [1] PEImportHintName { Hint = 30, Name = _crt_atexit } (RVA = 0x4054, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x414) + [2] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4038, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3F8) + [3] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x401C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3DC) + [4] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x3FAE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x36E) + [5] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x3FA4, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x364) + [6] PEImportHintName { Hint = 4, Name = __p___argc } (RVA = 0x3F88, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x348) + [7] PEImportHintName { Hint = 64, Name = _seh_filter_exe } (RVA = 0x3EBC, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x27C) + [8] PEImportHintName { Hint = 35, Name = _exit } (RVA = 0x3F72, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x332) + [9] PEImportHintName { Hint = 85, Name = exit } (RVA = 0x3F6A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x32A) + [10] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x3F5C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x31C) + [11] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x3F50, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x310) + [12] PEImportHintName { Hint = 40, Name = _get_initial_narrow_environment } (RVA = 0x3F2E, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x2EE) + [13] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x3F0C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x2CC) + [14] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x3EF2, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x2B2) + [15] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x3FB8, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x378) + [16] PEImportHintName { Hint = 66, Name = _set_app_type } (RVA = 0x3ECE, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x28E) + [17] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x3F96, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x356) - Section Data [20] PEImportLookupTable Position = 0x00002228, Size = 0x00000018, RVA = 0x00003C28, VirtualSize = 0x00000018 - [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x400C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x3CC) - [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x3F7A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x7C0, Position = 0x2240, Size = 0x7C0 }, Offset = 0x33A) + [20] PEImportLookupTable Position = 0x00002228, Size = 0x00000018, RVA = 0x00003C28, VirtualSize = 0x00000018 + [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x400C, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x3CC) + [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x3F7A, PEStreamSectionData { RVA = 0x3C40, VirtualSize = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x33A) - Section Data [21] PEStreamSectionData Position = 0x00002240, Size = 0x000007C0, RVA = 0x00003C40, VirtualSize = 0x000007C0 + [21] PEStreamSectionData Position = 0x00002240, Size = 0x00000648, RVA = 0x00003C40, VirtualSize = 0x00000648 [02] .data PESection Position = 0x00002A00, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x00000680, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) - Section Data [00] PEStreamSectionData Position = 0x00002A00, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x00000200 + [00] PEStreamSectionData Position = 0x00002A00, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x00000200 [03] .pdata PESection Position = 0x00002C00, Size = 0x00000200, RVA = 0x00006000, VirtualSize = 0x00000198, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) - Section Data [00] PEExceptionDirectory Position = 0x00002C00, Size = 0x00000198, RVA = 0x00006000, VirtualSize = 0x00000198 - [0] Begin = RVA = 0x1000, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x0 - [0] End = RVA = 0x1017, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x17 + [00] PEExceptionDirectory Position = 0x00002C00, Size = 0x00000198, RVA = 0x00006000, VirtualSize = 0x00000198 + [0] Begin = RVA = 0x1000, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x0 + [0] End = RVA = 0x1017, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x17 [0] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 - [1] Begin = RVA = 0x1020, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x20 - [1] End = RVA = 0x11D2, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1D2 + [1] Begin = RVA = 0x1020, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x20 + [1] End = RVA = 0x11D2, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x1D2 [1] UnwindInfoAddress = RVA = 0x3810, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x28 - [2] Begin = RVA = 0x11E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1E0 - [2] End = RVA = 0x121E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x21E + [2] Begin = RVA = 0x11E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x1E0 + [2] End = RVA = 0x121E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x21E [2] UnwindInfoAddress = RVA = 0x3870, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x88 - [3] Begin = RVA = 0x1220, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x220 - [3] End = RVA = 0x1244, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x244 + [3] Begin = RVA = 0x1220, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x220 + [3] End = RVA = 0x1244, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x244 [3] UnwindInfoAddress = RVA = 0x3894, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xAC - [4] Begin = RVA = 0x1260, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x260 - [4] End = RVA = 0x127E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x27E + [4] Begin = RVA = 0x1260, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x260 + [4] End = RVA = 0x127E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x27E [4] UnwindInfoAddress = RVA = 0x38B0, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xC8 - [5] Begin = RVA = 0x1280, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x280 - [5] End = RVA = 0x1336, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x336 + [5] Begin = RVA = 0x1280, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x280 + [5] End = RVA = 0x1336, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x336 [5] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC - [6] Begin = RVA = 0x1338, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x338 - [6] End = RVA = 0x1348, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x348 + [6] Begin = RVA = 0x1338, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x338 + [6] End = RVA = 0x1348, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x348 [6] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 - [7] Begin = RVA = 0x1348, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x348 - [7] End = RVA = 0x1361, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x361 + [7] Begin = RVA = 0x1348, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x348 + [7] End = RVA = 0x1361, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x361 [7] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 - [8] Begin = RVA = 0x1364, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x364 - [8] End = RVA = 0x14E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x4E0 + [8] Begin = RVA = 0x1364, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x364 + [8] End = RVA = 0x14E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x4E0 [8] UnwindInfoAddress = RVA = 0x38BC, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xD4 - [9] Begin = RVA = 0x14E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x4E0 - [9] End = RVA = 0x14F2, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x4F2 + [9] Begin = RVA = 0x14E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x4E0 + [9] End = RVA = 0x14F2, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x4F2 [9] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 - [10] Begin = RVA = 0x14F4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x4F4 - [10] End = RVA = 0x1528, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x528 + [10] Begin = RVA = 0x14F4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x4F4 + [10] End = RVA = 0x1528, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x528 [10] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC - [11] Begin = RVA = 0x1528, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x528 - [11] End = RVA = 0x15FB, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x5FB + [11] Begin = RVA = 0x1528, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x528 + [11] End = RVA = 0x15FB, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x5FB [11] UnwindInfoAddress = RVA = 0x38FC, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x114 - [12] Begin = RVA = 0x15FC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x5FC - [12] End = RVA = 0x166D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x66D + [12] Begin = RVA = 0x15FC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x5FC + [12] End = RVA = 0x166D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x66D [12] UnwindInfoAddress = RVA = 0x3904, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x11C - [13] Begin = RVA = 0x1670, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x670 - [13] End = RVA = 0x16A9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x6A9 + [13] Begin = RVA = 0x1670, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x670 + [13] End = RVA = 0x16A9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x6A9 [13] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 - [14] Begin = RVA = 0x16AC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x6AC - [14] End = RVA = 0x16E6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x6E6 + [14] Begin = RVA = 0x16AC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x6AC + [14] End = RVA = 0x16E6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x6E6 [14] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 - [15] Begin = RVA = 0x16E8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x6E8 - [15] End = RVA = 0x1773, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x773 + [15] Begin = RVA = 0x16E8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x6E8 + [15] End = RVA = 0x1773, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x773 [15] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC - [16] Begin = RVA = 0x1774, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x774 - [16] End = RVA = 0x180C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x80C + [16] Begin = RVA = 0x1774, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x774 + [16] End = RVA = 0x180C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x80C [16] UnwindInfoAddress = RVA = 0x3910, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x128 - [17] Begin = RVA = 0x180C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x80C - [17] End = RVA = 0x1830, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x830 + [17] Begin = RVA = 0x180C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x80C + [17] End = RVA = 0x1830, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x830 [17] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC - [18] Begin = RVA = 0x1830, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x830 - [18] End = RVA = 0x1859, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x859 + [18] Begin = RVA = 0x1830, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x830 + [18] End = RVA = 0x1859, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x859 [18] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC - [19] Begin = RVA = 0x185C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x85C - [19] End = RVA = 0x1896, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x896 + [19] Begin = RVA = 0x185C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x85C + [19] End = RVA = 0x1896, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x896 [19] UnwindInfoAddress = RVA = 0x38B4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0xCC - [20] Begin = RVA = 0x1898, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x898 - [20] End = RVA = 0x18AF, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x8AF + [20] Begin = RVA = 0x1898, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x898 + [20] End = RVA = 0x18AF, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x8AF [20] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 - [21] Begin = RVA = 0x18B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x8B0 - [21] End = RVA = 0x195C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x95C + [21] Begin = RVA = 0x18B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x8B0 + [21] End = RVA = 0x195C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x95C [21] UnwindInfoAddress = RVA = 0x3938, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x150 - [22] Begin = RVA = 0x1998, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x998 - [22] End = RVA = 0x19B3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x9B3 + [22] Begin = RVA = 0x1998, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x998 + [22] End = RVA = 0x19B3, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x9B3 [22] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 - [23] Begin = RVA = 0x19D8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x9D8 - [23] End = RVA = 0x1B20, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xB20 + [23] Begin = RVA = 0x19D8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x9D8 + [23] End = RVA = 0x1B20, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0xB20 [23] UnwindInfoAddress = RVA = 0x3944, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x15C - [24] Begin = RVA = 0x1B28, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xB28 - [24] End = RVA = 0x1B79, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xB79 + [24] Begin = RVA = 0x1B28, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0xB28 + [24] End = RVA = 0x1B79, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0xB79 [24] UnwindInfoAddress = RVA = 0x3808, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x20 - [25] Begin = RVA = 0x1B8C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xB8C - [25] End = RVA = 0x1BE7, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xBE7 + [25] Begin = RVA = 0x1B8C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0xB8C + [25] End = RVA = 0x1BE7, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0xBE7 [25] UnwindInfoAddress = RVA = 0x3954, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x16C - [26] Begin = RVA = 0x1BE8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xBE8 - [26] End = RVA = 0x1C24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xC24 + [26] Begin = RVA = 0x1BE8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0xBE8 + [26] End = RVA = 0x1C24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0xC24 [26] UnwindInfoAddress = RVA = 0x3954, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x16C - [27] Begin = RVA = 0x1C24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xC24 - [27] End = RVA = 0x1C60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xC60 + [27] Begin = RVA = 0x1C24, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0xC24 + [27] End = RVA = 0x1C60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0xC60 [27] UnwindInfoAddress = RVA = 0x3954, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x16C - [28] Begin = RVA = 0x1C60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xC60 - [28] End = RVA = 0x1F2A, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0xF2A + [28] Begin = RVA = 0x1C60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0xC60 + [28] End = RVA = 0x1F2A, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0xF2A [28] UnwindInfoAddress = RVA = 0x3960, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x178 - [29] Begin = RVA = 0x2010, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1010 - [29] End = RVA = 0x2012, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1012 + [29] Begin = RVA = 0x2010, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x1010 + [29] End = RVA = 0x2012, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x1012 [29] UnwindInfoAddress = RVA = 0x3978, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x190 - [30] Begin = RVA = 0x2030, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1030 - [30] End = RVA = 0x2036, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1036 + [30] Begin = RVA = 0x2030, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x1030 + [30] End = RVA = 0x2036, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x1036 [30] UnwindInfoAddress = RVA = 0x3980, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x198 - [31] Begin = RVA = 0x2058, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1058 - [31] End = RVA = 0x2093, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1093 + [31] Begin = RVA = 0x2058, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x1058 + [31] End = RVA = 0x2093, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x1093 [31] UnwindInfoAddress = RVA = 0x3868, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x80 - [32] Begin = RVA = 0x2093, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x1093 - [32] End = RVA = 0x20B1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x10B1 + [32] Begin = RVA = 0x2093, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x1093 + [32] End = RVA = 0x20B1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x10B1 [32] UnwindInfoAddress = RVA = 0x38F4, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x10C - [33] Begin = RVA = 0x20B1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x10B1 - [33] End = RVA = 0x20C9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1200, Position = 0x400, Size = 0x1200 }, Offset = 0x10C9 + [33] Begin = RVA = 0x20B1, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x10B1 + [33] End = RVA = 0x20C9, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x10C9 [33] UnwindInfoAddress = RVA = 0x3930, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x148 - Section Data [01] PEStreamSectionData Position = 0x00002D98, Size = 0x00000068, RVA = 0x00006198, VirtualSize = 0x00000068 - [04] .rsrc PESection Position = 0x00002E00, Size = 0x00000200, RVA = 0x00007000, VirtualSize = 0x000001E0, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) - Section Data [00] PEResourceDirectory Position = 0x00002E00, Size = 0x000001E0, RVA = 0x00007000, VirtualSize = 0x000001E0 - > PEResourceDirectoryEntry { Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0} + [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) } - Section Data [00] PEStreamSectionData Position = 0x00002FD8, Size = 0x00000008, RVA = 0x000071D8, VirtualSize = 0x00000008 - - - Section Data [01] PEStreamSectionData Position = 0x00002FE0, Size = 0x00000020, RVA = 0x000071E0, VirtualSize = 0x00000020 - - - [05] .reloc PESection Position = 0x00003000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x00000030, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) - - Section Data [00] PEBaseRelocationDirectory Position = 0x00003000, Size = 0x00000030, RVA = 0x00008000, VirtualSize = 0x00000030 - Block 0x3000 Relocations[20] - Dir64 Offset = 0x01F0, RVA = 0x31F0 (0x0000000140001984), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x01F8, RVA = 0x31F8 (0x0000000140001984), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0200, RVA = 0x3200 (0x0000000140002010), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0208, RVA = 0x3208 (0x0000000140002030), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0210, RVA = 0x3210 (0x0000000140002030), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0220, RVA = 0x3220 (0x0000000140001FEE), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0230, RVA = 0x3230 (0x0000000140001348), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0248, RVA = 0x3248 (0x0000000140001280), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0250, RVA = 0x3250 (0x0000000140001338), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0280, RVA = 0x3280 (0x0000000140005080), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0288, RVA = 0x3288 (0x0000000140005120), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0308, RVA = 0x3308 (0x0000000140005000), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x0320, RVA = 0x3320 (0x00000001400031F0), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x0328, RVA = 0x3328 (0x0000000140003200), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x03B0, RVA = 0x33B0 (0x0000000140003480), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x03C8, RVA = 0x33C8 (0x00000001400031F8), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x03D0, RVA = 0x33D0 (0x0000000140003208), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x03D8, RVA = 0x33D8 (0x0000000140003210), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x03E0, RVA = 0x33E0 (0x0000000140003218), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x03E8, RVA = 0x33E8 (0x0000000140003220), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] .rdata } - Section Data [00] PEStreamSectionData Position = 0x00003028, Size = 0x00000008, RVA = 0x00008028, VirtualSize = 0x00000008 - - - Section Data [01] PEStreamSectionData Position = 0x00003030, Size = 0x000001D0, RVA = 0x00008030, VirtualSize = 0x000001D0 - + > 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 + + + [05] .reloc PESection Position = 0x00003000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x00000030, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) + + [00] PEBaseRelocationDirectory Position = 0x00003000, Size = 0x00000030, RVA = 0x00008000, VirtualSize = 0x00000030 + Block 0x3000 Relocations[20] + [000] Dir64 Offset = 0x01F0, RVA = 0x31F0 (0x0000000140001984), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [001] Dir64 Offset = 0x01F8, RVA = 0x31F8 (0x0000000140001984), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [002] Dir64 Offset = 0x0200, RVA = 0x3200 (0x0000000140002010), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [003] Dir64 Offset = 0x0208, RVA = 0x3208 (0x0000000140002030), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [004] Dir64 Offset = 0x0210, RVA = 0x3210 (0x0000000140002030), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [005] Dir64 Offset = 0x0220, RVA = 0x3220 (0x0000000140001FEE), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [006] Dir64 Offset = 0x0230, RVA = 0x3230 (0x0000000140001348), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [007] Dir64 Offset = 0x0248, RVA = 0x3248 (0x0000000140001280), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [008] Dir64 Offset = 0x0250, RVA = 0x3250 (0x0000000140001338), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [009] Dir64 Offset = 0x0280, RVA = 0x3280 (0x0000000140005080), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [010] Dir64 Offset = 0x0288, RVA = 0x3288 (0x0000000140005120), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [011] Dir64 Offset = 0x0308, RVA = 0x3308 (0x0000000140005000), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [012] Dir64 Offset = 0x0320, RVA = 0x3320 (0x00000001400031F0), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [013] Dir64 Offset = 0x0328, RVA = 0x3328 (0x0000000140003200), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [014] Dir64 Offset = 0x03B0, RVA = 0x33B0 (0x0000000140003480), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [015] Dir64 Offset = 0x03C8, RVA = 0x33C8 (0x00000001400031F8), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [016] Dir64 Offset = 0x03D0, RVA = 0x33D0 (0x0000000140003208), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [017] Dir64 Offset = 0x03D8, RVA = 0x33D8 (0x0000000140003210), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [018] Dir64 Offset = 0x03E0, RVA = 0x33E0 (0x0000000140003218), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [019] Dir64 Offset = 0x03E8, RVA = 0x33E8 (0x0000000140003220), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + + 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 4bc60eb..369995c 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 @@ -83,48 +83,48 @@ Data Directories Sections [00] .text PESection Position = 0x00000400, Size = 0x00001000, RVA = 0x00001000, VirtualSize = 0x00000F18, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) - Section Data [00] PEStreamSectionData Position = 0x00000400, Size = 0x00001000, RVA = 0x00001000, VirtualSize = 0x00001000 + [00] PEStreamSectionData Position = 0x00000400, Size = 0x00000F18, RVA = 0x00001000, VirtualSize = 0x00000F18 [01] .rdata PESection Position = 0x00001400, Size = 0x00000E00, RVA = 0x00002000, VirtualSize = 0x00000C96, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) - Section Data [00] PEImportAddressTableDirectory Position = 0x00001400, Size = 0x000000E8, RVA = 0x00002000, VirtualSize = 0x000000E8 - Section Data [00] PEImportAddressTable Position = 0x00001478, Size = 0x00000028, RVA = 0x00002000, VirtualSize = 0x00000028 - [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x2C8C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x274) - [1] PEImportHintName { Hint = 37, Name = __std_type_info_destroy_list } (RVA = 0x2A30, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x18) - [2] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x2A18, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x0) - [3] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x2A50, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x38) - - Section Data [01] PEImportAddressTable Position = 0x000014A0, Size = 0x00000048, RVA = 0x00002028, VirtualSize = 0x00000048 - [0] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x2B08, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xF0) - [1] PEImportHintName { Hint = 34, Name = _execute_onexit_table } (RVA = 0x2AF0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xD8) - [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x2AD4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xBC) - [3] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x2AB2, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x9A) - [4] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x2A98, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x80) - [5] PEImportHintName { Hint = 63, Name = _seh_filter_dll } (RVA = 0x2A86, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x6E) - [6] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x2A78, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x60) - [7] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x2A6C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x54) - - Section Data [02] PEImportAddressTable Position = 0x00001400, Size = 0x00000078, RVA = 0x00002070, VirtualSize = 0x00000078 - [0] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x2C24, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x20C) - [1] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x2B62, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x14A) - [2] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x2B48, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x130) - [3] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x2B34, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x11C) - [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x2B92, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x17A) - [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x2BB0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x198) - [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x2C6A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x252) - [7] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x2C54, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x23C) - [8] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x2C3A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x222) - [9] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x2B76, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x15E) - [10] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x2C0E, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1F6) - [11] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x2BF4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1DC) - [12] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x2BD8, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1C0) - [13] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x2BC4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1AC) - + [00] PEImportAddressTableDirectory Position = 0x00001400, Size = 0x000000E8, RVA = 0x00002000, VirtualSize = 0x000000E8 + [00] PEImportAddressTable Position = 0x00001400, Size = 0x00000078, RVA = 0x00002000, VirtualSize = 0x00000078 + [0] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x2C24, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x20C) + [1] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x2B62, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x14A) + [2] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x2B48, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x130) + [3] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x2B34, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x11C) + [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x2B92, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x17A) + [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x2BB0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x198) + [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x2C6A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x252) + [7] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x2C54, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x23C) + [8] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x2C3A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x222) + [9] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x2B76, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x15E) + [10] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x2C0E, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1F6) + [11] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x2BF4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1DC) + [12] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x2BD8, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1C0) + [13] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x2BC4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1AC) + + [01] PEImportAddressTable Position = 0x00001478, Size = 0x00000028, RVA = 0x00002078, VirtualSize = 0x00000028 + [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x2C8C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x274) + [1] PEImportHintName { Hint = 37, Name = __std_type_info_destroy_list } (RVA = 0x2A30, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x18) + [2] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x2A18, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x0) + [3] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x2A50, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x38) + + [02] PEImportAddressTable Position = 0x000014A0, Size = 0x00000048, RVA = 0x000020A0, VirtualSize = 0x00000048 + [0] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x2B08, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xF0) + [1] PEImportHintName { Hint = 34, Name = _execute_onexit_table } (RVA = 0x2AF0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xD8) + [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x2AD4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xBC) + [3] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x2AB2, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x9A) + [4] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x2A98, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x80) + [5] PEImportHintName { Hint = 63, Name = _seh_filter_dll } (RVA = 0x2A86, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x6E) + [6] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x2A78, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x60) + [7] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x2A6C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x54) + - Section Data [01] PEStreamSectionData Position = 0x000014E8, Size = 0x000000A8, RVA = 0x000020E8, VirtualSize = 0x000000A8 + [01] PEStreamSectionData Position = 0x000014E8, Size = 0x000000A8, RVA = 0x000020E8, VirtualSize = 0x000000A8 - Section Data [02] PELoadConfigDirectory64 Position = 0x00001590, Size = 0x00000140, RVA = 0x00002190, VirtualSize = 0x00000140 + [02] PELoadConfigDirectory64 Position = 0x00001590, Size = 0x00000140, RVA = 0x00002190, VirtualSize = 0x00000140 Size = 0x140 TimeDateStamp = 0x0 MajorVersion = 0 @@ -179,290 +179,283 @@ Sections CastGuardOsDeterminedFailureMode = 0x180002110 GuardMemcpyFunctionPointer = 0x180002118 - Section Data [03] PEDebugDirectory Position = 0x000016D0, Size = 0x00000070, RVA = 0x000022D0, VirtualSize = 0x00000070 - [0] Type = CodeView, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2E, Data = RVA = 0x000023E4 (PEDebugSectionDataRSDS[5] .rdata - [1] Type = VCFeature, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2E, Data = RVA = 0x00002458 (PEDebugStreamSectionData[7] .rdata - [2] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2E, Data = RVA = 0x0000246C (PEDebugStreamSectionData[8] .rdata + [03] PEDebugDirectory Position = 0x000016D0, Size = 0x00000070, RVA = 0x000022D0, VirtualSize = 0x00000070 + [0] Type = CodeView, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2E, Data = RVA = 0x000023E4 (PEDebugSectionDataRSDS[5] -> .rdata) + [1] Type = VCFeature, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2E, Data = RVA = 0x00002458 (PEDebugStreamSectionData[7] -> .rdata) + [2] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2E, Data = RVA = 0x0000246C (PEDebugStreamSectionData[8] -> .rdata) [3] Type = ILTCG, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2E, Data = null - Section Data [04] PEStreamSectionData Position = 0x00001740, Size = 0x000000A4, RVA = 0x00002340, VirtualSize = 0x000000A4 + [04] PEStreamSectionData Position = 0x00001740, Size = 0x000000A4, RVA = 0x00002340, VirtualSize = 0x000000A4 - Section Data [05] PEDebugSectionDataRSDS Position = 0x000017E4, Size = 0x00000071, RVA = 0x000023E4, VirtualSize = 0x00000071 + [05] PEDebugSectionDataRSDS Position = 0x000017E4, Size = 0x00000071, RVA = 0x000023E4, VirtualSize = 0x00000071 Debug Section Data (RSDS) Guid = 9e24e83e-5203-490d-b4fe-ac81f739897a Age = 2 PdbPath = C:\code\LibObjectFile\src\native\Win64\NativeProjects\x64\Release\NativeLibraryWin64.pdb - Section Data [06] PEStreamSectionData Position = 0x00001855, Size = 0x00000003, RVA = 0x00002455, VirtualSize = 0x00000003 + [06] PEStreamSectionData Position = 0x00001855, Size = 0x00000003, RVA = 0x00002455, VirtualSize = 0x00000003 - Section Data [07] PEDebugStreamSectionData Position = 0x00001858, Size = 0x00000014, RVA = 0x00002458, VirtualSize = 0x00000014 + [07] PEDebugStreamSectionData Position = 0x00001858, Size = 0x00000014, RVA = 0x00002458, VirtualSize = 0x00000014 - Section Data [08] PEDebugStreamSectionData Position = 0x0000186C, Size = 0x00000258, RVA = 0x0000246C, VirtualSize = 0x00000258 + [08] PEDebugStreamSectionData Position = 0x0000186C, Size = 0x00000258, RVA = 0x0000246C, VirtualSize = 0x00000258 - Section Data [09] PEStreamSectionData Position = 0x00001AC4, Size = 0x000001AC, RVA = 0x000026C4, VirtualSize = 0x000001AC + [09] PEStreamSectionData Position = 0x00001AC4, Size = 0x000001AC, RVA = 0x000026C4, VirtualSize = 0x000001AC - Section Data [10] PEExportDirectory Position = 0x00001C70, Size = 0x00000070, RVA = 0x00002870, VirtualSize = 0x00000070 + [10] PEExportDirectory Position = 0x00001C70, Size = 0x00000070, RVA = 0x00002870, VirtualSize = 0x00000070 TimeStamp = 02/07/2106 06:28:15 MajorVersion = 0 MinorVersion = 0 OrdinalBase = 0x1 NameLink = NativeLibraryWin64.dll (RVA = 0x28AC, PEStreamSectionData { RVA = 0x28AC, VirtualSize = 0x34, Position = 0x1CAC, Size = 0x34 }, Offset = 0x0) - ExportFunctionAddressTable = RVA = 0x00002898 (PEExportAddressTable[0] - ExportNameTable = RVA = 0x000028A0 (PEExportNameTable[1] - ExportOrdinalTable = RVA = 0x000028A8 (PEExportOrdinalTable[2] - Section Data [00] PEExportAddressTable Position = 0x00001C98, Size = 0x00000008, RVA = 0x00002898, VirtualSize = 0x00000008 - [0] Exported RVA = 0x1020 (RVA = 0x00001000 (PEStreamSectionData[0] .text) - [1] Exported RVA = 0x1010 (RVA = 0x00001000 (PEStreamSectionData[0] .text) - - Section Data [01] PEExportNameTable Position = 0x00001CA0, Size = 0x00000008, RVA = 0x000028A0, VirtualSize = 0x00000008 - [0] AnotherFunction (RVA = 0x28C3, PEStreamSectionData { RVA = 0x28AC, VirtualSize = 0x34, Position = 0x1CAC, Size = 0x34 }, Offset = 0x17) - [1] HelloWorld (RVA = 0x28D3, PEStreamSectionData { RVA = 0x28AC, VirtualSize = 0x34, Position = 0x1CAC, Size = 0x34 }, Offset = 0x27) - - Section Data [02] PEExportOrdinalTable Position = 0x00001CA8, Size = 0x00000004, RVA = 0x000028A8, VirtualSize = 0x00000004 - [0] Ordinal = 0 - [1] Ordinal = 1 - - Section Data [03] PEStreamSectionData Position = 0x00001CAC, Size = 0x00000034, RVA = 0x000028AC, VirtualSize = 0x00000034 - + ExportFunctionAddressTable = RVA = 0x00002898 (PEExportAddressTable[0] -> PEExportDirectory[10] -> .rdata) + ExportNameTable = RVA = 0x000028A0 (PEExportNameTable[1] -> PEExportDirectory[10] -> .rdata) + ExportOrdinalTable = RVA = 0x000028A8 (PEExportOrdinalTable[2] -> PEExportDirectory[10] -> .rdata) + [00] PEExportAddressTable Position = 0x00001C98, Size = 0x00000008, RVA = 0x00002898, VirtualSize = 0x00000008 + [0] Exported RVA = 0x1020 (RVA = 0x00001000 (PEStreamSectionData[0] -> .text)) + [1] Exported RVA = 0x1010 (RVA = 0x00001000 (PEStreamSectionData[0] -> .text)) + + [01] PEExportNameTable Position = 0x00001CA0, Size = 0x00000008, RVA = 0x000028A0, VirtualSize = 0x00000008 + [0] AnotherFunction (RVA = 0x28C3, PEStreamSectionData { RVA = 0x28AC, VirtualSize = 0x34, Position = 0x1CAC, Size = 0x34 }, Offset = 0x17) + [1] HelloWorld (RVA = 0x28D3, PEStreamSectionData { RVA = 0x28AC, VirtualSize = 0x34, Position = 0x1CAC, Size = 0x34 }, Offset = 0x27) + + [02] PEExportOrdinalTable Position = 0x00001CA8, Size = 0x00000004, RVA = 0x000028A8, VirtualSize = 0x00000004 + [0] Ordinal = 0 + [1] Ordinal = 1 + + [03] PEStreamSectionData Position = 0x00001CAC, Size = 0x00000034, RVA = 0x000028AC, VirtualSize = 0x00000034 + - Section Data [11] PEImportDirectory Position = 0x00001CE0, Size = 0x00000050, RVA = 0x000028E0, VirtualSize = 0x00000050 - [0] ImportDllNameLink = VCRUNTIME140.dll (RVA = 0x2A5A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x42) - [0] ImportAddressTable = RVA = 0x00002000 (PEImportAddressTable[0] - [0] ImportLookupTable = RVA = 0x000029A8 (PEImportLookupTable[13] .rdata + [11] PEImportDirectory Position = 0x00001CE0, Size = 0x00000050, RVA = 0x000028E0, VirtualSize = 0x00000050 + [0] ImportDllNameLink = VCRUNTIME140.dll (RVA = 0x2A5A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x42) + [0] ImportAddressTable = RVA = 0x00002078 (PEImportAddressTable[1] -> PEImportAddressTableDirectory[0] -> .rdata) + [0] ImportLookupTable = RVA = 0x000029A8 (PEImportLookupTable[13] -> .rdata) - [1] ImportDllNameLink = api-ms-win-crt-runtime-l1-1-0.dll (RVA = 0x2B12, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xFA) - [1] ImportAddressTable = RVA = 0x00002028 (PEImportAddressTable[1] - [1] ImportLookupTable = RVA = 0x000029D0 (PEImportLookupTable[14] .rdata + [1] ImportDllNameLink = api-ms-win-crt-runtime-l1-1-0.dll (RVA = 0x2B12, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xFA) + [1] ImportAddressTable = RVA = 0x000020A0 (PEImportAddressTable[2] -> PEImportAddressTableDirectory[0] -> .rdata) + [1] ImportLookupTable = RVA = 0x000029D0 (PEImportLookupTable[14] -> .rdata) - [2] ImportDllNameLink = KERNEL32.dll (RVA = 0x2C7E, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x266) - [2] ImportAddressTable = RVA = 0x00002070 (PEImportAddressTable[2] - [2] ImportLookupTable = RVA = 0x00002930 (PEImportLookupTable[12] .rdata + [2] ImportDllNameLink = KERNEL32.dll (RVA = 0x2C7E, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x266) + [2] ImportAddressTable = RVA = 0x00002000 (PEImportAddressTable[0] -> PEImportAddressTableDirectory[0] -> .rdata) + [2] ImportLookupTable = RVA = 0x00002930 (PEImportLookupTable[12] -> .rdata) - Section Data [12] PEImportLookupTable Position = 0x00001D30, Size = 0x00000078, RVA = 0x00002930, VirtualSize = 0x00000078 - [0] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x2C24, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x20C) - [1] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x2B62, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x14A) - [2] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x2B48, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x130) - [3] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x2B34, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x11C) - [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x2B92, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x17A) - [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x2BB0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x198) - [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x2C6A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x252) - [7] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x2C54, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x23C) - [8] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x2C3A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x222) - [9] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x2B76, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x15E) - [10] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x2C0E, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1F6) - [11] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x2BF4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1DC) - [12] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x2BD8, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1C0) - [13] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x2BC4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x1AC) + [12] PEImportLookupTable Position = 0x00001D30, Size = 0x00000078, RVA = 0x00002930, VirtualSize = 0x00000078 + [0] PEImportHintName { Hint = 567, Name = GetCurrentThreadId } (RVA = 0x2C24, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x20C) + [1] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x2B62, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x14A) + [2] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x2B48, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x130) + [3] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x2B34, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x11C) + [4] PEImportHintName { Hint = 1444, Name = SetUnhandledExceptionFilter } (RVA = 0x2B92, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x17A) + [5] PEImportHintName { Hint = 562, Name = GetCurrentProcess } (RVA = 0x2BB0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x198) + [6] PEImportHintName { Hint = 928, Name = IsDebuggerPresent } (RVA = 0x2C6A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x252) + [7] PEImportHintName { Hint = 906, Name = InitializeSListHead } (RVA = 0x2C54, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x23C) + [8] PEImportHintName { Hint = 778, Name = GetSystemTimeAsFileTime } (RVA = 0x2C3A, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x222) + [9] PEImportHintName { Hint = 1510, Name = UnhandledExceptionFilter } (RVA = 0x2B76, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x15E) + [10] PEImportHintName { Hint = 563, Name = GetCurrentProcessId } (RVA = 0x2C0E, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1F6) + [11] PEImportHintName { Hint = 1136, Name = QueryPerformanceCounter } (RVA = 0x2BF4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1DC) + [12] PEImportHintName { Hint = 936, Name = IsProcessorFeaturePresent } (RVA = 0x2BD8, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1C0) + [13] PEImportHintName { Hint = 1476, Name = TerminateProcess } (RVA = 0x2BC4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x1AC) - Section Data [13] PEImportLookupTable Position = 0x00001DA8, Size = 0x00000028, RVA = 0x000029A8, VirtualSize = 0x00000028 - [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x2C8C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x274) - [1] PEImportHintName { Hint = 37, Name = __std_type_info_destroy_list } (RVA = 0x2A30, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x18) - [2] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x2A18, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x0) - [3] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x2A50, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x38) + [13] PEImportLookupTable Position = 0x00001DA8, Size = 0x00000028, RVA = 0x000029A8, VirtualSize = 0x00000028 + [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x2C8C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x274) + [1] PEImportHintName { Hint = 37, Name = __std_type_info_destroy_list } (RVA = 0x2A30, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x18) + [2] PEImportHintName { Hint = 8, Name = __C_specific_handler } (RVA = 0x2A18, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x0) + [3] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x2A50, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x38) - Section Data [14] PEImportLookupTable Position = 0x00001DD0, Size = 0x00000048, RVA = 0x000029D0, VirtualSize = 0x00000048 - [0] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x2B08, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xF0) - [1] PEImportHintName { Hint = 34, Name = _execute_onexit_table } (RVA = 0x2AF0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xD8) - [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x2AD4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0xBC) - [3] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x2AB2, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x9A) - [4] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x2A98, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x80) - [5] PEImportHintName { Hint = 63, Name = _seh_filter_dll } (RVA = 0x2A86, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x6E) - [6] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x2A78, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x60) - [7] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x2A6C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x3E8, Position = 0x1E18, Size = 0x3E8 }, Offset = 0x54) + [14] PEImportLookupTable Position = 0x00001DD0, Size = 0x00000048, RVA = 0x000029D0, VirtualSize = 0x00000048 + [0] PEImportHintName { Hint = 22, Name = _cexit } (RVA = 0x2B08, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xF0) + [1] PEImportHintName { Hint = 34, Name = _execute_onexit_table } (RVA = 0x2AF0, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xD8) + [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x2AD4, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0xBC) + [3] PEImportHintName { Hint = 51, Name = _initialize_narrow_environment } (RVA = 0x2AB2, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x9A) + [4] PEImportHintName { Hint = 24, Name = _configure_narrow_argv } (RVA = 0x2A98, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x80) + [5] PEImportHintName { Hint = 63, Name = _seh_filter_dll } (RVA = 0x2A86, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x6E) + [6] PEImportHintName { Hint = 55, Name = _initterm_e } (RVA = 0x2A78, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x60) + [7] PEImportHintName { Hint = 54, Name = _initterm } (RVA = 0x2A6C, PEStreamSectionData { RVA = 0x2A18, VirtualSize = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x54) - Section Data [15] PEStreamSectionData Position = 0x00001E18, Size = 0x000003E8, RVA = 0x00002A18, VirtualSize = 0x000003E8 + [15] PEStreamSectionData Position = 0x00001E18, Size = 0x0000027E, RVA = 0x00002A18, VirtualSize = 0x0000027E [02] .data PESection Position = 0x00002200, Size = 0x00000200, RVA = 0x00003000, VirtualSize = 0x00000680, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) - Section Data [00] PEStreamSectionData Position = 0x00002200, Size = 0x00000200, RVA = 0x00003000, VirtualSize = 0x00000200 + [00] PEStreamSectionData Position = 0x00002200, Size = 0x00000200, RVA = 0x00003000, VirtualSize = 0x00000200 [03] .pdata PESection Position = 0x00002400, Size = 0x00000200, RVA = 0x00004000, VirtualSize = 0x000001A4, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) - Section Data [00] PEExceptionDirectory Position = 0x00002400, Size = 0x000001A4, RVA = 0x00004000, VirtualSize = 0x000001A4 - [0] Begin = RVA = 0x1040, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x40 - [0] End = RVA = 0x105E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x5E + [00] PEExceptionDirectory Position = 0x00002400, Size = 0x000001A4, RVA = 0x00004000, VirtualSize = 0x000001A4 + [0] Begin = RVA = 0x1040, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x40 + [0] End = RVA = 0x105E, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x5E [0] UnwindInfoAddress = RVA = 0x26E8, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x24 - [1] Begin = RVA = 0x1060, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x60 - [1] End = RVA = 0x10B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xB0 + [1] Begin = RVA = 0x1060, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x60 + [1] End = RVA = 0x10B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xB0 [1] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 - [2] Begin = RVA = 0x10B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xB0 - [2] End = RVA = 0x11C6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x1C6 + [2] Begin = RVA = 0x10B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xB0 + [2] End = RVA = 0x11C6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x1C6 [2] UnwindInfoAddress = RVA = 0x26EC, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x28 - [3] Begin = RVA = 0x11C8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x1C8 - [3] End = RVA = 0x1248, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x248 + [3] Begin = RVA = 0x11C8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x1C8 + [3] End = RVA = 0x1248, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x248 [3] UnwindInfoAddress = RVA = 0x2730, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x6C - [4] Begin = RVA = 0x1248, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x248 - [4] End = RVA = 0x1370, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x370 + [4] Begin = RVA = 0x1248, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x248 + [4] End = RVA = 0x1370, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x370 [4] UnwindInfoAddress = RVA = 0x278C, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC8 - [5] Begin = RVA = 0x1370, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x370 - [5] End = RVA = 0x13AD, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x3AD + [5] Begin = RVA = 0x1370, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x370 + [5] End = RVA = 0x13AD, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x3AD [5] UnwindInfoAddress = RVA = 0x27BC, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xF8 - [6] Begin = RVA = 0x13B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x3B0 - [6] End = RVA = 0x13E4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x3E4 + [6] Begin = RVA = 0x13B0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x3B0 + [6] End = RVA = 0x13E4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x3E4 [6] UnwindInfoAddress = RVA = 0x27E0, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x11C - [7] Begin = RVA = 0x13E4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x3E4 - [7] End = RVA = 0x14B7, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x4B7 + [7] Begin = RVA = 0x13E4, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x3E4 + [7] End = RVA = 0x14B7, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x4B7 [7] UnwindInfoAddress = RVA = 0x27CC, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x108 - [8] Begin = RVA = 0x14B8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x4B8 - [8] End = RVA = 0x1529, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x529 + [8] Begin = RVA = 0x14B8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x4B8 + [8] End = RVA = 0x1529, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x529 [8] UnwindInfoAddress = RVA = 0x27D4, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x110 - [9] Begin = RVA = 0x152C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x52C - [9] End = RVA = 0x15D8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x5D8 + [9] Begin = RVA = 0x152C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x52C + [9] End = RVA = 0x15D8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x5D8 [9] UnwindInfoAddress = RVA = 0x27E8, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x124 - [10] Begin = RVA = 0x1604, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x604 - [10] End = RVA = 0x161F, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x61F + [10] Begin = RVA = 0x1604, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x604 + [10] End = RVA = 0x161F, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x61F [10] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 - [11] Begin = RVA = 0x1620, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x620 - [11] End = RVA = 0x1659, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x659 + [11] Begin = RVA = 0x1620, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x620 + [11] End = RVA = 0x1659, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x659 [11] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 - [12] Begin = RVA = 0x165C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x65C - [12] End = RVA = 0x1690, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x690 + [12] Begin = RVA = 0x165C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x65C + [12] End = RVA = 0x1690, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x690 [12] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 - [13] Begin = RVA = 0x1690, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x690 - [13] End = RVA = 0x16A5, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6A5 + [13] Begin = RVA = 0x1690, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x690 + [13] End = RVA = 0x16A5, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x6A5 [13] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 - [14] Begin = RVA = 0x16A8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6A8 - [14] End = RVA = 0x16D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6D0 + [14] Begin = RVA = 0x16A8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x6A8 + [14] End = RVA = 0x16D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x6D0 [14] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 - [15] Begin = RVA = 0x16D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6D0 - [15] End = RVA = 0x16E5, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6E5 + [15] Begin = RVA = 0x16D0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x6D0 + [15] End = RVA = 0x16E5, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x6E5 [15] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 - [16] Begin = RVA = 0x16E8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x6E8 - [16] End = RVA = 0x1748, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x748 + [16] Begin = RVA = 0x16E8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x6E8 + [16] End = RVA = 0x1748, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x748 [16] UnwindInfoAddress = RVA = 0x281C, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x158 - [17] Begin = RVA = 0x1748, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x748 - [17] End = RVA = 0x1778, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x778 + [17] Begin = RVA = 0x1748, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x748 + [17] End = RVA = 0x1778, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x778 [17] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 - [18] Begin = RVA = 0x1778, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x778 - [18] End = RVA = 0x178C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x78C + [18] Begin = RVA = 0x1778, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x778 + [18] End = RVA = 0x178C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x78C [18] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 - [19] Begin = RVA = 0x178C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x78C - [19] End = RVA = 0x17C6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x7C6 + [19] Begin = RVA = 0x178C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x78C + [19] End = RVA = 0x17C6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x7C6 [19] UnwindInfoAddress = RVA = 0x2784, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xC0 - [20] Begin = RVA = 0x17C8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x7C8 - [20] End = RVA = 0x1853, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x853 + [20] Begin = RVA = 0x17C8, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x7C8 + [20] End = RVA = 0x1853, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x853 [20] UnwindInfoAddress = RVA = 0x27E0, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x11C - [21] Begin = RVA = 0x1854, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x854 - [21] End = RVA = 0x18EC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x8EC + [21] Begin = RVA = 0x1854, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x854 + [21] End = RVA = 0x18EC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x8EC [21] UnwindInfoAddress = RVA = 0x27F4, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x130 - [22] Begin = RVA = 0x18EC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x8EC - [22] End = RVA = 0x1910, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x910 + [22] Begin = RVA = 0x18EC, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x8EC + [22] End = RVA = 0x1910, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x910 [22] UnwindInfoAddress = RVA = 0x27E0, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x11C - [23] Begin = RVA = 0x1910, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x910 - [23] End = RVA = 0x1939, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x939 + [23] Begin = RVA = 0x1910, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x910 + [23] End = RVA = 0x1939, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x939 [23] UnwindInfoAddress = RVA = 0x27E0, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x11C - [24] Begin = RVA = 0x194C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0x94C - [24] End = RVA = 0x1A94, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xA94 + [24] Begin = RVA = 0x194C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x94C + [24] End = RVA = 0x1A94, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xA94 [24] UnwindInfoAddress = RVA = 0x2830, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x16C - [25] Begin = RVA = 0x1A94, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xA94 - [25] End = RVA = 0x1AD0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xAD0 + [25] Begin = RVA = 0x1A94, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xA94 + [25] End = RVA = 0x1AD0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xAD0 [25] UnwindInfoAddress = RVA = 0x2840, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x17C - [26] Begin = RVA = 0x1AD0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xAD0 - [26] End = RVA = 0x1B0C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xB0C + [26] Begin = RVA = 0x1AD0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xAD0 + [26] End = RVA = 0x1B0C, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xB0C [26] UnwindInfoAddress = RVA = 0x2840, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x17C - [27] Begin = RVA = 0x1B10, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xB10 - [27] End = RVA = 0x1DDA, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xDDA + [27] Begin = RVA = 0x1B10, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xB10 + [27] End = RVA = 0x1DDA, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xDDA [27] UnwindInfoAddress = RVA = 0x284C, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x188 - [28] Begin = RVA = 0x1E60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE60 - [28] End = RVA = 0x1E62, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE62 + [28] Begin = RVA = 0x1E60, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xE60 + [28] End = RVA = 0x1E62, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xE62 [28] UnwindInfoAddress = RVA = 0x2860, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x19C - [29] Begin = RVA = 0x1E80, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE80 - [29] End = RVA = 0x1E86, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE86 + [29] Begin = RVA = 0x1E80, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xE80 + [29] End = RVA = 0x1E86, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xE86 [29] UnwindInfoAddress = RVA = 0x2868, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x1A4 - [30] Begin = RVA = 0x1E86, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE86 - [30] End = RVA = 0x1E9D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE9D + [30] Begin = RVA = 0x1E86, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xE86 + [30] End = RVA = 0x1E9D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xE9D [30] UnwindInfoAddress = RVA = 0x2728, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x64 - [31] Begin = RVA = 0x1E9D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xE9D - [31] End = RVA = 0x1EB6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xEB6 + [31] Begin = RVA = 0x1E9D, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xE9D + [31] End = RVA = 0x1EB6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xEB6 [31] UnwindInfoAddress = RVA = 0x2728, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x64 - [32] Begin = RVA = 0x1EB6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xEB6 - [32] End = RVA = 0x1ECA, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xECA + [32] Begin = RVA = 0x1EB6, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xEB6 + [32] End = RVA = 0x1ECA, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xECA [32] UnwindInfoAddress = RVA = 0x2728, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x64 - [33] Begin = RVA = 0x1ECA, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xECA - [33] End = RVA = 0x1F00, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xF00 + [33] Begin = RVA = 0x1ECA, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xECA + [33] End = RVA = 0x1F00, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xF00 [33] UnwindInfoAddress = RVA = 0x27B4, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0xF0 - [34] Begin = RVA = 0x1F00, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xF00 - [34] End = RVA = 0x1F18, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x1000, Position = 0x400, Size = 0x1000 }, Offset = 0xF18 + [34] Begin = RVA = 0x1F00, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xF00 + [34] End = RVA = 0x1F18, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0xF18 [34] UnwindInfoAddress = RVA = 0x2814, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x150 - Section Data [01] PEStreamSectionData Position = 0x000025A4, Size = 0x0000005C, RVA = 0x000041A4, VirtualSize = 0x0000005C - [04] .rsrc PESection Position = 0x00002600, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x000000F8, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) - Section Data [00] PEResourceDirectory Position = 0x00002600, Size = 0x000000F8, RVA = 0x00005000, VirtualSize = 0x000000F8 - > PEResourceDirectoryEntry { Entries[1] , TimeDateStamp = 01/01/1970 00:00:00, MajorVersion = 0, MinorVersion = 0} + [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) } - Section Data [00] PEStreamSectionData Position = 0x000026EC, Size = 0x0000000C, RVA = 0x000050EC, VirtualSize = 0x0000000C - - - Section Data [01] PEStreamSectionData Position = 0x000026F8, Size = 0x00000108, RVA = 0x000050F8, VirtualSize = 0x00000108 - - - [05] .reloc PESection Position = 0x00002800, Size = 0x00000200, RVA = 0x00006000, VirtualSize = 0x0000002C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) - - Section Data [00] PEBaseRelocationDirectory Position = 0x00002800, Size = 0x0000002C, RVA = 0x00006000, VirtualSize = 0x0000002C - Block 0x2000 Relocations[17] - Dir64 Offset = 0x00E8, RVA = 0x20E8 (0x0000000180001B0C), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x00F0, RVA = 0x20F0 (0x0000000180001B0C), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x00F8, RVA = 0x20F8 (0x0000000180001E60), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0100, RVA = 0x2100 (0x0000000180001E80), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0108, RVA = 0x2108 (0x0000000180001E80), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0118, RVA = 0x2118 (0x0000000180001E3B), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0168, RVA = 0x2168 (0x0000000180003090), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x0170, RVA = 0x2170 (0x0000000180003130), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] .rdata } - Dir64 Offset = 0x01E8, RVA = 0x21E8 (0x0000000180003000), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x0200, RVA = 0x2200 (0x00000001800020E8), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x0208, RVA = 0x2208 (0x00000001800020F8), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x0290, RVA = 0x2290 (0x0000000180002380), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x02A8, RVA = 0x22A8 (0x00000001800020F0), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x02B0, RVA = 0x22B0 (0x0000000180002100), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x02B8, RVA = 0x22B8 (0x0000000180002108), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x02C0, RVA = 0x22C0 (0x0000000180002110), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } - Dir64 Offset = 0x02C8, RVA = 0x22C8 (0x0000000180002118), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] .rdata } - Section Data [00] PEStreamSectionData Position = 0x00002824, Size = 0x00000008, RVA = 0x00006024, VirtualSize = 0x00000008 - - - Section Data [01] PEStreamSectionData Position = 0x0000282C, Size = 0x000001D4, RVA = 0x0000602C, VirtualSize = 0x000001D4 - + > 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 + + + [05] .reloc PESection Position = 0x00002800, Size = 0x00000200, RVA = 0x00006000, VirtualSize = 0x0000002C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) + + [00] PEBaseRelocationDirectory Position = 0x00002800, Size = 0x0000002C, RVA = 0x00006000, VirtualSize = 0x0000002C + Block 0x2000 Relocations[18] + [000] Dir64 Offset = 0x00E8, RVA = 0x20E8 (0x0000000180001B0C), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [001] Dir64 Offset = 0x00F0, RVA = 0x20F0 (0x0000000180001B0C), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [002] Dir64 Offset = 0x00F8, RVA = 0x20F8 (0x0000000180001E60), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [003] Dir64 Offset = 0x0100, RVA = 0x2100 (0x0000000180001E80), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [004] Dir64 Offset = 0x0108, RVA = 0x2108 (0x0000000180001E80), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [005] Dir64 Offset = 0x0118, RVA = 0x2118 (0x0000000180001E3B), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [006] Dir64 Offset = 0x0168, RVA = 0x2168 (0x0000000180003090), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [007] Dir64 Offset = 0x0170, RVA = 0x2170 (0x0000000180003130), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [008] Dir64 Offset = 0x01E8, RVA = 0x21E8 (0x0000000180003000), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [009] Dir64 Offset = 0x0200, RVA = 0x2200 (0x00000001800020E8), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [010] Dir64 Offset = 0x0208, RVA = 0x2208 (0x00000001800020F8), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [011] Dir64 Offset = 0x0290, RVA = 0x2290 (0x0000000180002380), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [012] Dir64 Offset = 0x02A8, RVA = 0x22A8 (0x00000001800020F0), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [013] Dir64 Offset = 0x02B0, RVA = 0x22B0 (0x0000000180002100), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [014] Dir64 Offset = 0x02B8, RVA = 0x22B8 (0x0000000180002108), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [015] Dir64 Offset = 0x02C0, RVA = 0x22C0 (0x0000000180002110), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [016] Dir64 Offset = 0x02C8, RVA = 0x22C8 (0x0000000180002118), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [017] Absolute Zero padding + + diff --git a/src/LibObjectFile/Ar/ArArchiveFile.cs b/src/LibObjectFile/Ar/ArArchiveFile.cs index 2f9a004..dccaeba 100644 --- a/src/LibObjectFile/Ar/ArArchiveFile.cs +++ b/src/LibObjectFile/Ar/ArArchiveFile.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. @@ -325,7 +325,7 @@ public void Write(Stream stream) writer.Write(); } - public override void UpdateLayout(ArVisitorContext visitorContext) + public override void UpdateLayout(ArVisitorContext context) { } diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 57fc0d2..df33aec 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.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. @@ -125,37 +125,12 @@ public enum DiagnosticId PE_ERR_InvalidSectionHeadersSize = 3007, PE_ERR_InvalidParent = 3008, PE_ERR_InvalidExtraData = 3009, - - // PE BaseRelocation - PE_ERR_BaseRelocationDirectoryInvalidEndOfStream = 3020, - PE_ERR_BaseRelocationDirectoryInvalidSection = 3021, - PE_ERR_BaseRelocationDirectoryInvalidSectionData = 3022, - PE_ERR_InvalidDataDirectorySection = 3023, - PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress = 3024, - - // 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, - PE_ERR_ImportAddressTableNotFound = 3046, - PE_ERR_InvalidInternalState = 3047, - - // PE Export - PE_ERR_ExportAddressTableInvalidRVA = 3060, - PE_ERR_ExportDirectoryInvalidAddressOfNames = 3061, - PE_ERR_ExportDirectoryInvalidAddressOfFunctions = 3062, - PE_ERR_ExportDirectoryInvalidAddressOfNameOrdinals = 3063, - PE_ERR_ExportDirectoryInvalidName = 3064, - PE_ERR_ExportNameTableInvalidRVA = 3065, - - // PE Resource directory - PE_ERR_InvalidResourceDirectory = 3080, - PE_ERR_InvalidResourceDirectoryEntry = 3081, - PE_ERR_InvalidResourceDirectoryEntryRVAOffsetToData = 3082, - + PE_ERR_SectionSizeLargerThanVirtualSize = 3010, + PE_ERR_SectionRVALessThanPrevious = 3011, + PE_ERR_TooManySections = 3012, + PE_ERR_FileAlignmentNotPowerOfTwo = 3013, + PE_ERR_SectionAlignmentNotPowerOfTwo = 3014, + PE_ERR_SectionAlignmentLessThanFileAlignment = 315, // PE Exception directory PE_ERR_InvalidExceptionDirectory_Entries = 3100, @@ -165,12 +140,6 @@ public enum DiagnosticId // PE Certificate directory PE_ERR_InvalidCertificateEntry = 3200, - // PE TLS directory - PE_ERR_InvalidTlsStartAddressOfRawData = 3300, - PE_ERR_InvalidTlsEndAddressOfRawData = 3301, - PE_ERR_InvalidTlsAddressOfIndex = 3302, - PE_ERR_InvalidTlsAddressOfCallBacks = 3303, - // PE BoundImport directory PE_ERR_BoundImportDirectoryInvalidEndOfStream = 3400, PE_ERR_BoundImportDirectoryInvalidModuleName = 3401, @@ -193,4 +162,34 @@ public enum DiagnosticId PE_ERR_InvalidDebugDataRSDSSignature = 3603, PE_ERR_InvalidDebugDataRSDSPdbPath = 3604, PE_ERR_DebugDirectoryExtraData = 3605, + + // PE BaseRelocation + PE_ERR_BaseRelocationDirectoryInvalidEndOfStream = 3700, + PE_ERR_BaseRelocationDirectoryInvalidSection = 3701, + PE_ERR_BaseRelocationDirectoryInvalidSectionData = 3702, + PE_ERR_InvalidDataDirectorySection = 3703, + PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress = 3704, + + // PE Import + PE_ERR_ImportDirectoryInvalidEndOfStream = 3800, + PE_ERR_ImportLookupTableInvalidEndOfStream = 3801, + PE_ERR_ImportLookupTableInvalidHintNameTableRVA = 3802, + PE_ERR_ImportLookupTableInvalidParent = 3803, + PE_ERR_ImportDirectoryInvalidImportAddressTableRVA = 3804, + PE_ERR_ImportDirectoryInvalidImportLookupTableRVA = 3805, + PE_ERR_ImportAddressTableNotFound = 3806, + PE_ERR_InvalidInternalState = 3807, + + // PE Export + PE_ERR_ExportAddressTableInvalidRVA = 3900, + PE_ERR_ExportDirectoryInvalidAddressOfNames = 3901, + PE_ERR_ExportDirectoryInvalidAddressOfFunctions = 3902, + PE_ERR_ExportDirectoryInvalidAddressOfNameOrdinals = 3903, + PE_ERR_ExportDirectoryInvalidName = 3904, + PE_ERR_ExportNameTableInvalidRVA = 3905, + + // PE Resource directory + PE_ERR_InvalidResourceDirectory = 4000, + PE_ERR_InvalidResourceDirectoryEntry = 4001, + PE_ERR_InvalidResourceDirectoryEntryRVAOffsetToData = 4002, } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs index f2ab5b3..69001cd 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.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. @@ -14,6 +14,11 @@ namespace LibObjectFile.PE; [DebuggerDisplay("{ToString(),nq}")] public readonly record struct PEBaseRelocation(PEBaseRelocationType Type, PESectionData? Container, RVO RVO) : IPELink { + ///

+ /// Gets a value indicating whether the base relocation is zero padding. + /// + public bool IsZero => Type == PEBaseRelocationType.Absolute; + /// /// Reads the address from the section data. /// @@ -60,5 +65,5 @@ public ulong ReadAddress(PEFile file) } } - public override string ToString() => $"{Type} {this.ToDisplayTextWithRVA()}"; + public override string ToString() => Type == PEBaseRelocationType.Absolute ? $"{Type} Zero Padding" : $"{Type} {this.ToDisplayTextWithRVA()}"; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs index bc4ef94..44072fb 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.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. @@ -53,7 +53,15 @@ internal unsafe uint CalculateSizeOf() return (uint)BlockBuffer.Length; } - return (uint)(Relocations.Count * sizeof(ushort)); + var count = Relocations.Count; + + // If we have an odd number of relocations, we need to add an extra 0x0 + if (count > 0 && (count & 1) != 0) + { + count++; + } + + return (uint)(count * sizeof(ushort)); } internal void ReadAndBind(PEImageReader reader) @@ -62,35 +70,33 @@ internal void ReadAndBind(PEImageReader reader) var relocSpan = MemoryMarshal.Cast(buffer.Span); - // Remove padding zeros at the end of the block - if (relocSpan.Length > 0 && relocSpan[^1].IsZero) - { - relocSpan = relocSpan.Slice(0, relocSpan.Length - 1); - } - var section = SectionLink.Container!; var blockBaseAddress = SectionLink.RVA(); // Iterate on all relocations - foreach (var relocation in relocSpan) + foreach (var rawReloc in relocSpan) { - if (relocation.IsZero) + PEBaseRelocation reloc; + if (rawReloc.IsZero) { - continue; + reloc = new PEBaseRelocation(); } + else + { + var va = blockBaseAddress + rawReloc.OffsetInBlockPart; - var va = blockBaseAddress + relocation.OffsetInBlockPart; + // Find the section data containing the virtual address + if (!section.TryFindSectionData(va, out var sectionData)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section data containing the virtual address 0x{va:X4}"); + continue; + } - // Find the section data containing the virtual address - if (!section.TryFindSectionData(va, out var sectionData)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section data containing the virtual address 0x{va:X4}"); - continue; - } + var offsetInSectionData = va - sectionData.RVA; + reloc = new PEBaseRelocation(rawReloc.Type, sectionData, offsetInSectionData); - var offsetInSectionData = va - sectionData.RVA; - var newRelocation = new PEBaseRelocation(relocation.Type, sectionData, offsetInSectionData); - Relocations.Add(newRelocation); + } + Relocations.Add(reloc); } // Clear the buffer, as we don't need it anymore diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs index 19e66d3..29fcd97 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.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. @@ -19,12 +19,12 @@ public PEBaseRelocationDirectory() : base(PEDataDirectoryKind.BaseRelocation) public List Blocks { get; } = new(); - protected override uint ComputeHeaderSize(PEVisitorContext context) + protected override unsafe uint ComputeHeaderSize(PEVisitorContext context) { var size = 0U; foreach (var block in Blocks) { - size += block.CalculateSizeOf(); + size += (uint)(block.CalculateSizeOf() + sizeof(ImageBaseRelocation)); } return size; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index f8107ff..8f10e87 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.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. @@ -43,22 +43,22 @@ public sealed override void UpdateLayout(PELayoutContext context) // 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; - foreach (var table in Content) + var position = Position + headerSize; + foreach (var subData in Content) { - table.RVA = va; + subData.RVA = va; // Update layout will update virtual address if (!context.UpdateSizeOnly) { - table.Position = position; + subData.Position = position; } - table.UpdateLayout(context); + subData.UpdateLayout(context); - va += (uint)table.Size; - size += table.Size; - position += table.Size; + va += (uint)subData.Size; + size += subData.Size; + position += subData.Size; } Size = size; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs index d352e3e..7bfc095 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.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. @@ -146,6 +146,21 @@ internal void Set(PEDataDirectoryKind kind, PEDataDirectory? directory) _count++; } } + + internal int CalculateNumberOfEntries() + { + int count = 0; + ReadOnlySpan span = _entries; + for(int i = 0; i < span.Length; i++) + { + if (_entries[i] is not null) + { + count = i + 1; + } + } + + return count; + } [InlineArray(15)] private struct InternalArray diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index 55e3d84..4b1e6a7 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -1,13 +1,17 @@ -// 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.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Xml.Linq; using LibObjectFile.Diagnostics; +using static System.Collections.Specialized.BitVector32; namespace LibObjectFile.PE; @@ -49,24 +53,17 @@ public override unsafe void Read(PEImageReader reader) for (int i = 0; i < Values.Count; i++) { var rva = spanRva[i]; - if (!reader.File.TryFindContainerByRVA(rva, out var container)) + if (!reader.File.TryFindSection(rva, out var section)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); return; } - ObjectElement? parent = container; - while (parent != null && parent is not PESection) - { - parent = parent.Parent; - } - - Debug.Assert(parent is not null); - var section = (PESection)parent!; + var found = section.TryFindSectionData(rva, out var sectionData); if (section.Name == PESectionName.EData) { - var streamSectionData = container as PEStreamSectionData; + var streamSectionData = sectionData as PEStreamSectionData; if (streamSectionData is null) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Invalid forwarder RVA {rva} for Export Address Table"); @@ -74,11 +71,17 @@ public override unsafe void Read(PEImageReader reader) } Values[i] = new PEExportFunctionEntry(new PEAsciiStringLink(streamSectionData, rva - streamSectionData.RVA)); - } else { - Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(container, rva - container.RVA)); + if (found) + { + Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(sectionData, rva - sectionData!.RVA)); + } + else + { + Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(section, rva - section.RVA)); + } } } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs index 60597d2..22d4579 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.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/ImageDosHeader.cs b/src/LibObjectFile/PE/ImageDosHeader.cs index 42b0072..a8f297d 100644 --- a/src/LibObjectFile/PE/ImageDosHeader.cs +++ b/src/LibObjectFile/PE/ImageDosHeader.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. @@ -106,5 +106,5 @@ public unsafe struct ImageDosHeader /// /// File address of new exe header. (Original DOS field is `e_lfanew`) /// - public int FileAddressPEHeader; + public uint FileAddressPEHeader; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 288eb9f..b6f873c 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.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. @@ -93,6 +93,7 @@ public override void Read(PEImageReader reader) _dosStub = []; } + read = (int)reader.Position; // Read any DOS stub extra data (e.g Rich) if (DosHeader.FileAddressPEHeader > read) { @@ -100,7 +101,10 @@ public override void Read(PEImageReader reader) } // Read the PE signature - reader.Stream.Seek(DosHeader.FileAddressPEHeader, SeekOrigin.Begin); + if (reader.Position != DosHeader.FileAddressPEHeader) + { + reader.Position = DosHeader.FileAddressPEHeader; + } var signature = default(ImagePESignature); read = reader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref signature, 1))); @@ -208,7 +212,7 @@ public override void Read(PEImageReader reader) } } - private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan headers) + private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan sectionHeaders) { _sections.Clear(); @@ -216,21 +220,23 @@ private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan 0) + if (sectionHeaders.Length > 0) { - positionFirstSection = headers[0].PointerToRawData; + positionFirstSection = sectionHeaders[0].PointerToRawData; } uint positionAfterLastSection = positionFirstSection; // Create sections - foreach (var section in headers) + foreach (var section in sectionHeaders) { // We don't validate the name var peSection = new PESection( new PESectionName(section.NameAsString, false), section.RVA, section.VirtualSize) { Position = section.PointerToRawData, - Size = section.SizeOfRawData, + // Use the exact size of the section on disk if virtual size is smaller + // as these are considered as padding between sections + Size = section.VirtualSize < section.SizeOfRawData ? section.VirtualSize : section.SizeOfRawData, Characteristics = section.Characteristics, }; @@ -402,15 +408,16 @@ private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan(); - listOrderedByPosition.AddRange(dataParts.UnsafeList); - listOrderedByPosition.Sort((a, b) => a.Position.CompareTo(b.Position)); + var listOrderedByPosition = dataParts.UnsafeList; + + 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++) { var data = listOrderedByPosition[i]; @@ -480,10 +492,12 @@ private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size)) { Position = currentPosition, + Parent = container, }; - dataParts.Insert(data.Index, sectionData); + listOrderedByPosition.Insert(i, sectionData); currentPosition = data.Position; + i++; } else if (currentPosition > data.Position) { @@ -501,27 +515,36 @@ private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size)) { Position = currentPosition, + Parent = container, }; - dataParts.Add(sectionData); + listOrderedByPosition.Add(sectionData); } else if (currentPosition > startPosition + totalSize) { imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data position {currentPosition} > {startPosition + totalSize} in {container}"); } + + // Make sure to update the indices after inserting the missing streams + for (var i = 0; i < listOrderedByPosition.Count; i++) + { + var data = listOrderedByPosition[i]; + data.Index = i; + } } - private static void FillExtraDataWithMissingStreams(PEImageReader imageReader, ObjectList list, ulong extraPosition, ulong extraTotalSize) + private static void FillExtraDataWithMissingStreams(PEImageReader imageReader, PEObjectBase parent, ObjectList list, ulong extraPosition, ulong extraTotalSize) { var currentPosition = extraPosition; imageReader.Position = extraPosition; // We are working on position, while the list is ordered by VirtualAddress - list.UnsafeList.Sort((a, b) => a.Position.CompareTo(b.Position)); + var listOrderedByPosition = list.UnsafeList; + listOrderedByPosition.Sort((a, b) => a.Position.CompareTo(b.Position)); - for (var i = 0; i < list.Count; i++) + for (var i = 0; i < listOrderedByPosition.Count; i++) { - var data = list[i]; + var data = listOrderedByPosition[i]; if (currentPosition < data.Position) { var size = data.Position - currentPosition; @@ -530,11 +553,12 @@ private static void FillExtraDataWithMissingStreams(PEImageReader imageReader, O var sectionData = new PEStreamExtraData(imageReader.ReadAsStream(size)) { Position = currentPosition, - Size = size, + Parent = parent, }; - list.Insert(data.Index, sectionData); + listOrderedByPosition.Insert(i, sectionData); currentPosition = data.Position; + i++; } else if (currentPosition > data.Position) { @@ -552,17 +576,24 @@ private static void FillExtraDataWithMissingStreams(PEImageReader imageReader, O var sectionData = new PEStreamExtraData(imageReader.ReadAsStream(size)) { Position = currentPosition, - Size = size, + Parent = parent, }; - list.Add(sectionData); + listOrderedByPosition.Add(sectionData); } else if (currentPosition > extraPosition + extraTotalSize) { imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid extra data position {currentPosition} > {extraPosition + extraTotalSize}"); } + + // Make sure to update the indices after inserting the missing streams + for (var i = 0; i < listOrderedByPosition.Count; i++) + { + var data = listOrderedByPosition[i]; + data.Index = i; + } } - + private static void FillDirectoryWithStreams(PEImageReader imageReader, PEDataDirectory directory) { FillSectionDataWithMissingStreams(imageReader, directory, directory.Content, directory.Position + directory.HeaderSize, directory.Size - directory.HeaderSize); diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 94bb9e3..9b8f247 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -6,11 +6,15 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Numerics; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; +using LibObjectFile.PE.Internal; +using LibObjectFile.Utils; namespace LibObjectFile.PE; @@ -250,8 +254,141 @@ public bool TryFindByVA(VA64 va, [NotNullWhen(true)] out PEObject? result, out R return false; } - public override void UpdateLayout(PELayoutContext layoutContext) + /// + /// Updates the layout of this PE file. + /// + /// The diagnostics to output errors. + public void UpdateLayout(DiagnosticBag diagnostics) { + var context = new PELayoutContext(this, diagnostics); + UpdateLayout(context); + } + + /// + public override unsafe void UpdateLayout(PELayoutContext context) + { + var position = 0U; + + // Update DOS header + position += (uint)sizeof(ImageDosHeader); + position += (uint)_dosStub.Length; + position += (uint)(_dosStubExtra?.Length ?? 0U); + + // Update optional header + position = AlignHelper.AlignUp(position, 8); // PE header is aligned on 8 bytes + + // Update offset to PE header + DosHeader.FileAddressPEHeader = position; + + position += sizeof(ImagePESignature); // PE00 header + + // COFF header + position += (uint)sizeof(ImageCoffHeader); + + // TODO: update other DosHeader fields + + position += (uint)(IsPE32 ? sizeof(RawImageOptionalHeader32) : sizeof(RawImageOptionalHeader64)); + + // Update directories + position += (uint)(Directories.CalculateNumberOfEntries() * sizeof(ImageDataDirectory)); + + // TODO: Additional optional header size? + + // Data before sections + foreach (var extraData in ExtraDataBeforeSections) + { + extraData.Position = position; + extraData.UpdateLayout(context); + var dataSize = (uint)extraData.Size; + position += dataSize; + } + + if (_sections.Count > 96) + { + context.Diagnostics.Error(DiagnosticId.PE_ERR_TooManySections, $"Too many sections {_sections.Count} (max 96)"); + } + + + // Update COFF header + CoffHeader.NumberOfSections = (ushort)_sections.Count; + CoffHeader.PointerToSymbolTable = 0; + CoffHeader.NumberOfSymbols = 0; + + OptionalHeader.SizeOfCode = 0; + OptionalHeader.SizeOfInitializedData = 0; + OptionalHeader.SizeOfUninitializedData = 0; + + if (!BitOperations.IsPow2(OptionalHeader.FileAlignment) || OptionalHeader.FileAlignment == 0) + { + context.Diagnostics.Error(DiagnosticId.PE_ERR_FileAlignmentNotPowerOfTwo, $"File alignment {OptionalHeader.FileAlignment} is not a power of two"); + return; + } + + if (!BitOperations.IsPow2(OptionalHeader.SectionAlignment) || OptionalHeader.SectionAlignment == 0) + { + context.Diagnostics.Error(DiagnosticId.PE_ERR_SectionAlignmentNotPowerOfTwo, $"Section alignment {OptionalHeader.SectionAlignment} is not a power of two"); + return; + } + + // Ensure that SectionAlignment is greater or equal to FileAlignment + if (OptionalHeader.SectionAlignment < OptionalHeader.FileAlignment) + { + context.Diagnostics.Error(DiagnosticId.PE_ERR_SectionAlignmentLessThanFileAlignment, $"Section alignment {OptionalHeader.SectionAlignment} is less than file alignment {OptionalHeader.FileAlignment}"); + return; + + } + + // Ensure that SectionAlignment is a multiple of FileAlignment + position = AlignHelper.AlignUp(position, OptionalHeader.FileAlignment); + OptionalHeader.SizeOfHeaders = position; + + // Update sections + RVA previousEndOfRVA = 0U; + foreach (var section in _sections) + { + section.Position = position; + section.UpdateLayout(context); + if (section.RVA < previousEndOfRVA) + { + context.Diagnostics.Error(DiagnosticId.PE_ERR_SectionRVALessThanPrevious, $"Section {section.Name} RVA {section.RVA} is less than the previous section end RVA {previousEndOfRVA}"); + } + + var sectionSize = (uint)section.Size; + position += sectionSize; + + var virtualSizeDiskAligned = AlignHelper.AlignUp(section.VirtualSize, OptionalHeader.FileAlignment); + + if ((section.Characteristics & SectionCharacteristics.ContainsCode) != 0) + { + OptionalHeader.SizeOfCode += virtualSizeDiskAligned; + } + else if ((section.Characteristics & SectionCharacteristics.ContainsInitializedData) != 0) + { + OptionalHeader.SizeOfInitializedData += virtualSizeDiskAligned; + } + else if ((section.Characteristics & SectionCharacteristics.ContainsUninitializedData) != 0) + { + OptionalHeader.SizeOfUninitializedData += virtualSizeDiskAligned; + } + + // Update the end of the RVA + previousEndOfRVA = section.RVA + AlignHelper.AlignUp(section.VirtualSize, OptionalHeader.SectionAlignment); + } + + // Update the (virtual) size of the image + OptionalHeader.SizeOfImage = previousEndOfRVA; + + // Data after sections + foreach (var extraData in ExtraDataAfterSections) + { + extraData.Position = position; + extraData.UpdateLayout(context); + var dataSize = (uint)extraData.Size; + position += dataSize; + } + + // Update the size of the file + Size = position; } protected override bool PrintMembers(StringBuilder builder) diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index 5b38b00..0436d5c 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.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. @@ -172,7 +172,7 @@ private static void PrintSections(PEFile file, ref TextWriterIndenter writer) private static void PrintSectionData(PEFile file, PESectionData data, ref TextWriterIndenter writer) { - writer.WriteLine($"Section Data [{data.Index:00}] {PEDescribe(data)}"); + writer.WriteLine($"[{data.Index:00}] {PEDescribe(data)}"); writer.Indent(); switch (data) { @@ -259,13 +259,11 @@ private static void PrintSectionData(PEFile file, PESectionData data, ref TextWr break; } - if (data is PEDataDirectory directory) + if (data is PEDataDirectory directory && directory.Content.Count > 0) { foreach (var content in directory.Content) { - writer.Indent(); PrintSectionData(file, content, ref writer); - writer.Unindent(); } } @@ -293,20 +291,26 @@ private static void Print(PEBaseRelocationDirectory data, ref TextWriterIndenter writer.WriteLine($"Block {pageRVA} Relocations[{block.Relocations.Count}]"); writer.Indent(); - foreach (var reloc in block.Relocations) + for (var i = 0; i < block.Relocations.Count; i++) { + var reloc = block.Relocations[i]; var relocRVA = reloc.RVA(); var offsetInPage = relocRVA - pageRVA; if (reloc.Type == PEBaseRelocationType.Dir64) { - writer.WriteLine($"{reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA} (0x{reloc.ReadAddress(peFile):X16}), SectionData = {{ {PELink(reloc.Container)} }}"); + writer.WriteLine($"[{i:000}] {reloc.Type} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA} (0x{reloc.ReadAddress(peFile):X16}), SectionData = {{ {PELink(reloc.Container)} }}"); + } + else if (reloc.Type == PEBaseRelocationType.Absolute) + { + writer.WriteLine($"[{i:000}] {reloc.Type} Zero padding"); } else { - writer.WriteLine($"{reloc.Type,6} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA}, SectionData = {{ {PELink(reloc.Container)} }}"); + writer.WriteLine($"[{i:000}] {reloc.Type} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA}, SectionData = {{ {PELink(reloc.Container)} }}"); } } + writer.Unindent(); } } @@ -533,7 +537,9 @@ private static void Print(PELoadConfigDirectory64 data, ref TextWriterIndenter w private static void Print(PEResourceDirectory data, ref TextWriterIndenter writer) { + writer.Indent(); Print(data.Root, ref writer); + writer.Unindent(); } private static void Print(PEResourceEntry data, ref TextWriterIndenter writer) @@ -548,10 +554,9 @@ private static void Print(PEResourceEntry data, ref TextWriterIndenter writer) writer.Indent(); foreach (var entry in dir.Entries) { - writer.Indent(); Print(entry, ref writer); - writer.Unindent(); } + writer.Unindent(); break; default: throw new ArgumentOutOfRangeException(nameof(data)); @@ -687,7 +692,7 @@ private static string PELink(PEObjectBase? peObjectBase) { if (peObjectBase is PEObject peObject) { - return $"RVA = 0x{peObject.RVA.Value:X8} ({peObject.GetType().Name}[{peObject.Index}]{PEParent((PEObjectBase?)peObject.Parent)}"; + return $"RVA = 0x{peObject.RVA.Value:X8} ({peObject.GetType().Name}[{peObject.Index}]{PEParent((PEObjectBase?)peObject.Parent)})"; } else if (peObjectBase is not null) { @@ -702,7 +707,16 @@ static string PEParent(PEObjectBase? obj) { if (obj is PESection section) { - return $" {section.Name}"; + return $" -> {section.Name}"; + } + else if (obj is PESectionData sectionData) + { + return $" -> {sectionData.GetType().Name}[{sectionData.Index}]{PEParent((PEObjectBase?)sectionData.Parent)}"; + } + + if (obj is not null) + { + return $" -> {obj.GetType().Name}"; } return ""; diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index ffc483d..1be9ee5 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.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. @@ -10,6 +10,8 @@ using System.Runtime.InteropServices; using System.Text; using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; +using LibObjectFile.Utils; namespace LibObjectFile.PE; @@ -69,20 +71,39 @@ public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESec /// 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 size = 0U; foreach (var data in Content) { data.RVA = va; + if (!context.UpdateSizeOnly) { data.Position = position; } data.UpdateLayout(context); - va += (uint)data.Size; - position += data.Size; + + var dataSize = (uint)data.Size; + va += dataSize; + position += dataSize; + size += dataSize; } + + // The size of a section is the size of the content aligned on the file alignment + Size = (Characteristics & SectionCharacteristics.ContainsUninitializedData) == 0 ? AlignHelper.AlignUp(size, fileAlignment) : (ulong)0; + + //if (Size > VirtualSize) + //{ + // // Log diagnostics error + // context.Diagnostics.Error(DiagnosticId.PE_ERR_SectionSizeLargerThanVirtualSize, $"Section {Name} size {Size} is greater than the virtual size {VirtualSize}"); + //} } public override void Read(PEImageReader reader) From 0f147b49e7faeedb5264d0d787a8eb2f1d12973b Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Fri, 27 Sep 2024 08:43:16 +0200 Subject: [PATCH 66/87] Improve PEPrinter --- ..._name=NativeConsole2Win64.exe.verified.txt | 20 +++++++---- ...r_name=NativeConsoleWin64.exe.verified.txt | 20 +++++++---- ...r_name=NativeLibraryWin64.dll.verified.txt | 20 +++++++---- src/LibObjectFile/PE/PEPrinter.cs | 33 ++++++++++++++----- 4 files changed, 66 insertions(+), 27 deletions(-) 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 1909d2c..31bd030 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 @@ -80,12 +80,21 @@ Data Directories [13] = PEDelayImportDirectory Position = 0x000029A8, Size = 0x00000040, RVA = 0x00003BA8, VirtualSize = 0x00000040 [14] = null +Section Headers + [00] .text PESection Position = 0x00000400, Size = 0x00001A00, RVA = 0x00001000, VirtualSize = 0x000019E9, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) + [01] .rdata PESection Position = 0x00001E00, Size = 0x00001A00, RVA = 0x00003000, VirtualSize = 0x0000183C, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + [02] .data PESection Position = 0x00003800, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x000006D0, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) + [03] .pdata PESection Position = 0x00003A00, Size = 0x00000400, RVA = 0x00006000, VirtualSize = 0x00000210, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + [04] .rsrc PESection Position = 0x00003E00, Size = 0x00000200, RVA = 0x00007000, VirtualSize = 0x000001E0, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + [05] .reloc PESection Position = 0x00004000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x0000003C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) + Sections + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [00] .text PESection Position = 0x00000400, Size = 0x00001A00, RVA = 0x00001000, VirtualSize = 0x000019E9, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) [00] PEStreamSectionData Position = 0x00000400, Size = 0x000019E9, RVA = 0x00001000, VirtualSize = 0x000019E9 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [01] .rdata PESection Position = 0x00001E00, Size = 0x00001A00, RVA = 0x00003000, VirtualSize = 0x0000183C, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) [00] PEImportAddressTableDirectory Position = 0x00001E00, Size = 0x00000250, RVA = 0x00003000, VirtualSize = 0x00000250 @@ -400,7 +409,7 @@ Sections [27] PEStreamSectionData Position = 0x00002E58, Size = 0x000007E4, RVA = 0x00004058, VirtualSize = 0x000007E4 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [02] .data PESection Position = 0x00003800, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x000006D0, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) [00] PEStreamSectionData Position = 0x00003800, Size = 0x00000078, RVA = 0x00005000, VirtualSize = 0x00000078 @@ -411,7 +420,7 @@ Sections [02] PEStreamSectionData Position = 0x00003890, Size = 0x00000170, RVA = 0x00005090, VirtualSize = 0x00000170 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [03] .pdata PESection Position = 0x00003A00, Size = 0x00000400, RVA = 0x00006000, VirtualSize = 0x00000210, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) [00] PEExceptionDirectory Position = 0x00003A00, Size = 0x00000210, RVA = 0x00006000, VirtualSize = 0x00000210 @@ -592,7 +601,7 @@ Sections [43] UnwindInfoAddress = RVA = 0x3AF0, PEStreamSectionData { RVA = 0x399C, VirtualSize = 0x20C, Position = 0x279C, Size = 0x20C }, Offset = 0x154 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [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 @@ -603,7 +612,7 @@ Sections [00] PEStreamSectionData Position = 0x00003FD8, Size = 0x00000008, RVA = 0x000071D8, VirtualSize = 0x00000008 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [05] .reloc PESection Position = 0x00004000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x0000003C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) [00] PEBaseRelocationDirectory Position = 0x00004000, Size = 0x0000003C, RVA = 0x00008000, VirtualSize = 0x0000003C @@ -632,4 +641,3 @@ Sections [000] Dir64 Offset = 0x0078, RVA = 0x5078 (0x000000014000133E), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] -> .data) } [001] Dir64 Offset = 0x0080, RVA = 0x5080 (0x0000000140001332), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] -> .data) } - 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 9d7b758..c62d765 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 @@ -80,12 +80,21 @@ Data Directories [13] = null [14] = null +Section Headers + [00] .text PESection Position = 0x00000400, Size = 0x00001200, RVA = 0x00001000, VirtualSize = 0x000010C9, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) + [01] .rdata PESection Position = 0x00001600, Size = 0x00001400, RVA = 0x00003000, VirtualSize = 0x00001288, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + [02] .data PESection Position = 0x00002A00, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x00000680, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) + [03] .pdata PESection Position = 0x00002C00, Size = 0x00000200, RVA = 0x00006000, VirtualSize = 0x00000198, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + [04] .rsrc PESection Position = 0x00002E00, Size = 0x00000200, RVA = 0x00007000, VirtualSize = 0x000001E0, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + [05] .reloc PESection Position = 0x00003000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x00000030, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) + Sections + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [00] .text PESection Position = 0x00000400, Size = 0x00001200, RVA = 0x00001000, VirtualSize = 0x000010C9, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) [00] PEStreamSectionData Position = 0x00000400, Size = 0x000010C9, RVA = 0x00001000, VirtualSize = 0x000010C9 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [01] .rdata PESection Position = 0x00001600, Size = 0x00001400, RVA = 0x00003000, VirtualSize = 0x00001288, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) [00] PEImportAddressTableDirectory Position = 0x00001600, Size = 0x000001F0, RVA = 0x00003000, VirtualSize = 0x000001F0 @@ -353,12 +362,12 @@ Sections [21] PEStreamSectionData Position = 0x00002240, Size = 0x00000648, RVA = 0x00003C40, VirtualSize = 0x00000648 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [02] .data PESection Position = 0x00002A00, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x00000680, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) [00] PEStreamSectionData Position = 0x00002A00, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x00000200 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [03] .pdata PESection Position = 0x00002C00, Size = 0x00000200, RVA = 0x00006000, VirtualSize = 0x00000198, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) [00] PEExceptionDirectory Position = 0x00002C00, Size = 0x00000198, RVA = 0x00006000, VirtualSize = 0x00000198 @@ -499,7 +508,7 @@ Sections [33] UnwindInfoAddress = RVA = 0x3930, PEStreamSectionData { RVA = 0x37E8, VirtualSize = 0x19C, Position = 0x1DE8, Size = 0x19C }, Offset = 0x148 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [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 @@ -510,7 +519,7 @@ Sections [00] PEStreamSectionData Position = 0x00002FD8, Size = 0x00000008, RVA = 0x000071D8, VirtualSize = 0x00000008 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [05] .reloc PESection Position = 0x00003000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x00000030, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) [00] PEBaseRelocationDirectory Position = 0x00003000, Size = 0x00000030, RVA = 0x00008000, VirtualSize = 0x00000030 @@ -536,4 +545,3 @@ Sections [018] Dir64 Offset = 0x03E0, RVA = 0x33E0 (0x0000000140003218), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } [019] Dir64 Offset = 0x03E8, RVA = 0x33E8 (0x0000000140003220), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } - 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 369995c..4d8d3d7 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 @@ -80,12 +80,21 @@ Data Directories [13] = null [14] = null +Section Headers + [00] .text PESection Position = 0x00000400, Size = 0x00001000, RVA = 0x00001000, VirtualSize = 0x00000F18, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) + [01] .rdata PESection Position = 0x00001400, Size = 0x00000E00, RVA = 0x00002000, VirtualSize = 0x00000C96, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + [02] .data PESection Position = 0x00002200, Size = 0x00000200, RVA = 0x00003000, VirtualSize = 0x00000680, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) + [03] .pdata PESection Position = 0x00002400, Size = 0x00000200, RVA = 0x00004000, VirtualSize = 0x000001A4, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + [04] .rsrc PESection Position = 0x00002600, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x000000F8, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + [05] .reloc PESection Position = 0x00002800, Size = 0x00000200, RVA = 0x00006000, VirtualSize = 0x0000002C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) + Sections + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [00] .text PESection Position = 0x00000400, Size = 0x00001000, RVA = 0x00001000, VirtualSize = 0x00000F18, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) [00] PEStreamSectionData Position = 0x00000400, Size = 0x00000F18, RVA = 0x00001000, VirtualSize = 0x00000F18 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [01] .rdata PESection Position = 0x00001400, Size = 0x00000E00, RVA = 0x00002000, VirtualSize = 0x00000C96, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) [00] PEImportAddressTableDirectory Position = 0x00001400, Size = 0x000000E8, RVA = 0x00002000, VirtualSize = 0x000000E8 @@ -273,12 +282,12 @@ Sections [15] PEStreamSectionData Position = 0x00001E18, Size = 0x0000027E, RVA = 0x00002A18, VirtualSize = 0x0000027E - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [02] .data PESection Position = 0x00002200, Size = 0x00000200, RVA = 0x00003000, VirtualSize = 0x00000680, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) [00] PEStreamSectionData Position = 0x00002200, Size = 0x00000200, RVA = 0x00003000, VirtualSize = 0x00000200 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [03] .pdata PESection Position = 0x00002400, Size = 0x00000200, RVA = 0x00004000, VirtualSize = 0x000001A4, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) [00] PEExceptionDirectory Position = 0x00002400, Size = 0x000001A4, RVA = 0x00004000, VirtualSize = 0x000001A4 @@ -423,7 +432,7 @@ Sections [34] UnwindInfoAddress = RVA = 0x2814, PEStreamSectionData { RVA = 0x26C4, VirtualSize = 0x1AC, Position = 0x1AC4, Size = 0x1AC }, Offset = 0x150 - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [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 @@ -434,7 +443,7 @@ Sections [00] PEStreamSectionData Position = 0x000026EC, Size = 0x0000000C, RVA = 0x000050EC, VirtualSize = 0x0000000C - + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [05] .reloc PESection Position = 0x00002800, Size = 0x00000200, RVA = 0x00006000, VirtualSize = 0x0000002C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) [00] PEBaseRelocationDirectory Position = 0x00002800, Size = 0x0000002C, RVA = 0x00006000, VirtualSize = 0x0000002C @@ -458,4 +467,3 @@ Sections [016] Dir64 Offset = 0x02C8, RVA = 0x22C8 (0x0000000180002118), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } [017] Absolute Zero padding - diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index 0436d5c..cb7d1f4 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -151,25 +151,40 @@ private static void PrintDataDirectories(PEFile file, ref TextWriterIndenter wri private static void PrintSections(PEFile file, ref TextWriterIndenter writer) { + writer.WriteLine("Section Headers"); + writer.Indent(); + for (var i = 0; i < file.Sections.Count; i++) + { + var section = file.Sections[i]; + writer.WriteLine($"[{i:00}] {section.Name,8} {PEDescribe(section)}, Characteristics = 0x{(uint)section.Characteristics:X8} ({section.Characteristics})"); + } + writer.Unindent(); + writer.WriteLine(); + writer.WriteLine("Sections"); + + string heading = new string('-', 224); + writer.Indent(); for (var i = 0; i < file.Sections.Count; i++) { var section = file.Sections[i]; - writer.Indent(); + + writer.WriteLine(heading); writer.WriteLine($"[{i:00}] {section.Name,8} {PEDescribe(section)}, Characteristics = 0x{(uint)section.Characteristics:X8} ({section.Characteristics})"); writer.WriteLine(); - foreach (var data in section.Content) + if (section.Content.Count > 0) { - writer.Indent(); - PrintSectionData(file, data, ref writer); - writer.Unindent(); + foreach (var data in section.Content) + { + writer.Indent(); + PrintSectionData(file, data, ref writer); + writer.Unindent(); + } } - - writer.Unindent(); - writer.WriteLine(); } + writer.Unindent(); } - + private static void PrintSectionData(PEFile file, PESectionData data, ref TextWriterIndenter writer) { writer.WriteLine($"[{data.Index:00}] {PEDescribe(data)}"); From a8831a132343c449a7316f26603c8c22a3812332 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sat, 28 Sep 2024 15:48:28 +0200 Subject: [PATCH 67/87] Add better support for resource directory --- ..._name=NativeConsole2Win64.exe.verified.txt | 19 +- ...r_name=NativeConsoleWin64.exe.verified.txt | 19 +- ...r_name=NativeLibraryWin64.dll.verified.txt | 21 +- src/LibObjectFile/ObjectFileElement.cs | 2 +- src/LibObjectFile/ObjectFileReaderWriter.cs | 17 +- .../DataDirectory/PEArchitectureDirectory.cs | 4 +- .../PEBaseRelocationDirectory.cs | 12 +- .../DataDirectory/PEBoundImportDirectory.cs | 26 +- .../PEBoundImportDirectoryEntry.cs | 2 +- .../PE/DataDirectory/PEClrMetadata.cs | 4 +- .../DataDirectory/PECompositeSectionData.cs | 107 ++++++++ .../PE/DataDirectory/PEDataDirectory.cs | 72 +----- .../PE/DataDirectory/PEDebugDirectory.cs | 51 +++- .../PE/DataDirectory/PEDebugSectionData.cs | 2 +- .../DataDirectory/PEDelayImportDirectory.cs | 41 +++- .../PE/DataDirectory/PEDirectoryTable.cs | 17 ++ .../PE/DataDirectory/PEExceptionDirectory.cs | 47 +++- .../PE/DataDirectory/PEExportDirectory.cs | 18 +- .../PE/DataDirectory/PEExportNameTable.cs | 2 +- .../DataDirectory/PEGlobalPointerDirectory.cs | 4 +- .../PEImportAddressTableDirectory.cs | 8 +- .../PE/DataDirectory/PEImportDirectory.cs | 25 +- .../PE/DataDirectory/PERawDataDirectory.cs | 4 +- .../PE/DataDirectory/PEResourceData.cs | 17 ++ .../PE/DataDirectory/PEResourceDataEntry.cs | 115 +++++---- .../PE/DataDirectory/PEResourceDirectory.cs | 176 ++------------ .../DataDirectory/PEResourceDirectoryEntry.cs | 229 +++++++++--------- .../PEResourceDirectoryEntryById.cs | 12 + .../PEResourceDirectoryEntryByName.cs | 12 + .../PE/DataDirectory/PEResourceEntry.cs | 142 +---------- .../PE/Internal/RawImageDebugDirectory.cs | 2 +- .../PE/Internal/RawImageOptionalHeader32.cs | 2 +- .../PE/Internal/RawImageSectionHeader.cs | 2 +- .../PE/Internal/RawPEBoundImportDirectory.cs | 2 +- .../Internal/RawPEBoundImportForwarderRef.cs | 2 +- src/LibObjectFile/PE/PEFile.Read.cs | 85 ++++--- src/LibObjectFile/PE/PEFile.Write.cs | 142 ++++++++++- src/LibObjectFile/PE/PEImageWriter.cs | 2 +- src/LibObjectFile/PE/PEObjectBase.cs | 18 +- src/LibObjectFile/PE/PEPrinter.cs | 21 +- src/LibObjectFile/PE/PESection.cs | 30 ++- src/LibObjectFile/PE/PESectionData.cs | 3 +- src/LibObjectFile/PE/PESectionName.cs | 9 +- src/LibObjectFile/PE/PEStreamSectionData.cs | 41 +++- 44 files changed, 937 insertions(+), 651 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEResourceData.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntryById.cs create mode 100644 src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntryByName.cs 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 From 819c74cd80c53c4398eb90e28ff6e8a8f81e41f9 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sat, 28 Sep 2024 21:28:32 +0200 Subject: [PATCH 68/87] Improve support for uncommon headers and PE directories layout --- src/LibObjectFile.Tests/PE/PEReaderTests.cs | 32 +++- ..._name=NativeConsole2Win64.exe.verified.txt | 1 + ...r_name=NativeConsoleWin64.exe.verified.txt | 1 + ...r_name=NativeLibraryWin64.dll.verified.txt | 1 + ...eaderTests.TestTinyExe97Bytes.verified.txt | 74 +++++++++ src/LibObjectFile/Diagnostics/DiagnosticId.cs | 6 +- .../PE/DataDirectory/PEDataDirectory.cs | 4 +- .../PE/DataDirectory/PEDirectoryTable.cs | 133 ++++++++------- .../PESecurityCertificateDirectory.cs | 4 +- .../PE/DataDirectory/PEUnknownDirectory.cs | 17 ++ src/LibObjectFile/PE/ImageCoffHeader.cs | 26 ++- src/LibObjectFile/PE/ImageDosHeader.cs | 5 +- src/LibObjectFile/PE/ImageOptionalHeader.cs | 45 ++++-- .../PE/Internal/RawImageOptionalHeader32.cs | 3 + .../PE/Internal/RawImageOptionalHeader64.cs | 5 +- src/LibObjectFile/PE/PEFile.Read.cs | 153 ++++++++++++------ src/LibObjectFile/PE/PEFile.cs | 76 +++++++-- src/LibObjectFile/PE/PEPrinter.cs | 4 +- 18 files changed, 440 insertions(+), 150 deletions(-) create mode 100644 src/LibObjectFile.Tests/Verified/PEReaderTests.TestTinyExe97Bytes.verified.txt create mode 100644 src/LibObjectFile/PE/DataDirectory/PEUnknownDirectory.cs diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index 84d06f8..fcaa4b3 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -4,13 +4,10 @@ using System; using System.IO; -using System.Linq; -using System.Runtime.InteropServices; using System.Threading.Tasks; using LibObjectFile.Diagnostics; using LibObjectFile.PE; using VerifyMSTest; -using VerifyTests; namespace LibObjectFile.Tests.PE; @@ -49,4 +46,33 @@ public async Task TestPrinter(string name) await Verifier.Verify(afterUpdateText).UseParameters(name).DisableRequireUniquePrefix(); } } + + [TestMethod] + public async Task TestTinyExe97Bytes() + { + // http://www.phreedom.org/research/tinype/ + // TinyPE: The smallest possible PE file + // 97 bytes + byte[] data = + [ + 0x4D, 0x5A, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x01, 0x00, 0x6A, 0x2A, 0x58, 0xC3, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x03, 0x01, 0x0B, 0x01, 0x08, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02 + ]; + + var stream = new MemoryStream(); + stream.Write(data, 0, data.Length); + stream.Position = 0; + + var peImage = PEFile.Read(stream); + var writer = new StringWriter(); + peImage.Print(writer); + var afterReadText = writer.ToString(); + + await Verifier.Verify(afterReadText); + } } \ No newline at end of file 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 b8a3e31..83c1694 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 @@ -79,6 +79,7 @@ Data Directories [12] = PEImportAddressTableDirectory Position = 0x00001E00, Size = 0x00000250, RVA = 0x00003000, VirtualSize = 0x00000250 [13] = PEDelayImportDirectory Position = 0x000029A8, Size = 0x00000040, RVA = 0x00003BA8, VirtualSize = 0x00000040 [14] = null + [15] = null Section Headers [00] .text PESection Position = 0x00000400, Size = 0x00001A00, RVA = 0x00001000, VirtualSize = 0x000019E9, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) 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 87af661..1d430df 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 @@ -79,6 +79,7 @@ Data Directories [12] = PEImportAddressTableDirectory Position = 0x00001600, Size = 0x000001F0, RVA = 0x00003000, VirtualSize = 0x000001F0 [13] = null [14] = null + [15] = null Section Headers [00] .text PESection Position = 0x00000400, Size = 0x00001200, RVA = 0x00001000, VirtualSize = 0x000010C9, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) 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 ba98b56..b38a9e1 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 @@ -79,6 +79,7 @@ Data Directories [12] = PEImportAddressTableDirectory Position = 0x00001400, Size = 0x000000E8, RVA = 0x00002000, VirtualSize = 0x000000E8 [13] = null [14] = null + [15] = null Section Headers [00] .text PESection Position = 0x00000400, Size = 0x00001000, RVA = 0x00001000, VirtualSize = 0x00000F18, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) diff --git a/src/LibObjectFile.Tests/Verified/PEReaderTests.TestTinyExe97Bytes.verified.txt b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestTinyExe97Bytes.verified.txt new file mode 100644 index 0000000..062ad5d --- /dev/null +++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestTinyExe97Bytes.verified.txt @@ -0,0 +1,74 @@ +DOS Header + Magic = DOS + ByteCountOnLastPage = 0x0 + PageCount = 0x4550 + RelocationCount = 0x0 + SizeOfParagraphsHeader = 0x14C + MinExtraParagraphs = 0x1 + MaxExtraParagraphs = 0x2A6A + InitialSSValue = 0xC358 + InitialSPValue = 0x0 + Checksum = 0x0 + InitialIPValue = 0x0 + InitialCSValue = 0x0 + FileAddressRelocationTable = 0x4 + OverlayNumber = 0x103 + Reserved = 0x10B, 0x8, 0x4, 0x0 + OEMIdentifier = 0x0 + OEMInformation = 0x0 + Reserved2 = 0x4, 0x0, 0xC, 0x0, 0x4, 0x0, 0xC, 0x0, 0x0, 0x40 + FileAddressPEHeader = 0x4 + +DOS Stub + DosStub = 0 bytes + +COFF Header + Machine = I386 + NumberOfSections = 1 + TimeDateStamp = 3277335146 + PointerToSymbolTable = 0x0 + NumberOfSymbols = 0 + SizeOfOptionalHeader = 4 + Characteristics = RelocsStripped, ExecutableImage, Bit32Machine + +Optional Header + Magic = PE32 + MajorLinkerVersion = 8 + MinorLinkerVersion = 0 + SizeOfCode = 0x4 + SizeOfInitializedData = 0x0 + SizeOfUninitializedData = 0x4 + AddressOfEntryPoint = 0xC + BaseOfCode = 0x4 + BaseOfData = 0xC + ImageBase = 0x0 + SectionAlignment = 0x4 + FileAlignment = 0x4 + MajorOperatingSystemVersion = 4 + MinorOperatingSystemVersion = 0 + MajorImageVersion = 0 + MinorImageVersion = 0 + MajorSubsystemVersion = 4 + MinorSubsystemVersion = 0 + Win32VersionValue = 0x0 + SizeOfImage = 0x68 + SizeOfHeaders = 0x64 + CheckSum = 0x0 + Subsystem = WindowsGui + DllCharacteristics = 0 + SizeOfStackReserve = 0x0 + SizeOfStackCommit = 0x0 + SizeOfHeapReserve = 0x0 + SizeOfHeapCommit = 0x0 + LoaderFlags = 0x0 + NumberOfRvaAndSizes = 0x0 + +Section Headers + [00]  PESection Position = 0x0000000C, Size = 0x00000004, RVA = 0x0000000C, VirtualSize = 0x00000004, Characteristics = 0x00000004 (TypeGroup) + +Sections + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [00]  PESection Position = 0x0000000C, Size = 0x00000004, RVA = 0x0000000C, VirtualSize = 0x00000004, Characteristics = 0x00000004 (TypeGroup) + + [00] PEStreamSectionData Position = 0x0000000C, Size = 0x00000004, RVA = 0x0000000C, VirtualSize = 0x00000004 + diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index df33aec..392b5e4 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -130,8 +130,10 @@ public enum DiagnosticId PE_ERR_TooManySections = 3012, PE_ERR_FileAlignmentNotPowerOfTwo = 3013, PE_ERR_SectionAlignmentNotPowerOfTwo = 3014, - PE_ERR_SectionAlignmentLessThanFileAlignment = 315, - + PE_ERR_SectionAlignmentLessThanFileAlignment = 3015, + PE_ERR_InvalidPEHeaderPosition = 3016, + PE_ERR_InvalidNumberOfDataDirectories = 3017, + // PE Exception directory PE_ERR_InvalidExceptionDirectory_Entries = 3100, PE_ERR_InvalidExceptionDirectory_Entry = 3101, diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index a9bec2f..dcda8a6 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -46,7 +46,7 @@ internal static PEDataDirectory Create(PEDataDirectoryKind kind, bool is32Bits) PEDataDirectoryKind.DelayImport => new PEDelayImportDirectory(), PEDataDirectoryKind.ImportAddressTable => new PEImportAddressTableDirectory(), PEDataDirectoryKind.ClrMetadata => new PEClrMetadata(), - _ => throw new ArgumentOutOfRangeException(nameof(kind)) + _ => new PEUnknownDirectory((int)kind) }; } -} +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs index 2c8614a..3bf8495 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Reflection; using System.Runtime.CompilerServices; namespace LibObjectFile.PE; @@ -17,20 +18,81 @@ namespace LibObjectFile.PE; [DebuggerDisplay($"{nameof(PEDirectoryTable)} {nameof(Count)} = {{{nameof(Count)}}}")] public sealed class PEDirectoryTable : IEnumerable { - private InternalArray _entries; + private PEObjectBase?[] _entries; private int _count; internal PEDirectoryTable() { + _entries = []; } - public PEObjectBase? this[PEDataDirectoryKind kind] => _entries[(int)kind]; + /// + /// Gets the directory entry at the specified index. + /// + /// The index of the directory entry to get. + /// The directory entry at the specified index. + /// Thrown if the index is out of range. + public PEObjectBase? this[int index] + { + get + { + if (index < 0 || index >= _count) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return _entries[index]; + } + } /// - /// Gets the number of directory entries in the array. + /// Gets the directory entry of the specified kind. Must be within the bounds of . /// - public int Count => _count; - + /// The kind of directory entry to get. + /// + /// Thrown if the kind is out of range. + public PEObjectBase? this[PEDataDirectoryKind kind] + { + get + { + int index = (int)(ushort)kind; + if (index < 0 || index >= _count) + { + throw new ArgumentOutOfRangeException(nameof(kind)); + } + return _entries[(int)kind]; + } + } + + /// + /// Gets the maximum number of directory entries in the array. + /// + public int Count + { + get => _count; + + set + { + ArgumentOutOfRangeException.ThrowIfLessThan(value, 0); + + var previousCount = _count; + // If the count is reduced, we need to check that all entries are null after + for (int i = value; i < previousCount; i++) + { + if (_entries[i] is not null) + { + throw new ArgumentOutOfRangeException(nameof(value), $"A non null directory entry was found at index {i}. This directory entry must be removed before setting a count of {value}"); + } + } + + if (_entries.Length < value) + { + Array.Resize(ref _entries, value); + } + + _count = value; + } + } + /// /// Gets the export directory information from the PE file. /// @@ -112,60 +174,23 @@ internal PEDirectoryTable() [EditorBrowsable(EditorBrowsableState.Never)] public Enumerator GetEnumerator() => new(this); - internal void Set(PESecurityCertificateDirectory? directory) - { - var kind = PEDataDirectoryKind.SecurityCertificate; - ref var entry = ref _entries[(int)kind]; - var previousEntry = entry; - entry = directory; + internal void Set(PESecurityCertificateDirectory? directory) => Set(PEDataDirectoryKind.SecurityCertificate, directory); - if (previousEntry is not null) - { - _count--; - } + internal void Set(PEDataDirectoryKind kind, PEObjectBase? directory) => Set((int)kind, directory); - if (directory is not null) - { - _count++; - } - } - - internal void Set(PEDataDirectoryKind kind, PEDataDirectory? directory) + internal void Set(int index, PEObjectBase? directory) { - ref var entry = ref _entries[(int)kind]; - var previousEntry = entry; - entry = directory; - - if (previousEntry is not null) + if (index >= Count) { - _count--; + throw new ArgumentOutOfRangeException(nameof(index), $"The directory entry only accepts {Count} entries. Set the count explicitly to allow more entries."); } - if (directory is not null) - { - _count++; - } + _entries[index] = directory; } - - internal int CalculateNumberOfEntries() - { - int count = 0; - ReadOnlySpan span = _entries; - for(int i = 0; i < span.Length; i++) - { - if (_entries[i] is not null) - { - count = i + 1; - } - } - - return count; - } - + internal unsafe void Write(PEImageWriter writer, ref uint position) { - var numberOfEntries = CalculateNumberOfEntries(); - for (int i = 0; i < numberOfEntries; i++) + for (int i = 0; i < Count; i++) { ImageDataDirectory rawDataDirectory = default; var entry = _entries[i]; @@ -176,13 +201,7 @@ internal unsafe void Write(PEImageWriter writer, ref uint position) } } - position += (uint)(numberOfEntries * sizeof(ImageDataDirectory)); - } - - [InlineArray(15)] - private struct InternalArray - { - private PEObjectBase? _element; + position += (uint)(Count * sizeof(ImageDataDirectory)); } /// diff --git a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs index 3f5cb3f..edf4221 100644 --- a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.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. @@ -71,9 +71,9 @@ public override unsafe void Read(PEImageReader reader) { Revision = header.Revision, Type = header.Type, + Data = reader.ReadAsStream(header.Length - 8) }; - certificate.Data = reader.ReadAsStream(header.Length - 8); Certificates.Add(certificate); if (reader.HasErrors) diff --git a/src/LibObjectFile/PE/DataDirectory/PEUnknownDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEUnknownDirectory.cs new file mode 100644 index 0000000..1705990 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEUnknownDirectory.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; + +/// +/// Represents an unknown directory (when going beyond the known directories). +/// +public sealed class PEUnknownDirectory : PEDataDirectory +{ + internal PEUnknownDirectory(int index) : base((PEDataDirectoryKind)index) + { + } + + protected override uint ComputeHeaderSize(PELayoutContext context) => 0; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImageCoffHeader.cs b/src/LibObjectFile/PE/ImageCoffHeader.cs index e0c03d5..d1f1575 100644 --- a/src/LibObjectFile/PE/ImageCoffHeader.cs +++ b/src/LibObjectFile/PE/ImageCoffHeader.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. @@ -19,30 +19,46 @@ public struct ImageCoffHeader /// public System.Reflection.PortableExecutable.Machine Machine; + internal ushort _NumberOfSections; + /// /// The number of sections in the file. /// - public ushort NumberOfSections; + /// + /// This value is readonly as it will be updated by and the number of sections will be computed from the sections list. + /// + public ushort NumberOfSections => _NumberOfSections; /// /// The low 32 bits of the time stamp indicating when the file was created. /// public uint TimeDateStamp; + internal uint _PointerToSymbolTable; + /// /// The file pointer to the COFF symbol table. This is zero if no symbol table is present. /// - public uint PointerToSymbolTable; + /// This value is readonly and is zero for a PE Image file. + public uint PointerToSymbolTable => _PointerToSymbolTable; + + internal uint _NumberOfSymbols; /// /// The number of entries in the symbol table. /// - public uint NumberOfSymbols; + /// This value is readonly and is zero for a PE Image file. + public uint NumberOfSymbols => _NumberOfSymbols; + + internal ushort _SizeOfOptionalHeader; /// /// The size of the optional header, which is required for executable files but not for object files. /// - public ushort SizeOfOptionalHeader; + /// + /// This value is readonly as it will be updated automatically when reading or updating the layout of the PE file. + /// + public ushort SizeOfOptionalHeader => _SizeOfOptionalHeader; /// /// The characteristics of the file that define its properties, such as whether it's an executable, a DLL, etc. diff --git a/src/LibObjectFile/PE/ImageDosHeader.cs b/src/LibObjectFile/PE/ImageDosHeader.cs index a8f297d..69ad31c 100644 --- a/src/LibObjectFile/PE/ImageDosHeader.cs +++ b/src/LibObjectFile/PE/ImageDosHeader.cs @@ -103,8 +103,11 @@ public unsafe struct ImageDosHeader /// public fixed ushort Reserved2[10]; + internal uint _FileAddressPEHeader; + /// /// File address of new exe header. (Original DOS field is `e_lfanew`) /// - public uint FileAddressPEHeader; + /// This property is automatically calculated but can be slightly adjusted with + public uint FileAddressPEHeader => _FileAddressPEHeader; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImageOptionalHeader.cs b/src/LibObjectFile/PE/ImageOptionalHeader.cs index cb4d0af..7375e03 100644 --- a/src/LibObjectFile/PE/ImageOptionalHeader.cs +++ b/src/LibObjectFile/PE/ImageOptionalHeader.cs @@ -1,8 +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.Diagnostics.CodeAnalysis; +using System.Numerics; using LibObjectFile.PE.Internal; namespace LibObjectFile.PE; @@ -20,10 +22,12 @@ public struct ImageOptionalHeader /// /// The magic number, which identifies the file format. Expected to be 0x10b for PE32. /// + /// + /// This value cannot be changed and must be set at construction time. + /// public ImageOptionalHeaderMagic Magic { get => OptionalHeaderCommonPart1.Magic; - set => OptionalHeaderCommonPart1.Magic = value; } /// @@ -50,7 +54,6 @@ public byte MinorLinkerVersion public uint SizeOfCode { get => OptionalHeaderCommonPart1.SizeOfCode; - set => OptionalHeaderCommonPart1.SizeOfCode = value; } /// @@ -59,7 +62,6 @@ public uint SizeOfCode public uint SizeOfInitializedData { get => OptionalHeaderCommonPart1.SizeOfInitializedData; - set => OptionalHeaderCommonPart1.SizeOfInitializedData = value; } /// @@ -68,7 +70,6 @@ public uint SizeOfInitializedData public uint SizeOfUninitializedData { get => OptionalHeaderCommonPart1.SizeOfUninitializedData; - set => OptionalHeaderCommonPart1.SizeOfUninitializedData = value; } /// @@ -86,7 +87,6 @@ public uint AddressOfEntryPoint public uint BaseOfCode { get => OptionalHeaderCommonPart1.BaseOfCode; - set => OptionalHeaderCommonPart1.BaseOfCode = value; } /// @@ -98,7 +98,6 @@ public uint BaseOfCode public uint BaseOfData { get => OptionalHeaderBase32.BaseOfData; - set => OptionalHeaderBase32.BaseOfData = value; } // NT additional fields. @@ -106,10 +105,12 @@ public uint BaseOfData /// /// The preferred address of the first byte of the image when loaded into memory. /// + /// + /// In order to change the ImageBase use + /// public ulong ImageBase { get => OptionalHeaderBase64.ImageBase; - set => OptionalHeaderBase64.ImageBase = value; } /// @@ -118,7 +119,20 @@ public ulong ImageBase public uint SectionAlignment { get => OptionalHeaderCommonPart2.SectionAlignment; - set => OptionalHeaderCommonPart2.SectionAlignment = value; + set + { + if (value == 0 || !BitOperations.IsPow2(value)) + { + throw new ArgumentOutOfRangeException(nameof(value), "SectionAlignment must be a power of 2 and not zero"); + } + + if (SectionAlignment < FileAlignment) + { + throw new ArgumentOutOfRangeException(nameof(value), "SectionAlignment must be greater than or equal to FileAlignment"); + } + + OptionalHeaderCommonPart2.SectionAlignment = value; + } } /// @@ -127,7 +141,15 @@ public uint SectionAlignment public uint FileAlignment { get => OptionalHeaderCommonPart2.FileAlignment; - set => OptionalHeaderCommonPart2.FileAlignment = value; + set + { + if (value == 0 || !BitOperations.IsPow2(value)) + { + throw new ArgumentOutOfRangeException(nameof(value), "FileAlignment must be a power of 2 and not zero"); + } + + OptionalHeaderCommonPart2.FileAlignment = value; + } } /// @@ -199,7 +221,6 @@ public uint Win32VersionValue public uint SizeOfImage { get => OptionalHeaderCommonPart2.SizeOfImage; - set => OptionalHeaderCommonPart2.SizeOfImage = value; } /// @@ -208,7 +229,6 @@ public uint SizeOfImage public uint SizeOfHeaders { get => OptionalHeaderCommonPart2.SizeOfHeaders; - set => OptionalHeaderCommonPart2.SizeOfHeaders = value; } /// @@ -289,7 +309,6 @@ public uint LoaderFlags public uint NumberOfRvaAndSizes { get => OptionalHeaderCommonPart3.NumberOfRvaAndSizes; - set => OptionalHeaderCommonPart3.NumberOfRvaAndSizes = value; } /// diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs index febb7a6..19b1b42 100644 --- a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs @@ -16,5 +16,8 @@ internal struct RawImageOptionalHeader32 public RawImageOptionalHeaderCommonPart2 Common2; public RawImageOptionalHeaderSize32 Size32; public RawImageOptionalHeaderCommonPart3 Common3; + + // In case of a PE Header with zero directories + public static unsafe int MinimumSize => sizeof(RawImageOptionalHeader32) - sizeof(ImageDataDirectoryArray); } diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.cs index 83de80e..fb04b5c 100644 --- a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.cs +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.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. @@ -16,4 +16,7 @@ internal struct RawImageOptionalHeader64 public RawImageOptionalHeaderCommonPart2 Common2; public RawImageOptionalHeaderSize64 Size64; public RawImageOptionalHeaderCommonPart3 Common3; + + // In case of a PE Header with zero directories + public static unsafe int MinimumSize => sizeof(RawImageOptionalHeader64) - sizeof(ImageDataDirectoryArray); } diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 3690eba..8c73d42 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -57,7 +57,7 @@ public static bool TryRead(Stream stream, [NotNullWhen(true)] out PEFile? peFile return !reader.Diagnostics.HasErrors; } - public override void Read(PEImageReader reader) + public override unsafe void Read(PEImageReader reader) { Debug.Assert(Unsafe.SizeOf() == 64); @@ -75,37 +75,58 @@ public override void Read(PEImageReader reader) return; } - // Read the DOS stub - var dosStubSize = DosHeader.SizeOfParagraphsHeader * 16; - if (dosStubSize > 0) + var pePosition = DosHeader.FileAddressPEHeader; + + if (pePosition < sizeof(ImageDosHeader)) { - var dosStub = new byte[dosStubSize]; - read = reader.Read(dosStub); - if (read != dosStubSize) + if (pePosition < 4) { - diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosStubSize, "Invalid DOS stub"); + diagnostics.Error(DiagnosticId.PE_ERR_InvalidPEHeaderPosition, "Invalid PE header position"); + return; } - _dosStub = dosStub; + _unsafeNegativePEHeaderOffset = (int)pePosition - sizeof(ImageDosHeader); } else { - _dosStub = []; - } + // Read the DOS stub + var dosStubSize = DosHeader.SizeOfParagraphsHeader * 16; - read = (int)reader.Position; - // Read any DOS stub extra data (e.g Rich) - if (DosHeader.FileAddressPEHeader > read) - { - _dosStubExtra = reader.ReadAsStream((ulong)(DosHeader.FileAddressPEHeader - read)); - } + if (dosStubSize + sizeof(ImageDosHeader) > pePosition) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosStubSize, $"Invalid DOS stub size {dosStubSize} going beyond the PE header"); + return; + } + + if (dosStubSize > 0) + { + var dosStub = new byte[dosStubSize]; + read = reader.Read(dosStub); + if (read != dosStubSize) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosStubSize, "Invalid DOS stub"); + return; + } - // Read the PE signature - if (reader.Position != DosHeader.FileAddressPEHeader) - { - reader.Position = DosHeader.FileAddressPEHeader; + _dosStub = dosStub; + } + else + { + _dosStub = []; + } + + var dosHeaderAndStubSize = sizeof(ImageDosHeader) + dosStubSize; + + // Read any DOS stub extra data (e.g Rich) + if (dosHeaderAndStubSize < DosHeader.FileAddressPEHeader) + { + _dosStubExtra = reader.ReadAsStream((ulong)(DosHeader.FileAddressPEHeader - dosHeaderAndStubSize)); + } } + // Read the PE header + reader.Position = DosHeader.FileAddressPEHeader; + var signature = default(ImagePESignature); read = reader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref signature, 1))); if (read != sizeof(ImagePESignature)) @@ -131,19 +152,49 @@ public override void Read(PEImageReader reader) } CoffHeader = coffHeader; - var tempArray = ArrayPool.Shared.Rent(CoffHeader.SizeOfOptionalHeader); + var positionAfterCoffHeader = reader.Position; + + // Cannot be smaller than the magic + if (CoffHeader.SizeOfOptionalHeader < sizeof(ImageOptionalHeaderMagic)) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidOptionalHeaderSize, $"Invalid optional header size {CoffHeader.SizeOfOptionalHeader}"); + return; + } + + var magic = (ImageOptionalHeaderMagic)reader.ReadU16(); + + int minimumSizeOfOptionalHeaderToRead = 0; + + // Process known PE32/PE32+ headers + if (magic == ImageOptionalHeaderMagic.PE32) + { + minimumSizeOfOptionalHeaderToRead = RawImageOptionalHeader32.MinimumSize; + } + else if (magic == ImageOptionalHeaderMagic.PE32Plus) + { + minimumSizeOfOptionalHeaderToRead = RawImageOptionalHeader64.MinimumSize; + } + else + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidOptionalHeaderMagic, $"Invalid optional header PE magic 0x{(uint)magic:X8}"); + return; + } + + minimumSizeOfOptionalHeaderToRead = Math.Max(CoffHeader.SizeOfOptionalHeader, minimumSizeOfOptionalHeaderToRead); + + var tempArray = ArrayPool.Shared.Rent(minimumSizeOfOptionalHeaderToRead); try { - var optionalHeader = new Span(tempArray, 0, CoffHeader.SizeOfOptionalHeader); - read = reader.Read(optionalHeader); - if (read != CoffHeader.SizeOfOptionalHeader) - { - diagnostics.Error(DiagnosticId.PE_ERR_InvalidOptionalHeaderSize, "Invalid optional header"); - return; - } + var optionalHeader = new Span(tempArray, 0, minimumSizeOfOptionalHeaderToRead); + MemoryMarshal.Write(optionalHeader, (ushort)magic); - var magic = MemoryMarshal.Cast(optionalHeader.Slice(0, 2))[0]; + // We have already read the magic number + var minimumSpan = optionalHeader.Slice(sizeof(ImageOptionalHeaderMagic)); + read = reader.Read(minimumSpan); + // The real size read (in case of tricked Tiny PE) + optionalHeader = optionalHeader.Slice(0, read + sizeof(ImageOptionalHeaderMagic)); + Debug.Assert(Unsafe.SizeOf() == 224); Debug.Assert(Unsafe.SizeOf() == 240); @@ -152,9 +203,9 @@ public override void Read(PEImageReader reader) { var optionalHeader32 = new RawImageOptionalHeader32(); var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref optionalHeader32, 1)); - if (span.Length > CoffHeader.SizeOfOptionalHeader) + if (span.Length > optionalHeader.Length) { - span = span.Slice(0, CoffHeader.SizeOfOptionalHeader); + span = span.Slice(0, optionalHeader.Length); } optionalHeader.CopyTo(span); @@ -165,14 +216,14 @@ public override void Read(PEImageReader reader) OptionalHeader.OptionalHeaderSize32 = optionalHeader32.Size32; OptionalHeader.OptionalHeaderCommonPart3 = optionalHeader32.Common3; } - else if (magic == ImageOptionalHeaderMagic.PE32Plus) + else { var optionalHeader64 = new RawImageOptionalHeader64(); // Skip 2 bytes as we read already the magic number var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref optionalHeader64, 1)); - if (span.Length > CoffHeader.SizeOfOptionalHeader) + if (span.Length > optionalHeader.Length) { - span = span.Slice(0, CoffHeader.SizeOfOptionalHeader); + span = span.Slice(0, optionalHeader.Length); } optionalHeader.CopyTo(span); @@ -183,13 +234,13 @@ public override void Read(PEImageReader reader) OptionalHeader.OptionalHeaderSize64 = optionalHeader64.Size64; OptionalHeader.OptionalHeaderCommonPart3 = optionalHeader64.Common3; } - else - { - diagnostics.Error(DiagnosticId.PE_ERR_InvalidOptionalHeaderMagic, $"Invalid optional header PE magic 0x{(uint)magic:X8}"); - return; - } + + // Sets the number of entries in the data directory + Directories.Count = (int)OptionalHeader.NumberOfRvaAndSizes; // Read sections + reader.Position = positionAfterCoffHeader + CoffHeader.SizeOfOptionalHeader; + ArrayPool.Shared.Return(tempArray); Debug.Assert(Unsafe.SizeOf() == 40); var sizeOfSections = CoffHeader.NumberOfSections * Unsafe.SizeOf(); @@ -205,6 +256,9 @@ public override void Read(PEImageReader reader) // Read all sections and directories ReadSectionsAndDirectories(reader, sectionHeaders); + + // Set the size to the full size of the file + Size = reader.Length; } finally { @@ -273,11 +327,12 @@ private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan positionAfterHeaders) + { + FillExtraDataWithMissingStreams(reader, this, ExtraDataBeforeSections, positionAfterHeaders, positionFirstSection - positionAfterHeaders); + } + FillExtraDataWithMissingStreams(reader, this, ExtraDataAfterSections, positionAfterLastSection, reader.Length - positionAfterLastSection); + // Create Stream data sections for remaining data per section based on directories already loaded for (var i = 0; i < _sections.Count; i++) { diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 9b8f247..87f5b9b 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -26,16 +26,20 @@ public sealed partial class PEFile : PEObjectBase private byte[] _dosStub = []; private Stream? _dosStubExtra; private readonly ObjectList _sections; + private int _unsafeNegativePEHeaderOffset; /// /// Initializes a new instance of the class. /// - public PEFile() + public PEFile(ImageOptionalHeaderMagic magic = ImageOptionalHeaderMagic.PE32Plus) { _sections = new(this); ExtraDataBeforeSections = new(this); ExtraDataAfterSections = new(this); + // TODO: Add default initialization + + OptionalHeader.OptionalHeaderCommonPart1.Magic = magic; } /// @@ -53,6 +57,30 @@ internal PEFile(bool unused) /// public ImageDosHeader DosHeader; + /// + /// Gets or sets an unsafe negative offset relative to the end of the DOS header. + /// + public unsafe int UnsafeNegativePEHeaderOffset + { + get => _unsafeNegativePEHeaderOffset; + set + { + // Should support Tiny PE layout http://www.phreedom.org/research/tinype/ + // Value must be >= sizeof(ImageDosHeader) - 4 and <= 0 + if (value < sizeof(ImageDosHeader) - 4 || value > 0) + { + throw new ArgumentOutOfRangeException(nameof(value), $"PEHeaderOffset must be greater than {sizeof(ImageDosHeader)}"); + } + + if (value < 0 && (_dosStub.Length != 0 || (DosStubExtra is not null && DosStubExtra.Length > 0))) + { + throw new InvalidOperationException("Setting a negative PEHeader offset is not compatible with having a DOS Stub and DosStubExtra"); + } + + _unsafeNegativePEHeaderOffset = value; + } + } + /// /// Gets or sets the DOS stub. /// @@ -60,7 +88,15 @@ public byte[] DosStub { get => _dosStub; - set => _dosStub = value ?? throw new ArgumentNullException(nameof(value)); + set + { + if (_unsafeNegativePEHeaderOffset < 0) + { + throw new InvalidOperationException("Cannot set a DosStub when UnsafeNegativePEHeaderOffset is negative"); + } + + _dosStub = value ?? throw new ArgumentNullException(nameof(value)); + } } /// @@ -70,7 +106,15 @@ public Stream? DosStubExtra { get => _dosStubExtra; - set => _dosStubExtra = value; + set + { + if (_unsafeNegativePEHeaderOffset < 0) + { + throw new InvalidOperationException("Cannot set a DosStubExtra when UnsafeNegativePEHeaderOffset is negative"); + } + + _dosStubExtra = value; + } } /// @@ -278,7 +322,7 @@ public override unsafe void UpdateLayout(PELayoutContext context) position = AlignHelper.AlignUp(position, 8); // PE header is aligned on 8 bytes // Update offset to PE header - DosHeader.FileAddressPEHeader = position; + DosHeader._FileAddressPEHeader = position; position += sizeof(ImagePESignature); // PE00 header @@ -290,7 +334,7 @@ public override unsafe void UpdateLayout(PELayoutContext context) position += (uint)(IsPE32 ? sizeof(RawImageOptionalHeader32) : sizeof(RawImageOptionalHeader64)); // Update directories - position += (uint)(Directories.CalculateNumberOfEntries() * sizeof(ImageDataDirectory)); + position += (uint)(Directories.Count * sizeof(ImageDataDirectory)); // TODO: Additional optional header size? @@ -310,13 +354,13 @@ public override unsafe void UpdateLayout(PELayoutContext context) // Update COFF header - CoffHeader.NumberOfSections = (ushort)_sections.Count; - CoffHeader.PointerToSymbolTable = 0; - CoffHeader.NumberOfSymbols = 0; + CoffHeader._NumberOfSections = (ushort)_sections.Count; + CoffHeader._PointerToSymbolTable = 0; + CoffHeader._NumberOfSymbols = 0; - OptionalHeader.SizeOfCode = 0; - OptionalHeader.SizeOfInitializedData = 0; - OptionalHeader.SizeOfUninitializedData = 0; + OptionalHeader.OptionalHeaderCommonPart1.SizeOfCode = 0; + OptionalHeader.OptionalHeaderCommonPart1.SizeOfInitializedData = 0; + OptionalHeader.OptionalHeaderCommonPart1.SizeOfUninitializedData = 0; if (!BitOperations.IsPow2(OptionalHeader.FileAlignment) || OptionalHeader.FileAlignment == 0) { @@ -340,7 +384,7 @@ public override unsafe void UpdateLayout(PELayoutContext context) // Ensure that SectionAlignment is a multiple of FileAlignment position = AlignHelper.AlignUp(position, OptionalHeader.FileAlignment); - OptionalHeader.SizeOfHeaders = position; + OptionalHeader.OptionalHeaderCommonPart2.SizeOfHeaders = position; // Update sections RVA previousEndOfRVA = 0U; @@ -360,15 +404,15 @@ public override unsafe void UpdateLayout(PELayoutContext context) if ((section.Characteristics & SectionCharacteristics.ContainsCode) != 0) { - OptionalHeader.SizeOfCode += virtualSizeDiskAligned; + OptionalHeader.OptionalHeaderCommonPart1.SizeOfCode += virtualSizeDiskAligned; } else if ((section.Characteristics & SectionCharacteristics.ContainsInitializedData) != 0) { - OptionalHeader.SizeOfInitializedData += virtualSizeDiskAligned; + OptionalHeader.OptionalHeaderCommonPart1.SizeOfInitializedData += virtualSizeDiskAligned; } else if ((section.Characteristics & SectionCharacteristics.ContainsUninitializedData) != 0) { - OptionalHeader.SizeOfUninitializedData += virtualSizeDiskAligned; + OptionalHeader.OptionalHeaderCommonPart1.SizeOfUninitializedData += virtualSizeDiskAligned; } // Update the end of the RVA @@ -376,7 +420,7 @@ public override unsafe void UpdateLayout(PELayoutContext context) } // Update the (virtual) size of the image - OptionalHeader.SizeOfImage = previousEndOfRVA; + OptionalHeader.OptionalHeaderCommonPart2.SizeOfImage = previousEndOfRVA; // Data after sections foreach (var extraData in ExtraDataAfterSections) diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index f8f4cde..871e43e 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -136,9 +136,11 @@ private static void PrintOptionalHeader(PEFile file, ref TextWriterIndenter writ private static void PrintDataDirectories(PEFile file, ref TextWriterIndenter writer) { + if (file.Directories.Count == 0) return; + writer.WriteLine("Data Directories"); writer.Indent(); - for(int i = (int)PEDataDirectoryKind.Export; i <= (int)PEDataDirectoryKind.ClrMetadata; i++) + for(int i = 0; i < file.Directories.Count; i++) { var kind = (PEDataDirectoryKind)i; var directory = file.Directories[kind]; From 3d7b8c5560608a4952ae9975504761b16c0bb88e Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 29 Sep 2024 06:55:04 +0200 Subject: [PATCH 69/87] Add PooledSpan --- src/LibObjectFile/Collections/PooledSpan.cs | 89 +++++++++++ .../PE/DataDirectory/PEDirectoryTable.cs | 5 +- .../PE/ImageDataDirectoryArray.cs | 27 ---- src/LibObjectFile/PE/ImageOptionalHeader.cs | 6 - .../RawImageDataDirectory.cs} | 6 +- .../PE/Internal/RawImageDataDirectoryArray.cs | 27 ++++ .../PE/Internal/RawImageOptionalHeader32.cs | 2 +- .../PE/Internal/RawImageOptionalHeader64.cs | 2 +- .../RawImageOptionalHeaderCommonPart3.cs | 4 +- src/LibObjectFile/PE/PEFile.Read.cs | 138 +++++++++--------- src/LibObjectFile/PE/PEFile.cs | 2 +- 11 files changed, 197 insertions(+), 111 deletions(-) create mode 100644 src/LibObjectFile/Collections/PooledSpan.cs delete mode 100644 src/LibObjectFile/PE/ImageDataDirectoryArray.cs rename src/LibObjectFile/PE/{ImageDataDirectory.cs => Internal/RawImageDataDirectory.cs} (81%) create mode 100644 src/LibObjectFile/PE/Internal/RawImageDataDirectoryArray.cs diff --git a/src/LibObjectFile/Collections/PooledSpan.cs b/src/LibObjectFile/Collections/PooledSpan.cs new file mode 100644 index 0000000..54ef173 --- /dev/null +++ b/src/LibObjectFile/Collections/PooledSpan.cs @@ -0,0 +1,89 @@ +// 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.Runtime.InteropServices; + +namespace LibObjectFile.Collections; + +/// +/// Represents a pooled span that can be created from a stack or heap memory. +/// +/// The type of the elements in the span. +public readonly ref struct PooledSpan where T : unmanaged +{ + private readonly Span _span; + private readonly byte[]? _buffer; + + /// + /// Initializes a new instance of the struct using a stack memory span. + /// + /// The stack memory span. + /// The size of the span in bytes. + private PooledSpan(Span span, int size) + { + _span = MemoryMarshal.Cast(span.Slice(0, size)); + _buffer = null; + } + + /// + /// Initializes a new instance of the struct using a heap memory span. + /// + /// The heap memory buffer. + /// The size of the span in bytes. + private PooledSpan(byte[] buffer, int size) + { + _span = MemoryMarshal.Cast(new(buffer, 0, size)); + _buffer = buffer; + } + + /// + /// Creates a new instance of the struct with the specified number of elements. + /// + /// The number of elements in the span. + /// A new instance of the struct. + public static unsafe PooledSpan Create(int count) + { + ArgumentOutOfRangeException.ThrowIfLessThan(count, 0); + var size = count * sizeof(T); + var buffer = ArrayPool.Shared.Rent(size); + return new PooledSpan(buffer, size); + } + + /// + /// Creates a new instance of the struct with the specified number of elements, using a stack memory span if possible. + /// + /// The stack memory span. + /// The number of elements in the span. + /// A new instance of the struct. + public static unsafe PooledSpan Create(Span stackSpan, int count) + { + ArgumentOutOfRangeException.ThrowIfLessThan(count, 0); + var size = count * sizeof(T); + return size <= stackSpan.Length ? new PooledSpan(stackSpan, size) : Create(count); + } + + /// + /// Gets the span of elements. + /// + public Span Span => _span; + + /// + /// Gets the span of elements as bytes. + /// + public Span SpanAsBytes => MemoryMarshal.AsBytes(_span); + + /// + /// Releases the underlying memory buffer if it was allocated on the heap. + /// + public void Dispose() + { + var buffer = _buffer; + if (buffer != null) + { + ArrayPool.Shared.Return(buffer); + } + } +} diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs index 3bf8495..b157cfe 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; +using LibObjectFile.PE.Internal; namespace LibObjectFile.PE; @@ -192,7 +193,7 @@ internal unsafe void Write(PEImageWriter writer, ref uint position) { for (int i = 0; i < Count; i++) { - ImageDataDirectory rawDataDirectory = default; + RawImageDataDirectory rawDataDirectory = default; var entry = _entries[i]; if (entry is not null) { @@ -201,7 +202,7 @@ internal unsafe void Write(PEImageWriter writer, ref uint position) } } - position += (uint)(Count * sizeof(ImageDataDirectory)); + position += (uint)(Count * sizeof(RawImageDataDirectory)); } /// diff --git a/src/LibObjectFile/PE/ImageDataDirectoryArray.cs b/src/LibObjectFile/PE/ImageDataDirectoryArray.cs deleted file mode 100644 index add7bd3..0000000 --- a/src/LibObjectFile/PE/ImageDataDirectoryArray.cs +++ /dev/null @@ -1,27 +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. - -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -namespace LibObjectFile.PE; - -#pragma warning disable CS0649 - -/// -/// An array of entries. -/// -[InlineArray(16)] -public struct ImageDataDirectoryArray -{ - private ImageDataDirectory _e; - - /// - /// Gets or sets the at the specified index. - /// - /// The index of the to get or set. - /// The at the specified index. - [UnscopedRef] - public ref ImageDataDirectory this[PEDataDirectoryKind kind] => ref this[(int)kind]; -} \ No newline at end of file diff --git a/src/LibObjectFile/PE/ImageOptionalHeader.cs b/src/LibObjectFile/PE/ImageOptionalHeader.cs index 7375e03..2df3eb2 100644 --- a/src/LibObjectFile/PE/ImageOptionalHeader.cs +++ b/src/LibObjectFile/PE/ImageOptionalHeader.cs @@ -310,12 +310,6 @@ public uint NumberOfRvaAndSizes { get => OptionalHeaderCommonPart3.NumberOfRvaAndSizes; } - - /// - /// The data directories array, which contains the location and size of special tables in the file. - /// - [UnscopedRef] - public ref ImageDataDirectoryArray DataDirectory => ref OptionalHeaderCommonPart3.DataDirectory; internal void SyncPE32PlusToPE32() { diff --git a/src/LibObjectFile/PE/ImageDataDirectory.cs b/src/LibObjectFile/PE/Internal/RawImageDataDirectory.cs similarity index 81% rename from src/LibObjectFile/PE/ImageDataDirectory.cs rename to src/LibObjectFile/PE/Internal/RawImageDataDirectory.cs index 781120e..31aeb80 100644 --- a/src/LibObjectFile/PE/ImageDataDirectory.cs +++ b/src/LibObjectFile/PE/Internal/RawImageDataDirectory.cs @@ -1,17 +1,17 @@ -// 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.Runtime.InteropServices; -namespace LibObjectFile.PE; +namespace LibObjectFile.PE.Internal; #pragma warning disable CS0649 /// /// Data directory entry in the optional header of a Portable Executable (PE) file. /// [StructLayout(LayoutKind.Sequential, Pack = 4)] -public struct ImageDataDirectory +internal struct RawImageDataDirectory { /// /// The relative virtual address of the data directory. diff --git a/src/LibObjectFile/PE/Internal/RawImageDataDirectoryArray.cs b/src/LibObjectFile/PE/Internal/RawImageDataDirectoryArray.cs new file mode 100644 index 0000000..ace99f1 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageDataDirectoryArray.cs @@ -0,0 +1,27 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace LibObjectFile.PE.Internal; + +#pragma warning disable CS0649 + +/// +/// An array of entries. +/// +[InlineArray(16)] +internal struct RawImageDataDirectoryArray +{ + private RawImageDataDirectory _e; + + /// + /// Gets or sets the at the specified index. + /// + /// The index of the to get or set. + /// The at the specified index. + [UnscopedRef] + public ref RawImageDataDirectory this[PEDataDirectoryKind kind] => ref this[(int)kind]; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs index 19b1b42..93ce0e8 100644 --- a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs @@ -18,6 +18,6 @@ internal struct RawImageOptionalHeader32 public RawImageOptionalHeaderCommonPart3 Common3; // In case of a PE Header with zero directories - public static unsafe int MinimumSize => sizeof(RawImageOptionalHeader32) - sizeof(ImageDataDirectoryArray); + public static unsafe int MinimumSize => sizeof(RawImageOptionalHeader32) - sizeof(RawImageDataDirectoryArray); } diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.cs index fb04b5c..514baf7 100644 --- a/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.cs +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.cs @@ -18,5 +18,5 @@ internal struct RawImageOptionalHeader64 public RawImageOptionalHeaderCommonPart3 Common3; // In case of a PE Header with zero directories - public static unsafe int MinimumSize => sizeof(RawImageOptionalHeader64) - sizeof(ImageDataDirectoryArray); + public static unsafe int MinimumSize => sizeof(RawImageOptionalHeader64) - sizeof(RawImageDataDirectoryArray); } diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart3.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart3.cs index ad13d8c..25c50c7 100644 --- a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart3.cs +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart3.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. @@ -24,5 +24,5 @@ internal struct RawImageOptionalHeaderCommonPart3 /// /// The data directories array, which contains the location and size of special tables in the file. /// - public ImageDataDirectoryArray DataDirectory; + public RawImageDataDirectoryArray DataDirectory; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 8c73d42..d4c34d4 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -180,93 +180,96 @@ public override unsafe void Read(PEImageReader reader) return; } - minimumSizeOfOptionalHeaderToRead = Math.Max(CoffHeader.SizeOfOptionalHeader, minimumSizeOfOptionalHeaderToRead); + Span optionalHeader = stackalloc byte[minimumSizeOfOptionalHeaderToRead]; + MemoryMarshal.Write(optionalHeader, (ushort)magic); - var tempArray = ArrayPool.Shared.Rent(minimumSizeOfOptionalHeaderToRead); - try - { - var optionalHeader = new Span(tempArray, 0, minimumSizeOfOptionalHeaderToRead); - MemoryMarshal.Write(optionalHeader, (ushort)magic); - - // We have already read the magic number - var minimumSpan = optionalHeader.Slice(sizeof(ImageOptionalHeaderMagic)); - read = reader.Read(minimumSpan); + // We have already read the magic number + var minimumSpan = optionalHeader.Slice(sizeof(ImageOptionalHeaderMagic)); + read = reader.Read(minimumSpan); - // The real size read (in case of tricked Tiny PE) - optionalHeader = optionalHeader.Slice(0, read + sizeof(ImageOptionalHeaderMagic)); - - Debug.Assert(Unsafe.SizeOf() == 224); - Debug.Assert(Unsafe.SizeOf() == 240); + // The real size read (in case of tricked Tiny PE) + optionalHeader = optionalHeader.Slice(0, read + sizeof(ImageOptionalHeaderMagic)); + + Debug.Assert(Unsafe.SizeOf() == 224); + Debug.Assert(Unsafe.SizeOf() == 240); - // Process known PE32/PE32+ headers - if (magic == ImageOptionalHeaderMagic.PE32) + // Process known PE32/PE32+ headers + if (magic == ImageOptionalHeaderMagic.PE32) + { + var optionalHeader32 = new RawImageOptionalHeader32(); + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref optionalHeader32, 1)); + if (span.Length > optionalHeader.Length) { - var optionalHeader32 = new RawImageOptionalHeader32(); - var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref optionalHeader32, 1)); - if (span.Length > optionalHeader.Length) - { - span = span.Slice(0, optionalHeader.Length); - } + span = span.Slice(0, optionalHeader.Length); + } - optionalHeader.CopyTo(span); + optionalHeader.CopyTo(span); - OptionalHeader.OptionalHeaderCommonPart1 = optionalHeader32.Common; - OptionalHeader.OptionalHeaderBase32 = optionalHeader32.Base32; - OptionalHeader.OptionalHeaderCommonPart2 = optionalHeader32.Common2; - OptionalHeader.OptionalHeaderSize32 = optionalHeader32.Size32; - OptionalHeader.OptionalHeaderCommonPart3 = optionalHeader32.Common3; - } - else + OptionalHeader.OptionalHeaderCommonPart1 = optionalHeader32.Common; + OptionalHeader.OptionalHeaderBase32 = optionalHeader32.Base32; + OptionalHeader.OptionalHeaderCommonPart2 = optionalHeader32.Common2; + OptionalHeader.OptionalHeaderSize32 = optionalHeader32.Size32; + OptionalHeader.OptionalHeaderCommonPart3 = optionalHeader32.Common3; + } + else + { + var optionalHeader64 = new RawImageOptionalHeader64(); + // Skip 2 bytes as we read already the magic number + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref optionalHeader64, 1)); + if (span.Length > optionalHeader.Length) { - var optionalHeader64 = new RawImageOptionalHeader64(); - // Skip 2 bytes as we read already the magic number - var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref optionalHeader64, 1)); - if (span.Length > optionalHeader.Length) - { - span = span.Slice(0, optionalHeader.Length); - } + span = span.Slice(0, optionalHeader.Length); + } - optionalHeader.CopyTo(span); + optionalHeader.CopyTo(span); - OptionalHeader.OptionalHeaderCommonPart1 = optionalHeader64.Common; - OptionalHeader.OptionalHeaderBase64 = optionalHeader64.Base64; - OptionalHeader.OptionalHeaderCommonPart2 = optionalHeader64.Common2; - OptionalHeader.OptionalHeaderSize64 = optionalHeader64.Size64; - OptionalHeader.OptionalHeaderCommonPart3 = optionalHeader64.Common3; - } + OptionalHeader.OptionalHeaderCommonPart1 = optionalHeader64.Common; + OptionalHeader.OptionalHeaderBase64 = optionalHeader64.Base64; + OptionalHeader.OptionalHeaderCommonPart2 = optionalHeader64.Common2; + OptionalHeader.OptionalHeaderSize64 = optionalHeader64.Size64; + OptionalHeader.OptionalHeaderCommonPart3 = optionalHeader64.Common3; + } - // Sets the number of entries in the data directory - Directories.Count = (int)OptionalHeader.NumberOfRvaAndSizes; + // Read Directory headers + using var pooledSpanDirectories = PooledSpan.Create(stackalloc byte[16 * sizeof(RawImageDataDirectory)], (int)OptionalHeader.NumberOfRvaAndSizes); + var directories = pooledSpanDirectories.Span; - // Read sections - reader.Position = positionAfterCoffHeader + CoffHeader.SizeOfOptionalHeader; - - ArrayPool.Shared.Return(tempArray); - Debug.Assert(Unsafe.SizeOf() == 40); - var sizeOfSections = CoffHeader.NumberOfSections * Unsafe.SizeOf(); - tempArray = ArrayPool.Shared.Rent(sizeOfSections); - var spanSections = new Span(tempArray, 0, sizeOfSections); - read = reader.Read(spanSections); + // Sets the number of entries in the data directory + Directories.Count = (int)OptionalHeader.NumberOfRvaAndSizes; - var sectionHeaders = MemoryMarshal.Cast(spanSections); - if (read != spanSections.Length) + if (OptionalHeader.NumberOfRvaAndSizes > 0) + { + var span = MemoryMarshal.AsBytes(directories); + read = reader.Read(span); + if (read != span.Length) { - diagnostics.Error(DiagnosticId.PE_ERR_InvalidSectionHeadersSize, "Invalid section headers"); + diagnostics.Error(DiagnosticId.PE_ERR_InvalidNumberOfDataDirectories, $"Invalid number of data directory {OptionalHeader.NumberOfRvaAndSizes} at position {reader.Position}"); + return; } + } + + // Read sections + reader.Position = positionAfterCoffHeader + CoffHeader.SizeOfOptionalHeader; + + Debug.Assert(Unsafe.SizeOf() == 40); - // Read all sections and directories - ReadSectionsAndDirectories(reader, sectionHeaders); + using var pooledSpanSections = PooledSpan.Create((int)CoffHeader.NumberOfSections); + read = reader.Read(pooledSpanSections.SpanAsBytes); - // Set the size to the full size of the file - Size = reader.Length; - } - finally + if (read != pooledSpanSections.SpanAsBytes.Length) { - ArrayPool.Shared.Return(tempArray); + diagnostics.Error(DiagnosticId.PE_ERR_InvalidSectionHeadersSize, "Invalid section headers"); + return; } + + // Read all sections and directories + ReadSectionsAndDirectories(reader, pooledSpanSections.Span, pooledSpanDirectories.Span); + + // Set the size to the full size of the file + Size = reader.Length; } - private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan sectionHeaders) + private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan sectionHeaders, ReadOnlySpan dataDirectories) { _sections.Clear(); @@ -309,7 +312,6 @@ private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan dataDirectories = OptionalHeader.DataDirectory; for (int i = 0; i < maxNumberOfDirectory && i < dataDirectories.Length; i++) { var directoryEntry = dataDirectories[i]; diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 87f5b9b..cb6f26c 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -334,7 +334,7 @@ public override unsafe void UpdateLayout(PELayoutContext context) position += (uint)(IsPE32 ? sizeof(RawImageOptionalHeader32) : sizeof(RawImageOptionalHeader64)); // Update directories - position += (uint)(Directories.Count * sizeof(ImageDataDirectory)); + position += (uint)(Directories.Count * sizeof(RawImageDataDirectory)); // TODO: Additional optional header size? From dc54965eaafd6dffa703f016a0cafd4d8005cbf5 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 29 Sep 2024 07:21:13 +0200 Subject: [PATCH 70/87] Extend usage of PooledSpan for PE --- src/LibObjectFile/Collections/PooledSpan.cs | 31 ++++++-- src/LibObjectFile/ObjectFileReaderWriter.cs | 8 ++- .../DataDirectory/PEBoundImportDirectory.cs | 11 ++- .../PE/DataDirectory/PEDebugDirectory.cs | 70 ++++++++----------- .../DataDirectory/PEDebugSectionDataRSDS.cs | 56 +++++++-------- .../DataDirectory/PEDelayImportDirectory.cs | 49 ++++++------- .../PE/DataDirectory/PEExceptionDirectory.cs | 10 +-- .../PE/DataDirectory/PEExportAddressTable.cs | 64 ++++++++--------- .../PE/DataDirectory/PEExportNameTable.cs | 48 ++++++------- .../PE/DataDirectory/PEImportFunctionTable.cs | 30 ++++---- .../DataDirectory/PEResourceDirectoryEntry.cs | 21 +++--- src/LibObjectFile/PE/PEFile.Read.cs | 13 ++-- .../PE/PESectionDataExtensions.cs | 56 +++++---------- 13 files changed, 208 insertions(+), 259 deletions(-) diff --git a/src/LibObjectFile/Collections/PooledSpan.cs b/src/LibObjectFile/Collections/PooledSpan.cs index 54ef173..047985a 100644 --- a/src/LibObjectFile/Collections/PooledSpan.cs +++ b/src/LibObjectFile/Collections/PooledSpan.cs @@ -44,12 +44,14 @@ private PooledSpan(byte[] buffer, int size) /// /// The number of elements in the span. /// A new instance of the struct. - public static unsafe PooledSpan Create(int count) + public static unsafe PooledSpan Create(int count, out Span span) { ArgumentOutOfRangeException.ThrowIfLessThan(count, 0); var size = count * sizeof(T); var buffer = ArrayPool.Shared.Rent(size); - return new PooledSpan(buffer, size); + var pooledSpan = new PooledSpan(buffer, size); + span = pooledSpan.Span; + return pooledSpan; } /// @@ -58,11 +60,18 @@ public static unsafe PooledSpan Create(int count) /// The stack memory span. /// The number of elements in the span. /// A new instance of the struct. - public static unsafe PooledSpan Create(Span stackSpan, int count) + public static unsafe PooledSpan Create(Span stackSpan, int count, out Span span) { ArgumentOutOfRangeException.ThrowIfLessThan(count, 0); var size = count * sizeof(T); - return size <= stackSpan.Length ? new PooledSpan(stackSpan, size) : Create(count); + if (size <= stackSpan.Length) + { + var pooledSpan = new PooledSpan(stackSpan, size); + span = pooledSpan.Span; + return pooledSpan; + } + + return Create(count, out span); } /// @@ -73,7 +82,7 @@ public static unsafe PooledSpan Create(Span stackSpan, int count) /// /// Gets the span of elements as bytes. /// - public Span SpanAsBytes => MemoryMarshal.AsBytes(_span); + public Span AsBytes => MemoryMarshal.AsBytes(_span); /// /// Releases the underlying memory buffer if it was allocated on the heap. @@ -86,4 +95,16 @@ public void Dispose() ArrayPool.Shared.Return(buffer); } } + + /// + /// Gets the span of elements. + /// + /// The pooled span to convert. + public static implicit operator Span(PooledSpan pooledSpan) => pooledSpan.Span; + + /// + /// Gets the span of elements as a span of bytes. + /// + /// The pooled span to convert. + public static implicit operator Span(PooledSpan pooledSpan) => pooledSpan.AsBytes; } diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs index 8c9424d..01ec1ac 100644 --- a/src/LibObjectFile/ObjectFileReaderWriter.cs +++ b/src/LibObjectFile/ObjectFileReaderWriter.cs @@ -212,7 +212,13 @@ public void WriteZero(int count) /// /// The buffer to write public void Write(ReadOnlySpan buffer) => Stream.Write(buffer); - + + /// + /// Writes to the and current position from the specified buffer. + /// + /// The buffer to write + public void Write(Span buffer) => Stream.Write(buffer); + /// /// Writes an element of type to the stream. /// diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs index 7ce6601..a5b53cc 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs @@ -8,6 +8,7 @@ using System.Buffers; using System.Collections.Generic; using System.Runtime.InteropServices; +using LibObjectFile.Collections; namespace LibObjectFile.PE; @@ -78,15 +79,13 @@ public override unsafe void Read(PEImageReader reader) if (rawEntry.NumberOfModuleForwarderRefs > 0) { - var size = sizeof(RawPEBoundImportForwarderRef) * rawEntry.NumberOfModuleForwarderRefs; - var buffer = ArrayPool.Shared.Rent(size); - var span = buffer.AsSpan(0, size); - var spanForwarderRef = MemoryMarshal.Cast(span); + using var pooledSpan = PooledSpan.Create(rawEntry.NumberOfModuleForwarderRefs, out var spanForwarderRef); + var span = pooledSpan.AsBytes; read = reader.Read(span); - if (read != size) + if (read != span.Length) { - diagnostics.Error(DiagnosticId.PE_ERR_BoundImportDirectoryInvalidEndOfStream, $"Unable to read the full content of the Bound Import Directory. Expected {size} bytes, but read {read} bytes"); + diagnostics.Error(DiagnosticId.PE_ERR_BoundImportDirectoryInvalidEndOfStream, $"Unable to read the full content of the Bound Import Directory. Expected {span.Length} bytes, but read {read} bytes"); return; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs index d30d140..323f196 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; +using LibObjectFile.Collections; using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; @@ -27,11 +28,10 @@ public override unsafe void Read(PEImageReader reader) var entryCount = size / sizeof(RawImageDebugDirectory); - var buffer = ArrayPool.Shared.Rent(size); - try + // Scope the pooled span to ensure it is returned to the pool as soon as possible { - var span = buffer.AsSpan(0, size); - var entries = MemoryMarshal.Cast(span); + using var pooledSpan = PooledSpan.Create(entryCount, out var entries); + Span span = pooledSpan; reader.Position = Position; int read = reader.Read(span); @@ -97,10 +97,6 @@ public override unsafe void Read(PEImageReader reader) Entries.Add(entry); } } - finally - { - ArrayPool.Shared.Return(buffer); - } // Read the data associated with the debug directory entries foreach (var entry in Entries) @@ -133,48 +129,38 @@ internal override IEnumerable CollectImplicitSectionDataList() } } - public override unsafe void Write(PEImageWriter writer) + public override void Write(PEImageWriter writer) { var entries = CollectionsMarshal.AsSpan(Entries); - var rawBufferSize = sizeof(RawImageDebugDirectory) * entries.Length; - var rawBuffer = ArrayPool.Shared.Rent(rawBufferSize); - try + using var pooledSpan = PooledSpan.Create(entries.Length, out var rawEntries); + + RawImageDebugDirectory rawEntry = default; + for (var i = 0; i < entries.Length; i++) { - var buffer = new Span(rawBuffer, 0, rawBufferSize); - var rawEntries = MemoryMarshal.Cast(buffer); + var entry = entries[i]; + rawEntry.Characteristics = entry.Characteristics; + rawEntry.MajorVersion = entry.MajorVersion; + rawEntry.MinorVersion = entry.MinorVersion; + rawEntry.TimeDateStamp = entry.TimeDateStamp; + rawEntry.Type = entry.Type; - RawImageDebugDirectory rawEntry = default; - for (var i = 0; i < entries.Length; i++) + if (entry.SectionData is not null) { - 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; + 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; } - writer.Write(rawBuffer); - } - finally - { - ArrayPool.Shared.Return(rawBuffer); + rawEntries[i] = rawEntry; } + + writer.Write(pooledSpan); } protected override unsafe uint ComputeHeaderSize(PELayoutContext context) diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs index 7372f62..ff79edd 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.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. @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; +using LibObjectFile.Collections; using LibObjectFile.Diagnostics; namespace LibObjectFile.PE; @@ -44,42 +45,35 @@ public PEDebugSectionDataRSDS() public override unsafe void Read(PEImageReader reader) { - reader.Position = Position; var size = (int)Size; - var buffer = ArrayPool.Shared.Rent((int)Size); - var span = buffer.AsSpan(0, size); - try - { - var read = reader.Read(span); - if (read != span.Length) - { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unable to read PEDebugDataRSDS"); - return; - } - - var signature = MemoryMarshal.Read(span); - if (signature != 0x53445352) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidDebugDataRSDSSignature, $"Invalid signature for PEDebugDataRSDS"); - return; - } + using var pooledSpan = PooledSpan.Create(size, out var span); - var pdbPath = span.Slice(sizeof(uint) + sizeof(Guid) + sizeof(uint)); - var indexOfZero = pdbPath.IndexOf((byte)0); - if (indexOfZero < 0) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidDebugDataRSDSPdbPath, $"Invalid PDB path for PEDebugDataRSDS"); - return; - } + reader.Position = Position; + var read = reader.Read(span); + if (read != span.Length) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unable to read PEDebugDataRSDS"); + return; + } - Guid = MemoryMarshal.Read(span.Slice(4)); - Age = MemoryMarshal.Read(span.Slice(sizeof(uint) + sizeof(Guid))); - PdbPath = System.Text.Encoding.UTF8.GetString(pdbPath.Slice(0, indexOfZero)); + var signature = MemoryMarshal.Read(span); + if (signature != 0x53445352) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidDebugDataRSDSSignature, $"Invalid signature for PEDebugDataRSDS"); + return; } - finally + + var pdbPath = span.Slice(sizeof(uint) + sizeof(Guid) + sizeof(uint)); + var indexOfZero = pdbPath.IndexOf((byte)0); + if (indexOfZero < 0) { - ArrayPool.Shared.Return(buffer); + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidDebugDataRSDSPdbPath, $"Invalid PDB path for PEDebugDataRSDS"); + return; } + + Guid = MemoryMarshal.Read(span.Slice(4)); + Age = MemoryMarshal.Read(span.Slice(sizeof(uint) + sizeof(Guid))); + PdbPath = System.Text.Encoding.UTF8.GetString(pdbPath.Slice(0, indexOfZero)); } /// diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs index 9e407f8..0327d48 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs @@ -8,6 +8,7 @@ using System.Buffers; using System.Collections.Generic; using System.Runtime.InteropServices; +using LibObjectFile.Collections; namespace LibObjectFile.PE; @@ -216,37 +217,27 @@ private unsafe uint CalculateSize() public override unsafe void Write(PEImageWriter writer) { 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; + using var pooledSpan = PooledSpan.Create(entries.Length + 1, out var rawEntries); - writer.Write(rawBuffer); - } - finally + RawDelayLoadDescriptor rawEntry = default; + for (var i = 0; i < entries.Length; i++) { - ArrayPool.Shared.Return(rawBuffer); + 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(pooledSpan.AsBytes); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs index f248f82..250ddd7 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; +using LibObjectFile.Collections; using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; @@ -116,11 +117,8 @@ public override unsafe void Read(PEImageReader reader) reader.Position = Position; - - var buffer = ArrayPool.Shared.Rent((int)size); - try { - var span = buffer.AsSpan().Slice(0, (int)size); + using var pooledSpan = PooledSpan.Create((int)size, out var span); int read = reader.Read(span); if (read != size) { @@ -140,10 +138,6 @@ public override unsafe void Read(PEImageReader reader) break; } } - finally - { - ArrayPool.Shared.Return(buffer); - } var headerSize = ComputeHeaderSize(reader); Debug.Assert(headerSize == size); diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index 4b1e6a7..343c86d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Xml.Linq; +using LibObjectFile.Collections; using LibObjectFile.Diagnostics; using static System.Collections.Specialized.BitVector32; @@ -37,58 +38,51 @@ public override unsafe void UpdateLayout(PELayoutContext context) public override unsafe void Read(PEImageReader reader) { + using var pooledSpan = PooledSpan.Create(Values.Count, out var spanRva); + var span = pooledSpan.AsBytes; + reader.Position = Position; - var buffer = ArrayPool.Shared.Rent(sizeof(RVA) * Values.Count); - var span = buffer.AsSpan(0, sizeof(RVA) * Values.Count); - var spanRva = MemoryMarshal.Cast(span); - try + int read = reader.Read(span); + if (read != span.Length) + { + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unable to read Export Address Table"); + return; + } + + for (int i = 0; i < Values.Count; i++) { - int read = reader.Read(buffer.AsSpan(0, sizeof(RVA) * Values.Count)); - if (read != sizeof(RVA) * Values.Count) + var rva = spanRva[i]; + if (!reader.File.TryFindSection(rva, out var section)) { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unable to read Export Address Table"); + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); return; } - - for (int i = 0; i < Values.Count; i++) + + var found = section.TryFindSectionData(rva, out var sectionData); + + if (section.Name == PESectionName.EData) { - var rva = spanRva[i]; - if (!reader.File.TryFindSection(rva, out var section)) + var streamSectionData = sectionData as PEStreamSectionData; + if (streamSectionData is null) { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Invalid forwarder RVA {rva} for Export Address Table"); return; } - var found = section.TryFindSectionData(rva, out var sectionData); - - if (section.Name == PESectionName.EData) + Values[i] = new PEExportFunctionEntry(new PEAsciiStringLink(streamSectionData, rva - streamSectionData.RVA)); + } + else + { + if (found) { - var streamSectionData = sectionData as PEStreamSectionData; - if (streamSectionData is null) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Invalid forwarder RVA {rva} for Export Address Table"); - return; - } - - Values[i] = new PEExportFunctionEntry(new PEAsciiStringLink(streamSectionData, rva - streamSectionData.RVA)); + Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(sectionData, rva - sectionData!.RVA)); } else { - if (found) - { - Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(sectionData, rva - sectionData!.RVA)); - } - else - { - Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(section, rva - section.RVA)); - } + Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(section, rva - section.RVA)); } } } - finally - { - ArrayPool.Shared.Return(buffer); - } } public override void Write(PEImageWriter writer) diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs index 600bc21..dfd2692 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Collections.Generic; using System.Runtime.InteropServices; +using LibObjectFile.Collections; using LibObjectFile.Diagnostics; namespace LibObjectFile.PE; @@ -33,40 +34,33 @@ public override unsafe void UpdateLayout(PELayoutContext context) public override unsafe void Read(PEImageReader reader) { reader.Position = Position; - var buffer = ArrayPool.Shared.Rent(sizeof(RVA) * Values.Count); - var span = buffer.AsSpan(0, sizeof(RVA) * Values.Count); - var spanRva = MemoryMarshal.Cast(span); - try + using var pooledSpan = PooledSpan.Create(Values.Count, out var spanRva); + var span = pooledSpan.AsBytes; + + int read = reader.Read(span); + if (read != span.Length) { - int read = reader.Read(buffer.AsSpan(0, sizeof(RVA) * Values.Count)); - if (read != sizeof(RVA) * Values.Count) + reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unable to read Export Name Table"); + return; + } + + for (int i = 0; i < Values.Count; i++) + { + var rva = spanRva[i]; + if (!reader.File.TryFindContainerByRVA(rva, out var sectionData)) { - reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unable to read Export Name Table"); + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportNameTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); return; } - for (int i = 0; i < Values.Count; i++) + var streamSectionData = sectionData as PEStreamSectionData; + if (streamSectionData is null) { - var rva = spanRva[i]; - if (!reader.File.TryFindContainerByRVA(rva, out var sectionData)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportNameTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); - return; - } - - var streamSectionData = sectionData as PEStreamSectionData; - if (streamSectionData is null) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportNameTableInvalidRVA, $"The section data for RVA {rva} is not a stream section data"); - return; - } - - Values[i] = new PEAsciiStringLink(streamSectionData, rva - streamSectionData.RVA); + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportNameTableInvalidRVA, $"The section data for RVA {rva} is not a stream section data"); + return; } - } - finally - { - ArrayPool.Shared.Return(buffer); + + Values[i] = new PEAsciiStringLink(streamSectionData, rva - streamSectionData.RVA); } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index d0832e8..7101b20 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.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. @@ -6,6 +6,7 @@ using System.Buffers; using System.Collections.Generic; using System.Runtime.InteropServices; +using LibObjectFile.Collections; using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; @@ -133,26 +134,19 @@ public void Write(PEImageWriter writer) private unsafe void Write32(PEImageWriter writer) { - var buffer = ArrayPool.Shared.Rent(Entries.Count * sizeof(RawImportFunctionEntry32)); - try + using var pooledSpan = PooledSpan.Create(Entries.Count, out var span); + + for (var i = 0; i < Entries.Count; i++) { - 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.HintName.RVA(); - span[i] = new RawImportFunctionEntry32(entry.IsImportByOrdinal ? 0x8000_0000U | entry.Ordinal : va); - } + var entry = Entries[i]; + var va = entry.HintName.RVA(); + span[i] = new RawImportFunctionEntry32(entry.IsImportByOrdinal ? 0x8000_0000U | entry.Ordinal : va); + } - // Last entry is null terminator - span[^1] = default; + // Last entry is null terminator + span[^1] = default; - writer.Write(MemoryMarshal.AsBytes(span)); - } - finally - { - ArrayPool.Shared.Return(buffer); - } + writer.Write(pooledSpan); } private unsafe void Write64(PEImageWriter writer) diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs index 54ed5db..eb3b8f1 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; +using LibObjectFile.Collections; namespace LibObjectFile.PE; @@ -126,21 +127,15 @@ private void ReadEntry(PEImageReader reader, PEResourceDirectory directory, RawI { // 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 + using var pooledSpan = PooledSpan.Create(length, out var span); + + int readLength = reader.Read(span); + if (readLength != length) { - ArrayPool.Shared.Return(buffer); + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource directory string at position {reader.Position}"); + return; } + name = Encoding.Unicode.GetString(span); } else { diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index d4c34d4..2dc48bb 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -231,15 +231,14 @@ public override unsafe void Read(PEImageReader reader) } // Read Directory headers - using var pooledSpanDirectories = PooledSpan.Create(stackalloc byte[16 * sizeof(RawImageDataDirectory)], (int)OptionalHeader.NumberOfRvaAndSizes); - var directories = pooledSpanDirectories.Span; + using var pooledSpanDirectories = PooledSpan.Create(stackalloc byte[16 * sizeof(RawImageDataDirectory)], (int)OptionalHeader.NumberOfRvaAndSizes, out var rawDirectories); // Sets the number of entries in the data directory Directories.Count = (int)OptionalHeader.NumberOfRvaAndSizes; if (OptionalHeader.NumberOfRvaAndSizes > 0) { - var span = MemoryMarshal.AsBytes(directories); + var span = MemoryMarshal.AsBytes(rawDirectories); read = reader.Read(span); if (read != span.Length) { @@ -253,17 +252,17 @@ public override unsafe void Read(PEImageReader reader) Debug.Assert(Unsafe.SizeOf() == 40); - using var pooledSpanSections = PooledSpan.Create((int)CoffHeader.NumberOfSections); - read = reader.Read(pooledSpanSections.SpanAsBytes); + using var pooledSpanSections = PooledSpan.Create((int)CoffHeader.NumberOfSections, out var rawSections); + read = reader.Read(pooledSpanSections.AsBytes); - if (read != pooledSpanSections.SpanAsBytes.Length) + if (read != pooledSpanSections.AsBytes.Length) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidSectionHeadersSize, "Invalid section headers"); return; } // Read all sections and directories - ReadSectionsAndDirectories(reader, pooledSpanSections.Span, pooledSpanDirectories.Span); + ReadSectionsAndDirectories(reader, rawSections, rawDirectories); // Set the size to the full size of the file Size = reader.Length; diff --git a/src/LibObjectFile/PE/PESectionDataExtensions.cs b/src/LibObjectFile/PE/PESectionDataExtensions.cs index c4fbd25..6adcc36 100644 --- a/src/LibObjectFile/PE/PESectionDataExtensions.cs +++ b/src/LibObjectFile/PE/PESectionDataExtensions.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. @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; +using LibObjectFile.Collections; namespace LibObjectFile.PE; @@ -83,49 +84,30 @@ private static void WriteAsciiStringInternal(Stream stream, bool isHint, ushort { maxLength++; } - - if (maxLength > 256) + + using var pooledSpan = PooledSpan.Create(maxLength + (isHint ? 2 : 0), out var span); + + var text = span; + + if (isHint) { - var array = ArrayPool.Shared.Rent(maxLength + (isHint ? 2 : 0)); - try - { - WriteString(stream, array, isHint, hint, value); - } - finally - { - ArrayPool.Shared.Return(array); - } + MemoryMarshal.Write(span, hint); + text = span.Slice(2); } - else + + int actualLength = Encoding.ASCII.GetBytes(value, text); + text[actualLength] = 0; + if ((actualLength & 1) != 0) { - Span buffer = stackalloc byte[258]; - WriteString(stream, buffer, isHint, hint, value); + actualLength++; } - static void WriteString(Stream stream, Span buffer, bool isHint, ushort hint, string value) + if (isHint) { - var text = buffer; - - if (isHint) - { - MemoryMarshal.Write(buffer, hint); - text = buffer.Slice(2); - } - - int actualLength = Encoding.ASCII.GetBytes(value, text); - text[actualLength] = 0; - if ((actualLength & 1) != 0) - { - actualLength++; - } - - if (isHint) - { - actualLength += 2; - } - - stream.Write(buffer.Slice(0, actualLength)); + actualLength += 2; } + + stream.Write(span.Slice(0, actualLength)); } private static string ReadAsciiStringInternal(Stream stream, bool isHint, out ushort hint) From 6f97a431705b8c01484b070ccbe7f5faf93fb37e Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 29 Sep 2024 07:26:25 +0200 Subject: [PATCH 71/87] Renamed PooledSpan to TempSpan --- .../{PooledSpan.cs => TempSpan.cs} | 42 +++++++++---------- .../DataDirectory/PEBoundImportDirectory.cs | 4 +- .../PE/DataDirectory/PEDebugDirectory.cs | 8 ++-- .../DataDirectory/PEDebugSectionDataRSDS.cs | 2 +- .../DataDirectory/PEDelayImportDirectory.cs | 4 +- .../PE/DataDirectory/PEExceptionDirectory.cs | 2 +- .../PE/DataDirectory/PEExportAddressTable.cs | 4 +- .../PE/DataDirectory/PEExportNameTable.cs | 4 +- .../PE/DataDirectory/PEImportFunctionTable.cs | 4 +- .../DataDirectory/PEResourceDirectoryEntry.cs | 2 +- src/LibObjectFile/PE/PEFile.Read.cs | 4 +- .../PE/PESectionDataExtensions.cs | 2 +- 12 files changed, 41 insertions(+), 41 deletions(-) rename src/LibObjectFile/Collections/{PooledSpan.cs => TempSpan.cs} (61%) diff --git a/src/LibObjectFile/Collections/PooledSpan.cs b/src/LibObjectFile/Collections/TempSpan.cs similarity index 61% rename from src/LibObjectFile/Collections/PooledSpan.cs rename to src/LibObjectFile/Collections/TempSpan.cs index 047985a..95c483e 100644 --- a/src/LibObjectFile/Collections/PooledSpan.cs +++ b/src/LibObjectFile/Collections/TempSpan.cs @@ -12,63 +12,63 @@ namespace LibObjectFile.Collections; /// Represents a pooled span that can be created from a stack or heap memory. /// /// The type of the elements in the span. -public readonly ref struct PooledSpan where T : unmanaged +public readonly ref struct TempSpan where T : unmanaged { private readonly Span _span; private readonly byte[]? _buffer; /// - /// Initializes a new instance of the struct using a stack memory span. + /// Initializes a new instance of the struct using a stack memory span. /// /// The stack memory span. /// The size of the span in bytes. - private PooledSpan(Span span, int size) + private TempSpan(Span span, int size) { _span = MemoryMarshal.Cast(span.Slice(0, size)); _buffer = null; } /// - /// Initializes a new instance of the struct using a heap memory span. + /// Initializes a new instance of the struct using a heap memory span. /// /// The heap memory buffer. /// The size of the span in bytes. - private PooledSpan(byte[] buffer, int size) + private TempSpan(byte[] buffer, int size) { _span = MemoryMarshal.Cast(new(buffer, 0, size)); _buffer = buffer; } /// - /// Creates a new instance of the struct with the specified number of elements. + /// Creates a new instance of the struct with the specified number of elements. /// /// The number of elements in the span. - /// A new instance of the struct. - public static unsafe PooledSpan Create(int count, out Span span) + /// A new instance of the struct. + public static unsafe TempSpan Create(int count, out Span span) { ArgumentOutOfRangeException.ThrowIfLessThan(count, 0); var size = count * sizeof(T); var buffer = ArrayPool.Shared.Rent(size); - var pooledSpan = new PooledSpan(buffer, size); - span = pooledSpan.Span; - return pooledSpan; + var tempSpan = new TempSpan(buffer, size); + span = tempSpan.Span; + return tempSpan; } /// - /// Creates a new instance of the struct with the specified number of elements, using a stack memory span if possible. + /// Creates a new instance of the struct with the specified number of elements, using a stack memory span if possible. /// /// The stack memory span. /// The number of elements in the span. - /// A new instance of the struct. - public static unsafe PooledSpan Create(Span stackSpan, int count, out Span span) + /// A new instance of the struct. + public static unsafe TempSpan Create(Span stackSpan, int count, out Span span) { ArgumentOutOfRangeException.ThrowIfLessThan(count, 0); var size = count * sizeof(T); if (size <= stackSpan.Length) { - var pooledSpan = new PooledSpan(stackSpan, size); - span = pooledSpan.Span; - return pooledSpan; + var tempSpan = new TempSpan(stackSpan, size); + span = tempSpan.Span; + return tempSpan; } return Create(count, out span); @@ -99,12 +99,12 @@ public void Dispose() /// /// Gets the span of elements. /// - /// The pooled span to convert. - public static implicit operator Span(PooledSpan pooledSpan) => pooledSpan.Span; + /// The pooled span to convert. + public static implicit operator Span(TempSpan tempSpan) => tempSpan.Span; /// /// Gets the span of elements as a span of bytes. /// - /// The pooled span to convert. - public static implicit operator Span(PooledSpan pooledSpan) => pooledSpan.AsBytes; + /// The pooled span to convert. + public static implicit operator Span(TempSpan tempSpan) => tempSpan.AsBytes; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs index a5b53cc..478066b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs @@ -79,8 +79,8 @@ public override unsafe void Read(PEImageReader reader) if (rawEntry.NumberOfModuleForwarderRefs > 0) { - using var pooledSpan = PooledSpan.Create(rawEntry.NumberOfModuleForwarderRefs, out var spanForwarderRef); - var span = pooledSpan.AsBytes; + using var tempSpan = TempSpan.Create(rawEntry.NumberOfModuleForwarderRefs, out var spanForwarderRef); + var span = tempSpan.AsBytes; read = reader.Read(span); if (read != span.Length) diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs index 323f196..48740ad 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs @@ -30,8 +30,8 @@ public override unsafe void Read(PEImageReader reader) // Scope the pooled span to ensure it is returned to the pool as soon as possible { - using var pooledSpan = PooledSpan.Create(entryCount, out var entries); - Span span = pooledSpan; + using var tempSpan = TempSpan.Create(entryCount, out var entries); + Span span = tempSpan; reader.Position = Position; int read = reader.Read(span); @@ -132,7 +132,7 @@ internal override IEnumerable CollectImplicitSectionDataList() public override void Write(PEImageWriter writer) { var entries = CollectionsMarshal.AsSpan(Entries); - using var pooledSpan = PooledSpan.Create(entries.Length, out var rawEntries); + using var tempSpan = TempSpan.Create(entries.Length, out var rawEntries); RawImageDebugDirectory rawEntry = default; for (var i = 0; i < entries.Length; i++) @@ -160,7 +160,7 @@ public override void Write(PEImageWriter writer) rawEntries[i] = rawEntry; } - writer.Write(pooledSpan); + writer.Write(tempSpan); } protected override unsafe uint ComputeHeaderSize(PELayoutContext context) diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs index ff79edd..899c3d2 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs @@ -46,7 +46,7 @@ public PEDebugSectionDataRSDS() public override unsafe void Read(PEImageReader reader) { var size = (int)Size; - using var pooledSpan = PooledSpan.Create(size, out var span); + using var tempSpan = TempSpan.Create(size, out var span); reader.Position = Position; var read = reader.Read(span); diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs index 0327d48..62b6e49 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs @@ -217,7 +217,7 @@ private unsafe uint CalculateSize() public override unsafe void Write(PEImageWriter writer) { var entries = CollectionsMarshal.AsSpan(Entries); - using var pooledSpan = PooledSpan.Create(entries.Length + 1, out var rawEntries); + using var tempSpan = TempSpan.Create(entries.Length + 1, out var rawEntries); RawDelayLoadDescriptor rawEntry = default; for (var i = 0; i < entries.Length; i++) @@ -238,6 +238,6 @@ public override unsafe void Write(PEImageWriter writer) // Write the null entry rawEntries[entries.Length] = default; - writer.Write(pooledSpan.AsBytes); + writer.Write(tempSpan.AsBytes); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs index 250ddd7..06b51ea 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs @@ -118,7 +118,7 @@ public override unsafe void Read(PEImageReader reader) reader.Position = Position; { - using var pooledSpan = PooledSpan.Create((int)size, out var span); + using var tempSpan = TempSpan.Create((int)size, out var span); int read = reader.Read(span); if (read != size) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index 343c86d..71c0c08 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -38,8 +38,8 @@ public override unsafe void UpdateLayout(PELayoutContext context) public override unsafe void Read(PEImageReader reader) { - using var pooledSpan = PooledSpan.Create(Values.Count, out var spanRva); - var span = pooledSpan.AsBytes; + using var tempSpan = TempSpan.Create(Values.Count, out var spanRva); + var span = tempSpan.AsBytes; reader.Position = Position; int read = reader.Read(span); diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs index dfd2692..b2ee841 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs @@ -34,8 +34,8 @@ public override unsafe void UpdateLayout(PELayoutContext context) public override unsafe void Read(PEImageReader reader) { reader.Position = Position; - using var pooledSpan = PooledSpan.Create(Values.Count, out var spanRva); - var span = pooledSpan.AsBytes; + using var tempSpan = TempSpan.Create(Values.Count, out var spanRva); + var span = tempSpan.AsBytes; int read = reader.Read(span); if (read != span.Length) diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index 7101b20..ac608c0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -134,7 +134,7 @@ public void Write(PEImageWriter writer) private unsafe void Write32(PEImageWriter writer) { - using var pooledSpan = PooledSpan.Create(Entries.Count, out var span); + using var tempSpan = TempSpan.Create(Entries.Count, out var span); for (var i = 0; i < Entries.Count; i++) { @@ -146,7 +146,7 @@ private unsafe void Write32(PEImageWriter writer) // Last entry is null terminator span[^1] = default; - writer.Write(pooledSpan); + writer.Write(tempSpan); } private unsafe void Write64(PEImageWriter writer) diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs index eb3b8f1..3ac5faa 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs @@ -127,7 +127,7 @@ private void ReadEntry(PEImageReader reader, PEResourceDirectory directory, RawI { // Read the string var length = reader.ReadU16() * 2; - using var pooledSpan = PooledSpan.Create(length, out var span); + using var tempSpan = TempSpan.Create(length, out var span); int readLength = reader.Read(span); if (readLength != length) diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 2dc48bb..a6baff9 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -231,7 +231,7 @@ public override unsafe void Read(PEImageReader reader) } // Read Directory headers - using var pooledSpanDirectories = PooledSpan.Create(stackalloc byte[16 * sizeof(RawImageDataDirectory)], (int)OptionalHeader.NumberOfRvaAndSizes, out var rawDirectories); + using var pooledSpanDirectories = TempSpan.Create(stackalloc byte[16 * sizeof(RawImageDataDirectory)], (int)OptionalHeader.NumberOfRvaAndSizes, out var rawDirectories); // Sets the number of entries in the data directory Directories.Count = (int)OptionalHeader.NumberOfRvaAndSizes; @@ -252,7 +252,7 @@ public override unsafe void Read(PEImageReader reader) Debug.Assert(Unsafe.SizeOf() == 40); - using var pooledSpanSections = PooledSpan.Create((int)CoffHeader.NumberOfSections, out var rawSections); + using var pooledSpanSections = TempSpan.Create((int)CoffHeader.NumberOfSections, out var rawSections); read = reader.Read(pooledSpanSections.AsBytes); if (read != pooledSpanSections.AsBytes.Length) diff --git a/src/LibObjectFile/PE/PESectionDataExtensions.cs b/src/LibObjectFile/PE/PESectionDataExtensions.cs index 6adcc36..dcce1d8 100644 --- a/src/LibObjectFile/PE/PESectionDataExtensions.cs +++ b/src/LibObjectFile/PE/PESectionDataExtensions.cs @@ -85,7 +85,7 @@ private static void WriteAsciiStringInternal(Stream stream, bool isHint, ushort maxLength++; } - using var pooledSpan = PooledSpan.Create(maxLength + (isHint ? 2 : 0), out var span); + using var tempSpan = TempSpan.Create(maxLength + (isHint ? 2 : 0), out var span); var text = span; From ba62b5f8c8790c08d9a04c65f71b45f9edaf8d56 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 29 Sep 2024 07:53:35 +0200 Subject: [PATCH 72/87] Rename ImageXXXHeader to PEXXXHeader --- .../RawImageOptionalHeaderCommonPart1.cs | 4 +- .../{ImageCoffHeader.cs => PECoffHeader.cs} | 2 +- .../PE/{ImageDosHeader.cs => PEDosHeader.cs} | 4 +- .../PE/{ImageDosMagic.cs => PEDosMagic.cs} | 6 +- src/LibObjectFile/PE/PEFile.Read.cs | 40 +++---- src/LibObjectFile/PE/PEFile.Write.cs | 8 +- src/LibObjectFile/PE/PEFile.cs | 26 ++-- ...eOptionalHeader.cs => PEOptionalHeader.cs} | 4 +- ...eaderMagic.cs => PEOptionalHeaderMagic.cs} | 4 +- src/LibObjectFile/PE/PEPrinter.cs | 112 +++++++++--------- .../{ImagePESignature.cs => PESignature.cs} | 4 +- 11 files changed, 107 insertions(+), 107 deletions(-) rename src/LibObjectFile/PE/{ImageCoffHeader.cs => PECoffHeader.cs} (98%) rename src/LibObjectFile/PE/{ImageDosHeader.cs => PEDosHeader.cs} (97%) rename src/LibObjectFile/PE/{ImageDosMagic.cs => PEDosMagic.cs} (76%) rename src/LibObjectFile/PE/{ImageOptionalHeader.cs => PEOptionalHeader.cs} (99%) rename src/LibObjectFile/PE/{ImageOptionalHeaderMagic.cs => PEOptionalHeaderMagic.cs} (74%) rename src/LibObjectFile/PE/{ImagePESignature.cs => PESignature.cs} (76%) diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.cs index 87df1da..a4b8a19 100644 --- a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.cs +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.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. @@ -17,7 +17,7 @@ internal struct RawImageOptionalHeaderCommonPart1 /// /// The magic number, which identifies the file format. Expected to be 0x10b for PE32. /// - public ImageOptionalHeaderMagic Magic; + public PEOptionalHeaderMagic Magic; /// /// The major version number of the linker. diff --git a/src/LibObjectFile/PE/ImageCoffHeader.cs b/src/LibObjectFile/PE/PECoffHeader.cs similarity index 98% rename from src/LibObjectFile/PE/ImageCoffHeader.cs rename to src/LibObjectFile/PE/PECoffHeader.cs index d1f1575..cb727f7 100644 --- a/src/LibObjectFile/PE/ImageCoffHeader.cs +++ b/src/LibObjectFile/PE/PECoffHeader.cs @@ -12,7 +12,7 @@ namespace LibObjectFile.PE; /// Represents the COFF (Common Object File Format) header in a Portable Executable (PE) file. /// [StructLayout(LayoutKind.Sequential, Pack = 4)] -public struct ImageCoffHeader +public struct PECoffHeader { /// /// The machine type that the file is intended for. diff --git a/src/LibObjectFile/PE/ImageDosHeader.cs b/src/LibObjectFile/PE/PEDosHeader.cs similarity index 97% rename from src/LibObjectFile/PE/ImageDosHeader.cs rename to src/LibObjectFile/PE/PEDosHeader.cs index 69ad31c..7845223 100644 --- a/src/LibObjectFile/PE/ImageDosHeader.cs +++ b/src/LibObjectFile/PE/PEDosHeader.cs @@ -11,12 +11,12 @@ namespace LibObjectFile.PE; /// Represents the DOS header of a PE file. /// [StructLayout(LayoutKind.Sequential, Pack = 2)] -public unsafe struct ImageDosHeader +public unsafe struct PEDosHeader { /// /// Magic number. (Original DOS field is `e_magic`) /// - public ImageDosMagic Magic; + public PEDosMagic Magic; /// /// Bytes on last page of file. (Original DOS field is `e_cblp`) diff --git a/src/LibObjectFile/PE/ImageDosMagic.cs b/src/LibObjectFile/PE/PEDosMagic.cs similarity index 76% rename from src/LibObjectFile/PE/ImageDosMagic.cs rename to src/LibObjectFile/PE/PEDosMagic.cs index 7801426..cefff80 100644 --- a/src/LibObjectFile/PE/ImageDosMagic.cs +++ b/src/LibObjectFile/PE/PEDosMagic.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. @@ -6,9 +6,9 @@ namespace LibObjectFile.PE; #pragma warning disable CS0649 /// -/// Magic number for the > +/// Magic number for the > /// -public enum ImageDosMagic : ushort +public enum PEDosMagic : ushort { /// /// MZ - DOS executable file signature. diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index a6baff9..618dba1 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -59,17 +59,17 @@ public static bool TryRead(Stream stream, [NotNullWhen(true)] out PEFile? peFile public override unsafe void Read(PEImageReader reader) { - Debug.Assert(Unsafe.SizeOf() == 64); + Debug.Assert(Unsafe.SizeOf() == 64); var diagnostics = reader.Diagnostics; int read = reader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref DosHeader, 1))); - if (read != Unsafe.SizeOf()) + if (read != Unsafe.SizeOf()) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosHeaderSize, "Invalid DOS header"); return; } - if (DosHeader.Magic != ImageDosMagic.DOS) + if (DosHeader.Magic != PEDosMagic.DOS) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosHeaderMagic, "Invalid DOS header"); return; @@ -77,7 +77,7 @@ public override unsafe void Read(PEImageReader reader) var pePosition = DosHeader.FileAddressPEHeader; - if (pePosition < sizeof(ImageDosHeader)) + if (pePosition < sizeof(PEDosHeader)) { if (pePosition < 4) { @@ -85,14 +85,14 @@ public override unsafe void Read(PEImageReader reader) return; } - _unsafeNegativePEHeaderOffset = (int)pePosition - sizeof(ImageDosHeader); + _unsafeNegativePEHeaderOffset = (int)pePosition - sizeof(PEDosHeader); } else { // Read the DOS stub var dosStubSize = DosHeader.SizeOfParagraphsHeader * 16; - if (dosStubSize + sizeof(ImageDosHeader) > pePosition) + if (dosStubSize + sizeof(PEDosHeader) > pePosition) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosStubSize, $"Invalid DOS stub size {dosStubSize} going beyond the PE header"); return; @@ -115,7 +115,7 @@ public override unsafe void Read(PEImageReader reader) _dosStub = []; } - var dosHeaderAndStubSize = sizeof(ImageDosHeader) + dosStubSize; + var dosHeaderAndStubSize = sizeof(PEDosHeader) + dosStubSize; // Read any DOS stub extra data (e.g Rich) if (dosHeaderAndStubSize < DosHeader.FileAddressPEHeader) @@ -127,25 +127,25 @@ public override unsafe void Read(PEImageReader reader) // Read the PE header reader.Position = DosHeader.FileAddressPEHeader; - var signature = default(ImagePESignature); + var signature = default(PESignature); read = reader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref signature, 1))); - if (read != sizeof(ImagePESignature)) + if (read != sizeof(PESignature)) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidPESignature, "Invalid PE signature"); return; } - if (signature != ImagePESignature.PE) + if (signature != PESignature.PE) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidPESignature, $"Invalid PE signature 0x{(uint)signature:X8}"); return; } // Read the COFF header - Debug.Assert(Unsafe.SizeOf() == 20); - var coffHeader = default(ImageCoffHeader); + Debug.Assert(Unsafe.SizeOf() == 20); + var coffHeader = default(PECoffHeader); read = reader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref coffHeader, 1))); - if (read != Unsafe.SizeOf()) + if (read != Unsafe.SizeOf()) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidCoffHeaderSize, "Invalid COFF header"); return; @@ -155,22 +155,22 @@ public override unsafe void Read(PEImageReader reader) var positionAfterCoffHeader = reader.Position; // Cannot be smaller than the magic - if (CoffHeader.SizeOfOptionalHeader < sizeof(ImageOptionalHeaderMagic)) + if (CoffHeader.SizeOfOptionalHeader < sizeof(PEOptionalHeaderMagic)) { diagnostics.Error(DiagnosticId.PE_ERR_InvalidOptionalHeaderSize, $"Invalid optional header size {CoffHeader.SizeOfOptionalHeader}"); return; } - var magic = (ImageOptionalHeaderMagic)reader.ReadU16(); + var magic = (PEOptionalHeaderMagic)reader.ReadU16(); int minimumSizeOfOptionalHeaderToRead = 0; // Process known PE32/PE32+ headers - if (magic == ImageOptionalHeaderMagic.PE32) + if (magic == PEOptionalHeaderMagic.PE32) { minimumSizeOfOptionalHeaderToRead = RawImageOptionalHeader32.MinimumSize; } - else if (magic == ImageOptionalHeaderMagic.PE32Plus) + else if (magic == PEOptionalHeaderMagic.PE32Plus) { minimumSizeOfOptionalHeaderToRead = RawImageOptionalHeader64.MinimumSize; } @@ -184,17 +184,17 @@ public override unsafe void Read(PEImageReader reader) MemoryMarshal.Write(optionalHeader, (ushort)magic); // We have already read the magic number - var minimumSpan = optionalHeader.Slice(sizeof(ImageOptionalHeaderMagic)); + var minimumSpan = optionalHeader.Slice(sizeof(PEOptionalHeaderMagic)); read = reader.Read(minimumSpan); // The real size read (in case of tricked Tiny PE) - optionalHeader = optionalHeader.Slice(0, read + sizeof(ImageOptionalHeaderMagic)); + optionalHeader = optionalHeader.Slice(0, read + sizeof(PEOptionalHeaderMagic)); Debug.Assert(Unsafe.SizeOf() == 224); Debug.Assert(Unsafe.SizeOf() == 240); // Process known PE32/PE32+ headers - if (magic == ImageOptionalHeaderMagic.PE32) + if (magic == PEOptionalHeaderMagic.PE32) { var optionalHeader32 = new RawImageOptionalHeader32(); var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref optionalHeader32, 1)); diff --git a/src/LibObjectFile/PE/PEFile.Write.cs b/src/LibObjectFile/PE/PEFile.Write.cs index 8fa31ef..baf029f 100644 --- a/src/LibObjectFile/PE/PEFile.Write.cs +++ b/src/LibObjectFile/PE/PEFile.Write.cs @@ -68,7 +68,7 @@ public override unsafe void Write(PEImageWriter writer) // Update DOS header writer.Write(DosHeader); - position += (uint)sizeof(ImageDosHeader); + position += (uint)sizeof(PEDosHeader); // Write DOS stub writer.Write(_dosStub); @@ -86,12 +86,12 @@ public override unsafe void Write(PEImageWriter writer) position += (uint)zeroSize; // PE00 header - writer.Write(ImagePESignature.PE); - position += sizeof(ImagePESignature); // PE00 header + writer.Write(PESignature.PE); + position += sizeof(PESignature); // PE00 header // COFF header writer.Write(CoffHeader); - position += (uint)sizeof(ImageCoffHeader); + position += (uint)sizeof(PECoffHeader); if (IsPE32) diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index cb6f26c..f6f1982 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -31,7 +31,7 @@ public sealed partial class PEFile : PEObjectBase /// /// Initializes a new instance of the class. /// - public PEFile(ImageOptionalHeaderMagic magic = ImageOptionalHeaderMagic.PE32Plus) + public PEFile(PEOptionalHeaderMagic magic = PEOptionalHeaderMagic.PE32Plus) { _sections = new(this); ExtraDataBeforeSections = new(this); @@ -55,7 +55,7 @@ internal PEFile(bool unused) /// /// Gets the DOS header. /// - public ImageDosHeader DosHeader; + public PEDosHeader DosHeader; /// /// Gets or sets an unsafe negative offset relative to the end of the DOS header. @@ -67,9 +67,9 @@ public unsafe int UnsafeNegativePEHeaderOffset { // Should support Tiny PE layout http://www.phreedom.org/research/tinype/ // Value must be >= sizeof(ImageDosHeader) - 4 and <= 0 - if (value < sizeof(ImageDosHeader) - 4 || value > 0) + if (value < sizeof(PEDosHeader) - 4 || value > 0) { - throw new ArgumentOutOfRangeException(nameof(value), $"PEHeaderOffset must be greater than {sizeof(ImageDosHeader)}"); + throw new ArgumentOutOfRangeException(nameof(value), $"PEHeaderOffset must be greater than {sizeof(PEDosHeader)}"); } if (value < 0 && (_dosStub.Length != 0 || (DosStubExtra is not null && DosStubExtra.Length > 0))) @@ -120,22 +120,22 @@ public Stream? DosStubExtra /// /// Gets the COFF header. /// - public ImageCoffHeader CoffHeader; + public PECoffHeader CoffHeader; /// /// Gets the optional header. /// - public ImageOptionalHeader OptionalHeader; + public PEOptionalHeader OptionalHeader; /// /// Gets a boolean indicating whether this instance is a PE32 image. /// - public bool IsPE32 => OptionalHeader.Magic == ImageOptionalHeaderMagic.PE32; + public bool IsPE32 => OptionalHeader.Magic == PEOptionalHeaderMagic.PE32; /// /// Gets a boolean indicating whether this instance is a PE32+ image. /// - public bool IsPE32Plus => OptionalHeader.Magic == ImageOptionalHeaderMagic.PE32Plus; + public bool IsPE32Plus => OptionalHeader.Magic == PEOptionalHeaderMagic.PE32Plus; /// /// Gets the directories. @@ -314,7 +314,7 @@ public override unsafe void UpdateLayout(PELayoutContext context) var position = 0U; // Update DOS header - position += (uint)sizeof(ImageDosHeader); + position += (uint)sizeof(PEDosHeader); position += (uint)_dosStub.Length; position += (uint)(_dosStubExtra?.Length ?? 0U); @@ -324,10 +324,10 @@ public override unsafe void UpdateLayout(PELayoutContext context) // Update offset to PE header DosHeader._FileAddressPEHeader = position; - position += sizeof(ImagePESignature); // PE00 header + position += sizeof(PESignature); // PE00 header // COFF header - position += (uint)sizeof(ImageCoffHeader); + position += (uint)sizeof(PECoffHeader); // TODO: update other DosHeader fields @@ -452,10 +452,10 @@ protected override bool PrintMembers(StringBuilder builder) switch (OptionalHeader.Magic) { - case ImageOptionalHeaderMagic.PE32: + case PEOptionalHeaderMagic.PE32: builder.Append("PE32"); break; - case ImageOptionalHeaderMagic.PE32Plus: + case PEOptionalHeaderMagic.PE32Plus: builder.Append("PE32+"); break; } diff --git a/src/LibObjectFile/PE/ImageOptionalHeader.cs b/src/LibObjectFile/PE/PEOptionalHeader.cs similarity index 99% rename from src/LibObjectFile/PE/ImageOptionalHeader.cs rename to src/LibObjectFile/PE/PEOptionalHeader.cs index 2df3eb2..99b73e5 100644 --- a/src/LibObjectFile/PE/ImageOptionalHeader.cs +++ b/src/LibObjectFile/PE/PEOptionalHeader.cs @@ -9,7 +9,7 @@ namespace LibObjectFile.PE; -public struct ImageOptionalHeader +public struct PEOptionalHeader { internal RawImageOptionalHeaderCommonPart1 OptionalHeaderCommonPart1; internal RawImageOptionalHeaderBase32 OptionalHeaderBase32; @@ -25,7 +25,7 @@ public struct ImageOptionalHeader /// /// This value cannot be changed and must be set at construction time. /// - public ImageOptionalHeaderMagic Magic + public PEOptionalHeaderMagic Magic { get => OptionalHeaderCommonPart1.Magic; } diff --git a/src/LibObjectFile/PE/ImageOptionalHeaderMagic.cs b/src/LibObjectFile/PE/PEOptionalHeaderMagic.cs similarity index 74% rename from src/LibObjectFile/PE/ImageOptionalHeaderMagic.cs rename to src/LibObjectFile/PE/PEOptionalHeaderMagic.cs index 2a63dfd..d64f749 100644 --- a/src/LibObjectFile/PE/ImageOptionalHeaderMagic.cs +++ b/src/LibObjectFile/PE/PEOptionalHeaderMagic.cs @@ -1,10 +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. namespace LibObjectFile.PE; -public enum ImageOptionalHeaderMagic : ushort +public enum PEOptionalHeaderMagic : ushort { /// /// PE32 diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index 871e43e..e7e1cb4 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -37,26 +37,26 @@ private static unsafe void PrintDosHeader(PEFile file, ref TextWriterIndenter wr writer.WriteLine("DOS Header"); writer.Indent(); { - writer.WriteLine($"{nameof(ImageDosHeader.Magic),indent} = {file.DosHeader.Magic}"); - writer.WriteLine($"{nameof(ImageDosHeader.ByteCountOnLastPage),indent} = 0x{file.DosHeader.ByteCountOnLastPage:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.PageCount),indent} = 0x{file.DosHeader.PageCount:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.RelocationCount),indent} = 0x{file.DosHeader.RelocationCount:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.SizeOfParagraphsHeader),indent} = 0x{file.DosHeader.SizeOfParagraphsHeader:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.MinExtraParagraphs),indent} = 0x{file.DosHeader.MinExtraParagraphs:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.MaxExtraParagraphs),indent} = 0x{file.DosHeader.MaxExtraParagraphs:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.InitialSSValue),indent} = 0x{file.DosHeader.InitialSSValue:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.InitialSPValue),indent} = 0x{file.DosHeader.InitialSPValue:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.Checksum),indent} = 0x{file.DosHeader.Checksum:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.InitialIPValue),indent} = 0x{file.DosHeader.InitialIPValue:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.InitialCSValue),indent} = 0x{file.DosHeader.InitialCSValue:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.FileAddressRelocationTable),indent} = 0x{file.DosHeader.FileAddressRelocationTable:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.OverlayNumber),indent} = 0x{file.DosHeader.OverlayNumber:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.Reserved),indent} = 0x{file.DosHeader.Reserved[0]:X}, 0x{file.DosHeader.Reserved[1]:X}, 0x{file.DosHeader.Reserved[2]:X}, 0x{file.DosHeader.Reserved[3]:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.OEMIdentifier),indent} = 0x{file.DosHeader.OEMIdentifier:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.OEMInformation),indent} = 0x{file.DosHeader.OEMInformation:X}"); + writer.WriteLine($"{nameof(PEDosHeader.Magic),indent} = {file.DosHeader.Magic}"); + writer.WriteLine($"{nameof(PEDosHeader.ByteCountOnLastPage),indent} = 0x{file.DosHeader.ByteCountOnLastPage:X}"); + writer.WriteLine($"{nameof(PEDosHeader.PageCount),indent} = 0x{file.DosHeader.PageCount:X}"); + writer.WriteLine($"{nameof(PEDosHeader.RelocationCount),indent} = 0x{file.DosHeader.RelocationCount:X}"); + writer.WriteLine($"{nameof(PEDosHeader.SizeOfParagraphsHeader),indent} = 0x{file.DosHeader.SizeOfParagraphsHeader:X}"); + writer.WriteLine($"{nameof(PEDosHeader.MinExtraParagraphs),indent} = 0x{file.DosHeader.MinExtraParagraphs:X}"); + writer.WriteLine($"{nameof(PEDosHeader.MaxExtraParagraphs),indent} = 0x{file.DosHeader.MaxExtraParagraphs:X}"); + writer.WriteLine($"{nameof(PEDosHeader.InitialSSValue),indent} = 0x{file.DosHeader.InitialSSValue:X}"); + writer.WriteLine($"{nameof(PEDosHeader.InitialSPValue),indent} = 0x{file.DosHeader.InitialSPValue:X}"); + writer.WriteLine($"{nameof(PEDosHeader.Checksum),indent} = 0x{file.DosHeader.Checksum:X}"); + writer.WriteLine($"{nameof(PEDosHeader.InitialIPValue),indent} = 0x{file.DosHeader.InitialIPValue:X}"); + writer.WriteLine($"{nameof(PEDosHeader.InitialCSValue),indent} = 0x{file.DosHeader.InitialCSValue:X}"); + writer.WriteLine($"{nameof(PEDosHeader.FileAddressRelocationTable),indent} = 0x{file.DosHeader.FileAddressRelocationTable:X}"); + writer.WriteLine($"{nameof(PEDosHeader.OverlayNumber),indent} = 0x{file.DosHeader.OverlayNumber:X}"); + writer.WriteLine($"{nameof(PEDosHeader.Reserved),indent} = 0x{file.DosHeader.Reserved[0]:X}, 0x{file.DosHeader.Reserved[1]:X}, 0x{file.DosHeader.Reserved[2]:X}, 0x{file.DosHeader.Reserved[3]:X}"); + writer.WriteLine($"{nameof(PEDosHeader.OEMIdentifier),indent} = 0x{file.DosHeader.OEMIdentifier:X}"); + writer.WriteLine($"{nameof(PEDosHeader.OEMInformation),indent} = 0x{file.DosHeader.OEMInformation:X}"); writer.WriteLine( - $"{nameof(ImageDosHeader.Reserved2),indent} = 0x{file.DosHeader.Reserved2[0]:X}, 0x{file.DosHeader.Reserved2[1]:X}, 0x{file.DosHeader.Reserved2[2]:X}, 0x{file.DosHeader.Reserved2[3]:X}, 0x{file.DosHeader.Reserved2[4]:X}, 0x{file.DosHeader.Reserved2[5]:X}, 0x{file.DosHeader.Reserved2[6]:X}, 0x{file.DosHeader.Reserved2[7]:X}, 0x{file.DosHeader.Reserved2[8]:X}, 0x{file.DosHeader.Reserved2[9]:X}"); - writer.WriteLine($"{nameof(ImageDosHeader.FileAddressPEHeader),indent} = 0x{file.DosHeader.FileAddressPEHeader:X}"); + $"{nameof(PEDosHeader.Reserved2),indent} = 0x{file.DosHeader.Reserved2[0]:X}, 0x{file.DosHeader.Reserved2[1]:X}, 0x{file.DosHeader.Reserved2[2]:X}, 0x{file.DosHeader.Reserved2[3]:X}, 0x{file.DosHeader.Reserved2[4]:X}, 0x{file.DosHeader.Reserved2[5]:X}, 0x{file.DosHeader.Reserved2[6]:X}, 0x{file.DosHeader.Reserved2[7]:X}, 0x{file.DosHeader.Reserved2[8]:X}, 0x{file.DosHeader.Reserved2[9]:X}"); + writer.WriteLine($"{nameof(PEDosHeader.FileAddressPEHeader),indent} = 0x{file.DosHeader.FileAddressPEHeader:X}"); } writer.Unindent(); writer.WriteLine(); @@ -81,13 +81,13 @@ private static void PrintCoffHeader(PEFile file, ref TextWriterIndenter writer) writer.WriteLine("COFF Header"); writer.Indent(); { - writer.WriteLine($"{nameof(ImageCoffHeader.Machine),indent} = {file.CoffHeader.Machine}"); - writer.WriteLine($"{nameof(ImageCoffHeader.NumberOfSections),indent} = {file.CoffHeader.NumberOfSections}"); - writer.WriteLine($"{nameof(ImageCoffHeader.TimeDateStamp),indent} = {file.CoffHeader.TimeDateStamp}"); - writer.WriteLine($"{nameof(ImageCoffHeader.PointerToSymbolTable),indent} = 0x{file.CoffHeader.PointerToSymbolTable:X}"); - writer.WriteLine($"{nameof(ImageCoffHeader.NumberOfSymbols),indent} = {file.CoffHeader.NumberOfSymbols}"); - writer.WriteLine($"{nameof(ImageCoffHeader.SizeOfOptionalHeader),indent} = {file.CoffHeader.SizeOfOptionalHeader}"); - writer.WriteLine($"{nameof(ImageCoffHeader.Characteristics),indent} = {file.CoffHeader.Characteristics}"); + writer.WriteLine($"{nameof(PECoffHeader.Machine),indent} = {file.CoffHeader.Machine}"); + writer.WriteLine($"{nameof(PECoffHeader.NumberOfSections),indent} = {file.CoffHeader.NumberOfSections}"); + writer.WriteLine($"{nameof(PECoffHeader.TimeDateStamp),indent} = {file.CoffHeader.TimeDateStamp}"); + writer.WriteLine($"{nameof(PECoffHeader.PointerToSymbolTable),indent} = 0x{file.CoffHeader.PointerToSymbolTable:X}"); + writer.WriteLine($"{nameof(PECoffHeader.NumberOfSymbols),indent} = {file.CoffHeader.NumberOfSymbols}"); + writer.WriteLine($"{nameof(PECoffHeader.SizeOfOptionalHeader),indent} = {file.CoffHeader.SizeOfOptionalHeader}"); + writer.WriteLine($"{nameof(PECoffHeader.Characteristics),indent} = {file.CoffHeader.Characteristics}"); } writer.Unindent(); writer.WriteLine(); @@ -99,36 +99,36 @@ private static void PrintOptionalHeader(PEFile file, ref TextWriterIndenter writ writer.WriteLine("Optional Header"); writer.Indent(); { - writer.WriteLine($"{nameof(ImageOptionalHeader.Magic),indent} = {file.OptionalHeader.Magic}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.MajorLinkerVersion),indent} = {file.OptionalHeader.MajorLinkerVersion}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.MinorLinkerVersion),indent} = {file.OptionalHeader.MinorLinkerVersion}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfCode),indent} = 0x{file.OptionalHeader.SizeOfCode:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfInitializedData),indent} = 0x{file.OptionalHeader.SizeOfInitializedData:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfUninitializedData),indent} = 0x{file.OptionalHeader.SizeOfUninitializedData:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.AddressOfEntryPoint),indent} = 0x{file.OptionalHeader.AddressOfEntryPoint:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.BaseOfCode),indent} = 0x{file.OptionalHeader.BaseOfCode:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.BaseOfData),indent} = 0x{file.OptionalHeader.BaseOfData:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.ImageBase),indent} = 0x{file.OptionalHeader.ImageBase:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.SectionAlignment),indent} = 0x{file.OptionalHeader.SectionAlignment:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.FileAlignment),indent} = 0x{file.OptionalHeader.FileAlignment:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.MajorOperatingSystemVersion),indent} = {file.OptionalHeader.MajorOperatingSystemVersion}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.MinorOperatingSystemVersion),indent} = {file.OptionalHeader.MinorOperatingSystemVersion}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.MajorImageVersion),indent} = {file.OptionalHeader.MajorImageVersion}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.MinorImageVersion),indent} = {file.OptionalHeader.MinorImageVersion}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.MajorSubsystemVersion),indent} = {file.OptionalHeader.MajorSubsystemVersion}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.MinorSubsystemVersion),indent} = {file.OptionalHeader.MinorSubsystemVersion}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.Win32VersionValue),indent} = 0x{file.OptionalHeader.Win32VersionValue:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfImage),indent} = 0x{file.OptionalHeader.SizeOfImage:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfHeaders),indent} = 0x{file.OptionalHeader.SizeOfHeaders:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.CheckSum),indent} = 0x{file.OptionalHeader.CheckSum:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.Subsystem),indent} = {file.OptionalHeader.Subsystem}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.DllCharacteristics),indent} = {file.OptionalHeader.DllCharacteristics}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfStackReserve),indent} = 0x{file.OptionalHeader.SizeOfStackReserve:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfStackCommit),indent} = 0x{file.OptionalHeader.SizeOfStackCommit:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfHeapReserve),indent} = 0x{file.OptionalHeader.SizeOfHeapReserve:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.SizeOfHeapCommit),indent} = 0x{file.OptionalHeader.SizeOfHeapCommit:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.LoaderFlags),indent} = 0x{file.OptionalHeader.LoaderFlags:X}"); - writer.WriteLine($"{nameof(ImageOptionalHeader.NumberOfRvaAndSizes),indent} = 0x{file.OptionalHeader.NumberOfRvaAndSizes:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.Magic),indent} = {file.OptionalHeader.Magic}"); + writer.WriteLine($"{nameof(PEOptionalHeader.MajorLinkerVersion),indent} = {file.OptionalHeader.MajorLinkerVersion}"); + writer.WriteLine($"{nameof(PEOptionalHeader.MinorLinkerVersion),indent} = {file.OptionalHeader.MinorLinkerVersion}"); + writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfCode),indent} = 0x{file.OptionalHeader.SizeOfCode:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfInitializedData),indent} = 0x{file.OptionalHeader.SizeOfInitializedData:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfUninitializedData),indent} = 0x{file.OptionalHeader.SizeOfUninitializedData:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.AddressOfEntryPoint),indent} = 0x{file.OptionalHeader.AddressOfEntryPoint:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.BaseOfCode),indent} = 0x{file.OptionalHeader.BaseOfCode:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.BaseOfData),indent} = 0x{file.OptionalHeader.BaseOfData:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.ImageBase),indent} = 0x{file.OptionalHeader.ImageBase:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.SectionAlignment),indent} = 0x{file.OptionalHeader.SectionAlignment:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.FileAlignment),indent} = 0x{file.OptionalHeader.FileAlignment:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.MajorOperatingSystemVersion),indent} = {file.OptionalHeader.MajorOperatingSystemVersion}"); + writer.WriteLine($"{nameof(PEOptionalHeader.MinorOperatingSystemVersion),indent} = {file.OptionalHeader.MinorOperatingSystemVersion}"); + writer.WriteLine($"{nameof(PEOptionalHeader.MajorImageVersion),indent} = {file.OptionalHeader.MajorImageVersion}"); + writer.WriteLine($"{nameof(PEOptionalHeader.MinorImageVersion),indent} = {file.OptionalHeader.MinorImageVersion}"); + writer.WriteLine($"{nameof(PEOptionalHeader.MajorSubsystemVersion),indent} = {file.OptionalHeader.MajorSubsystemVersion}"); + writer.WriteLine($"{nameof(PEOptionalHeader.MinorSubsystemVersion),indent} = {file.OptionalHeader.MinorSubsystemVersion}"); + writer.WriteLine($"{nameof(PEOptionalHeader.Win32VersionValue),indent} = 0x{file.OptionalHeader.Win32VersionValue:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfImage),indent} = 0x{file.OptionalHeader.SizeOfImage:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfHeaders),indent} = 0x{file.OptionalHeader.SizeOfHeaders:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.CheckSum),indent} = 0x{file.OptionalHeader.CheckSum:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.Subsystem),indent} = {file.OptionalHeader.Subsystem}"); + writer.WriteLine($"{nameof(PEOptionalHeader.DllCharacteristics),indent} = {file.OptionalHeader.DllCharacteristics}"); + writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfStackReserve),indent} = 0x{file.OptionalHeader.SizeOfStackReserve:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfStackCommit),indent} = 0x{file.OptionalHeader.SizeOfStackCommit:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfHeapReserve),indent} = 0x{file.OptionalHeader.SizeOfHeapReserve:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfHeapCommit),indent} = 0x{file.OptionalHeader.SizeOfHeapCommit:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.LoaderFlags),indent} = 0x{file.OptionalHeader.LoaderFlags:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.NumberOfRvaAndSizes),indent} = 0x{file.OptionalHeader.NumberOfRvaAndSizes:X}"); } writer.Unindent(); writer.WriteLine(); diff --git a/src/LibObjectFile/PE/ImagePESignature.cs b/src/LibObjectFile/PE/PESignature.cs similarity index 76% rename from src/LibObjectFile/PE/ImagePESignature.cs rename to src/LibObjectFile/PE/PESignature.cs index ad4c7b8..2ad6f00 100644 --- a/src/LibObjectFile/PE/ImagePESignature.cs +++ b/src/LibObjectFile/PE/PESignature.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. @@ -7,7 +7,7 @@ namespace LibObjectFile.PE; /// /// Defines the NT signature for a PE image. /// -public enum ImagePESignature : uint +public enum PESignature : uint { /// /// PE00 From 7d18fd94fef8072200694917ecf3b38c5d9c760e Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 29 Sep 2024 10:03:47 +0200 Subject: [PATCH 73/87] Improve handling of PEResourceDirectory --- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 1 + .../PE/DataDirectory/PEExportDirectory.cs | 51 ++++---- .../PE/DataDirectory/PEImportDirectory.cs | 32 ++--- .../PE/DataDirectory/PEResourceDataEntry.cs | 26 ++-- .../PE/DataDirectory/PEResourceDirectory.cs | 77 ++++++++++-- .../DataDirectory/PEResourceDirectoryEntry.cs | 115 +++++++++++------- .../PEResourceDirectoryEntryByName.cs | 2 +- .../PE/DataDirectory/PEResourceEntry.cs | 6 +- .../PE/DataDirectory/PEResourceString.cs | 84 +++++++++++++ 9 files changed, 279 insertions(+), 115 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PEResourceString.cs diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 392b5e4..e31b848 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -194,4 +194,5 @@ public enum DiagnosticId PE_ERR_InvalidResourceDirectory = 4000, PE_ERR_InvalidResourceDirectoryEntry = 4001, PE_ERR_InvalidResourceDirectoryEntryRVAOffsetToData = 4002, + PE_ERR_InvalidResourceString = 4003, } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs index 545d30f..d1fcc80 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs @@ -32,29 +32,6 @@ public PEExportDirectory() : base(PEDataDirectoryKind.Export) public PEExportOrdinalTable? ExportOrdinalTable { get; set; } - protected override unsafe uint ComputeHeaderSize(PELayoutContext context) - { - return (uint)sizeof(RawImageExportDirectory); - } - - internal override IEnumerable CollectImplicitSectionDataList() - { - if (ExportFunctionAddressTable is not null) - { - yield return ExportFunctionAddressTable; - } - - if (ExportNameTable is not null) - { - yield return ExportNameTable; - } - - if (ExportOrdinalTable is not null) - { - yield return ExportOrdinalTable; - } - } - public override unsafe void Read(PEImageReader reader) { reader.Position = Position; @@ -164,8 +141,8 @@ public override void Write(PEImageWriter writer) MinorVersion = MinorVersion, Base = OrdinalBase, Name = NameLink.RVA(), - NumberOfFunctions = (uint)ExportFunctionAddressTable!.Values.Count, - NumberOfNames = (uint)ExportNameTable!.Values.Count, + NumberOfFunctions = (uint)(ExportFunctionAddressTable?.Values.Count ?? 0), + NumberOfNames = (uint)(ExportNameTable?.Values.Count ?? 0), AddressOfFunctions = (RVA)(uint)(ExportFunctionAddressTable?.RVA ?? (RVA)0), AddressOfNames = (RVA)(uint)(ExportNameTable?.RVA ?? 0), AddressOfNameOrdinals = (RVA)(uint)(ExportOrdinalTable?.RVA ?? 0) @@ -174,6 +151,30 @@ public override void Write(PEImageWriter writer) writer.Write(exportDirectory); } + protected override unsafe uint ComputeHeaderSize(PELayoutContext context) + { + return (uint)sizeof(RawImageExportDirectory); + } + + internal override IEnumerable CollectImplicitSectionDataList() + { + if (ExportFunctionAddressTable is not null) + { + yield return ExportFunctionAddressTable; + } + + if (ExportNameTable is not null) + { + yield return ExportNameTable; + } + + if (ExportOrdinalTable is not null) + { + yield return ExportOrdinalTable; + } + } + + private struct RawImageExportDirectory { #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 7dd5feb..1a8c4fd 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -101,6 +101,22 @@ public override void Read(PEImageReader reader) } } + public override void Write(PEImageWriter writer) + { + 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); + } + + // Null entry + rawEntry = default; + writer.Write(rawEntry); + } + protected override unsafe uint ComputeHeaderSize(PELayoutContext context) => CalculateSize(); internal override IEnumerable CollectImplicitSectionDataList() @@ -153,20 +169,4 @@ private unsafe uint CalculateSize() { return (uint)(((_entries.Count + 1) * sizeof(RawImportDirectoryEntry))); } - - public override void Write(PEImageWriter writer) - { - 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); - } - - // Null entry - rawEntry = default; - writer.Write(rawEntry); - } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs index d45c9da..0d0042b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs @@ -96,23 +96,19 @@ internal override unsafe void Read(in ReaderContext context) Position = section.Position + rawDataEntry.OffsetToData - section.RVA, Size = rawDataEntry.Size, }; + } - // 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) - { - Data.RequiredPositionAlignment = 1; - } - else if (context.ResourceDataList.Count == 0 && (Data.Position & 0xF) == 0) + internal override void Write(in WriterContext context) => Write(context.Writer); + + public override void Write(PEImageWriter writer) + { + var rawDataEntry = new RawImageResourceDataEntry { - // 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); + CodePage = (uint)(CodePage?.CodePage ?? 0), + OffsetToData = Data.RVA, + Size = (uint)Data.Size, + }; - // Register the list of data being loaded - context.ResourceDataList.Add(Data); + writer.Write(rawDataEntry); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs index 434c314..ccbf631 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs @@ -2,8 +2,10 @@ // 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.Collections.Generic; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace LibObjectFile.PE; @@ -12,9 +14,9 @@ namespace LibObjectFile.PE; /// public sealed class PEResourceDirectory : PEDataDirectory { - private List? _tempResourceDataList; - - + private List? _tempResourceStrings; + private List? _tempResourceEntries; + /// /// Initializes a new instance of the class. /// @@ -45,31 +47,86 @@ public override void Read(PEImageReader reader) Root.Position = Position; - _tempResourceDataList = new(); - var readerContext = new PEResourceEntry.ReaderContext(reader, this, _tempResourceDataList); + _tempResourceStrings = new(); + _tempResourceEntries = new(); + + // Read the resource directory recursively + var readerContext = new PEResourceEntry.ReaderContext(reader, this, _tempResourceStrings, _tempResourceEntries); Root.Read(readerContext); + // Read the resource strings (that should follow the resource directory) + foreach (var resourceString in _tempResourceStrings) + { + resourceString.Read(reader); + } + + // Read the content of the resource data (that should usually be stored after the resource strings) + bool isFirstDataEntry = true; + foreach (var resourceEntry in _tempResourceEntries) + { + if (resourceEntry is not PEResourceDataEntry dataEntry) + { + continue; + } + + var resourceData = dataEntry.Data; + + // 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(resourceData.Position, resourceData.RequiredPositionAlignment); + if (checkPosition != resourceData.Position) + { + resourceData.RequiredPositionAlignment = 1; + } + else if (isFirstDataEntry && (resourceData.Position & 0xF) == 0) + { + // If we are the first resource data entry and the position is aligned on 16 bytes, we can assume this alignment + resourceData.RequiredPositionAlignment = 16; + } + + // Read the data + resourceData.Read(reader); + + isFirstDataEntry = true; + } + HeaderSize = ComputeHeaderSize(reader); } internal override IEnumerable CollectImplicitSectionDataList() { - if (_tempResourceDataList is not null) + if (_tempResourceStrings is not null) + { + foreach (var data in _tempResourceStrings) + { + yield return data; + } + + // We clear the list after being used - as this method is called once and we don't want to hold a reference + _tempResourceStrings.Clear(); + _tempResourceStrings = null; + } + + if (_tempResourceEntries is not null) { - foreach (var data in _tempResourceDataList) + foreach (var data in _tempResourceEntries) { yield return data; + + if (data is PEResourceDataEntry dataEntry) + { + yield return dataEntry.Data; + } } // 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; + _tempResourceEntries.Clear(); + _tempResourceEntries = null; } } /// public override void Write(PEImageWriter writer) { - throw new NotImplementedException(); + Root.Write(writer); } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs index 3ac5faa..6b4f781 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs @@ -5,8 +5,9 @@ using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; using System; -using System.Buffers; using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using LibObjectFile.Collections; @@ -31,6 +32,11 @@ public PEResourceDirectoryEntry() ByIds = new(); } + /// + /// Gets or sets the characteristics of the resource directory entry. + /// + public uint Characteristics { get; set; } + /// /// Gets or sets the time stamp of the resource directory entry. /// @@ -39,12 +45,12 @@ public PEResourceDirectoryEntry() /// /// Gets or sets the major version of the resource directory entry. /// - public uint MajorVersion { get; set; } + public ushort MajorVersion { get; set; } /// /// Gets or sets the minor version of the resource directory entry. /// - public uint MinorVersion { get; set; } + public ushort MinorVersion { get; set; } /// /// Gets the list of resource entries within the directory. @@ -59,7 +65,6 @@ public PEResourceDirectoryEntry() internal override unsafe void Read(in ReaderContext context) { var reader = context.Reader; - var directory = context.Directory; reader.Position = Position; if (!reader.TryReadData(sizeof(RawImageResourceDirectory), out var data)) @@ -68,6 +73,7 @@ internal override unsafe void Read(in ReaderContext context) return; } + Characteristics = data.Characteristics; TimeDateStamp = DateTime.UnixEpoch.AddSeconds(data.TimeDateStamp); MajorVersion = data.MajorVersion; MinorVersion = data.MinorVersion; @@ -86,7 +92,7 @@ internal override unsafe void Read(in ReaderContext context) for (int i = 0; i < data.NumberOfNamedEntries + data.NumberOfIdEntries; i++) { var entry = spanEntries[i]; - ReadEntry(reader, directory, entry); + ReadEntry(context, entry); if (reader.Diagnostics.HasErrors) { @@ -97,50 +103,71 @@ internal override unsafe void Read(in ReaderContext context) // Update the size Size = reader.Position - Position; - var size = CalculateSize(); - if (Size != size) - { + Debug.Assert(Size == CalculateSize()); - } - - - // Process all the entries recursively + // Read all strings (they should follow the directory) var byNames = CollectionsMarshal.AsSpan(ByNames); - foreach (ref var entry in byNames) + foreach (ref var item in byNames) { - entry.Entry.Read(context); + context.Strings.Add(item.Name); + context.Entries.Add(item.Entry); + item.Entry.Read(context); } + // Read the entry content var byIds = CollectionsMarshal.AsSpan(ByIds); - foreach (ref var entry in byIds) + foreach (ref var item in byIds) { - entry.Entry.Read(context); + context.Entries.Add(item.Entry); + item.Entry.Read(context); } } - private void ReadEntry(PEImageReader reader, PEResourceDirectory directory, RawImageResourceDirectoryEntry rawEntry) + internal override unsafe void Write(in WriterContext context) { - string? name = null; - int id = 0; + var size = Size; + Debug.Assert(size == CalculateSize()); - if ((rawEntry.NameOrId & IMAGE_RESOURCE_NAME_IS_STRING) != 0) + using var tempSpan = TempSpan.Create((int)size, out var span); + + + ref var rawResourceDirectory = ref Unsafe.As(ref MemoryMarshal.GetReference(span)); + + rawResourceDirectory.Characteristics = Characteristics; + rawResourceDirectory.TimeDateStamp = (uint)(TimeDateStamp - DateTime.UnixEpoch).TotalSeconds; + rawResourceDirectory.MajorVersion = MajorVersion; + rawResourceDirectory.MinorVersion = MinorVersion; + rawResourceDirectory.NumberOfNamedEntries = (ushort)ByNames.Count; + rawResourceDirectory.NumberOfIdEntries = (ushort)ByIds.Count; + + var rawEntries = MemoryMarshal.Cast(span.Slice(sizeof(RawImageResourceDirectory), (ByNames.Count + ByIds.Count) * sizeof(RawImageResourceDirectoryEntry))); + + var directoryPosition = context.Directory.Position; + + var byNames = CollectionsMarshal.AsSpan(ByNames); + for (int i = 0; i < byNames.Length; i++) { - // Read the string - var length = reader.ReadU16() * 2; - using var tempSpan = TempSpan.Create(length, out var span); + ref var rawEntry = ref rawEntries[i]; + var entry = byNames[i]; - int readLength = reader.Read(span); - if (readLength != length) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource directory string at position {reader.Position}"); - return; - } - name = Encoding.Unicode.GetString(span); + rawEntry.NameOrId = IMAGE_RESOURCE_NAME_IS_STRING | (uint)(entry.Name.Position - directoryPosition); + rawEntry.OffsetToDataOrDirectoryEntry = (uint)(entry.Entry.Position - directoryPosition); } - else + + var byIds = CollectionsMarshal.AsSpan(ByIds); + for (int i = 0; i < byIds.Length; i++) { - id = (int)(rawEntry.NameOrId & ~IMAGE_RESOURCE_NAME_IS_STRING); + ref var rawEntry = ref rawEntries[byNames.Length + i]; + var entry = byIds[i]; + + rawEntry.NameOrId = (uint)entry.Id.Value; + rawEntry.OffsetToDataOrDirectoryEntry = IMAGE_RESOURCE_DATA_IS_DIRECTORY | (uint)(entry.Entry.Position - directoryPosition); } + } + + private void ReadEntry(in ReaderContext context, RawImageResourceDirectoryEntry rawEntry) + { + var directory = context.Directory; bool isDirectory = (rawEntry.OffsetToDataOrDirectoryEntry & IMAGE_RESOURCE_DATA_IS_DIRECTORY) != 0; var offset = rawEntry.OffsetToDataOrDirectoryEntry & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY; @@ -148,17 +175,20 @@ private void ReadEntry(PEImageReader reader, PEResourceDirectory directory, RawI PEResourceEntry entry = isDirectory ? new PEResourceDirectoryEntry() : new PEResourceDataEntry(); entry.Position = directory.Position + offset; - if (name is not null) + if ((rawEntry.NameOrId & IMAGE_RESOURCE_NAME_IS_STRING) != 0) { - ByNames.Add(new(name, entry)); + var resourceString = new PEResourceString() + { + Position = context.Directory.Position + (rawEntry.NameOrId & ~IMAGE_RESOURCE_NAME_IS_STRING) + }; + + ByNames.Add(new(resourceString, entry)); } else { + var id = (int)rawEntry.NameOrId; 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); } public override unsafe void UpdateLayout(PELayoutContext layoutContext) @@ -172,15 +202,6 @@ private unsafe uint CalculateSize() size += (uint)sizeof(RawImageResourceDirectory); size += (uint)(ByNames.Count + ByIds.Count) * (uint)sizeof(RawImageResourceDirectoryEntry); - if (ByNames.Count > 0) - { - var byNames = CollectionsMarshal.AsSpan(ByNames); - foreach (ref readonly var entry in byNames) - { - size += sizeof(ushort) + (uint)entry.Name.Length * 2; - } - } - return size; } @@ -198,4 +219,4 @@ protected override bool PrintMembers(StringBuilder builder) 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/PEResourceDirectoryEntryByName.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntryByName.cs index 59b0463..9292de7 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntryByName.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntryByName.cs @@ -9,4 +9,4 @@ namespace LibObjectFile.PE; /// /// 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 +public readonly record struct PEResourceDirectoryEntryByName(PEResourceString 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 64abb72..d1b829d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs @@ -15,5 +15,9 @@ public abstract class PEResourceEntry : PESectionData internal abstract void Read(in ReaderContext context); - internal readonly record struct ReaderContext(PEImageReader Reader, PEResourceDirectory Directory, List ResourceDataList); + internal abstract void Write(in WriterContext context); + + internal readonly record struct ReaderContext(PEImageReader Reader, PEResourceDirectory Directory, List Strings, List Entries); + + internal readonly record struct WriterContext(PEImageWriter Writer, PEResourceDirectory Directory); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs new file mode 100644 index 0000000..38354d0 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs @@ -0,0 +1,84 @@ +// 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.Runtime.InteropServices; +using System.Text; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// Represents a resource string in a Portable Executable (PE) file. +/// +public sealed class PEResourceString : PESectionData +{ + /// + /// Initializes a new instance of the class. + /// + public PEResourceString() + { + Text = string.Empty; + } + + /// + /// Initializes a new instance of the class with the specified text. + /// + /// The resource string text. + public PEResourceString(string text) + { + Text = text; + } + + /// + /// Gets or sets the resource string text. + /// + public string Text { get; set; } + + /// + public override bool HasChildren => false; + + /// + public override void Read(PEImageReader reader) + { + var length = reader.ReadU16(); + using var tempSpan = TempSpan.Create(length, out var span); + reader.Position = Position; + var read = reader.Read(tempSpan.AsBytes); + if (read != length) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceString, $"Invalid resource string length. Expected: {length}, Read: {read}"); + } + Text = new string(span); + + // Update the size after reading the string + Size = (uint)(sizeof(ushort) + length); + } + + /// + public override void Write(PEImageWriter writer) + { + // TODO: validate string length <= ushort.MaxValue + writer.WriteU16((ushort)Text.Length); + writer.Write(MemoryMarshal.Cast(Text.AsSpan())); + } + + public override void UpdateLayout(PELayoutContext layoutContext) + { + Size = (uint)(sizeof(ushort) + Text.Length * sizeof(char)); + } + + /// + protected override bool PrintMembers(StringBuilder builder) + { + if (base.PrintMembers(builder)) + { + builder.Append(", "); + } + + builder.Append($"Text = {Text}"); + return true; + } +} From ad8d59936716bf130d55d02bbbb1273618d7d3d1 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 29 Sep 2024 11:11:45 +0200 Subject: [PATCH 74/87] Improve handling of PEBaseRelocationDirectory --- ..._name=NativeConsole2Win64.exe.verified.txt | 52 +++--- ...r_name=NativeConsoleWin64.exe.verified.txt | 44 ++--- ...r_name=NativeLibraryWin64.dll.verified.txt | 40 +++-- src/LibObjectFile.sln.DotSettings | 1 + src/LibObjectFile/Diagnostics/DiagnosticId.cs | 1 + .../PE/DataDirectory/PEBaseRelocation.cs | 72 +++----- .../PE/DataDirectory/PEBaseRelocationBlock.cs | 166 +++++++++++++----- .../PEBaseRelocationDirectory.cs | 109 ++---------- .../PE/DataDirectory/PEExceptionDirectory.cs | 46 ++--- .../PEExceptionFunctionEntryArm.cs | 8 +- .../PE/DataDirectory/PEResourceData.cs | 11 ++ .../PE/DataDirectory/PEResourceDataEntry.cs | 11 +- .../PE/DataDirectory/PEResourceDirectory.cs | 5 +- .../DataDirectory/PEResourceDirectoryEntry.cs | 59 ++++--- .../PE/DataDirectory/PEResourceEntry.cs | 12 +- .../PE/DataDirectory/PEResourceString.cs | 9 + .../PE/Internal/RawImageBaseRelocation.cs | 37 ---- src/LibObjectFile/PE/PEPrinter.cs | 60 ++++--- 18 files changed, 372 insertions(+), 371 deletions(-) delete mode 100644 src/LibObjectFile/PE/Internal/RawImageBaseRelocation.cs 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 83c1694..e087f49 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 @@ -626,28 +626,32 @@ Sections [05] .reloc PESection Position = 0x00004000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x0000003C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) [00] PEBaseRelocationDirectory Position = 0x00004000, Size = 0x0000003C, RVA = 0x00008000, VirtualSize = 0x0000003C - Block 0x3000 Relocations[20] - [000] Dir64 Offset = 0x0250, RVA = 0x3250 (0x0000000140001A84), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } - [001] Dir64 Offset = 0x0258, RVA = 0x3258 (0x0000000140001A84), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } - [002] Dir64 Offset = 0x0260, RVA = 0x3260 (0x0000000140002930), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } - [003] Dir64 Offset = 0x0268, RVA = 0x3268 (0x0000000140002950), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } - [004] Dir64 Offset = 0x0270, RVA = 0x3270 (0x0000000140002950), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } - [005] Dir64 Offset = 0x0280, RVA = 0x3280 (0x000000014000290C), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } - [006] Dir64 Offset = 0x0290, RVA = 0x3290 (0x0000000140001448), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } - [007] Dir64 Offset = 0x02A8, RVA = 0x32A8 (0x0000000140001380), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } - [008] Dir64 Offset = 0x02B0, RVA = 0x32B0 (0x0000000140001438), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } - [009] Dir64 Offset = 0x02F8, RVA = 0x32F8 (0x00000001400050A0), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } - [010] Dir64 Offset = 0x0300, RVA = 0x3300 (0x0000000140005140), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } - [011] Dir64 Offset = 0x03E8, RVA = 0x33E8 (0x0000000140005000), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } - [012] Dir64 Offset = 0x0400, RVA = 0x3400 (0x0000000140003250), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } - [013] Dir64 Offset = 0x0408, RVA = 0x3408 (0x0000000140003260), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } - [014] Dir64 Offset = 0x0490, RVA = 0x3490 (0x0000000140003580), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } - [015] Dir64 Offset = 0x04A8, RVA = 0x34A8 (0x0000000140003258), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } - [016] Dir64 Offset = 0x04B0, RVA = 0x34B0 (0x0000000140003268), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } - [017] Dir64 Offset = 0x04B8, RVA = 0x34B8 (0x0000000140003270), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } - [018] Dir64 Offset = 0x04C0, RVA = 0x34C0 (0x0000000140003278), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } - [019] Dir64 Offset = 0x04C8, RVA = 0x34C8 (0x0000000140003280), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } - Block 0x5000 Relocations[2] - [000] Dir64 Offset = 0x0078, RVA = 0x5078 (0x000000014000133E), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] -> .data) } - [001] Dir64 Offset = 0x0080, RVA = 0x5080 (0x0000000140001332), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] -> .data) } + [00] PEBaseRelocationBlock Position = 0x00004000, Size = 0x00000030, RVA = 0x00008000, VirtualSize = 0x00000030 + Block 0x3000 Relocations[20] + [000] Dir64 Offset = 0x0250, RVA = 0x3250 (0x0000000140001A84), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [001] Dir64 Offset = 0x0258, RVA = 0x3258 (0x0000000140001A84), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [002] Dir64 Offset = 0x0260, RVA = 0x3260 (0x0000000140002930), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [003] Dir64 Offset = 0x0268, RVA = 0x3268 (0x0000000140002950), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [004] Dir64 Offset = 0x0270, RVA = 0x3270 (0x0000000140002950), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [005] Dir64 Offset = 0x0280, RVA = 0x3280 (0x000000014000290C), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [006] Dir64 Offset = 0x0290, RVA = 0x3290 (0x0000000140001448), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [007] Dir64 Offset = 0x02A8, RVA = 0x32A8 (0x0000000140001380), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [008] Dir64 Offset = 0x02B0, RVA = 0x32B0 (0x0000000140001438), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [009] Dir64 Offset = 0x02F8, RVA = 0x32F8 (0x00000001400050A0), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [010] Dir64 Offset = 0x0300, RVA = 0x3300 (0x0000000140005140), SectionData = { RVA = 0x00003250 (PEStreamSectionData[1] -> .rdata) } + [011] Dir64 Offset = 0x03E8, RVA = 0x33E8 (0x0000000140005000), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [012] Dir64 Offset = 0x0400, RVA = 0x3400 (0x0000000140003250), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [013] Dir64 Offset = 0x0408, RVA = 0x3408 (0x0000000140003260), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [014] Dir64 Offset = 0x0490, RVA = 0x3490 (0x0000000140003580), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [015] Dir64 Offset = 0x04A8, RVA = 0x34A8 (0x0000000140003258), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [016] Dir64 Offset = 0x04B0, RVA = 0x34B0 (0x0000000140003268), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [017] Dir64 Offset = 0x04B8, RVA = 0x34B8 (0x0000000140003270), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [018] Dir64 Offset = 0x04C0, RVA = 0x34C0 (0x0000000140003278), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + [019] Dir64 Offset = 0x04C8, RVA = 0x34C8 (0x0000000140003280), SectionData = { RVA = 0x00003390 (PELoadConfigDirectory64[2] -> .rdata) } + + [01] PEBaseRelocationBlock Position = 0x00004030, Size = 0x0000000C, RVA = 0x00008030, VirtualSize = 0x0000000C + Block 0x5000 Relocations[2] + [000] Dir64 Offset = 0x0078, RVA = 0x5078 (0x000000014000133E), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] -> .data) } + [001] Dir64 Offset = 0x0080, RVA = 0x5080 (0x0000000140001332), SectionData = { RVA = 0x00005078 (PEBoundImportAddressTable64[1] -> .data) } + 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 1d430df..aa12b2f 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 @@ -533,25 +533,27 @@ Sections [05] .reloc PESection Position = 0x00003000, Size = 0x00000200, RVA = 0x00008000, VirtualSize = 0x00000030, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) [00] PEBaseRelocationDirectory Position = 0x00003000, Size = 0x00000030, RVA = 0x00008000, VirtualSize = 0x00000030 - Block 0x3000 Relocations[20] - [000] Dir64 Offset = 0x01F0, RVA = 0x31F0 (0x0000000140001984), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } - [001] Dir64 Offset = 0x01F8, RVA = 0x31F8 (0x0000000140001984), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } - [002] Dir64 Offset = 0x0200, RVA = 0x3200 (0x0000000140002010), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } - [003] Dir64 Offset = 0x0208, RVA = 0x3208 (0x0000000140002030), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } - [004] Dir64 Offset = 0x0210, RVA = 0x3210 (0x0000000140002030), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } - [005] Dir64 Offset = 0x0220, RVA = 0x3220 (0x0000000140001FEE), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } - [006] Dir64 Offset = 0x0230, RVA = 0x3230 (0x0000000140001348), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } - [007] Dir64 Offset = 0x0248, RVA = 0x3248 (0x0000000140001280), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } - [008] Dir64 Offset = 0x0250, RVA = 0x3250 (0x0000000140001338), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } - [009] Dir64 Offset = 0x0280, RVA = 0x3280 (0x0000000140005080), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } - [010] Dir64 Offset = 0x0288, RVA = 0x3288 (0x0000000140005120), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } - [011] Dir64 Offset = 0x0308, RVA = 0x3308 (0x0000000140005000), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } - [012] Dir64 Offset = 0x0320, RVA = 0x3320 (0x00000001400031F0), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } - [013] Dir64 Offset = 0x0328, RVA = 0x3328 (0x0000000140003200), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } - [014] Dir64 Offset = 0x03B0, RVA = 0x33B0 (0x0000000140003480), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } - [015] Dir64 Offset = 0x03C8, RVA = 0x33C8 (0x00000001400031F8), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } - [016] Dir64 Offset = 0x03D0, RVA = 0x33D0 (0x0000000140003208), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } - [017] Dir64 Offset = 0x03D8, RVA = 0x33D8 (0x0000000140003210), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } - [018] Dir64 Offset = 0x03E0, RVA = 0x33E0 (0x0000000140003218), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } - [019] Dir64 Offset = 0x03E8, RVA = 0x33E8 (0x0000000140003220), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [00] PEBaseRelocationBlock Position = 0x00003000, Size = 0x00000030, RVA = 0x00008000, VirtualSize = 0x00000030 + Block 0x3000 Relocations[20] + [000] Dir64 Offset = 0x01F0, RVA = 0x31F0 (0x0000000140001984), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [001] Dir64 Offset = 0x01F8, RVA = 0x31F8 (0x0000000140001984), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [002] Dir64 Offset = 0x0200, RVA = 0x3200 (0x0000000140002010), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [003] Dir64 Offset = 0x0208, RVA = 0x3208 (0x0000000140002030), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [004] Dir64 Offset = 0x0210, RVA = 0x3210 (0x0000000140002030), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [005] Dir64 Offset = 0x0220, RVA = 0x3220 (0x0000000140001FEE), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [006] Dir64 Offset = 0x0230, RVA = 0x3230 (0x0000000140001348), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [007] Dir64 Offset = 0x0248, RVA = 0x3248 (0x0000000140001280), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [008] Dir64 Offset = 0x0250, RVA = 0x3250 (0x0000000140001338), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [009] Dir64 Offset = 0x0280, RVA = 0x3280 (0x0000000140005080), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [010] Dir64 Offset = 0x0288, RVA = 0x3288 (0x0000000140005120), SectionData = { RVA = 0x000031F0 (PEStreamSectionData[1] -> .rdata) } + [011] Dir64 Offset = 0x0308, RVA = 0x3308 (0x0000000140005000), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [012] Dir64 Offset = 0x0320, RVA = 0x3320 (0x00000001400031F0), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [013] Dir64 Offset = 0x0328, RVA = 0x3328 (0x0000000140003200), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [014] Dir64 Offset = 0x03B0, RVA = 0x33B0 (0x0000000140003480), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [015] Dir64 Offset = 0x03C8, RVA = 0x33C8 (0x00000001400031F8), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [016] Dir64 Offset = 0x03D0, RVA = 0x33D0 (0x0000000140003208), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [017] Dir64 Offset = 0x03D8, RVA = 0x33D8 (0x0000000140003210), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [018] Dir64 Offset = 0x03E0, RVA = 0x33E0 (0x0000000140003218), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + [019] Dir64 Offset = 0x03E8, RVA = 0x33E8 (0x0000000140003220), SectionData = { RVA = 0x000032B0 (PELoadConfigDirectory64[2] -> .rdata) } + 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 b38a9e1..4955137 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 @@ -459,23 +459,25 @@ Sections [05] .reloc PESection Position = 0x00002800, Size = 0x00000200, RVA = 0x00006000, VirtualSize = 0x0000002C, Characteristics = 0x42000040 (ContainsInitializedData, MemDiscardable, MemRead) [00] PEBaseRelocationDirectory Position = 0x00002800, Size = 0x0000002C, RVA = 0x00006000, VirtualSize = 0x0000002C - Block 0x2000 Relocations[18] - [000] Dir64 Offset = 0x00E8, RVA = 0x20E8 (0x0000000180001B0C), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } - [001] Dir64 Offset = 0x00F0, RVA = 0x20F0 (0x0000000180001B0C), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } - [002] Dir64 Offset = 0x00F8, RVA = 0x20F8 (0x0000000180001E60), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } - [003] Dir64 Offset = 0x0100, RVA = 0x2100 (0x0000000180001E80), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } - [004] Dir64 Offset = 0x0108, RVA = 0x2108 (0x0000000180001E80), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } - [005] Dir64 Offset = 0x0118, RVA = 0x2118 (0x0000000180001E3B), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } - [006] Dir64 Offset = 0x0168, RVA = 0x2168 (0x0000000180003090), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } - [007] Dir64 Offset = 0x0170, RVA = 0x2170 (0x0000000180003130), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } - [008] Dir64 Offset = 0x01E8, RVA = 0x21E8 (0x0000000180003000), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } - [009] Dir64 Offset = 0x0200, RVA = 0x2200 (0x00000001800020E8), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } - [010] Dir64 Offset = 0x0208, RVA = 0x2208 (0x00000001800020F8), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } - [011] Dir64 Offset = 0x0290, RVA = 0x2290 (0x0000000180002380), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } - [012] Dir64 Offset = 0x02A8, RVA = 0x22A8 (0x00000001800020F0), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } - [013] Dir64 Offset = 0x02B0, RVA = 0x22B0 (0x0000000180002100), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } - [014] Dir64 Offset = 0x02B8, RVA = 0x22B8 (0x0000000180002108), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } - [015] Dir64 Offset = 0x02C0, RVA = 0x22C0 (0x0000000180002110), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } - [016] Dir64 Offset = 0x02C8, RVA = 0x22C8 (0x0000000180002118), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } - [017] Absolute Zero padding + [00] PEBaseRelocationBlock Position = 0x00002800, Size = 0x0000002C, RVA = 0x00006000, VirtualSize = 0x0000002C + Block 0x2000 Relocations[18] + [000] Dir64 Offset = 0x00E8, RVA = 0x20E8 (0x0000000180001B0C), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [001] Dir64 Offset = 0x00F0, RVA = 0x20F0 (0x0000000180001B0C), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [002] Dir64 Offset = 0x00F8, RVA = 0x20F8 (0x0000000180001E60), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [003] Dir64 Offset = 0x0100, RVA = 0x2100 (0x0000000180001E80), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [004] Dir64 Offset = 0x0108, RVA = 0x2108 (0x0000000180001E80), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [005] Dir64 Offset = 0x0118, RVA = 0x2118 (0x0000000180001E3B), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [006] Dir64 Offset = 0x0168, RVA = 0x2168 (0x0000000180003090), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [007] Dir64 Offset = 0x0170, RVA = 0x2170 (0x0000000180003130), SectionData = { RVA = 0x000020E8 (PEStreamSectionData[1] -> .rdata) } + [008] Dir64 Offset = 0x01E8, RVA = 0x21E8 (0x0000000180003000), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [009] Dir64 Offset = 0x0200, RVA = 0x2200 (0x00000001800020E8), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [010] Dir64 Offset = 0x0208, RVA = 0x2208 (0x00000001800020F8), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [011] Dir64 Offset = 0x0290, RVA = 0x2290 (0x0000000180002380), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [012] Dir64 Offset = 0x02A8, RVA = 0x22A8 (0x00000001800020F0), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [013] Dir64 Offset = 0x02B0, RVA = 0x22B0 (0x0000000180002100), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [014] Dir64 Offset = 0x02B8, RVA = 0x22B8 (0x0000000180002108), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [015] Dir64 Offset = 0x02C0, RVA = 0x22C0 (0x0000000180002110), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [016] Dir64 Offset = 0x02C8, RVA = 0x22C8 (0x0000000180002118), SectionData = { RVA = 0x00002190 (PELoadConfigDirectory64[2] -> .rdata) } + [017] Absolute Zero padding + diff --git a/src/LibObjectFile.sln.DotSettings b/src/LibObjectFile.sln.DotSettings index 7f73775..8a3b5ef 100644 --- a/src/LibObjectFile.sln.DotSettings +++ b/src/LibObjectFile.sln.DotSettings @@ -2,6 +2,7 @@ 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. + ARM BSD DIE GNU diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index e31b848..058ac93 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -171,6 +171,7 @@ public enum DiagnosticId PE_ERR_BaseRelocationDirectoryInvalidSectionData = 3702, PE_ERR_InvalidDataDirectorySection = 3703, PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress = 3704, + PE_ERR_BaseRelocationDirectoryInvalidSizeOfBlock = 3705, // PE Import PE_ERR_ImportDirectoryInvalidEndOfStream = 3800, diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs index 69001cd..da28d2b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs @@ -12,58 +12,38 @@ namespace LibObjectFile.PE; /// A base relocation in a Portable Executable (PE) image. /// [DebuggerDisplay("{ToString(),nq}")] -public readonly record struct PEBaseRelocation(PEBaseRelocationType Type, PESectionData? Container, RVO RVO) : IPELink +public readonly struct PEBaseRelocation { + public const ushort MaxVirtualOffset = (1 << 12) - 1; + private const ushort TypeMask = unchecked((ushort)~MaxVirtualOffset); + private const ushort VirtualOffsetMask = MaxVirtualOffset; + + private readonly ushort _value; + + public PEBaseRelocation(ushort value) + { + _value = value; + } + + public PEBaseRelocation(PEBaseRelocationType type, ushort offsetInBlock) + { + _value = (ushort)((ushort)type | (offsetInBlock & VirtualOffsetMask)); + } + /// - /// Gets a value indicating whether the base relocation is zero padding. + /// Gets a value indicating whether the base relocation is zero (used for padding). /// - public bool IsZero => Type == PEBaseRelocationType.Absolute; + public bool IsZero => _value == 0; /// - /// Reads the address from the section data. + /// Gets the type of the base relocation. /// - /// The PE file. - /// The address read from the section data. - /// The section data link is not set or the type is not supported. - public ulong ReadAddress(PEFile file) - { - if (Container is null) - { - throw new InvalidOperationException("The section data link is not set"); - } + public PEBaseRelocationType Type => (PEBaseRelocationType)(_value & TypeMask); - if (Type != PEBaseRelocationType.Dir64) - { - throw new InvalidOperationException($"The base relocation type {Type} not supported. Only Dir64 is supported for this method."); - } - - if (file.IsPE32) - { - VA32 va = default; - var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref va, 1)); - - int read = Container!.ReadAt(RVO, span); - if (read != 4) - { - throw new InvalidOperationException($"Unable to read the VA32 from the section data type: {Container.GetType().FullName}"); - } - - return va; - } - else - { - VA64 va = default; - var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref va, 1)); - - int read = Container!.ReadAt(RVO, span); - if (read != 8) - { - throw new InvalidOperationException($"Unable to read the VA64 from the section data type: {Container.GetType().FullName}"); - } - - return va; - } - } + /// + /// Gets the virtual offset of the base relocation relative to the offset of the associated . + /// + public ushort OffsetInBlock => (ushort)(_value & VirtualOffsetMask); - public override string ToString() => Type == PEBaseRelocationType.Absolute ? $"{Type} Zero Padding" : $"{Type} {this.ToDisplayTextWithRVA()}"; + public override string ToString() => Type == PEBaseRelocationType.Absolute ? $"{Type} Zero Padding" : $"{Type} OffsetInBlock = 0x{OffsetInBlock:X}"; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs index 44072fb..cb23b87 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs @@ -3,11 +3,11 @@ // See the license.txt file in the project root for more information. using LibObjectFile.Diagnostics; -using LibObjectFile.PE.Internal; using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Text; namespace LibObjectFile.PE; @@ -15,17 +15,17 @@ namespace LibObjectFile.PE; /// A block of base relocations for a page. /// [DebuggerDisplay("{ToString(),nq}")] -public sealed class PEBaseRelocationBlock +public sealed class PEBaseRelocationBlock : PESectionData { /// /// Initializes a new instance of the class. /// - /// The section link. - public PEBaseRelocationBlock(PESectionLink sectionLink) + public PEBaseRelocationBlock() { - ArgumentNullException.ThrowIfNull(sectionLink.Container, nameof(sectionLink)); - SectionLink = sectionLink; } + + /// + public override bool HasChildren => false; /// /// Gets or sets the linked and its virtual offset within it. @@ -37,22 +37,8 @@ public PEBaseRelocationBlock(PESectionLink sectionLink) /// public List Relocations { get; } = new(); - /// - /// Internal buffer used to read the block before transforming it into parts. - /// - internal Memory BlockBuffer { get; set; } - - /// - /// Gets the size of this block. - /// - internal unsafe uint CalculateSizeOf() + public override unsafe void UpdateLayout(PELayoutContext layoutContext) { - // If we have a block buffer (when reading an image), use it directly until it is transformed into parts - if (!BlockBuffer.IsEmpty) - { - return (uint)BlockBuffer.Length; - } - var count = Relocations.Count; // If we have an odd number of relocations, we need to add an extra 0x0 @@ -61,50 +47,134 @@ internal unsafe uint CalculateSizeOf() count++; } - return (uint)(count * sizeof(ushort)); + Size = (uint)(sizeof(ImageBaseRelocation) + count * sizeof(PEBaseRelocation)); } - internal void ReadAndBind(PEImageReader reader) + /// + /// Gets the RVA of a relocation in this block. + /// + /// The relocation. + /// The RVA of the relocation. + public RVA GetRVA(PEBaseRelocation relocation) => SectionLink.RVA() + relocation.OffsetInBlock; + + /// + /// Reads the address from the section data. + /// + /// The PE file. + /// The address read from the section data. + /// The section data link is not set or the type is not supported. + public ulong ReadAddress(PEFile file, PEBaseRelocation relocation) { - var buffer = BlockBuffer; + if (relocation.Type != PEBaseRelocationType.Dir64) + { + throw new InvalidOperationException($"The base relocation type {relocation.Type} not supported. Only Dir64 is supported for this method."); + } - var relocSpan = MemoryMarshal.Cast(buffer.Span); + var vaOfReloc = SectionLink.RVA() + relocation.OffsetInBlock; - var section = SectionLink.Container!; - var blockBaseAddress = SectionLink.RVA(); + if (!file.TryFindContainerByRVA(vaOfReloc, out var container)) + { + throw new InvalidOperationException($"Unable to find the section data containing the virtual address {vaOfReloc}"); + } + + var rvo = vaOfReloc - container!.RVA; - // Iterate on all relocations - foreach (var rawReloc in relocSpan) + if (file.IsPE32) { - PEBaseRelocation reloc; - if (rawReloc.IsZero) + VA32 va32 = default; + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref va32, 1)); + + int read = container!.ReadAt(rvo, span); + if (read != 4) { - reloc = new PEBaseRelocation(); + throw new InvalidOperationException($"Unable to read the VA32 from the section data type: {container.GetType().FullName}"); } - else + + return va32; + } + else + { + VA64 va64 = default; + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref va64, 1)); + + int read = container!.ReadAt(rvo, span); + if (read != 8) { - var va = blockBaseAddress + rawReloc.OffsetInBlockPart; + throw new InvalidOperationException($"Unable to read the VA64 from the section data type: {container.GetType().FullName}"); + } - // Find the section data containing the virtual address - if (!section.TryFindSectionData(va, out var sectionData)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section data containing the virtual address 0x{va:X4}"); - continue; - } + return va64; + } + } + + public override unsafe void Read(PEImageReader reader) + { + reader.Position = Position; - var offsetInSectionData = va - sectionData.RVA; - reloc = new PEBaseRelocation(rawReloc.Type, sectionData, offsetInSectionData); + var position = reader.Position; + if (!reader.TryReadData(sizeof(ImageBaseRelocation), out ImageBaseRelocation block)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidEndOfStream, $"Unable to read BaseRelocation Block. Expected {sizeof(ImageBaseRelocation)} bytes at position {position}"); + return; + } - } - Relocations.Add(reloc); + if (!reader.File.TryFindSection(block.PageRVA, out var section)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section containing the virtual address {block.PageRVA} at position {position}"); + return; } - // Clear the buffer, as we don't need it anymore - BlockBuffer = Memory.Empty; + + SectionLink = new PESectionLink(section, (uint)(block.PageRVA - section.RVA)); + + var sizeOfRelocations = block.SizeOfBlock - sizeof(ImageBaseRelocation); + + var (relocationCount, remainder) = Math.DivRem(sizeOfRelocations, sizeof(PEBaseRelocation)); + if (remainder != 0) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidSizeOfBlock, $"Invalid size of relocations {sizeOfRelocations} not a multiple of {sizeof(PEBaseRelocation)} bytes at position {position}"); + return; + } + + // Read all relocations straight to memory + CollectionsMarshal.SetCount(Relocations, (int)relocationCount); + var span = CollectionsMarshal.AsSpan(Relocations); + var spanBytes = MemoryMarshal.AsBytes(span); + + var read = reader.Read(spanBytes); + if (read != spanBytes.Length) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidEndOfStream, $"Unable to read the full content of the BaseRelocation directory. Expected {relocationCount} bytes, but read {read} bytes at position {position}"); + return; + } + + Size = (uint)(sizeof(ImageBaseRelocation) + Relocations.Count * sizeof(PEBaseRelocation)); + Debug.Assert(Size == block.SizeOfBlock); + } + + protected override bool PrintMembers(StringBuilder builder) + { + if (base.PrintMembers(builder)) + { + builder.Append(", "); + } + + builder.Append($"Section = {SectionLink.Container?.Name}, Block RVA = {SectionLink.RVA()}, Relocations[{Relocations.Count}]"); + return true; + } + + protected override void ValidateParent(ObjectElement parent) + { + if (parent is not PEBaseRelocationDirectory) + { + throw new InvalidOperationException($"Invalid parent type {parent.GetType().FullName}. Expected {typeof(PEBaseRelocationDirectory).FullName}"); + } } - public override string ToString() + private struct ImageBaseRelocation { - return $"{nameof(PEBaseRelocationBlock)}, Section = {SectionLink.Container?.Name}, RVA = {SectionLink.RVA()}, Size = {CalculateSizeOf()}, Relocations[{Relocations.Count}]"; +#pragma warning disable CS0649 + public RVA PageRVA; + public uint SizeOfBlock; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs index e034cc5..f37236a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs @@ -2,113 +2,42 @@ // 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 System.Text; -using LibObjectFile.Diagnostics; - namespace LibObjectFile.PE; +/// +/// Represents the Base Relocation Directory in a Portable Executable (PE) file. +/// public sealed class PEBaseRelocationDirectory : PEDataDirectory { + /// + /// Initializes a new instance of the class. + /// public PEBaseRelocationDirectory() : base(PEDataDirectoryKind.BaseRelocation) { } - - public List Blocks { get; } = new(); - protected override unsafe uint ComputeHeaderSize(PELayoutContext context) - { - var size = 0U; - foreach (var block in Blocks) - { - size += (uint)(block.CalculateSizeOf() + sizeof(ImageBaseRelocation)); - } + /// + protected override uint ComputeHeaderSize(PELayoutContext context) => 0; - return size; - } - - public override unsafe void Read(PEImageReader reader) + /// + public override void Read(PEImageReader reader) { reader.Position = Position; - var size = (int)Size; - - var array = new byte[size]; // Ideally would be nice to have this coming from ArrayPool with a ref counting - var buffer = array.AsMemory(0, (int)size); - int read = reader.Read(buffer.Span); - if (read != size) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidEndOfStream, $"Unable to read the full content of the BaseRelocation directory. Expected {size} bytes, but read {read} bytes"); - return; - } + var size = (long)Size; - int blockIndex = 0; - while (buffer.Length > 0) + while (size > 0) { - var location = MemoryMarshal.Read(buffer.Span); - buffer = buffer.Slice(sizeof(ImageBaseRelocation)); - - if (!reader.File.TryFindSection(location.PageRVA, out var section)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section containing the virtual address {location.PageRVA} in block #{blockIndex}"); - continue; - } - - var sizeOfRelocations = (int)location.SizeOfBlock - sizeof(ImageBaseRelocation); - - // Create a block - var block = new PEBaseRelocationBlock(new PESectionLink(section, (uint)(location.PageRVA - section.RVA))) + var block = new PEBaseRelocationBlock() { - BlockBuffer = buffer.Slice(0, sizeOfRelocations) + Position = reader.Position }; - Blocks.Add(block); - - // Move to the next block - buffer = buffer.Slice(sizeOfRelocations); - blockIndex++; - } - - // Update the header size - HeaderSize = ComputeHeaderSize(reader); - } - internal override void Bind(PEImageReader reader) - { - foreach (var block in Blocks) - { - block.ReadAndBind(reader); - } + block.Read(reader); - HeaderSize = ComputeHeaderSize(reader); - } + size -= (uint)block.Size; - public override void Write(PEImageWriter writer) - { - ImageBaseRelocation rawBlock = default; - foreach (var block in Blocks) - { - rawBlock.PageRVA = block.SectionLink.RVA(); - rawBlock.SizeOfBlock = block.CalculateSizeOf(); + // Add the block to the content + Content.Add(block); } } - - protected override bool PrintMembers(StringBuilder builder) - { - if (base.PrintMembers(builder)) - { - builder.Append(", "); - } - - builder.Append($"Blocks[{Blocks.Count}]"); - return true; - } - - private struct ImageBaseRelocation - { -#pragma warning disable CS0649 - public RVA PageRVA; - public uint SizeOfBlock; - } -} \ No newline at end of file +} diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs index 06b51ea..0fb3b3e 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs @@ -134,7 +134,7 @@ public override unsafe void Read(PEImageReader reader) break; case Machine.Arm: case Machine.Arm64: - ReadEntriesArm(MemoryMarshal.Cast(span)); + ReadEntriesARM(MemoryMarshal.Cast(span)); break; } } @@ -197,7 +197,7 @@ internal override void Bind(PEImageReader reader) entryX86.EndAddress = new PESectionDataLink(endAddressSectionData, (uint)entryX86.EndAddress.RVO - endAddressSectionData.RVA); entryX86.UnwindInfoAddress = new PESectionDataLink(unwindInfoAddressSectionData, (uint)entryX86.UnwindInfoAddress.RVO - unwindInfoAddressSectionData.RVA); } - else if (entry is PEExceptionFunctionEntryArm entryARM) + else if (entry is PEExceptionFunctionEntryARM entryARM) { if (!peFile.TryFindContainerByRVA((RVA)(uint)entryARM.BeginAddress.RVO, out var beginAddressContainer)) { @@ -230,7 +230,7 @@ public override void Write(PEImageWriter writer) break; case Machine.Arm: case Machine.Arm64: - WriteArm(writer); + WriteARM(writer); break; default: // We don't write the exception directory for other architectures @@ -240,37 +240,43 @@ public override void Write(PEImageWriter writer) private void WriteX86(PEImageWriter writer) { - foreach (var entry in Entries) + using var tempSpan = TempSpan.Create(Entries.Count, out var span); + + for (var i = 0; i < Entries.Count; i++) { + var entry = Entries[i]; var entryX86 = (PEExceptionFunctionEntryX86)entry; - writer.Write(new RawExceptionFunctionEntryX86 - { - BeginAddress = (uint)entryX86.BeginAddress.RVA(), - EndAddress = (uint)entryX86.EndAddress.RVA(), - UnwindInfoAddress = (uint)entryX86.UnwindInfoAddress.RVA() - }); + ref var rawEntry = ref span[i]; + rawEntry.BeginAddress = (uint)entryX86.BeginAddress.RVA(); + rawEntry.EndAddress = (uint)entryX86.EndAddress.RVA(); + rawEntry.UnwindInfoAddress = (uint)entryX86.UnwindInfoAddress.RVA(); } + + writer.Write(tempSpan); } - private void WriteArm(PEImageWriter writer) + private void WriteARM(PEImageWriter writer) { - foreach (var entry in Entries) + using var tempSpan = TempSpan.Create(Entries.Count, out var span); + + for (var i = 0; i < Entries.Count; i++) { - var entryARM = (PEExceptionFunctionEntryArm)entry; - writer.Write(new RawExceptionFunctionEntryARM - { - BeginAddress = (uint)entryARM.BeginAddress.RVA(), - UnwindData = entryARM.UnwindData - }); + var entry = Entries[i]; + var entryArm = (PEExceptionFunctionEntryARM)entry; + ref var rawEntry = ref span[i]; + rawEntry.BeginAddress = (uint)entryArm.BeginAddress.RVA(); + rawEntry.UnwindData = entryArm.UnwindData; } + + writer.Write(tempSpan); } - private void ReadEntriesArm(Span rawEntries) + private void ReadEntriesARM(Span rawEntries) { foreach (ref var rawEntry in rawEntries) { // Create entries with links to data but encode the RVO as the RVA until we bind it to the actual section data - var entry = new PEExceptionFunctionEntryArm( + var entry = new PEExceptionFunctionEntryARM( new PESectionDataLink(PEStreamSectionData.Empty, (RVO)(uint)rawEntry.BeginAddress), rawEntry.UnwindData ); diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryArm.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryArm.cs index 91efda9..428b483 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryArm.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryArm.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. @@ -7,14 +7,14 @@ namespace LibObjectFile.PE; /// /// Represents an ARM exception function entry in a Portable Executable (PE) file. /// -public sealed class PEExceptionFunctionEntryArm : PEExceptionFunctionEntry +public sealed class PEExceptionFunctionEntryARM : PEExceptionFunctionEntry { /// - /// Initializes a new instance of the class with the specified begin address and unwind data. + /// Initializes a new instance of the class with the specified begin address and unwind data. /// /// The begin address of the exception function entry. /// The unwind data. - public PEExceptionFunctionEntryArm(PESectionDataLink beginAddress, uint unwindData) : base(beginAddress) + public PEExceptionFunctionEntryARM(PESectionDataLink beginAddress, uint unwindData) : base(beginAddress) { UnwindData = unwindData; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceData.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceData.cs index 643aa0f..505d096 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceData.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceData.cs @@ -2,6 +2,8 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using System; + namespace LibObjectFile.PE; /// @@ -14,4 +16,13 @@ public PEResourceData() RequiredPositionAlignment = 4; RequiredSizeAlignment = 4; } + + /// + protected override void ValidateParent(ObjectElement parent) + { + if (parent is not PEResourceDirectory) + { + throw new ArgumentException($"Invalid parent type {parent.GetType().FullName}. Expecting a parent of type {typeof(PEResourceDirectory).FullName}"); + } + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs index 0d0042b..a54ccb3 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs @@ -52,6 +52,11 @@ public PEResourceDataEntry(Encoding? codePage, PEResourceData data) /// public PEResourceData Data { get; set; } + /// + /// Gets or sets the reserved field. + /// + public uint Reserved { get; set; } + protected override bool PrintMembers(StringBuilder builder) { if (base.PrintMembers(builder)) @@ -83,6 +88,7 @@ internal override unsafe void Read(in ReaderContext context) } CodePage = rawDataEntry.CodePage != 0 ? Encoding.GetEncoding((int)rawDataEntry.CodePage) : null; + Reserved = rawDataEntry.Reserved; var peFile = context.Reader.File; if (!peFile.TryFindSection(rawDataEntry.OffsetToData, out var section)) @@ -98,15 +104,14 @@ internal override unsafe void Read(in ReaderContext context) }; } - internal override void Write(in WriterContext context) => Write(context.Writer); - public override void Write(PEImageWriter writer) { var rawDataEntry = new RawImageResourceDataEntry { - CodePage = (uint)(CodePage?.CodePage ?? 0), OffsetToData = Data.RVA, Size = (uint)Data.Size, + CodePage = (uint)(CodePage?.CodePage ?? 0), + Reserved = Reserved, }; writer.Write(rawDataEntry); diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs index ccbf631..462bfc7 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs @@ -125,8 +125,5 @@ internal override IEnumerable CollectImplicitSectionDataList() } /// - public override void Write(PEImageWriter writer) - { - Root.Write(writer); - } + public override void Write(PEImageWriter writer) => Root.Write(writer); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs index 6b4f781..54e9074 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs @@ -123,45 +123,50 @@ internal override unsafe void Read(in ReaderContext context) } } - internal override unsafe void Write(in WriterContext context) + public override unsafe void Write(PEImageWriter writer) { var size = Size; Debug.Assert(size == CalculateSize()); + var directory = (PEResourceDirectory)Parent!; - using var tempSpan = TempSpan.Create((int)size, out var span); + // Write the content directory + var byNames = CollectionsMarshal.AsSpan(ByNames); + var byIds = CollectionsMarshal.AsSpan(ByIds); + { + using var tempSpan = TempSpan.Create((int)size, out var span); + ref var rawResourceDirectory = ref Unsafe.As(ref MemoryMarshal.GetReference(span)); - ref var rawResourceDirectory = ref Unsafe.As(ref MemoryMarshal.GetReference(span)); + rawResourceDirectory.Characteristics = Characteristics; + rawResourceDirectory.TimeDateStamp = (uint)(TimeDateStamp - DateTime.UnixEpoch).TotalSeconds; + rawResourceDirectory.MajorVersion = MajorVersion; + rawResourceDirectory.MinorVersion = MinorVersion; + rawResourceDirectory.NumberOfNamedEntries = (ushort)ByNames.Count; + rawResourceDirectory.NumberOfIdEntries = (ushort)ByIds.Count; - rawResourceDirectory.Characteristics = Characteristics; - rawResourceDirectory.TimeDateStamp = (uint)(TimeDateStamp - DateTime.UnixEpoch).TotalSeconds; - rawResourceDirectory.MajorVersion = MajorVersion; - rawResourceDirectory.MinorVersion = MinorVersion; - rawResourceDirectory.NumberOfNamedEntries = (ushort)ByNames.Count; - rawResourceDirectory.NumberOfIdEntries = (ushort)ByIds.Count; + var rawEntries = MemoryMarshal.Cast(span.Slice(sizeof(RawImageResourceDirectory), (ByNames.Count + ByIds.Count) * sizeof(RawImageResourceDirectoryEntry))); - var rawEntries = MemoryMarshal.Cast(span.Slice(sizeof(RawImageResourceDirectory), (ByNames.Count + ByIds.Count) * sizeof(RawImageResourceDirectoryEntry))); + var directoryPosition = directory.Position; - var directoryPosition = context.Directory.Position; + for (int i = 0; i < byNames.Length; i++) + { + ref var rawEntry = ref rawEntries[i]; + var entry = byNames[i]; - var byNames = CollectionsMarshal.AsSpan(ByNames); - for (int i = 0; i < byNames.Length; i++) - { - ref var rawEntry = ref rawEntries[i]; - var entry = byNames[i]; + rawEntry.NameOrId = IMAGE_RESOURCE_NAME_IS_STRING | (uint)(entry.Name.Position - directoryPosition); + rawEntry.OffsetToDataOrDirectoryEntry = (uint)(entry.Entry.Position - directoryPosition); + } - rawEntry.NameOrId = IMAGE_RESOURCE_NAME_IS_STRING | (uint)(entry.Name.Position - directoryPosition); - rawEntry.OffsetToDataOrDirectoryEntry = (uint)(entry.Entry.Position - directoryPosition); - } - - var byIds = CollectionsMarshal.AsSpan(ByIds); - for (int i = 0; i < byIds.Length; i++) - { - ref var rawEntry = ref rawEntries[byNames.Length + i]; - var entry = byIds[i]; + for (int i = 0; i < byIds.Length; i++) + { + ref var rawEntry = ref rawEntries[byNames.Length + i]; + var entry = byIds[i]; + + rawEntry.NameOrId = (uint)entry.Id.Value; + rawEntry.OffsetToDataOrDirectoryEntry = IMAGE_RESOURCE_DATA_IS_DIRECTORY | (uint)(entry.Entry.Position - directoryPosition); + } - rawEntry.NameOrId = (uint)entry.Id.Value; - rawEntry.OffsetToDataOrDirectoryEntry = IMAGE_RESOURCE_DATA_IS_DIRECTORY | (uint)(entry.Entry.Position - directoryPosition); + writer.Write(span); } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs index d1b829d..f17ea7c 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs @@ -2,6 +2,7 @@ // 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; @@ -15,9 +16,14 @@ public abstract class PEResourceEntry : PESectionData internal abstract void Read(in ReaderContext context); - internal abstract void Write(in WriterContext context); - internal readonly record struct ReaderContext(PEImageReader Reader, PEResourceDirectory Directory, List Strings, List Entries); - internal readonly record struct WriterContext(PEImageWriter Writer, PEResourceDirectory Directory); + /// + protected override void ValidateParent(ObjectElement parent) + { + if (parent is not PEResourceDirectory) + { + throw new ArgumentException($"Invalid parent type {parent.GetType().FullName}. Expecting a parent of type {typeof(PEResourceDirectory).FullName}"); + } + } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs index 38354d0..f39d56c 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs @@ -81,4 +81,13 @@ protected override bool PrintMembers(StringBuilder builder) builder.Append($"Text = {Text}"); return true; } + + /// + protected override void ValidateParent(ObjectElement parent) + { + if (parent is not PEResourceDirectory) + { + throw new ArgumentException($"Invalid parent type {parent.GetType().FullName}. Expecting a parent of type {typeof(PEResourceDirectory).FullName}"); + } + } } diff --git a/src/LibObjectFile/PE/Internal/RawImageBaseRelocation.cs b/src/LibObjectFile/PE/Internal/RawImageBaseRelocation.cs deleted file mode 100644 index 500e0f3..0000000 --- a/src/LibObjectFile/PE/Internal/RawImageBaseRelocation.cs +++ /dev/null @@ -1,37 +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.Internal; - -/// -/// Structure representing a base relocation. -/// -internal struct RawImageBaseRelocation -{ - public const ushort MaxVirtualOffset = (1 << 12) - 1; - private const ushort TypeMask = unchecked((ushort)~MaxVirtualOffset); - private const ushort VirtualOffsetMask = MaxVirtualOffset; - - private readonly ushort _value; - - public RawImageBaseRelocation(ushort value) - { - _value = value; - } - - /// - /// Gets a value indicating whether the base relocation is zero (used for padding). - /// - public bool IsZero => _value == 0; - - /// - /// Gets the type of the base relocation. - /// - public PEBaseRelocationType Type => (PEBaseRelocationType)(_value & TypeMask); - - /// - /// Gets the virtual offset of the base relocation relative to the offset of the associated . - /// - public ushort OffsetInBlockPart => (ushort)(_value & VirtualOffsetMask); -} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index e7e1cb4..dda6840 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -197,6 +197,9 @@ private static void PrintSectionData(PEFile file, PESectionData data, ref TextWr case PEBaseRelocationDirectory peBaseRelocationDirectory: Print(peBaseRelocationDirectory, ref writer); break; + case PEBaseRelocationBlock baseRelocationBlock: + Print(baseRelocationBlock, ref writer); + break; case PEBoundImportDirectory peBoundImportDirectory: Print(peBoundImportDirectory, ref writer); break; @@ -305,35 +308,42 @@ private static void Print(PEDebugSectionDataRSDS data, ref TextWriterIndenter wr private static void Print(PEBaseRelocationDirectory data, ref TextWriterIndenter writer) { - var peFile = data.GetPEFile()!; - foreach (var block in data.Blocks) + } + + private static void Print(PEBaseRelocationBlock block, ref TextWriterIndenter writer) + { + var pageRVA = block.SectionLink.RVA(); + writer.WriteLine($"Block {pageRVA} Relocations[{block.Relocations.Count}]"); + + var peFile = block.GetPEFile()!; + + writer.Indent(); + for (var i = 0; i < block.Relocations.Count; i++) { - var pageRVA = block.SectionLink.RVA(); - writer.WriteLine($"Block {pageRVA} Relocations[{block.Relocations.Count}]"); + var reloc = block.Relocations[i]; + var relocRVA = block.GetRVA(reloc); + var offsetInPage = relocRVA - pageRVA; - writer.Indent(); - for (var i = 0; i < block.Relocations.Count; i++) - { - var reloc = block.Relocations[i]; - var relocRVA = reloc.RVA(); - var offsetInPage = relocRVA - pageRVA; - if (reloc.Type == PEBaseRelocationType.Dir64) - { - writer.WriteLine($"[{i:000}] {reloc.Type} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA} (0x{reloc.ReadAddress(peFile):X16}), SectionData = {{ {PELink(reloc.Container)} }}"); - } - else if (reloc.Type == PEBaseRelocationType.Absolute) - { - writer.WriteLine($"[{i:000}] {reloc.Type} Zero padding"); - } - else - { - writer.WriteLine($"[{i:000}] {reloc.Type} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA}, SectionData = {{ {PELink(reloc.Container)} }}"); - } - } + var section = block.SectionLink.Container!; + section.TryFindSectionData(relocRVA, out var sectionData); + - writer.Unindent(); + if (reloc.Type == PEBaseRelocationType.Dir64) + { + writer.WriteLine($"[{i:000}] {reloc.Type} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA} (0x{block.ReadAddress(peFile, reloc):X16}), SectionData = {{ {PELink(sectionData)} }}"); + } + else if (reloc.Type == PEBaseRelocationType.Absolute) + { + writer.WriteLine($"[{i:000}] {reloc.Type} Zero padding"); + } + else + { + writer.WriteLine($"[{i:000}] {reloc.Type} Offset = 0x{offsetInPage:X4}, RVA = {relocRVA}, SectionData = {{ {PELink(sectionData)} }}"); + } } + + writer.Unindent(); } private static void Print(PEBoundImportDirectory data, ref TextWriterIndenter writer) @@ -391,7 +401,7 @@ private static void Print(PEExceptionDirectory data, ref TextWriterIndenter writ var entry = data.Entries[i]; switch (entry) { - case PEExceptionFunctionEntryArm entryArm: + case PEExceptionFunctionEntryARM entryArm: writer.WriteLine($"[{i}] Begin = {entry.BeginAddress.RVA()}"); writer.WriteLine($"[{i}] UnwindData = 0x{entryArm.UnwindData:X}"); break; From 6b5147bc5ac5c6c337cb9c601c343b1b57a480ca Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sun, 29 Sep 2024 17:31:39 +0200 Subject: [PATCH 75/87] Fix PE writer, first version working in unit tests --- src/LibObjectFile.Tests/PE/PEReaderTests.cs | 20 ++++++++++- .../PE/DataDirectory/PEBaseRelocationBlock.cs | 20 +++++++++++ .../PE/DataDirectory/PEClrMetadata.cs | 16 ++++++--- .../DataDirectory/PECompositeSectionData.cs | 18 ++++++++++ .../PE/DataDirectory/PEDebugDirectory.cs | 13 ++++++-- .../DataDirectory/PEDebugSectionDataRSDS.cs | 33 ++++++++++++++++++- .../PE/DataDirectory/PEDirectoryTable.cs | 12 +++++-- .../PE/DataDirectory/PEExportAddressTable.cs | 17 +++++----- .../PE/DataDirectory/PEExportNameTable.cs | 12 +++++-- .../PE/DataDirectory/PEExportOrdinalTable.cs | 8 ++++- .../PE/DataDirectory/PEImportFunctionTable.cs | 28 ++++++---------- .../PE/DataDirectory/PELoadConfigDirectory.cs | 10 ++---- .../PE/DataDirectory/PEResourceDirectory.cs | 2 +- .../DataDirectory/PEResourceDirectoryEntry.cs | 10 +++++- .../PE/DataDirectory/PEThunkAddressTable.cs | 6 +--- src/LibObjectFile/PE/PEFile.Write.cs | 27 +++++++++++---- src/LibObjectFile/PE/PEStreamExtraData.cs | 3 +- 17 files changed, 190 insertions(+), 65 deletions(-) diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index fcaa4b3..b1e4714 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -23,7 +23,8 @@ public partial class PEReaderTests public async Task TestPrinter(string name) { - await using var stream = File.OpenRead(Path.Combine(AppContext.BaseDirectory, "PE", name)); + var sourceFile = Path.Combine(AppContext.BaseDirectory, "PE", name); + await using var stream = File.OpenRead(sourceFile); var peImage = PEFile.Read(stream); var afterReadWriter = new StringWriter(); peImage.Print(afterReadWriter); @@ -45,6 +46,23 @@ public async Task TestPrinter(string name) TestContext.WriteLine("Error while verifying UpdateLayout"); await Verifier.Verify(afterUpdateText).UseParameters(name).DisableRequireUniquePrefix(); } + + // Read in input as raw bytes + stream.Position = 0; + var inputBuffer = new byte[stream.Length]; + stream.ReadExactly(inputBuffer); + + // Write the PE back to a byte buffer + var output = new MemoryStream(); + peImage.Write(output); + output.Position = 0; + var outputBuffer = output.ToArray(); + + //await Verifier.Verify(outputBuffer, sourceFile sourceFile). + await File.WriteAllBytesAsync($"{sourceFile}.bak", outputBuffer); + + // Compare the input and output buffer + CollectionAssert.AreEqual(inputBuffer, outputBuffer); } [TestMethod] diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs index cb23b87..44c4ea4 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs @@ -152,6 +152,26 @@ public override unsafe void Read(PEImageReader reader) Debug.Assert(Size == block.SizeOfBlock); } + public override unsafe void Write(PEImageWriter writer) + { + var block = new ImageBaseRelocation + { + PageRVA = SectionLink.RVA(), + SizeOfBlock = (uint)Size + }; + + writer.Write(block); + + var span = CollectionsMarshal.AsSpan(Relocations); + var spanBytes = MemoryMarshal.AsBytes(span); + writer.Write(spanBytes); + + if ((Relocations.Count & 1) != 0) + { + writer.WriteZero((int)sizeof(PEBaseRelocation)); + } + } + protected override bool PrintMembers(StringBuilder builder) { if (base.PrintMembers(builder)) diff --git a/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs b/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs index 8964545..80c72e8 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.cs @@ -6,22 +6,28 @@ namespace LibObjectFile.PE; +/// +/// Represents the CLR metadata directory in a PE file. +/// public sealed class PEClrMetadata : PEDataDirectory { + /// + /// Initializes a new instance of the class. + /// public PEClrMetadata() : base(PEDataDirectoryKind.ClrMetadata) { } - protected override uint ComputeHeaderSize(PELayoutContext context) - { - return 0; - } + /// + protected override uint ComputeHeaderSize(PELayoutContext context) => 0; + /// public override void Read(PEImageReader reader) { } + /// public override void Write(PEImageWriter writer) { } -} \ No newline at end of file +} diff --git a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs index 56895bc..bfe30b5 100644 --- a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs +++ b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using LibObjectFile.Collections; using LibObjectFile.Utils; @@ -86,7 +87,24 @@ internal void WriteHeaderAndContent(PEImageWriter writer) foreach (var table in Content) { + var position = writer.Position; + var alignment = table.GetRequiredPositionAlignment(writer.PEFile); + if (alignment > 1) + { + var zeroSize = AlignHelper.AlignUp(position, alignment) - (uint)position; + writer.WriteZero((int)zeroSize); + } + + Debug.Assert(table.Position == writer.Position); + table.Write(writer); + + alignment = table.GetRequiredSizeAlignment(writer.PEFile); + if (alignment > 1) + { + var zeroSize = AlignHelper.AlignUp((uint)table.Size, alignment) - table.Size; + writer.WriteZero((int)zeroSize); + } } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs index 48740ad..1b1eac0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs @@ -134,10 +134,11 @@ public override void Write(PEImageWriter writer) var entries = CollectionsMarshal.AsSpan(Entries); using var tempSpan = TempSpan.Create(entries.Length, out var rawEntries); - RawImageDebugDirectory rawEntry = default; for (var i = 0; i < entries.Length; i++) { var entry = entries[i]; + ref var rawEntry = ref rawEntries[i]; + rawEntry.Characteristics = entry.Characteristics; rawEntry.MajorVersion = entry.MajorVersion; rawEntry.MinorVersion = entry.MinorVersion; @@ -147,8 +148,8 @@ public override void Write(PEImageWriter writer) if (entry.SectionData is not null) { rawEntry.SizeOfData = (uint)entry.SectionData.Size; - rawEntry.AddressOfRawData = (uint)entry.SectionData.RVA; - rawEntry.PointerToRawData = 0; + rawEntry.AddressOfRawData = entry.SectionData.RVA; + rawEntry.PointerToRawData = (uint)entry.SectionData.Position; } else if (entry.ExtraData is not null) { @@ -156,6 +157,12 @@ public override void Write(PEImageWriter writer) rawEntry.AddressOfRawData = 0; rawEntry.PointerToRawData = (uint)entry.ExtraData.Position; } + else + { + rawEntry.SizeOfData = 0; + rawEntry.AddressOfRawData = 0; + rawEntry.PointerToRawData = 0; + } rawEntries[i] = rawEntry; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs index 899c3d2..71b2deb 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using LibObjectFile.Collections; @@ -18,6 +19,8 @@ namespace LibObjectFile.PE; [DebuggerDisplay("{ToString(),nq}")] public sealed class PEDebugSectionDataRSDS : PEDebugSectionData { + private const uint Signature = 0x53445352; + /// /// Initializes a new instance of the class. /// @@ -57,7 +60,7 @@ public override unsafe void Read(PEImageReader reader) } var signature = MemoryMarshal.Read(span); - if (signature != 0x53445352) + if (signature != Signature) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidDebugDataRSDSSignature, $"Invalid signature for PEDebugDataRSDS"); return; @@ -74,8 +77,36 @@ public override unsafe void Read(PEImageReader reader) Guid = MemoryMarshal.Read(span.Slice(4)); Age = MemoryMarshal.Read(span.Slice(sizeof(uint) + sizeof(Guid))); PdbPath = System.Text.Encoding.UTF8.GetString(pdbPath.Slice(0, indexOfZero)); + + Debug.Assert(size == CalculateSize()); } + public override unsafe void Write(PEImageWriter writer) + { + var size = (int)Size; + using var tempSpan = TempSpan.Create(size, out var span); + + MemoryMarshal.Write(span, Signature); + span = span.Slice(sizeof(uint)); + MemoryMarshal.Write(span, Guid); + span = span.Slice(sizeof(Guid)); + MemoryMarshal.Write(span, Age); + span = span.Slice(sizeof(uint)); + int written = Encoding.UTF8.GetBytes(PdbPath, span); + span[written] = 0; + span.Slice(written + 1); + + writer.Write(tempSpan.AsBytes); + } + + public override void UpdateLayout(PELayoutContext layoutContext) + { + Size = CalculateSize(); + } + + private unsafe uint CalculateSize() + => (uint)(sizeof(uint) + sizeof(Guid) + sizeof(uint) + Encoding.UTF8.GetByteCount(PdbPath) + 1); + /// protected override bool PrintMembers(StringBuilder builder) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs index b157cfe..cca3e2a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; +using LibObjectFile.Collections; using LibObjectFile.PE.Internal; namespace LibObjectFile.PE; @@ -191,17 +192,22 @@ internal void Set(int index, PEObjectBase? directory) internal unsafe void Write(PEImageWriter writer, ref uint position) { + using var tempSpan = TempSpan.Create(stackalloc byte[16 * sizeof(RawImageDataDirectory)], Count, out var span); + span.Clear(); + for (int i = 0; i < Count; i++) { - RawImageDataDirectory 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; + ref var rawEntry = ref span[i]; + rawEntry.RVA = entry is PEDataDirectory dataDirectory ? dataDirectory.RVA : (uint)entry.Position; + rawEntry.Size = (uint)entry.Size; } } + writer.Write(tempSpan); + position += (uint)(Count * sizeof(RawImageDataDirectory)); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index 71c0c08..8dd1fe7 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -2,17 +2,10 @@ // 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.ComponentModel; -using System.Diagnostics; using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Xml.Linq; using LibObjectFile.Collections; using LibObjectFile.Diagnostics; -using static System.Collections.Specialized.BitVector32; namespace LibObjectFile.PE; @@ -87,6 +80,14 @@ public override unsafe void Read(PEImageReader reader) public override void Write(PEImageWriter writer) { - throw new NotImplementedException(); + using var tempSpan = TempSpan.Create(Values.Count, out var spanRva); + + for (int i = 0; i < Values.Count; i++) + { + var value = Values[i]; + spanRva[i] = value.IsForwarderRVA ? value.ForwarderRVA.RVA() : value.ExportRVA.RVA(); + } + + writer.Write(tempSpan); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs index b2ee841..e5fddc6 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs @@ -2,8 +2,6 @@ // 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.Collections; @@ -66,6 +64,14 @@ public override unsafe void Read(PEImageReader reader) public override void Write(PEImageWriter writer) { - throw new NotImplementedException(); + using var tempSpan = TempSpan.Create(Values.Count, out var spanRva); + + for (int i = 0; i < Values.Count; i++) + { + var value = Values[i]; + spanRva[i] = value.RVA(); + } + + writer.Write(tempSpan.AsBytes); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs index f2fe97d..7333281 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.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. @@ -42,4 +42,10 @@ public override void Read(PEImageReader reader) return; } } + + public override void Write(PEImageWriter writer) + { + var span = CollectionsMarshal.AsSpan(Values); + writer.Write(MemoryMarshal.AsBytes(span)); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index ac608c0..7dfc4db 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -134,7 +134,7 @@ public void Write(PEImageWriter writer) private unsafe void Write32(PEImageWriter writer) { - using var tempSpan = TempSpan.Create(Entries.Count, out var span); + using var tempSpan = TempSpan.Create(Entries.Count + 1, out var span); for (var i = 0; i < Entries.Count; i++) { @@ -151,24 +151,16 @@ private unsafe void Write32(PEImageWriter writer) 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.HintName.RVA(); - 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 + using var tempSpan = TempSpan.Create(Entries.Count + 1, out var span); + for (var i = 0; i < Entries.Count; i++) { - ArrayPool.Shared.Return(buffer); + var entry = Entries[i]; + var va = entry.HintName.RVA(); + 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)); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs index 64ce6a1..5e4a634 100644 --- a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.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. @@ -21,12 +21,8 @@ private protected PELoadConfigDirectory(int minSize) : base(PEDataDirectoryKind. { } - /// - /// Gets the config size. - /// - /// - /// Use to set the config size. - /// + + /// public sealed override int RawDataSize => (int)MemoryMarshal.Read(RawData); /// diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs index 462bfc7..6d69aab 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs @@ -86,7 +86,7 @@ public override void Read(PEImageReader reader) // Read the data resourceData.Read(reader); - isFirstDataEntry = true; + isFirstDataEntry = false; } HeaderSize = ComputeHeaderSize(reader); diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs index 54e9074..8aae146 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs @@ -155,6 +155,10 @@ public override unsafe void Write(PEImageWriter writer) rawEntry.NameOrId = IMAGE_RESOURCE_NAME_IS_STRING | (uint)(entry.Name.Position - directoryPosition); rawEntry.OffsetToDataOrDirectoryEntry = (uint)(entry.Entry.Position - directoryPosition); + if (entry.Entry is PEResourceDirectoryEntry) + { + rawEntry.OffsetToDataOrDirectoryEntry |= IMAGE_RESOURCE_DATA_IS_DIRECTORY; + } } for (int i = 0; i < byIds.Length; i++) @@ -163,7 +167,11 @@ public override unsafe void Write(PEImageWriter writer) var entry = byIds[i]; rawEntry.NameOrId = (uint)entry.Id.Value; - rawEntry.OffsetToDataOrDirectoryEntry = IMAGE_RESOURCE_DATA_IS_DIRECTORY | (uint)(entry.Entry.Position - directoryPosition); + rawEntry.OffsetToDataOrDirectoryEntry = (uint)(entry.Entry.Position - directoryPosition); + if (entry.Entry is PEResourceDirectoryEntry) + { + rawEntry.OffsetToDataOrDirectoryEntry |= IMAGE_RESOURCE_DATA_IS_DIRECTORY; + } } writer.Write(span); diff --git a/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs index aa8c67d..3a0b91a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.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. @@ -92,8 +92,6 @@ private protected void Read64(PEImageReader reader, List entries) private protected void Write32(PEImageWriter writer, List entries) { - writer.Position = Position; - var span = CollectionsMarshal.AsSpan(entries); var bytes = MemoryMarshal.AsBytes(span); writer.Write(bytes); @@ -102,8 +100,6 @@ private protected void Write32(PEImageWriter writer, List entries) private protected void Write64(PEImageWriter writer, List entries) { - writer.Position = Position; - var span = CollectionsMarshal.AsSpan(entries); var bytes = MemoryMarshal.AsBytes(span); writer.Write(bytes); diff --git a/src/LibObjectFile/PE/PEFile.Write.cs b/src/LibObjectFile/PE/PEFile.Write.cs index baf029f..9e206f9 100644 --- a/src/LibObjectFile/PE/PEFile.Write.cs +++ b/src/LibObjectFile/PE/PEFile.Write.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using System.Diagnostics; using System.IO; using System.Numerics; using System.Reflection.PortableExecutable; @@ -10,6 +11,7 @@ using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; using LibObjectFile.Utils; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace LibObjectFile.PE; @@ -64,6 +66,11 @@ public override unsafe void Write(PEImageWriter writer) var context = new PELayoutContext(this, writer.Diagnostics); UpdateLayout(context); + // Set the size of the stream before writing to it. + // It will help to avoid resizing the stream + writer.Stream.SetLength((uint)Size); + writer.Stream.Position = 0; + var position = 0U; // Update DOS header @@ -93,7 +100,6 @@ public override unsafe void Write(PEImageWriter writer) writer.Write(CoffHeader); position += (uint)sizeof(PECoffHeader); - if (IsPE32) { RawImageOptionalHeader32 header32; @@ -102,8 +108,9 @@ public override unsafe void Write(PEImageWriter writer) header32.Common2 = OptionalHeader.OptionalHeaderCommonPart2; header32.Size32 = OptionalHeader.OptionalHeaderSize32; header32.Common3 = OptionalHeader.OptionalHeaderCommonPart3; - writer.Write(header32); - position += (uint)sizeof(RawImageOptionalHeader32); + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref header32, 1)).Slice(0, RawImageOptionalHeader32.MinimumSize); + writer.Write(span); + position += (uint)span.Length; } else { @@ -113,11 +120,11 @@ public override unsafe void Write(PEImageWriter writer) header64.Common2 = OptionalHeader.OptionalHeaderCommonPart2; header64.Size64 = OptionalHeader.OptionalHeaderSize64; header64.Common3 = OptionalHeader.OptionalHeaderCommonPart3; - writer.Write(header64); - position += (uint)sizeof(RawImageOptionalHeader64); + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref header64, 1)).Slice(0, RawImageOptionalHeader64.MinimumSize); + writer.Write(span); + position += (uint)span.Length; } - - + // Update directories Directories.Write(writer, ref position); @@ -154,6 +161,9 @@ public override unsafe void Write(PEImageWriter writer) for (var i = 0; i < span.Length; i++) { var data = span[i]; + + Debug.Assert(data.Position == writer.Position); + 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}"); @@ -174,6 +184,9 @@ public override unsafe void Write(PEImageWriter writer) zeroSize = (int)(AlignHelper.AlignUp(position, writer.PEFile.OptionalHeader.FileAlignment) - position); writer.WriteZero(zeroSize); + position += (uint)zeroSize; + + Debug.Assert(position == writer.Position); } // Data after sections diff --git a/src/LibObjectFile/PE/PEStreamExtraData.cs b/src/LibObjectFile/PE/PEStreamExtraData.cs index 0b270a3..28bba91 100644 --- a/src/LibObjectFile/PE/PEStreamExtraData.cs +++ b/src/LibObjectFile/PE/PEStreamExtraData.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. @@ -60,6 +60,7 @@ public override void Read(PEImageReader reader) public override void Write(PEImageWriter writer) { + Stream.Position = 0; Stream.CopyTo(writer.Stream); } } \ No newline at end of file From f7d0c272256dc4fc7e8e12487a87da4b89e66f14 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 30 Sep 2024 19:11:05 +0200 Subject: [PATCH 76/87] Add full support for PE writing --- src/Directory.Packages.props | 1 + src/LibObjectFile.Tests/PE/PEReaderTests.cs | 83 ++++- ..._name=NativeConsole2Win64.exe.verified.txt | 64 ++-- ...r_name=NativeConsoleWin64.exe.verified.txt | 6 +- ...r_name=NativeLibraryWin64.dll.verified.txt | 6 +- src/LibObjectFile/Ar/ArArchiveFile.cs | 2 +- src/LibObjectFile/Ar/ArBinaryFile.cs | 2 +- src/LibObjectFile/Ar/ArElfFile.cs | 2 +- src/LibObjectFile/Ar/ArLongNamesTable.cs | 2 +- src/LibObjectFile/Ar/ArSymbolTable.cs | 2 +- .../Diagnostics/DiagnosticBag.cs | 17 +- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 1 + .../Diagnostics/DiagnosticMessage.cs | 18 +- src/LibObjectFile/Dwarf/DwarfAbbreviation.cs | 2 +- .../Dwarf/DwarfAbbreviationItem.cs | 2 +- .../Dwarf/DwarfAbbreviationTable.cs | 2 +- .../Dwarf/DwarfAddressRangeTable.cs | 2 +- src/LibObjectFile/Dwarf/DwarfAttribute.cs | 2 +- src/LibObjectFile/Dwarf/DwarfDIE.cs | 2 +- src/LibObjectFile/Dwarf/DwarfExpression.cs | 2 +- src/LibObjectFile/Dwarf/DwarfFile.cs | 2 +- src/LibObjectFile/Dwarf/DwarfInfoSection.cs | 2 +- src/LibObjectFile/Dwarf/DwarfLine.cs | 2 +- .../Dwarf/DwarfLineProgramTable.cs | 2 +- src/LibObjectFile/Dwarf/DwarfLineSection.cs | 2 +- src/LibObjectFile/Dwarf/DwarfLineSequence.cs | 2 +- src/LibObjectFile/Dwarf/DwarfLocationList.cs | 2 +- .../Dwarf/DwarfLocationListEntry.cs | 2 +- .../Dwarf/DwarfLocationSection.cs | 2 +- src/LibObjectFile/Dwarf/DwarfOperation.cs | 2 +- src/LibObjectFile/Dwarf/DwarfStringTable.cs | 2 +- src/LibObjectFile/Dwarf/DwarfUnit.cs | 2 +- src/LibObjectFile/Elf/ElfObjectFile.cs | 2 +- src/LibObjectFile/Elf/ElfSegment.cs | 2 +- .../Elf/Sections/ElfAlignedShadowSection.cs | 2 +- .../Elf/Sections/ElfBinarySection.cs | 2 +- .../Elf/Sections/ElfBinaryShadowSection.cs | 2 +- .../Elf/Sections/ElfNoteTable.cs | 4 +- .../Elf/Sections/ElfNullSection.cs | 2 +- .../Elf/Sections/ElfProgramHeaderTable.cs | 2 +- .../Elf/Sections/ElfRelocationTable.cs | 2 +- .../Elf/Sections/ElfStringTable.cs | 2 +- .../Elf/Sections/ElfSymbolTable.cs | 2 +- .../ElfSymbolTableSectionHeaderIndices.cs | 2 +- src/LibObjectFile/LibObjectFile.csproj | 2 + src/LibObjectFile/ObjectFileElement.cs | 5 + .../PE/DataDirectory/PEBaseRelocationBlock.cs | 2 +- .../DataDirectory/PECompositeSectionData.cs | 17 +- .../PE/DataDirectory/PEDataDirectory.cs | 8 +- .../DataDirectory/PEDebugSectionDataRSDS.cs | 25 +- .../DataDirectory/PEDelayImportDirectory.cs | 111 +++---- .../PEDelayImportDirectoryEntry.cs | 6 +- .../PE/DataDirectory/PEDirectoryTable.cs | 61 +--- .../PE/DataDirectory/PEExportAddressTable.cs | 12 +- .../PE/DataDirectory/PEExportDirectory.cs | 69 ++-- .../PE/DataDirectory/PEExportFunctionEntry.cs | 8 +- .../PE/DataDirectory/PEExportNameTable.cs | 2 +- .../PE/DataDirectory/PEExportOrdinalTable.cs | 4 +- .../PE/DataDirectory/PEImportAddressTable.cs | 2 +- .../PE/DataDirectory/PEImportDirectory.cs | 16 +- .../DataDirectory/PEImportDirectoryEntry.cs | 16 +- .../PE/DataDirectory/PEImportFunctionEntry.cs | 38 ++- .../PE/DataDirectory/PEImportFunctionTable.cs | 33 +- .../PE/DataDirectory/PEImportLookupTable.cs | 2 +- .../PE/DataDirectory/PELoadConfigDirectory.cs | 17 +- .../DataDirectory/PELoadConfigDirectory32.cs | 4 +- .../DataDirectory/PELoadConfigDirectory64.cs | 4 +- .../PE/DataDirectory/PERawDataDirectory.cs | 25 +- .../PE/DataDirectory/PEResourceDataEntry.cs | 22 +- .../PE/DataDirectory/PEResourceDirectory.cs | 23 +- .../DataDirectory/PEResourceDirectoryEntry.cs | 21 +- .../PE/DataDirectory/PEResourceEntry.cs | 33 +- .../PE/DataDirectory/PEResourceString.cs | 15 +- .../DataDirectory/PESectionVirtualSizeMode.cs | 24 ++ .../PESecurityCertificateDirectory.cs | 38 ++- .../PE/DataDirectory/PEThunkAddressTable.cs | 2 +- .../PE/DataDirectory/PETlsDirectory32.cs | 8 +- .../PE/DataDirectory/PETlsDirectory64.cs | 8 +- .../PE/Internal/RawImportFunctionEntry64.cs | 4 +- src/LibObjectFile/PE/PEExtraData.cs | 4 +- src/LibObjectFile/PE/PEFile.Read.cs | 299 ++++++++++-------- src/LibObjectFile/PE/PEFile.Write.cs | 39 ++- src/LibObjectFile/PE/PEFile.cs | 73 ++++- src/LibObjectFile/PE/PEImageReaderOptions.cs | 4 +- src/LibObjectFile/PE/PEModuleHandleLink.cs | 4 +- src/LibObjectFile/PE/PEObject.cs | 69 +++- src/LibObjectFile/PE/PEObjectBase.cs | 32 +- src/LibObjectFile/PE/PEObjectExtensions.cs | 35 +- src/LibObjectFile/PE/PEPrinter.cs | 6 +- src/LibObjectFile/PE/PESection.cs | 138 +++++--- src/LibObjectFile/PE/PEStreamExtraData.cs | 2 +- src/LibObjectFile/PE/PEStreamSectionData.cs | 3 +- src/LibObjectFile/PE/PEVisitorContext.cs | 4 +- src/LibObjectFile/Utils/AlignHelper.cs | 4 +- 94 files changed, 1091 insertions(+), 582 deletions(-) create mode 100644 src/LibObjectFile/PE/DataDirectory/PESectionVirtualSizeMode.cs diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index e1c43c9..0dfc2d1 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -6,6 +6,7 @@ + diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index b1e4714..23bb21b 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -3,6 +3,8 @@ // See the license.txt file in the project root for more information. using System; +using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Threading.Tasks; using LibObjectFile.Diagnostics; @@ -25,7 +27,7 @@ public async Task TestPrinter(string name) var sourceFile = Path.Combine(AppContext.BaseDirectory, "PE", name); await using var stream = File.OpenRead(sourceFile); - var peImage = PEFile.Read(stream); + var peImage = PEFile.Read(stream, new() { EnableStackTrace = true }); var afterReadWriter = new StringWriter(); peImage.Print(afterReadWriter); @@ -64,7 +66,86 @@ public async Task TestPrinter(string name) // Compare the input and output buffer CollectionAssert.AreEqual(inputBuffer, outputBuffer); } + + [DataTestMethod] + [DynamicData(nameof(GetWindowsExeAndDlls), DynamicDataSourceType.Method)] + public async Task TestWindows(string sourceFile) + { + if (!OperatingSystem.IsWindows()) + { + Assert.Inconclusive("This test can only run on Windows"); + return; + } + + TestContext.WriteLine($"Testing {sourceFile}"); + await using var stream = File.OpenRead(sourceFile); + var peImage = PEFile.Read(stream, new() { EnableStackTrace = true }); + + if (peImage.CoffHeader.PointerToSymbolTable != 0) + { + Assert.Inconclusive($"The file {sourceFile} contains a non supported symbol table"); + return; + } + + var sizeOfInitializedData = peImage.OptionalHeader.SizeOfInitializedData; + // Read in input as raw bytes + stream.Position = 0; + var inputBuffer = new byte[stream.Length]; + stream.ReadExactly(inputBuffer); + + peImage.UpdateLayout(new DiagnosticBag()); + + var newSizeOfInitializedData = peImage.OptionalHeader.SizeOfInitializedData; + + if (newSizeOfInitializedData != sizeOfInitializedData) + { + TestContext.WriteLine($"SizeOfInitializedData changed from {sizeOfInitializedData} to {newSizeOfInitializedData}. Trying to reuse old size"); + peImage.ForceSizeOfInitializedData = sizeOfInitializedData; + } + + + // Write the PE back to a byte buffer + var output = new MemoryStream(); + peImage.Write(output); + output.Position = 0; + var outputBuffer = output.ToArray(); + + if (!inputBuffer.AsSpan().SequenceEqual(outputBuffer)) + { + // Uncomment the following code to save the output file to compare it with the original file + //{ + // var dir = Path.Combine(AppContext.BaseDirectory, "Errors"); + // Directory.CreateDirectory(dir); + + // var sourceFileName = Path.GetFileName(sourceFile); + // var outputFileName = Path.Combine(dir, $"{sourceFileName}.new"); + + // await File.WriteAllBytesAsync(outputFileName, outputBuffer); + //} + + CollectionAssert.AreEqual(inputBuffer, outputBuffer); + } + } + + public static IEnumerable GetWindowsExeAndDlls() + { + if (!OperatingSystem.IsWindows()) + { + yield break; + } + + foreach (var file in Directory.EnumerateFiles(Environment.SystemDirectory, "*.exe", SearchOption.TopDirectoryOnly)) + { + yield return [file]; + } + + foreach (var file in Directory.EnumerateFiles(Environment.SystemDirectory, "*.dll", SearchOption.TopDirectoryOnly)) + { + yield return [file]; + } + } + [TestMethod] public async Task TestTinyExe97Bytes() { 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 e087f49..ba180a3 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 @@ -269,63 +269,57 @@ Sections [0] Attributes = 1 [0] DelayImportAddressTable RVA = 0x00005078 (PEBoundImportAddressTable64[1] -> .data) [0] DelayImportNameTable RVA = 0x00003BE8 (PEImportLookupTable[12] -> .rdata) - [0] BoundImportAddressTable RVA = 0x00003C20 (PEBoundImportAddressTable64[14] -> .rdata) - [0] UnloadDelayInformationTable null + [0] BoundImportAddressTable RVA = 0x3C20, PEStreamSectionData { RVA = 0x3C00, VirtualSize = 0x13C, Position = 0x2A00, Size = 0x13C }, Offset = 0x20 + [0] UnloadDelayInformationTable [12] PEImportLookupTable Position = 0x000029E8, Size = 0x00000018, RVA = 0x00003BE8, VirtualSize = 0x00000018 - [0] PEImportHintName { Hint = 0, Name = AnotherFunction } (RVA = 0x3C0E, PEStreamSectionData { RVA = 0x3C00, VirtualSize = 0x20, Position = 0x2A00, Size = 0x20 }, Offset = 0xE) - [1] PEImportHintName { Hint = 1, Name = HelloWorld } (RVA = 0x3C00, PEStreamSectionData { RVA = 0x3C00, VirtualSize = 0x20, Position = 0x2A00, Size = 0x20 }, Offset = 0x0) + [0] PEImportHintName { Hint = 0, Name = AnotherFunction } (RVA = 0x3C0E, PEStreamSectionData { RVA = 0x3C00, VirtualSize = 0x13C, Position = 0x2A00, Size = 0x13C }, Offset = 0xE) + [1] PEImportHintName { Hint = 1, Name = HelloWorld } (RVA = 0x3C00, PEStreamSectionData { RVA = 0x3C00, VirtualSize = 0x13C, Position = 0x2A00, Size = 0x13C }, Offset = 0x0) - [13] PEStreamSectionData Position = 0x00002A00, Size = 0x00000020, RVA = 0x00003C00, VirtualSize = 0x00000020 + [13] PEStreamSectionData Position = 0x00002A00, Size = 0x0000013C, RVA = 0x00003C00, VirtualSize = 0x0000013C - [14] PEBoundImportAddressTable64 Position = 0x00002A20, Size = 0x00000018, RVA = 0x00003C20, VirtualSize = 0x00000018 - [0] VA = 0x0 - [1] VA = 0x0 - - [15] PEStreamSectionData Position = 0x00002A38, Size = 0x00000104, RVA = 0x00003C38, VirtualSize = 0x00000104 - - [16] PEImportDirectory Position = 0x00002B3C, Size = 0x000000C8, RVA = 0x00003D3C, VirtualSize = 0x000000C8 + [14] PEImportDirectory Position = 0x00002B3C, Size = 0x000000C8, RVA = 0x00003D3C, VirtualSize = 0x000000C8 [0] ImportDllNameLink = MSVCP140.dll (RVA = 0x4338, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x2E0) [0] ImportAddressTable = RVA = 0x000030C0 (PEImportAddressTable[1] -> PEImportAddressTableDirectory[0] -> .rdata) - [0] ImportLookupTable = RVA = 0x00003EC8 (PEImportLookupTable[19] -> .rdata) + [0] ImportLookupTable = RVA = 0x00003EC8 (PEImportLookupTable[17] -> .rdata) [1] ImportDllNameLink = VCRUNTIME140_1.dll (RVA = 0x43C4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x36C) [1] ImportAddressTable = RVA = 0x00003160 (PEImportAddressTable[3] -> PEImportAddressTableDirectory[0] -> .rdata) - [1] ImportLookupTable = RVA = 0x00003F68 (PEImportLookupTable[21] -> .rdata) + [1] ImportLookupTable = RVA = 0x00003F68 (PEImportLookupTable[19] -> .rdata) [2] ImportDllNameLink = VCRUNTIME140.dll (RVA = 0x43D8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x380) [2] ImportAddressTable = RVA = 0x00003128 (PEImportAddressTable[2] -> PEImportAddressTableDirectory[0] -> .rdata) - [2] ImportLookupTable = RVA = 0x00003F30 (PEImportLookupTable[20] -> .rdata) + [2] ImportLookupTable = RVA = 0x00003F30 (PEImportLookupTable[18] -> .rdata) [3] ImportDllNameLink = api-ms-win-crt-runtime-l1-1-0.dll (RVA = 0x459C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x544) [3] ImportAddressTable = RVA = 0x000031A0 (PEImportAddressTable[7] -> PEImportAddressTableDirectory[0] -> .rdata) - [3] ImportLookupTable = RVA = 0x00003FA8 (PEImportLookupTable[25] -> .rdata) + [3] ImportLookupTable = RVA = 0x00003FA8 (PEImportLookupTable[23] -> .rdata) [4] ImportDllNameLink = api-ms-win-crt-math-l1-1-0.dll (RVA = 0x45BE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x566) [4] ImportAddressTable = RVA = 0x00003190 (PEImportAddressTable[6] -> PEImportAddressTableDirectory[0] -> .rdata) - [4] ImportLookupTable = RVA = 0x00003F98 (PEImportLookupTable[24] -> .rdata) + [4] ImportLookupTable = RVA = 0x00003F98 (PEImportLookupTable[22] -> .rdata) [5] ImportDllNameLink = api-ms-win-crt-stdio-l1-1-0.dll (RVA = 0x45DE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x586) [5] ImportAddressTable = RVA = 0x00003238 (PEImportAddressTable[8] -> PEImportAddressTableDirectory[0] -> .rdata) - [5] ImportLookupTable = RVA = 0x00004040 (PEImportLookupTable[26] -> .rdata) + [5] ImportLookupTable = RVA = 0x00004040 (PEImportLookupTable[24] -> .rdata) [6] ImportDllNameLink = api-ms-win-crt-locale-l1-1-0.dll (RVA = 0x45FE, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5A6) [6] ImportAddressTable = RVA = 0x00003180 (PEImportAddressTable[5] -> PEImportAddressTableDirectory[0] -> .rdata) - [6] ImportLookupTable = RVA = 0x00003F88 (PEImportLookupTable[23] -> .rdata) + [6] ImportLookupTable = RVA = 0x00003F88 (PEImportLookupTable[21] -> .rdata) [7] ImportDllNameLink = api-ms-win-crt-heap-l1-1-0.dll (RVA = 0x4620, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5C8) [7] ImportAddressTable = RVA = 0x00003170 (PEImportAddressTable[4] -> PEImportAddressTableDirectory[0] -> .rdata) - [7] ImportLookupTable = RVA = 0x00003F78 (PEImportLookupTable[22] -> .rdata) + [7] ImportLookupTable = RVA = 0x00003F78 (PEImportLookupTable[20] -> .rdata) [8] ImportDllNameLink = KERNEL32.dll (RVA = 0x479E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x746) [8] ImportAddressTable = RVA = 0x00003000 (PEImportAddressTable[0] -> PEImportAddressTableDirectory[0] -> .rdata) - [8] ImportLookupTable = RVA = 0x00003E08 (PEImportLookupTable[18] -> .rdata) + [8] ImportLookupTable = RVA = 0x00003E08 (PEImportLookupTable[16] -> .rdata) - [17] PEStreamSectionData Position = 0x00002C04, Size = 0x00000004, RVA = 0x00003E04, VirtualSize = 0x00000004 + [15] PEStreamSectionData Position = 0x00002C04, Size = 0x00000004, RVA = 0x00003E04, VirtualSize = 0x00000004 - [18] PEImportLookupTable Position = 0x00002C08, Size = 0x000000C0, RVA = 0x00003E08, VirtualSize = 0x000000C0 + [16] PEImportLookupTable Position = 0x00002C08, Size = 0x000000C0, RVA = 0x00003E08, VirtualSize = 0x000000C0 [0] PEImportHintName { Hint = 997, Name = LoadLibraryExA } (RVA = 0x482A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x7D2) [1] PEImportHintName { Hint = 1277, Name = RtlLookupFunctionEntry } (RVA = 0x4654, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5FC) [2] PEImportHintName { Hint = 1284, Name = RtlVirtualUnwind } (RVA = 0x466E, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x616) @@ -350,7 +344,7 @@ Sections [21] PEImportHintName { Hint = 772, Name = GetSystemInfo } (RVA = 0x47D8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x780) [22] PEImportHintName { Hint = 1269, Name = RtlCaptureContext } (RVA = 0x4640, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x5E8) - [19] PEImportLookupTable Position = 0x00002CC8, Size = 0x00000068, RVA = 0x00003EC8, VirtualSize = 0x00000068 + [17] PEImportLookupTable Position = 0x00002CC8, Size = 0x00000068, RVA = 0x00003EC8, VirtualSize = 0x00000068 [0] PEImportHintName { Hint = 255, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z } (RVA = 0x42D6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x27E) [1] PEImportHintName { Hint = 262, Name = ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@P6AAEAV01@AEAV01@@Z@Z } (RVA = 0x4282, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x22A) [2] PEImportHintName { Hint = 1221, Name = ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QEAAXH_N@Z } (RVA = 0x4242, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x1EA) @@ -364,7 +358,7 @@ Sections [10] PEImportHintName { Hint = 965, Name = ?good@ios_base@std@@QEBA_NXZ } (RVA = 0x4318, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x2C0) [11] PEImportHintName { Hint = 692, Name = ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A } (RVA = 0x4058, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x0) - [20] PEImportLookupTable Position = 0x00002D30, Size = 0x00000038, RVA = 0x00003F30, VirtualSize = 0x00000038 + [18] PEImportLookupTable Position = 0x00002D30, Size = 0x00000038, RVA = 0x00003F30, VirtualSize = 0x00000038 [0] PEImportHintName { Hint = 60, Name = memcpy } (RVA = 0x47AC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x754) [1] PEImportHintName { Hint = 28, Name = __current_exception_context } (RVA = 0x439C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x344) [2] PEImportHintName { Hint = 62, Name = memset } (RVA = 0x43BA, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x362) @@ -372,19 +366,19 @@ Sections [4] PEImportHintName { Hint = 27, Name = __current_exception } (RVA = 0x4386, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x32E) [5] PEImportHintName { Hint = 35, Name = __std_terminate } (RVA = 0x435C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x304) - [21] PEImportLookupTable Position = 0x00002D68, Size = 0x00000010, RVA = 0x00003F68, VirtualSize = 0x00000010 + [19] PEImportLookupTable Position = 0x00002D68, Size = 0x00000010, RVA = 0x00003F68, VirtualSize = 0x00000010 [0] PEImportHintName { Hint = 0, Name = __CxxFrameHandler4 } (RVA = 0x4346, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x2EE) - [22] PEImportLookupTable Position = 0x00002D78, Size = 0x00000010, RVA = 0x00003F78, VirtualSize = 0x00000010 + [20] PEImportLookupTable Position = 0x00002D78, Size = 0x00000010, RVA = 0x00003F78, VirtualSize = 0x00000010 [0] PEImportHintName { Hint = 22, Name = _set_new_mode } (RVA = 0x452A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4D2) - [23] PEImportLookupTable Position = 0x00002D88, Size = 0x00000010, RVA = 0x00003F88, VirtualSize = 0x00000010 + [21] PEImportLookupTable Position = 0x00002D88, Size = 0x00000010, RVA = 0x00003F88, VirtualSize = 0x00000010 [0] PEImportHintName { Hint = 8, Name = _configthreadlocale } (RVA = 0x4514, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4BC) - [24] PEImportLookupTable Position = 0x00002D98, Size = 0x00000010, RVA = 0x00003F98, VirtualSize = 0x00000010 + [22] PEImportLookupTable Position = 0x00002D98, Size = 0x00000010, RVA = 0x00003F98, VirtualSize = 0x00000010 [0] PEImportHintName { Hint = 9, Name = __setusermatherr } (RVA = 0x440C, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x3B4) - [25] PEImportLookupTable Position = 0x00002DA8, Size = 0x00000098, RVA = 0x00003FA8, VirtualSize = 0x00000098 + [23] PEImportLookupTable Position = 0x00002DA8, Size = 0x00000098, RVA = 0x00003FA8, VirtualSize = 0x00000098 [0] PEImportHintName { Hint = 5, Name = __p___argv } (RVA = 0x44C4, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x46C) [1] PEImportHintName { Hint = 60, Name = _register_onexit_function } (RVA = 0x4566, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x50E) [2] PEImportHintName { Hint = 52, Name = _initialize_onexit_table } (RVA = 0x454A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4F2) @@ -404,11 +398,11 @@ Sections [16] PEImportHintName { Hint = 61, Name = _register_thread_local_exe_atexit_callback } (RVA = 0x44E6, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x48E) [17] PEImportHintName { Hint = 21, Name = _c_exit } (RVA = 0x44DC, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x484) - [26] PEImportLookupTable Position = 0x00002E40, Size = 0x00000018, RVA = 0x00004040, VirtualSize = 0x00000018 + [24] PEImportLookupTable Position = 0x00002E40, Size = 0x00000018, RVA = 0x00004040, VirtualSize = 0x00000018 [0] PEImportHintName { Hint = 1, Name = __p__commode } (RVA = 0x453A, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x4E2) [1] PEImportHintName { Hint = 84, Name = _set_fmode } (RVA = 0x44A8, PEStreamSectionData { RVA = 0x4058, VirtualSize = 0x7E4, Position = 0x2E58, Size = 0x7E4 }, Offset = 0x450) - [27] PEStreamSectionData Position = 0x00002E58, Size = 0x000007E4, RVA = 0x00004058, VirtualSize = 0x000007E4 + [25] PEStreamSectionData Position = 0x00002E58, Size = 0x000007E4, RVA = 0x00004058, VirtualSize = 0x000007E4 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [02] .data PESection Position = 0x00003800, Size = 0x00000200, RVA = 0x00005000, VirtualSize = 0x000006D0, Characteristics = 0xC0000040 (ContainsInitializedData, MemRead, MemWrite) @@ -619,7 +613,11 @@ Sections [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 + [03] PEStreamSectionData Position = 0x00003E58, Size = 0x00000008, RVA = 0x00007058, VirtualSize = 0x00000008 + + [04] PEResourceData Position = 0x00003E60, Size = 0x0000017D, RVA = 0x00007060, VirtualSize = 0x0000017D + + [05] PEStreamSectionData Position = 0x00003FDD, Size = 0x00000003, RVA = 0x000071DD, VirtualSize = 0x00000003 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 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 aa12b2f..bffdfb8 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 @@ -526,7 +526,11 @@ Sections [02] PEResourceDataEntry Position = 0x00002E48, Size = 0x00000010, RVA = 0x00007048, VirtualSize = 0x00000010 > CodePage = null, Data = PEResourceData { RVA = 0x7060, VirtualSize = 0x17D, Position = 0x2E60, Size = 0x17D } - [03] PEResourceData Position = 0x00002E60, Size = 0x0000017D, RVA = 0x00007060, VirtualSize = 0x0000017D + [03] PEStreamSectionData Position = 0x00002E58, Size = 0x00000008, RVA = 0x00007058, VirtualSize = 0x00000008 + + [04] PEResourceData Position = 0x00002E60, Size = 0x0000017D, RVA = 0x00007060, VirtualSize = 0x0000017D + + [05] PEStreamSectionData Position = 0x00002FDD, Size = 0x00000003, RVA = 0x000071DD, VirtualSize = 0x00000003 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 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 4955137..f35d8fc 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 @@ -450,9 +450,11 @@ Sections [02] PEResourceDataEntry Position = 0x00002648, Size = 0x00000010, RVA = 0x00005048, VirtualSize = 0x00000010 > CodePage = null, Data = PEResourceData { RVA = 0x5060, VirtualSize = 0x91, Position = 0x2660, Size = 0x91 } - [03] PEResourceData Position = 0x00002660, Size = 0x00000091, RVA = 0x00005060, VirtualSize = 0x00000091 + [03] PEStreamSectionData Position = 0x00002658, Size = 0x00000008, RVA = 0x00005058, VirtualSize = 0x00000008 - [04] PEStreamSectionData Position = 0x000026F4, Size = 0x00000004, RVA = 0x000050F4, VirtualSize = 0x00000004 + [04] PEResourceData Position = 0x00002660, Size = 0x00000091, RVA = 0x00005060, VirtualSize = 0x00000091 + + [05] PEStreamSectionData Position = 0x000026F1, Size = 0x00000007, RVA = 0x000050F1, VirtualSize = 0x00000007 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/src/LibObjectFile/Ar/ArArchiveFile.cs b/src/LibObjectFile/Ar/ArArchiveFile.cs index dccaeba..e91f925 100644 --- a/src/LibObjectFile/Ar/ArArchiveFile.cs +++ b/src/LibObjectFile/Ar/ArArchiveFile.cs @@ -325,7 +325,7 @@ public void Write(Stream stream) writer.Write(); } - public override void UpdateLayout(ArVisitorContext context) + protected override void UpdateLayoutCore(ArVisitorContext context) { } diff --git a/src/LibObjectFile/Ar/ArBinaryFile.cs b/src/LibObjectFile/Ar/ArBinaryFile.cs index 83743de..8f73bba 100644 --- a/src/LibObjectFile/Ar/ArBinaryFile.cs +++ b/src/LibObjectFile/Ar/ArBinaryFile.cs @@ -29,7 +29,7 @@ public override void Write(ArArchiveFileWriter writer) } } - public override void UpdateLayout(ArVisitorContext context) + protected override void UpdateLayoutCore(ArVisitorContext context) { Size = Stream != null ? (ulong) Stream.Length : 0; } diff --git a/src/LibObjectFile/Ar/ArElfFile.cs b/src/LibObjectFile/Ar/ArElfFile.cs index 322a136..b12873a 100644 --- a/src/LibObjectFile/Ar/ArElfFile.cs +++ b/src/LibObjectFile/Ar/ArElfFile.cs @@ -43,7 +43,7 @@ public override void Write(ArArchiveFileWriter writer) } } - public override void UpdateLayout(ArVisitorContext context) + protected override void UpdateLayoutCore(ArVisitorContext context) { Size = 0; diff --git a/src/LibObjectFile/Ar/ArLongNamesTable.cs b/src/LibObjectFile/Ar/ArLongNamesTable.cs index dab75ca..c045884 100644 --- a/src/LibObjectFile/Ar/ArLongNamesTable.cs +++ b/src/LibObjectFile/Ar/ArLongNamesTable.cs @@ -108,7 +108,7 @@ public override void Write(ArArchiveFileWriter writer) ArrayPool.Shared.Return(buffer); } - public override void UpdateLayout(ArVisitorContext context) + protected override void UpdateLayoutCore(ArVisitorContext context) { Size = 0; diff --git a/src/LibObjectFile/Ar/ArSymbolTable.cs b/src/LibObjectFile/Ar/ArSymbolTable.cs index d8868d9..6db07b2 100644 --- a/src/LibObjectFile/Ar/ArSymbolTable.cs +++ b/src/LibObjectFile/Ar/ArSymbolTable.cs @@ -205,7 +205,7 @@ protected override bool PrintMembers(StringBuilder builder) } - public override void UpdateLayout(ArVisitorContext context) + protected override void UpdateLayoutCore(ArVisitorContext context) { if (Parent == null) return; diff --git a/src/LibObjectFile/Diagnostics/DiagnosticBag.cs b/src/LibObjectFile/Diagnostics/DiagnosticBag.cs index 261e4c2..f3f71d9 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticBag.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticBag.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. @@ -33,6 +33,11 @@ public DiagnosticBag() /// public bool HasErrors { get; private set; } + /// + /// Gets or sets a value indicating whether to enable stack trace for each message. + /// + public bool EnableStackTrace { get; set; } + /// /// Clear all messages. /// @@ -78,7 +83,10 @@ public void Log(DiagnosticMessage message) public void Error(DiagnosticId id, string message, object? context = null) { if (message == null) throw new ArgumentNullException(nameof(message)); - Log(new DiagnosticMessage(DiagnosticKind.Error, id, message, context)); + Log(new DiagnosticMessage(DiagnosticKind.Error, id, message, context) + { + StackTrace = EnableStackTrace ? new StackTrace(1, true) : null + }); } /// @@ -90,7 +98,10 @@ public void Error(DiagnosticId id, string message, object? context = null) public void Warning(DiagnosticId id, string message, object? context = null) { if (message == null) throw new ArgumentNullException(nameof(message)); - Log(new DiagnosticMessage(DiagnosticKind.Warning, id, message, context)); + Log(new DiagnosticMessage(DiagnosticKind.Warning, id, message, context) + { + StackTrace = EnableStackTrace ? new StackTrace(1, true) : null + }); } public override string ToString() diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 058ac93..526c3f4 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -182,6 +182,7 @@ public enum DiagnosticId PE_ERR_ImportDirectoryInvalidImportLookupTableRVA = 3805, PE_ERR_ImportAddressTableNotFound = 3806, PE_ERR_InvalidInternalState = 3807, + PE_WRN_ImportLookupTableInvalidRVAOutOfRange = 3808, // PE Export PE_ERR_ExportAddressTableInvalidRVA = 3900, diff --git a/src/LibObjectFile/Diagnostics/DiagnosticMessage.cs b/src/LibObjectFile/Diagnostics/DiagnosticMessage.cs index a384b90..970f297 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticMessage.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticMessage.cs @@ -1,7 +1,9 @@ -// Copyright (c) Alexandre Mutel. All rights reserved. +// Copyright (c) Alexandre Mutel. All rights reserved. // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using System.Diagnostics; + namespace LibObjectFile.Diagnostics; /// @@ -45,8 +47,20 @@ public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string message, o /// public string Message { get; } + /// + /// Gets the associated stack trace of this message. + /// + public StackTrace? StackTrace { get; init; } + public override string ToString() { - return $"{Kind} LB{(uint)Id:0000}: {Message}"; + if (StackTrace is not null) + { + return $"{Kind} LB{(uint)Id:0000}: {Message}\n{StackTrace}"; + } + else + { + return $"{Kind} LB{(uint)Id:0000}: {Message}"; + } } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs index 3ebb6b2..773a36a 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs @@ -206,7 +206,7 @@ public override void Write(DwarfWriter writer) Debug.Assert(writer.Position - startOffset == Size); } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { var endOffset = Position; diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs index 64186c7..d2f602b 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs @@ -30,7 +30,7 @@ internal DwarfAbbreviationItem(ulong code, DwarfTagEx tag, bool hasChildren, Dwa public DwarfAttributeDescriptors Descriptors { get; private set; } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { var endOffset = Position; diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs index 796bd0a..9591b0c 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs @@ -28,7 +28,7 @@ internal void Reset() _abbreviations.Clear(); } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { ulong endOffset = Position; foreach (var abbreviation in _abbreviations) diff --git a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs index 61bedea..61ad6a0 100644 --- a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs @@ -139,7 +139,7 @@ public override void Verify(DwarfVerifyContext context) } } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { ulong sizeOf = 0; // unit_length diff --git a/src/LibObjectFile/Dwarf/DwarfAttribute.cs b/src/LibObjectFile/Dwarf/DwarfAttribute.cs index d4c5af6..69e9263 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttribute.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttribute.cs @@ -130,7 +130,7 @@ protected override bool PrintMembers(StringBuilder builder) return true; } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { var addressSize = layoutContext.CurrentUnit!.AddressSize; var is64BitEncoding = layoutContext.CurrentUnit.Is64BitEncoding; diff --git a/src/LibObjectFile/Dwarf/DwarfDIE.cs b/src/LibObjectFile/Dwarf/DwarfDIE.cs index b3f29db..aef73ee 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIE.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIE.cs @@ -296,7 +296,7 @@ protected unsafe void SetAttributeValueOpt(DwarfAttributeKind kind, TVal } } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { var abbrev = Abbrev; diff --git a/src/LibObjectFile/Dwarf/DwarfExpression.cs b/src/LibObjectFile/Dwarf/DwarfExpression.cs index a2bb571..82377b4 100644 --- a/src/LibObjectFile/Dwarf/DwarfExpression.cs +++ b/src/LibObjectFile/Dwarf/DwarfExpression.cs @@ -94,7 +94,7 @@ internal void UpdateLayout(DwarfLayoutContext layoutContext, bool inLocationSect Size = OperationLengthInBytes + deltaLength; } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { UpdateLayout(layoutContext, inLocationSection: false); } diff --git a/src/LibObjectFile/Dwarf/DwarfFile.cs b/src/LibObjectFile/Dwarf/DwarfFile.cs index be0f216..9d0b33b 100644 --- a/src/LibObjectFile/Dwarf/DwarfFile.cs +++ b/src/LibObjectFile/Dwarf/DwarfFile.cs @@ -448,7 +448,7 @@ private static void CheckErrors(DiagnosticBag diagnostics) } } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { } diff --git a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs index 78ca148..ddde005 100644 --- a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs @@ -61,7 +61,7 @@ public override void Verify(DwarfVerifyContext context) } } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { var offset = Position; foreach (var unit in Units) diff --git a/src/LibObjectFile/Dwarf/DwarfLine.cs b/src/LibObjectFile/Dwarf/DwarfLine.cs index e2528d8..214720c 100644 --- a/src/LibObjectFile/Dwarf/DwarfLine.cs +++ b/src/LibObjectFile/Dwarf/DwarfLine.cs @@ -173,7 +173,7 @@ protected override bool PrintMembers(StringBuilder builder) private static string Bool2Str(bool value) => value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(); - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { } diff --git a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs index 3afa71d..87a8bac 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs @@ -664,7 +664,7 @@ public override void Verify(DwarfVerifyContext context) } } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { ulong sizeOf = 0; diff --git a/src/LibObjectFile/Dwarf/DwarfLineSection.cs b/src/LibObjectFile/Dwarf/DwarfLineSection.cs index 53e5200..d783249 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSection.cs @@ -41,7 +41,7 @@ public override void Verify(DwarfVerifyContext context) } } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { ulong sizeOf = 0; diff --git a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs index e888e53..1491fd3 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs @@ -29,7 +29,7 @@ public void Add(DwarfLine line) _lines.Add(line); } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { // This is implemented in DwarfLineSection } diff --git a/src/LibObjectFile/Dwarf/DwarfLocationList.cs b/src/LibObjectFile/Dwarf/DwarfLocationList.cs index 1aa8015..64c8142 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationList.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationList.cs @@ -19,7 +19,7 @@ public DwarfLocationList() public ObjectList LocationListEntries => _locationListEntries; - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { var endOffset = Position; diff --git a/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs b/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs index 841b1f4..28f8290 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs @@ -42,7 +42,7 @@ public override void Read(DwarfReader reader) Expression.ReadInternal(reader, inLocationSection: true); } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { var endOffset = Position; diff --git a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs index 6f808ea..d7c7a4b 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs @@ -41,7 +41,7 @@ public override void Verify(DwarfVerifyContext context) } } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { ulong sizeOf = 0; diff --git a/src/LibObjectFile/Dwarf/DwarfOperation.cs b/src/LibObjectFile/Dwarf/DwarfOperation.cs index ace98ae..a565f31 100644 --- a/src/LibObjectFile/Dwarf/DwarfOperation.cs +++ b/src/LibObjectFile/Dwarf/DwarfOperation.cs @@ -426,7 +426,7 @@ public override void Read(DwarfReader reader) Size = reader.Position - Position; } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { var endOffset = Position; // 1 byte per opcode diff --git a/src/LibObjectFile/Dwarf/DwarfStringTable.cs b/src/LibObjectFile/Dwarf/DwarfStringTable.cs index d72076c..38fdb55 100644 --- a/src/LibObjectFile/Dwarf/DwarfStringTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfStringTable.cs @@ -82,7 +82,7 @@ public override void Write(DwarfWriter writer) writer.Write(Stream); } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { Size = (ulong?)(Stream?.Length) ?? 0UL; } diff --git a/src/LibObjectFile/Dwarf/DwarfUnit.cs b/src/LibObjectFile/Dwarf/DwarfUnit.cs index b6bb0a5..1c1717c 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnit.cs @@ -86,7 +86,7 @@ protected override void ValidateParent(ObjectElement parent) } } - public override void UpdateLayout(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { var offset = this.Position; diff --git a/src/LibObjectFile/Elf/ElfObjectFile.cs b/src/LibObjectFile/Elf/ElfObjectFile.cs index 0dc8c00..3440c94 100644 --- a/src/LibObjectFile/Elf/ElfObjectFile.cs +++ b/src/LibObjectFile/Elf/ElfObjectFile.cs @@ -792,7 +792,7 @@ private static int CompareStreamIndexAndIndex(ElfSection left, ElfSection right) return left.Index.CompareTo(right.Index); } - public override void UpdateLayout(ElfVisitorContext layoutContext) + protected override void UpdateLayoutCore(ElfVisitorContext layoutContext) { } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSegment.cs b/src/LibObjectFile/Elf/ElfSegment.cs index c57f439..a363d50 100644 --- a/src/LibObjectFile/Elf/ElfSegment.cs +++ b/src/LibObjectFile/Elf/ElfSegment.cs @@ -51,7 +51,7 @@ public sealed class ElfSegment : ElfObject /// public ulong Alignment { get; set; } - public override void UpdateLayout(ElfVisitorContext context) + protected override void UpdateLayoutCore(ElfVisitorContext context) { var diagnostics = context.Diagnostics; diff --git a/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs b/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs index d11215c..fe0d240 100644 --- a/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs @@ -33,7 +33,7 @@ public ElfAlignedShadowSection(uint upperAlignment) /// public uint UpperAlignment { get; set; } - public override void UpdateLayout(ElfVisitorContext context) + protected override void UpdateLayoutCore(ElfVisitorContext context) { var nextSectionOffset = AlignHelper.AlignUp(Position, UpperAlignment); Size = nextSectionOffset - Position; diff --git a/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs b/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs index bff47f2..a8d1640 100644 --- a/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs @@ -60,7 +60,7 @@ public override void Write(ElfWriter writer) writer.Write(Stream); } - public override void UpdateLayout(ElfVisitorContext context) + protected override void UpdateLayoutCore(ElfVisitorContext context) { if (Type != ElfSectionType.NoBits) { diff --git a/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs b/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs index 72d2e4a..5898788 100644 --- a/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs @@ -28,7 +28,7 @@ public override void Write(ElfWriter writer) writer.Write(Stream); } - public override void UpdateLayout(ElfVisitorContext context) + protected override void UpdateLayoutCore(ElfVisitorContext context) { Size = Stream != null ? (ulong)Stream.Length : 0; } diff --git a/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs b/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs index 909ba0d..30348d2 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNoteTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNoteTable.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. @@ -39,7 +39,7 @@ public override ElfSectionType Type } } - public override unsafe void UpdateLayout(ElfVisitorContext context) + protected override unsafe void UpdateLayoutCore(ElfVisitorContext context) { ulong size = 0; ulong entrySize = (ulong)sizeof(ElfNative.Elf32_Nhdr); diff --git a/src/LibObjectFile/Elf/Sections/ElfNullSection.cs b/src/LibObjectFile/Elf/Sections/ElfNullSection.cs index 4c8d48f..f82ea06 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNullSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNullSection.cs @@ -27,7 +27,7 @@ public override void Verify(ElfVisitorContext context) } } - public override void UpdateLayout(ElfVisitorContext context) + protected override void UpdateLayoutCore(ElfVisitorContext context) { } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs b/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs index cb69817..05b6efb 100644 --- a/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs @@ -28,7 +28,7 @@ public override unsafe ulong TableEntrySize } } - public override void UpdateLayout(ElfVisitorContext context) + protected override void UpdateLayoutCore(ElfVisitorContext context) { Size = 0; diff --git a/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs b/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs index e328ddd..f1c8e51 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs @@ -300,7 +300,7 @@ public override void Verify(ElfVisitorContext context) } } - public override unsafe void UpdateLayout(ElfVisitorContext context) + protected override unsafe void UpdateLayoutCore(ElfVisitorContext context) { Size = Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : Parent.FileClass == ElfFileClass.Is32 diff --git a/src/LibObjectFile/Elf/Sections/ElfStringTable.cs b/src/LibObjectFile/Elf/Sections/ElfStringTable.cs index f120f9e..e6c47a2 100644 --- a/src/LibObjectFile/Elf/Sections/ElfStringTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfStringTable.cs @@ -55,7 +55,7 @@ public override ElfSectionType Type } } - public override void UpdateLayout(ElfVisitorContext context) + protected override void UpdateLayoutCore(ElfVisitorContext context) { if (_reservedStrings.Count > 0) FlushReservedStrings(); Size = (ulong)_table.Length; diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs index ba9b518..637e649 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs @@ -277,7 +277,7 @@ public override void Verify(ElfVisitorContext context) } } - public override unsafe void UpdateLayout(ElfVisitorContext context) + protected override unsafe void UpdateLayoutCore(ElfVisitorContext context) { Size = Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : Parent.FileClass == ElfFileClass.Is32 ? (ulong)(Entries.Count * sizeof(ElfNative.Elf32_Sym)) : (ulong)(Entries.Count * sizeof(ElfNative.Elf64_Sym)); diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs index 79af1e1..3e4f4d9 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs @@ -91,7 +91,7 @@ public override void Verify(ElfVisitorContext context) } } - public override void UpdateLayout(ElfVisitorContext context) + protected override void UpdateLayoutCore(ElfVisitorContext context) { // Verify that the link is safe and configured as expected Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, context.Diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable); diff --git a/src/LibObjectFile/LibObjectFile.csproj b/src/LibObjectFile/LibObjectFile.csproj index e706b4d..eec2cd9 100644 --- a/src/LibObjectFile/LibObjectFile.csproj +++ b/src/LibObjectFile/LibObjectFile.csproj @@ -37,6 +37,8 @@ + + diff --git a/src/LibObjectFile/ObjectFileElement.cs b/src/LibObjectFile/ObjectFileElement.cs index bd3cad8..32568e8 100644 --- a/src/LibObjectFile/ObjectFileElement.cs +++ b/src/LibObjectFile/ObjectFileElement.cs @@ -85,6 +85,11 @@ public abstract class ObjectFileElement public List Relocations { get; } = new(); - public override unsafe void UpdateLayout(PELayoutContext layoutContext) + protected override unsafe void UpdateLayoutCore(PELayoutContext layoutContext) { var count = Relocations.Count; diff --git a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs index bfe30b5..1ea9d99 100644 --- a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs +++ b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using LibObjectFile.Collections; using LibObjectFile.Utils; @@ -30,7 +31,7 @@ protected PECompositeSectionData() /// public ObjectList Content { get; } - public sealed override void UpdateLayout(PELayoutContext context) + protected sealed override void UpdateLayoutCore(PELayoutContext context) { var va = RVA; @@ -97,7 +98,14 @@ internal void WriteHeaderAndContent(PEImageWriter writer) Debug.Assert(table.Position == writer.Position); - table.Write(writer); + if (table is PECompositeSectionData compositeSectionData) + { + compositeSectionData.WriteHeaderAndContent(writer); + } + else + { + table.Write(writer); + } alignment = table.GetRequiredSizeAlignment(writer.PEFile); if (alignment > 1) @@ -110,9 +118,12 @@ internal void WriteHeaderAndContent(PEImageWriter writer) protected abstract uint ComputeHeaderSize(PELayoutContext context); - protected sealed override bool TryFindByRVAInChildren(RVA rva, out PEObject? result) + protected sealed override bool TryFindByRVAInChildren(RVA rva, [NotNullWhen(true)] out PEObject? result) => Content.TryFindByRVA(rva, true, out result); + protected sealed override bool TryFindByPositionInChildren(uint position, [NotNullWhen(true)] out PEObjectBase? result) + => Content.TryFindByPosition(position, true, out result); + protected sealed override void UpdateRVAInChildren() { var va = RVA; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs index dcda8a6..144088b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs @@ -17,10 +17,10 @@ protected PEDataDirectory(PEDataDirectoryKind kind) protected override void ValidateParent(ObjectElement parent) { - if (parent is not PESection) - { - throw new ArgumentException($"Invalid parent type [{parent?.GetType()}] for [{GetType()}]"); - } + //if (parent is not PESection) + //{ + // throw new ArgumentException($"Invalid parent type [{parent?.GetType()}] for [{GetType()}]"); + //} } /// diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs index 71b2deb..5ceb9c0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs @@ -29,6 +29,7 @@ public PEDebugSectionDataRSDS() Guid = Guid.Empty; Age = 0; PdbPath = string.Empty; + ExtraData = []; } /// @@ -45,6 +46,11 @@ public PEDebugSectionDataRSDS() /// Gets or sets the path of the PDB. /// public string PdbPath { get; set; } + + /// + /// Gets or sets the extra data after the PDB path. + /// + public byte[] ExtraData { get; set; } public override unsafe void Read(PEImageReader reader) { @@ -78,6 +84,15 @@ public override unsafe void Read(PEImageReader reader) Age = MemoryMarshal.Read(span.Slice(sizeof(uint) + sizeof(Guid))); PdbPath = System.Text.Encoding.UTF8.GetString(pdbPath.Slice(0, indexOfZero)); + // Remaining + pdbPath = pdbPath.Slice(indexOfZero + 1); + if (pdbPath.Length > 0) + { + ExtraData = pdbPath.ToArray(); + } + + var newSize = CalculateSize(); + Debug.Assert(size == CalculateSize()); } @@ -94,18 +109,22 @@ public override unsafe void Write(PEImageWriter writer) span = span.Slice(sizeof(uint)); int written = Encoding.UTF8.GetBytes(PdbPath, span); span[written] = 0; - span.Slice(written + 1); + span = span.Slice(written + 1); + if (ExtraData.Length > 0) + { + ExtraData.CopyTo(span); + } writer.Write(tempSpan.AsBytes); } - public override void UpdateLayout(PELayoutContext layoutContext) + protected override void UpdateLayoutCore(PELayoutContext layoutContext) { Size = CalculateSize(); } private unsafe uint CalculateSize() - => (uint)(sizeof(uint) + sizeof(Guid) + sizeof(uint) + Encoding.UTF8.GetByteCount(PdbPath) + 1); + => (uint)(sizeof(uint) + sizeof(Guid) + sizeof(uint) + Encoding.UTF8.GetByteCount(PdbPath) + 1 + ExtraData.Length); /// protected override bool PrintMembers(StringBuilder builder) diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs index 62b6e49..98bfe3e 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs @@ -68,42 +68,14 @@ public override void Read(PEImageReader reader) // Calculate its position within the original stream var importDelayLoadImportNameTablePositionInFile = section.Position + rawEntry.DelayLoadImportNameTableRVA - section.RVA; - PEBoundImportAddressTable? boundImportAddressTable = null; - if (rawEntry.BoundDelayLoadImportAddressTableRVA != 0) - { - // BoundDelayLoadImportAddressTableRVA - if (!reader.File.TryFindSection(rawEntry.BoundDelayLoadImportAddressTableRVA, out section)) - { - diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidBoundDelayLoadImportAddressTableRVA, $"Unable to find the section for BoundDelayLoadImportAddressTableRVA {rawEntry.BoundDelayLoadImportAddressTableRVA}"); - return; - } - - boundImportAddressTable = is32Bits ? new PEBoundImportAddressTable32() : new PEBoundImportAddressTable64(); - boundImportAddressTable.Position = (uint)section.Position + rawEntry.BoundDelayLoadImportAddressTableRVA - section.RVA; - } - - PEBoundImportAddressTable? unloadDelayInformationTable = null; - if (rawEntry.UnloadDelayLoadImportAddressTableRVA != 0) - { - // UnloadDelayLoadImportAddressTableRVA - if (!reader.File.TryFindSection(rawEntry.UnloadDelayLoadImportAddressTableRVA, out section)) - { - diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidUnloadDelayLoadImportAddressTableRVA, $"Unable to find the section for UnloadDelayLoadImportAddressTableRVA {rawEntry.UnloadDelayLoadImportAddressTableRVA}"); - return; - } - - unloadDelayInformationTable = is32Bits ? new PEBoundImportAddressTable32() : new PEBoundImportAddressTable64(); - unloadDelayInformationTable.Position = (uint)section.Position + rawEntry.UnloadDelayLoadImportAddressTableRVA - section.RVA; - } - PEBoundImportAddressTable delayImportAddressTable = is32Bits ? new PEBoundImportAddressTable32() : new PEBoundImportAddressTable64(); delayImportAddressTable.Position = (uint)importDelayLoadImportAddressTablePositionInFile; Entries.Add( new PEDelayImportDirectoryEntry( - new PEAsciiStringLink(PEStreamSectionData.Empty, (RVO)(uint)rawEntry.NameRVA), - new PEModuleHandleLink(PEStreamSectionData.Empty, (RVO)(uint)rawEntry.ModuleHandleRVA), + new PEAsciiStringLink(null, (RVO)(uint)rawEntry.NameRVA), + new PEModuleHandleLink(null, (RVO)(uint)rawEntry.ModuleHandleRVA), delayImportAddressTable, new PEImportLookupTable() { @@ -112,8 +84,8 @@ public override void Read(PEImageReader reader) ) { Attributes = rawEntry.Attributes, - BoundImportAddressTable = boundImportAddressTable, - UnloadDelayInformationTable = unloadDelayInformationTable + BoundImportAddressTableLink = new PESectionDataLink(null, (RVO)(uint)rawEntry.BoundDelayLoadImportAddressTableRVA), + UnloadDelayInformationTableLink = new PESectionDataLink(null, (RVO)(uint)rawEntry.UnloadDelayLoadImportAddressTableRVA) } ); } @@ -127,18 +99,6 @@ public override void Read(PEImageReader reader) { entry.DelayImportAddressTable.Read(reader); entry.DelayImportNameTable.Read(reader); - - if (entry.BoundImportAddressTable != null) - { - entry.BoundImportAddressTable.SetCount(entry.DelayImportAddressTable.Count); - entry.BoundImportAddressTable.Read(reader); - } - - if (entry.UnloadDelayInformationTable != null) - { - entry.UnloadDelayInformationTable.SetCount(entry.DelayImportAddressTable.Count); - entry.UnloadDelayInformationTable.Read(reader); - } } } internal override IEnumerable CollectImplicitSectionDataList() @@ -147,16 +107,6 @@ internal override IEnumerable CollectImplicitSectionDataList() { yield return entry.DelayImportAddressTable; yield return entry.DelayImportNameTable; - - if (entry.BoundImportAddressTable != null) - { - yield return entry.BoundImportAddressTable; - } - - if (entry.UnloadDelayInformationTable != null) - { - yield return entry.UnloadDelayInformationTable; - } } } @@ -185,23 +135,56 @@ internal override void Bind(PEImageReader reader) entry.DllName = new PEAsciiStringLink(streamSectionData, rva - container.RVA); + // The ModuleHandle could be in Virtual memory and not bound, so we link to a section and not a particular data on the disk rva = (RVA)(uint)entry.ModuleHandle.RVO; - if (!peFile.TryFindContainerByRVA(rva, out container)) + if (!peFile.TryFindSection(rva, out var moduleSection)) { diagnostics.Error(DiagnosticId.PE_ERR_DelayImportDirectoryInvalidModuleHandleRVA, $"Unable to find the section data for ModuleHandleRVA {rva}"); return; } - streamSectionData = container as PEStreamSectionData; - if (streamSectionData is null) + entry.ModuleHandle = new PEModuleHandleLink(moduleSection, rva - moduleSection.RVA); + + entry.DelayImportNameTable.FunctionTable.Bind(reader, false); + + + if (entry.BoundImportAddressTableLink.RVO != 0) { - diagnostics.Error(DiagnosticId.PE_ERR_DelayImportDirectoryInvalidModuleHandleRVA, $"The section data for ModuleHandleRVA {rva} is not a stream section data"); - return; + rva = (RVA)(uint)entry.BoundImportAddressTableLink.RVO; + if (!peFile.TryFindContainerByRVA(rva, out container)) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidBoundDelayLoadImportAddressTableRVA, $"Unable to find the section data for BoundImportAddressTableRVA {rva}"); + return; + } + + streamSectionData = container as PEStreamSectionData; + if (streamSectionData is null) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidBoundDelayLoadImportAddressTableRVA, $"The section data for BoundImportAddressTableRVA {rva} is not a stream section data"); + return; + } + + entry.BoundImportAddressTableLink = new PESectionDataLink(streamSectionData, rva - container.RVA); } - entry.ModuleHandle = new PEModuleHandleLink(streamSectionData, rva - container.RVA); - - entry.DelayImportNameTable.FunctionTable.Bind(reader); + if (entry.UnloadDelayInformationTableLink.RVO != 0) + { + rva = (RVA)(uint)entry.UnloadDelayInformationTableLink.RVO; + if (!peFile.TryFindContainerByRVA(rva, out container)) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidUnloadDelayLoadImportAddressTableRVA, $"Unable to find the section data for UnloadDelayInformationTableRVA {rva}"); + return; + } + + streamSectionData = container as PEStreamSectionData; + if (streamSectionData is null) + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidUnloadDelayLoadImportAddressTableRVA, $"The section data for UnloadDelayInformationTableRVA {rva} is not a stream section data"); + return; + } + + entry.UnloadDelayInformationTableLink = new PESectionDataLink(streamSectionData, rva - container.RVA); + } } } @@ -228,8 +211,8 @@ public override unsafe void Write(PEImageWriter writer) 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.BoundDelayLoadImportAddressTableRVA = entry.BoundImportAddressTableLink.RVA(); + rawEntry.UnloadDelayLoadImportAddressTableRVA = entry.UnloadDelayInformationTableLink.RVA(); rawEntry.TimeDateStamp = 0; rawEntries[i] = rawEntry; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs index 8706057..3554843 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.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. @@ -24,7 +24,7 @@ public PEDelayImportDirectoryEntry(PEAsciiStringLink dllName, PEModuleHandleLink public PEImportLookupTable DelayImportNameTable { get; set; } - public PEBoundImportAddressTable? BoundImportAddressTable { get; set; } + public PESectionDataLink BoundImportAddressTableLink { get; set; } - public PEBoundImportAddressTable? UnloadDelayInformationTable { get; set; } + public PESectionDataLink UnloadDelayInformationTableLink { get; set; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs index cca3e2a..370e593 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -18,7 +18,7 @@ namespace LibObjectFile.PE; /// Contains the array of directory entries in a Portable Executable (PE) file. /// [DebuggerDisplay($"{nameof(PEDirectoryTable)} {nameof(Count)} = {{{nameof(Count)}}}")] -public sealed class PEDirectoryTable : IEnumerable +public sealed class PEDirectoryTable { private PEObjectBase?[] _entries; private int _count; @@ -170,12 +170,6 @@ public int Count /// public PEClrMetadata? ClrMetadata => (PEClrMetadata?)this[PEDataDirectoryKind.ClrMetadata]; - /// - /// Gets the enumerator for the directory entries. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public Enumerator GetEnumerator() => new(this); - internal void Set(PESecurityCertificateDirectory? directory) => Set(PEDataDirectoryKind.SecurityCertificate, directory); internal void Set(PEDataDirectoryKind kind, PEObjectBase? directory) => Set((int)kind, directory); @@ -210,57 +204,4 @@ internal unsafe void Write(PEImageWriter writer, ref uint position) position += (uint)(Count * sizeof(RawImageDataDirectory)); } - - /// - /// Enumerator for the directory entries. - /// - public struct Enumerator : IEnumerator - { - private readonly PEDirectoryTable _table; - private int _index; - - internal Enumerator(PEDirectoryTable table) - { - _table = table; - _index = -1; - } - - public PEDataDirectory Current => _index >= 0 ? (PEDataDirectory)_table._entries[_index]! : null!; - - object? IEnumerator.Current => Current; - - - public void Dispose() - { - } - - public bool MoveNext() - { - Span entries = _table._entries; - while (++_index < entries.Length) - { - if (_table._entries[_index] is PEDataDirectory) - { - return true; - } - } - - return false; - } - - public void Reset() - { - _index = -1; - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index 8dd1fe7..9c4fd3a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -24,7 +24,7 @@ public PEExportAddressTable(int count) public List Values { get; } = new(); - public override unsafe void UpdateLayout(PELayoutContext context) + protected override unsafe void UpdateLayoutCore(PELayoutContext context) { Size = (ulong)(Values.Count * sizeof(RVA)); } @@ -45,13 +45,19 @@ public override unsafe void Read(PEImageReader reader) for (int i = 0; i < Values.Count; i++) { var rva = spanRva[i]; + if (rva == 0) + { + Values[i] = new PEExportFunctionEntry(); + continue; + } + if (!reader.File.TryFindSection(rva, out var section)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); return; } - var found = section.TryFindSectionData(rva, out var sectionData); + var found = section.TryFindSectionDataByRVA(rva, out var sectionData); if (section.Name == PESectionName.EData) { @@ -85,7 +91,7 @@ public override void Write(PEImageWriter writer) for (int i = 0; i < Values.Count; i++) { var value = Values[i]; - spanRva[i] = value.IsForwarderRVA ? value.ForwarderRVA.RVA() : value.ExportRVA.RVA(); + spanRva[i] = value.IsEmpty ? default : value.IsForwarderRVA ? value.ForwarderRVA.RVA() : value.ExportRVA.RVA(); } writer.Write(tempSpan); diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs index d1fcc80..ad7ee4c 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs @@ -55,43 +55,54 @@ public override unsafe void Read(PEImageReader reader) // Link to a fake section data until we have recorded the different export tables in the sections // Store a fake RVO that is the RVA until we resolve it in the Bind phase NameLink = new PEAsciiStringLink(PEStreamSectionData.Empty, (RVO)(uint)exportDirectory.Name); - - if (!reader.File.TryFindSection(exportDirectory.AddressOfFunctions, out var sectionAddressOfFunctions)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfFunctions, $"Unable to find the section for AddressOfFunctions {exportDirectory.AddressOfFunctions}"); - return; - } - if (!reader.File.TryFindSection(exportDirectory.AddressOfNames, out var sectionAddressOfNames)) - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfNames, $"Unable to find the section for AddressOfNames {exportDirectory.AddressOfNames}"); - return; + // Not sure this one happen + if (exportDirectory.AddressOfFunctions != 0) + { + if (!reader.File.TryFindSection(exportDirectory.AddressOfFunctions, out var sectionAddressOfFunctions)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfFunctions, $"Unable to find the section for AddressOfFunctions {exportDirectory.AddressOfFunctions}"); + return; + } + ExportFunctionAddressTable = new PEExportAddressTable((int)exportDirectory.NumberOfFunctions) + { + Position = sectionAddressOfFunctions.Position + exportDirectory.AddressOfFunctions - sectionAddressOfFunctions.RVA, + Size = (ulong)(exportDirectory.NumberOfFunctions * sizeof(RVA)) + }; } - if (!reader.File.TryFindSection(exportDirectory.AddressOfNameOrdinals, out var sectionAddressOfNameOrdinals)) + // AddressOfNames can be 0 + if (exportDirectory.AddressOfNames != 0) { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfNameOrdinals, $"Unable to find the section for AddressOfNameOrdinals {exportDirectory.AddressOfNameOrdinals}"); - return; - } + if (!reader.File.TryFindSection(exportDirectory.AddressOfNames, out var sectionAddressOfNames)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfNames, $"Unable to find the section for AddressOfNames {exportDirectory.AddressOfNames}"); + return; + } - ExportFunctionAddressTable = new PEExportAddressTable((int)exportDirectory.NumberOfFunctions) - { - Position = sectionAddressOfFunctions.Position + exportDirectory.AddressOfFunctions - sectionAddressOfFunctions.RVA, - Size = (ulong)(exportDirectory.NumberOfFunctions * sizeof(RVA)) - }; - - ExportNameTable = new PEExportNameTable((int)exportDirectory.NumberOfNames) - { - Position = sectionAddressOfNames.Position + exportDirectory.AddressOfNames - sectionAddressOfNames.RVA, - Size = (ulong)(exportDirectory.NumberOfNames * sizeof(RVA)) - }; + ExportNameTable = new PEExportNameTable((int)exportDirectory.NumberOfNames) + { + Position = sectionAddressOfNames.Position + exportDirectory.AddressOfNames - sectionAddressOfNames.RVA, + Size = (ulong)(exportDirectory.NumberOfNames * sizeof(RVA)) + }; + } - ExportOrdinalTable = new PEExportOrdinalTable((int)exportDirectory.NumberOfFunctions) + // AddressOfNames can be 0 + if (exportDirectory.AddressOfNameOrdinals != 0) { - Position = sectionAddressOfNameOrdinals.Position + exportDirectory.AddressOfNameOrdinals - sectionAddressOfNameOrdinals.RVA, - Size = (ulong)(exportDirectory.NumberOfFunctions * sizeof(ushort)) - }; + if (!reader.File.TryFindSection(exportDirectory.AddressOfNameOrdinals, out var sectionAddressOfNameOrdinals)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfNameOrdinals, $"Unable to find the section for AddressOfNameOrdinals {exportDirectory.AddressOfNameOrdinals}"); + return; + } + ExportOrdinalTable = new PEExportOrdinalTable((int)exportDirectory.NumberOfNames) + { + Position = sectionAddressOfNameOrdinals.Position + exportDirectory.AddressOfNameOrdinals - sectionAddressOfNameOrdinals.RVA, + Size = (ulong)(exportDirectory.NumberOfNames * sizeof(ushort)) + }; + } + // Update the header size HeaderSize = ComputeHeaderSize(reader); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs index a6123e3..250ce32 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.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. @@ -26,10 +26,12 @@ public PEExportFunctionEntry(PEAsciiStringLink forwarderRVA) } public bool IsForwarderRVA => _isForwarderRVA; - + + public bool IsEmpty => _container is null && _offset == 0; + public PEFunctionAddressLink ExportRVA => IsForwarderRVA ? default : new(_container, _offset); public PEAsciiStringLink ForwarderRVA => IsForwarderRVA ? new(_container as PEStreamSectionData, _offset) : default; - public override string ToString() => ForwarderRVA.IsNull() ? $"{ExportRVA}" : $"{ExportRVA}, ForwarderRVA = {ForwarderRVA}"; + public override string ToString() => IsEmpty ? "null" : ForwarderRVA.IsNull() ? $"{ExportRVA}" : $"{ExportRVA}, ForwarderRVA = {ForwarderRVA}"; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs index e5fddc6..dd3df7b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs @@ -24,7 +24,7 @@ public PEExportNameTable(int count) public List Values { get; } = new(); - public override unsafe void UpdateLayout(PELayoutContext context) + protected override unsafe void UpdateLayoutCore(PELayoutContext context) { Size = (ulong)(Values.Count * sizeof(RVA)); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs index 7333281..367e5da 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs @@ -14,7 +14,7 @@ public PEExportOrdinalTable() { } - public PEExportOrdinalTable(int count) + internal PEExportOrdinalTable(int count) { CollectionsMarshal.SetCount(Values, count); } @@ -24,7 +24,7 @@ public PEExportOrdinalTable(int count) public List Values { get; } = new(); - public override void UpdateLayout(PELayoutContext context) + protected override void UpdateLayoutCore(PELayoutContext context) { Size = (ulong)Values.Count * sizeof(ushort); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs index 0bca02e..6feae0d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs @@ -19,7 +19,7 @@ public PEImportAddressTable() public List Entries => FunctionTable.Entries; - public override void UpdateLayout(PELayoutContext context) + protected override void UpdateLayoutCore(PELayoutContext context) { Size = FunctionTable.CalculateSize(context); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 1a8c4fd..6315d61 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -86,6 +86,10 @@ public override void Read(PEImageReader reader) Position = importLookupTablePositionInFile } ) + { + TimeDateStamp = rawEntry.TimeDateStamp, + ForwarderChain = rawEntry.ForwarderChain + } ); } @@ -109,6 +113,8 @@ public override void Write(PEImageWriter writer) rawEntry.NameRVA = (uint)entry.ImportDllNameLink.RVA(); rawEntry.ImportLookupTableRVA = (uint)entry.ImportLookupTable.RVA; rawEntry.ImportAddressTableRVA = (uint)entry.ImportAddressTable.RVA; + rawEntry.TimeDateStamp = entry.TimeDateStamp; + rawEntry.ForwarderChain = entry.ForwarderChain; writer.Write(rawEntry); } @@ -154,14 +160,18 @@ internal override void Bind(PEImageReader reader) entry = new PEImportDirectoryEntry( new PEAsciiStringLink(streamSectionData, va - container.RVA), entry.ImportAddressTable, - entry.ImportLookupTable); + entry.ImportLookupTable) + { + TimeDateStamp = entry.TimeDateStamp, + ForwarderChain = entry.ForwarderChain + }; } foreach (var entry in Entries) { - entry.ImportAddressTable.FunctionTable.Bind(reader); - entry.ImportLookupTable.FunctionTable.Bind(reader); + entry.ImportAddressTable.FunctionTable.Bind(reader, true); + entry.ImportLookupTable.FunctionTable.Bind(reader, false); } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs index 2b1300d..825e55d 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.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,4 +18,18 @@ public PEImportDirectoryEntry(PEAsciiStringLink importDllNameLink, PEImportAddre public PEImportAddressTable ImportAddressTable { get; set; } public PEImportLookupTable ImportLookupTable { get; set; } + + /// + /// The stamp that is set to zero until the image is bound. After the image is bound, this field is set to the time/data stamp of the DLL. + /// + /// 0 if not bound, + /// -1 if bound, and real date\time stamp in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) + /// O.W. date/time stamp of DLL bound to (Old BIND) + /// + public uint TimeDateStamp { get; set; } + + /// + /// The index of the first forwarder reference. -1 if no forwarders + /// + public uint ForwarderChain { get; set; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs index 6a786c4..b4c17ef 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.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,8 @@ 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 PEStreamSectionData? _peSectionData; - private readonly uint _offset; + private readonly ulong _offset; + private readonly bool _isLongOffset; /// /// Initializes a new instance of the class by name. @@ -22,8 +23,19 @@ public PEImportFunctionEntry(PEAsciiStringLink name) { _peSectionData = name.Container; _offset = name.RVO; + _isLongOffset = false; } + /// + /// Initializes a new instance of the class by name. + /// + /// A long offset + public PEImportFunctionEntry(ulong offset) + { + _offset = offset; + _isLongOffset = true; + } + /// /// Initializes a new instance of the class by ordinal. /// @@ -32,20 +44,36 @@ public PEImportFunctionEntry(ushort ordinal) { _peSectionData = null; _offset = ordinal; + _isLongOffset = false; } + /// + /// Gets the raw offset of the import. + /// + public ulong UnsafeRawOffset => _offset; + /// /// Gets a value indicating whether this import is by ordinal. /// public bool IsImportByOrdinal => _peSectionData is null; - + + /// + /// Gets a value indicating whether this import is a long offset. + /// + public bool IsLongOffset => _isLongOffset; + /// /// Gets the name of the import if not by ordinal. /// - public PEImportHintNameLink HintName => _peSectionData is null ? default : new PEImportHintNameLink(_peSectionData, _offset); + public PEImportHintNameLink HintName => _peSectionData is null || IsLongOffset ? default : new PEImportHintNameLink(_peSectionData, (uint)_offset); + /// + /// Gets the long offset of the import if by long offset. + /// + public ulong LongOffset => IsLongOffset ? _offset : 0; + /// /// Gets the ordinal of the import if by ordinal. /// - public ushort Ordinal => _peSectionData is null ? (ushort)(_offset) : (ushort)0; + public ushort Ordinal => _peSectionData is null && !IsLongOffset ? (ushort)(_offset) : (ushort)0; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index 7dfc4db..8687a00 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -38,22 +38,33 @@ public void Read(PEImageReader reader, ulong position) } } - public void Bind(PEImageReader reader) + public void Bind(PEImageReader reader, bool allowOutOfRange) { var peFile = reader.File; var diagnostics = reader.Diagnostics; var entries = CollectionsMarshal.AsSpan(Entries); - foreach (ref var entry in entries) + for (var i = 0; i < entries.Length; i++) { - if (!entry.IsImportByOrdinal) + ref var entry = ref entries[i]; + if (!entry.IsImportByOrdinal && !entry.IsLongOffset) { // The RVO is an RVA until we bind it to a container below var va = (RVA)(uint)entry.HintName.RVO; if (!peFile.TryFindContainerByRVA(va, out var container)) { - diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"Unable to find the section data for HintNameTableRVA {va}"); - return; + if (allowOutOfRange) + { + diagnostics.Warning(DiagnosticId.PE_WRN_ImportLookupTableInvalidRVAOutOfRange, $"Unable to find the section data for HintNameTableRVA {va}"); + entry = new PEImportFunctionEntry(entry.UnsafeRawOffset); + continue; + } + else + { + diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"Unable to find the section data for HintNameTableRVA {va}"); + //entry = new PEImportFunctionEntry(container.RVA); + return; + } } var streamSectionData = container as PEStreamSectionData; @@ -115,7 +126,7 @@ private unsafe void Read64(PEImageReader reader) Entries.Add( entry.IsImportByOrdinal ? new PEImportFunctionEntry(entry.Ordinal) - : new PEImportFunctionEntry(new PEAsciiStringLink(PEStreamSectionData.Empty, entry.HintNameTableRVA)) + : entry.HintNameTableRVA > uint.MaxValue ? new PEImportFunctionEntry(entry.HintNameTableRVA) : new PEImportFunctionEntry(new PEAsciiStringLink(PEStreamSectionData.Empty, (uint)entry.HintNameTableRVA)) ); } } @@ -155,8 +166,14 @@ private unsafe void Write64(PEImageWriter writer) for (var i = 0; i < Entries.Count; i++) { var entry = Entries[i]; - var va = entry.HintName.RVA(); - span[i] = new RawImportFunctionEntry64(entry.IsImportByOrdinal ? 0x8000_0000_0000_0000UL | entry.Ordinal : va); + if (entry.IsLongOffset) + { + span[i] = new RawImportFunctionEntry64(entry.LongOffset); + } + else + { + span[i] = new RawImportFunctionEntry64(entry.IsImportByOrdinal ? 0x8000_0000_0000_0000UL | entry.Ordinal : entry.HintName.RVA()); + } } // Last entry is null terminator span[^1] = default; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs index 782769e..ab167a1 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -21,7 +21,7 @@ public PEImportLookupTable() public List Entries => FunctionTable.Entries; - public override void UpdateLayout(PELayoutContext context) + protected override void UpdateLayoutCore(PELayoutContext context) { Size = FunctionTable.CalculateSize(context); } diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs index 5e4a634..aa371bd 100644 --- a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.cs @@ -21,22 +21,15 @@ private protected PELoadConfigDirectory(int minSize) : base(PEDataDirectoryKind. { } - - /// - public sealed override int RawDataSize => (int)MemoryMarshal.Read(RawData); - /// - /// Sets the config size. + /// Change the recorded size of the Load Configuration Directory. /// - /// The new config size. - /// - /// The underlying buffer will be resized if necessary. - /// - public sealed override void SetRawDataSize(int value) + /// + public void SetConfigSize(uint size) { - base.SetRawDataSize(value); + SetRawDataSize(size); // Write back the configuration size - MemoryMarshal.Write(RawData, value); + MemoryMarshal.Write(RawData, size); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs index b22928c..7701302 100644 --- a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.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. @@ -17,7 +17,7 @@ public class PELoadConfigDirectory32 : PELoadConfigDirectory /// public unsafe PELoadConfigDirectory32() : base(sizeof(PELoadConfigDirectoryData32)) { - SetRawDataSize(sizeof(PELoadConfigDirectoryData32)); + SetConfigSize((uint)sizeof(PELoadConfigDirectoryData32)); } public uint ConfigSize => Data.Size; diff --git a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs index 08d0fd5..0e09070 100644 --- a/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.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. @@ -20,7 +20,7 @@ public class PELoadConfigDirectory64 : PELoadConfigDirectory public unsafe PELoadConfigDirectory64() : base(sizeof(PELoadConfigDirectoryData64)) { // ReSharper disable once VirtualMemberCallInConstructor - SetRawDataSize(sizeof(PELoadConfigDirectoryData64)); + SetConfigSize((uint)sizeof(PELoadConfigDirectoryData64)); } public uint ConfigSize => Data.Size; diff --git a/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs index 8e06b9a..2c7e20f 100644 --- a/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs @@ -21,6 +21,7 @@ public abstract class PERawDataDirectory : PEDataDirectory private protected PERawDataDirectory(PEDataDirectoryKind kind, int minSize) : base(kind) { _rawData = new byte[minSize]; + RawDataSize = (uint)minSize; } /// @@ -34,8 +35,8 @@ private protected PERawDataDirectory(PEDataDirectoryKind kind, int minSize) : ba /// /// Use to set the config size. /// - public abstract int RawDataSize { get; } - + public uint RawDataSize { get; private set; } + /// /// Sets the config size. /// @@ -43,10 +44,8 @@ private protected PERawDataDirectory(PEDataDirectoryKind kind, int minSize) : ba /// /// The underlying buffer will be resized if necessary. /// - public virtual void SetRawDataSize(int value) + public virtual void SetRawDataSize(uint value) { - ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(value, 0); - if (value > _rawData.Length) { var rawData = new byte[value]; @@ -56,13 +55,15 @@ public virtual void SetRawDataSize(int value) else if (value < _rawData.Length) { // Clear the rest of the buffer - var span = _rawData.AsSpan().Slice(value); + var span = _rawData.AsSpan().Slice((int)value); span.Fill(0); } + + RawDataSize = value; } /// - public override void Read(PEImageReader reader) + public sealed override void Read(PEImageReader reader) { var size = (int)Size; if (_rawData.Length < size) @@ -77,15 +78,17 @@ public override void Read(PEImageReader reader) reader.Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file while reading additional data in {nameof(PERawDataDirectory)}"); return; } - + + RawDataSize = (uint)size; + HeaderSize = ComputeHeaderSize(reader); } /// - public override void Write(PEImageWriter writer) + public sealed override void Write(PEImageWriter writer) { var rawDataSize = RawDataSize; - var span = _rawData.AsSpan().Slice(0, rawDataSize); + var span = _rawData.AsSpan().Slice(0, (int)rawDataSize); writer.Write(span); } @@ -96,7 +99,7 @@ public override void Write(PEImageWriter writer) public override void WriteAt(uint offset, ReadOnlySpan source) => DataUtils.WriteAt(_rawData, offset, source); - protected override uint ComputeHeaderSize(PELayoutContext context) + protected sealed 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/PEResourceDataEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs index a54ccb3..1d04d65 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs @@ -68,7 +68,7 @@ protected override bool PrintMembers(StringBuilder builder) return true; } - public override unsafe void UpdateLayout(PELayoutContext layoutContext) + protected override unsafe void UpdateLayoutCore(PELayoutContext layoutContext) { Size = (uint)sizeof(RawImageResourceDataEntry); } @@ -97,11 +97,23 @@ internal override unsafe void Read(in ReaderContext context) return; } - Data = new PEResourceData + var resourceDataPosition = (uint)(section.Position + rawDataEntry.OffsetToData - section.RVA); + + if (!context.TryFindResourceDataByPosition(resourceDataPosition, out var resourceData)) { - Position = section.Position + rawDataEntry.OffsetToData - section.RVA, - Size = rawDataEntry.Size, - }; + resourceData = new PEResourceData + { + Position = section.Position + rawDataEntry.OffsetToData - section.RVA, + Size = rawDataEntry.Size, + // Force required alignment to 1 byte when reading from disk + RequiredPositionAlignment = 1, + RequiredSizeAlignment = 1, + }; + + context.AddResourceDataByPosition(resourceDataPosition, resourceData); + } + + Data = resourceData; } public override void Write(PEImageWriter writer) diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs index 6d69aab..c9af390 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs @@ -44,7 +44,6 @@ protected override uint ComputeHeaderSize(PELayoutContext context) public override void Read(PEImageReader reader) { reader.Position = Position; - Root.Position = Position; _tempResourceStrings = new(); @@ -61,7 +60,6 @@ public override void Read(PEImageReader reader) } // Read the content of the resource data (that should usually be stored after the resource strings) - bool isFirstDataEntry = true; foreach (var resourceEntry in _tempResourceEntries) { if (resourceEntry is not PEResourceDataEntry dataEntry) @@ -71,22 +69,12 @@ public override void Read(PEImageReader reader) var resourceData = dataEntry.Data; - // 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(resourceData.Position, resourceData.RequiredPositionAlignment); - if (checkPosition != resourceData.Position) - { - resourceData.RequiredPositionAlignment = 1; - } - else if (isFirstDataEntry && (resourceData.Position & 0xF) == 0) - { - // If we are the first resource data entry and the position is aligned on 16 bytes, we can assume this alignment - resourceData.RequiredPositionAlignment = 16; - } + // Force alignment to 1 when reading as we want always to recover intermediate data + resourceData.RequiredPositionAlignment = 1; + resourceData.RequiredSizeAlignment = 1; // Read the data resourceData.Read(reader); - - isFirstDataEntry = false; } HeaderSize = ComputeHeaderSize(reader); @@ -108,11 +96,14 @@ internal override IEnumerable CollectImplicitSectionDataList() if (_tempResourceEntries is not null) { + var resourceData = new HashSet(); + foreach (var data in _tempResourceEntries) { yield return data; - if (data is PEResourceDataEntry dataEntry) + // If a resource data is not already in the set, we add it and return it + if (data is PEResourceDataEntry dataEntry && resourceData.Add(dataEntry.Data)) { yield return dataEntry.Data; } diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs index 8aae146..165f1ed 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs @@ -109,7 +109,12 @@ internal override unsafe void Read(in ReaderContext context) var byNames = CollectionsMarshal.AsSpan(ByNames); foreach (ref var item in byNames) { - context.Strings.Add(item.Name); + // They can be duplicated, so we need to check if it is already in the context + if (!context.Strings.Contains(item.Name)) + { + context.Strings.Add(item.Name); + } + context.Entries.Add(item.Entry); item.Entry.Read(context); } @@ -190,10 +195,16 @@ private void ReadEntry(in ReaderContext context, RawImageResourceDirectoryEntry if ((rawEntry.NameOrId & IMAGE_RESOURCE_NAME_IS_STRING) != 0) { - var resourceString = new PEResourceString() + var position = (uint)(context.Directory.Position + (rawEntry.NameOrId & ~IMAGE_RESOURCE_NAME_IS_STRING)); + + // ResourceString might be reused, so we need to check if it is already in the context + if (!context.TryFindResourceStringByPosition(position, out var resourceString)) { - Position = context.Directory.Position + (rawEntry.NameOrId & ~IMAGE_RESOURCE_NAME_IS_STRING) - }; + resourceString = new PEResourceString() + { + Position = context.Directory.Position + (rawEntry.NameOrId & ~IMAGE_RESOURCE_NAME_IS_STRING) + }; + } ByNames.Add(new(resourceString, entry)); } @@ -204,7 +215,7 @@ private void ReadEntry(in ReaderContext context, RawImageResourceDirectoryEntry } } - public override unsafe void UpdateLayout(PELayoutContext layoutContext) + protected override unsafe void UpdateLayoutCore(PELayoutContext layoutContext) { Size = CalculateSize(); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs index f17ea7c..c88796c 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace LibObjectFile.PE; @@ -16,7 +17,37 @@ public abstract class PEResourceEntry : PESectionData internal abstract void Read(in ReaderContext context); - internal readonly record struct ReaderContext(PEImageReader Reader, PEResourceDirectory Directory, List Strings, List Entries); + internal readonly record struct ReaderContext(PEImageReader Reader, PEResourceDirectory Directory, List Strings, List Entries) + { + private readonly Dictionary _resourceDataByPosition = new(); + + public bool TryFindResourceDataByPosition(uint position, [NotNullWhen(true)] out PEResourceData? resourceData) + { + return _resourceDataByPosition.TryGetValue(position, out resourceData); + } + + public void AddResourceDataByPosition(uint position, PEResourceData resourceData) + { + _resourceDataByPosition.Add(position, resourceData); + } + + + public bool TryFindResourceStringByPosition(uint position, [NotNullWhen(true)] out PEResourceString? resourceString) + { + resourceString = null; + + foreach (var item in Strings) + { + if (item.Position == position) + { + resourceString = item; + return true; + } + } + return false; + } + + } /// protected override void ValidateParent(ObjectElement parent) diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs index f39d56c..3524650 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs @@ -43,18 +43,19 @@ public PEResourceString(string text) /// public override void Read(PEImageReader reader) { + reader.Position = Position; var length = reader.ReadU16(); using var tempSpan = TempSpan.Create(length, out var span); - reader.Position = Position; - var read = reader.Read(tempSpan.AsBytes); - if (read != length) + var spanBytes = tempSpan.AsBytes; + var read = reader.Read(spanBytes); + if (read != spanBytes.Length) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceString, $"Invalid resource string length. Expected: {length}, Read: {read}"); } Text = new string(span); // Update the size after reading the string - Size = (uint)(sizeof(ushort) + length); + Size = CalculateSize(); } /// @@ -65,11 +66,13 @@ public override void Write(PEImageWriter writer) writer.Write(MemoryMarshal.Cast(Text.AsSpan())); } - public override void UpdateLayout(PELayoutContext layoutContext) + protected override void UpdateLayoutCore(PELayoutContext layoutContext) { - Size = (uint)(sizeof(ushort) + Text.Length * sizeof(char)); + Size = CalculateSize(); } + private uint CalculateSize() => (uint)(sizeof(ushort) + Text.Length * sizeof(char)); + /// protected override bool PrintMembers(StringBuilder builder) { diff --git a/src/LibObjectFile/PE/DataDirectory/PESectionVirtualSizeMode.cs b/src/LibObjectFile/PE/DataDirectory/PESectionVirtualSizeMode.cs new file mode 100644 index 0000000..3b66834 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PESectionVirtualSizeMode.cs @@ -0,0 +1,24 @@ +// 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; + +/// +/// Defines the way the virtual size of a section is computed. +/// +public enum PESectionVirtualSizeMode +{ + /// + /// The virtual size of the section is automatically computed by the raw size of its content without file alignment. + /// + Auto = 0, + + /// + /// The virtual size of the section is fixed. + /// + /// + /// This is usually the case when the virtual size is requested to be bigger than the raw size (e.g. uninitialized data). + /// + Fixed = 1, +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs index edf4221..b84cdf4 100644 --- a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs @@ -71,7 +71,7 @@ public override unsafe void Read(PEImageReader reader) { Revision = header.Revision, Type = header.Type, - Data = reader.ReadAsStream(header.Length - 8) + Data = reader.ReadAsStream(header.Length - (uint)sizeof(RawCertificateHeader)) }; Certificates.Add(certificate); @@ -87,26 +87,52 @@ public override unsafe void Read(PEImageReader reader) reader.Position = AlignHelper.AlignUp(reader.Position, 8); } - var computedSize = ComputeHeaderSize(reader); + var computedSize = CalculateSize(); Debug.Assert(computedSize == Size); } - private uint ComputeHeaderSize(PEVisitorContext context) + private unsafe uint CalculateSize() { var size = 0u; foreach (var certificate in Certificates) { - size += 8; // CertificateSize + Version + Type + size += (uint)sizeof(RawCertificateHeader); // CertificateSize + Version + Type size += (uint)certificate.Data.Length; size = AlignHelper.AlignUp(size, 8U); } return size; } + + protected override void UpdateLayoutCore(PELayoutContext layoutContext) + { + Size = CalculateSize(); + } - public override void Write(PEImageWriter writer) + public override unsafe void Write(PEImageWriter writer) { - throw new NotImplementedException(); + var position = 0U; + foreach (var certificate in Certificates) + { + var header = new RawCertificateHeader + { + Length = (uint)((uint)sizeof(RawCertificateHeader) + certificate.Data.Length), + Revision = certificate.Revision, + Type = certificate.Type + }; + + writer.Write(header); + writer.Write(certificate.Data); + + position += header.Length; + var zeroSize = AlignHelper.AlignUp(position, 8U) - position; + if (zeroSize > 0) + { + writer.WriteZero((int)zeroSize); + position += zeroSize; + } + } + } private struct RawCertificateHeader diff --git a/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs index 3a0b91a..63b02eb 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs @@ -106,7 +106,7 @@ private protected void Write64(PEImageWriter writer, List entries) writer.WriteU64(0); } - public sealed override unsafe void UpdateLayout(PELayoutContext context) + protected sealed override unsafe void UpdateLayoutCore(PELayoutContext context) { Size = (uint)((Count + 1) * (Is32Bits ? sizeof(VA32) : sizeof(VA64))); } diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs index 6701528..a3b8820 100644 --- a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.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. @@ -19,8 +19,6 @@ public sealed class PETlsDirectory32 : PETlsDirectory public unsafe PETlsDirectory32() : base(sizeof(PETlsDirectoryData32)) { } - - public override unsafe int RawDataSize => sizeof(PETlsDirectoryData32); /// /// The starting address of the TLS template. @@ -102,9 +100,9 @@ public ref PETlsDirectoryData32 Data get => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); } - public override unsafe void SetRawDataSize(int value) + public override unsafe void SetRawDataSize(uint value) { - ArgumentOutOfRangeException.ThrowIfLessThan(value, sizeof(PETlsDirectoryData32)); + ArgumentOutOfRangeException.ThrowIfLessThan(value, (uint)sizeof(PETlsDirectoryData32)); base.SetRawDataSize(value); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs index ab31227..efe921b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.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. @@ -20,8 +20,6 @@ public unsafe PETlsDirectory64() : base(sizeof(PETlsDirectoryData64)) { } - public override unsafe int RawDataSize => sizeof(PETlsDirectoryData64); - /// /// The starting address of the TLS template. /// @@ -102,9 +100,9 @@ public ref PETlsDirectoryData64 Data get => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); } - public override unsafe void SetRawDataSize(int value) + public override unsafe void SetRawDataSize(uint value) { - ArgumentOutOfRangeException.ThrowIfLessThan(value, sizeof(PETlsDirectoryData64)); + ArgumentOutOfRangeException.ThrowIfLessThan(value, (uint)sizeof(PETlsDirectoryData64)); base.SetRawDataSize(value); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs index 9e9c1e0..8647393 100644 --- a/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.cs +++ b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.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. @@ -14,7 +14,7 @@ public RawImportFunctionEntry64(ulong hintNameTableRVA) _hintNameTableRVA = hintNameTableRVA; } - public uint HintNameTableRVA => IsImportByOrdinal ? 0U : (uint)_hintNameTableRVA; + public ulong HintNameTableRVA => IsImportByOrdinal ? 0U : _hintNameTableRVA; public bool IsNull => _hintNameTableRVA == 0; diff --git a/src/LibObjectFile/PE/PEExtraData.cs b/src/LibObjectFile/PE/PEExtraData.cs index bc8b010..cec783c 100644 --- a/src/LibObjectFile/PE/PEExtraData.cs +++ b/src/LibObjectFile/PE/PEExtraData.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,4 +12,6 @@ public abstract class PEExtraData : PEObjectBase protected PEExtraData() { } + + public override bool HasChildren => false; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 618dba1..1536239 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -51,7 +52,7 @@ public static bool TryRead(Stream stream, [NotNullWhen(true)] out PEFile? peFile peFile = new PEFile(false); var reader = new PEImageReader(peFile, stream, options); diagnostics = reader.Diagnostics; - + diagnostics.EnableStackTrace = options.EnableStackTrace; peFile.Read(reader); return !reader.Diagnostics.HasErrors; @@ -61,6 +62,9 @@ public override unsafe void Read(PEImageReader reader) { Debug.Assert(Unsafe.SizeOf() == 64); + Position = 0; + Size = reader.Length; + var diagnostics = reader.Diagnostics; int read = reader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref DosHeader, 1))); if (read != Unsafe.SizeOf()) @@ -268,7 +272,7 @@ public override unsafe void Read(PEImageReader reader) Size = reader.Length; } - private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan sectionHeaders, ReadOnlySpan dataDirectories) + private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan rawSectionHeaders, ReadOnlySpan rawDirectories) { _sections.Clear(); @@ -276,33 +280,38 @@ private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan 0) + if (rawSectionHeaders.Length > 0) { - positionFirstSection = sectionHeaders[0].PointerToRawData; + positionFirstSection = rawSectionHeaders[0].PointerToRawData; } uint positionAfterLastSection = positionFirstSection; // Create sections - foreach (var section in sectionHeaders) + foreach (var rawSection in rawSectionHeaders) { // We don't validate the name - var peSection = new PESection( new PESectionName(section.NameAsString, false), section.RVA, section.VirtualSize) + var section = new PESection( new PESectionName(rawSection.NameAsString, false), rawSection.RVA) { - Position = section.PointerToRawData, - // Use the exact size of the section on disk if virtual size is smaller - // as these are considered as padding between sections - Size = section.VirtualSize < section.SizeOfRawData ? section.VirtualSize : section.SizeOfRawData, - Characteristics = section.Characteristics, + Position = rawSection.PointerToRawData, + Size = rawSection.SizeOfRawData, + Characteristics = rawSection.Characteristics, }; - var positionAfterSection = section.PointerToRawData + section.SizeOfRawData; + // Sets the virtual size of the section (auto or fixed) + section.SetVirtualSizeMode( + rawSection.VirtualSize <= rawSection.SizeOfRawData + ? PESectionVirtualSizeMode.Auto + : PESectionVirtualSizeMode.Fixed, + rawSection.VirtualSize); + + var positionAfterSection = rawSection.PointerToRawData + rawSection.SizeOfRawData; if (positionAfterSection > positionAfterLastSection) { positionAfterLastSection = positionAfterSection; } - _sections.Add(peSection); + _sections.Add(section); } // A list that contains all the section data that we have created (e.g. from directories, import tables...) @@ -311,20 +320,14 @@ private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan dataParts; + + if (container is PECompositeSectionData compositeContainer) + { + dataParts = compositeContainer.Content; + } + else if (container is PESection section) + { + dataParts = section.Content; + } + else { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Unable to find the section for {vObj} at position {vObj.Position}"); + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid container {container} for {vObj} at position {vObj.Position}"); return; } - - sectionIndex = Math.Min(sectionIndex, Math.Max(0, _sections.Count - 1)); - var peSection = _sections[sectionIndex]; - // Insert the directory at the right position in the section - int sectionDataIndex = 0; - var dataParts = peSection.Content; - - bool addedToSectionData = false; - - for (; sectionDataIndex < dataParts.Count; sectionDataIndex++) + int insertIndex = 0; + for (; insertIndex < dataParts.Count; insertIndex++) { - var sectionData = dataParts[sectionDataIndex]; + var sectionData = dataParts[insertIndex]; if (vObj.Position < sectionData.Position) { break; } - else if (sectionData.Contains(vObj.Position, (uint)vObj.Size)) - { - if (sectionData is PECompositeSectionData compositeSectionData) - { - addedToSectionData = true; - compositeSectionData.Content.Add(vObj); - break; - } - else - { - reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data {sectionData} at position {sectionData.Position} contains {vObj} at position {vObj.Position}"); - return; - } - } } - if (!addedToSectionData) - { - dataParts.Insert(sectionDataIndex, vObj); - } + dataParts.Insert(insertIndex, vObj); } // Make sure that we have a proper stream for all directories - foreach (var directory in Directories) + for (var i = 0; i < Directories.Count; i++) { - FillDirectoryWithStreams(reader, directory); + var entry = Directories[i]; + if (entry is PEDataDirectory directory) + { + FillDirectoryWithStreams(reader, directory); + } } // Create all remaining extra data section data (before and after sections) @@ -475,30 +474,36 @@ private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan /// For a list of section data (e.g. used by a section or a ImportAddressTableDirectory...), we need to fill any hole with the actual stream of original data from the image /// - private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, PEObjectBase container, ObjectList dataParts, ulong startPosition, ulong totalSize) + private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, PEObjectBase parent, ObjectList dataParts, uint startPosition, uint totalSize, uint? totalVirtualSizeOpt) { 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 @@ -541,77 +544,90 @@ private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, { return; } - + + // We are working on position, while the list is ordered by VirtualAddress listOrderedByPosition.Sort((a, b) => a.Position.CompareTo(b.Position)); for (var i = 0; i < listOrderedByPosition.Count; i++) { var data = listOrderedByPosition[i]; data.Index = i; } - - if (listOrderedByPosition.Count == 0) + + for (var i = 0; i < listOrderedByPosition.Count; i++) { - var size = endPosition - currentPosition; - imageReader.Position = currentPosition; - var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size)) - { - Position = currentPosition, - Parent = container, - }; + var data = listOrderedByPosition[i]; - 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)); - // 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) + { + var size = (uint)(data.Position - currentPosition); + imageReader.Position = currentPosition; - if (currentPosition < data.Position) - { - var size = data.Position - currentPosition; - imageReader.Position = currentPosition; - - var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size)) - { - Position = currentPosition, - Parent = container, - }; - - listOrderedByPosition.Insert(i, sectionData); - currentPosition = data.Position; - i++; - } - else if (currentPosition > data.Position) + var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size)) { - imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data position {currentPosition} > {data.Position} in {container}"); - return; - } + Position = currentPosition, + Parent = parent, + }; - var dataSize = AlignHelper.AlignUp(data.Size, data.GetRequiredSizeAlignment(imageReader.File)); - currentPosition += dataSize; + listOrderedByPosition.Insert(i, sectionData); + currentPosition += size; + i++; + } + else if (currentPosition > data.Position) + { + imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data position 0x{currentPosition:X} > 0x{data.Position:X} in {parent}"); + return; } + + var dataSize = (uint)data.Size; + //var dataSize = AlignHelper.AlignUp(data.Size, data.GetRequiredSizeAlignment(imageReader.File)); + currentPosition += dataSize; } if (currentPosition < endPosition) { var size = endPosition - currentPosition; - imageReader.Position = currentPosition; - var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size)) + + uint paddingSize = 0; + + if (totalVirtualSizeOpt.HasValue && totalVirtualSizeOpt.Value < totalSize) { - Position = currentPosition, - Parent = container, - }; + paddingSize = totalSize - totalVirtualSizeOpt.Value; + } - listOrderedByPosition.Add(sectionData); + if (paddingSize > size) + { + imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid padding size 0x{paddingSize:X} while remaining size is 0x{size:X} at position 0x{currentPosition:X} > 0x{endPosition:X} in {parent}"); + return; + } + + size -= paddingSize; + + // If we have actual data before the padding, create a normal stream + if (size > 0) + { + imageReader.Position = currentPosition; + var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size)) + { + Position = currentPosition, + Parent = parent, + }; + listOrderedByPosition.Add(sectionData); + currentPosition += size; + } + + // Create a padding stream if we have a virtual size smaller than the actual size + if (paddingSize > 0) + { + imageReader.Position = currentPosition; + ((PESection)parent).PaddingStream = imageReader.ReadAsStream(paddingSize); + } } else if (currentPosition > endPosition) { - imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data position {currentPosition} > {endPosition} in {container}"); + imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid section data position 0x{currentPosition:X} > 0x{endPosition:X} in {parent}"); } // Make sure to update the indices after inserting the missing streams @@ -622,14 +638,26 @@ private static void FillSectionDataWithMissingStreams(PEImageReader imageReader, } } - private static void FillExtraDataWithMissingStreams(PEImageReader imageReader, PEObjectBase parent, ObjectList list, ulong extraPosition, ulong extraTotalSize) + private static void FillExtraDataWithMissingStreams(PEImageReader imageReader, PEObjectBase parent, ObjectList list, ulong startPosition, ulong totalSize) { - var currentPosition = extraPosition; - imageReader.Position = extraPosition; + var currentPosition = startPosition; + var endPosition = startPosition + totalSize; // We are working on position, while the list is ordered by VirtualAddress var listOrderedByPosition = list.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++) { @@ -639,42 +667,43 @@ private static void FillExtraDataWithMissingStreams(PEImageReader imageReader, P var size = data.Position - currentPosition; imageReader.Position = currentPosition; - var sectionData = new PEStreamExtraData(imageReader.ReadAsStream(size)) + var extraData = new PEStreamExtraData(imageReader.ReadAsStream(size)) { Position = currentPosition, Parent = parent, }; - listOrderedByPosition.Insert(i, sectionData); + listOrderedByPosition.Insert(i, extraData); currentPosition = data.Position; i++; } else if (currentPosition > data.Position) { - imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid extra data position {currentPosition} > {data.Position}"); + imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid extra data position 0x{currentPosition:X} > 0x{data.Position:X}"); return; } currentPosition += data.Size; } - if (currentPosition < extraPosition + extraTotalSize) + if (currentPosition < endPosition) { - var size = extraPosition + extraTotalSize - currentPosition; + var size = endPosition - currentPosition; imageReader.Position = currentPosition; - var sectionData = new PEStreamExtraData(imageReader.ReadAsStream(size)) + var extraData = new PEStreamExtraData(imageReader.ReadAsStream(size)) { Position = currentPosition, Parent = parent, }; - listOrderedByPosition.Add(sectionData); + listOrderedByPosition.Add(extraData); } - else if (currentPosition > extraPosition + extraTotalSize) + else if (currentPosition > endPosition) { - imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid extra data position {currentPosition} > {extraPosition + extraTotalSize}"); + imageReader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid extra data position 0x{currentPosition:X} > 0x{endPosition:X} in {parent}"); } + // Make sure to update the indices after inserting the missing streams for (var i = 0; i < listOrderedByPosition.Count; i++) { @@ -685,6 +714,6 @@ private static void FillExtraDataWithMissingStreams(PEImageReader imageReader, P private static void FillDirectoryWithStreams(PEImageReader imageReader, PEDataDirectory directory) { - FillSectionDataWithMissingStreams(imageReader, directory, directory.Content, directory.Position + directory.HeaderSize, directory.Size - directory.HeaderSize); + FillSectionDataWithMissingStreams(imageReader, directory, directory.Content, (uint)(directory.Position + directory.HeaderSize), (uint)(directory.Size - directory.HeaderSize), null); } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Write.cs b/src/LibObjectFile/PE/PEFile.Write.cs index 9e206f9..409ba35 100644 --- a/src/LibObjectFile/PE/PEFile.Write.cs +++ b/src/LibObjectFile/PE/PEFile.Write.cs @@ -5,13 +5,10 @@ using System; using System.Diagnostics; using System.IO; -using System.Numerics; -using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; using LibObjectFile.Utils; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace LibObjectFile.PE; @@ -145,6 +142,7 @@ public override unsafe void Write(PEImageWriter writer) // Data before sections foreach (var extraData in ExtraDataBeforeSections) { + Debug.Assert(position == extraData.Position); extraData.Write(writer); position += (uint)extraData.Size; } @@ -182,9 +180,38 @@ public override unsafe void Write(PEImageWriter writer) position += (uint)data.Size; } - zeroSize = (int)(AlignHelper.AlignUp(position, writer.PEFile.OptionalHeader.FileAlignment) - position); - writer.WriteZero(zeroSize); - position += (uint)zeroSize; + // Write the padding stream + var paddingSize = (uint)(AlignHelper.AlignUp(position, writer.PEFile.OptionalHeader.FileAlignment) - position); + + // Use the padding stream if it is defined + if (section.PaddingStream is not null) + { + var paddingStreamLength = (uint)section.PaddingStream.Length; + + if (paddingSize >= paddingStreamLength) + { + section.PaddingStream.Position = 0; + section.PaddingStream.CopyTo(writer.Stream); + position += paddingStreamLength; + paddingSize -= paddingStreamLength; + } + else if (paddingSize > 0) + { + section.PaddingStream.Position = paddingStreamLength - paddingSize; + section.PaddingStream.CopyTo(writer.Stream); + position += paddingSize; + paddingSize = 0; + } + + section.PaddingStream.Position = 0; + } + + // Otherwise pad with remaining zero (in case the padding stream above is not enough) + if (paddingSize > 0) + { + writer.WriteZero((int)paddingSize); + position += paddingSize; + } Debug.Assert(position == writer.Position); } diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index f6f1982..4245c33 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -28,6 +28,12 @@ public sealed partial class PEFile : PEObjectBase private readonly ObjectList _sections; private int _unsafeNegativePEHeaderOffset; + static PEFile() + { + // Required for resolving code pages + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } + /// /// Initializes a new instance of the class. /// @@ -76,7 +82,7 @@ public unsafe int UnsafeNegativePEHeaderOffset { throw new InvalidOperationException("Setting a negative PEHeader offset is not compatible with having a DOS Stub and DosStubExtra"); } - + _unsafeNegativePEHeaderOffset = value; } } @@ -154,18 +160,38 @@ public Stream? DosStubExtra /// Gets the sections. /// public ObjectList Sections => _sections; - + /// /// Gets the data present after the sections in the file (e.g ) /// public ObjectList ExtraDataAfterSections { get; } - public PESection AddSection(PESectionName name, uint virtualAddress, uint virtualSize) + /// + /// Used for testing when the size of initialized data is not correctly computed because an existing PE is using a different algorithm for computing the size. + /// + internal uint ForceSizeOfInitializedData { get; set; } + + public override bool HasChildren => true; + + + public PESection AddSection(PESectionName name, RVA rva) { - var section = new PESection(name, virtualAddress, virtualSize) + var section = new PESection(name, rva) { Characteristics = PESection.GetDefaultSectionCharacteristics(name) }; + section.SetVirtualSizeModeToAuto(); + _sections.Add(section); + return section; + } + + public PESection AddSection(PESectionName name, RVA rva, uint virtualSize) + { + var section = new PESection(name, rva) + { + Characteristics = PESection.GetDefaultSectionCharacteristics(name) + }; + section.SetVirtualSizeModeToFixed(virtualSize); _sections.Add(section); return section; } @@ -182,7 +208,13 @@ public bool TryFindSection(RVA rva, uint size, [NotNullWhen(true)] out PESection public bool TryFindContainerByRVA(RVA rva, [NotNullWhen(true)] out PEObject? container) => _sections.TryFindByRVA(rva, true, out container); - + + + protected override bool TryFindByPositionInChildren(uint position, [NotNullWhen(true)] out PEObjectBase? result) + => _sections.TryFindByPosition(position, true, out result) || + ExtraDataBeforeSections.TryFindByPosition(position, true, out result) || + ExtraDataAfterSections.TryFindByPosition(position, true, out result); + public void RemoveSection(PESectionName name) { ArgumentNullException.ThrowIfNull(name); @@ -193,7 +225,7 @@ public void RemoveSection(PESectionName name) _sections.Remove(section); } - + public void ClearSections() => _sections.Clear(); public PESection GetSection(PESectionName name) @@ -235,6 +267,7 @@ public List GetAllSectionData() { count += section.Content.Count; } + dataList.Capacity = count; foreach (var section in sections) @@ -259,7 +292,7 @@ public List GetAllSectionData() public bool TryFindByVA(VA32 va, [NotNullWhen(true)] out PEObject? result, out RVO offset) { if (!IsPE32) throw new InvalidOperationException("PEFile is not a PE32 image"); - + var rawRva = va - (uint)OptionalHeader.ImageBase; var rva = (RVA)(uint)rawRva; if (rawRva <= int.MaxValue && TryFindContainerByRVA(rva, out result)) @@ -309,7 +342,7 @@ public void UpdateLayout(DiagnosticBag diagnostics) } /// - public override unsafe void UpdateLayout(PELayoutContext context) + protected override unsafe void UpdateLayoutCore(PELayoutContext context) { var position = 0U; @@ -328,14 +361,15 @@ public override unsafe void UpdateLayout(PELayoutContext context) // COFF header position += (uint)sizeof(PECoffHeader); - + // TODO: update other DosHeader fields - position += (uint)(IsPE32 ? sizeof(RawImageOptionalHeader32) : sizeof(RawImageOptionalHeader64)); + position += (uint)(IsPE32 ? RawImageOptionalHeader32.MinimumSize : RawImageOptionalHeader64.MinimumSize); // Update directories position += (uint)(Directories.Count * sizeof(RawImageDataDirectory)); + position += (uint)(sizeof(RawImageSectionHeader) * _sections.Count); // TODO: Additional optional header size? // Data before sections @@ -390,7 +424,7 @@ public override unsafe void UpdateLayout(PELayoutContext context) RVA previousEndOfRVA = 0U; foreach (var section in _sections) { - section.Position = position; + section.Position = section.Size > 0 ? position : (ulong)0; section.UpdateLayout(context); if (section.RVA < previousEndOfRVA) { @@ -400,25 +434,33 @@ public override unsafe void UpdateLayout(PELayoutContext context) var sectionSize = (uint)section.Size; position += sectionSize; - var virtualSizeDiskAligned = AlignHelper.AlignUp(section.VirtualSize, OptionalHeader.FileAlignment); + var minDataSize = AlignHelper.AlignUp((uint)section.VirtualSize, OptionalHeader.FileAlignment); + //minDataSize = section.Size > minDataSize ? (uint)section.Size : minDataSize; + //var minDataSize = (uint)section.Size; if ((section.Characteristics & SectionCharacteristics.ContainsCode) != 0) { - OptionalHeader.OptionalHeaderCommonPart1.SizeOfCode += virtualSizeDiskAligned; + OptionalHeader.OptionalHeaderCommonPart1.SizeOfCode += minDataSize; } else if ((section.Characteristics & SectionCharacteristics.ContainsInitializedData) != 0) { - OptionalHeader.OptionalHeaderCommonPart1.SizeOfInitializedData += virtualSizeDiskAligned; + OptionalHeader.OptionalHeaderCommonPart1.SizeOfInitializedData += minDataSize; } else if ((section.Characteristics & SectionCharacteristics.ContainsUninitializedData) != 0) { - OptionalHeader.OptionalHeaderCommonPart1.SizeOfUninitializedData += virtualSizeDiskAligned; + OptionalHeader.OptionalHeaderCommonPart1.SizeOfUninitializedData += minDataSize; } // Update the end of the RVA previousEndOfRVA = section.RVA + AlignHelper.AlignUp(section.VirtualSize, OptionalHeader.SectionAlignment); } + // Used by tests to force the size of initialized data that seems to be calculated differently from the standard way by some linkers + if (ForceSizeOfInitializedData != 0) + { + OptionalHeader.OptionalHeaderCommonPart1.SizeOfInitializedData = ForceSizeOfInitializedData; + } + // Update the (virtual) size of the image OptionalHeader.OptionalHeaderCommonPart2.SizeOfImage = previousEndOfRVA; @@ -463,5 +505,4 @@ protected override bool PrintMembers(StringBuilder builder) builder.Append($"Directories[{Directories.Count}], Sections[{Sections.Count}]"); return true; } - } diff --git a/src/LibObjectFile/PE/PEImageReaderOptions.cs b/src/LibObjectFile/PE/PEImageReaderOptions.cs index 9ba1282..3022b10 100644 --- a/src/LibObjectFile/PE/PEImageReaderOptions.cs +++ b/src/LibObjectFile/PE/PEImageReaderOptions.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. @@ -7,4 +7,6 @@ namespace LibObjectFile.PE; public class PEImageReaderOptions { public bool IsReadOnly { get; init; } + + public bool EnableStackTrace { get; init; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEModuleHandleLink.cs b/src/LibObjectFile/PE/PEModuleHandleLink.cs index 57f1afc..cfc1fff 100644 --- a/src/LibObjectFile/PE/PEModuleHandleLink.cs +++ b/src/LibObjectFile/PE/PEModuleHandleLink.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. @@ -10,7 +10,7 @@ namespace LibObjectFile.PE; /// A link to a module handle. /// [DebuggerDisplay("{ToString(),nq}")] -public readonly record struct PEModuleHandleLink(PEStreamSectionData? Container, RVO RVO) : IPELink +public readonly record struct PEModuleHandleLink(PESection? Container, RVO RVO) : IPELink { /// public override string ToString() => this.ToDisplayTextWithRVA(); diff --git a/src/LibObjectFile/PE/PEObject.cs b/src/LibObjectFile/PE/PEObject.cs index 32f873f..4e53273 100644 --- a/src/LibObjectFile/PE/PEObject.cs +++ b/src/LibObjectFile/PE/PEObject.cs @@ -1,8 +1,9 @@ -// Copyright (c) Alexandre Mutel. All rights reserved. +// Copyright (c) Alexandre Mutel. All rights reserved. // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -15,15 +16,13 @@ namespace LibObjectFile.PE; /// public abstract class PEObject : PEObjectBase { + private uint _customVirtualSize; + private bool _useCustomVirtualSize; + protected PEObject() { } - /// - /// Gets a value indicating whether this object has children. - /// - public abstract bool HasChildren { get; } - /// /// The address of the first byte of the section when loaded into memory, relative to the image base. /// @@ -32,7 +31,7 @@ protected PEObject() /// /// The size of this object in virtual memory. /// - public virtual uint VirtualSize => (uint)Size; + public uint VirtualSize => _useCustomVirtualSize ? _customVirtualSize : (uint)Size; /// /// Checks if the specified virtual address is contained in this object. @@ -58,7 +57,7 @@ public bool ContainsVirtual(RVA rva, uint size) /// The virtual address to search for. /// The virtual object that contains the virtual address, if found. /// true if the virtual object was found; otherwise, false. - public bool TryFindByRVA(RVA rva, out PEObject? result) + public bool TryFindByRVA(RVA rva, [NotNullWhen(true)] out PEObject? result) { if (ContainsVirtual(rva)) { @@ -82,7 +81,7 @@ public bool TryFindByRVA(RVA rva, out PEObject? result) /// The virtual object that contains the virtual address, if found. /// true if the virtual object was found; otherwise, false. /// - protected virtual bool TryFindByRVAInChildren(RVA rva, out PEObject? result) + protected virtual bool TryFindByRVAInChildren(RVA rva, [NotNullWhen(true)] out PEObject? result) { throw new NotImplementedException("This method must be implemented by PEVirtualObject with children"); } @@ -114,6 +113,58 @@ protected override void ValidateParent(ObjectElement parent) { } + private protected void SetCustomVirtualSize(uint customVirtualSize) + { + _customVirtualSize = customVirtualSize; + _useCustomVirtualSize = true; + } + + private protected void ResetCustomVirtualSize() + { + _useCustomVirtualSize = false; + } + + public sealed override void UpdateLayout(PELayoutContext layoutContext) + { + var section = layoutContext.CurrentSection; + + if (section is null) + { + // Update the layout normally + base.UpdateLayout(layoutContext); + } + else + { + // If the section is auto, we reset the custom virtual size to Size + if (section.VirtualSizeMode == PESectionVirtualSizeMode.Auto) + { + ResetCustomVirtualSize(); + } + + var currentSectionVirtualSize = this.RVA - section.RVA; + + // Update the layout normally + base.UpdateLayout(layoutContext); + + // Accumulate the virtual size of the section + currentSectionVirtualSize += (uint)Size; + + // If the section is fixed, we update the virtual size of the current object being visited + if (section.VirtualSizeMode == PESectionVirtualSizeMode.Fixed && currentSectionVirtualSize > section.VirtualSize) + { + var virtualSizeToRemove = currentSectionVirtualSize - section.VirtualSize; + if (virtualSizeToRemove >= VirtualSize) + { + SetCustomVirtualSize(0); + } + else + { + SetCustomVirtualSize(VirtualSize - virtualSizeToRemove); + } + } + } + } + public static ObjectList CreateObjectList(PEObject parent) where TPEObject : PEObject { ObjectList objectList = default; diff --git a/src/LibObjectFile/PE/PEObjectBase.cs b/src/LibObjectFile/PE/PEObjectBase.cs index 155b757..dcd0abd 100644 --- a/src/LibObjectFile/PE/PEObjectBase.cs +++ b/src/LibObjectFile/PE/PEObjectBase.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace LibObjectFile.PE; @@ -21,7 +22,12 @@ public abstract class PEObjectBase : ObjectFileElement public PEFile? GetPEFile() => FindParent(); - + + /// + /// Gets a value indicating whether this object has children. + /// + public abstract bool HasChildren { get; } + public virtual int ReadAt(uint offset, Span destination) { throw new NotSupportedException($"The read operation is not supported for {this.GetType().FullName}"); @@ -32,6 +38,30 @@ public virtual void WriteAt(uint offset, ReadOnlySpan source) throw new NotSupportedException($"The write operation is not supported for {this.GetType().FullName}"); } + + public bool TryFindByPosition(uint position, [NotNullWhen(true)] out PEObjectBase? result) + { + if (Contains(position)) + { + if (HasChildren && TryFindByPositionInChildren(position, out result)) + { + return true; + } + + result = this; + return true; + } + + result = null; + return false; + } + + protected virtual bool TryFindByPositionInChildren(uint position, [NotNullWhen(true)] out PEObjectBase? result) + { + throw new NotImplementedException("This method must be implemented by PEVirtualObject with children"); + } + + /// /// Gets the required alignment for this object. /// diff --git a/src/LibObjectFile/PE/PEObjectExtensions.cs b/src/LibObjectFile/PE/PEObjectExtensions.cs index a19a0b2..73a8310 100644 --- a/src/LibObjectFile/PE/PEObjectExtensions.cs +++ b/src/LibObjectFile/PE/PEObjectExtensions.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. @@ -88,10 +88,11 @@ public static bool TryFindByRVA(this ObjectList list, RVA if (recurse && trySectionData.TryFindByRVA(virtualAddress, out var virtualItem)) { item = virtualItem; - return item is not null; } - - item = trySectionData; + else + { + item = trySectionData; + } return true; } @@ -109,4 +110,30 @@ public static bool TryFindByRVA(this ObjectList list, RVA return false; } + + public static bool TryFindByPosition(this ObjectList list, uint position, bool recurse, [NotNullWhen(true)] out PEObjectBase? item) + where TPEObject : PEObjectBase + { + // Cannot binary search because position/size can be null in the middle of a list (e.g. for uninitialized sections) + var dataParts = CollectionsMarshal.AsSpan(list.UnsafeList); + foreach (var trySectionData in dataParts) + { + if (trySectionData.Contains(position)) + { + if (recurse && trySectionData.TryFindByPosition(position, out var virtualItem)) + { + item = virtualItem; + } + else + { + item = trySectionData; + } + return true; + } + } + + item = null; + return false; + } + } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index dda6840..5311e19 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -326,7 +326,7 @@ private static void Print(PEBaseRelocationBlock block, ref TextWriterIndenter wr var section = block.SectionLink.Container!; - section.TryFindSectionData(relocRVA, out var sectionData); + section.TryFindSectionDataByRVA(relocRVA, out var sectionData); if (reloc.Type == PEBaseRelocationType.Dir64) @@ -388,8 +388,8 @@ private static void Print(PEDelayImportDirectory data, ref TextWriterIndenter wr writer.WriteLine($"[{i}] Attributes = {dirEntry.Attributes}"); writer.WriteLine($"[{i}] DelayImportAddressTable {PELink(dirEntry.DelayImportAddressTable)}"); writer.WriteLine($"[{i}] DelayImportNameTable {PELink(dirEntry.DelayImportNameTable)}"); - writer.WriteLine($"[{i}] BoundImportAddressTable {PELink(dirEntry.BoundImportAddressTable)}"); - writer.WriteLine($"[{i}] UnloadDelayInformationTable {PELink(dirEntry.UnloadDelayInformationTable)}"); + writer.WriteLine($"[{i}] BoundImportAddressTable {dirEntry.BoundImportAddressTableLink}"); + writer.WriteLine($"[{i}] UnloadDelayInformationTable {dirEntry.UnloadDelayInformationTableLink}"); writer.WriteLine(); } } diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index cc88a8f..ec1d723 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -4,7 +4,7 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Drawing; +using System.IO; using System.Reflection.PortableExecutable; using System.Text; using LibObjectFile.Collections; @@ -18,15 +18,18 @@ namespace LibObjectFile.PE; public sealed class PESection : PEObject { private readonly ObjectList _content; + private PESectionVirtualSizeMode _virtualSizeMode; - public PESection(PESectionName name, RVA rva, RVA virtualSize) + public PESection(PESectionName name, RVA rva) { Name = name; RVA = rva; - VirtualSize = virtualSize; _content = PEObject.CreateObjectList(this); // Most of the time readable Characteristics = SectionCharacteristics.MemRead; + + // A PESection always has a custom virtual size calculated from its content + SetCustomVirtualSize(0); } /// @@ -36,12 +39,6 @@ public PESection(PESectionName name, RVA rva, RVA virtualSize) public override bool HasChildren => true; - /// - /// The total size of the section when loaded into memory. - /// If this value is greater than , the section is zero-padded. - /// - public override uint VirtualSize { get; } - /// /// Flags that describe the characteristics of the section. /// @@ -51,6 +48,39 @@ public PESection(PESectionName name, RVA rva, RVA virtualSize) /// Gets the list of data associated with this section. /// public ObjectList Content => _content; + + /// + /// Gets the mode of the virtual size of this section. + /// + public PESectionVirtualSizeMode VirtualSizeMode => _virtualSizeMode; + + /// + /// Gets or sets the stream added to the end of the section to pad it to a specific size and fill it with specific padding data. + /// + public Stream? PaddingStream { get; set; } + + /// + /// Sets the virtual size of this section to be automatically computed from its raw size. + /// + /// + /// The layout of the PEFile should be updated after calling this method via . + /// + public void SetVirtualSizeModeToAuto() => SetVirtualSizeMode(PESectionVirtualSizeMode.Auto, 0); + + /// + /// Sets the virtual size of this section to a fixed size. + /// + /// The virtual size of the section. + /// + /// The layout of the PEFile should be updated after calling this method via . + /// + public void SetVirtualSizeModeToFixed(uint virtualSize) => SetVirtualSizeMode(PESectionVirtualSizeMode.Fixed, virtualSize); + + internal void SetVirtualSizeMode(PESectionVirtualSizeMode mode, uint initialSize) + { + _virtualSizeMode = mode; + SetCustomVirtualSize(initialSize); + } /// public override uint GetRequiredPositionAlignment(PEFile file) => file.OptionalHeader.FileAlignment; @@ -64,7 +94,7 @@ public PESection(PESectionName name, RVA rva, RVA virtualSize) /// The virtual address to search for. /// The section data that contains the virtual address, if found. /// true if the section data was found; otherwise, false. - public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESectionData? sectionData) + public bool TryFindSectionDataByRVA(RVA virtualAddress, [NotNullWhen(true)] out PESectionData? sectionData) { var result = _content.TryFindByRVA(virtualAddress, true, out var sectionObj); sectionData = sectionObj as PESectionData; @@ -72,50 +102,65 @@ public bool TryFindSectionData(RVA virtualAddress, [NotNullWhen(true)] out PESec } /// - public override void UpdateLayout(PELayoutContext context) + protected override void UpdateLayoutCore(PELayoutContext context) { - var peFile = context.File; - - var va = RVA; - var position = (uint)Position; - var size = 0U; - foreach (var data in Content) + context.CurrentSection = this; + try { - // Make sure we align the position and the virtual address - var alignment = data.GetRequiredPositionAlignment(context.File); + var peFile = context.File; - if (alignment > 1) + var va = RVA; + var position = (uint)Position; + var size = 0U; + foreach (var data in Content) { - var newPosition = AlignHelper.AlignUp(position, alignment); - size += newPosition - position; - position = newPosition; - va = AlignHelper.AlignUp(va, alignment); + // 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) + { + data.Position = position; + } + + data.UpdateLayout(context); + + var dataSize = AlignHelper.AlignUp((uint)data.Size, data.GetRequiredSizeAlignment(peFile)); + va += dataSize; + position += dataSize; + size += dataSize; } - data.RVA = va; - - if (!context.UpdateSizeOnly) + if (_virtualSizeMode == PESectionVirtualSizeMode.Auto) { - data.Position = position; + SetCustomVirtualSize(size); } - data.UpdateLayout(context); - - var dataSize = AlignHelper.AlignUp((uint)data.Size, data.GetRequiredSizeAlignment(peFile)); - va += dataSize; - position += dataSize; - size += dataSize; + if ((Characteristics & SectionCharacteristics.ContainsUninitializedData) == 0) + { + // The size of a section is the size of the content aligned on the file alignment + var fileAlignment = peFile.OptionalHeader.FileAlignment; + var sizeWithAlignment = AlignHelper.AlignUp(size, fileAlignment); + Size = sizeWithAlignment; + } + else + { + Size = 0; + } + } + finally + { + context.CurrentSection = null; } - - // 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) - //{ - // // Log diagnostics error - // context.Diagnostics.Error(DiagnosticId.PE_ERR_SectionSizeLargerThanVirtualSize, $"Section {Name} size {Size} is greater than the virtual size {VirtualSize}"); - //} } public override void Read(PEImageReader reader) @@ -140,9 +185,12 @@ protected override bool PrintMembers(StringBuilder builder) return true; } - protected override bool TryFindByRVAInChildren(RVA rva, out PEObject? result) + protected override bool TryFindByRVAInChildren(RVA rva, [NotNullWhen(true)] out PEObject? result) => _content.TryFindByRVA(rva, true, out result); + protected override bool TryFindByPositionInChildren(uint position, [NotNullWhen(true)] out PEObjectBase? result) + => _content.TryFindByPosition(position, true, out result); + protected override void UpdateRVAInChildren() { // TODO? diff --git a/src/LibObjectFile/PE/PEStreamExtraData.cs b/src/LibObjectFile/PE/PEStreamExtraData.cs index 28bba91..8a25ca0 100644 --- a/src/LibObjectFile/PE/PEStreamExtraData.cs +++ b/src/LibObjectFile/PE/PEStreamExtraData.cs @@ -47,7 +47,7 @@ public Stream Stream } } - public override void UpdateLayout(PELayoutContext layoutContext) + protected override void UpdateLayoutCore(PELayoutContext layoutContext) { Size = (uint)Stream.Length; } diff --git a/src/LibObjectFile/PE/PEStreamSectionData.cs b/src/LibObjectFile/PE/PEStreamSectionData.cs index 800aaf7..f38f977 100644 --- a/src/LibObjectFile/PE/PEStreamSectionData.cs +++ b/src/LibObjectFile/PE/PEStreamSectionData.cs @@ -4,7 +4,6 @@ using System; using System.IO; -using LibObjectFile.Utils; namespace LibObjectFile.PE; @@ -81,7 +80,7 @@ public uint RequiredSizeAlignment } } - public override void UpdateLayout(PELayoutContext layoutContext) + protected override void UpdateLayoutCore(PELayoutContext layoutContext) { Size = (ulong)Stream.Length; } diff --git a/src/LibObjectFile/PE/PEVisitorContext.cs b/src/LibObjectFile/PE/PEVisitorContext.cs index b36b73c..d3254fb 100644 --- a/src/LibObjectFile/PE/PEVisitorContext.cs +++ b/src/LibObjectFile/PE/PEVisitorContext.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. @@ -21,4 +21,6 @@ internal PELayoutContext(PEFile peFile, DiagnosticBag diagnostics, bool updateSi } public bool UpdateSizeOnly { get; } + + internal PESection? CurrentSection { get; set; } } \ No newline at end of file diff --git a/src/LibObjectFile/Utils/AlignHelper.cs b/src/LibObjectFile/Utils/AlignHelper.cs index 1188355..b24cfde 100644 --- a/src/LibObjectFile/Utils/AlignHelper.cs +++ b/src/LibObjectFile/Utils/AlignHelper.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. @@ -24,7 +24,7 @@ public static ulong AlignUp(ulong value, ulong align) { if (align == 0) throw new ArgumentOutOfRangeException(nameof(align), "Alignment must be > 0"); if (!BitOperations.IsPow2(align)) throw new ArgumentOutOfRangeException(nameof(align), "Alignment must be a power of 2"); - + var nextValue = ((value + align - 1) / align) * align; return nextValue; } From 2ff9b8c37f6a845752c3eef1339664d098045413 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 30 Sep 2024 19:32:06 +0200 Subject: [PATCH 77/87] Fix unit tests on CI for non Windows --- src/LibObjectFile.Tests/PE/PEReaderTests.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index 23bb21b..dc08261 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -132,17 +132,20 @@ public static IEnumerable GetWindowsExeAndDlls() { if (!OperatingSystem.IsWindows()) { - yield break; + yield return [""]; } - - foreach (var file in Directory.EnumerateFiles(Environment.SystemDirectory, "*.exe", SearchOption.TopDirectoryOnly)) + else { - yield return [file]; - } - foreach (var file in Directory.EnumerateFiles(Environment.SystemDirectory, "*.dll", SearchOption.TopDirectoryOnly)) - { - yield return [file]; + foreach (var file in Directory.EnumerateFiles(Environment.SystemDirectory, "*.exe", SearchOption.TopDirectoryOnly)) + { + yield return [file]; + } + + foreach (var file in Directory.EnumerateFiles(Environment.SystemDirectory, "*.dll", SearchOption.TopDirectoryOnly)) + { + yield return [file]; + } } } From 64006dd3dd51027912c3c8eb13a19b6b0419ad6d Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 30 Sep 2024 20:24:45 +0200 Subject: [PATCH 78/87] Improve tests when comparing buffers --- src/LibObjectFile.Tests/Ar/ArTests.cs | 8 +-- src/LibObjectFile.Tests/ByteArrayAssert.cs | 56 +++++++++++++++++++ src/LibObjectFile.Tests/Dwarf/DwarfTests.cs | 16 +++--- src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs | 2 +- src/LibObjectFile.Tests/Elf/ElfTestBase.cs | 4 +- .../LibObjectFile.Tests.csproj | 1 + src/LibObjectFile.Tests/LinuxUtil.cs | 4 +- src/LibObjectFile.Tests/PE/PEReaderTests.cs | 12 ++-- 8 files changed, 81 insertions(+), 22 deletions(-) create mode 100644 src/LibObjectFile.Tests/ByteArrayAssert.cs diff --git a/src/LibObjectFile.Tests/Ar/ArTests.cs b/src/LibObjectFile.Tests/Ar/ArTests.cs index 85f346e..28f43d0 100644 --- a/src/LibObjectFile.Tests/Ar/ArTests.cs +++ b/src/LibObjectFile.Tests/Ar/ArTests.cs @@ -160,13 +160,13 @@ public void CheckInvalidFileEntry() Assert.AreEqual(2U, arFile.Files[0].Size, "Invalid size of file entry[0] found"); Assert.IsInstanceOfType(arFile.Files[0], "Invalid instance of of file entry[0] "); - var fileStream = ((ArBinaryFile) arFile.Files[0]).Stream; + var fileStream = ((ArBinaryFile) arFile.Files[0]).Stream!; var read = new byte[] { (byte)fileStream.ReadByte(), (byte)fileStream.ReadByte() }; - CollectionAssert.AreEqual(new byte[] { 0, 1}, read, "Invalid content of of file entry[0] "); + ByteArrayAssert.AreEqual(new byte[] { 0, 1}, read, "Invalid content of of file entry[0] "); Assert.IsNull(arFile.SymbolTable, "Invalid non-null symbol table found"); @@ -312,7 +312,7 @@ public void CheckLibraryWithELF() stream.CopyTo(originalStream); var originalArray = originalStream.ToArray(); - CollectionAssert.AreEqual(originalArray, newArray, $"Non binary matching between file {cppLib} and {cppLibCopy}"); + ByteArrayAssert.AreEqual(originalArray, newArray, $"Non binary matching between file {cppLib} and {cppLibCopy}"); } } @@ -364,7 +364,7 @@ public void CheckCreateArLibrary() { if (!(fileEntry is ArBinaryFile arBinary)) continue; - arBinary.Stream.Position = 0; + arBinary.Stream!.Position = 0; contentBuilder.Append(Encoding.UTF8.GetString(((MemoryStream) arBinary.Stream).ToArray())); } diff --git a/src/LibObjectFile.Tests/ByteArrayAssert.cs b/src/LibObjectFile.Tests/ByteArrayAssert.cs new file mode 100644 index 0000000..f84e37a --- /dev/null +++ b/src/LibObjectFile.Tests/ByteArrayAssert.cs @@ -0,0 +1,56 @@ +// 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; + +namespace LibObjectFile.Tests; + +/// +/// Helper class to assert two byte arrays are equal. +/// +public static class ByteArrayAssert +{ + public static void AreEqual(byte[] expected, byte[] actual, string? message = null) + { + if (expected.Length != actual.Length) + { + throw new AssertFailedException($"The expected array `{expected.Length}` is not equal to the actual array `{actual.Length}`. {message}"); + } + + for (int i = 0; i < expected.Length; i++) + { + if (expected[i] != actual[i]) + { + StringBuilder builder = new(); + + var minLength = Math.Min(expected.Length, actual.Length); + + const int maxLines = 5; + + int j = Math.Max(i - maxLines, 0); + + var minText = $"[{j} / 0x{j:X}]".Length; + + builder.AppendLine("```"); + builder.AppendLine($"{new(' ',minText - 2)} Expected == Actual"); + + for (; j < Math.Min(minLength, i + maxLines + 1); j++) + { + if (expected[j] != actual[j]) + { + builder.AppendLine($"[{j} / 0x{j:X}] 0x{expected[j]:X2} != 0x{actual[j]:X2} <---- Actual value is not matching expecting"); + } + else + { + builder.AppendLine($"[{j} / 0x{j:X}] 0x{expected[j]:X2}"); + } + } + builder.AppendLine("```"); + + throw new AssertFailedException($"The expected array is not equal to the actual array at index {i}. {message}{Environment.NewLine}{Environment.NewLine}{builder}"); + } + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs index 6b036bc..ab81c65 100644 --- a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs +++ b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs @@ -98,7 +98,7 @@ public void TestDebugLineHelloWorld() inputContext.DebugLinePrinter = Console.Out; var dwarf = DwarfFile.Read(inputContext); - inputContext.DebugLineStream.Position = 0; + inputContext.DebugLineStream!.Position = 0; var copyInputDebugLineStream = new MemoryStream(); inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); @@ -134,7 +134,7 @@ public void TestDebugLineHelloWorld() var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - CollectionAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + ByteArrayAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); } [TestMethod] @@ -157,7 +157,7 @@ public void TestDebugLineLibMultipleObjs() inputContext.DebugLinePrinter = Console.Out; var dwarf = DwarfFile.Read(inputContext); - inputContext.DebugLineStream.Position = 0; + inputContext.DebugLineStream!.Position = 0; var copyInputDebugLineStream = new MemoryStream(); inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); @@ -193,7 +193,7 @@ public void TestDebugLineLibMultipleObjs() var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - CollectionAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + ByteArrayAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); } [TestMethod] @@ -215,7 +215,7 @@ public void TestDebugLineSmall() inputContext.DebugLinePrinter = Console.Out; var dwarf = DwarfFile.Read(inputContext); - inputContext.DebugLineStream.Position = 0; + inputContext.DebugLineStream!.Position = 0; var copyInputDebugLineStream = new MemoryStream(); inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); inputContext.DebugLineStream.Position = 0; @@ -249,7 +249,7 @@ public void TestDebugLineSmall() var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - CollectionAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + ByteArrayAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); } @@ -274,7 +274,7 @@ public void TestDebugLineMultipleFunctions() inputContext.DebugLinePrinter = Console.Out; var dwarf = DwarfFile.Read(inputContext); - inputContext.DebugLineStream.Position = 0; + inputContext.DebugLineStream!.Position = 0; var copyInputDebugLineStream = new MemoryStream(); inputContext.DebugLineStream.CopyTo(copyInputDebugLineStream); inputContext.DebugLineStream.Position = 0; @@ -308,7 +308,7 @@ public void TestDebugLineMultipleFunctions() var inputDebugLineBuffer = copyInputDebugLineStream.ToArray(); var outputDebugLineBuffer = ((MemoryStream)reloadContext.DebugLineStream).ToArray(); - CollectionAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + ByteArrayAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); } diff --git a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs index a4b801a..250693d 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -476,7 +476,7 @@ public void TestManySections() symbolTable = (ElfSymbolTable)elf.Sections[ushort.MaxValue + 3]; for (int i = 0; i < ushort.MaxValue; i++) { - Assert.AreEqual($".section{i}", symbolTable.Entries[i + 1].Section.Section.Name.Value); + Assert.AreEqual($".section{i}", symbolTable.Entries[i + 1].Section.Section!.Name.Value); } } diff --git a/src/LibObjectFile.Tests/Elf/ElfTestBase.cs b/src/LibObjectFile.Tests/Elf/ElfTestBase.cs index 63c4bf7..04b10f1 100644 --- a/src/LibObjectFile.Tests/Elf/ElfTestBase.cs +++ b/src/LibObjectFile.Tests/Elf/ElfTestBase.cs @@ -18,7 +18,7 @@ protected static void AssertReadElf(ElfObjectFile elf, string fileName) AssertLsbMsb(elf, fileName); } - protected static void AssertReadElfInternal(ElfObjectFile elf, string fileName, bool writeFile = true, string context = null, string readElfParams = null) + protected static void AssertReadElfInternal(ElfObjectFile elf, string fileName, bool writeFile = true, string? context = null, string? readElfParams = null) { if (writeFile) { @@ -76,7 +76,7 @@ protected static void AssertReadBack(ElfObjectFile elf, string fileName, bool re newObjectFile.Write(memoryStream); var newBuffer = memoryStream.ToArray(); - CollectionAssert.AreEqual(originalBuffer, newBuffer, "Invalid binary diff between write -> (original) -> read -> write -> (new)"); + ByteArrayAssert.AreEqual(originalBuffer, newBuffer, "Invalid binary diff between write -> (original) -> read -> write -> (new)"); } } diff --git a/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj index 9dafa08..f59f32b 100644 --- a/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj +++ b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj @@ -5,6 +5,7 @@ true false true + enable diff --git a/src/LibObjectFile.Tests/LinuxUtil.cs b/src/LibObjectFile.Tests/LinuxUtil.cs index 3f8c89c..2df9acb 100644 --- a/src/LibObjectFile.Tests/LinuxUtil.cs +++ b/src/LibObjectFile.Tests/LinuxUtil.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. @@ -33,7 +33,7 @@ public static string RunLinuxExe(string exe, string arguments, string distributi exe = "wsl.exe"; } - StringBuilder errorBuilder = null; + StringBuilder? errorBuilder = null; StringBuilder outputBuilder = new StringBuilder(); using (var process = new Process() diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index dc08261..1b5eb1e 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -58,13 +58,16 @@ public async Task TestPrinter(string name) var output = new MemoryStream(); peImage.Write(output); output.Position = 0; - var outputBuffer = output.ToArray(); + byte[] outputBuffer = output.ToArray(); + + // Fake an error + //outputBuffer[250] = 0x44; //await Verifier.Verify(outputBuffer, sourceFile sourceFile). await File.WriteAllBytesAsync($"{sourceFile}.bak", outputBuffer); // Compare the input and output buffer - CollectionAssert.AreEqual(inputBuffer, outputBuffer); + ByteArrayAssert.AreEqual(inputBuffer, outputBuffer, $"Invalid roundtrip for `{name}`"); } [DataTestMethod] @@ -103,8 +106,7 @@ public async Task TestWindows(string sourceFile) TestContext.WriteLine($"SizeOfInitializedData changed from {sizeOfInitializedData} to {newSizeOfInitializedData}. Trying to reuse old size"); peImage.ForceSizeOfInitializedData = sizeOfInitializedData; } - - + // Write the PE back to a byte buffer var output = new MemoryStream(); peImage.Write(output); @@ -124,7 +126,7 @@ public async Task TestWindows(string sourceFile) // await File.WriteAllBytesAsync(outputFileName, outputBuffer); //} - CollectionAssert.AreEqual(inputBuffer, outputBuffer); + ByteArrayAssert.AreEqual(inputBuffer, outputBuffer, $"Invalid roundtrip for `{sourceFile}`"); } } From f12216e361799d26d93b92d547919807469d9c26 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 30 Sep 2024 23:18:56 +0200 Subject: [PATCH 79/87] Add unit test for creating a PE file from scratch --- .../LibObjectFile.Tests.csproj | 4 + src/LibObjectFile.Tests/PE/PEReaderTests.cs | 68 ++++++- .../PE/RawNativeConsoleWin64.exe | Bin 0 -> 2560 bytes ...ame=RawNativeConsoleWin64.exe.verified.txt | 134 +++++++++++++ .../DataDirectory/PECompositeSectionData.cs | 11 +- .../PE/DataDirectory/PEDirectoryTable.cs | 109 +++++++++-- .../PE/DataDirectory/PEImportAddressTable.cs | 14 +- .../PEImportAddressTableDirectory.cs | 1 + .../PE/DataDirectory/PEImportDirectory.cs | 1 + .../PE/DataDirectory/PEImportFunctionEntry.cs | 8 +- .../PE/DataDirectory/PEImportFunctionTable.cs | 6 +- .../PE/DataDirectory/PEImportLookupTable.cs | 13 +- .../PE/Internal/RawImageSectionHeader.cs | 15 ++ src/LibObjectFile/PE/PEDosHeader.cs | 22 +++ src/LibObjectFile/PE/PEFile.Write.cs | 6 +- src/LibObjectFile/PE/PEFile.cs | 66 ++++--- src/LibObjectFile/PE/PEOptionalHeader.cs | 37 ++++ .../PE/PESectionDataExtensions.cs | 5 +- src/LibObjectFile/PE/PEStreamSectionData.cs | 4 +- .../Win64/NativeProjects/NativeProjects.sln | 10 + .../RawNativeConsoleWin64.cpp | 6 + .../RawNativeConsoleWin64.vcxproj | 185 ++++++++++++++++++ .../RawNativeConsoleWin64.vcxproj.filters | 22 +++ 23 files changed, 684 insertions(+), 63 deletions(-) create mode 100644 src/LibObjectFile.Tests/PE/RawNativeConsoleWin64.exe create mode 100644 src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=RawNativeConsoleWin64.exe.verified.txt create mode 100644 src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.cpp create mode 100644 src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.vcxproj create mode 100644 src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.vcxproj.filters diff --git a/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj index f59f32b..c07f1ef 100644 --- a/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj +++ b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj @@ -17,6 +17,7 @@ + @@ -42,6 +43,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index 1b5eb1e..4f2b83e 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -21,10 +21,10 @@ public partial class PEReaderTests [DataRow("NativeConsoleWin64.exe")] [DataRow("NativeConsole2Win64.exe")] [DataRow("NativeLibraryWin64.dll")] + [DataRow("RawNativeConsoleWin64.exe")] public async Task TestPrinter(string name) { - var sourceFile = Path.Combine(AppContext.BaseDirectory, "PE", name); await using var stream = File.OpenRead(sourceFile); var peImage = PEFile.Read(stream, new() { EnableStackTrace = true }); @@ -69,7 +69,71 @@ public async Task TestPrinter(string name) // Compare the input and output buffer ByteArrayAssert.AreEqual(inputBuffer, outputBuffer, $"Invalid roundtrip for `{name}`"); } - + + [TestMethod] + public void TestCreatePE() + { + var pe = new PEFile(); + + // Add a sections + var codeSection = pe.AddSection(PESectionName.Text, 0x1000); + var streamCode = new PEStreamSectionData(); + + streamCode.Stream.Write([ + // SUB RSP, 0x28 + 0x48, 0x83, 0xEC, 0x28, + // MOV ECX, 0x9C + 0xB9, 0x9C, 0x00, 0x00, 0x00, + // CALL ExitProcess (CALL [RIP + 0xFF1]) + 0xFF, 0x15, 0xF1, 0x0F, 0x00, 0x00, + // INT3 + 0xCC + ]); + + codeSection.Content.Add(streamCode); + + var dataSection = pe.AddSection(PESectionName.RData, 0x2000); + + var streamData = new PEStreamSectionData(); + var kernelName = streamData.WriteAsciiString("KERNEL32.DLL"); + var exitProcessFunction = streamData.WriteHintName(new(0x178, "ExitProcess")); + + var peImportAddressTable = new PEImportAddressTable() + { + exitProcessFunction + }; + var iatDirectory = new PEImportAddressTableDirectory() + { + peImportAddressTable + }; + dataSection.Content.Add(iatDirectory); + + var peImportLookupTable = new PEImportLookupTable() + { + exitProcessFunction + }; + dataSection.Content.Add(peImportLookupTable); + + var importDirectory = new PEImportDirectory(); + importDirectory.Entries.Add(new PEImportDirectoryEntry(kernelName, peImportAddressTable, peImportLookupTable)); + dataSection.Content.Add(importDirectory); + + dataSection.Content.Add(streamData); + + pe.Directories[PEDataDirectoryKind.Import] = importDirectory; + pe.Directories[PEDataDirectoryKind.ImportAddressTable] = iatDirectory; + + pe.OptionalHeader.AddressOfEntryPoint = 0x1000; + pe.OptionalHeader.BaseOfCode = 0x1000; + + var output = new MemoryStream(); + pe.Write(output); + output.Position = 0; + + var sourceFile = Path.Combine(AppContext.BaseDirectory, "PE", "generated_win64.exe"); + File.WriteAllBytes(sourceFile, output.ToArray()); + } + [DataTestMethod] [DynamicData(nameof(GetWindowsExeAndDlls), DynamicDataSourceType.Method)] public async Task TestWindows(string sourceFile) diff --git a/src/LibObjectFile.Tests/PE/RawNativeConsoleWin64.exe b/src/LibObjectFile.Tests/PE/RawNativeConsoleWin64.exe new file mode 100644 index 0000000000000000000000000000000000000000..f2db2dc5084f134ba2b0a29f77075df3e6df2d5a GIT binary patch literal 2560 zcmeHH&rcIk5T2!tQJ{r`CI(4(H9g>EV?YfD4h_&iVo`z|jHi}vfhFy3vt6WVI5Z*Q zNIXEIhChP?@DJ!g4tm!AfCHXA)NkGoXtwA{4#ZCT-n{wdo85V{$v6Mz9mR+!j@4=r z9l^6@!@c+LUw60mmEV+p(JihqH`>`kmdM$O(NOHy zipibRL+a~EKB68Vd_lHBy>La##K_;`Gt78709_NKd{Y&I!1dDf;UaEJAwQEM8r4kc z9uxJq$ND@dh$gPU{|JHK_R0>L9hT}LP|4w~{qCAAi1K#e6v6`00S^Iy zT~$2UJMJKPw>f)h8A-j2yQiBofw}Fo(c=U3+q(C2fauH3>Ac(NznDN1Iu$jep$R0u zW90>w)&`SHMvCrp+{7F6&GBDPu+pjW*4J!_QT5`mJ9M zD-L4rhx^iaqtS4dR}oL*E-X!UU1=6;@Li78JH~LN*TCd66*kUT6`1T#VNV$2xeUYW z+GmUr^pADWXrdLjC_bg?Wggou>PVQV(?{oBx3YCNTnPMPsaB)s*~J&x-1wO7l*^s9 Q*Mji3!Q$C .rdata) + [1] Type = ILTCG, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66FB031A, Data = null + + [02] PEStreamSectionData Position = 0x00000648, Size = 0x00000018, RVA = 0x00002048, VirtualSize = 0x00000018 + + [03] PEDebugStreamSectionData Position = 0x00000660, Size = 0x000000DC, RVA = 0x00002060, VirtualSize = 0x000000DC + + [04] PEStreamSectionData Position = 0x0000073C, Size = 0x00000008, RVA = 0x0000213C, VirtualSize = 0x00000008 + + [05] PEImportDirectory Position = 0x00000744, Size = 0x00000028, RVA = 0x00002144, VirtualSize = 0x00000028 + [0] ImportDllNameLink = KERNEL32.dll (RVA = 0x218E, PEStreamSectionData { RVA = 0x2180, VirtualSize = 0x1C, Position = 0x780, Size = 0x1C }, Offset = 0xE) + [0] ImportAddressTable = RVA = 0x00002000 (PEImportAddressTable[0] -> PEImportAddressTableDirectory[0] -> .rdata) + [0] ImportLookupTable = RVA = 0x00002170 (PEImportLookupTable[7] -> .rdata) + + + [06] PEStreamSectionData Position = 0x0000076C, Size = 0x00000004, RVA = 0x0000216C, VirtualSize = 0x00000004 + + [07] PEImportLookupTable Position = 0x00000770, Size = 0x00000010, RVA = 0x00002170, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 376, Name = ExitProcess } (RVA = 0x2180, PEStreamSectionData { RVA = 0x2180, VirtualSize = 0x1C, Position = 0x780, Size = 0x1C }, Offset = 0x0) + + [08] PEStreamSectionData Position = 0x00000780, Size = 0x0000001C, RVA = 0x00002180, VirtualSize = 0x0000001C + + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [02] .pdata PESection Position = 0x00000800, Size = 0x00000200, RVA = 0x00003000, VirtualSize = 0x0000000C, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + [00] PEExceptionDirectory Position = 0x00000800, Size = 0x0000000C, RVA = 0x00003000, VirtualSize = 0x0000000C + [0] Begin = RVA = 0x1000, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10, Position = 0x400, Size = 0x10 }, Offset = 0x0 + [0] End = RVA = 0x1010, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10, Position = 0x400, Size = 0x10 }, Offset = 0x10 + [0] UnwindInfoAddress = RVA = 0x213C, PEStreamSectionData { RVA = 0x213C, VirtualSize = 0x8, Position = 0x73C, Size = 0x8 }, Offset = 0x0 + + diff --git a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs index 1ea9d99..9ea2a28 100644 --- a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs +++ b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs @@ -2,6 +2,7 @@ // 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; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -15,7 +16,7 @@ namespace LibObjectFile.PE; /// /// A section data that contains a list of and an optional header of data. /// -public abstract class PECompositeSectionData : PESectionData +public abstract class PECompositeSectionData : PESectionData, IEnumerable { protected PECompositeSectionData() { @@ -133,4 +134,12 @@ protected sealed override void UpdateRVAInChildren() va += (uint)table.Size; } } + + public void Add(PESectionData data) => Content.Add(data); + + public List.Enumerator GetEnumerator() => Content.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => Content.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)Content).GetEnumerator(); } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs index 370e593..d4c10ab 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -44,6 +44,15 @@ public PEObjectBase? this[int index] } return _entries[index]; } + + set + { + if (index < 0 || index >= _count) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + _entries[index] = value; + } } /// @@ -63,6 +72,16 @@ public PEObjectBase? this[PEDataDirectoryKind kind] } return _entries[(int)kind]; } + + set + { + int index = (int)(ushort)kind; + if (index < 0 || index >= _count) + { + throw new ArgumentOutOfRangeException(nameof(kind)); + } + _entries[(int)kind] = value; + } } /// @@ -98,77 +117,137 @@ public int Count /// /// Gets the export directory information from the PE file. /// - public PEExportDirectory? Export => (PEExportDirectory?)this[PEDataDirectoryKind.Export]; + public PEExportDirectory? Export + { + get => (PEExportDirectory?)this[PEDataDirectoryKind.Export]; + set => this[PEDataDirectoryKind.Export] = value; + } /// /// Gets the import directory information from the PE file. /// - public PEImportDirectory? Import => (PEImportDirectory?)this[PEDataDirectoryKind.Import]; + public PEImportDirectory? Import + { + get => (PEImportDirectory?)this[PEDataDirectoryKind.Import]; + set => this[PEDataDirectoryKind.Import] = value; + } /// /// Gets the resource directory information from the PE file. /// - public PEResourceDirectory? Resource => (PEResourceDirectory?)this[PEDataDirectoryKind.Resource]; + public PEResourceDirectory? Resource + { + get => (PEResourceDirectory?)this[PEDataDirectoryKind.Resource]; + set => this[PEDataDirectoryKind.Resource] = value; + } /// /// Gets the exception directory information from the PE file. /// - public PEExceptionDirectory? Exception => (PEExceptionDirectory?)this[PEDataDirectoryKind.Exception]; + public PEExceptionDirectory? Exception + { + get => (PEExceptionDirectory?)this[PEDataDirectoryKind.Exception]; + set => this[PEDataDirectoryKind.Exception] = value; + } /// /// Gets the certificate/security directory information from the PE file. /// - public PESecurityCertificateDirectory? Certificate => (PESecurityCertificateDirectory?)this[PEDataDirectoryKind.SecurityCertificate]; + public PESecurityCertificateDirectory? Certificate + { + get => (PESecurityCertificateDirectory?)this[PEDataDirectoryKind.SecurityCertificate]; + set => this[PEDataDirectoryKind.SecurityCertificate] = value; + } /// /// Gets the base relocation directory information from the PE file. /// - public PEBaseRelocationDirectory? BaseRelocation => (PEBaseRelocationDirectory?)this[PEDataDirectoryKind.BaseRelocation]; + public PEBaseRelocationDirectory? BaseRelocation + { + get => (PEBaseRelocationDirectory?)this[PEDataDirectoryKind.BaseRelocation]; + set => this[PEDataDirectoryKind.BaseRelocation] = value; + } /// /// Gets the debug directory information from the PE file. /// - public PEDebugDirectory? Debug => (PEDebugDirectory?)this[PEDataDirectoryKind.Debug]; + public PEDebugDirectory? Debug + { + get => (PEDebugDirectory?)this[PEDataDirectoryKind.Debug]; + set => this[PEDataDirectoryKind.Debug] = value; + } /// /// Gets the architecture-specific directory information from the PE file. /// - public PEArchitectureDirectory? Architecture => (PEArchitectureDirectory?)this[PEDataDirectoryKind.Architecture]; + public PEArchitectureDirectory? Architecture + { + get => (PEArchitectureDirectory?)this[PEDataDirectoryKind.Architecture]; + set => this[PEDataDirectoryKind.Architecture] = value; + } /// /// Gets the global pointer directory information from the PE file. /// - public PEGlobalPointerDirectory? GlobalPointer => (PEGlobalPointerDirectory?)this[PEDataDirectoryKind.GlobalPointer]; + public PEGlobalPointerDirectory? GlobalPointer + { + get => (PEGlobalPointerDirectory?)this[PEDataDirectoryKind.GlobalPointer]; + set => this[PEDataDirectoryKind.GlobalPointer] = value; + } /// /// Gets the TLS (Thread Local Storage) directory information from the PE file. /// - public PETlsDirectory? Tls => (PETlsDirectory?)this[PEDataDirectoryKind.Tls]; + public PETlsDirectory? Tls + { + get => (PETlsDirectory?)this[PEDataDirectoryKind.Tls]; + set => this[PEDataDirectoryKind.Tls] = value; + } /// /// Gets the load configuration directory information from the PE file. /// - public PELoadConfigDirectory? LoadConfig => (PELoadConfigDirectory?)this[PEDataDirectoryKind.LoadConfig]; + public PELoadConfigDirectory? LoadConfig + { + get => (PELoadConfigDirectory?)this[PEDataDirectoryKind.LoadConfig]; + set => this[PEDataDirectoryKind.LoadConfig] = value; + } /// /// Gets the bound import directory information from the PE file. /// - public PEBoundImportDirectory? BoundImport => (PEBoundImportDirectory?)this[PEDataDirectoryKind.BoundImport]; + public PEBoundImportDirectory? BoundImport + { + get => (PEBoundImportDirectory?)this[PEDataDirectoryKind.BoundImport]; + set => this[PEDataDirectoryKind.BoundImport] = value; + } /// /// Gets the delay import directory information from the PE file. /// - public PEDelayImportDirectory? DelayImport => (PEDelayImportDirectory?)this[PEDataDirectoryKind.DelayImport]; + public PEDelayImportDirectory? DelayImport + { + get => (PEDelayImportDirectory?)this[PEDataDirectoryKind.DelayImport]; + set => this[PEDataDirectoryKind.DelayImport] = value; + } /// /// Gets the import address table directory information from the PE file. /// - public PEImportAddressTableDirectory? ImportAddressTableDirectory => (PEImportAddressTableDirectory?)this[PEDataDirectoryKind.ImportAddressTable]; + public PEImportAddressTableDirectory? ImportAddressTableDirectory + { + get => (PEImportAddressTableDirectory?)this[PEDataDirectoryKind.ImportAddressTable]; + set => this[PEDataDirectoryKind.ImportAddressTable] = value; + } /// /// Gets the CLR metadata directory information from the PE file, if present. /// - public PEClrMetadata? ClrMetadata => (PEClrMetadata?)this[PEDataDirectoryKind.ClrMetadata]; + public PEClrMetadata? ClrMetadata + { + get => (PEClrMetadata?)this[PEDataDirectoryKind.ClrMetadata]; + set => this[PEDataDirectoryKind.ClrMetadata] = value; + } internal void Set(PESecurityCertificateDirectory? directory) => Set(PEDataDirectoryKind.SecurityCertificate, directory); diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs index 6feae0d..1e6bbb3 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs @@ -1,12 +1,13 @@ -// 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.Collections; using System.Collections.Generic; namespace LibObjectFile.PE; -public class PEImportAddressTable : PESectionData +public class PEImportAddressTable : PESectionData, IEnumerable { internal readonly PEImportFunctionTable FunctionTable; @@ -31,4 +32,13 @@ public override void Read(PEImageReader reader) } public override void Write(PEImageWriter writer) => FunctionTable.Write(writer); + + public void Add(PEImportFunctionEntry entry) => Entries.Add(entry); + + public List.Enumerator GetEnumerator() => Entries.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => Entries.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => Entries.GetEnumerator(); + } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs index 36b5629..a502935 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using LibObjectFile.Collections; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 6315d61..8de4562 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -3,6 +3,7 @@ // See the license.txt file in the project root for more information. using System; +using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; using LibObjectFile.Collections; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs index b4c17ef..f20022a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs @@ -19,7 +19,7 @@ public readonly struct PEImportFunctionEntry /// Initializes a new instance of the class by name. /// /// The name of the import. - public PEImportFunctionEntry(PEAsciiStringLink name) + public PEImportFunctionEntry(PEImportHintNameLink name) { _peSectionData = name.Container; _offset = name.RVO; @@ -76,4 +76,10 @@ public PEImportFunctionEntry(ushort ordinal) /// Gets the ordinal of the import if by ordinal. /// public ushort Ordinal => _peSectionData is null && !IsLongOffset ? (ushort)(_offset) : (ushort)0; + + /// + /// Converts a PE Import Hint Name to a PE Import Function Entry. + /// + /// The PE Import Hint Name to convert. + public static implicit operator PEImportFunctionEntry(PEImportHintNameLink name) => new(name); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index 8687a00..ba2c8b2 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -74,7 +74,7 @@ public void Bind(PEImageReader reader, bool allowOutOfRange) return; } - entry = new PEImportFunctionEntry(new PEAsciiStringLink(streamSectionData, va - container.RVA)); + entry = new PEImportFunctionEntry(new PEImportHintNameLink(streamSectionData, va - container.RVA)); } } } @@ -100,7 +100,7 @@ private unsafe void Read32(PEImageReader reader) Entries.Add( entry.IsImportByOrdinal ? new PEImportFunctionEntry(entry.Ordinal) - : new PEImportFunctionEntry(new PEAsciiStringLink(PEStreamSectionData.Empty, entry.HintNameTableRVA)) + : new PEImportFunctionEntry(new PEImportHintNameLink(PEStreamSectionData.Empty, entry.HintNameTableRVA)) ); } } @@ -126,7 +126,7 @@ private unsafe void Read64(PEImageReader reader) Entries.Add( entry.IsImportByOrdinal ? new PEImportFunctionEntry(entry.Ordinal) - : entry.HintNameTableRVA > uint.MaxValue ? new PEImportFunctionEntry(entry.HintNameTableRVA) : new PEImportFunctionEntry(new PEAsciiStringLink(PEStreamSectionData.Empty, (uint)entry.HintNameTableRVA)) + : entry.HintNameTableRVA > uint.MaxValue ? new PEImportFunctionEntry(entry.HintNameTableRVA) : new PEImportFunctionEntry(new PEImportHintNameLink(PEStreamSectionData.Empty, (uint)entry.HintNameTableRVA)) ); } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs index ab167a1..ebb4d22 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -1,14 +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.Collections; using System.Collections.Generic; using LibObjectFile.Diagnostics; namespace LibObjectFile.PE; -public sealed class PEImportLookupTable : PESectionData +public sealed class PEImportLookupTable : PESectionData, IEnumerable { internal readonly PEImportFunctionTable FunctionTable; @@ -34,4 +35,12 @@ public override void Read(PEImageReader reader) public override void Write(PEImageWriter writer) => FunctionTable.Write(writer); + public void Add(PEImportFunctionEntry entry) => Entries.Add(entry); + + public List.Enumerator GetEnumerator() => Entries.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => Entries.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => Entries.GetEnumerator(); + } \ No newline at end of file diff --git a/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs b/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs index b703bf6..7372845 100644 --- a/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs +++ b/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs @@ -4,8 +4,10 @@ using System; using System.Diagnostics; +using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; using System.Text; +using static System.Collections.Specialized.BitVector32; namespace LibObjectFile.PE.Internal; @@ -41,6 +43,19 @@ public string NameAsString } } + public void SetName(PESectionName sectionName) + { + var spanName = MemoryMarshal.CreateSpan(ref Name[0], 8); + int count = Encoding.ASCII.GetBytes(sectionName.Name.AsSpan(), spanName); + if (count < 8) + { + for (int i = count; i < 8; i++) + { + spanName[i] = 0; + } + } + } + /// /// The total size of the section when loaded into memory. /// If this value is greater than , the section is zero-padded. diff --git a/src/LibObjectFile/PE/PEDosHeader.cs b/src/LibObjectFile/PE/PEDosHeader.cs index 7845223..071ddf7 100644 --- a/src/LibObjectFile/PE/PEDosHeader.cs +++ b/src/LibObjectFile/PE/PEDosHeader.cs @@ -2,6 +2,8 @@ // 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.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibObjectFile.PE; @@ -110,4 +112,24 @@ public unsafe struct PEDosHeader /// /// This property is automatically calculated but can be slightly adjusted with public uint FileAddressPEHeader => _FileAddressPEHeader; + + /// + /// A default DOS header for a PE file. + /// + internal static ref readonly PEDosHeader Default => ref Unsafe.As(ref MemoryMarshal.GetReference(new ReadOnlySpan([ + 0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 // Offset to PE (0x80 == sizeof(PEDosHeader) + DefaultDosStub.Length) + ]))); + + /// + /// A default DOS stub for a PE file. + /// + internal static ReadOnlySpan DefaultDosStub => [ + 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, + 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, + 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ]; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Write.cs b/src/LibObjectFile/PE/PEFile.Write.cs index 409ba35..86c57ec 100644 --- a/src/LibObjectFile/PE/PEFile.Write.cs +++ b/src/LibObjectFile/PE/PEFile.Write.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Text; using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; using LibObjectFile.Utils; @@ -60,9 +61,6 @@ public bool TryWrite(Stream stream, out DiagnosticBag diagnostics) public override unsafe void Write(PEImageWriter writer) { - var context = new PELayoutContext(this, writer.Diagnostics); - UpdateLayout(context); - // Set the size of the stream before writing to it. // It will help to avoid resizing the stream writer.Stream.SetLength((uint)Size); @@ -129,7 +127,7 @@ public override unsafe void Write(PEImageWriter writer) RawImageSectionHeader sectionHeader = default; foreach (var section in _sections) { - section.Name.CopyTo(new Span(sectionHeader.Name, 8)); + sectionHeader.SetName(section.Name); sectionHeader.VirtualSize = section.VirtualSize; sectionHeader.RVA = section.RVA; sectionHeader.SizeOfRawData = (uint)section.Size; diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 4245c33..e9358fa 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -34,18 +34,39 @@ static PEFile() Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); } + /// + /// Initializes a new instance of the class PE32+. + /// + public PEFile() : this(PEOptionalHeaderMagic.PE32Plus) + { + } + /// /// Initializes a new instance of the class. /// - public PEFile(PEOptionalHeaderMagic magic = PEOptionalHeaderMagic.PE32Plus) + public PEFile(PEOptionalHeaderMagic magic) { _sections = new(this); + Directories = new(); ExtraDataBeforeSections = new(this); ExtraDataAfterSections = new(this); - // TODO: Add default initialization + DosHeader = PEDosHeader.Default; + DosStub = PEDosHeader.DefaultDosStub.ToArray(); + + CoffHeader = new() + { + Machine = Machine.Amd64, + TimeDateStamp = 0, + Characteristics = Characteristics.ExecutableImage | Characteristics.LargeAddressAware + }; + OptionalHeader = new(); OptionalHeader.OptionalHeaderCommonPart1.Magic = magic; + Directories.Count = (int)OptionalHeader.NumberOfRvaAndSizes; + + // Update the layout which is only going to calculate the size. + UpdateLayout(new()); } /// @@ -54,6 +75,7 @@ public PEFile(PEOptionalHeaderMagic magic = PEOptionalHeaderMagic.PE32Plus) internal PEFile(bool unused) { _sections = new(this); + Directories = new(); ExtraDataBeforeSections = new(this); ExtraDataAfterSections = new(this); } @@ -149,7 +171,7 @@ public Stream? DosStubExtra /// /// Directories must be added/removed from or . /// - public PEDirectoryTable Directories { get; } = new(); + public PEDirectoryTable Directories { get; } /// /// Gets the data present before the sections in the file. @@ -256,31 +278,6 @@ public bool TryGetSection(PESectionName name, [NotNullWhen(true)] out PESection? return false; } - public List GetAllSectionData() - { - var dataList = new List(); - - // Precalculate the capacity - int count = 0; - var sections = _sections; - foreach (var section in sections) - { - count += section.Content.Count; - } - - dataList.Capacity = count; - - foreach (var section in sections) - { - foreach (var data in section.Content) - { - dataList.Add(data); - } - } - - return dataList; - } - /// /// Tries to find the section data that contains the specified virtual address. /// @@ -364,12 +361,17 @@ protected override unsafe void UpdateLayoutCore(PELayoutContext context) // TODO: update other DosHeader fields + var startPositionHeader = position; + position += (uint)(IsPE32 ? RawImageOptionalHeader32.MinimumSize : RawImageOptionalHeader64.MinimumSize); // Update directories position += (uint)(Directories.Count * sizeof(RawImageDataDirectory)); + CoffHeader._SizeOfOptionalHeader = (ushort)(position - startPositionHeader); + position += (uint)(sizeof(RawImageSectionHeader) * _sections.Count); + // TODO: Additional optional header size? // Data before sections @@ -387,6 +389,7 @@ protected override unsafe void UpdateLayoutCore(PELayoutContext context) } + // Update COFF header CoffHeader._NumberOfSections = (ushort)_sections.Count; CoffHeader._PointerToSymbolTable = 0; @@ -424,8 +427,13 @@ protected override unsafe void UpdateLayoutCore(PELayoutContext context) RVA previousEndOfRVA = 0U; foreach (var section in _sections) { - section.Position = section.Size > 0 ? position : (ulong)0; + section.Position = position; section.UpdateLayout(context); + if (section.Size == 0) + { + section.Position = 0; + } + if (section.RVA < previousEndOfRVA) { context.Diagnostics.Error(DiagnosticId.PE_ERR_SectionRVALessThanPrevious, $"Section {section.Name} RVA {section.RVA} is less than the previous section end RVA {previousEndOfRVA}"); diff --git a/src/LibObjectFile/PE/PEOptionalHeader.cs b/src/LibObjectFile/PE/PEOptionalHeader.cs index 99b73e5..25730c0 100644 --- a/src/LibObjectFile/PE/PEOptionalHeader.cs +++ b/src/LibObjectFile/PE/PEOptionalHeader.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Numerics; +using System.Reflection.PortableExecutable; using LibObjectFile.PE.Internal; namespace LibObjectFile.PE; @@ -19,6 +20,41 @@ public struct PEOptionalHeader internal RawImageOptionalHeaderSize64 OptionalHeaderSize64; internal RawImageOptionalHeaderCommonPart3 OptionalHeaderCommonPart3; + public PEOptionalHeader() + { + // Clear all fields + OptionalHeaderCommonPart1 = default; + OptionalHeaderBase32 = default; + OptionalHeaderBase64 = default; + OptionalHeaderCommonPart2 = default; + OptionalHeaderSize32 = default; + OptionalHeaderSize64 = default; + OptionalHeaderCommonPart3 = default; + + // Setup some fields to some default values + OptionalHeaderCommonPart1.Magic = PEOptionalHeaderMagic.PE32Plus; + OptionalHeaderCommonPart1.MajorLinkerVersion = 1; + + OptionalHeaderBase64.ImageBase = 0x400000; + + OptionalHeaderCommonPart2.SectionAlignment = 0x1000; + OptionalHeaderCommonPart2.FileAlignment = 0x200; + OptionalHeaderCommonPart2.MajorOperatingSystemVersion = 6; + OptionalHeaderCommonPart2.MajorSubsystemVersion = 6; + OptionalHeaderCommonPart2.Subsystem = Subsystem.WindowsCui; + OptionalHeaderCommonPart2.DllCharacteristics = DllCharacteristics.HighEntropyVirtualAddressSpace + | DllCharacteristics.DynamicBase + | DllCharacteristics.NxCompatible + | DllCharacteristics.TerminalServerAware; + + OptionalHeaderSize64.SizeOfStackReserve = 0x100_000; + OptionalHeaderSize64.SizeOfStackCommit = 0x1000; + OptionalHeaderSize64.SizeOfHeapReserve = 0x100_000; + OptionalHeaderSize64.SizeOfHeapCommit = 0x1000; + + OptionalHeaderCommonPart3.NumberOfRvaAndSizes = 16; + } + /// /// The magic number, which identifies the file format. Expected to be 0x10b for PE32. /// @@ -87,6 +123,7 @@ public uint AddressOfEntryPoint public uint BaseOfCode { get => OptionalHeaderCommonPart1.BaseOfCode; + set => OptionalHeaderCommonPart1.BaseOfCode = value; } /// diff --git a/src/LibObjectFile/PE/PESectionDataExtensions.cs b/src/LibObjectFile/PE/PESectionDataExtensions.cs index dcce1d8..d58e506 100644 --- a/src/LibObjectFile/PE/PESectionDataExtensions.cs +++ b/src/LibObjectFile/PE/PESectionDataExtensions.cs @@ -85,7 +85,8 @@ private static void WriteAsciiStringInternal(Stream stream, bool isHint, ushort maxLength++; } - using var tempSpan = TempSpan.Create(maxLength + (isHint ? 2 : 0), out var span); + using var tempSpan = TempSpan.Create(maxLength + 1 + (isHint ? 2 : 0), out var span); + span.Clear(); var text = span; @@ -107,7 +108,7 @@ private static void WriteAsciiStringInternal(Stream stream, bool isHint, ushort actualLength += 2; } - stream.Write(span.Slice(0, actualLength)); + stream.Write(span.Slice(0, actualLength + 1)); } private static string ReadAsciiStringInternal(Stream stream, bool isHint, out ushort hint) diff --git a/src/LibObjectFile/PE/PEStreamSectionData.cs b/src/LibObjectFile/PE/PEStreamSectionData.cs index f38f977..dd0ee83 100644 --- a/src/LibObjectFile/PE/PEStreamSectionData.cs +++ b/src/LibObjectFile/PE/PEStreamSectionData.cs @@ -16,12 +16,12 @@ public class PEStreamSectionData : PESectionData private uint _requiredPositionAlignment; private uint _requiredSizeAlignment; - internal static PEStreamSectionData Empty = new(); + internal static PEStreamSectionData Empty = new(System.IO.Stream.Null); /// /// Initializes a new instance of the class. /// - public PEStreamSectionData() : this(System.IO.Stream.Null) + public PEStreamSectionData() : this(new MemoryStream()) { } diff --git a/src/native/Win64/NativeProjects/NativeProjects.sln b/src/native/Win64/NativeProjects/NativeProjects.sln index 4c5e0a1..17fdbf8 100644 --- a/src/native/Win64/NativeProjects/NativeProjects.sln +++ b/src/native/Win64/NativeProjects/NativeProjects.sln @@ -9,6 +9,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NativeConsole2Win64", "Nati EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NativeLibraryWin64", "NativeLibraryWin64\NativeLibraryWin64.vcxproj", "{6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RawNativeConsoleWin64", "RawNativeConsoleWin64\RawNativeConsoleWin64.vcxproj", "{269227A8-1C63-4EB1-88CF-5C80B7C61F0F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -41,6 +43,14 @@ Global {6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}.Release|x64.Build.0 = Release|x64 {6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}.Release|x86.ActiveCfg = Release|Win32 {6FCDB0A8-ED32-4F64-91AF-6A41FE4C30C5}.Release|x86.Build.0 = Release|Win32 + {269227A8-1C63-4EB1-88CF-5C80B7C61F0F}.Debug|x64.ActiveCfg = Debug|x64 + {269227A8-1C63-4EB1-88CF-5C80B7C61F0F}.Debug|x64.Build.0 = Debug|x64 + {269227A8-1C63-4EB1-88CF-5C80B7C61F0F}.Debug|x86.ActiveCfg = Debug|Win32 + {269227A8-1C63-4EB1-88CF-5C80B7C61F0F}.Debug|x86.Build.0 = Debug|Win32 + {269227A8-1C63-4EB1-88CF-5C80B7C61F0F}.Release|x64.ActiveCfg = Release|x64 + {269227A8-1C63-4EB1-88CF-5C80B7C61F0F}.Release|x64.Build.0 = Release|x64 + {269227A8-1C63-4EB1-88CF-5C80B7C61F0F}.Release|x86.ActiveCfg = Release|Win32 + {269227A8-1C63-4EB1-88CF-5C80B7C61F0F}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.cpp b/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.cpp new file mode 100644 index 0000000..cf099f9 --- /dev/null +++ b/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.cpp @@ -0,0 +1,6 @@ +#include +// WinMain +void mainCRTStartup(void) +{ + ExitProcess(156); +} \ No newline at end of file diff --git a/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.vcxproj b/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.vcxproj new file mode 100644 index 0000000..0c2b90d --- /dev/null +++ b/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.vcxproj @@ -0,0 +1,185 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {269227a8-1c63-4eb1-88cf-5c80b7c61f0f} + RawNativeConsoleWin64 + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + false + + + false + + + false + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + false + false + /EHa- %(AdditionalOptions) + None + false + false + + + Console + false + true + false + false + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + false + false + /EHa- %(AdditionalOptions) + None + false + false + + + Console + true + true + false + true + false + false + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + false + false + /EHa- %(AdditionalOptions) + None + false + false + + + Console + false + true + false + false + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + false + false + /EHa- %(AdditionalOptions) + None + false + false + + + Console + true + true + false + true + false + false + + + + + + + + + \ No newline at end of file diff --git a/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.vcxproj.filters b/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.vcxproj.filters new file mode 100644 index 0000000..713ea2d --- /dev/null +++ b/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file From d54f1be1f7ccc4fb2ad5e054ecec5241e8f40408 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Mon, 30 Sep 2024 23:26:46 +0200 Subject: [PATCH 80/87] Add comments to TestCreatePE --- src/LibObjectFile.Tests/PE/PEReaderTests.cs | 30 ++++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index 4f2b83e..6f2b20a 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -74,8 +74,10 @@ public async Task TestPrinter(string name) public void TestCreatePE() { var pe = new PEFile(); - - // Add a sections + + // *************************************************************************** + // Code section + // *************************************************************************** var codeSection = pe.AddSection(PESectionName.Text, 0x1000); var streamCode = new PEStreamSectionData(); @@ -91,13 +93,17 @@ public void TestCreatePE() ]); codeSection.Content.Add(streamCode); - + + // *************************************************************************** + // Data section + // *************************************************************************** var dataSection = pe.AddSection(PESectionName.RData, 0x2000); var streamData = new PEStreamSectionData(); var kernelName = streamData.WriteAsciiString("KERNEL32.DLL"); var exitProcessFunction = streamData.WriteHintName(new(0x178, "ExitProcess")); + // PEImportAddressTableDirectory comes first, it is referenced by the RIP + 0xFF1, first address being ExitProcess var peImportAddressTable = new PEImportAddressTable() { exitProcessFunction @@ -106,26 +112,36 @@ public void TestCreatePE() { peImportAddressTable }; - dataSection.Content.Add(iatDirectory); var peImportLookupTable = new PEImportLookupTable() { exitProcessFunction }; - dataSection.Content.Add(peImportLookupTable); var importDirectory = new PEImportDirectory(); importDirectory.Entries.Add(new PEImportDirectoryEntry(kernelName, peImportAddressTable, peImportLookupTable)); + + // Layout of the data section + dataSection.Content.Add(iatDirectory); + dataSection.Content.Add(peImportLookupTable); dataSection.Content.Add(importDirectory); - dataSection.Content.Add(streamData); - + + // *************************************************************************** + // Directories + // *************************************************************************** pe.Directories[PEDataDirectoryKind.Import] = importDirectory; pe.Directories[PEDataDirectoryKind.ImportAddressTable] = iatDirectory; + // *************************************************************************** + // Optional Header + // *************************************************************************** pe.OptionalHeader.AddressOfEntryPoint = 0x1000; pe.OptionalHeader.BaseOfCode = 0x1000; + // *************************************************************************** + // Write the PE to a file + // *************************************************************************** var output = new MemoryStream(); pe.Write(output); output.Position = 0; From 3901ecf90eced588c7c840d9fb0bd927595ad176 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 1 Oct 2024 08:38:35 +0200 Subject: [PATCH 81/87] Improve support for directories. Add auto discovery from section content --- src/LibObjectFile.Tests/PE/PEReaderTests.cs | 20 +-- ..._name=NativeConsole2Win64.exe.verified.txt | 6 +- ...r_name=NativeConsoleWin64.exe.verified.txt | 6 +- ...r_name=NativeLibraryWin64.dll.verified.txt | 6 +- ...ame=RawNativeConsoleWin64.exe.verified.txt | 6 +- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 5 +- .../PE/DataDirectory/PEBaseRelocationBlock.cs | 4 +- .../DataDirectory/PEBoundImportDirectory.cs | 4 +- .../DataDirectory/PECompositeSectionData.cs | 25 ++++ .../PE/DataDirectory/PEDebugDirectory.cs | 2 +- .../DataDirectory/PEDelayImportDirectory.cs | 12 +- .../PE/DataDirectory/PEDirectoryTable.cs | 38 +++--- .../PE/DataDirectory/PEExceptionDirectory.cs | 8 +- .../PE/DataDirectory/PEExportAddressTable.cs | 2 +- .../PE/DataDirectory/PEExportDirectory.cs | 10 +- .../PE/DataDirectory/PEExportNameTable.cs | 2 +- .../PE/DataDirectory/PEImportDirectory.cs | 6 +- .../PE/DataDirectory/PEImportFunctionTable.cs | 2 +- .../PE/DataDirectory/PEResourceDataEntry.cs | 2 +- .../Internal/RawImageOptionalHeaderBase32.cs | 4 +- .../RawImageOptionalHeaderCommonPart1.cs | 4 +- src/LibObjectFile/PE/PEFile.Read.cs | 57 ++++++--- src/LibObjectFile/PE/PEFile.Write.cs | 8 +- src/LibObjectFile/PE/PEFile.cs | 115 +++++++++--------- src/LibObjectFile/PE/PEOptionalHeader.cs | 54 ++++++-- src/LibObjectFile/PE/PEPrinter.cs | 4 +- .../RawNativeConsoleWin64.cpp | 2 +- 27 files changed, 249 insertions(+), 165 deletions(-) diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index 6f2b20a..c5054fc 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -118,8 +118,13 @@ public void TestCreatePE() exitProcessFunction }; - var importDirectory = new PEImportDirectory(); - importDirectory.Entries.Add(new PEImportDirectoryEntry(kernelName, peImportAddressTable, peImportLookupTable)); + var importDirectory = new PEImportDirectory() + { + Entries = + { + new PEImportDirectoryEntry(kernelName, peImportAddressTable, peImportLookupTable) + } + }; // Layout of the data section dataSection.Content.Add(iatDirectory); @@ -127,17 +132,11 @@ public void TestCreatePE() dataSection.Content.Add(importDirectory); dataSection.Content.Add(streamData); - // *************************************************************************** - // Directories - // *************************************************************************** - pe.Directories[PEDataDirectoryKind.Import] = importDirectory; - pe.Directories[PEDataDirectoryKind.ImportAddressTable] = iatDirectory; - // *************************************************************************** // Optional Header // *************************************************************************** - pe.OptionalHeader.AddressOfEntryPoint = 0x1000; - pe.OptionalHeader.BaseOfCode = 0x1000; + pe.OptionalHeader.AddressOfEntryPoint = new(streamCode, 0); + pe.OptionalHeader.BaseOfCode = codeSection; // *************************************************************************** // Write the PE to a file @@ -232,6 +231,7 @@ public static IEnumerable GetWindowsExeAndDlls() } [TestMethod] + [Ignore("PEFile does not support PE files that are folding the PE header into the DosHeader")] public async Task TestTinyExe97Bytes() { // http://www.phreedom.org/research/tinype/ 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 ba180a3..eb2711c 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 @@ -38,9 +38,9 @@ Optional Header SizeOfCode = 0x1A00 SizeOfInitializedData = 0x2A00 SizeOfUninitializedData = 0x0 - AddressOfEntryPoint = 0x15E0 - BaseOfCode = 0x1000 - BaseOfData = 0x0 + AddressOfEntryPoint = RVA = 0x15E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x19E9 }, Offset = 0x5E0 + BaseOfCode = PESection { .text RVA = 0x1000, VirtualSize = 0x19E9, Position = 0x400, Size = 0x1A00, Content[1] } + BaseOfData = 0x0x0 ImageBase = 0x140000000 SectionAlignment = 0x1000 FileAlignment = 0x200 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 bffdfb8..e4a0e93 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 @@ -38,9 +38,9 @@ Optional Header SizeOfCode = 0x1200 SizeOfInitializedData = 0x2200 SizeOfUninitializedData = 0x0 - AddressOfEntryPoint = 0x14E0 - BaseOfCode = 0x1000 - BaseOfData = 0x0 + AddressOfEntryPoint = RVA = 0x14E0, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x10C9 }, Offset = 0x4E0 + BaseOfCode = PESection { .text RVA = 0x1000, VirtualSize = 0x10C9, Position = 0x400, Size = 0x1200, Content[1] } + BaseOfData = 0x0x0 ImageBase = 0x140000000 SectionAlignment = 0x1000 FileAlignment = 0x200 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 f35d8fc..e908aec 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 @@ -38,9 +38,9 @@ Optional Header SizeOfCode = 0x1000 SizeOfInitializedData = 0x1C00 SizeOfUninitializedData = 0x0 - AddressOfEntryPoint = 0x1370 - BaseOfCode = 0x1000 - BaseOfData = 0x0 + AddressOfEntryPoint = RVA = 0x1370, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0xF18 }, Offset = 0x370 + BaseOfCode = PESection { .text RVA = 0x1000, VirtualSize = 0xF18, Position = 0x400, Size = 0x1000, Content[1] } + BaseOfData = 0x0x0 ImageBase = 0x180000000 SectionAlignment = 0x1000 FileAlignment = 0x200 diff --git a/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=RawNativeConsoleWin64.exe.verified.txt b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=RawNativeConsoleWin64.exe.verified.txt index 1076e91..f9ae4eb 100644 --- a/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=RawNativeConsoleWin64.exe.verified.txt +++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=RawNativeConsoleWin64.exe.verified.txt @@ -38,9 +38,9 @@ Optional Header SizeOfCode = 0x200 SizeOfInitializedData = 0x400 SizeOfUninitializedData = 0x0 - AddressOfEntryPoint = 0x1000 - BaseOfCode = 0x1000 - BaseOfData = 0x0 + AddressOfEntryPoint = RVA = 0x1000, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10, Position = 0x400, Size = 0x10 }, Offset = 0x0 + BaseOfCode = PESection { .text RVA = 0x1000, VirtualSize = 0x10, Position = 0x400, Size = 0x200, Content[1] } + BaseOfData = 0x0x0 ImageBase = 0x140000000 SectionAlignment = 0x1000 FileAlignment = 0x200 diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 526c3f4..8efa9b6 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -133,7 +133,10 @@ public enum DiagnosticId PE_ERR_SectionAlignmentLessThanFileAlignment = 3015, PE_ERR_InvalidPEHeaderPosition = 3016, PE_ERR_InvalidNumberOfDataDirectories = 3017, - + PE_ERR_InvalidBaseOfCode = 3018, + PE_ERR_InvalidAddressOfEntryPoint = 3019, + PE_ERR_DirectoryWithSameKindAlreadyAdded = 3020, + // PE Exception directory PE_ERR_InvalidExceptionDirectory_Entries = 3100, PE_ERR_InvalidExceptionDirectory_Entry = 3101, diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs index 0f78eeb..8e161ad 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs @@ -72,7 +72,7 @@ public ulong ReadAddress(PEFile file, PEBaseRelocation relocation) var vaOfReloc = SectionLink.RVA() + relocation.OffsetInBlock; - if (!file.TryFindContainerByRVA(vaOfReloc, out var container)) + if (!file.TryFindByRVA(vaOfReloc, out var container)) { throw new InvalidOperationException($"Unable to find the section data containing the virtual address {vaOfReloc}"); } @@ -118,7 +118,7 @@ public override unsafe void Read(PEImageReader reader) return; } - if (!reader.File.TryFindSection(block.PageRVA, out var section)) + if (!reader.File.TryFindSectionByRVA(block.PageRVA, out var section)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress, $"Unable to find the section containing the virtual address {block.PageRVA} at position {position}"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs index 478066b..6954336 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs @@ -113,7 +113,7 @@ internal override void Bind(PEImageReader reader) { // The RVO is actually an RVA until we bind it here var va = (RVA)(uint)entry.ModuleName.RVO; - if (!peFile.TryFindContainerByRVA(va, out var container)) + if (!peFile.TryFindByRVA(va, out var container)) { diagnostics.Error(DiagnosticId.PE_ERR_BoundImportDirectoryInvalidModuleName, $"Unable to find the section data for ModuleName {va}"); return; @@ -134,7 +134,7 @@ internal override void Bind(PEImageReader reader) { // The RVO is actually an RVA until we bind it here va = (RVA)(uint)forwarderRef.ModuleName.RVO; - if (!peFile.TryFindContainerByRVA(va, out container)) + if (!peFile.TryFindByRVA(va, out container)) { diagnostics.Error(DiagnosticId.PE_ERR_BoundImportDirectoryInvalidForwarderRefModuleName, $"Unable to find the section data for ForwarderRef ModuleName {va}"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs index 9ea2a28..34f6393 100644 --- a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs +++ b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; using LibObjectFile.Utils; using static System.Runtime.InteropServices.JavaScript.JSType; @@ -32,6 +33,30 @@ protected PECompositeSectionData() /// public ObjectList Content { get; } + internal void UpdateDirectories(PEFile peFile, DiagnosticBag diagnostics) + { + if (this is PEDataDirectory directory) + { + var existingDirectory = peFile.Directories[directory.Kind]; + if (existingDirectory is not null) + { + diagnostics.Error(DiagnosticId.PE_ERR_DirectoryWithSameKindAlreadyAdded, $"A directory with the kind {directory.Kind} was already found {existingDirectory} while trying to add new directory {directory}"); + } + else + { + peFile.Directories[directory.Kind] = directory; + } + } + + foreach (var data in Content) + { + if (data is PECompositeSectionData compositeSectionData) + { + compositeSectionData.UpdateDirectories(peFile, diagnostics); + } + } + } + protected sealed override void UpdateLayoutCore(PELayoutContext context) { var va = RVA; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs index 1b1eac0..0991435 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs @@ -56,7 +56,7 @@ public override unsafe void Read(PEImageReader reader) if (rawEntry.AddressOfRawData != 0) { - if (!reader.File.TryFindSection(rawEntry.AddressOfRawData, out var section)) + if (!reader.File.TryFindSectionByRVA(rawEntry.AddressOfRawData, out var section)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_DebugDirectorySectionNotFound, $"Unable to find the section for the debug directory entry at {rawEntry.AddressOfRawData}"); continue; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs index 98bfe3e..73d6f92 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs @@ -49,7 +49,7 @@ public override void Read(PEImageReader reader) } // DelayLoadImportAddressTableRVA - if (!reader.File.TryFindSection(rawEntry.DelayLoadImportAddressTableRVA, out var section)) + if (!reader.File.TryFindSectionByRVA(rawEntry.DelayLoadImportAddressTableRVA, out var section)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidDelayLoadImportAddressTableRVA, $"Unable to find the section for DelayLoadImportAddressTableRVA {rawEntry.DelayLoadImportAddressTableRVA}"); return; @@ -59,7 +59,7 @@ public override void Read(PEImageReader reader) var importDelayLoadImportAddressTablePositionInFile = section.Position + rawEntry.DelayLoadImportAddressTableRVA - section.RVA; // DelayLoadImportNameTableRVA - if (!reader.File.TryFindSection(rawEntry.DelayLoadImportNameTableRVA, out section)) + if (!reader.File.TryFindSectionByRVA(rawEntry.DelayLoadImportNameTableRVA, out section)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidDelayLoadImportNameTableRVA, $"Unable to find the section for DelayLoadImportNameTableRVA {rawEntry.DelayLoadImportNameTableRVA}"); return; @@ -120,7 +120,7 @@ internal override void Bind(PEImageReader reader) foreach (ref var entry in entries) { var rva = (RVA)(uint)entry.DllName.RVO; - if (!peFile.TryFindContainerByRVA(rva, out var container)) + if (!peFile.TryFindByRVA(rva, out var container)) { diagnostics.Error(DiagnosticId.PE_ERR_DelayImportDirectoryInvalidDllNameRVA, $"Unable to find the section data for DllNameRVA {rva}"); return; @@ -137,7 +137,7 @@ internal override void Bind(PEImageReader reader) // The ModuleHandle could be in Virtual memory and not bound, so we link to a section and not a particular data on the disk rva = (RVA)(uint)entry.ModuleHandle.RVO; - if (!peFile.TryFindSection(rva, out var moduleSection)) + if (!peFile.TryFindSectionByRVA(rva, out var moduleSection)) { diagnostics.Error(DiagnosticId.PE_ERR_DelayImportDirectoryInvalidModuleHandleRVA, $"Unable to find the section data for ModuleHandleRVA {rva}"); return; @@ -151,7 +151,7 @@ internal override void Bind(PEImageReader reader) if (entry.BoundImportAddressTableLink.RVO != 0) { rva = (RVA)(uint)entry.BoundImportAddressTableLink.RVO; - if (!peFile.TryFindContainerByRVA(rva, out container)) + if (!peFile.TryFindByRVA(rva, out container)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidBoundDelayLoadImportAddressTableRVA, $"Unable to find the section data for BoundImportAddressTableRVA {rva}"); return; @@ -170,7 +170,7 @@ internal override void Bind(PEImageReader reader) if (entry.UnloadDelayInformationTableLink.RVO != 0) { rva = (RVA)(uint)entry.UnloadDelayInformationTableLink.RVO; - if (!peFile.TryFindContainerByRVA(rva, out container)) + if (!peFile.TryFindByRVA(rva, out container)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidUnloadDelayLoadImportAddressTableRVA, $"Unable to find the section data for UnloadDelayInformationTableRVA {rva}"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs index d4c10ab..8d0c025 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -3,12 +3,7 @@ // See the license.txt file in the project root for more information. using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; -using System.Reflection; -using System.Runtime.CompilerServices; using LibObjectFile.Collections; using LibObjectFile.PE.Internal; @@ -17,6 +12,12 @@ namespace LibObjectFile.PE; /// /// Contains the array of directory entries in a Portable Executable (PE) file. /// +/// +/// The list of directory is automatically updated by the content of the sections +/// when calling or +/// or +/// or +/// [DebuggerDisplay($"{nameof(PEDirectoryTable)} {nameof(Count)} = {{{nameof(Count)}}}")] public sealed class PEDirectoryTable { @@ -28,6 +29,14 @@ internal PEDirectoryTable() _entries = []; } + /// + /// Clear the directory + /// + public void Clear() + { + Array.Clear(_entries); + } + /// /// Gets the directory entry at the specified index. /// @@ -45,7 +54,7 @@ public PEObjectBase? this[int index] return _entries[index]; } - set + internal set { if (index < 0 || index >= _count) { @@ -73,7 +82,7 @@ public PEObjectBase? this[PEDataDirectoryKind kind] return _entries[(int)kind]; } - set + internal set { int index = (int)(ushort)kind; if (index < 0 || index >= _count) @@ -120,7 +129,6 @@ public int Count public PEExportDirectory? Export { get => (PEExportDirectory?)this[PEDataDirectoryKind.Export]; - set => this[PEDataDirectoryKind.Export] = value; } /// @@ -129,7 +137,6 @@ public PEExportDirectory? Export public PEImportDirectory? Import { get => (PEImportDirectory?)this[PEDataDirectoryKind.Import]; - set => this[PEDataDirectoryKind.Import] = value; } /// @@ -138,7 +145,6 @@ public PEImportDirectory? Import public PEResourceDirectory? Resource { get => (PEResourceDirectory?)this[PEDataDirectoryKind.Resource]; - set => this[PEDataDirectoryKind.Resource] = value; } /// @@ -147,7 +153,6 @@ public PEResourceDirectory? Resource public PEExceptionDirectory? Exception { get => (PEExceptionDirectory?)this[PEDataDirectoryKind.Exception]; - set => this[PEDataDirectoryKind.Exception] = value; } /// @@ -156,7 +161,6 @@ public PEExceptionDirectory? Exception public PESecurityCertificateDirectory? Certificate { get => (PESecurityCertificateDirectory?)this[PEDataDirectoryKind.SecurityCertificate]; - set => this[PEDataDirectoryKind.SecurityCertificate] = value; } /// @@ -165,7 +169,6 @@ public PESecurityCertificateDirectory? Certificate public PEBaseRelocationDirectory? BaseRelocation { get => (PEBaseRelocationDirectory?)this[PEDataDirectoryKind.BaseRelocation]; - set => this[PEDataDirectoryKind.BaseRelocation] = value; } /// @@ -174,7 +177,6 @@ public PEBaseRelocationDirectory? BaseRelocation public PEDebugDirectory? Debug { get => (PEDebugDirectory?)this[PEDataDirectoryKind.Debug]; - set => this[PEDataDirectoryKind.Debug] = value; } /// @@ -183,7 +185,6 @@ public PEDebugDirectory? Debug public PEArchitectureDirectory? Architecture { get => (PEArchitectureDirectory?)this[PEDataDirectoryKind.Architecture]; - set => this[PEDataDirectoryKind.Architecture] = value; } /// @@ -192,7 +193,6 @@ public PEArchitectureDirectory? Architecture public PEGlobalPointerDirectory? GlobalPointer { get => (PEGlobalPointerDirectory?)this[PEDataDirectoryKind.GlobalPointer]; - set => this[PEDataDirectoryKind.GlobalPointer] = value; } /// @@ -201,7 +201,6 @@ public PEGlobalPointerDirectory? GlobalPointer public PETlsDirectory? Tls { get => (PETlsDirectory?)this[PEDataDirectoryKind.Tls]; - set => this[PEDataDirectoryKind.Tls] = value; } /// @@ -210,7 +209,6 @@ public PETlsDirectory? Tls public PELoadConfigDirectory? LoadConfig { get => (PELoadConfigDirectory?)this[PEDataDirectoryKind.LoadConfig]; - set => this[PEDataDirectoryKind.LoadConfig] = value; } /// @@ -219,7 +217,6 @@ public PELoadConfigDirectory? LoadConfig public PEBoundImportDirectory? BoundImport { get => (PEBoundImportDirectory?)this[PEDataDirectoryKind.BoundImport]; - set => this[PEDataDirectoryKind.BoundImport] = value; } /// @@ -228,7 +225,6 @@ public PEBoundImportDirectory? BoundImport public PEDelayImportDirectory? DelayImport { get => (PEDelayImportDirectory?)this[PEDataDirectoryKind.DelayImport]; - set => this[PEDataDirectoryKind.DelayImport] = value; } /// @@ -237,7 +233,6 @@ public PEDelayImportDirectory? DelayImport public PEImportAddressTableDirectory? ImportAddressTableDirectory { get => (PEImportAddressTableDirectory?)this[PEDataDirectoryKind.ImportAddressTable]; - set => this[PEDataDirectoryKind.ImportAddressTable] = value; } /// @@ -246,7 +241,6 @@ public PEImportAddressTableDirectory? ImportAddressTableDirectory public PEClrMetadata? ClrMetadata { get => (PEClrMetadata?)this[PEDataDirectoryKind.ClrMetadata]; - set => this[PEDataDirectoryKind.ClrMetadata] = value; } internal void Set(PESecurityCertificateDirectory? directory) => Set(PEDataDirectoryKind.SecurityCertificate, directory); diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs index 0fb3b3e..8c03b0b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs @@ -151,7 +151,7 @@ internal override void Bind(PEImageReader reader) { if (entry is PEExceptionFunctionEntryX86 entryX86) { - if (!peFile.TryFindContainerByRVA((RVA)(uint)entryX86.BeginAddress.RVO, out var beginAddressContainer)) + if (!peFile.TryFindByRVA((RVA)(uint)entryX86.BeginAddress.RVO, out var beginAddressContainer)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid begin address {entryX86.BeginAddress.RVO} in exception directory"); return; @@ -165,7 +165,7 @@ internal override void Bind(PEImageReader reader) } // Need to subtract 1 to get the end address - if (!peFile.TryFindContainerByRVA((RVA)(uint)entryX86.EndAddress.RVO - 1, out var endAddressContainer)) + if (!peFile.TryFindByRVA((RVA)(uint)entryX86.EndAddress.RVO - 1, out var endAddressContainer)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid end address {entryX86.EndAddress.RVO} in exception directory"); return; @@ -179,7 +179,7 @@ internal override void Bind(PEImageReader reader) } - if (!peFile.TryFindContainerByRVA((RVA)(uint)entryX86.UnwindInfoAddress.RVO, out var unwindInfoAddressContainer)) + if (!peFile.TryFindByRVA((RVA)(uint)entryX86.UnwindInfoAddress.RVO, out var unwindInfoAddressContainer)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid unwind info address {entryX86.UnwindInfoAddress.RVO} in exception directory"); return; @@ -199,7 +199,7 @@ internal override void Bind(PEImageReader reader) } else if (entry is PEExceptionFunctionEntryARM entryARM) { - if (!peFile.TryFindContainerByRVA((RVA)(uint)entryARM.BeginAddress.RVO, out var beginAddressContainer)) + if (!peFile.TryFindByRVA((RVA)(uint)entryARM.BeginAddress.RVO, out var beginAddressContainer)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidExceptionDirectory_Entry, $"Invalid begin address {entryARM.BeginAddress.RVO} in exception directory"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs index 9c4fd3a..d0af0dc 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs @@ -51,7 +51,7 @@ public override unsafe void Read(PEImageReader reader) continue; } - if (!reader.File.TryFindSection(rva, out var section)) + if (!reader.File.TryFindSectionByRVA(rva, out var section)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportAddressTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs index ad7ee4c..4121aeb 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs @@ -46,7 +46,7 @@ public override unsafe void Read(PEImageReader reader) MinorVersion = exportDirectory.MinorVersion; OrdinalBase = (ushort)exportDirectory.Base; - if (!reader.File.TryFindSection(exportDirectory.Name, out _)) + if (!reader.File.TryFindSectionByRVA(exportDirectory.Name, out _)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidName, $"Unable to find the section for Name {exportDirectory.Name}"); return; @@ -59,7 +59,7 @@ public override unsafe void Read(PEImageReader reader) // Not sure this one happen if (exportDirectory.AddressOfFunctions != 0) { - if (!reader.File.TryFindSection(exportDirectory.AddressOfFunctions, out var sectionAddressOfFunctions)) + if (!reader.File.TryFindSectionByRVA(exportDirectory.AddressOfFunctions, out var sectionAddressOfFunctions)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfFunctions, $"Unable to find the section for AddressOfFunctions {exportDirectory.AddressOfFunctions}"); return; @@ -74,7 +74,7 @@ public override unsafe void Read(PEImageReader reader) // AddressOfNames can be 0 if (exportDirectory.AddressOfNames != 0) { - if (!reader.File.TryFindSection(exportDirectory.AddressOfNames, out var sectionAddressOfNames)) + if (!reader.File.TryFindSectionByRVA(exportDirectory.AddressOfNames, out var sectionAddressOfNames)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfNames, $"Unable to find the section for AddressOfNames {exportDirectory.AddressOfNames}"); return; @@ -90,7 +90,7 @@ public override unsafe void Read(PEImageReader reader) // AddressOfNames can be 0 if (exportDirectory.AddressOfNameOrdinals != 0) { - if (!reader.File.TryFindSection(exportDirectory.AddressOfNameOrdinals, out var sectionAddressOfNameOrdinals)) + if (!reader.File.TryFindSectionByRVA(exportDirectory.AddressOfNameOrdinals, out var sectionAddressOfNameOrdinals)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidAddressOfNameOrdinals, $"Unable to find the section for AddressOfNameOrdinals {exportDirectory.AddressOfNameOrdinals}"); return; @@ -112,7 +112,7 @@ internal override void Bind(PEImageReader reader) var peFile = reader.File; var diagnostics = reader.Diagnostics; - if (!peFile.TryFindContainerByRVA((RVA)(uint)NameLink.RVO, out var container)) + if (!peFile.TryFindByRVA((RVA)(uint)NameLink.RVO, out var container)) { diagnostics.Error(DiagnosticId.PE_ERR_ExportDirectoryInvalidName, $"Unable to find the section data for Name {(RVA)(uint)NameLink.RVO}"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs index dd3df7b..af46d6f 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs @@ -45,7 +45,7 @@ public override unsafe void Read(PEImageReader reader) for (int i = 0; i < Values.Count; i++) { var rva = spanRva[i]; - if (!reader.File.TryFindContainerByRVA(rva, out var sectionData)) + if (!reader.File.TryFindByRVA(rva, out var sectionData)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_ExportNameTableInvalidRVA, $"Unable to find the section data for RVA {rva}"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 8de4562..8d67558 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -52,7 +52,7 @@ public override void Read(PEImageReader reader) } // Find the section data for the ImportLookupTableRVA - if (!reader.File.TryFindSection(rawEntry.ImportAddressTableRVA, out var section)) + if (!reader.File.TryFindSectionByRVA(rawEntry.ImportAddressTableRVA, out var section)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidImportAddressTableRVA, $"Unable to find the section data for ImportAddressTableRVA {rawEntry.ImportAddressTableRVA}"); return; @@ -62,7 +62,7 @@ public override void Read(PEImageReader reader) var importLookupAddressTablePositionInFile = section.Position + rawEntry.ImportAddressTableRVA - section.RVA; // Find the section data for the ImportLookupTableRVA - if (!reader.File.TryFindSection(rawEntry.ImportLookupTableRVA, out section)) + if (!reader.File.TryFindSectionByRVA(rawEntry.ImportLookupTableRVA, out section)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportDirectoryInvalidImportLookupTableRVA, $"Unable to find the section data for ImportLookupTableRVA {rawEntry.ImportLookupTableRVA}"); return; @@ -145,7 +145,7 @@ internal override void Bind(PEImageReader reader) { // The RVO is actually an RVA until we bind it here var va = (RVA)(uint)entry.ImportDllNameLink.RVO; - if (!peFile.TryFindContainerByRVA(va, out var container)) + if (!peFile.TryFindByRVA(va, out var container)) { diagnostics.Error(DiagnosticId.PE_ERR_ImportLookupTableInvalidHintNameTableRVA, $"Unable to find the section data for HintNameTableRVA {va}"); return; diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index ba2c8b2..af31875 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -51,7 +51,7 @@ public void Bind(PEImageReader reader, bool allowOutOfRange) { // The RVO is an RVA until we bind it to a container below var va = (RVA)(uint)entry.HintName.RVO; - if (!peFile.TryFindContainerByRVA(va, out var container)) + if (!peFile.TryFindByRVA(va, out var container)) { if (allowOutOfRange) { diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs index 1d04d65..7fdfe35 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs @@ -91,7 +91,7 @@ internal override unsafe void Read(in ReaderContext context) Reserved = rawDataEntry.Reserved; var peFile = context.Reader.File; - if (!peFile.TryFindSection(rawDataEntry.OffsetToData, out var section)) + if (!peFile.TryFindSectionByRVA(rawDataEntry.OffsetToData, out var section)) { reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntryRVAOffsetToData, $"Invalid resource data entry RVA OffsetToData {rawDataEntry.OffsetToData} at position {reader.Position}"); return; diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase32.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase32.cs index a362d15..bcb04df 100644 --- a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase32.cs +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase32.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. @@ -11,7 +11,7 @@ internal struct RawImageOptionalHeaderBase32 /// /// The address relative to the image base of the beginning of the data section. /// - public uint BaseOfData; + public RVA BaseOfData; // // NT additional fields. diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.cs index a4b8a19..4cd0c18 100644 --- a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.cs +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderCommonPart1.cs @@ -47,10 +47,10 @@ internal struct RawImageOptionalHeaderCommonPart1 /// /// The address of the entry point relative to the image base when the executable starts. /// - public uint AddressOfEntryPoint; + public RVA AddressOfEntryPoint; /// /// The address relative to the image base of the beginning of the code section. /// - public uint BaseOfCode; + public RVA BaseOfCode; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEFile.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs index 1536239..e9f0361 100644 --- a/src/LibObjectFile/PE/PEFile.Read.cs +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -83,13 +83,8 @@ public override unsafe void Read(PEImageReader reader) if (pePosition < sizeof(PEDosHeader)) { - if (pePosition < 4) - { - diagnostics.Error(DiagnosticId.PE_ERR_InvalidPEHeaderPosition, "Invalid PE header position"); - return; - } - - _unsafeNegativePEHeaderOffset = (int)pePosition - sizeof(PEDosHeader); + diagnostics.Error(DiagnosticId.PE_ERR_InvalidPEHeaderPosition, $"Invalid PE header position 0x{pePosition:X}. PEFile does not support PE position < 0x{sizeof(PEDosHeader):X}"); + return; } else { @@ -214,6 +209,7 @@ public override unsafe void Read(PEImageReader reader) OptionalHeader.OptionalHeaderCommonPart2 = optionalHeader32.Common2; OptionalHeader.OptionalHeaderSize32 = optionalHeader32.Size32; OptionalHeader.OptionalHeaderCommonPart3 = optionalHeader32.Common3; + OptionalHeader.SyncPE32ToPE32Plus(); } else { @@ -235,18 +231,18 @@ public override unsafe void Read(PEImageReader reader) } // Read Directory headers - using var pooledSpanDirectories = TempSpan.Create(stackalloc byte[16 * sizeof(RawImageDataDirectory)], (int)OptionalHeader.NumberOfRvaAndSizes, out var rawDirectories); + using var pooledSpanDirectories = TempSpan.Create(stackalloc byte[16 * sizeof(RawImageDataDirectory)], (int)OptionalHeader.OptionalHeaderCommonPart3.NumberOfRvaAndSizes, out var rawDirectories); // Sets the number of entries in the data directory - Directories.Count = (int)OptionalHeader.NumberOfRvaAndSizes; + Directories.Count = (int)OptionalHeader.OptionalHeaderCommonPart3.NumberOfRvaAndSizes; - if (OptionalHeader.NumberOfRvaAndSizes > 0) + if (OptionalHeader.OptionalHeaderCommonPart3.NumberOfRvaAndSizes > 0) { var span = MemoryMarshal.AsBytes(rawDirectories); read = reader.Read(span); if (read != span.Length) { - diagnostics.Error(DiagnosticId.PE_ERR_InvalidNumberOfDataDirectories, $"Invalid number of data directory {OptionalHeader.NumberOfRvaAndSizes} at position {reader.Position}"); + diagnostics.Error(DiagnosticId.PE_ERR_InvalidNumberOfDataDirectories, $"Invalid number of data directory {OptionalHeader.OptionalHeaderCommonPart3.NumberOfRvaAndSizes} at position {reader.Position}"); return; } } @@ -286,7 +282,7 @@ private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan(); // Create directories and find the section for each directory - var maxNumberOfDirectory = (int)Math.Min(OptionalHeader.NumberOfRvaAndSizes, 15); + var maxNumberOfDirectory = (int)Math.Min(OptionalHeader.OptionalHeaderCommonPart3.NumberOfRvaAndSizes, 15); for (int i = 0; i < maxNumberOfDirectory && i < rawDirectories.Length; i++) { var directoryEntry = rawDirectories[i]; @@ -348,7 +344,7 @@ private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan _sections; - private int _unsafeNegativePEHeaderOffset; static PEFile() { @@ -61,9 +60,10 @@ public PEFile(PEOptionalHeaderMagic magic) Characteristics = Characteristics.ExecutableImage | Characteristics.LargeAddressAware }; - OptionalHeader = new(); - OptionalHeader.OptionalHeaderCommonPart1.Magic = magic; - Directories.Count = (int)OptionalHeader.NumberOfRvaAndSizes; + OptionalHeader = new PEOptionalHeader(this, magic); + + // Support all directories by default + Directories.Count = 16; // Update the layout which is only going to calculate the size. UpdateLayout(new()); @@ -78,6 +78,8 @@ internal PEFile(bool unused) Directories = new(); ExtraDataBeforeSections = new(this); ExtraDataAfterSections = new(this); + + OptionalHeader = new PEOptionalHeader(this); } /// @@ -85,46 +87,13 @@ internal PEFile(bool unused) /// public PEDosHeader DosHeader; - /// - /// Gets or sets an unsafe negative offset relative to the end of the DOS header. - /// - public unsafe int UnsafeNegativePEHeaderOffset - { - get => _unsafeNegativePEHeaderOffset; - set - { - // Should support Tiny PE layout http://www.phreedom.org/research/tinype/ - // Value must be >= sizeof(ImageDosHeader) - 4 and <= 0 - if (value < sizeof(PEDosHeader) - 4 || value > 0) - { - throw new ArgumentOutOfRangeException(nameof(value), $"PEHeaderOffset must be greater than {sizeof(PEDosHeader)}"); - } - - if (value < 0 && (_dosStub.Length != 0 || (DosStubExtra is not null && DosStubExtra.Length > 0))) - { - throw new InvalidOperationException("Setting a negative PEHeader offset is not compatible with having a DOS Stub and DosStubExtra"); - } - - _unsafeNegativePEHeaderOffset = value; - } - } - /// /// Gets or sets the DOS stub. /// public byte[] DosStub { get => _dosStub; - - set - { - if (_unsafeNegativePEHeaderOffset < 0) - { - throw new InvalidOperationException("Cannot set a DosStub when UnsafeNegativePEHeaderOffset is negative"); - } - - _dosStub = value ?? throw new ArgumentNullException(nameof(value)); - } + set => _dosStub = value ?? throw new ArgumentNullException(nameof(value)); } /// @@ -133,16 +102,7 @@ public byte[] DosStub public Stream? DosStubExtra { get => _dosStubExtra; - - set - { - if (_unsafeNegativePEHeaderOffset < 0) - { - throw new InvalidOperationException("Cannot set a DosStubExtra when UnsafeNegativePEHeaderOffset is negative"); - } - - _dosStubExtra = value; - } + set => _dosStubExtra = value; } /// @@ -153,7 +113,7 @@ public Stream? DosStubExtra /// /// Gets the optional header. /// - public PEOptionalHeader OptionalHeader; + public PEOptionalHeader OptionalHeader { get; } /// /// Gets a boolean indicating whether this instance is a PE32 image. @@ -218,17 +178,17 @@ public PESection AddSection(PESectionName name, RVA rva, uint virtualSize) return section; } - public bool TryFindSection(RVA rva, [NotNullWhen(true)] out PESection? section) + public bool TryFindSectionByRVA(RVA rva, [NotNullWhen(true)] out PESection? section) { var result = _sections.TryFindByRVA(rva, false, out var sectionObj); section = sectionObj as PESection; return result && section is not null; } - public bool TryFindSection(RVA rva, uint size, [NotNullWhen(true)] out PESection? section) + public bool TryFindSectionByRVA(RVA rva, uint size, [NotNullWhen(true)] out PESection? section) => _sections.TryFindByRVA(rva, size, out section); - public bool TryFindContainerByRVA(RVA rva, [NotNullWhen(true)] out PEObject? container) + public bool TryFindByRVA(RVA rva, [NotNullWhen(true)] out PEObject? container) => _sections.TryFindByRVA(rva, true, out container); @@ -292,7 +252,7 @@ public bool TryFindByVA(VA32 va, [NotNullWhen(true)] out PEObject? result, out R var rawRva = va - (uint)OptionalHeader.ImageBase; var rva = (RVA)(uint)rawRva; - if (rawRva <= int.MaxValue && TryFindContainerByRVA(rva, out result)) + if (rawRva <= int.MaxValue && TryFindByRVA(rva, out result)) { offset = rva - result.RVA; return true; @@ -317,7 +277,7 @@ public bool TryFindByVA(VA64 va, [NotNullWhen(true)] out PEObject? result, out R var rawRva = va - OptionalHeader.ImageBase; var rva = (RVA)rawRva; - if (rawRva <= uint.MaxValue && TryFindContainerByRVA(rva, out result)) + if (rawRva <= uint.MaxValue && TryFindByRVA(rva, out result)) { offset = rva - result.RVA; return true; @@ -328,6 +288,44 @@ public bool TryFindByVA(VA64 va, [NotNullWhen(true)] out PEObject? result, out R return false; } + /// + /// Automatically update directories from the content of the sections. + /// + /// The diagnostics to output errors. + public void UpdateDirectories(DiagnosticBag diagnostics) + { + ArgumentNullException.ThrowIfNull(diagnostics); + + Directories.Clear(); + + foreach (var section in _sections) + { + foreach (var content in section.Content) + { + if (content is PECompositeSectionData compositeSectionData) + { + compositeSectionData.UpdateDirectories(this, diagnostics); + } + } + } + + foreach (var extraData in ExtraDataAfterSections) + { + if (extraData is PESecurityCertificateDirectory securityCertificate) + { + var existingSecurityCertificate = Directories[PEDataDirectoryKind.SecurityCertificate]; + if (existingSecurityCertificate is not null) + { + diagnostics.Error(DiagnosticId.PE_ERR_DirectoryWithSameKindAlreadyAdded, $"A directory with the kind {PEDataDirectoryKind.SecurityCertificate} was already found {existingSecurityCertificate} while trying to add new directory {securityCertificate}"); + } + else + { + Directories[PEDataDirectoryKind.SecurityCertificate] = securityCertificate; + } + } + } + } + /// /// Updates the layout of this PE file. /// @@ -341,6 +339,9 @@ public void UpdateLayout(DiagnosticBag diagnostics) /// protected override unsafe void UpdateLayoutCore(PELayoutContext context) { + // Update the content of Directories + UpdateDirectories(context.Diagnostics); + var position = 0U; // Update DOS header @@ -367,6 +368,8 @@ protected override unsafe void UpdateLayoutCore(PELayoutContext context) // Update directories position += (uint)(Directories.Count * sizeof(RawImageDataDirectory)); + // Update the internal header + OptionalHeader.OptionalHeaderCommonPart3.NumberOfRvaAndSizes = (uint)Directories.Count; CoffHeader._SizeOfOptionalHeader = (ushort)(position - startPositionHeader); @@ -387,9 +390,7 @@ protected override unsafe void UpdateLayoutCore(PELayoutContext context) { context.Diagnostics.Error(DiagnosticId.PE_ERR_TooManySections, $"Too many sections {_sections.Count} (max 96)"); } - - - + // Update COFF header CoffHeader._NumberOfSections = (ushort)_sections.Count; CoffHeader._PointerToSymbolTable = 0; diff --git a/src/LibObjectFile/PE/PEOptionalHeader.cs b/src/LibObjectFile/PE/PEOptionalHeader.cs index 25730c0..088aaf5 100644 --- a/src/LibObjectFile/PE/PEOptionalHeader.cs +++ b/src/LibObjectFile/PE/PEOptionalHeader.cs @@ -10,7 +10,7 @@ namespace LibObjectFile.PE; -public struct PEOptionalHeader +public sealed class PEOptionalHeader { internal RawImageOptionalHeaderCommonPart1 OptionalHeaderCommonPart1; internal RawImageOptionalHeaderBase32 OptionalHeaderBase32; @@ -20,8 +20,19 @@ public struct PEOptionalHeader internal RawImageOptionalHeaderSize64 OptionalHeaderSize64; internal RawImageOptionalHeaderCommonPart3 OptionalHeaderCommonPart3; - public PEOptionalHeader() + private readonly PEFile _peFile; + private PESection? _baseOfCode; + private PESectionDataLink _entryPointLink; + + internal PEOptionalHeader(PEFile peFile) + { + _peFile = peFile; + } + + internal PEOptionalHeader(PEFile peFile, PEOptionalHeaderMagic magic) { + _peFile = peFile; + // Clear all fields OptionalHeaderCommonPart1 = default; OptionalHeaderBase32 = default; @@ -32,19 +43,19 @@ public PEOptionalHeader() OptionalHeaderCommonPart3 = default; // Setup some fields to some default values - OptionalHeaderCommonPart1.Magic = PEOptionalHeaderMagic.PE32Plus; + OptionalHeaderCommonPart1.Magic = magic; OptionalHeaderCommonPart1.MajorLinkerVersion = 1; - OptionalHeaderBase64.ImageBase = 0x400000; + OptionalHeaderBase64.ImageBase = magic == PEOptionalHeaderMagic.PE32 ? 0x1400_0000U : 0x14000_0000UL; OptionalHeaderCommonPart2.SectionAlignment = 0x1000; OptionalHeaderCommonPart2.FileAlignment = 0x200; OptionalHeaderCommonPart2.MajorOperatingSystemVersion = 6; OptionalHeaderCommonPart2.MajorSubsystemVersion = 6; + OptionalHeaderCommonPart2.Subsystem = Subsystem.WindowsCui; OptionalHeaderCommonPart2.DllCharacteristics = DllCharacteristics.HighEntropyVirtualAddressSpace | DllCharacteristics.DynamicBase - | DllCharacteristics.NxCompatible | DllCharacteristics.TerminalServerAware; OptionalHeaderSize64.SizeOfStackReserve = 0x100_000; @@ -111,19 +122,26 @@ public uint SizeOfUninitializedData /// /// The address of the entry point relative to the image base when the executable starts. /// - public uint AddressOfEntryPoint + public PESectionDataLink AddressOfEntryPoint { - get => OptionalHeaderCommonPart1.AddressOfEntryPoint; - set => OptionalHeaderCommonPart1.AddressOfEntryPoint = value; + get => _entryPointLink; + set => _entryPointLink = value; } /// /// The address relative to the image base of the beginning of the code section. /// - public uint BaseOfCode + public PESection? BaseOfCode { - get => OptionalHeaderCommonPart1.BaseOfCode; - set => OptionalHeaderCommonPart1.BaseOfCode = value; + get + { + return _baseOfCode; + } + set + { + _baseOfCode = value; + OptionalHeaderCommonPart1.BaseOfCode = _baseOfCode?.RVA ?? 0; + } } /// @@ -132,9 +150,10 @@ public uint BaseOfCode /// /// Only valid for PE32. /// - public uint BaseOfData + public RVA BaseOfData { get => OptionalHeaderBase32.BaseOfData; + set => OptionalHeaderBase32.BaseOfData = value; } // NT additional fields. @@ -345,7 +364,7 @@ public uint LoaderFlags /// public uint NumberOfRvaAndSizes { - get => OptionalHeaderCommonPart3.NumberOfRvaAndSizes; + get => (uint)_peFile.Directories.Count; } internal void SyncPE32PlusToPE32() @@ -356,4 +375,13 @@ internal void SyncPE32PlusToPE32() OptionalHeaderSize32.SizeOfHeapReserve = (uint)OptionalHeaderSize64.SizeOfHeapReserve; OptionalHeaderSize32.SizeOfHeapCommit = (uint)OptionalHeaderSize64.SizeOfHeapCommit; } + + internal void SyncPE32ToPE32Plus() + { + OptionalHeaderBase64.ImageBase = OptionalHeaderBase32.ImageBase; + OptionalHeaderSize64.SizeOfStackReserve = OptionalHeaderSize32.SizeOfStackReserve; + OptionalHeaderSize64.SizeOfStackCommit = OptionalHeaderSize32.SizeOfStackCommit; + OptionalHeaderSize64.SizeOfHeapReserve = OptionalHeaderSize32.SizeOfHeapReserve; + OptionalHeaderSize64.SizeOfHeapCommit = OptionalHeaderSize32.SizeOfHeapCommit; + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs index 5311e19..bb68224 100644 --- a/src/LibObjectFile/PE/PEPrinter.cs +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -105,8 +105,8 @@ private static void PrintOptionalHeader(PEFile file, ref TextWriterIndenter writ writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfCode),indent} = 0x{file.OptionalHeader.SizeOfCode:X}"); writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfInitializedData),indent} = 0x{file.OptionalHeader.SizeOfInitializedData:X}"); writer.WriteLine($"{nameof(PEOptionalHeader.SizeOfUninitializedData),indent} = 0x{file.OptionalHeader.SizeOfUninitializedData:X}"); - writer.WriteLine($"{nameof(PEOptionalHeader.AddressOfEntryPoint),indent} = 0x{file.OptionalHeader.AddressOfEntryPoint:X}"); - writer.WriteLine($"{nameof(PEOptionalHeader.BaseOfCode),indent} = 0x{file.OptionalHeader.BaseOfCode:X}"); + writer.WriteLine($"{nameof(PEOptionalHeader.AddressOfEntryPoint),indent} = {file.OptionalHeader.AddressOfEntryPoint}"); + writer.WriteLine($"{nameof(PEOptionalHeader.BaseOfCode),indent} = {file.OptionalHeader.BaseOfCode}"); writer.WriteLine($"{nameof(PEOptionalHeader.BaseOfData),indent} = 0x{file.OptionalHeader.BaseOfData:X}"); writer.WriteLine($"{nameof(PEOptionalHeader.ImageBase),indent} = 0x{file.OptionalHeader.ImageBase:X}"); writer.WriteLine($"{nameof(PEOptionalHeader.SectionAlignment),indent} = 0x{file.OptionalHeader.SectionAlignment:X}"); diff --git a/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.cpp b/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.cpp index cf099f9..b9dd07c 100644 --- a/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.cpp +++ b/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.cpp @@ -1,5 +1,5 @@ #include -// WinMain + void mainCRTStartup(void) { ExitProcess(156); From 105cafefbf753f940891b3c86e4afa5a1b6f7671 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 1 Oct 2024 08:58:54 +0200 Subject: [PATCH 82/87] Update logo --- img/banner.png | Bin 4353 -> 20867 bytes img/libobjectfile.pdn | Bin 0 -> 67678 bytes img/libobjectfile.png | Bin 3691 -> 19765 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/libobjectfile.pdn diff --git a/img/banner.png b/img/banner.png index b24db17b05a25d8ebab51ebd887de290547bb7e4..843f16f3803130a0a5dd0a878ce0bb4455ba322a 100644 GIT binary patch literal 20867 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 QCqjO7J94n%z{%wQ0F^2jNB{r; literal 4353 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 diff --git a/img/libobjectfile.pdn b/img/libobjectfile.pdn new file mode 100644 index 0000000000000000000000000000000000000000..a7abbd3d14198b32c5db7ddf0c44f16e5584f492 GIT binary patch literal 67678 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 vYQX$v3%!1Fl63C1TrE|>Xx#q{3*|0Dr0 literal 0 HcmV?d00001 diff --git a/img/libobjectfile.png b/img/libobjectfile.png index c163ba6d6855b61fb9aa55bb88e97db15052d3df..e95b684238827423fb724646d8dfc5c71954d2bc 100644 GIT binary patch literal 19765 zcmdpd^n7m`iJhW`A^=d8NO5U-gYcW&Rn6D~004Jge~1t+h1LLo zdjE4}MSWkhoz{SD*%`l-(`C92NRgjo%1Y}h8kIHMsmsf4icqzjS$^XkZOEZulCZ*KzYP^YWaG= z+WD&N)?nMsmu(!J1#C)EOs zaZfNWzVTz;^1a)BVL2gul%r(y1i`3ER^P#2fN|F_4YpTXKN!iEQ-D31JZ8e)@_rIs zDOLO}($(Q;xt^LXIM`o}_xE4QDrhe%&+FD41AHKXl*!6fvzTyZ@5WV!Yc@)C_fut zg3Z0cK!1A0m7DufF9zlOZ@wec+js{g|~ukDkJ7|-0vQ$(U9BSC%R{n zF12QaEL~P>n(7m9STf*?;e0B{L~)OVN>`2ZzfaI1-Vh_mJ;PSUG(exP4j97nKy)|A zxfiVoZ2t5$+v{5`{>vM-VkqinTta=%OXVp|Q5XrGa_95QI4&y7=alTTTW3b$2*0yU zdAxk!(Nbdj;V|jkKK`oiErVG|iMZy{zhc;UZH;YITw{*k%4C@l{4)mwey~Ikbc%61F z8gkO-0P^)*P$Knx-%==@aS*q?7f(XRsw0g=X*v%_hT;a>@1&((<+B5 z2r-Bnm51F`ytjRiKs+3|Y9|o{fm>y5lXL4rV@FZs+?HQRV1jqDZf^c1XWz;v`Gju? zy3Af5m$S&-fU0q56QoN)kcJ2RTkhpFT4iH5mh~q0k0TeVdj!4s5lBkj^PPQYN^`zT z7&Amld%IJUun>JZ8RyKqP&ExDMuO(m;V&6EwDuaDRKjCK63n=^BY5@o3psf#U%Ne#ll3i3bsD{?VO*~Paa z@vEo(WHx>Mk6hpzfrj$WobKxcPON-cW!yLI-+Xy#Ojri4u@*(ao-Y=f8YRs`&=?nc zj?LQ0m^g1r^~PHPtJa3!FOB6(V(|}tukr<|v}S%a?)wj+?&T>}ep9CuONN83>7{vl z_c|Rru6_*cUd${n!P7Vrq1U%F+_agZB{nE;h2)nP1o?aXT z9!xrQO?sD}rCoA=d}(}aXx#Y4Yuy?|=e_TH^691FX_p|y`9a;;T7EF7+Tj7+?w3J@ zPR2ykBaw>+fquS-G*XF?)-)vFk2!50hNV~#VHI2=Fu^EIu9`$?>(t20^t!VUQOQFE z)R)!4r%!D;7jMYOWejdF%Wc|n88q8+c|BMkOV4ALc^y*+6~f;g5$+-=Yhd`oC*#Is zA~YjGr%Ih{FeWtsib3Rp43(D=dGwZ{lkx}hDSEq0)RAC+oQxMDTGl7Dy^CQ_!&O>8 znVGwcv=>Rhm%#+nlYB!Na2_{t`QkG^KQ?q2rH~mJ_m7mEg@@>#LY(aO50hSDi&0L( zeE8=svb?`1rv=G573FwO%isMjY9E$?(yRJAoc zx$1fl5_^0E!}mE7jQ~P1_fj3>W`T`bqSkSjMYDJ^gzzls`E93t(sXP9Xa5DKAAG6zH|0w3?i#4hy1-rpZj-|oqsBhv;Iq(AvfC%Sq|&E z!be09RK3zx*-piIX&;sGO~8Js{ZH$!&DeaN=%b{Jvv1=cGtTCz9WnQB9`v6zas;Ml z0v0lS<)@WH&lKp1x)%s5;OQosy@Md3Q3i1Nw)}_%be@vH{Gp;&>sN(D5ffi!#*RKc zASSD!fs(=_P~@B}Jemt_leZPfz=Ohs9hq?1{RA#g#~s?>(u>4d0u)JTh=D{A14bmY zwrn2&o&IfFfT@RW#HY_V6v)Uxu$-B0zU~hAAQV-~<9h~6h2JGm>e!qxCJPHAI2t7; z62ih|Bb(AVnGl);s1{lx*`>~ECyMA$2wf1qT|=e3Am~eDLMH5Jw?I&q7A0rj1t$q9 zCXzQXLtk96N$QQx+w1X1{W0uJbs&v9RwOdm<<)F76 z2o@wr!KFP@C5-7+tHImOVKNW zZefBL9cLwotnYTy&T@bNEBAQ?Z2XG)JK)u)vc0w(i*iMZ5gisZLa>M9iGSqi6vUrq z!f}=~+DLv_ScLwu2=%Sq3}!M)ZZQ}m#C}f=FvaNxI*r6Rak_$*D7!e9+;MHYEW0eg zP@;%YPT_pH(9rh{mP?eoq!-Le;KkWMl_dCgYW92(jO0^14B#$5G_f%ian7)&9&kE-iuY-JFgOy}Fcr8{`{&pzVk{TWv&fwP7sDave# zi@nczoH;JemdV*IdCkttedMtV@>e{3S0fKxaxX;8Re7l-O(N@B{9&?N3VfkQktAMqhwdAFWuw2k@k#l+!6S?QFW zVI;7f?`{u#BR+QUS(9;xT_3U~+Ry(a&K)GzSDx`wKhADO#%yn=f4`DiX*?9iUqL*oIG6$niZBpr zdAwDUF=yBR^&38D$o3SNQhi+2sVC3`@`LCse%Wz$+)|w5tSermU}-qF#AdVVZeiC?#Whna=66 z-ju+Dyn>KR)AqV2S6TUJQ3t6t(Zx*hY7t)ryMg&3QSn1q@VSw^A}1~JSGAw) zLtakx=~xVO{Vd>cnzlnF|E*^Kv4Xp+vC@n(9Bn~OG1x^C&vA>dt%Emj1^x+d*W!se z`{Fq7rzhk>YSwBw$r7C@EI8})75bQjx4r*>Jw~cm$b3|#pZr_!_xkm!P@h}(F@|CW z=8Bw~hjmoQVem&+&mKdv`18AnK^m#-cg4Uikx*s@##>r{`Z5Y!d|ErdQEPfxWD@(W zw5b}&5>Q^qyH5TunoKNkzZ0I<(|tvC!te2x`FrCJwi{1xOZg9cNU-g@kk0To*8z^| z?zp~k77CqA^|0ko01#r+#ys`SV1J2do($GoUQvHpRM}`egc41fZc4!We)2gg#b*WO zY$-dqv)knFooiSr{+Je(O8=N z2f7I!0pFrKjQ%OyTx9J3$+z9Put+}3h4AZd2$@VVS7I9w)LXD^@{ZR?H0}Rh>Tz(j ztbtewLV80U*E1-D_GZXjFcNoiyhpPI?g)}%?!A~>k!DC`rDRrNqx7)E56X4^5gL42 zc?U=)9Hak+NqQZ$tsGVP;>{k9H)lsk7FNHlkd|+qN-Zv zMPd|nsFp_oPo*txWqxzb?=+^c6U_V#S>3Iu@Xtrc#mLaiju?^EL#;BZ5@dcaZF*$h z75}E!YfH0L$;Gx_R2Oa!IB&@hfea(@+IpCk+V|+EOp?fTQQSWQky8yUhldI!Gl*NK{9@$>LXv18nUAfVNlLFrbN`;BANZk+usNsj>T0(N4+nF4WtKB-U zVgDlp8s?A$hu4(N=0a%GlHxlX>XM5zVyXx+TXDE>Dp{F*;}36uRvLY_%nU@N5D+lZ2_|A}B@ zRg}eEnFb{@C=nXls|p6`dWtH|&Clu|My0#V@8=un?JKV(v?bykTxmEWsJnxVzvXaXrU=E8kbJ@Et z2T!g1wyXv@G~XxHD26%5O2&`p_$_P4?;4b5yvV5sH?N_pi{T_;n?UcUIFAod|$EgbZ5!CdVLeRm1t$FVsK|FutP5honQcD!NEl^>wbCa`# zjNO%*s9S|J%fV}JY*_0jY9>&&nR^HIOPg(SJ(oG!rm%(z8VxFI8E1a>vLr#e_nTsO z+H(#y*1zkOU(-|M02)0nd!&5WWQvKbo#)o%lO@_z?uV*(^rp|e^DYLeA`+km;MtXKFe4Hv#>p|2zl!zSY!S2WsEk{T5EM?v%Wqun9{vD)gFUIG~&7XWW(CxdB74UNnWmf8*^mycRe?>!}II=F3YY-1Lxez9gzGTQ`%}*x>SK;F9VsNG)z8 z^t<5X!=@-SL^%QFUp+R{FDaO#cTfsuoi8fZS6iliY7^4mEWoIU%rwA}W(>RSE~&)lCB3MdXH2|y%PUYTdZB#3UCE1Ip92u!W2 zzKbq$auXNlP_URx#w*SG$fUDf{|k7FpcmctO>TYen?Q(4uBJzur|nl`)Q_Jwc$poD zx!gV9e&E=d8bO}`)wPz?}W(-2-ROKEQ{0)Kx5y_KGHih1XbuWB)Ojic{K zeL6`ZaGoCW?BHd3Z+6+<1NT~vw705WGtc2x5$H=z~YOR?bw+`+5a! z0U@qO=Ks1wOWWODVN%-iKDKl1?Y~>Fm%k0)lj%_cvg>hoZ8EF4X-K*}pIm>le(SK3t*b56aKe*HdS0#fG@*pE|8Xt7Ho6J(Or=gITrLdWRza zWXKGnM0gW@iiyZBu1cd1>u8))ElxGo)ker+oE zRSusnQQSsd7EZ9^TDVcZqP#=D`nOyUOpib5<~nL{CRD#*xliHg)fmlaRl|-FwmYMN zmASL*K=8u1dNcOH)p2N8Hz1sCr#j2ZFZ++WWh&QYV>Gq=s*fvrHe~OPRg72C#(}sR zqv&A$nmLqAKgU;W16HfY(hpLm&JwrD7u3#bNtpA>+>~K|Cvs2@FGA&DkU|u0^IE|t z82nz>2O8&i%aeU`30fI#T)P-zP$7SkdPeg~ViLIhvAqbcG{z}Rx|J(0f_gN*|L+mB z?+FP_)gp}c;`ZRJ2U6I%-_0g$GKjNqCO?*#LA_%lWvf(*3tjx+)6RIp8aC|MNfNKF zRH@ht1UGk@yC@e?GJ(EG4t-KoMQ$OWNb~AlR11E)D|(&u0TgteUVnZ{M2M&cgnge| z)X3>bAj7vYmgsj!DY8w<7X|6VPiRnbZ_ks`9I}f<$z}Bqh)|H%_Y!0!`%wb~-VI`u z@ed#wDe@K`XakYrpJ;3s2ryExqc;G;E4X%>7~ z&HfLb-IU@Wb|_3FEqcE8AZhxbq!2*~gyJ>T3Krb_8c?M8z+71Lm2b!`HV~yf=uCdK z%VX9hgYuww+sf1>)`Ry$?ZOoyZt5I)|;>a%j1os~}>Z zScpIjwyC9yMr3#YSum4Jt~dKI~Q)O2_KaT_`c9)#Vsy=g_#$mxe^l7}Tm5C6A62&c zO9VE`-Yo@Z!{Po?AHc$DLOp8_=I0&J=kywbf`b`hYYrNN%^Z}BRESjfSHmno#E&bI zA)l)_0bGkJ26rs_jBLxs7kw!pZuuBu>PVEa7okb|;sX_vLS+eBp*AJR`|nJg92Bo1 z+&dxhsXb*F!16QhOIY>0iDXp0y0&=2ah`_N z{iRoxr63vsdP$1K*N-&W&r87JH{m}yXed=cBtXD1Q3UdP5~C+2h z_{+KB3kn}$)FkKBChT1I`uDriA|u2^2IboFaF(+oS5?5wyU3dQJ3F5+l?HV^ZTZub zXdu~1=X0*Q?TU_H4sBK$S3k5fdK0{krbXuU_!GK>j_$0z{LsFTilx}VMV^13Gv`bQ zE4}!J6#(CEyY0xwlAx$;H9}6QBJ~Nr;YlNOW}*^^^7bYh&Iw{m{m^0@o!OSKYI?Zd zEQgoFNbQxOsv24^J{vx<+l`F)3VkjkN9#^$b!R#a?ew>5cQWV0MML|!pJM_P!ha%d zi)sC1_+|oB>#^#;Wiz+ZJdm=X>IgjCnCXx*{rcu6=LmK9w4%3Emf}%)brLLV1z<6c z-qtWi_&C-)c0qlH5#;2TWX=ZAPAOOj2J0W-BhR0`GJ4!4P|;TMyXxbUBk_Fbs)tL` zbn`_X?8G2X8WOZ ztR}oMAnipJcnrRfTLw%X4bwiXTmC&zWSiyvU5c~!pzhM6&rG9`JrN)u>ZP?~#+_vicXke9bQ9YwzFCp*`0C$iseAZYV6H{0xJodEX_8b zy)|>a1?jqXi$&lMUJ}S6r zORtXN=}m9Q#dynaA3jVF)QNxBe5+i?mVxdSDF0AUbIaGJ^dOSQ^n+L7^r9r|yV^AW zz=R@M!;mUe_7fhP(fqGIMtM6X#=Dz1 z-l5(8W%J|7n|%Akl^%g|Y|AC+uP!f8l4rmf}AUmI(JnI?+dNHE;mX1a}v++#W7=;ym< zl0?`a^G`^lV_e%H23jqy-_6E{tCqB>9)UjFCwgo@o7r{(9S0LP$|BHCjnjvzISK^j zkGH^Ouz|Pz49mdG2gy}BP8QM`NY2jzQSi_sN5FZbN}%+R;&2HnfDfN8F#pZe54y>l znq%`0weMxBm4o`ya^Yp+f0KPe* zS@Hb0V7!OU;gJz*3f8jJ99HnR>nBsT6uv|w;d$>}%^DDlS=k8TW999q89;O>;; zi)~;yMfKRPV^Y{XC*3^9M^@#J)v+UQXlNFQe}>m8XG?f=RS50lJhMJuk<`>e!j6K+ z!sRkS1P|jM=&H2hdrO+o6MXW``;T1mFE*zeCc8Tg!Mi(+{HJkF6YF9Un6au_0Jrzk zdEVAsd|*sd>ji7GGdWs_H4{V|cXG#uFip>1leZm5oRT%5NF=YcQ7XHL50NTa=$eY?E{3aBdX{E_>K4ixc6dOU!O z$U5L%Ac!XZjDlXdQ#a0}>0rf%&zDhwlHKb>2dEm}*Z&+a1qOjv(o9_X+8-(2%gmsY zVRY$BVzA!RwxS*yThO`vDuKE-!bzLs)zV6+;lnds3H*)#{`KJ#jl_eH(Jo#}YKrPU z`40yXk)aEvU8gT_AaCl+!RNST{?q z2o4SHDDBKxj|sJ0*sJ~u2Uf`!BgiC_^GpgfME4X|+J@2$ z*LT$w5t!|+Pz!(?==)h*i{rBnOZfxAhhgs!mS&;tlG#-D`1}yjSdN2yT=SkA{8yPoqF3Ys|Cp+StF?ZP@d(T{hnZGs^K4b$7u8l^$l8J z?0YI!=NVFJK`IF~%B$c)O*1BC?1Dwd22l0i9PST=dYAanaEYnnkA4f?iQ_OJVWB#2 z_fDVfxUjV=ek-PCIF?=sUJHAKTX&T{(rlFq(ub-UP~GxkYUHRi`Rm%%J%pjZFkLqv zBaJaFBGxM$SoAIeXRkhjJ~iEiM$Q|Cy`KaKom~9%!QMo#2mR-Px}tXzWvyOr0|o;n77fBg+pilZ?4dLR`p zv~3UmXOh>Do_)92^`vyUs?8n^0sCUjliQB5kyg-cLYRQiYjHh6h1pWkDfx-%Qe$$`KYv^&+b00scqcEI z8sWdKvF0E-{(m?b^a?dWDoVXV4Q{oDi9sf>Z!i&vP`ExY8b_CAz3~ z8(Dp4YYG`Om?!nuL3-?(2l8vt>61=M8?vtJK_P#y`eKYwr>%Ko@J_L;t?{ipyOj9| zE)y1k{n3}Q1ku@6NyO3412ySMKZx{Har5uzQh&8!70|4gGRfS&&$e?t;ugq}Y(k|# za?Qe2#W~vJs5EF0{%XD7pN;RrTaUNG&t5gQ+pe$zT&(`4!u=^GRhwP^jMe)<_M zn9tM!_jEEA2#Ije%i^_L&75_035{N(k$-P6C$YC}S`bVB3}O)JQ3hrp-8u1K-7XTh zN}G7yPLr^vZ$-A4DadX>#ofRGpgR!|C68!yXPv606q~QIGFq!0 z6NvaNsHS8@A+>DEyTIu7SxOz%^8Bmj*Hyi&M~L!O*H;Qn4vo(9T}@XPP5*vw-RX4f z1^JRqbneF}Wp>($Z~D?AYg07GTN2&s#^6b+)LI{*Gn?Hdy$KS@6LQJL=43qAHr2fh zI4GY8{#V(MlhS(bbx*)$#P3xBdaC(tMsbh~KSc=kAn+>S>Lic)@^3w54!fBWx1)T% z|Dc_rb~}w`)TTF?!&~HYjaH(aAQFBQw*W6&M#aqcXa(`LZ9Il2M8g#2Fdj74RPpB2 z*7u2OYl~Q<$GAX5&HpxMSPI>DY>3DS4n@f*ZHC(|H16aH8g@_%&g{v*;*c|r^1qZ6 z_bJCly1rVYT%$=QE|5U%?_5A^MSs9zwsh>Ztg?1;ivqM@C zYS#Lsa)`~%7O%={zimYV?60ggXBUVdz5_#~Gy}OwIsKzp;{qCn|M60(iK-gB|M|N? z_`&&?1)>XLwXCq*qQN3_bHsl+peTUIu$9=YK@3sIt^3(?RoL^<1)^4Hg$N1Zz$*BW z2SIP&3DHnYXo6@-zr<<>g=JbFgTKnUB@?wSdS#I$f%1{`9e@iJ2-gO4nUH`?$?Cxg zP4!jK5Q+kkTE!_$eiqB649OUP$acojNZ!es$NIzVerE%t=|9J z&zC~p!vtI^SBrKE2;;A;>221?-40f7Z?Q&OaVO%FG&mfWj<;lFujn=OAo;;7=39n2 z>eQCB{|N>_GP|kF9uMOf2mGw^+JobyF5xZ6mYsARA}mqTNT00~FTdPnN5(!s+g%lw zy9aYgLa0J~`?c9*W^@g0X$M~yE;pi60CfB}K`foqLz|0`t&1E&#-STov)zNmCOkZZ zd5|Q!IB^3OX30TcCfwb(i(iA;{41I;v)HsdsZo^={~JUKtwwd0Ak?^Mx$#6?O4}S9 z`Id}5r1q=n!n+c65>6DmyU6q2UX(ArNY0 z+lrUS-0}J)H8u5R0bZ)qT(rvKaXB_sQ?{xNO7Iw?z8@O#@!DFS%0kPDNJxu3dfm__ zZpE{iuf~2?XMtO!CXJC^)@zD#E6)LiHh&uf0>|Hg>S{If*NZv)l2V)so*EI_qbfn3 z-coX`d2^nnz-I@dLz{HV=Bu>nL3e`Al8pRAP&egw^=#Zx{_ziv6PqH4Kh{xAss0rU|;kZ z2m7knb{-{snI!{~&GQ{XjU%bnWbw~B?D_>w%@X{S2c)Ucj7MGa!JOkQp$ks@q7}GpKCIHPt=#{ zp3*|7{A@Qg5aOBe^HAK^x3P$t-D?eAH%PFX!_Js#&A#s94-`mf=86YC(oQ7@ z^+ks&?Cp(wC5o_tk`m>c)g4TZ6(!9Ny8z^>gFiSG6Kp{08Q79)P_n2Wy(0A2@kH?pT)^0)%faUv9;_K~?a#Ak%kbdTrUQGMj^Od?jl+3UvR8heT{_o2&(f zE}_d7t+QRm{_;Q~AerWMCh|$@LNF#*>d+#?3L0I*X8b*vq)!T%77$1C;F`@|6Veio zV`0=}5Y54O(ehrapQfNIgDxR+{@qYxt>_ia0$}fyQP>%&1zPi z&Mc$3sEwm!C1+p!Zru?nmV4O6v~%cc}?Fhwb_}?I(6*b_Eh&Xv!5a7Lo&N!c-HNjVCA#>)>AOO68yw|7IoI z%HGCV9aG6}d4Eag|bKu_w@P(zK5La7}JCnxCKD zh2A(b=GhWwN8yTR8NxR8XHkJDgLb`v+nx8WMNkN?^eKmE?kbID{A)Eb#~5t9D}fqV zJUR|Yl&`Bu|8N0IKb71f_2|Fd$(PC=?dsIhttW8o+ZdhEGbgALNDa|oeTck;_|VV| z#hPm^dX{wc{APD=U|(kiH;z-8RtIzOfq>!>4talBJu3O{)^|zzkISuDlz7;*f|G;F z7cH*wcfoZe7F@G-K%Zt4Qoav&d%m66tz~D`WZA86Q>S=WCeJrHmJyPHYZv?`HC%No zI>w_E%&;EoS>5Vc?pOjXvs+K`tiHylN~|4(d;zU4`FMRLDgY=W8h!S>V|IGVe2SwnA6r_v*G7xM4$%JD`*@Vk;`+q0ACNU(i;}I5RT+2(rx?Kjy&(iC> z!>v3!YvQHMgkuunIr37iGQ;-rJ8@+c6olqReyk$K-3!c@SQNZow@{rkF*xh~ADrXD zpIAE}HtCN;J}ZkJs7>HMMb|449_UKT{nmBN#InsC4i49kuF~2q=slQYCV1m9a?F3; z{$IVBe9^JO_@62h#5o~P5tb(;y~-o7i2|_WLhO>&LUq)U7UyGgmv6aR6QL?)NecuN zL~u<^^b+1WFn2RZ1LNe6s=U0)rxyQ2KtVe>vu5dK6BvEg3$az+3njIGESVzY>M-eN6)u6+m-h)T@zs=yhC-H$hGO!>(l@2DLcldsA#mlE=W)CO<$;BKJAXpqbJ55(q#hi*F#ab~zA7 z9UR2M_h)=d;`TKBXlk-!r;|yCP`L#|PaWYvvvaJW9QLze2>D0C?p$k`sU5V_)0R=; z@D!RKFY2GkZ8ssMjqG$wCnGJUw~?J+R&UMBJLHtl>J^*v$0ZOBWq=8lVO8c81}sJy zPfFRDg-oKP)XaS}yFT%>Z~s;m2%h=Kftzf{Rn5=IwN=GlJ!7qrH#SPjD{`_j{-jbq ztGWL1aP*v;)!{Zc?Km@S>)j{KOI^ZAf?g#Rl3B(&k!rH7&J6+-b!?#g$eeHsh<&oN zTrajGl8t_+R$*gu+>l(8oMw|7H`cfWam$71vKHGBz_-6KV&KkyLeaQe$+0wOD?o## zu(T@P`2z`Tz?HU(mU6?%C6MK#i~UkV>%Il#W--^Xiuj$az_{+!^l=O?k0za65%ihV zWV*j?!sIK`=90OWdRx`WSij34sXJ@O`!AZZ|ybCAO|6$iO8{L6`57L^2^wgDtyxci^m zRh|Z#W9Z`~4YPWF?oYm)aJgd@G*nR%UghPn$ll$vklhz5-VyXE=v3LBO{;9nl+cUI z#4pGAai8!$QPR#jPibSwff-YhX(4YLXE%k4`6Oe$%JnQ)kD6Xg5d*AdnV0 z>Xw;ZcG_9x@29$sp7O((R6HwVDOMaw>ucQdDVKX%n}a^!2M4u3TvaZf2Jq3r}J@iD4HAh-FVzQ@ezru~qV1YNpF&6~8QvM*tHEEb5 z2vd0tj=w4E@no5o+7Q?FfJ08nOl!TGO!KB{xW|-__?9o1O*84qSXsaeMy#P`>Z8Px zT=shY(}tj&XyU&PBNGVJm|tVL?$!{6`oGgJa@-ZuJhh1?6BO!jcbM6e&@b2^FZvW> zHhkYp=-_T8QTXGXDErnl;A13;-E1lzo9YB z?-rF|6;8sT??|TsDP&bdwzk=7#syXWk-cqU%xhN7?kau;U14zFEA6D-Wq|;r6Usqj zGDVW;Ts;^m!ZV@CQxkn#!|b(=R5AL8zmlBws9s&`gH|ebha@YnCR>gjbV1G6&08Rr zvn~yik8>b&tbSJHs8l3vrf~G3N9o;$TeF7>Ca1>*EmjgEpKK~Nr!oPjwtWr_zwvi9 zj_1@{`Qy=EK82cSqq4NNOv0h%@sRDrMxK=SMxIdF4JQR@&a#TLqEp3j{oS#rN|CEZ ze>uCg1^t1fZ%uKxZ=$JkJU@x3WsWZ71{W4xy-?>dh`)eYa=2I4E0{+s(M?-_%IVwP z%nRb-^4i`;S2!q0)&ddjs}msX!Gx&iy-+^H>-YkR+_^I`&f1OD=%w7NihNVB{q}Zc zos(DoY4~ybD~_ED-tF?>Ch;-Qtu1VIMcHggWbe)OpWqfTs;8`#y}CAgg?8p>?o*c! zQ0&!%Z07Q?AMyibDs8uFsfx!zHm^l}$$~^X1bziRthi#Wqvm#Sl;|IyA<8+O>PYWz zmwH#~8axi!5QW(*jQRR^`C;i2o_~OLN{$&;jMmInRflhy43?k{horpT%d9;MkET?- zYR1k}Dit z=yX#r;nRAeS7FLp^Jj@N=twn#lKq4h=TSmLa{am|TWOI;*TGS5HRjOZVTu|g&j3Wj z1_2jKtyj5Ec>QoRBqnuf4B_H9znfF}xUi*pQ&vN+aghEJblXNj(oXwRQ{Y%Vi)WDX zUS*EAY42;6S|`);oi{06?BkP-mVPHpeOe~x2VZGKiwVUuyLzi$jGb5X4IMXi%T*k)G|N4^qYzZNU~zAp3d9MQ*ScDBL+xtae9(uqm|1F%6_s z_SN`i>3XT-m;$}ZV>B6iy?9z+0>-|sWNNImD^kVz4Mu}&sR7psx>IVoCDg6i~ z-N0RFwEr42;ZaqjelK$F^v0)J?TdVYX82t>+pZrTN;Vou^7UByPA-FjA7|MIXcPIm z{En(bX8kCO1DEs`a#PIRxEr6vrb>e>ImX()4<<`He(`h$01?x~F(W#gsl_5c2RZy) zdnL%=)}cAwSZy=hu!2|LxX%;q?6#0m<^dKYB^xVo>7OB9YR$qI(a(yV=M`loD9+*e zSZfKz?t#}qG{zLQ@@fuDC7mkI*Q5H=GlgsvTSeSWaaZ}gH87rZ--elt{RRj6fI zw(L-9;_zha(Q|-tatU>>L~sww^R~R$>bU2<eD`o;+4Qwn?Ex6MDIg*H#VS`IXPTMh z1G6YPDDlpq+$-_?lX#wA$wIX+EpB>AZgUb@OQHdnUmly+jH}Ur%vPJ4{@9f-FgM#w z86wLpD>)57)WoV;eu3jRd-dl9PQF&>7@7S|Encn8tZi9TBdiUsxDgt77Pu)=xj490 zA?g+we?#_-Zc53x_ME9rU7-C-i$tWTKF$Q%r}WQoRJFO6&b_(z>x9k9=}q-6Fyj|X zN!B>ZAymT%Qz}B0S#k*DB*qUYBuN_B~pR0FOGt`g*d`gD&WmoGv5UozQ8fO z6*hAQb5EH3Iry+BZ3O*JWt7lQd-?q=oc_g@fg!72a*2S;6OQST)YpWAL7U+rTIkVY zoZI_lCd%~+aX*vzpXg0RW<3|>z$@3k6((4|`o{vrtF@WU{msv;FwiI?LtVu9<8smb zAtR3c#*zc`>G)TwrA(k3Z);=B@X&;KzW1??d^!4x;~n;cN6L9eYIR)gVCe*yuVA@&+LiHJ z4tMmS;tY4Vs61jt0w=@TOW)g49e&D&gXXK6AD{*Nj`}CsKld0@ssNAqXuIHof^_`P zf+}ODNbpHB_&>O$t|n@+sw@?fFKO$vzRc-{4%)U?|K?9cv-H>Q_J4GnICxc+fURV^ zu$aX(kA*zEFMvG=(Kxu1Q7RqO)!woiQgoM4+46zNqLGttO@>uv;K+bqZS9-PRyT#p zu|hp3+tkxU;tGCMVOmbuxqOGXqSt0^R?;)$RR+N48)^V!Fp%t^9!EG9bo!yxyWn^6 zNCNwSMO%nDQ%dD83%ECQ(PA)j-;%FTf;BTNDz8ezzO;pe#!hqX z205p6-~Cb>w}nr_j7+I9iaW-%z*7lvoT2)9qKzPHT)qS=WiB)Lf7Nm&Zb@frSg(aP z7tCo|Z~=7enwqAVnxdlRHkM0@mO7~=(HLr)NlJ?8*tjLo+?m7{b3qZ%Bs6H;$cYq3 z%3V-!$!p{U8dqFOX}Q1ti2DzG=bZ0+=Y8JiJMa5#Vm+ayiLf~?L>^(a;euFYZ%$|u zv%e^wDR?jHwF5cSzL2UB0s5{7V|Lsb7d&-3shPTTU^S6s??ALL+dLiD55f~|^yIBn zpvLCmBIyXgkpf})OvJQFXW^YTI;%46rRhW$5~>L>F#JaID=y9Bvxiy64- z%LOic(w?L6d+(DlB%^e0B@I?OC9E>KwJZ49vLP(C%oAUI?1H|CAU3Y=gw7FA(`u_2l zsi=6(OjNwgfAL0b+fHrl1x?ohX4>u47+416dyK^pi}bvMi;U9t=Z3$O0+uSx-GTcW z36j^;b=Kr6(7doCnBzUc5g$cvJ<35(N3tBm$*KHTAIm9(Z_vs`(Ss}WP z48=UYvy9raTK5oE&7)@alMq~cZH+IM4X0NS-&NlIFFM2lpK391 zl_ktcOI{Cux`b3ln{!xp;j`)ccbL64TNypI;#m{&Ya^$fLlz`66f4yA2ZMQ+ztgnYY<{W5&D z(e1Tth^an+ZW90qI!5C}JoV+hNU}XaJF;Y<`?__!pbXkGkECZK)r?Yj68)= zAR9AEHAOIh2|PfZbun|K5iMk=IL;Kyo02QC%4z7WKM~!3nQPAc_tmaX ztIA~4&31ml^2xEh_BV(3P(u(*nFFIg)$4-2$U@0~a&p%3*z7L^ufEyay<~w+?v?L; zik|j ze5?D$h7=SgOP0^C3CTrNsM8Y5(}Rkxp08E{pVip1YC%SD!kcJqiQ234h|V|~Rdj{Q zY7m>EmU_}*Y_7_H)Mo2&XQ3gx>%1ZNg;?`}vOEd59L{#!1rBJW3(s{+^9gPS43nW~ zqDpL04fP`i(QG*i$NUeYfs|h4#im%pcuAn~#UJBe7t<5HSKIfFi?-$`lT7st28a0; z>%G^vZIczH?r2$RvV44~o=4pv+elUtg#xN|vEd!3akOS%NJ>@<5~p)HI6OW9e9?OY zTSju5{g>GE8*pX1mopr~MEQ|}q8ddtBAs-U+jnM%K=rlO0^jxE&%1>zh5zsR;jRNI zF*yfF?=;tP&3Q9!7v?Vgep5XOdhy%G%U06e>wa#oelr*M$$sWr+?ot%Ud-Xsm-u0+ z`yQpH&}6Q>xRY~%{8G~Sr{j5vUbvcRJ{|YBI0MYS1uqX=#(Vl;HvCSZ)lt9atjR%Ez%h_fllSi^-kFL*FR_16tQnzRmrqU!bb zFY6g78kQcvjMh$i4_rOE5e7TWhY+Xsngte5(~cJBGO3gX2bDl05psmIP>P^zCvT5>y_>Z-|#Nx7K%wR_8hr=R6I4!n7t6*+@vs1c#d?F@XmW! zD=GMUj#_}p^09NX-)<~p@8yemAxntoSxaGgubU3P6DJyPq3ghZ@4ob~tJbIShHAu= zVo1#!u{m|1WaX>@$_HG31P%!-7V@Ff7R9+>tioSvt&WcAt9V)Xv0vU=Gh6T@rC}UV zh*O2d${WhmsitxfAd)0A+b*Ww!tj=8CQN)JGbD2p~qeJLrT=?D5#M<2jnWfC5ea^7xd4yo;0K+ zyBk&iO7pebsXZPSZ=yPehY>PR{^vW(dE~*9Q7JS572kPFg8ol%HK|`UiB9xzK2>Bh z9ZMAIz>HdM_7$=%pZ}0qRDzSNz=}@6S}FQ^$57_L2{BcJXMklWY3zkRSxX21IcF)x z!f$g4i*=l0LR-uDy)C)xX?zEVg2ez(lz1HbT;>1d}r5dmf`K*^^}E;9u4o2ml0v&TiISxr#b&}4m880Lf(gyc1mhlOAKIt@-H5EmNBz`CRES#$-oxn}a+54*c+kLuqboK1*a7>;&kY*i&efAD>a*~VDxaT<4u zYw7}|$yrQ0Msg48G?`cWk1^7C2U`ZmF8^aadQnNoJ_7TClMkt9Fdi0X5L)_?U0{7> z$8i@5vf!;d*RDc5B(>&N!E@A3WP=Yl1$q8I(tC{fO?e~|aq{j0dYoseF z331{iN#v{z&Yge%#3PR2Q`TbAJ(u+nBv34BO0r3q+XI_Vk3oe%pfo}`hZX0<_`eVK fItQo1KYelBsi;(Ple~dZXF5Utp?)pL&i?#A+52sH literal 3691 zcmcJRc{J4jzsEl_W0@HlBiY4}C1j~2S!c#FS+h%ov7~I#)ECucFeq6DaTQrg_CgAg zF!`E7Ojms=!dM1b%bFSEj_>c>bMNimbARXlaX;rg&+GGgpYwWuUZ2$O&P(9+#A5s4C5T-wfN0CXT(g3k8Wks&J6Ig(~5(MT<15MU)RQWrQB(x z56?RVgt(~$RNO>Mm?A7Wj zO{lOc&qr7hEril~cwpJh!j1OvE!0UkYjA3b1N!u~J5g!`nl*KIh?$FPx%5%RzakF6 zd#I)9*abU)%4&zY`KgmUtih$$9mr6k%f4w?+O-Ocq1$kBW@+$2s$VE;(D}A~K2Usb zPB}^Q-ptQ0zj{_!R|hN(cU2~^oeu__dSWiGFBJ_-4}}WD>F?j+qC%{1Q|==yWT}r4 zSaOP|Szu@*8E3vhoA`?Zs_ywtK4HuFfeUfU!nF)DHfLD;wFnUP92vJLyI5+~=~3aH z3g87#nns=d)_}fUs{o?Td9Gf(D==SehMUl#KDNb@MQ6=QL}Wun6!K5=E2OT>#v>U# z#{Af1p^2Q0Uk$iPQea3k^%PanC#951|HOt$ScGV+H5m}=f$VTlv095*Chfp1F zy{gB3-1ffsE@^zav=k{d_7IbtjvTR$<�>|@-LIKO_V|q60M|EAGgZ8li zr4t@sRbiZ4j@85Rg3iU z5V3_K^D99!>huzLxv!i6DmOlCDS8aS-%Oy!Yd|xO{$rh-R$)Apsa6t$2rnb^pFk=Q z32^Q{-}v6Sc@wikc?$?*!S+P^yI)zpU3E82mRKAPzOY$MEB#vHN~8+0=G11ZG6Rzu zq@Nv7aQ4he(RX!_ldqPR)8D_kTqLvF+1z}(Q;A=hJ#0Qk=p)wHu46tlJj3>i%m=># zyI&d4>3+5R5+)Qh=qPuhrgz{v=_)Z6KI)1)V}) zQI0xZ1z})e(-wu|jG% zB3m!OOw4CJv)e4Iy;6Fkj|lliuFG4E+tyyCY7e3T&2XjKObxsJtLup2TQ zHtD{+_RG)qS?mki(Z?s3|I|p8yl~9QU9vZuFyBYJ_orabw;Ja)f@Bb zV76+W#C@w@QZgSPC>?J6nx$`Vs+yFUu1BOY)hXeq#2w5pJa43dZn5f1Z`L~*S(kow z>$PyW`V4WWu-9lv`dac#gg|vk``IOCV8+!5LI0&7{8C$s|5C?jiBpppHn^eESS)2X zq^xECOi|Fa^$$G!F-1F4{F)1sPdBd*(&b0Kl5;|c+utiA3}G?GF0v{bJcOkdi&oxu zwPdJ9`Oa7Vo+xQ8?%?z3KY|xus7&YV&9(ws-!}pG6g&1>H1Dbz2~axlYEm-*M(HcZ zmfOx|Eq5wE6h@)LVmdXr_Ml^ey}+RHwlbQO@5!3Y%?_g7)Ff+EkXdDWqb!P@ah4Ck zaN+YeVD|GB!HX3?)AXEC5GZ8ZE(Gh!r4iVFUq7{-qu2m)l}C5|@EgI3Ri^*Y8;_4L zBFm6zxiV`YnDAeUhF#O`LN=6ect!Bcdds0)Nr0;FEFA*`kouG9#z~LVQtd%!a6m8BMSe)kALUL|73sLlq9+^Ayyf2 zYh%SwH}K0tVn(Z+_j5XQ!lrXvMb`E&|XEuaa%CY1+J$H0uLf(!%u*~IH-`G z4XGVOg`^!vOZ4SO`oe^o3NTbkTEguWQuMw9mGx`&Qe`$h?32#U+8;tu~Kf6vvU-&xpxi$ zd&K`I@=*LVGdklPv#r=5w7t=@+-&qmSisLx;mZO0yV_sYIQ07!i6YQS%ze34WXu|V zi2Xc=`0U zjie0wQYD%;j=PkIpmuJkgyEg=$6{pj%tPyki`k2 za&!f+q^d!Y&DZe0S_(Hc%GxB7fno!3<>e0vGxE-}IY66)7Nr4ljsLP*j1bMT0sz46dCv8G->aj=Wn?aS<{i5zFB=x6mx;;8@hc!1cbe2V^?ik*lkKWP#Z8K=*_}}^j)a^ zT=UYeB=TL~}nMTcytZ-ks806|}?bHrr97(lgVCxQ+KnB&jA^Mc0Wf(dZoX z*m(U`Na@N_T_2PZM;%uu&MyT<opt&Jqx^hcD0MD+oGrT% zd9tBin|@})I!dtKwNYK}@g@8fgNAx~SvL|T{qD=FDVe1uFUekxSS5|9G&M#7ZV`B*hhGS!XEdOAmF|S)WT}y>kDYVVEN4J$4&bStx z`3CRbOdm47zjCIAi}n^S>vI>KQ_7lWeTfTy&H$z0O?6AS^d@To1N{FE~KqSt)Z Date: Tue, 1 Oct 2024 18:54:19 +0200 Subject: [PATCH 83/87] Add PE verification --- src/LibObjectFile.Tests/PE/PEReaderTests.cs | 13 +- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 5 +- src/LibObjectFile/Dwarf/DwarfAbbreviation.cs | 8 +- .../Dwarf/DwarfAbbreviationItem.cs | 4 +- .../Dwarf/DwarfAbbreviationTable.cs | 6 +- .../Dwarf/DwarfAddressRangeTable.cs | 4 +- src/LibObjectFile/Dwarf/DwarfAttribute.cs | 12 +- src/LibObjectFile/Dwarf/DwarfDIE.cs | 8 +- src/LibObjectFile/Dwarf/DwarfExpression.cs | 6 +- src/LibObjectFile/Dwarf/DwarfFile.cs | 4 +- src/LibObjectFile/Dwarf/DwarfInfoSection.cs | 8 +- src/LibObjectFile/Dwarf/DwarfLine.cs | 4 +- .../Dwarf/DwarfLineProgramTable.cs | 4 +- src/LibObjectFile/Dwarf/DwarfLineSection.cs | 6 +- src/LibObjectFile/Dwarf/DwarfLineSequence.cs | 4 +- src/LibObjectFile/Dwarf/DwarfLocationList.cs | 8 +- .../Dwarf/DwarfLocationListEntry.cs | 8 +- .../Dwarf/DwarfLocationSection.cs | 6 +- src/LibObjectFile/Dwarf/DwarfOperation.cs | 34 ++-- src/LibObjectFile/Dwarf/DwarfStringTable.cs | 4 +- src/LibObjectFile/Dwarf/DwarfUnit.cs | 10 +- src/LibObjectFile/Elf/ElfObjectFile.cs | 4 +- src/LibObjectFile/ObjectFileElement.cs | 35 +++- .../PE/DataDirectory/PEBaseRelocationBlock.cs | 7 +- .../PEBaseRelocationDirectory.cs | 17 ++ .../DataDirectory/PEBoundImportDirectory.cs | 17 ++ .../DataDirectory/PECompositeSectionData.cs | 8 + .../PE/DataDirectory/PEDebugDirectory.cs | 22 +++ .../DataDirectory/PEDebugSectionDataRSDS.cs | 2 +- .../DataDirectory/PEDelayImportDirectory.cs | 11 ++ .../PEDelayImportDirectoryEntry.cs | 11 ++ .../PE/DataDirectory/PEExceptionDirectory.cs | 12 ++ .../DataDirectory/PEExceptionFunctionEntry.cs | 7 +- .../PEExceptionFunctionEntryX86.cs | 10 +- .../PE/DataDirectory/PEExportDirectory.cs | 10 + .../PE/DataDirectory/PEExportFunctionEntry.cs | 6 + .../PE/DataDirectory/PEExportNameTable.cs | 9 + .../PE/DataDirectory/PEImportAddressTable.cs | 6 +- .../PE/DataDirectory/PEImportDirectory.cs | 12 ++ .../DataDirectory/PEImportDirectoryEntry.cs | 7 + .../PE/DataDirectory/PEImportFunctionEntry.cs | 9 + .../PE/DataDirectory/PEImportFunctionTable.cs | 10 + .../PE/DataDirectory/PEImportLookupTable.cs | 4 + .../PE/DataDirectory/PEResourceDataEntry.cs | 8 +- .../PE/DataDirectory/PEResourceDirectory.cs | 6 + .../DataDirectory/PEResourceDirectoryEntry.cs | 20 +- .../PE/DataDirectory/PEResourceString.cs | 2 +- .../PESecurityCertificateDirectory.cs | 2 +- src/LibObjectFile/PE/PEFile.Layout.cs | 186 ++++++++++++++++++ src/LibObjectFile/PE/PEFile.Verify.cs | 51 +++++ src/LibObjectFile/PE/PEFile.Write.cs | 17 +- src/LibObjectFile/PE/PEFile.cs | 160 --------------- src/LibObjectFile/PE/PEImageReader.cs | 4 +- src/LibObjectFile/PE/PEImageReaderOptions.cs | 7 +- src/LibObjectFile/PE/PEObject.cs | 8 +- src/LibObjectFile/PE/PEObjectBase.cs | 2 +- src/LibObjectFile/PE/PESection.cs | 11 +- src/LibObjectFile/PE/PEStreamExtraData.cs | 2 +- src/LibObjectFile/PE/PEStreamSectionData.cs | 2 +- src/LibObjectFile/PE/PEVisitorContext.cs | 29 +++ 60 files changed, 647 insertions(+), 272 deletions(-) create mode 100644 src/LibObjectFile/PE/PEFile.Layout.cs create mode 100644 src/LibObjectFile/PE/PEFile.Verify.cs diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index c5054fc..5e10fa8 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -36,7 +36,7 @@ public async Task TestPrinter(string name) await Verifier.Verify(afterReadText).UseParameters(name); // Update the layout - var diagnostics = new DiagnosticBag(); + var diagnostics = new DiagnosticBag() { EnableStackTrace = true }; peImage.UpdateLayout(diagnostics); var afterUpdateWriter = new StringWriter(); @@ -56,7 +56,7 @@ public async Task TestPrinter(string name) // Write the PE back to a byte buffer var output = new MemoryStream(); - peImage.Write(output); + peImage.Write(output, new PEImageWriterOptions() { EnableStackTrace = true }); output.Position = 0; byte[] outputBuffer = output.ToArray(); @@ -142,7 +142,7 @@ public void TestCreatePE() // Write the PE to a file // *************************************************************************** var output = new MemoryStream(); - pe.Write(output); + pe.Write(output, new() { EnableStackTrace = true }); output.Position = 0; var sourceFile = Path.Combine(AppContext.BaseDirectory, "PE", "generated_win64.exe"); @@ -176,7 +176,10 @@ public async Task TestWindows(string sourceFile) var inputBuffer = new byte[stream.Length]; stream.ReadExactly(inputBuffer); - peImage.UpdateLayout(new DiagnosticBag()); + peImage.UpdateLayout(new DiagnosticBag() + { + EnableStackTrace = true + }); var newSizeOfInitializedData = peImage.OptionalHeader.SizeOfInitializedData; @@ -188,7 +191,7 @@ public async Task TestWindows(string sourceFile) // Write the PE back to a byte buffer var output = new MemoryStream(); - peImage.Write(output); + peImage.Write(output, new PEImageWriterOptions() { EnableStackTrace = true }); output.Position = 0; var outputBuffer = output.ToArray(); diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index 8efa9b6..f672e0b 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -136,6 +136,7 @@ public enum DiagnosticId PE_ERR_InvalidBaseOfCode = 3018, PE_ERR_InvalidAddressOfEntryPoint = 3019, PE_ERR_DirectoryWithSameKindAlreadyAdded = 3020, + PE_ERR_VerifyContextInvalidObject = 3021, // PE Exception directory PE_ERR_InvalidExceptionDirectory_Entries = 3100, @@ -175,7 +176,9 @@ public enum DiagnosticId PE_ERR_InvalidDataDirectorySection = 3703, PE_ERR_BaseRelocationDirectoryInvalidVirtualAddress = 3704, PE_ERR_BaseRelocationDirectoryInvalidSizeOfBlock = 3705, - + PE_ERR_InvalidBaseRelocationBlock = 3706, + PE_ERR_BaseRelocationDirectoryInvalidSectionLink = 3707, + // PE Import PE_ERR_ImportDirectoryInvalidEndOfStream = 3800, PE_ERR_ImportLookupTableInvalidEndOfStream = 3801, diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs index 773a36a..2996406 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviation.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. @@ -206,7 +206,7 @@ public override void Write(DwarfWriter writer) Debug.Assert(writer.Position - startOffset == Size); } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { var endOffset = Position; @@ -216,7 +216,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { var item = itemPair.Value; item.Position = endOffset; - item.UpdateLayout(layoutContext); + item.UpdateLayout(context); endOffset += item.Size; } @@ -228,7 +228,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) foreach (var item in _items) { item.Position = endOffset; - item.UpdateLayout(layoutContext); + item.UpdateLayout(context); endOffset += item.Size; } } diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs index d2f602b..0a2a9d3 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.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,7 +30,7 @@ internal DwarfAbbreviationItem(ulong code, DwarfTagEx tag, bool hasChildren, Dwa public DwarfAttributeDescriptors Descriptors { get; private set; } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { var endOffset = Position; diff --git a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs index 9591b0c..8dcc94c 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.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. @@ -28,13 +28,13 @@ internal void Reset() _abbreviations.Clear(); } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { ulong endOffset = Position; foreach (var abbreviation in _abbreviations) { abbreviation.Position = endOffset; - abbreviation.UpdateLayout(layoutContext); + abbreviation.UpdateLayout(context); endOffset += abbreviation.Size; } diff --git a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs index 61ad6a0..38f2510 100644 --- a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.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. @@ -139,7 +139,7 @@ public override void Verify(DwarfVerifyContext context) } } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { ulong sizeOf = 0; // unit_length diff --git a/src/LibObjectFile/Dwarf/DwarfAttribute.cs b/src/LibObjectFile/Dwarf/DwarfAttribute.cs index 69e9263..cc5f5dc 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttribute.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttribute.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. @@ -130,10 +130,10 @@ protected override bool PrintMembers(StringBuilder builder) return true; } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { - var addressSize = layoutContext.CurrentUnit!.AddressSize; - var is64BitEncoding = layoutContext.CurrentUnit.Is64BitEncoding; + var addressSize = context.CurrentUnit!.AddressSize; + var is64BitEncoding = context.CurrentUnit.Is64BitEncoding; var endOffset = Position; switch (Form.Value) @@ -214,7 +214,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) case DwarfAttributeForm.RefUdata: { var dieRef = (DwarfDIE)ValueAsObject!; - endOffset += DwarfHelper.SizeOfULEB128(dieRef.Position - layoutContext.CurrentUnit.Position); // WriteULEB128((dieRef.Offset - _currentUnit.Offset)); + endOffset += DwarfHelper.SizeOfULEB128(dieRef.Position - context.CurrentUnit.Position); // WriteULEB128((dieRef.Offset - _currentUnit.Offset)); break; } @@ -239,7 +239,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) case DwarfAttributeForm.Exprloc: var expr = (DwarfExpression)ValueAsObject!; expr.Position = endOffset; - expr.UpdateLayout(layoutContext); + expr.UpdateLayout(context); endOffset += expr.Size; break; diff --git a/src/LibObjectFile/Dwarf/DwarfDIE.cs b/src/LibObjectFile/Dwarf/DwarfDIE.cs index aef73ee..cd103f4 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIE.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIE.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. @@ -296,7 +296,7 @@ protected unsafe void SetAttributeValueOpt(DwarfAttributeKind kind, TVal } } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { var abbrev = Abbrev; @@ -310,7 +310,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) foreach (var attr in _attributes) { attr.Position = endOffset; - attr.UpdateLayout(layoutContext); + attr.UpdateLayout(context); endOffset += attr.Size; } @@ -319,7 +319,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) foreach (var child in _children) { child.Position = endOffset; - child.UpdateLayout(layoutContext); + child.UpdateLayout(context); endOffset += child.Size; } diff --git a/src/LibObjectFile/Dwarf/DwarfExpression.cs b/src/LibObjectFile/Dwarf/DwarfExpression.cs index 82377b4..fdcef71 100644 --- a/src/LibObjectFile/Dwarf/DwarfExpression.cs +++ b/src/LibObjectFile/Dwarf/DwarfExpression.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. @@ -94,9 +94,9 @@ internal void UpdateLayout(DwarfLayoutContext layoutContext, bool inLocationSect Size = OperationLengthInBytes + deltaLength; } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { - UpdateLayout(layoutContext, inLocationSection: false); + UpdateLayout(context, inLocationSection: false); } public override void Read(DwarfReader reader) diff --git a/src/LibObjectFile/Dwarf/DwarfFile.cs b/src/LibObjectFile/Dwarf/DwarfFile.cs index 9d0b33b..bf0d71f 100644 --- a/src/LibObjectFile/Dwarf/DwarfFile.cs +++ b/src/LibObjectFile/Dwarf/DwarfFile.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. @@ -448,7 +448,7 @@ private static void CheckErrors(DiagnosticBag diagnostics) } } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { } diff --git a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs index ddde005..2cc0b75 100644 --- a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfInfoSection.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. @@ -61,14 +61,14 @@ public override void Verify(DwarfVerifyContext context) } } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { var offset = Position; foreach (var unit in Units) { - layoutContext.CurrentUnit = unit; + context.CurrentUnit = unit; unit.Position = offset; - unit.UpdateLayout(layoutContext); + unit.UpdateLayout(context); offset += unit.Size; } Size = offset - Position; diff --git a/src/LibObjectFile/Dwarf/DwarfLine.cs b/src/LibObjectFile/Dwarf/DwarfLine.cs index 214720c..f16152f 100644 --- a/src/LibObjectFile/Dwarf/DwarfLine.cs +++ b/src/LibObjectFile/Dwarf/DwarfLine.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. @@ -173,7 +173,7 @@ protected override bool PrintMembers(StringBuilder builder) private static string Bool2Str(bool value) => value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(); - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { } diff --git a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs index 87a8bac..f772f93 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.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. @@ -664,7 +664,7 @@ public override void Verify(DwarfVerifyContext context) } } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { ulong sizeOf = 0; diff --git a/src/LibObjectFile/Dwarf/DwarfLineSection.cs b/src/LibObjectFile/Dwarf/DwarfLineSection.cs index d783249..a81ef42 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSection.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. @@ -41,14 +41,14 @@ public override void Verify(DwarfVerifyContext context) } } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { ulong sizeOf = 0; foreach (var dwarfLineProgramTable in _tables) { dwarfLineProgramTable.Position = Position + sizeOf; - dwarfLineProgramTable.UpdateLayout(layoutContext); + dwarfLineProgramTable.UpdateLayout(context); sizeOf += dwarfLineProgramTable.Size; } Size = sizeOf; diff --git a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs index 1491fd3..361cd9b 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSequence.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. @@ -29,7 +29,7 @@ public void Add(DwarfLine line) _lines.Add(line); } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { // This is implemented in DwarfLineSection } diff --git a/src/LibObjectFile/Dwarf/DwarfLocationList.cs b/src/LibObjectFile/Dwarf/DwarfLocationList.cs index 64c8142..b6c1934 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationList.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationList.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. @@ -19,19 +19,19 @@ public DwarfLocationList() public ObjectList LocationListEntries => _locationListEntries; - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { var endOffset = Position; foreach (var locationListEntry in _locationListEntries) { locationListEntry.Position = endOffset; - locationListEntry.UpdateLayout(layoutContext); + locationListEntry.UpdateLayout(context); endOffset += locationListEntry.Size; } // End of list - endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); + endOffset += 2 * DwarfHelper.SizeOfUInt(context.CurrentUnit!.AddressSize); Size = endOffset - Position; } diff --git a/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs b/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs index 28f8290..561b513 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationListEntry.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. @@ -42,15 +42,15 @@ public override void Read(DwarfReader reader) Expression.ReadInternal(reader, inLocationSection: true); } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { var endOffset = Position; - endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); + endOffset += 2 * DwarfHelper.SizeOfUInt(context.CurrentUnit!.AddressSize); if (Expression != null) { Expression.Position = endOffset; - Expression.UpdateLayout(layoutContext, inLocationSection: true); + Expression.UpdateLayout(context, inLocationSection: true); endOffset += Expression.Size; } diff --git a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs index d7c7a4b..1ca11bc 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationSection.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. @@ -41,14 +41,14 @@ public override void Verify(DwarfVerifyContext context) } } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { ulong sizeOf = 0; foreach (var locationList in _locationLists) { locationList.Position = Position + sizeOf; - locationList.UpdateLayout(layoutContext); + locationList.UpdateLayout(context); sizeOf += locationList.Size; } Size = sizeOf; diff --git a/src/LibObjectFile/Dwarf/DwarfOperation.cs b/src/LibObjectFile/Dwarf/DwarfOperation.cs index a565f31..b1547c2 100644 --- a/src/LibObjectFile/Dwarf/DwarfOperation.cs +++ b/src/LibObjectFile/Dwarf/DwarfOperation.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. @@ -426,7 +426,7 @@ public override void Read(DwarfReader reader) Size = reader.Position - Position; } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { var endOffset = Position; // 1 byte per opcode @@ -435,7 +435,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) switch (Kind.Value) { case DwarfOperationKind.Addr: - endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); + endOffset += DwarfHelper.SizeOfUInt(context.CurrentUnit!.AddressSize); break; case DwarfOperationKind.Const1u: case DwarfOperationKind.Const1s: @@ -618,7 +618,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) break; case DwarfOperationKind.CallRef: - endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit!.AddressSize); + endOffset += DwarfHelper.SizeOfUInt(context.CurrentUnit!.AddressSize); break; case DwarfOperationKind.BitPiece: @@ -629,7 +629,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) case DwarfOperationKind.ImplicitValue: if (Operand0 == null) { - layoutContext.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of implicit value operation {this} from DIE cannot be null."); + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of implicit value operation {this} from DIE cannot be null."); } else if (Operand0 is Stream stream) { @@ -639,7 +639,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) } else { - layoutContext.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of implicit value operation {this} must be a System.IO.Stream."); + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of implicit value operation {this} must be a System.IO.Stream."); } break; @@ -647,13 +647,13 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) case DwarfOperationKind.ImplicitPointer: case DwarfOperationKind.GNUImplicitPointer: // a reference to a debugging information entry that describes the dereferenced object’s value - if (layoutContext.CurrentUnit!.Version == 2) + if (context.CurrentUnit!.Version == 2) { - endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.AddressSize); + endOffset += DwarfHelper.SizeOfUInt(context.CurrentUnit.AddressSize); } else { - endOffset += DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.Is64BitEncoding); + endOffset += DwarfHelper.SizeOfUInt(context.CurrentUnit.Is64BitEncoding); } // a signed number that is treated as a byte offset from the start of that value @@ -669,12 +669,12 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) else if (Operand0 is DwarfExpression expr) { expr.Position = endOffset; - expr.UpdateLayout(layoutContext); + expr.UpdateLayout(context); endOffset += DwarfHelper.SizeOfULEB128(expr.Size); } else { - layoutContext.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of EntryValue operation {this} must be a {nameof(DwarfExpression)} instead of {Operand0.GetType()}."); + context.Diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidData, $"The object operand of EntryValue operation {this} must be a {nameof(DwarfExpression)} instead of {Operand0.GetType()}."); } break; @@ -688,8 +688,8 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) // of a debugging information entry in the current compilation unit, which // must be a DW_TAG_base_type entry that provides the type of the constant provided - endOffset += SizeOfDIEReference(layoutContext); - endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, layoutContext.CurrentUnit!.AddressSize); + endOffset += SizeOfDIEReference(context); + endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, context.CurrentUnit!.AddressSize); break; } @@ -705,7 +705,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) // The second operand is an unsigned LEB128 number that represents the offset // of a debugging information entry in the current compilation unit - endOffset += SizeOfDIEReference(layoutContext); + endOffset += SizeOfDIEReference(context); break; } @@ -722,7 +722,7 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) // The second operand is an unsigned LEB128 number that represents the offset // of a debugging information entry in the current compilation unit - endOffset += SizeOfDIEReference(layoutContext); + endOffset += SizeOfDIEReference(context); break; } @@ -730,11 +730,11 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) case DwarfOperationKind.GNUConvert: case DwarfOperationKind.Reinterpret: case DwarfOperationKind.GNUReinterpret: - endOffset += SizeOfDIEReference(layoutContext); + endOffset += SizeOfDIEReference(context); break; case DwarfOperationKind.GNUEncodedAddr: - endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, layoutContext.CurrentUnit!.AddressSize); + endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, context.CurrentUnit!.AddressSize); break; case DwarfOperationKind.GNUParameterRef: diff --git a/src/LibObjectFile/Dwarf/DwarfStringTable.cs b/src/LibObjectFile/Dwarf/DwarfStringTable.cs index 38fdb55..6a41620 100644 --- a/src/LibObjectFile/Dwarf/DwarfStringTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfStringTable.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. @@ -82,7 +82,7 @@ public override void Write(DwarfWriter writer) writer.Write(Stream); } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { Size = (ulong?)(Stream?.Length) ?? 0UL; } diff --git a/src/LibObjectFile/Dwarf/DwarfUnit.cs b/src/LibObjectFile/Dwarf/DwarfUnit.cs index 1c1717c..102e284 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnit.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. @@ -86,7 +86,7 @@ protected override void ValidateParent(ObjectElement parent) } } - protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) + protected override void UpdateLayoutCore(DwarfLayoutContext context) { var offset = this.Position; @@ -114,14 +114,14 @@ protected override void UpdateLayoutCore(DwarfLayoutContext layoutContext) { // Before updating the layout, we need to compute the abbreviation Abbreviation = new DwarfAbbreviation(); - layoutContext.File.AbbreviationTable.Abbreviations.Add(Abbreviation); + context.File.AbbreviationTable.Abbreviations.Add(Abbreviation); - Root.UpdateAbbreviationItem(layoutContext); + Root.UpdateAbbreviationItem(context); DebugAbbreviationOffset = Abbreviation.Position; Root.Position = offset; - Root.UpdateLayout(layoutContext); + Root.UpdateLayout(context); offset += Root.Size; } diff --git a/src/LibObjectFile/Elf/ElfObjectFile.cs b/src/LibObjectFile/Elf/ElfObjectFile.cs index 3440c94..3105366 100644 --- a/src/LibObjectFile/Elf/ElfObjectFile.cs +++ b/src/LibObjectFile/Elf/ElfObjectFile.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. @@ -792,7 +792,7 @@ private static int CompareStreamIndexAndIndex(ElfSection left, ElfSection right) return left.Index.CompareTo(right.Index); } - protected override void UpdateLayoutCore(ElfVisitorContext layoutContext) + protected override void UpdateLayoutCore(ElfVisitorContext context) { } } \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileElement.cs b/src/LibObjectFile/ObjectFileElement.cs index 32568e8..26b6537 100644 --- a/src/LibObjectFile/ObjectFileElement.cs +++ b/src/LibObjectFile/ObjectFileElement.cs @@ -78,29 +78,56 @@ protected override bool PrintMembers(StringBuilder builder) } } +/// +/// Base class for an object file element. +/// +/// The type used for the layout context. +/// The type used for the verify context. +/// The type used for the reader. +/// The type used for the writer. public abstract class ObjectFileElement : ObjectFileElement where TLayoutContext : VisitorContextBase where TVerifyContext : VisitorContextBase where TReader : ObjectFileReaderWriter where TWriter : ObjectFileReaderWriter { - public virtual void UpdateLayout(TLayoutContext layoutContext) + /// + /// Updates the layout of this element. + /// + /// The layout context. + public virtual void UpdateLayout(TLayoutContext context) { - UpdateLayoutCore(layoutContext); + UpdateLayoutCore(context); } - protected virtual void UpdateLayoutCore(TLayoutContext layoutContext) + /// + /// Updates the layout of this element. + /// + /// The layout context. + protected virtual void UpdateLayoutCore(TLayoutContext context) { } - public virtual void Verify(TVerifyContext diagnostics) + /// + /// Verifies this element. + /// + /// The verify context. + public virtual void Verify(TVerifyContext context) { } + /// + /// Reads this element from the specified reader. + /// + /// The reader to read from. public virtual void Read(TReader reader) { } + /// + /// Writes this element to the specified writer. + /// + /// The writer to write to. public virtual void Write(TWriter writer) { } diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs index 8e161ad..07d3ef8 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs @@ -37,7 +37,7 @@ public PEBaseRelocationBlock() /// public List Relocations { get; } = new(); - protected override unsafe void UpdateLayoutCore(PELayoutContext layoutContext) + protected override unsafe void UpdateLayoutCore(PELayoutContext context) { var count = Relocations.Count; @@ -172,6 +172,11 @@ public override unsafe void Write(PEImageWriter writer) } } + public override void Verify(PEVerifyContext context) + { + context.VerifyObject(SectionLink.Container, this, nameof(SectionLink), false); + } + protected override bool PrintMembers(StringBuilder builder) { if (base.PrintMembers(builder)) diff --git a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs index f37236a..fb7a891 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs @@ -2,6 +2,8 @@ // 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; + namespace LibObjectFile.PE; /// @@ -40,4 +42,19 @@ public override void Read(PEImageReader reader) Content.Add(block); } } + + public override void Verify(PEVerifyContext context) + { + foreach (var block in Content) + { + if (block is not PEBaseRelocationBlock relocationBlock) + { + context.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidBaseRelocationBlock, $"Invalid block found in BaseRelocationDirectory: {block}. Only PEBaseRelocationBlock are allowed."); + } + + block.Verify(context); + } + + base.Verify(context); + } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs index 6954336..f8cd559 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs @@ -175,4 +175,21 @@ public override void Write(PEImageWriter writer) rawEntry = default; writer.Write(rawEntry); } + + public override void Verify(PEVerifyContext context) + { + for (var i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + context.VerifyObject(entry.ModuleName.Container, this, $"the ModuleName in entry #{i}", false); + + for (var j = 0; j < entry.ForwarderRefs.Count; j++) + { + var forwarderRef = entry.ForwarderRefs[j]; + context.VerifyObject(forwarderRef.ModuleName.Container, this, $"the ForwarderRef.ModuleName in entry #{i} ForwarderRef #{j}", false); + } + } + + base.Verify(context); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs index 34f6393..1642c5b 100644 --- a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs +++ b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs @@ -57,6 +57,14 @@ internal void UpdateDirectories(PEFile peFile, DiagnosticBag diagnostics) } } + public override void Verify(PEVerifyContext context) + { + foreach (var data in Content) + { + data.Verify(context); + } + } + protected sealed override void UpdateLayoutCore(PELayoutContext context) { var va = RVA; diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs index 0991435..dc3dd48 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs @@ -174,4 +174,26 @@ protected override unsafe uint ComputeHeaderSize(PELayoutContext context) { return (uint)(Entries.Count * sizeof(RawImageDebugDirectory)); } + + public override void Verify(PEVerifyContext context) + { + for (var i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + if (entry.SectionData is not null) + { + context.VerifyObject(entry.SectionData, this, $"the {nameof(PEDebugDirectoryEntry.SectionData)} of the {nameof(PEDebugDirectoryEntry)} #{i}", false); + } + else if (entry.ExtraData is not null) + { + context.VerifyObject(entry.ExtraData, this, $"the {nameof(PEDebugDirectoryEntry.ExtraData)} of the {nameof(PEDebugDirectoryEntry)} #{i}", false); + } + //else + //{ + // context.Diagnostics.Error(DiagnosticId.PE_ERR_VerifyContextInvalidObject, $"Invalid entry found in the Debug directory at index {i}. Either {nameof(PEDebugDirectoryEntry.SectionData)} or {nameof(PEDebugDirectoryEntry.ExtraData)} must be set"); + //} + } + + base.Verify(context); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs index 5ceb9c0..a91c6de 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs @@ -118,7 +118,7 @@ public override unsafe void Write(PEImageWriter writer) writer.Write(tempSpan.AsBytes); } - protected override void UpdateLayoutCore(PELayoutContext layoutContext) + protected override void UpdateLayoutCore(PELayoutContext context) { Size = CalculateSize(); } diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs index 73d6f92..6542a49 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs @@ -223,4 +223,15 @@ public override unsafe void Write(PEImageWriter writer) writer.Write(tempSpan.AsBytes); } + + public override void Verify(PEVerifyContext context) + { + for (var i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + entry.Verify(context, this, i); + } + + base.Verify(context); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs index 3554843..c72e3ad 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs @@ -27,4 +27,15 @@ public PEDelayImportDirectoryEntry(PEAsciiStringLink dllName, PEModuleHandleLink public PESectionDataLink BoundImportAddressTableLink { get; set; } public PESectionDataLink UnloadDelayInformationTableLink { get; set; } + + internal void Verify(PEVerifyContext context, PEDelayImportDirectory parent, int index) + { + context.VerifyObject(DllName.Container, parent, $"the {nameof(DllName)} of the {nameof(PEDelayImportDirectoryEntry)} at #{index}", false); + context.VerifyObject(ModuleHandle.Container, parent, $"the {nameof(ModuleHandle)} of the {nameof(PEDelayImportDirectoryEntry)} at #{index}", false); + context.VerifyObject(DelayImportAddressTable, parent, $"the {nameof(DelayImportAddressTable)} of the {nameof(PEDelayImportDirectoryEntry)} at #{index}", false); + context.VerifyObject(DelayImportNameTable, parent, $"the {nameof(DelayImportNameTable)} of the {nameof(PEDelayImportDirectoryEntry)} at #{index}", false); + // Allow null + context.VerifyObject(BoundImportAddressTableLink.Container, parent, $"the {nameof(BoundImportAddressTableLink)} of the {nameof(PEDelayImportDirectoryEntry)} at #{index}", true); + context.VerifyObject(UnloadDelayInformationTableLink.Container, parent, $"the {nameof(UnloadDelayInformationTableLink)} of the {nameof(PEDelayImportDirectoryEntry)} at #{index}", true); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs index 8c03b0b..7a7fe83 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs @@ -238,6 +238,18 @@ public override void Write(PEImageWriter writer) } } + public override void Verify(PEVerifyContext context) + { + var entries = CollectionsMarshal.AsSpan(Entries); + for (int i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + entry.Verify(context, this, i); + } + + base.Verify(context); + } + private void WriteX86(PEImageWriter writer) { using var tempSpan = TempSpan.Create(Entries.Count, out var span); diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntry.cs index 3438a57..294e011 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntry.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. @@ -22,4 +22,9 @@ internal PEExceptionFunctionEntry(PESectionDataLink beginAddress) /// Gets or sets the begin address of the exception function entry. /// public PESectionDataLink BeginAddress { get; set; } + + internal virtual void Verify(PEVerifyContext context, PEExceptionDirectory exceptionDirectory, int index) + { + context.VerifyObject(BeginAddress.Container, exceptionDirectory, $"the {nameof(BeginAddress)} of the {nameof(PEExceptionFunctionEntry)} at #{index}", false); + } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryX86.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryX86.cs index fe7a4a3..fffad18 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryX86.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryX86.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. @@ -36,4 +36,12 @@ public override string ToString() { return $"{nameof(BeginAddress)} = {BeginAddress.RVA()}, {nameof(EndAddress)} = {EndAddress.RVA()}, {nameof(UnwindInfoAddress)} = {UnwindInfoAddress.RVA()}"; } + + /// + internal override void Verify(PEVerifyContext context, PEExceptionDirectory exceptionDirectory, int index) + { + base.Verify(context, exceptionDirectory, index); + context.VerifyObject(EndAddress.Container, exceptionDirectory, $"the {nameof(EndAddress)} of the {nameof(PEExceptionFunctionEntryX86)} at #{index}", false); + context.VerifyObject(UnwindInfoAddress.Container, exceptionDirectory, $"the {nameof(UnwindInfoAddress)} of the {nameof(PEExceptionFunctionEntryX86)} at #{index}", false); + } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs index 4121aeb..4643279 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs @@ -167,6 +167,16 @@ protected override unsafe uint ComputeHeaderSize(PELayoutContext context) return (uint)sizeof(RawImageExportDirectory); } + public override void Verify(PEVerifyContext context) + { + context.VerifyObject(NameLink.Container, this, $"the {nameof(NameLink)} of the {nameof(PEExportDirectory)}", false); + context.VerifyObject(ExportFunctionAddressTable, this, $"the {nameof(ExportFunctionAddressTable)} of the {nameof(PEExportDirectory)}", true); + context.VerifyObject(ExportNameTable, this, $"the {nameof(ExportNameTable)} of the {nameof(PEExportDirectory)}", true); + context.VerifyObject(ExportOrdinalTable, this, $"the {nameof(ExportOrdinalTable)} of the {nameof(PEExportDirectory)}", true); + + base.Verify(context); + } + internal override IEnumerable CollectImplicitSectionDataList() { if (ExportFunctionAddressTable is not null) diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs index 250ce32..7a704e0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.cs @@ -34,4 +34,10 @@ public PEExportFunctionEntry(PEAsciiStringLink forwarderRVA) public PEAsciiStringLink ForwarderRVA => IsForwarderRVA ? new(_container as PEStreamSectionData, _offset) : default; public override string ToString() => IsEmpty ? "null" : ForwarderRVA.IsNull() ? $"{ExportRVA}" : $"{ExportRVA}, ForwarderRVA = {ForwarderRVA}"; + + + internal void Verify(PEVerifyContext context, PEExportAddressTable parent, int index) + { + context.VerifyObject(_container, parent, $"the object pointed by the {nameof(PEExportFunctionEntry)} at #{index}", false); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs index af46d6f..a0fc161 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.cs @@ -74,4 +74,13 @@ public override void Write(PEImageWriter writer) writer.Write(tempSpan.AsBytes); } + + public override void Verify(PEVerifyContext context) + { + for (int i = 0; i < Values.Count; i++) + { + var value = Values[i]; + context.VerifyObject(value.Container, this, $"the {nameof(PEAsciiStringLink)} of the {nameof(PEExportNameTable)} #{i}", false); + } + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs index 1e6bbb3..fe3250a 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs @@ -40,5 +40,9 @@ public override void Read(PEImageReader reader) IEnumerator IEnumerable.GetEnumerator() => Entries.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => Entries.GetEnumerator(); - + + public override void Verify(PEVerifyContext context) + { + FunctionTable.Verify(context, this); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs index 8d67558..6fdc4cf 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -176,6 +176,18 @@ internal override void Bind(PEImageReader reader) } } + public override void Verify(PEVerifyContext context) + { + var entries = CollectionsMarshal.AsSpan(_entries); + for (var i = 0; i < entries.Length; i++) + { + var entry = entries[i]; + entry.Verify(context, this, i); + } + + base.Verify(context); + } + private unsafe uint CalculateSize() { return (uint)(((_entries.Count + 1) * sizeof(RawImportDirectoryEntry))); diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs index 825e55d..32c65de 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.cs @@ -32,4 +32,11 @@ public PEImportDirectoryEntry(PEAsciiStringLink importDllNameLink, PEImportAddre /// The index of the first forwarder reference. -1 if no forwarders /// public uint ForwarderChain { get; set; } + + internal void Verify(PEVerifyContext context, PEObject parent, int index) + { + context.VerifyObject(ImportDllNameLink.Container, parent, $"the {nameof(ImportDllNameLink)} for {nameof(PEImportDirectoryEntry)} at index #{index}", false); + context.VerifyObject(ImportAddressTable, parent, $"the {nameof(ImportAddressTable)} for {nameof(PEImportDirectoryEntry)} at index #{index}", false); + context.VerifyObject(ImportLookupTable, parent, $"the {nameof(ImportLookupTable)} for {nameof(PEImportDirectoryEntry)} at index #{index}", false); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs index f20022a..9d8721c 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs @@ -82,4 +82,13 @@ public PEImportFunctionEntry(ushort ordinal) /// /// The PE Import Hint Name to convert. public static implicit operator PEImportFunctionEntry(PEImportHintNameLink name) => new(name); + + + internal void Verify(PEVerifyContext context, PEObject parent, int index) + { + if (IsLongOffset) return; + if (_peSectionData is null) return; + + context.VerifyObject(_peSectionData, parent, $"the {nameof(HintName)} of the {nameof(PEImportFunctionEntry)} at index {index}", false); + } } diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs index af31875..756b323 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -180,4 +180,14 @@ private unsafe void Write64(PEImageWriter writer) writer.Write(MemoryMarshal.AsBytes(span)); } + + public void Verify(PEVerifyContext context, PEObject parent) + { + for (var i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + entry.Verify(context, parent, i); + } + } + } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs index ebb4d22..15f206f 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.cs @@ -43,4 +43,8 @@ public override void Read(PEImageReader reader) IEnumerator IEnumerable.GetEnumerator() => Entries.GetEnumerator(); + public override void Verify(PEVerifyContext context) + { + FunctionTable.Verify(context, this); + } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs index 7fdfe35..3127978 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs @@ -68,11 +68,17 @@ protected override bool PrintMembers(StringBuilder builder) return true; } - protected override unsafe void UpdateLayoutCore(PELayoutContext layoutContext) + protected override unsafe void UpdateLayoutCore(PELayoutContext context) { Size = (uint)sizeof(RawImageResourceDataEntry); } + public override void Verify(PEVerifyContext context) + { + context.VerifyObject(Data, this, $"the {nameof(Data)}", false); + } + + internal override unsafe void Read(in ReaderContext context) { var reader = context.Reader; diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs index c9af390..7e7f7d0 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs @@ -80,6 +80,12 @@ public override void Read(PEImageReader reader) HeaderSize = ComputeHeaderSize(reader); } + public override void Verify(PEVerifyContext context) + { + Root.Verify(context); + base.Verify(context); + } + internal override IEnumerable CollectImplicitSectionDataList() { if (_tempResourceStrings is not null) diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs index 165f1ed..b7414f8 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs @@ -215,11 +215,29 @@ private void ReadEntry(in ReaderContext context, RawImageResourceDirectoryEntry } } - protected override unsafe void UpdateLayoutCore(PELayoutContext layoutContext) + protected override unsafe void UpdateLayoutCore(PELayoutContext context) { Size = CalculateSize(); } + public override void Verify(PEVerifyContext context) + { + for (var i = 0; i < ByNames.Count; i++) + { + var entry = ByNames[i]; + context.VerifyObject(entry.Name, this, $"the {nameof(entry.Name)} in the {nameof(ByNames)} at index #{i}", false); + context.VerifyObject(entry.Entry, this, $"the {nameof(entry.Entry)} in the {nameof(ByNames)} at index #{i}", false); + } + + for (var i = 0; i < ByIds.Count; i++) + { + var entry = ByIds[i]; + context.VerifyObject(entry.Entry, this, $"the {nameof(entry.Entry)} in the {nameof(ByIds)} at index #{i}", false); + } + + base.Verify(context); + } + private unsafe uint CalculateSize() { var size = 0U; diff --git a/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs index 3524650..f756821 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs @@ -66,7 +66,7 @@ public override void Write(PEImageWriter writer) writer.Write(MemoryMarshal.Cast(Text.AsSpan())); } - protected override void UpdateLayoutCore(PELayoutContext layoutContext) + protected override void UpdateLayoutCore(PELayoutContext context) { Size = CalculateSize(); } diff --git a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs index b84cdf4..da171d9 100644 --- a/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs +++ b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs @@ -104,7 +104,7 @@ private unsafe uint CalculateSize() return size; } - protected override void UpdateLayoutCore(PELayoutContext layoutContext) + protected override void UpdateLayoutCore(PELayoutContext context) { Size = CalculateSize(); } diff --git a/src/LibObjectFile/PE/PEFile.Layout.cs b/src/LibObjectFile/PE/PEFile.Layout.cs new file mode 100644 index 0000000..ce7a40f --- /dev/null +++ b/src/LibObjectFile/PE/PEFile.Layout.cs @@ -0,0 +1,186 @@ +// 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.Numerics; +using System.Reflection.PortableExecutable; +using LibObjectFile.Diagnostics; +using LibObjectFile.PE.Internal; +using LibObjectFile.Utils; + +namespace LibObjectFile.PE; + +/// +/// A Portable Executable file that can be read, modified and written. +/// +partial class PEFile +{ + /// + /// Updates the layout of this PE file. + /// + /// The diagnostics to output errors. + public void UpdateLayout(DiagnosticBag diagnostics) + { + var context = new PELayoutContext(this, diagnostics); + UpdateLayout(context); + } + + private bool TryVerifyAlignment(DiagnosticBag diagnostics) + { + if (!BitOperations.IsPow2(OptionalHeader.FileAlignment) || OptionalHeader.FileAlignment == 0) + { + diagnostics.Error(DiagnosticId.PE_ERR_FileAlignmentNotPowerOfTwo, $"File alignment {OptionalHeader.FileAlignment} is not a power of two"); + return false; + } + + if (!BitOperations.IsPow2(OptionalHeader.SectionAlignment) || OptionalHeader.SectionAlignment == 0) + { + diagnostics.Error(DiagnosticId.PE_ERR_SectionAlignmentNotPowerOfTwo, $"Section alignment {OptionalHeader.SectionAlignment} is not a power of two"); + return false; + } + + // Ensure that SectionAlignment is greater or equal to FileAlignment + if (OptionalHeader.SectionAlignment < OptionalHeader.FileAlignment) + { + diagnostics.Error(DiagnosticId.PE_ERR_SectionAlignmentLessThanFileAlignment, $"Section alignment {OptionalHeader.SectionAlignment} is less than file alignment {OptionalHeader.FileAlignment}"); + return false; + } + + return true; + } + + /// + protected override unsafe void UpdateLayoutCore(PELayoutContext context) + { + if (!TryVerifyAlignment(context.Diagnostics)) + { + return; + } + + // Update the content of Directories + UpdateDirectories(context.Diagnostics); + + var position = 0U; + + // Update DOS header + position += (uint)sizeof(PEDosHeader); + position += (uint)_dosStub.Length; + position += (uint)(_dosStubExtra?.Length ?? 0U); + + // Update optional header + position = AlignHelper.AlignUp(position, 8); // PE header is aligned on 8 bytes + + // Update offset to PE header + DosHeader._FileAddressPEHeader = position; + + position += sizeof(PESignature); // PE00 header + + // COFF header + position += (uint)sizeof(PECoffHeader); + + // TODO: update other DosHeader fields + + var startPositionHeader = position; + + position += (uint)(IsPE32 ? RawImageOptionalHeader32.MinimumSize : RawImageOptionalHeader64.MinimumSize); + + // Update directories + position += (uint)(Directories.Count * sizeof(RawImageDataDirectory)); + // Update the internal header + OptionalHeader.OptionalHeaderCommonPart3.NumberOfRvaAndSizes = (uint)Directories.Count; + + CoffHeader._SizeOfOptionalHeader = (ushort)(position - startPositionHeader); + + position += (uint)(sizeof(RawImageSectionHeader) * _sections.Count); + + // TODO: Additional optional header size? + + // Data before sections + foreach (var extraData in ExtraDataBeforeSections) + { + extraData.Position = position; + extraData.UpdateLayout(context); + var dataSize = (uint)extraData.Size; + position += dataSize; + } + + if (_sections.Count > 96) + { + context.Diagnostics.Error(DiagnosticId.PE_ERR_TooManySections, $"Too many sections {_sections.Count} (max 96)"); + } + + // Update COFF header + CoffHeader._NumberOfSections = (ushort)_sections.Count; + CoffHeader._PointerToSymbolTable = 0; + CoffHeader._NumberOfSymbols = 0; + + OptionalHeader.OptionalHeaderCommonPart1.SizeOfCode = 0; + OptionalHeader.OptionalHeaderCommonPart1.SizeOfInitializedData = 0; + OptionalHeader.OptionalHeaderCommonPart1.SizeOfUninitializedData = 0; + + // Ensure that SectionAlignment is a multiple of FileAlignment + position = AlignHelper.AlignUp(position, OptionalHeader.FileAlignment); + OptionalHeader.OptionalHeaderCommonPart2.SizeOfHeaders = position; + + // Update sections + RVA previousEndOfRVA = 0U; + foreach (var section in _sections) + { + section.Position = position; + section.UpdateLayout(context); + if (section.Size == 0) + { + section.Position = 0; + } + + if (section.RVA < previousEndOfRVA) + { + context.Diagnostics.Error(DiagnosticId.PE_ERR_SectionRVALessThanPrevious, $"Section {section.Name} RVA {section.RVA} is less than the previous section end RVA {previousEndOfRVA}"); + } + + var sectionSize = (uint)section.Size; + position += sectionSize; + + var minDataSize = AlignHelper.AlignUp((uint)section.VirtualSize, OptionalHeader.FileAlignment); + //minDataSize = section.Size > minDataSize ? (uint)section.Size : minDataSize; + //var minDataSize = (uint)section.Size; + + if ((section.Characteristics & SectionCharacteristics.ContainsCode) != 0) + { + OptionalHeader.OptionalHeaderCommonPart1.SizeOfCode += minDataSize; + } + else if ((section.Characteristics & SectionCharacteristics.ContainsInitializedData) != 0) + { + OptionalHeader.OptionalHeaderCommonPart1.SizeOfInitializedData += minDataSize; + } + else if ((section.Characteristics & SectionCharacteristics.ContainsUninitializedData) != 0) + { + OptionalHeader.OptionalHeaderCommonPart1.SizeOfUninitializedData += minDataSize; + } + + // Update the end of the RVA + previousEndOfRVA = section.RVA + AlignHelper.AlignUp(section.VirtualSize, OptionalHeader.SectionAlignment); + } + + // Used by tests to force the size of initialized data that seems to be calculated differently from the standard way by some linkers + if (ForceSizeOfInitializedData != 0) + { + OptionalHeader.OptionalHeaderCommonPart1.SizeOfInitializedData = ForceSizeOfInitializedData; + } + + // Update the (virtual) size of the image + OptionalHeader.OptionalHeaderCommonPart2.SizeOfImage = previousEndOfRVA; + + // Data after sections + foreach (var extraData in ExtraDataAfterSections) + { + extraData.Position = position; + extraData.UpdateLayout(context); + var dataSize = (uint)extraData.Size; + position += dataSize; + } + + // Update the size of the file + Size = position; + } +} diff --git a/src/LibObjectFile/PE/PEFile.Verify.cs b/src/LibObjectFile/PE/PEFile.Verify.cs new file mode 100644 index 0000000..79d81fe --- /dev/null +++ b/src/LibObjectFile/PE/PEFile.Verify.cs @@ -0,0 +1,51 @@ +// 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.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// A Portable Executable file that can be read, modified and written. +/// +partial class PEFile +{ + /// + /// Verifies the validity of this PE file. + /// + /// The diagnostics to output errors. + public void Verify(DiagnosticBag diagnostics) + { + ArgumentNullException.ThrowIfNull(diagnostics); + var context = new PEVerifyContext(this, diagnostics); + Verify(context); + } + + /// + public override void Verify(PEVerifyContext context) + { + ArgumentNullException.ThrowIfNull(context); + + if (!TryVerifyAlignment(context.Diagnostics)) + { + return; + } + + foreach (var data in ExtraDataBeforeSections) + { + data.Verify(context); + } + + foreach (var section in Sections) + { + section.Verify(context); + } + + foreach (var data in ExtraDataAfterSections) + { + data.Verify(context); + } + } +} diff --git a/src/LibObjectFile/PE/PEFile.Write.cs b/src/LibObjectFile/PE/PEFile.Write.cs index ad70052..6c8f8d1 100644 --- a/src/LibObjectFile/PE/PEFile.Write.cs +++ b/src/LibObjectFile/PE/PEFile.Write.cs @@ -19,9 +19,9 @@ partial class PEFile /// Writes this PE file to the specified stream. /// /// The stream to write to. - public void Write(Stream stream) + public void Write(Stream stream, PEImageWriterOptions? options = null) { - if (!TryWrite(stream, out var diagnostics)) + if (!TryWrite(stream, out var diagnostics, options)) { throw new ObjectFileException($"Invalid PE File", diagnostics); } @@ -33,27 +33,34 @@ public void Write(Stream stream) /// The stream to write to. /// The output diagnostics /// true if writing was successful. otherwise false - public bool TryWrite(Stream stream, out DiagnosticBag diagnostics) + public bool TryWrite(Stream stream, out DiagnosticBag diagnostics, PEImageWriterOptions? options = null) { if (stream == null) throw new ArgumentNullException(nameof(stream)); var peWriter = new PEImageWriter(this, stream); diagnostics = peWriter.Diagnostics; - var context = new PELayoutContext(this, diagnostics); + if (options is not null) + { + diagnostics.EnableStackTrace = options.EnableStackTrace; + } - Verify(context); + // Verify the coherence of the PE file + Verify(diagnostics); if (diagnostics.HasErrors) { return false; } + // Layout the PE file + var context = new PELayoutContext(this, diagnostics); UpdateLayout(context); if (diagnostics.HasErrors) { return false; } + // Write the PE file Write(peWriter); return !diagnostics.HasErrors; diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs index 946a17a..9ba9f9b 100644 --- a/src/LibObjectFile/PE/PEFile.cs +++ b/src/LibObjectFile/PE/PEFile.cs @@ -325,166 +325,6 @@ public void UpdateDirectories(DiagnosticBag diagnostics) } } } - - /// - /// Updates the layout of this PE file. - /// - /// The diagnostics to output errors. - public void UpdateLayout(DiagnosticBag diagnostics) - { - var context = new PELayoutContext(this, diagnostics); - UpdateLayout(context); - } - - /// - protected override unsafe void UpdateLayoutCore(PELayoutContext context) - { - // Update the content of Directories - UpdateDirectories(context.Diagnostics); - - var position = 0U; - - // Update DOS header - position += (uint)sizeof(PEDosHeader); - position += (uint)_dosStub.Length; - position += (uint)(_dosStubExtra?.Length ?? 0U); - - // Update optional header - position = AlignHelper.AlignUp(position, 8); // PE header is aligned on 8 bytes - - // Update offset to PE header - DosHeader._FileAddressPEHeader = position; - - position += sizeof(PESignature); // PE00 header - - // COFF header - position += (uint)sizeof(PECoffHeader); - - // TODO: update other DosHeader fields - - var startPositionHeader = position; - - position += (uint)(IsPE32 ? RawImageOptionalHeader32.MinimumSize : RawImageOptionalHeader64.MinimumSize); - - // Update directories - position += (uint)(Directories.Count * sizeof(RawImageDataDirectory)); - // Update the internal header - OptionalHeader.OptionalHeaderCommonPart3.NumberOfRvaAndSizes = (uint)Directories.Count; - - CoffHeader._SizeOfOptionalHeader = (ushort)(position - startPositionHeader); - - position += (uint)(sizeof(RawImageSectionHeader) * _sections.Count); - - // TODO: Additional optional header size? - - // Data before sections - foreach (var extraData in ExtraDataBeforeSections) - { - extraData.Position = position; - extraData.UpdateLayout(context); - var dataSize = (uint)extraData.Size; - position += dataSize; - } - - if (_sections.Count > 96) - { - context.Diagnostics.Error(DiagnosticId.PE_ERR_TooManySections, $"Too many sections {_sections.Count} (max 96)"); - } - - // Update COFF header - CoffHeader._NumberOfSections = (ushort)_sections.Count; - CoffHeader._PointerToSymbolTable = 0; - CoffHeader._NumberOfSymbols = 0; - - OptionalHeader.OptionalHeaderCommonPart1.SizeOfCode = 0; - OptionalHeader.OptionalHeaderCommonPart1.SizeOfInitializedData = 0; - OptionalHeader.OptionalHeaderCommonPart1.SizeOfUninitializedData = 0; - - if (!BitOperations.IsPow2(OptionalHeader.FileAlignment) || OptionalHeader.FileAlignment == 0) - { - context.Diagnostics.Error(DiagnosticId.PE_ERR_FileAlignmentNotPowerOfTwo, $"File alignment {OptionalHeader.FileAlignment} is not a power of two"); - return; - } - - if (!BitOperations.IsPow2(OptionalHeader.SectionAlignment) || OptionalHeader.SectionAlignment == 0) - { - context.Diagnostics.Error(DiagnosticId.PE_ERR_SectionAlignmentNotPowerOfTwo, $"Section alignment {OptionalHeader.SectionAlignment} is not a power of two"); - return; - } - - // Ensure that SectionAlignment is greater or equal to FileAlignment - if (OptionalHeader.SectionAlignment < OptionalHeader.FileAlignment) - { - context.Diagnostics.Error(DiagnosticId.PE_ERR_SectionAlignmentLessThanFileAlignment, $"Section alignment {OptionalHeader.SectionAlignment} is less than file alignment {OptionalHeader.FileAlignment}"); - return; - - } - - // Ensure that SectionAlignment is a multiple of FileAlignment - position = AlignHelper.AlignUp(position, OptionalHeader.FileAlignment); - OptionalHeader.OptionalHeaderCommonPart2.SizeOfHeaders = position; - - // Update sections - RVA previousEndOfRVA = 0U; - foreach (var section in _sections) - { - section.Position = position; - section.UpdateLayout(context); - if (section.Size == 0) - { - section.Position = 0; - } - - if (section.RVA < previousEndOfRVA) - { - context.Diagnostics.Error(DiagnosticId.PE_ERR_SectionRVALessThanPrevious, $"Section {section.Name} RVA {section.RVA} is less than the previous section end RVA {previousEndOfRVA}"); - } - - var sectionSize = (uint)section.Size; - position += sectionSize; - - var minDataSize = AlignHelper.AlignUp((uint)section.VirtualSize, OptionalHeader.FileAlignment); - //minDataSize = section.Size > minDataSize ? (uint)section.Size : minDataSize; - //var minDataSize = (uint)section.Size; - - if ((section.Characteristics & SectionCharacteristics.ContainsCode) != 0) - { - OptionalHeader.OptionalHeaderCommonPart1.SizeOfCode += minDataSize; - } - else if ((section.Characteristics & SectionCharacteristics.ContainsInitializedData) != 0) - { - OptionalHeader.OptionalHeaderCommonPart1.SizeOfInitializedData += minDataSize; - } - else if ((section.Characteristics & SectionCharacteristics.ContainsUninitializedData) != 0) - { - OptionalHeader.OptionalHeaderCommonPart1.SizeOfUninitializedData += minDataSize; - } - - // Update the end of the RVA - previousEndOfRVA = section.RVA + AlignHelper.AlignUp(section.VirtualSize, OptionalHeader.SectionAlignment); - } - - // Used by tests to force the size of initialized data that seems to be calculated differently from the standard way by some linkers - if (ForceSizeOfInitializedData != 0) - { - OptionalHeader.OptionalHeaderCommonPart1.SizeOfInitializedData = ForceSizeOfInitializedData; - } - - // Update the (virtual) size of the image - OptionalHeader.OptionalHeaderCommonPart2.SizeOfImage = previousEndOfRVA; - - // Data after sections - foreach (var extraData in ExtraDataAfterSections) - { - extraData.Position = position; - extraData.UpdateLayout(context); - var dataSize = (uint)extraData.Size; - position += dataSize; - } - - // Update the size of the file - Size = position; - } protected override bool PrintMembers(StringBuilder builder) { diff --git a/src/LibObjectFile/PE/PEImageReader.cs b/src/LibObjectFile/PE/PEImageReader.cs index 7d46f54..5ea4a77 100644 --- a/src/LibObjectFile/PE/PEImageReader.cs +++ b/src/LibObjectFile/PE/PEImageReader.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 @@ internal PEImageReader(PEFile file, Stream stream, PEImageReaderOptions readerOp public PEImageReaderOptions Options { get; } - public override bool KeepOriginalStreamForSubStreams => Options.IsReadOnly; + public override bool KeepOriginalStreamForSubStreams => Options.UseSubStream; public PELayoutContext LayoutContext { get; } diff --git a/src/LibObjectFile/PE/PEImageReaderOptions.cs b/src/LibObjectFile/PE/PEImageReaderOptions.cs index 3022b10..5441c4d 100644 --- a/src/LibObjectFile/PE/PEImageReaderOptions.cs +++ b/src/LibObjectFile/PE/PEImageReaderOptions.cs @@ -6,7 +6,12 @@ namespace LibObjectFile.PE; public class PEImageReaderOptions { - public bool IsReadOnly { get; init; } + public bool UseSubStream { get; init; } + public bool EnableStackTrace { get; init; } +} + +public class PEImageWriterOptions +{ public bool EnableStackTrace { get; init; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEObject.cs b/src/LibObjectFile/PE/PEObject.cs index 4e53273..a975b8d 100644 --- a/src/LibObjectFile/PE/PEObject.cs +++ b/src/LibObjectFile/PE/PEObject.cs @@ -124,14 +124,14 @@ private protected void ResetCustomVirtualSize() _useCustomVirtualSize = false; } - public sealed override void UpdateLayout(PELayoutContext layoutContext) + public sealed override void UpdateLayout(PELayoutContext context) { - var section = layoutContext.CurrentSection; + var section = context.CurrentSection; if (section is null) { // Update the layout normally - base.UpdateLayout(layoutContext); + base.UpdateLayout(context); } else { @@ -144,7 +144,7 @@ public sealed override void UpdateLayout(PELayoutContext layoutContext) var currentSectionVirtualSize = this.RVA - section.RVA; // Update the layout normally - base.UpdateLayout(layoutContext); + base.UpdateLayout(context); // Accumulate the virtual size of the section currentSectionVirtualSize += (uint)Size; diff --git a/src/LibObjectFile/PE/PEObjectBase.cs b/src/LibObjectFile/PE/PEObjectBase.cs index dcd0abd..3f215b3 100644 --- a/src/LibObjectFile/PE/PEObjectBase.cs +++ b/src/LibObjectFile/PE/PEObjectBase.cs @@ -12,7 +12,7 @@ namespace LibObjectFile.PE; /// Base class for all Portable Executable (PE) objects. /// [DebuggerDisplay("{ToString(),nq}")] -public abstract class PEObjectBase : ObjectFileElement +public abstract class PEObjectBase : ObjectFileElement { /// /// Gets the PE file containing this object. diff --git a/src/LibObjectFile/PE/PESection.cs b/src/LibObjectFile/PE/PESection.cs index ec1d723..9d95e4c 100644 --- a/src/LibObjectFile/PE/PESection.cs +++ b/src/LibObjectFile/PE/PESection.cs @@ -8,6 +8,7 @@ using System.Reflection.PortableExecutable; using System.Text; using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; using LibObjectFile.Utils; namespace LibObjectFile.PE; @@ -163,6 +164,14 @@ protected override void UpdateLayoutCore(PELayoutContext context) } } + public override void Verify(PEVerifyContext context) + { + foreach (var data in Content) + { + data.Verify(context); + } + } + public override void Read(PEImageReader reader) { throw new NotImplementedException(); @@ -174,8 +183,6 @@ public override void Write(PEImageWriter writer) throw new NotImplementedException(); } - - /// protected override bool PrintMembers(StringBuilder builder) { diff --git a/src/LibObjectFile/PE/PEStreamExtraData.cs b/src/LibObjectFile/PE/PEStreamExtraData.cs index 8a25ca0..193111a 100644 --- a/src/LibObjectFile/PE/PEStreamExtraData.cs +++ b/src/LibObjectFile/PE/PEStreamExtraData.cs @@ -47,7 +47,7 @@ public Stream Stream } } - protected override void UpdateLayoutCore(PELayoutContext layoutContext) + protected override void UpdateLayoutCore(PELayoutContext context) { Size = (uint)Stream.Length; } diff --git a/src/LibObjectFile/PE/PEStreamSectionData.cs b/src/LibObjectFile/PE/PEStreamSectionData.cs index dd0ee83..8f77966 100644 --- a/src/LibObjectFile/PE/PEStreamSectionData.cs +++ b/src/LibObjectFile/PE/PEStreamSectionData.cs @@ -80,7 +80,7 @@ public uint RequiredSizeAlignment } } - protected override void UpdateLayoutCore(PELayoutContext layoutContext) + protected override void UpdateLayoutCore(PELayoutContext context) { Size = (ulong)Stream.Length; } diff --git a/src/LibObjectFile/PE/PEVisitorContext.cs b/src/LibObjectFile/PE/PEVisitorContext.cs index d3254fb..8180cd7 100644 --- a/src/LibObjectFile/PE/PEVisitorContext.cs +++ b/src/LibObjectFile/PE/PEVisitorContext.cs @@ -23,4 +23,33 @@ internal PELayoutContext(PEFile peFile, DiagnosticBag diagnostics, bool updateSi public bool UpdateSizeOnly { get; } internal PESection? CurrentSection { get; set; } +} + + +public sealed class PEVerifyContext : PEVisitorContext +{ + internal PEVerifyContext(PEFile peFile, DiagnosticBag diagnostics) : base(peFile, diagnostics) + { + } + + public void VerifyObject(PEObjectBase? peObject, PEObjectBase currentObject, string objectKindText, bool allowNull) + { + if (peObject is null) + { + if (allowNull) return; + + Diagnostics.Error(DiagnosticId.PE_ERR_VerifyContextInvalidObject, $"Error while processing {currentObject}. The parent object for {objectKindText} is null."); + return; + } + + var peFile = peObject.GetPEFile(); + if (peFile is null) + { + Diagnostics.Error(DiagnosticId.PE_ERR_VerifyContextInvalidObject, $"Error while processing {currentObject}. The parent of the object {peObject} from {objectKindText} is null. This object is not attached to the PE file."); + } + else if (peFile != File) + { + Diagnostics.Error(DiagnosticId.PE_ERR_VerifyContextInvalidObject, $"Error while processing {currentObject}. The parent object {peObject} for {objectKindText} is invalid. The object is attached to another PE File than the current PE file being processed."); + } + } } \ No newline at end of file From 1ee82999e7d4e63c943f299bb65dfeb9bbf96af6 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 1 Oct 2024 20:53:29 +0200 Subject: [PATCH 84/87] Add PE checksum --- .../PE/NativeConsole2Win64.exe | Bin 16896 -> 16896 bytes src/LibObjectFile.Tests/PE/PEReaderTests.cs | 29 +++++-- ..._name=NativeConsole2Win64.exe.verified.txt | 14 ++-- src/LibObjectFile/Diagnostics/DiagnosticId.cs | 1 + .../PE/DataDirectory/PEDebugDirectoryEntry.cs | 2 +- src/LibObjectFile/PE/PEFile.Write.cs | 71 +++++++++++++++++- src/LibObjectFile/PE/PEImageReaderOptions.cs | 5 -- src/LibObjectFile/PE/PEImageWriter.cs | 5 +- src/LibObjectFile/PE/PEImageWriterOptions.cs | 14 ++++ .../NativeConsole2Win64.vcxproj | 4 + 10 files changed, 119 insertions(+), 26 deletions(-) create mode 100644 src/LibObjectFile/PE/PEImageWriterOptions.cs diff --git a/src/LibObjectFile.Tests/PE/NativeConsole2Win64.exe b/src/LibObjectFile.Tests/PE/NativeConsole2Win64.exe index 237ba87985ee97eebba8cd866c87468f3e210176..b41296777d5c9c3b18866f979beb0ce3ab6a5f2b 100644 GIT binary patch delta 103 zcmZo@VQgq&T)@aYN9WIEW=3D8@S4p*jFS|tf&4TEAYcOGA|RG917a1B2m=F5jt9sV d0pcE@Tn|ESvb?e+*gR|HkBsb_byY6Q0sza>7h3=T delta 103 zcmZo@VQgq&T)@bzKlkHgW=3Bo28PW+jFS|tf&4TEAOH$56ale>84#<0L>L%gay&q` d2oU!G<$4fuljW5q!RA>je`I9atgCWa765#a7GwYb diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index 5e10fa8..44f5d8b 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -56,7 +56,11 @@ public async Task TestPrinter(string name) // Write the PE back to a byte buffer var output = new MemoryStream(); - peImage.Write(output, new PEImageWriterOptions() { EnableStackTrace = true }); + peImage.Write(output, new PEImageWriterOptions() + { + EnableStackTrace = true, + EnableChecksum = peImage.OptionalHeader.CheckSum != 0 // Recalculate the checksum if it was present + }); output.Position = 0; byte[] outputBuffer = output.ToArray(); @@ -74,13 +78,13 @@ public async Task TestPrinter(string name) public void TestCreatePE() { var pe = new PEFile(); - + // *************************************************************************** // Code section // *************************************************************************** var codeSection = pe.AddSection(PESectionName.Text, 0x1000); var streamCode = new PEStreamSectionData(); - + streamCode.Stream.Write([ // SUB RSP, 0x28 0x48, 0x83, 0xEC, 0x28, @@ -93,7 +97,7 @@ public void TestCreatePE() ]); codeSection.Content.Add(streamCode); - + // *************************************************************************** // Data section // *************************************************************************** @@ -112,7 +116,7 @@ public void TestCreatePE() { peImportAddressTable }; - + var peImportLookupTable = new PEImportLookupTable() { exitProcessFunction @@ -145,10 +149,15 @@ public void TestCreatePE() pe.Write(output, new() { EnableStackTrace = true }); output.Position = 0; - var sourceFile = Path.Combine(AppContext.BaseDirectory, "PE", "generated_win64.exe"); + var sourceFile = Path.Combine(AppContext.BaseDirectory, "PE", "RawNativeConsoleWin64_Generated.exe"); File.WriteAllBytes(sourceFile, output.ToArray()); - } + // Check the generated exe + var process = Process.Start(sourceFile); + process.WaitForExit(); + Assert.AreEqual(156, process.ExitCode); + } + [DataTestMethod] [DynamicData(nameof(GetWindowsExeAndDlls), DynamicDataSourceType.Method)] public async Task TestWindows(string sourceFile) @@ -191,7 +200,11 @@ public async Task TestWindows(string sourceFile) // Write the PE back to a byte buffer var output = new MemoryStream(); - peImage.Write(output, new PEImageWriterOptions() { EnableStackTrace = true }); + peImage.Write(output, new PEImageWriterOptions() + { + EnableStackTrace = true, + //EnableChecksum = peImage.OptionalHeader.CheckSum != 0 // Recalculate the checksum if it was present, we cannot enable it because some DLLs have an invalid checksum + }); output.Position = 0; var outputBuffer = output.ToArray(); 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 eb2711c..ed6b33c 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 @@ -25,7 +25,7 @@ DOS Stub COFF Header Machine = Amd64 NumberOfSections = 6 - TimeDateStamp = 1727110447 + TimeDateStamp = 1727802524 PointerToSymbolTable = 0x0 NumberOfSymbols = 0 SizeOfOptionalHeader = 240 @@ -53,7 +53,7 @@ Optional Header Win32VersionValue = 0x0 SizeOfImage = 0x9000 SizeOfHeaders = 0x400 - CheckSum = 0x0 + CheckSum = 0x7C57 Subsystem = WindowsCui DllCharacteristics = HighEntropyVirtualAddressSpace, DynamicBase, NxCompatible, TerminalServerAware SizeOfStackReserve = 0x100000 @@ -243,17 +243,17 @@ Sections [03] PEStreamSectionData Position = 0x000022D0, Size = 0x00000010, RVA = 0x000034D0, VirtualSize = 0x00000010 [04] PEDebugDirectory Position = 0x000022E0, Size = 0x00000070, RVA = 0x000034E0, VirtualSize = 0x00000070 - [0] Type = CodeView, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x00003618 (PEDebugSectionDataRSDS[6] -> .rdata) - [1] Type = VCFeature, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x0000368C (PEDebugStreamSectionData[8] -> .rdata) - [2] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = RVA = 0x000036A0 (PEDebugStreamSectionData[9] -> .rdata) - [3] Type = ILTCG, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66F19D2F, Data = null + [0] Type = CodeView, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66FC2C9C, Data = RVA = 0x00003618 (PEDebugSectionDataRSDS[6] -> .rdata) + [1] Type = VCFeature, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66FC2C9C, Data = RVA = 0x0000368C (PEDebugStreamSectionData[8] -> .rdata) + [2] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66FC2C9C, Data = RVA = 0x000036A0 (PEDebugStreamSectionData[9] -> .rdata) + [3] Type = ILTCG, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66FC2C9C, Data = null [05] PEStreamSectionData Position = 0x00002350, Size = 0x000000C8, RVA = 0x00003550, VirtualSize = 0x000000C8 [06] PEDebugSectionDataRSDS Position = 0x00002418, Size = 0x00000072, RVA = 0x00003618, VirtualSize = 0x00000072 Debug Section Data (RSDS) Guid = ffed6f99-5708-452c-a889-ff343b6ce898 - Age = 6 + Age = 7 PdbPath = C:\code\LibObjectFile\src\native\Win64\NativeProjects\x64\Release\NativeConsole2Win64.pdb [07] PEStreamSectionData Position = 0x0000248A, Size = 0x00000002, RVA = 0x0000368A, VirtualSize = 0x00000002 diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs index f672e0b..3690353 100644 --- a/src/LibObjectFile/Diagnostics/DiagnosticId.cs +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.cs @@ -137,6 +137,7 @@ public enum DiagnosticId PE_ERR_InvalidAddressOfEntryPoint = 3019, PE_ERR_DirectoryWithSameKindAlreadyAdded = 3020, PE_ERR_VerifyContextInvalidObject = 3021, + PE_ERR_ChecksumNotSupported = 3022, // PE Exception directory PE_ERR_InvalidExceptionDirectory_Entries = 3100, diff --git a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs index 1373d77..aad3b5e 100644 --- a/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.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.Write.cs b/src/LibObjectFile/PE/PEFile.Write.cs index 6c8f8d1..6cc4ba3 100644 --- a/src/LibObjectFile/PE/PEFile.Write.cs +++ b/src/LibObjectFile/PE/PEFile.Write.cs @@ -5,11 +5,13 @@ using System; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using LibObjectFile.Diagnostics; using LibObjectFile.PE.Internal; using LibObjectFile.Utils; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace LibObjectFile.PE; @@ -37,12 +39,15 @@ public bool TryWrite(Stream stream, out DiagnosticBag diagnostics, PEImageWriter { if (stream == null) throw new ArgumentNullException(nameof(stream)); - var peWriter = new PEImageWriter(this, stream); + var peWriter = new PEImageWriter(this, stream, options ?? PEImageWriterOptions.Default); diagnostics = peWriter.Diagnostics; - if (options is not null) + diagnostics.EnableStackTrace = peWriter.Options.EnableStackTrace; + + if (peWriter.Options.EnableChecksum && stream is not MemoryStream) { - diagnostics.EnableStackTrace = options.EnableStackTrace; + diagnostics.Error(DiagnosticId.PE_ERR_ChecksumNotSupported, "Checksum is only supported for MemoryStream"); + return false; } // Verify the coherence of the PE file @@ -105,6 +110,8 @@ public override unsafe void Write(PEImageWriter writer) // Update OptionalHeader OptionalHeader.OptionalHeaderCommonPart1.AddressOfEntryPoint = OptionalHeader.AddressOfEntryPoint.RVA(); OptionalHeader.OptionalHeaderCommonPart1.BaseOfCode = OptionalHeader.BaseOfCode?.RVA ?? 0; + + var optionalHeaderPosition = position; if (IsPE32) { @@ -244,5 +251,61 @@ public override unsafe void Write(PEImageWriter writer) { writer.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Generated size {position} does not match expecting size {Size}"); } + + if (writer.Options.EnableChecksum) + { + CalculateAndApplyChecksum((MemoryStream)writer.Stream, optionalHeaderPosition); + } + } + + private void CalculateAndApplyChecksum(MemoryStream stream, uint optionalHeaderPosition) + { + var data = stream.GetBuffer(); + var length = (int)stream.Length; + var buffer = new Span(data, 0, length); + + // Zero the checksum before calculating it + MemoryMarshal.Write(buffer.Slice((int)optionalHeaderPosition + 64), 0); + + var checksum = CalculateChecksum(buffer); + + // Update the checksum in the PE header + MemoryMarshal.Write(buffer.Slice((int)optionalHeaderPosition + 64), checksum); + } + + private static uint CalculateChecksum(Span peFile) + { + ulong checksum = 0; + + var shortBuffer = MemoryMarshal.Cast(peFile); + foreach (var value in shortBuffer) + { + checksum = AggregateChecksum(checksum, value); + } + + if ((peFile.Length & 1) != 0) + { + checksum = AggregateChecksum(checksum, peFile[peFile.Length - 1]); + } + + checksum = ((ushort)checksum) + (checksum >> 16); + checksum += checksum >> 16; + checksum &= 0xffff; + checksum += (ulong)peFile.Length; + + return (uint)checksum; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ulong AggregateChecksum(ulong checksum, ushort value) + { + checksum += value; + checksum = (uint)checksum + (checksum >> 32); + if (checksum > uint.MaxValue) + { + checksum = unchecked((uint)checksum) + (checksum >> 32); + } + + return checksum; + } } -} \ No newline at end of file +} diff --git a/src/LibObjectFile/PE/PEImageReaderOptions.cs b/src/LibObjectFile/PE/PEImageReaderOptions.cs index 5441c4d..e5a76f9 100644 --- a/src/LibObjectFile/PE/PEImageReaderOptions.cs +++ b/src/LibObjectFile/PE/PEImageReaderOptions.cs @@ -8,10 +8,5 @@ public class PEImageReaderOptions { public bool UseSubStream { get; init; } - public bool EnableStackTrace { get; init; } -} - -public class PEImageWriterOptions -{ public bool EnableStackTrace { get; init; } } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEImageWriter.cs b/src/LibObjectFile/PE/PEImageWriter.cs index 676bc1d..bd95031 100644 --- a/src/LibObjectFile/PE/PEImageWriter.cs +++ b/src/LibObjectFile/PE/PEImageWriter.cs @@ -8,11 +8,14 @@ namespace LibObjectFile.PE; public sealed class PEImageWriter : ObjectFileReaderWriter { - internal PEImageWriter(PEFile file, Stream stream) : base(file, stream) + internal PEImageWriter(PEFile file, Stream stream, PEImageWriterOptions options) : base(file, stream) { + Options = options; } public PEFile PEFile => (PEFile)base.File; + public PEImageWriterOptions Options { get; } + public override bool KeepOriginalStreamForSubStreams => false; } \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEImageWriterOptions.cs b/src/LibObjectFile/PE/PEImageWriterOptions.cs new file mode 100644 index 0000000..a27c906 --- /dev/null +++ b/src/LibObjectFile/PE/PEImageWriterOptions.cs @@ -0,0 +1,14 @@ +// 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; + +public class PEImageWriterOptions +{ + public static readonly PEImageWriterOptions Default = new(); + + public bool EnableStackTrace { get; init; } + + public bool EnableChecksum { get; init; } +} \ No newline at end of file diff --git a/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj b/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj index 9faca16..192b364 100644 --- a/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj +++ b/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj @@ -81,6 +81,7 @@ Console true NativeLibraryWin64.dll + true @@ -98,6 +99,7 @@ true true NativeLibraryWin64.dll + true @@ -111,6 +113,7 @@ Console true NativeLibraryWin64.dll + true @@ -128,6 +131,7 @@ true true NativeLibraryWin64.dll + true From d44d38715d1a51d47c52c2ddbb16cc0818e3686c Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 1 Oct 2024 21:05:55 +0200 Subject: [PATCH 85/87] Fix PE create test to run only on Windows x64 --- src/LibObjectFile.Tests/PE/PEReaderTests.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs index 44f5d8b..8f8d822 100644 --- a/src/LibObjectFile.Tests/PE/PEReaderTests.cs +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.InteropServices; using System.Threading.Tasks; using LibObjectFile.Diagnostics; using LibObjectFile.PE; @@ -152,10 +153,14 @@ public void TestCreatePE() var sourceFile = Path.Combine(AppContext.BaseDirectory, "PE", "RawNativeConsoleWin64_Generated.exe"); File.WriteAllBytes(sourceFile, output.ToArray()); - // Check the generated exe - var process = Process.Start(sourceFile); - process.WaitForExit(); - Assert.AreEqual(156, process.ExitCode); + // Only try to run the generated exe on Windows x64 + if (OperatingSystem.IsWindows() && RuntimeInformation.OSArchitecture == Architecture.X64) + { + // Check the generated exe + var process = Process.Start(sourceFile); + process.WaitForExit(); + Assert.AreEqual(156, process.ExitCode); + } } [DataTestMethod] From e6786d6c942c7ac1f6bc3556c14060b4f0390cb3 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 1 Oct 2024 21:06:05 +0200 Subject: [PATCH 86/87] Add some advanced documentation --- doc/readme.md | 267 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 265 insertions(+), 2 deletions(-) diff --git a/doc/readme.md b/doc/readme.md index 804b6ab..d638bc4 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -249,7 +249,7 @@ The main entry-point for reading/writing PE file is the [`PEFile`](https://githu ![PE class diagram](PE.png) -## Sections and Directories +#### Sections and Directories In `LibObjectFile` all the section data `PESectionData` - e.g code, data but also including PE directories - are part of either: @@ -263,7 +263,7 @@ A PE Directory itself can contain also a collection of `PESectionData`. If the size of a section data is modified (e.g adding elements to a directory table or modifying a stream in a `PEStreamSectionData`), it is important to call `PEFile.UpdateLayout` to update the layout of the PE file. -## VA, RVA, RVO +#### VA, RVA, RVO In the PE file format, there are different types of addresses: @@ -275,6 +275,269 @@ In the PE file format, there are different types of addresses: In `LibObjectFile` links to RVA between section and section datas are done through a `IPELink` that is combining a reference to a `PEObjectBase` and a `RVO`. It means that RVA are always up to date and linked to the right section data. +### Reading a PE File + +The PE API allows to read from a `System.IO.Stream` via the method `PEFile.Read`: + +```csharp +PEFile pe = PEFile.Read(inputStream); +foreach(var section in pe.Sections) +{ + Console.WriteLine($"{section}"); +} +``` + +### Writing a PE File + +The PE API allows to write to a `System.IO.Stream` via the method `PEFile.Write`: + +```csharp +PEFile pe = PEFile.Read(inputStream); +// Modify the PE file +// .... +pe.Write(stream); +``` + +### Printing a PE File + +You can print a PE file to a textual by using the extension method `PEFile.Print(TextWriter)`: + +```csharp +PEFile pe = PEFile.Read(inputStream); +pe.Print(Console.Out); +``` + +It will generate an output like this: + +``` +DOS Header + Magic = DOS + ByteCountOnLastPage = 0x90 + PageCount = 0x3 + RelocationCount = 0x0 + SizeOfParagraphsHeader = 0x4 + MinExtraParagraphs = 0x0 + MaxExtraParagraphs = 0xFFFF + InitialSSValue = 0x0 + InitialSPValue = 0xB8 + Checksum = 0x0 + InitialIPValue = 0x0 + InitialCSValue = 0x0 + FileAddressRelocationTable = 0x40 + OverlayNumber = 0x0 + Reserved = 0x0, 0x0, 0x0, 0x0 + OEMIdentifier = 0x0 + OEMInformation = 0x0 + Reserved2 = 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + FileAddressPEHeader = 0xC8 + +DOS Stub + DosStub = 64 bytes + +COFF Header + Machine = Amd64 + NumberOfSections = 3 + TimeDateStamp = 1727726362 + PointerToSymbolTable = 0x0 + NumberOfSymbols = 0 + SizeOfOptionalHeader = 240 + Characteristics = ExecutableImage, LargeAddressAware + +Optional Header + Magic = PE32Plus + MajorLinkerVersion = 14 + MinorLinkerVersion = 41 + SizeOfCode = 0x200 + SizeOfInitializedData = 0x400 + SizeOfUninitializedData = 0x0 + AddressOfEntryPoint = RVA = 0x1000, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10, Position = 0x400, Size = 0x10 }, Offset = 0x0 + BaseOfCode = PESection { .text RVA = 0x1000, VirtualSize = 0x10, Position = 0x400, Size = 0x200, Content[1] } + BaseOfData = 0x0x0 + ImageBase = 0x140000000 + SectionAlignment = 0x1000 + FileAlignment = 0x200 + MajorOperatingSystemVersion = 6 + MinorOperatingSystemVersion = 0 + MajorImageVersion = 0 + MinorImageVersion = 0 + MajorSubsystemVersion = 6 + MinorSubsystemVersion = 0 + Win32VersionValue = 0x0 + SizeOfImage = 0x4000 + SizeOfHeaders = 0x400 + CheckSum = 0x0 + Subsystem = WindowsCui + DllCharacteristics = HighEntropyVirtualAddressSpace, DynamicBase, TerminalServerAware + SizeOfStackReserve = 0x100000 + SizeOfStackCommit = 0x1000 + SizeOfHeapReserve = 0x100000 + SizeOfHeapCommit = 0x1000 + LoaderFlags = 0x0 + NumberOfRvaAndSizes = 0x10 + +Data Directories + [00] = null + [01] = PEImportDirectory Position = 0x00000744, Size = 0x00000028, RVA = 0x00002144, VirtualSize = 0x00000028 + [02] = null + [03] = PEExceptionDirectory Position = 0x00000800, Size = 0x0000000C, RVA = 0x00003000, VirtualSize = 0x0000000C + [04] = null + [05] = null + [06] = PEDebugDirectory Position = 0x00000610, Size = 0x00000038, RVA = 0x00002010, VirtualSize = 0x00000038 + [07] = null + [08] = null + [09] = null + [10] = null + [11] = null + [12] = PEImportAddressTableDirectory Position = 0x00000600, Size = 0x00000010, RVA = 0x00002000, VirtualSize = 0x00000010 + [13] = null + [14] = null + [15] = null + +Section Headers + [00] .text PESection Position = 0x00000400, Size = 0x00000200, RVA = 0x00001000, VirtualSize = 0x00000010, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) + [01] .rdata PESection Position = 0x00000600, Size = 0x00000200, RVA = 0x00002000, VirtualSize = 0x0000019C, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + [02] .pdata PESection Position = 0x00000800, Size = 0x00000200, RVA = 0x00003000, VirtualSize = 0x0000000C, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + +Sections + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [00] .text PESection Position = 0x00000400, Size = 0x00000200, RVA = 0x00001000, VirtualSize = 0x00000010, Characteristics = 0x60000020 (ContainsCode, MemExecute, MemRead) + + [00] PEStreamSectionData Position = 0x00000400, Size = 0x00000010, RVA = 0x00001000, VirtualSize = 0x00000010 + + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [01] .rdata PESection Position = 0x00000600, Size = 0x00000200, RVA = 0x00002000, VirtualSize = 0x0000019C, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + [00] PEImportAddressTableDirectory Position = 0x00000600, Size = 0x00000010, RVA = 0x00002000, VirtualSize = 0x00000010 + [00] PEImportAddressTable Position = 0x00000600, Size = 0x00000010, RVA = 0x00002000, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 376, Name = ExitProcess } (RVA = 0x2180, PEStreamSectionData { RVA = 0x2180, VirtualSize = 0x1C, Position = 0x780, Size = 0x1C }, Offset = 0x0) + + + [01] PEDebugDirectory Position = 0x00000610, Size = 0x00000038, RVA = 0x00002010, VirtualSize = 0x00000038 + [0] Type = POGO, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66FB031A, Data = RVA = 0x00002060 (PEDebugStreamSectionData[3] -> .rdata) + [1] Type = ILTCG, Characteristics = 0x0, Version = 0.0, TimeStamp = 0x66FB031A, Data = null + + [02] PEStreamSectionData Position = 0x00000648, Size = 0x00000018, RVA = 0x00002048, VirtualSize = 0x00000018 + + [03] PEDebugStreamSectionData Position = 0x00000660, Size = 0x000000DC, RVA = 0x00002060, VirtualSize = 0x000000DC + + [04] PEStreamSectionData Position = 0x0000073C, Size = 0x00000008, RVA = 0x0000213C, VirtualSize = 0x00000008 + + [05] PEImportDirectory Position = 0x00000744, Size = 0x00000028, RVA = 0x00002144, VirtualSize = 0x00000028 + [0] ImportDllNameLink = KERNEL32.dll (RVA = 0x218E, PEStreamSectionData { RVA = 0x2180, VirtualSize = 0x1C, Position = 0x780, Size = 0x1C }, Offset = 0xE) + [0] ImportAddressTable = RVA = 0x00002000 (PEImportAddressTable[0] -> PEImportAddressTableDirectory[0] -> .rdata) + [0] ImportLookupTable = RVA = 0x00002170 (PEImportLookupTable[7] -> .rdata) + + + [06] PEStreamSectionData Position = 0x0000076C, Size = 0x00000004, RVA = 0x0000216C, VirtualSize = 0x00000004 + + [07] PEImportLookupTable Position = 0x00000770, Size = 0x00000010, RVA = 0x00002170, VirtualSize = 0x00000010 + [0] PEImportHintName { Hint = 376, Name = ExitProcess } (RVA = 0x2180, PEStreamSectionData { RVA = 0x2180, VirtualSize = 0x1C, Position = 0x780, Size = 0x1C }, Offset = 0x0) + + [08] PEStreamSectionData Position = 0x00000780, Size = 0x0000001C, RVA = 0x00002180, VirtualSize = 0x0000001C + + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [02] .pdata PESection Position = 0x00000800, Size = 0x00000200, RVA = 0x00003000, VirtualSize = 0x0000000C, Characteristics = 0x40000040 (ContainsInitializedData, MemRead) + + [00] PEExceptionDirectory Position = 0x00000800, Size = 0x0000000C, RVA = 0x00003000, VirtualSize = 0x0000000C + [0] Begin = RVA = 0x1000, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10, Position = 0x400, Size = 0x10 }, Offset = 0x0 + [0] End = RVA = 0x1010, PEStreamSectionData { RVA = 0x1000, VirtualSize = 0x10, Position = 0x400, Size = 0x10 }, Offset = 0x10 + [0] UnwindInfoAddress = RVA = 0x213C, PEStreamSectionData { RVA = 0x213C, VirtualSize = 0x8, Position = 0x73C, Size = 0x8 }, Offset = 0x0 +``` + +### Creating a PE File + +The PE format is complex and requires a lot of information to be created. + +While LibObjectFile provides a way to create a PE file from scratch, it is not easy to create a working exe/dll file. If you are trying to create a file from scratch, use the `PEFile.Print` on existing exe/dll files to understand the structure and how to create a similar file. + +The following example is a complete example that creates a PE file with a code section that calls `ExitProcess` from `KERNEL32.DLL` with the value `156`: + +```csharp +var pe = new PEFile(); + +// *************************************************************************** +// Code section +// *************************************************************************** +var codeSection = pe.AddSection(PESectionName.Text, 0x1000); +var streamCode = new PEStreamSectionData(); + +streamCode.Stream.Write([ + // SUB RSP, 0x28 + 0x48, 0x83, 0xEC, 0x28, + // MOV ECX, 0x9C + 0xB9, 0x9C, 0x00, 0x00, 0x00, + // CALL ExitProcess (CALL [RIP + 0xFF1]) + 0xFF, 0x15, 0xF1, 0x0F, 0x00, 0x00, + // INT3 + 0xCC +]); + +codeSection.Content.Add(streamCode); + +// *************************************************************************** +// Data section +// *************************************************************************** +var dataSection = pe.AddSection(PESectionName.RData, 0x2000); + +var streamData = new PEStreamSectionData(); +var kernelName = streamData.WriteAsciiString("KERNEL32.DLL"); +var exitProcessFunction = streamData.WriteHintName(new(0x178, "ExitProcess")); + +// PEImportAddressTableDirectory comes first, it is referenced by the RIP + 0xFF1, first address being ExitProcess +var peImportAddressTable = new PEImportAddressTable() +{ + exitProcessFunction +}; +var iatDirectory = new PEImportAddressTableDirectory() +{ + peImportAddressTable +}; + +var peImportLookupTable = new PEImportLookupTable() +{ + exitProcessFunction +}; + +var importDirectory = new PEImportDirectory() +{ + Entries = + { + new PEImportDirectoryEntry(kernelName, peImportAddressTable, peImportLookupTable) + } +}; + +// Layout of the data section +dataSection.Content.Add(iatDirectory); +dataSection.Content.Add(peImportLookupTable); +dataSection.Content.Add(importDirectory); +dataSection.Content.Add(streamData); + +// *************************************************************************** +// Optional Header +// *************************************************************************** +pe.OptionalHeader.AddressOfEntryPoint = new(streamCode, 0); +pe.OptionalHeader.BaseOfCode = codeSection; + +// *************************************************************************** +// Write the PE to a file +// *************************************************************************** +var output = new MemoryStream(); +pe.Write(output, new() { EnableStackTrace = true }); +output.Position = 0; + +var sourceFile = Path.Combine(AppContext.BaseDirectory, "PE", "RawNativeConsoleWin64_Generated.exe"); +File.WriteAllBytes(sourceFile, output.ToArray()); + +// Check the generated exe +var process = Process.Start(sourceFile); +process.WaitForExit(); +Assert.AreEqual(156, process.ExitCode); +``` + +> Notice that the code above doesn't have to setup the `PEFile.Directories` explicitly. +> +> In fact when writing a PE file, the `PEFile.Write` method will automatically populate the directories based on the content of the sections. + ### Links - [PE and COFF Specification](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format) From d29a07c352839741b8e5a646d15c59ddddcbeedc Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Tue, 1 Oct 2024 21:09:36 +0200 Subject: [PATCH 87/87] Update readme --- readme.md | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/readme.md b/readme.md index be144af..1e503fc 100644 --- a/readme.md +++ b/readme.md @@ -36,7 +36,6 @@ elf.Write(outStream); - Full support for the **PE file format** - Read and write from/to a `System.IO.Stream` - All PE Directories are supported - - ImageBase relocation is supported with `PEFile.Relocate(ulong)` method - `PEFile.Print` to print the content of a PE file to a textual representation - - Good support for the **ELF file format**: - Read and write from/to a `System.IO.Stream` @@ -64,32 +63,6 @@ elf.Write(outStream); The [doc/readme.md](doc/readme.md) explains how the library is designed and can be used. -## Known Issues - -PR Welcome if you are willing to contribute to one of these issues: - -- [ ] Add more unit tests - -### ELF -There are still a few missing implementation of `ElfSection` for completeness: - -- [ ] Dynamic Linking Table (`SHT_DYNAMIC`) -- [ ] Version Symbol Table (`SHT_VERSYM`) -- [ ] Version Needs Table (`SHT_VERNEED`) - -These sections are currently loaded as `ElfCustomSection` but should have their own ElfXXXTable (e.g `ElfDynamicLinkingTable`, `ElfVersionSymbolTable`...) - -### DWARF - -- [ ] Version 4: support for `.debug_types`, `.debug_frame`, `.debug_loc`, `.debug_ranges`, `.debug_pubnames`, `.debug_pubtypes`, `.debug_macinfo` section -- [ ] Version 5 - -### Other file formats -In a future version I would like to implement the following file format: - -- [ ] COFF -- [ ] Mach-O - ## Download LibObjectFile is available as a NuGet package: [![NuGet](https://img.shields.io/nuget/v/LibObjectFile.svg)](https://www.nuget.org/packages/LibObjectFile/)