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/doc/PE.png b/doc/PE.png new file mode 100644 index 0000000..02a8b24 Binary files /dev/null and b/doc/PE.png differ diff --git a/doc/readme.md b/doc/readme.md index 9a2240a..d638bc4 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -240,3 +240,304 @@ 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. + +### 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) diff --git a/img/banner.png b/img/banner.png new file mode 100644 index 0000000..843f16f Binary files /dev/null and b/img/banner.png differ diff --git a/img/libobjectfile.pdn b/img/libobjectfile.pdn new file mode 100644 index 0000000..a7abbd3 Binary files /dev/null and b/img/libobjectfile.pdn differ diff --git a/img/libobjectfile.png b/img/libobjectfile.png index 13a891a..e95b684 100644 Binary files a/img/libobjectfile.png and b/img/libobjectfile.png differ diff --git a/readme.md b/readme.md index 591ecd3..1e503fc 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,12 @@ 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 + - `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 +48,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 @@ -58,33 +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 -- [ ] Portable in Memory file format to easily convert between ELF/COFF/Mach-O file formats. - ## Download LibObjectFile is available as a NuGet package: [![NuGet](https://img.shields.io/nuget/v/LibObjectFile.svg)](https://www.nuget.org/packages/LibObjectFile/) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 0000000..0dfc2d1 --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,17 @@ + + + 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.CodeGen/Program.Dwarf.cs b/src/LibObjectFile.CodeGen/Program.Dwarf.cs index 28fe2c0..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,780 +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") ); + // 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 CSharpFreeType(map.Kind == AttributeKind.Managed ? attrType : $"{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 3ff2e74..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,317 +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") ); + // 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 = 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..2a4ce68 100644 --- a/src/LibObjectFile.Tests/Ar/ArTestBase.cs +++ b/src/LibObjectFile.Tests/Ar/ArTestBase.cs @@ -1,42 +1,41 @@ -// 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 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..28f43d0 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. @@ -7,423 +7,423 @@ using System.Linq; using System.Text; using LibObjectFile.Ar; -using NUnit.Framework; +using LibObjectFile.Diagnostics; -namespace LibObjectFile.Tests.Ar +namespace LibObjectFile.Tests.Ar; + +[TestClass] +public class ArTests : ArTestBase { - public class ArTests : ArTestBase + [TestMethod] + 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.IsFalse(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.IsFalse(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.IsTrue(ArArchiveFile.IsAr(stream, out var diagnostics)); + ExpectNoDiagnostics(diagnostics); } + } - [Test] - public void CheckInvalidFileEntry() + [TestMethod] + 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.IsFalse(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); + Assert.IsFalse(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); + 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.IsFalse(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out var diagnostics)); + ExpectDiagnostics(diagnostics, DiagnosticId.AR_ERR_InvalidCharacterInFileEntryName); + } + + // 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.Position = continuePosition; + stream.Write(entry); - stream.WriteByte(0); - stream.WriteByte(1); + var continuePosition = stream.Position; - stream.Position = 0; + stream.Position = 0; + + Assert.IsFalse(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); + + stream.Position = 0; - // Check that we can actually read the content + // 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 result = ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out var arFile, out diagnostics); + ExpectNoDiagnostics(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(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[] - { - (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() + }; + ByteArrayAssert.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"); - } + } - // 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.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; + 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.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; + stream.Position = continuePosition; + stream.WriteByte((byte)'\n'); + stream.Position = 0; - Assert.True(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out diagnostics)); - ExpectNoDiagnostics(diagnostics); - } + Assert.IsTrue(ArArchiveFile.TryRead(stream, ArArchiveKind.GNU, out _, out diagnostics)); + ExpectNoDiagnostics(diagnostics); } + } - [Test] - public void CheckInvalidBSDFileEntry() + [TestMethod] + 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'; - - stream.Write(entry); + 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.Position = 0; + stream.Write(entry); - 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.IsFalse(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.IsFalse(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.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.IsNull(arFile.SymbolTable, "Invalid non-null symbol table found"); } + } - [Test] - public void CheckLibraryWithELF() + [TestMethod] + 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.IsTrue(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.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"); - 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}"); - } + ByteArrayAssert.AreEqual(originalArray, newArray, $"Non binary matching between file {cppLib} and {cppLibCopy}"); } + } - [Test] - public void CheckCreateArLibrary() - { - var libName = "libcustom.a"; + [TestMethod] + 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.IsNotNull(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/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 1793215..ab81c65 100644 --- a/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs +++ b/src/LibObjectFile.Tests/Dwarf/DwarfTests.cs @@ -1,546 +1,546 @@ -// 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.Diagnostics; using LibObjectFile.Dwarf; using LibObjectFile.Elf; -using NUnit.Framework; -namespace LibObjectFile.Tests.Dwarf +namespace LibObjectFile.Tests.Dwarf; + +[TestClass] +public class DwarfTests { - public class DwarfTests + [DataTestMethod] + [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) { - [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); + } + + [DataTestMethod] + [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(); + { + // 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); + [TestMethod] + 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(); + ByteArrayAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + } + + [TestMethod] + 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(); + ByteArrayAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + } + + [TestMethod] + 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(); + ByteArrayAssert.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); + [TestMethod] + 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(); + ByteArrayAssert.AreEqual(inputDebugLineBuffer, outputDebugLineBuffer); + } + + + [TestMethod] + 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(); + [TestMethod] + 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.IsFalse(elfDiagnostics.HasErrors); + + // Create DWARF Object + var dwarfFile = new DwarfFile(); - // Create .debug_line information - var fileName = new DwarfFileName() - { - Name = "check1.cpp", - Directory = Environment.CurrentDirectory, - }; - var fileName2 = new DwarfFileName() - { - Name = "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.LineTables.Add(lineTable); + + lineTable.AddressSize = DwarfAddressSize.Bit64; + lineTable.FileNames.Add(fileName); + lineTable.FileNames.Add(fileName2); - new DwarfLine() - { - File = fileName, - Address = 0, - Column = 1, - Line = 1, - }, - new DwarfLine() - { - File = fileName, - Address = 1, - Column = 1, - Line = 2, - } + lineTable.LineSequences.Add(new DwarfLineSequence() + { + 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.LineSequences.Add(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.Children.Add(subProgram); + + var locationList = new DwarfLocationList(); + var regExpression = new DwarfExpression(); + regExpression.Operations.Add(new DwarfOperation { Kind = DwarfOperationKindEx.Reg0 }); + var regExpression2 = new DwarfExpression(); + regExpression2.Operations.Add(new DwarfOperation { Kind = DwarfOperationKindEx.Reg2 }); + locationList.LocationListEntries.Add(new DwarfLocationListEntry + { + Start = 0, + End = 0x10, + Expression = regExpression, + }); + locationList.LocationListEntries.Add(new DwarfLocationListEntry + { + Start = 0x10, + End = 0x20, + Expression = regExpression2, + }); + var variable = new DwarfDIEVariable() + { + Name = "a", + Location = locationList, + }; + dwarfFile.LocationSection.LocationLists.Add(locationList); + subProgram.Children.Add(variable); + + var cu = new DwarfCompilationUnit() + { + AddressSize = DwarfAddressSize.Bit64, + Root = rootDIE + }; + dwarfFile.InfoSection.Units.Add(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 bae4ae5..250693d 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -5,21 +5,22 @@ using System; using System.IO; using System.Text; +using LibObjectFile.Diagnostics; using LibObjectFile.Elf; -using NUnit.Framework; 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.Offset); + 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.Offset, "Invalid alignment"); + 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,24 +463,24 @@ 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++) { - Assert.AreEqual($".section{i}", symbolTable.Entries[i + 1].Section.Section.Name.Value); + Assert.AreEqual($".section{i}", symbolTable.Entries[i + 1].Section.Section!.Name.Value); } } - [Test] + [TestMethod] public void TestReadLibStdc() { ElfObjectFile elf; diff --git a/src/LibObjectFile.Tests/Elf/ElfTestBase.cs b/src/LibObjectFile.Tests/Elf/ElfTestBase.cs index fa7b486..04b10f1 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)"); - } + ByteArrayAssert.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/LibObjectFile.Tests.csproj b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj index 8de8309..c07f1ef 100644 --- a/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj +++ b/src/LibObjectFile.Tests/LibObjectFile.Tests.csproj @@ -1,9 +1,11 @@ - + net8.0 true false + true + enable @@ -12,6 +14,10 @@ + + + + @@ -28,6 +34,18 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -41,13 +59,18 @@ - - - + + + + + + + + diff --git a/src/LibObjectFile.Tests/LinuxUtil.cs b/src/LibObjectFile.Tests/LinuxUtil.cs index 06205fc..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. @@ -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.Tests/PE/NativeConsole2Win64.exe b/src/LibObjectFile.Tests/PE/NativeConsole2Win64.exe new file mode 100644 index 0000000..b412967 Binary files /dev/null and b/src/LibObjectFile.Tests/PE/NativeConsole2Win64.exe differ diff --git a/src/LibObjectFile.Tests/PE/NativeConsoleWin64.exe b/src/LibObjectFile.Tests/PE/NativeConsoleWin64.exe new file mode 100644 index 0000000..8179ecd Binary files /dev/null and b/src/LibObjectFile.Tests/PE/NativeConsoleWin64.exe differ diff --git a/src/LibObjectFile.Tests/PE/NativeLibraryWin64.dll b/src/LibObjectFile.Tests/PE/NativeLibraryWin64.dll new file mode 100644 index 0000000..1185985 Binary files /dev/null and b/src/LibObjectFile.Tests/PE/NativeLibraryWin64.dll differ diff --git a/src/LibObjectFile.Tests/PE/PEReaderTests.cs b/src/LibObjectFile.Tests/PE/PEReaderTests.cs new file mode 100644 index 0000000..8f8d822 --- /dev/null +++ b/src/LibObjectFile.Tests/PE/PEReaderTests.cs @@ -0,0 +1,283 @@ +// Copyright (c) Alexandre Mutel. All 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.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using LibObjectFile.Diagnostics; +using LibObjectFile.PE; +using VerifyMSTest; + +namespace LibObjectFile.Tests.PE; + +[TestClass] +[UsesVerify] +public partial class PEReaderTests +{ + [DataTestMethod] + [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 }); + var afterReadWriter = new StringWriter(); + peImage.Print(afterReadWriter); + + var afterReadText = afterReadWriter.ToString(); + + await Verifier.Verify(afterReadText).UseParameters(name); + + // Update the layout + var diagnostics = new DiagnosticBag() { EnableStackTrace = true }; + 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(); + } + + // 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, new PEImageWriterOptions() + { + EnableStackTrace = true, + EnableChecksum = peImage.OptionalHeader.CheckSum != 0 // Recalculate the checksum if it was present + }); + output.Position = 0; + 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 + ByteArrayAssert.AreEqual(inputBuffer, outputBuffer, $"Invalid roundtrip for `{name}`"); + } + + [TestMethod] + 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, + // 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()); + + // 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] + [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() + { + EnableStackTrace = true + }); + + 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, 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(); + + 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); + //} + + ByteArrayAssert.AreEqual(inputBuffer, outputBuffer, $"Invalid roundtrip for `{sourceFile}`"); + } + } + + public static IEnumerable GetWindowsExeAndDlls() + { + if (!OperatingSystem.IsWindows()) + { + yield return [""]; + } + else + { + + 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] + [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/ + // 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/PE/RawNativeConsoleWin64.exe b/src/LibObjectFile.Tests/PE/RawNativeConsoleWin64.exe new file mode 100644 index 0000000..f2db2dc Binary files /dev/null and b/src/LibObjectFile.Tests/PE/RawNativeConsoleWin64.exe differ diff --git a/src/LibObjectFile.Tests/TestsInitializer.cs b/src/LibObjectFile.Tests/TestsInitializer.cs new file mode 100644 index 0000000..bb8384b --- /dev/null +++ b/src/LibObjectFile.Tests/TestsInitializer.cs @@ -0,0 +1,20 @@ +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; + VerifierSettings.DontScrubSolutionDirectory(); + } +} \ No newline at end of file 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.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..ed6b33c --- /dev/null +++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsole2Win64.exe.verified.txt @@ -0,0 +1,655 @@ +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 = 1727802524 + PointerToSymbolTable = 0x0 + NumberOfSymbols = 0 + SizeOfOptionalHeader = 240 + Characteristics = ExecutableImage, LargeAddressAware + +Optional Header + Magic = PE32Plus + MajorLinkerVersion = 14 + MinorLinkerVersion = 41 + SizeOfCode = 0x1A00 + SizeOfInitializedData = 0x2A00 + SizeOfUninitializedData = 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 + MajorOperatingSystemVersion = 6 + MinorOperatingSystemVersion = 0 + MajorImageVersion = 0 + MinorImageVersion = 0 + MajorSubsystemVersion = 6 + MinorSubsystemVersion = 0 + Win32VersionValue = 0x0 + SizeOfImage = 0x9000 + SizeOfHeaders = 0x400 + CheckSum = 0x7C57 + 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 + [15] = 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 + [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 + 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 + + [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 = 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 = 7 + PdbPath = C:\code\LibObjectFile\src\native\Win64\NativeProjects\x64\Release\NativeConsole2Win64.pdb + + [07] PEStreamSectionData Position = 0x0000248A, Size = 0x00000002, RVA = 0x0000368A, VirtualSize = 0x00000002 + + [08] PEDebugStreamSectionData Position = 0x0000248C, Size = 0x00000014, RVA = 0x0000368C, VirtualSize = 0x00000014 + + [09] PEDebugStreamSectionData Position = 0x000024A0, Size = 0x000002FC, RVA = 0x000036A0, VirtualSize = 0x000002FC + + [10] PEStreamSectionData Position = 0x0000279C, Size = 0x0000020C, RVA = 0x0000399C, VirtualSize = 0x0000020C + + [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 = 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 = 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 = 0x0000013C, RVA = 0x00003C00, VirtualSize = 0x0000013C + + [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[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[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[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[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[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[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[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[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[16] -> .rdata) + + + [15] PEStreamSectionData Position = 0x00002C04, Size = 0x00000004, RVA = 0x00003E04, VirtualSize = 0x00000004 + + [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) + [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) + + [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) + [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) + + [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) + [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) + + [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) + + [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) + + [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) + + [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) + + [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) + [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) + + [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) + + [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) + + [00] PEStreamSectionData Position = 0x00003800, Size = 0x00000078, RVA = 0x00005000, VirtualSize = 0x00000078 + + [01] PEBoundImportAddressTable64 Position = 0x00003878, Size = 0x00000018, RVA = 0x00005078, VirtualSize = 0x00000018 + [0] VA = 0x14000133E + [1] VA = 0x140001332 + + [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 + [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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 + + + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [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 + > 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] 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 + + + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [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 + [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 new file mode 100644 index 0000000..e4a0e93 --- /dev/null +++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeConsoleWin64.exe.verified.txt @@ -0,0 +1,563 @@ +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 = 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 + 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 + [15] = 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 + [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) + + + [01] PEStreamSectionData Position = 0x000017F0, Size = 0x000000C0, RVA = 0x000031F0, VirtualSize = 0x000000C0 + + [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 + + [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 + + [04] PEStreamSectionData Position = 0x00001A60, Size = 0x0000007C, RVA = 0x00003460, VirtualSize = 0x0000007C + + [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 + + [06] PEStreamSectionData Position = 0x00001B4D, Size = 0x00000003, RVA = 0x0000354D, VirtualSize = 0x00000003 + + [07] PEDebugStreamSectionData Position = 0x00001B50, Size = 0x00000014, RVA = 0x00003550, VirtualSize = 0x00000014 + + [08] PEDebugStreamSectionData Position = 0x00001B64, Size = 0x00000284, RVA = 0x00003564, VirtualSize = 0x00000284 + + [09] PEStreamSectionData Position = 0x00001DE8, Size = 0x0000019C, RVA = 0x000037E8, VirtualSize = 0x0000019C + + [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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 0x648, Position = 0x2240, Size = 0x648 }, Offset = 0x630) + [8] ImportAddressTable = RVA = 0x00003000 (PEImportAddressTable[0] -> PEImportAddressTableDirectory[0] -> .rdata) + [8] ImportLookupTable = RVA = 0x00003A50 (PEImportLookupTable[12] -> .rdata) + + + [11] PEStreamSectionData Position = 0x0000204C, Size = 0x00000004, RVA = 0x00003A4C, VirtualSize = 0x00000004 + + [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) + + [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) + + [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) + + [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) + + [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) + + [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) + + [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) + + [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) + + [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) + + [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 + [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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 + + + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [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 + > 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] 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 + + + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [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 + [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 new file mode 100644 index 0000000..e908aec --- /dev/null +++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=NativeLibraryWin64.dll.verified.txt @@ -0,0 +1,485 @@ +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 = 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 + 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 + [15] = 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 + [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) + + + [01] PEStreamSectionData Position = 0x000014E8, Size = 0x000000A8, RVA = 0x000020E8, VirtualSize = 0x000000A8 + + [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 + + [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 + + [04] PEStreamSectionData Position = 0x00001740, Size = 0x000000A4, RVA = 0x00002340, VirtualSize = 0x000000A4 + + [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 + + [06] PEStreamSectionData Position = 0x00001855, Size = 0x00000003, RVA = 0x00002455, VirtualSize = 0x00000003 + + [07] PEDebugStreamSectionData Position = 0x00001858, Size = 0x00000014, RVA = 0x00002458, VirtualSize = 0x00000014 + + [08] PEDebugStreamSectionData Position = 0x0000186C, Size = 0x00000258, RVA = 0x0000246C, VirtualSize = 0x00000258 + + [09] PEStreamSectionData Position = 0x00001AC4, Size = 0x000001AC, RVA = 0x000026C4, VirtualSize = 0x000001AC + + [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] -> 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 + + + [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 = 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 = 0x27E, Position = 0x1E18, Size = 0x27E }, Offset = 0x266) + [2] ImportAddressTable = RVA = 0x00002000 (PEImportAddressTable[0] -> PEImportAddressTableDirectory[0] -> .rdata) + [2] ImportLookupTable = RVA = 0x00002930 (PEImportLookupTable[12] -> .rdata) + + + [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) + + [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) + + [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) + + [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 + [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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 + + + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [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 + > 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] PEStreamSectionData Position = 0x00002658, Size = 0x00000008, RVA = 0x00005058, VirtualSize = 0x00000008 + + [04] PEResourceData Position = 0x00002660, Size = 0x00000091, RVA = 0x00005060, VirtualSize = 0x00000091 + + [05] PEStreamSectionData Position = 0x000026F1, Size = 0x00000007, RVA = 0x000050F1, VirtualSize = 0x00000007 + + + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [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 + [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.Tests/Verified/PEReaderTests.TestPrinter_name=RawNativeConsoleWin64.exe.verified.txt b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=RawNativeConsoleWin64.exe.verified.txt new file mode 100644 index 0000000..f9ae4eb --- /dev/null +++ b/src/LibObjectFile.Tests/Verified/PEReaderTests.TestPrinter_name=RawNativeConsoleWin64.exe.verified.txt @@ -0,0 +1,134 @@ +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 + + 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.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.sln.DotSettings b/src/LibObjectFile.sln.DotSettings index a8c814e..8a3b5ef 100644 --- a/src/LibObjectFile.sln.DotSettings +++ b/src/LibObjectFile.sln.DotSettings @@ -2,16 +2,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. + ARM BSD DIE GNU LEB + RSDS + RVA + RVO + VA True True True + True True + True True + True True True True + True True \ No newline at end of file diff --git a/src/LibObjectFile/Ar/ArArchiveFile.cs b/src/LibObjectFile/Ar/ArArchiveFile.cs index 1564d02..e91f925 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. @@ -6,13 +6,15 @@ using System.Collections.Generic; using System.IO; using System.Text; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; namespace LibObjectFile.Ar; /// /// An 'ar' archive file. /// -public sealed class ArArchiveFile : ObjectFileNode +public sealed class ArArchiveFile : ArObjectBase { private readonly List _files; @@ -48,17 +50,17 @@ 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. /// - public IReadOnlyList Files => _files; + public ReadOnlyList Files => _files; /// /// Adds a file to . @@ -80,7 +82,7 @@ public void AddFile(ArFile file) } file.Parent = this; - file.Index = (uint)_files.Count; + file.Index = _files.Count; _files.Add(file); } @@ -122,7 +124,7 @@ public void InsertFileAt(int index, ArFile file) } } - file.Index = (uint)index; + file.Index = index; _files.Insert(index, file); file.Parent = this; @@ -154,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++) @@ -178,9 +180,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++) { @@ -198,7 +214,7 @@ public override void Verify(DiagnosticBag diagnostics) } } - item.Verify(diagnostics); + item.Verify(context); } } @@ -308,10 +324,17 @@ public void Write(Stream stream) var writer = new ArArchiveFileWriter(this, stream); writer.Write(); } - - public override void UpdateLayout(DiagnosticBag diagnostics) + + protected override void UpdateLayoutCore(ArVisitorContext context) + { + + } + + + public void UpdateLayout(DiagnosticBag diagnostics) { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + ArgumentNullException.ThrowIfNull(diagnostics); + var layoutContext = new ArVisitorContext(this, diagnostics); Size = 0; @@ -352,25 +375,25 @@ 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; 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 557d794..c5259db 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileReader.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileReader.cs @@ -5,348 +5,348 @@ using System; using System.Buffers; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; +using LibObjectFile.Diagnostics; 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; + KeepOriginalStreamForSubStreams = options.IsReadOnly; + } + + public ArArchiveFileReaderOptions Options { get; } - public override bool IsReadOnly { get; } + public override bool KeepOriginalStreamForSubStreams { 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.Offset} while another table was already found at offset {_futureHeaders.Offset}. 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, 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.Offset = (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 f0181cc..c775610 100644 --- a/src/LibObjectFile/Ar/ArArchiveFileWriter.cs +++ b/src/LibObjectFile/Ar/ArArchiveFileWriter.cs @@ -7,197 +7,196 @@ using System.Diagnostics; using System.IO; using System.Text; +using LibObjectFile.Diagnostics; -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) + { + KeepOriginalStreamForSubStreams = false; + } - private ArArchiveFile ArArchiveFile { get; } + public ArArchiveFile ArArchiveFile => (ArArchiveFile)base.File; - public override bool IsReadOnly { get; } + public override bool KeepOriginalStreamForSubStreams { 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.Offset); - 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 c85c7d9..8f73bba 100644 --- a/src/LibObjectFile/Ar/ArBinaryFile.cs +++ b/src/LibObjectFile/Ar/ArBinaryFile.cs @@ -2,38 +2,35 @@ // 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 +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; - } + protected override void UpdateLayoutCore(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 4a496eb..b12873a 100644 --- a/src/LibObjectFile/Ar/ArElfFile.cs +++ b/src/LibObjectFile/Ar/ArElfFile.cs @@ -2,60 +2,57 @@ // 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 +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 SliceStream(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; + protected override void UpdateLayoutCore(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 3977b5c..5940ba6 100644 --- a/src/LibObjectFile/Ar/ArFile.cs +++ b/src/LibObjectFile/Ar/ArFile.cs @@ -3,146 +3,132 @@ // See the license.txt file in the project root for more information. using System; +using System.Text; +using LibObjectFile.Diagnostics; -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 c6ef197..c045884 100644 --- a/src/LibObjectFile/Ar/ArLongNamesTable.cs +++ b/src/LibObjectFile/Ar/ArLongNamesTable.cs @@ -2,147 +2,144 @@ // 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; -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 class ArLongNamesTable : ArFile - { - public const string DefaultName = "//"; + public const string DefaultName = "//"; - public ArLongNamesTable() - { - } + 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 = new Dictionary(); - - 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; + protected override void UpdateLayoutCore(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 44e0fa0..791d384 100644 --- a/src/LibObjectFile/Ar/ArObject.cs +++ b/src/LibObjectFile/Ar/ArObject.cs @@ -8,25 +8,28 @@ 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 . /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public new ArArchiveFile Parent + public new ArArchiveFile? Parent { - get => (ArArchiveFile)base.Parent; + get => (ArArchiveFile?)base.Parent; internal set => base.Parent = value; } + + protected override void ValidateParent(ObjectElement 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 716da0a..6db07b2 100644 --- a/src/LibObjectFile/Ar/ArSymbolTable.cs +++ b/src/LibObjectFile/Ar/ArSymbolTable.cs @@ -2,241 +2,235 @@ // 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; +using LibObjectFile.IO; -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.Offset] = 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.Offset); - - 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; + } + + + protected override void UpdateLayoutCore(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..ccd600b --- /dev/null +++ b/src/LibObjectFile/Ar/ArVisitorContext.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. + +using LibObjectFile.Diagnostics; + +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/Collections/ObjectList.cs b/src/LibObjectFile/Collections/ObjectList.cs new file mode 100644 index 0000000..839445f --- /dev/null +++ b/src/LibObjectFile/Collections/ObjectList.cs @@ -0,0 +1,241 @@ +// Copyright (c) Alexandre Mutel. All 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(ObjectList<>.ObjectListDebuggerView))] +public readonly struct ObjectList : IList + where TObject : ObjectElement +{ + // 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( + ObjectElement 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, adding, added, removing, removed, updating, updated); + } + + public int Count => _items.Count; + + public bool IsReadOnly => false; + + public List UnsafeList => _items; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(TObject item) + { + CheckAdd(item); + var items = _items; + int index = items.Count; + items.Adding(index, item); + items.Add(AssignAdd(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) + { + if ((uint)index > (uint)_items.Count) throw new ArgumentOutOfRangeException(nameof(index)); + + CheckAdd(item); + var items = _items; + items.Adding(index, item); + items.Insert(index, AssignAdd(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]; + items.Removing(item); + item.Parent = null; + item.ResetIndex(); + + items.RemoveAt(index); + + for (int i = index; i < items.Count; i++) + { + items[i].Index = i; + } + } + + public TObject this[int index] + { + get => _items[index]; + set + { + 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] = AssignAdd(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(); + } + + 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(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 ObjectElement 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); + + [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(ObjectList 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; + } + } + } +} diff --git a/src/LibObjectFile/Collections/ReadOnlyList.cs b/src/LibObjectFile/Collections/ReadOnlyList.cs new file mode 100644 index 0000000..05b96db --- /dev/null +++ b/src/LibObjectFile/Collections/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.Collections; + +/// +/// 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(collection, nameof(collection)); + _collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items + { + get + { + T[] array = new T[_collection.Count]; + _collection.CopyTo(array, 0); + return array; + } + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/Collections/SortedObjectList.cs b/src/LibObjectFile/Collections/SortedObjectList.cs new file mode 100644 index 0000000..4dc08e1 --- /dev/null +++ b/src/LibObjectFile/Collections/SortedObjectList.cs @@ -0,0 +1,236 @@ +// Copyright (c) Alexandre Mutel. All 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.Reflection; +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 : ObjectElement, 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(ObjectElement parent) + { + ArgumentNullException.ThrowIfNull(parent); + _items = new InternalList(parent); + } + + public int Count => _items.Count; + + public bool IsReadOnly => false; + + public List UnsafeList => _items; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(TObject item) + { + CheckAdd(item); + var items = _items; + if (items.Count > 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() + { + var items = _items; + for (var i = items.Count - 1; i >= 0; i--) + { + var item = items[i]; + 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) + { + 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) + { + var items = _items; + var item = items[index]; + item.Parent = null; + item.ResetIndex(); + + items.RemoveAt(index); + + for (int i = index; i < items.Count; i++) + { + items[i].Index = i; + } + } + + public TObject this[int index] + { + get => _items[index]; + set + { + if ((uint)index >= (uint)_items.Count) throw new ArgumentOutOfRangeException(nameof(index)); + CheckAdd(value); + + // Unbind previous entry + var items = _items; + + 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; + } + } + } + + public List.Enumerator GetEnumerator() => _items.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + { + return _items.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_items).GetEnumerator(); + } + + 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(ObjectElement parent) : List + { + public readonly ObjectElement Parent = parent; + } + + 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; + } + } + } +} diff --git a/src/LibObjectFile/Collections/TempSpan.cs b/src/LibObjectFile/Collections/TempSpan.cs new file mode 100644 index 0000000..95c483e --- /dev/null +++ b/src/LibObjectFile/Collections/TempSpan.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; +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 TempSpan 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 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. + /// + /// The heap memory buffer. + /// The size of the span in bytes. + 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. + /// + /// The number of elements in the 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 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. + /// + /// The stack memory span. + /// The number of elements in the 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 tempSpan = new TempSpan(stackSpan, size); + span = tempSpan.Span; + return tempSpan; + } + + return Create(count, out span); + } + + /// + /// Gets the span of elements. + /// + public Span Span => _span; + + /// + /// Gets the span of elements as bytes. + /// + public Span AsBytes => 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); + } + } + + /// + /// Gets the span of elements. + /// + /// 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(TempSpan tempSpan) => tempSpan.AsBytes; +} diff --git a/src/LibObjectFile/DiagnosticBag.cs b/src/LibObjectFile/DiagnosticBag.cs deleted file mode 100644 index 75e6ed0..0000000 --- a/src/LibObjectFile/DiagnosticBag.cs +++ /dev/null @@ -1,106 +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.Text; - -namespace LibObjectFile -{ - /// - /// 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; - - public DiagnosticBag() - { - _messages = new List(); - } - - /// - /// List of messages. - /// - public IReadOnlyList Messages => _messages; - - /// - /// If this instance contains error messages. - /// - public bool HasErrors { get; private set; } - - /// - /// 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) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - foreach (var diagnosticMessage in Messages) - { - diagnostics.Log(diagnosticMessage); - } - } - - /// - /// 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) - { - 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 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() - { - var builder = new StringBuilder(); - foreach (var diagnosticMessage in Messages) - { - 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 deleted file mode 100644 index 170dd84..0000000 --- a/src/LibObjectFile/DiagnosticId.cs +++ /dev/null @@ -1,118 +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 the various diagnostic message ids. - /// - public enum DiagnosticId - { - 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, - - 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, - - } -} \ No newline at end of file diff --git a/src/LibObjectFile/DiagnosticKind.cs b/src/LibObjectFile/DiagnosticKind.cs deleted file mode 100644 index 2ec7425..0000000 --- a/src/LibObjectFile/DiagnosticKind.cs +++ /dev/null @@ -1,22 +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 the kind of a - /// - public enum DiagnosticKind - { - /// - /// A warning message. - /// - Warning, - - /// - /// An error message. - /// - Error, - } -} \ No newline at end of file diff --git a/src/LibObjectFile/DiagnosticMessage.cs b/src/LibObjectFile/DiagnosticMessage.cs deleted file mode 100644 index 52633fd..0000000 --- a/src/LibObjectFile/DiagnosticMessage.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. - -namespace LibObjectFile -{ - /// - /// A diagnostic message. - /// - public readonly struct DiagnosticMessage - { - public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string 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; - } - - /// - /// Gets the kind of this message. - /// - public DiagnosticKind Kind { get; } - - /// - /// Gets the id of this message. - /// - public DiagnosticId Id { get; } - - /// - /// Gets the context of this message. - /// - public object Context { get; } - - /// - /// Gets the associated text of this message. - /// - public string Message { get; } - - public override string ToString() - { - return $"{Kind} LB{(uint)Id:0000}: {Message}"; - } - } -} \ No newline at end of file diff --git a/src/LibObjectFile/Diagnostics/DiagnosticBag.cs b/src/LibObjectFile/Diagnostics/DiagnosticBag.cs new file mode 100644 index 0000000..f3f71d9 --- /dev/null +++ b/src/LibObjectFile/Diagnostics/DiagnosticBag.cs @@ -0,0 +1,117 @@ +// Copyright (c) Alexandre Mutel. All 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.Text; +using LibObjectFile.Collections; + +namespace LibObjectFile.Diagnostics; + +/// +/// 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; + + public DiagnosticBag() + { + _messages = new List(); + } + + /// + /// List of messages. + /// + public ReadOnlyList Messages => _messages; + + /// + /// If this instance contains error messages. + /// + 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. + /// + 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) + { + if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + foreach (var diagnosticMessage in Messages) + { + diagnostics.Log(diagnosticMessage); + } + } + + /// + /// 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) + { + 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) + { + StackTrace = EnableStackTrace ? new StackTrace(1, true) : null + }); + } + + /// + /// 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) + { + StackTrace = EnableStackTrace ? new StackTrace(1, true) : null + }); + } + + public override string ToString() + { + var builder = new StringBuilder(); + foreach (var diagnosticMessage in Messages) + { + builder.AppendLine(diagnosticMessage.ToString()); + } + + return builder.ToString(); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/Diagnostics/DiagnosticId.cs b/src/LibObjectFile/Diagnostics/DiagnosticId.cs new file mode 100644 index 0000000..3690353 --- /dev/null +++ b/src/LibObjectFile/Diagnostics/DiagnosticId.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. + +namespace LibObjectFile.Diagnostics; + +/// +/// Defines the various diagnostic message ids. +/// +public enum DiagnosticId +{ + 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, + + 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, + + // 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_ERR_InvalidExtraData = 3009, + PE_ERR_SectionSizeLargerThanVirtualSize = 3010, + PE_ERR_SectionRVALessThanPrevious = 3011, + PE_ERR_TooManySections = 3012, + PE_ERR_FileAlignmentNotPowerOfTwo = 3013, + PE_ERR_SectionAlignmentNotPowerOfTwo = 3014, + 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_ERR_VerifyContextInvalidObject = 3021, + PE_ERR_ChecksumNotSupported = 3022, + + // PE Exception directory + PE_ERR_InvalidExceptionDirectory_Entries = 3100, + PE_ERR_InvalidExceptionDirectory_Entry = 3101, + PE_ERR_InvalidExceptionDirectory_Size = 3102, + + // PE Certificate directory + PE_ERR_InvalidCertificateEntry = 3200, + + // PE BoundImport directory + 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, + + // PE Debug directory + PE_ERR_DebugDirectorySize = 3600, + PE_ERR_DebugDirectorySectionNotFound = 3601, + PE_ERR_DebugDirectoryContainerNotFound = 3602, + 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_ERR_BaseRelocationDirectoryInvalidSizeOfBlock = 3705, + PE_ERR_InvalidBaseRelocationBlock = 3706, + PE_ERR_BaseRelocationDirectoryInvalidSectionLink = 3707, + + // 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_WRN_ImportLookupTableInvalidRVAOutOfRange = 3808, + + // 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, + PE_ERR_InvalidResourceString = 4003, +} \ No newline at end of file diff --git a/src/LibObjectFile/Diagnostics/DiagnosticKind.cs b/src/LibObjectFile/Diagnostics/DiagnosticKind.cs new file mode 100644 index 0000000..d663cec --- /dev/null +++ b/src/LibObjectFile/Diagnostics/DiagnosticKind.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.Diagnostics; + +/// +/// Defines the kind of a +/// +public enum DiagnosticKind +{ + /// + /// A warning message. + /// + Warning, + + /// + /// An error message. + /// + Error, +} \ No newline at end of file diff --git a/src/LibObjectFile/Diagnostics/DiagnosticMessage.cs b/src/LibObjectFile/Diagnostics/DiagnosticMessage.cs new file mode 100644 index 0000000..970f297 --- /dev/null +++ b/src/LibObjectFile/Diagnostics/DiagnosticMessage.cs @@ -0,0 +1,66 @@ +// Copyright (c) Alexandre Mutel. All 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; + +/// +/// A diagnostic message. +/// +public readonly struct DiagnosticMessage +{ + public DiagnosticMessage(DiagnosticKind kind, DiagnosticId id, string 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; + } + + /// + /// Gets the kind of this message. + /// + public DiagnosticKind Kind { get; } + + /// + /// Gets the id of this message. + /// + public DiagnosticId Id { 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 stack trace of this message. + /// + public StackTrace? StackTrace { get; init; } + + public override string ToString() + { + 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 593b015..2996406 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviation.cs @@ -1,241 +1,241 @@ -// 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.Generic; using System.Diagnostics; -using System.IO; +using System.Diagnostics.CodeAnalysis; +using LibObjectFile.Diagnostics; -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, 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.Offset; - var code = reader.ReadULEB128(); - if (code == 0) - { - return false; - } + Position = startOffset, + Code = code + }; - var item = new DwarfAbbreviationItem - { - Offset = 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) { - Offset = reader.Offset; - while (TryReadNext(reader)) + foreach (var itemPair in _mapItems) { + var item = itemPair.Value; + item.Write(writer); } - Size = (ulong)reader.Offset - Offset; } - - protected override void Write(DwarfWriter writer) + else { - var startOffset = writer.Offset; - Debug.Assert(startOffset == Offset); - 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.Offset - startOffset == Size); - } + Debug.Assert(writer.Position - startOffset == Size); + } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - var endOffset = Offset; + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + var endOffset = Position; - if (_mapItems.Count > 0) + if (_mapItems.Count > 0) + { + foreach (var itemPair in _mapItems) { - foreach (var itemPair in _mapItems) - { - var item = itemPair.Value; - item.Offset = endOffset; - item.UpdateLayoutInternal(layoutContext); - endOffset += item.Size; - } - + var item = itemPair.Value; + item.Position = endOffset; + item.UpdateLayout(context); + endOffset += item.Size; } - else + + } + else + { + if (_items.Count > 0) { - if (_items.Count > 0) + foreach (var item in _items) { - foreach (var item in _items) - { - item.Offset = endOffset; - item.UpdateLayoutInternal(layoutContext); - endOffset += item.Size; - } + item.Position = endOffset; + item.UpdateLayout(context); + endOffset += item.Size; } } + } - endOffset += DwarfHelper.SizeOfULEB128(0); + 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 32d1df7..0a2a9d3 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationItem.cs @@ -1,125 +1,125 @@ -// 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 LibObjectFile.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 = Offset; + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + 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 - Offset; - } + 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.Offset - Offset; + if (descriptors == null) descriptors = new List(1); + descriptors.Add(new DwarfAttributeDescriptor(attributeName, attributeForm)); } - protected override void Write(DwarfWriter writer) - { - var startOffset = writer.Offset; - Debug.Assert(startOffset == Offset); + 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.Offset - 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 a94024a..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 dc607b2..8dcc94c 100644 --- a/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAbbreviationTable.cs @@ -1,86 +1,71 @@ -// 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 LibObjectFile.Collections; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfAbbreviationTable : DwarfSection { - public class DwarfAbbreviationTable : DwarfSection - { - private readonly List _abbreviations; + private readonly ObjectList _abbreviations; - public DwarfAbbreviationTable() - { - _abbreviations = new List(); - } + public DwarfAbbreviationTable() + { + _abbreviations = new ObjectList(this); + } - public IReadOnlyList Abbreviations => _abbreviations; + public ObjectList Abbreviations => _abbreviations; - internal void AddAbbreviation(DwarfAbbreviation abbreviation) + internal void Reset() + { + foreach(var abbreviation in _abbreviations) { - _abbreviations.Add(this, abbreviation); + abbreviation.Reset(); } + _abbreviations.Clear(); + } - internal void RemoveAbbreviation(DwarfAbbreviation abbreviation) + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + ulong endOffset = Position; + foreach (var abbreviation in _abbreviations) { - _abbreviations.Remove(this, abbreviation); + abbreviation.Position = endOffset; + abbreviation.UpdateLayout(context); + endOffset += abbreviation.Size; } - internal DwarfAbbreviation RemoveAbbreviationAt(int index) - { - return _abbreviations.RemoveAt(this, index); - } + Size = endOffset - Position; + } - internal void Reset() + public override void Read(DwarfReader reader) + { + var endOffset = reader.Position; + while (reader.Position < reader.Length) { - foreach(var abbreviation in _abbreviations) + var abbreviation = new DwarfAbbreviation { - abbreviation.Reset(); - } - _abbreviations.Clear(); + Position = endOffset + }; + abbreviation.Read(reader); + endOffset += abbreviation.Size; + _abbreviations.Add(abbreviation); } - - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - ulong endOffset = Offset; - foreach (var abbreviation in _abbreviations) - { - abbreviation.Offset = endOffset; - abbreviation.UpdateLayoutInternal(layoutContext); - endOffset += abbreviation.Size; - } - Size = endOffset - Offset; - } + Size = endOffset - Position; + } - protected override void Read(DwarfReader reader) + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; + foreach (var abbreviation in _abbreviations) { - var endOffset = reader.Offset; - while (reader.Offset < reader.Length) - { - var abbreviation = new DwarfAbbreviation - { - Offset = endOffset - }; - abbreviation.ReadInternal(reader); - endOffset += abbreviation.Size; - AddAbbreviation(abbreviation); - } - - Size = endOffset - Offset; + abbreviation.Write(writer); } - protected override void Write(DwarfWriter writer) - { - var startOffset = writer.Offset; - 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/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 8bc3a3d..38f2510 100644 --- a/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfAddressRangeTable.cs @@ -1,277 +1,274 @@ -// 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.Generic; using System.Diagnostics; -using System.IO; +using LibObjectFile.Diagnostics; 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) - { - Offset = reader.Offset; - 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.Offset = AlignHelper.AlignToUpper(reader.Offset, 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.AlignUp(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.Offset - Offset; + 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); + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + 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.AlignUp(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.Offset; - } + if (Unit != null) + { + DebugInfoOffset = Unit.Position; } + } - protected override void Write(DwarfWriter writer) - { - var startOffset = writer.Offset; + 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.Offset, align); - for (ulong offset = writer.Offset; offset < nextOffset; offset++) - { - writer.WriteU8(0); - } - Debug.Assert(writer.Offset == 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.AlignUp(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.Offset - 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 db0c381..cc5f5dc 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttribute.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttribute.cs @@ -1,1001 +1,1006 @@ -// 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; -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) { - 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}, "); + } + base.PrintMembers(builder); + + return true; + } - public override string ToString() + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + var addressSize = context.CurrentUnit!.AddressSize; + var is64BitEncoding = context.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 = Offset; - 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.Offset - layoutContext.CurrentUnit.Offset); // 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.Offset = 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 - context.CurrentUnit.Position); // WriteULEB128((dieRef.Offset - _currentUnit.Offset)); + break; } - Size = endOffset - Offset; + //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(context); + 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.Offset - Offset; + 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 = Offset; - Debug.Assert(Offset == 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.Offset); - break; - } - case DwarfAttributeForm.Ref1: - { - 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)); - break; - } - case DwarfAttributeForm.Ref4: - { - var dieRef = (DwarfDIE) ValueAsObject; - writer.WriteU32((uint) (dieRef.Offset - writer.CurrentUnit.Offset)); - 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.Offset - writer.CurrentUnit.Offset)); - 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.Offset - writer.CurrentUnit.Offset)); - 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).Offset); - } - 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.Offset - 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 8add13e..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 50ffe43..f6c28d2 100644 --- a/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs +++ b/src/LibObjectFile/Dwarf/DwarfAttributeDescriptors.cs @@ -3,80 +3,78 @@ // See the license.txt file in the project root for more information. using System; -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 fa00158..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 eb12ac9..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 ce84db3..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 86603c4..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.Offset; - 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.Offset; - 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 8c83fa0..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..55cd4bb 100644 --- a/src/LibObjectFile/Dwarf/DwarfContainer.cs +++ b/src/LibObjectFile/Dwarf/DwarfContainer.cs @@ -2,15 +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.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 2c73998..cd103f4 100644 --- a/src/LibObjectFile/Dwarf/DwarfDIE.cs +++ b/src/LibObjectFile/Dwarf/DwarfDIE.cs @@ -1,516 +1,462 @@ -// 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. +// Copyright (c) Alexandre Mutel. All 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.Text; +using LibObjectFile.Collections; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfDIE : DwarfContainer { - public class DwarfDIE : DwarfContainer + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly SortedObjectList _attributes; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly ObjectList _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(); - } - - protected DwarfDIE(DwarfTagEx tag) - { - _tag = tag; - } + _attributes = new SortedObjectList(this); + _children = new ObjectList(this); + } - public virtual DwarfTagEx Tag - { - get => _tag; - set => _tag = value; - } + public virtual DwarfTagEx Tag + { + get => _tag; + set => _tag = value; + } - public IReadOnlyList Attributes => _attributes; + public SortedObjectList Attributes => _attributes; - public IReadOnlyList Children => _children; + public ObjectList 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); - } - /// - /// 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) + public override void Verify(DwarfVerifyContext context) + { + foreach (var attr in _attributes) { - _children.InsertAt(this, index, child); + attr.Verify(context); } - /// - /// Removes a child from - /// - /// The child to remove - public void RemoveChild(DwarfDIE child) + foreach (var child in _children) { - _children.Remove(this, child); + child.Verify(context); } + } - /// - /// 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); - } + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"{nameof(Tag)}: {Tag}, {nameof(Attributes)}: {Attributes.Count}, {nameof(Children)}: {Children.Count}, "); + base.PrintMembers(builder); + return true; + } - /// - /// Adds an attribute to . - /// - /// A attribute - public void AddAttribute(DwarfAttribute attribute) + protected TValue? GetAttributeValue(DwarfAttributeKind kind) + { + foreach (var attr in _attributes) { - _attributes.AddSorted(this, attribute, true); + if (attr.Kind == kind) + { + return (TValue?)attr.ValueAsObject; + } } - /// - /// Removes an attribute from - /// - /// The attribute to remove - public void RemoveAttribute(DwarfAttribute attribute) - { - _attributes.Remove(this, attribute); - } + return default; + } - /// - /// Removes an attribute from at the specified index. - /// - /// Index into to remove the specified attribute - public DwarfAttribute RemoveAttributeAt(int index) + protected unsafe TValue? GetAttributeValueOpt(DwarfAttributeKind kind) where TValue : unmanaged + { + Debug.Assert(sizeof(TValue) <= sizeof(ulong)); + + foreach (var attr in _attributes) { - return _attributes.RemoveAt(this, index); + if (attr.Kind == kind) + { + ulong localU64 = attr.ValueAsU64; + return *(TValue*) &localU64; + } } + return default; + } - public override void Verify(DiagnosticBag diagnostics) + protected DwarfConstant? GetAttributeConstantOpt(DwarfAttributeKind kind) + { + foreach (var attr in _attributes) { - base.Verify(diagnostics); - - foreach (var attr in _attributes) - { - attr.Verify(diagnostics); - } - - foreach (var child in _children) + if (attr.Kind == kind) { - child.Verify(diagnostics); + return new DwarfConstant + { + AsValue = + { + U64 = attr.ValueAsU64 + }, + AsObject = attr.ValueAsObject + }; } } - public override string ToString() - { - return $"{nameof(Tag)}: {Tag}, {nameof(Attributes)}: {Attributes.Count}, {nameof(Children)}: {Children.Count}"; - } + return null; + } - protected TValue GetAttributeValue(DwarfAttributeKind kind) + protected void SetAttributeConstantOpt(DwarfAttributeKind kind, DwarfConstant? cst) + { + 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 (!cst.HasValue) + { + Attributes.RemoveAt(i); + } + else { - return (TValue)attr.ValueAsObject; + var value = cst.Value; + attr.ValueAsU64 = value.AsValue.U64; + attr.ValueAsObject = value.AsExpression; } + return; } - - return default; } - protected unsafe TValue? GetAttributeValueOpt(DwarfAttributeKind kind) where TValue : unmanaged + if (cst.HasValue) { - Debug.Assert(sizeof(TValue) <= sizeof(ulong)); - - foreach (var attr in _attributes) + var value = cst.Value; + Attributes.Add(new DwarfAttribute() { - if (attr.Kind == kind) - { - ulong localU64 = attr.ValueAsU64; - return *(TValue*) &localU64; - } - } - - return default; + 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; + Attributes.RemoveAt(i); } - } - - if (cst.HasValue) - { - var value = cst.Value; - AddAttribute(new DwarfAttribute() + else { - Kind = kind, - ValueAsU64 = value.AsValue.U64, - ValueAsObject = value.AsExpression - }); + var value = cst.Value; + attr.ValueAsU64 = value.AsValue.U64; + attr.ValueAsObject = value.AsObject; + } + return; } } - protected DwarfLocation? GetAttributeLocationOpt(DwarfAttributeKind kind) + if (cst.HasValue) { - foreach (var attr in _attributes) + var value = cst.Value; + Attributes.Add(new DwarfAttribute() { - if (attr.Kind == kind) - { - return new DwarfLocation - { - AsValue = - { - U64 = attr.ValueAsU64 - }, - AsObject = attr.ValueAsObject - }; - } - } - - return null; + Kind = kind, + ValueAsU64 = value.AsValue.U64, + ValueAsObject = value.AsObject + }); } + } - protected void SetAttributeLocationOpt(DwarfAttributeKind kind, DwarfLocation? cst) + public DwarfAttribute? FindAttributeByKey(DwarfAttributeKind kind) + { + foreach (var attr in _attributes) { - for (int i = 0; i < _attributes.Count; i++) + if (attr.Kind == kind) { - var attr = _attributes[i]; - if (attr.Kind == kind) - { - if (!cst.HasValue) - { - RemoveAttributeAt(i); - } - else - { - var value = cst.Value; - attr.ValueAsU64 = value.AsValue.U64; - attr.ValueAsObject = value.AsObject; - } - return; - } - } - - if (cst.HasValue) - { - var value = cst.Value; - AddAttribute(new DwarfAttribute() - { - Kind = kind, - ValueAsU64 = value.AsValue.U64, - ValueAsObject = value.AsObject - }); + return attr; } } - public DwarfAttribute FindAttributeByKey(DwarfAttributeKind kind) + return null; + } + + protected unsafe void SetAttributeValue(DwarfAttributeKind kind, TValue? value) + { + 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 (value == null) { - return attr; + Attributes.RemoveAt(i); } - } - - return null; - } - - protected unsafe void SetAttributeValue(DwarfAttributeKind kind, TValue value) - { - for (int i = 0; i < _attributes.Count; i++) - { - var attr = _attributes[i]; - if (attr.Kind == kind) + else { - if (value == null) - { - RemoveAttributeAt(i); - } - else - { - attr.ValueAsObject = value; - } - return; + attr.ValueAsObject = value; } + return; } - - if (value == null) return; - AddAttribute(new DwarfAttribute() { Kind = kind, ValueAsObject = value}); } - protected void SetAttributeLinkValue(DwarfAttributeKind kind, TLink link) where TLink : IObjectFileNodeLink + if (value == null) return; + Attributes.Add(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 unsafe void SetAttributeValueOpt(DwarfAttributeKind kind, TValue? value) where TValue : unmanaged + { + Debug.Assert(sizeof(TValue) <= sizeof(ulong)); + + 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; + Attributes.RemoveAt(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; + Attributes.Add(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; - } - } + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + 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; - - var endOffset = Offset; - endOffset += DwarfHelper.SizeOfULEB128(abbrev.Code); // WriteULEB128(abbreviationItem.Code); + attr.Position = endOffset; + attr.UpdateLayout(context); + endOffset += attr.Size; + } - foreach (var attr in _attributes) + if (abbrev.HasChildren) + { + foreach (var child in _children) { - attr.Offset = endOffset; - attr.UpdateLayoutInternal(layoutContext); - endOffset += attr.Size; + child.Position = endOffset; + child.UpdateLayout(context); + endOffset += child.Size; } - if (abbrev.HasChildren) - { - foreach (var child in _children) - { - child.Offset = endOffset; - child.UpdateLayout(layoutContext); - endOffset += child.Size; - } + // Encode abbreviation 0 code + endOffset += DwarfHelper.SizeOfULEB128(0); + } - // Encode abbreviation 0 code - endOffset += DwarfHelper.SizeOfULEB128(0); - } + Size = endOffset - Position; + } - Size = endOffset - Offset; - } + public override void Read(DwarfReader reader) + { + // Store map offset to DIE to resolve references + reader.PushDIE(this); - protected override void Read(DwarfReader reader) - { - // Store map offset to DIE to resolve references - reader.PushDIE(this); + // Console.WriteLine($" <{level}><{die.Offset:x}> Abbrev Number: {abbreviationCode} ({die.Tag})"); - // 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) + 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() - { - Offset = reader.Offset, - }; + var attribute = new DwarfAttribute() + { + Position = reader.Position, + }; - attribute.ReadInternal(reader); + attribute.Read(reader); - AddAttribute(attribute); - } + Attributes.Add(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); - } + Children.Add(child); } + } - reader.PopDIE(); + reader.PopDIE(); - Size = reader.Offset - Offset; - } + 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.Offset; - 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.Offset = 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) - Offset = 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]; + + for (var i = 0; i < Attributes.Count; i++) + { + var attr = Attributes[i]; + attr.UpdateAttributeForm(context); + descriptorArray[i] = new DwarfAttributeDescriptor(attr.Kind, attr.Form); + } - var key = new DwarfAbbreviationItemKey(Tag, Children.Count > 0, new DwarfAttributeDescriptors(descriptorArray)); - var item = context.CurrentUnit.Abbreviation.GetOrCreate(key); + var key = new DwarfAbbreviationItemKey(Tag, Children.Count > 0, new DwarfAttributeDescriptors(descriptorArray)); + var item = context.CurrentUnit!.Abbreviation!.GetOrCreate(key); - Abbrev = item; + Abbrev = item; - foreach (var children in Children) - { - children.UpdateAbbreviationItem(context); - } + 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 = Offset; - Debug.Assert(Offset == startDIEOffset); - var abbrev = Abbrev; - writer.WriteULEB128(abbrev.Code); + throw new InvalidOperationException("Abbreviation is not set"); + } - foreach (var attr in _attributes) - { - attr.WriteInternal(writer); - } + writer.WriteULEB128(abbrev.Code); - if (abbrev.HasChildren) + foreach (var attr in _attributes) + { + attr.Write(writer); + } + + 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.Offset - 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 179a1c0..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 a8bf895..1547da7 100644 --- a/src/LibObjectFile/Dwarf/DwarfElfContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfElfContext.cs @@ -6,336 +6,336 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using LibObjectFile.Diagnostics; 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 9dbe2aa..fdcef71 100644 --- a/src/LibObjectFile/Dwarf/DwarfExpression.cs +++ b/src/LibObjectFile/Dwarf/DwarfExpression.cs @@ -1,132 +1,111 @@ -// 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.Generic; using System.Diagnostics; +using LibObjectFile.Collections; -namespace LibObjectFile.Dwarf -{ - [DebuggerDisplay("Count = {Operations.Count,nq}")] - public class DwarfExpression : DwarfObject - { - private readonly List _operations; +namespace LibObjectFile.Dwarf; - public DwarfExpression() - { - _operations = new List(); - } +[DebuggerDisplay("Count = {Operations.Count,nq}")] +public class DwarfExpression : DwarfObject +{ + private readonly ObjectList _operations; - public IReadOnlyList Operations => _operations; + public DwarfExpression() + { + _operations = new ObjectList(this); + } - internal List InternalOperations => _operations; + public ObjectList Operations => _operations; - public ulong OperationLengthInBytes { get; internal set; } + public ulong OperationLengthInBytes { get; internal set; } - public void AddOperation(DwarfOperation operation) + public override void Verify(DwarfVerifyContext context) + { + foreach (var op in _operations) { - if (operation == null) throw new ArgumentNullException(nameof(operation)); - _operations.Add(this, operation); + op.Verify(context); } + } - public void RemoveOperation(DwarfOperation operation) - { - if (operation == null) throw new ArgumentNullException(nameof(operation)); - _operations.Remove(this, operation); - } + 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); + Operations.Add(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) { - Offset = reader.Offset; - var size = inLocationSection ? reader.ReadU16() : reader.ReadULEB128(); - OperationLengthInBytes = size; - var endPosition = reader.Offset + size; - - while (reader.Offset < endPosition) - { - var op = new DwarfOperation() {Offset = reader.Offset}; - op.ReadInternal(reader); - AddOperation(op); - } - - Size = reader.Offset - Offset; + writer.WriteU16((ushort)OperationLengthInBytes); } - - internal void WriteInternal(DwarfWriter writer, bool inLocationSection = false) + else { - Debug.Assert(Offset == writer.Offset); - Debug.Assert(!inLocationSection || OperationLengthInBytes <= ushort.MaxValue); - - var startExpressionOffset = writer.Offset; - if (inLocationSection) - { - writer.WriteU16((ushort)OperationLengthInBytes); - } - else - { - writer.WriteULEB128(OperationLengthInBytes); - } - - foreach (var op in Operations) - { - op.WriteInternal(writer); - } - - Debug.Assert(writer.Offset - startExpressionOffset == Size); + writer.WriteULEB128(OperationLengthInBytes); } - internal void UpdateLayoutInternal(DwarfLayoutContext layoutContext, bool inLocationSection = false) + foreach (var op in Operations) { - var endOffset = Offset; - foreach (var op in _operations) - { - op.Offset = endOffset; - op.UpdateLayoutInternal(layoutContext); - endOffset += op.Size; - } - - OperationLengthInBytes = endOffset - Offset; - - // 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; - } - - 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 _operations.UnsafeList) { - WriteInternal(writer, inLocationSection: false); + op.Position += deltaLength; } + + Size = OperationLengthInBytes + deltaLength; + } + + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + UpdateLayout(context, 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 0d37d0d..bf0d71f 100644 --- a/src/LibObjectFile/Dwarf/DwarfFile.cs +++ b/src/LibObjectFile/Dwarf/DwarfFile.cs @@ -1,456 +1,458 @@ -// 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 LibObjectFile.Diagnostics; using LibObjectFile.Elf; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfFile : DwarfContainer { - public class DwarfFile : DwarfContainer + private ObjectFileElementHolder _abbreviationTable; + private ObjectFileElementHolder _stringTable; + private ObjectFileElementHolder _lineSection; + private ObjectFileElementHolder _infoSection; + private ObjectFileElementHolder _addressRangeTable; + private ObjectFileElementHolder _locationSection; + + public DwarfFile() { - private DwarfAbbreviationTable _abbreviationTable; - private DwarfStringTable _stringTable; - private DwarfLineSection _lineSection; - private DwarfInfoSection _infoSection; - private DwarfAddressRangeTable _addressRangeTable; - private DwarfLocationSection _locationSection; - - public DwarfFile() - { - AbbreviationTable = new DwarfAbbreviationTable(); - StringTable = new DwarfStringTable(); - LineSection = new DwarfLineSection(); - InfoSection = new DwarfInfoSection(); - LocationSection = new DwarfLocationSection(); - AddressRangeTable = new DwarfAddressRangeTable(); - } + _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, false); - } + public DwarfAbbreviationTable AbbreviationTable + { + get => _abbreviationTable; + set => _abbreviationTable.Set(this, value); + } - public DwarfStringTable StringTable - { - get => _stringTable; - set => AttachChild(this, value, ref _stringTable, false); - } + public DwarfStringTable StringTable + { + get => _stringTable; + set => _stringTable.Set(this, value); + } - public DwarfLineSection LineSection - { - get => _lineSection; - set => AttachChild(this, value, ref _lineSection, false); - } + public DwarfLineSection LineSection + { + get => _lineSection; + set => _lineSection.Set(this, value); + } - public DwarfAddressRangeTable AddressRangeTable - { - get => _addressRangeTable; - set => AttachChild(this, value, ref _addressRangeTable, false); - } + public DwarfAddressRangeTable AddressRangeTable + { + get => _addressRangeTable; + set => _addressRangeTable.Set(this, value); + } - public DwarfInfoSection InfoSection + public DwarfInfoSection InfoSection + { + get => _infoSection; + set => _infoSection.Set(this, value); + } + + public DwarfLocationSection LocationSection + { + get => _locationSection; + set => _locationSection.Set(this, value); + } + + 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, false); + 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, false); + 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); - - LineSection.Offset = 0; - LineSection.UpdateLayoutInternal(layoutContext); - if (layoutContext.HasErrors) - { - return; - } - // Reset the abbreviation table - // TODO: Make this configurable via the DwarfWriterContext - AbbreviationTable.Offset = 0; - AbbreviationTable.Reset(); + // Update the abbrev table right after we have computed the entire layout of Info + AbbreviationTable.Position = 0; + AbbreviationTable.UpdateLayout(layoutContext); - InfoSection.Offset = 0; - InfoSection.UpdateLayoutInternal(layoutContext); - if (layoutContext.HasErrors) - { - return; - } - - // Update AddressRangeTable layout after Info - AddressRangeTable.Offset = 0; - AddressRangeTable.UpdateLayoutInternal(layoutContext); - if (layoutContext.HasErrors) - { - return; - } - - // Update string table right after updating the layout of Info - StringTable.Offset = 0; - StringTable.UpdateLayoutInternal(layoutContext); - if (layoutContext.HasErrors) - { - return; - } - - // Update the abbrev table right after we have computed the entire layout of Info - AbbreviationTable.Offset = 0; - AbbreviationTable.UpdateLayoutInternal(layoutContext); + LocationSection.Position = 0; + LocationSection.UpdateLayout(layoutContext); + } - LocationSection.Offset = 0; - LocationSection.UpdateLayoutInternal(layoutContext); - } + public void Write(DwarfWriterContext writerContext) + { + if (writerContext == null) throw new ArgumentNullException(nameof(writerContext)); - public void Write(DwarfWriterContext writerContext) - { - if (writerContext == null) throw new ArgumentNullException(nameof(writerContext)); + var diagnostics = new DiagnosticBag(); - var diagnostics = new DiagnosticBag(); + var verifyContext = new DwarfVerifyContext(this, diagnostics); - // Verify correctness - Verify(diagnostics); - CheckErrors(diagnostics); + // Verify correctness + Verify(verifyContext); + CheckErrors(diagnostics); - // Update the layout of all section and tables - UpdateLayout(writerContext.LayoutConfig, diagnostics); - CheckErrors(diagnostics); + // Update the layout of all section and tables + UpdateLayout(writerContext.LayoutConfig, diagnostics); + CheckErrors(diagnostics); - // Write all section and stables - var writer = new DwarfWriter(this, writerContext.IsLittleEndian, diagnostics); - writer.AddressSize = writerContext.AddressSize; - writer.EnableRelocation = writerContext.EnableRelocation; + // 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; + writer.DebugLog = writerContext.DebugLinePrinter; + if (writerContext.DebugLineStream != null) + { writer.Stream = writerContext.DebugLineStream; - if (writer.Stream != null) - { - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = LineSection; - LineSection.Relocations.Clear(); - LineSection.WriteInternal(writer); - } + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = LineSection; + LineSection.Relocations.Clear(); + LineSection.Write(writer); + } - writer.Log = null; + writer.DebugLog = null; + if (writerContext.DebugAbbrevStream != null) + { writer.Stream = writerContext.DebugAbbrevStream; - if (writer.Stream != null) - { - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = AbbreviationTable; - AbbreviationTable.WriteInternal(writer); - } + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = AbbreviationTable; + AbbreviationTable.Write(writer); + } + if (writerContext.DebugAddressRangeStream != null) + { writer.Stream = writerContext.DebugAddressRangeStream; - if (writer.Stream != null) - { - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = AddressRangeTable; - AddressRangeTable.Relocations.Clear(); - AddressRangeTable.WriteInternal(writer); - } + 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; - if (writer.Stream != null) - { - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = StringTable; - StringTable.WriteInternal(writer); - } + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = StringTable; + StringTable.Write(writer); + } + if (writerContext.DebugInfoStream != null) + { writer.Stream = writerContext.DebugInfoStream; - if (writer.Stream != null) - { - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = InfoSection; - InfoSection.Relocations.Clear(); - InfoSection.WriteInternal(writer); - } + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = InfoSection; + InfoSection.Relocations.Clear(); + InfoSection.Write(writer); + } + if (writerContext.DebugLocationStream != null) + { writer.Stream = writerContext.DebugLocationStream; - if (writer.Stream != null) - { - writer.Stream.Position = 0; - writer.Stream.SetLength(0); - writer.CurrentSection = LocationSection; - LocationSection.Relocations.Clear(); - LocationSection.WriteInternal(writer); - } - - CheckErrors(diagnostics); + writer.Stream.Position = 0; + writer.Stream.SetLength(0); + writer.CurrentSection = LocationSection; + LocationSection.Relocations.Clear(); + LocationSection.Write(writer); } + + CheckErrors(diagnostics); + } - public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig layoutConfig = null) - { - if (elfContext == null) throw new ArgumentNullException(nameof(elfContext)); + public void WriteToElf(DwarfElfContext elfContext, DwarfLayoutConfig? layoutConfig = null) + { + if (elfContext == null) throw new ArgumentNullException(nameof(elfContext)); - var diagnostics = new DiagnosticBag(); + var diagnostics = new DiagnosticBag(); - layoutConfig ??= new DwarfLayoutConfig(); + layoutConfig ??= new DwarfLayoutConfig(); - // Verify correctness - Verify(diagnostics); - CheckErrors(diagnostics); + var verifyContext = new DwarfVerifyContext(this, diagnostics); - // Update the layout of all section and tables - UpdateLayout(layoutConfig, diagnostics); - CheckErrors(diagnostics); + // Verify correctness + Verify(verifyContext); + 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(); - } + // Update the layout of all section and tables + UpdateLayout(layoutConfig, diagnostics); + CheckErrors(diagnostics); - // 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(); - } + // 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); + } + else + { + elfContext.RemoveStringTable(); + } - // Line table - if (LineSection.Size > 0) + // 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) + { + 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)); + elfContext.RemoveLocationSection(); + } + + CheckErrors(diagnostics); + } - var dwarf = new DwarfFile(); - var reader = new DwarfReader(readerContext, dwarf, new DiagnosticBag()); + public static DwarfFile Read(DwarfReaderContext readerContext) + { + if (readerContext == null) throw new ArgumentNullException(nameof(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; - if (reader.Stream != null) - { - reader.CurrentSection = dwarf.AbbreviationTable; - dwarf.AbbreviationTable.ReadInternal(reader); - } + reader.CurrentSection = dwarf.AbbreviationTable; + dwarf.AbbreviationTable.Read(reader); + } + if (readerContext.DebugStringStream != null) + { reader.Stream = readerContext.DebugStringStream; - if (reader.Stream != null) - { - reader.CurrentSection = dwarf.StringTable; - dwarf.StringTable.ReadInternal(reader); - } + reader.CurrentSection = dwarf.StringTable; + dwarf.StringTable.Read(reader); + } - reader.Log = readerContext.DebugLinePrinter; + reader.DebugLog = readerContext.DebugLinePrinter; + if (readerContext.DebugLineStream != null) + { reader.Stream = readerContext.DebugLineStream; - if (reader.Stream != null) - { - reader.CurrentSection = dwarf.LineSection; - dwarf.LineSection.ReadInternal(reader); - } + reader.CurrentSection = dwarf.LineSection; + dwarf.LineSection.Read(reader); + } + reader.DebugLog = null; - reader.Log = null; + if (readerContext.DebugAddressRangeStream != null) + { reader.Stream = readerContext.DebugAddressRangeStream; - if (reader.Stream != null) - { - reader.CurrentSection = dwarf.AddressRangeTable; - dwarf.AddressRangeTable.ReadInternal(reader); - } + reader.CurrentSection = dwarf.AddressRangeTable; + dwarf.AddressRangeTable.Read(reader); + } - reader.Log = null; + reader.DebugLog = null; + if (readerContext.DebugLocationStream != null) + { reader.Stream = readerContext.DebugLocationStream; - if (reader.Stream != null) - { - reader.CurrentSection = dwarf.LocationSection; - dwarf.LocationSection.ReadInternal(reader); - } + reader.CurrentSection = dwarf.LocationSection; + dwarf.LocationSection.Read(reader); + } - reader.Log = null; + reader.DebugLog = null; + if (readerContext.DebugInfoStream != null) + { reader.Stream = readerContext.DebugInfoStream; - if (reader.Stream != null) - { - reader.DefaultUnitKind = DwarfUnitKind.Compile; - reader.CurrentSection = dwarf.InfoSection; - dwarf.InfoSection.ReadInternal(reader); - } + reader.DefaultUnitKind = DwarfUnitKind.Compile; + reader.CurrentSection = dwarf.InfoSection; + dwarf.InfoSection.Read(reader); + } - CheckErrors(reader.Diagnostics); + CheckErrors(reader.Diagnostics); - return dwarf; - } + 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(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)); - } + public static DwarfFile ReadFromElf(ElfObjectFile elf) + { + return ReadFromElf(new DwarfElfContext(elf)); + } - private static void CheckErrors(DiagnosticBag diagnostics) + private static void CheckErrors(DiagnosticBag diagnostics) + { + if (diagnostics.HasErrors) { - if (diagnostics.HasErrors) - { - throw new ObjectFileException("Unexpected errors while verifying and updating the layout", diagnostics); - } + throw new ObjectFileException("Unexpected errors while verifying and updating the layout", diagnostics); } + } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - } + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + } - protected override void Write(DwarfWriter writer) - { - } + 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 ece43bc..e7558da 100644 --- a/src/LibObjectFile/Dwarf/DwarfFileName.cs +++ b/src/LibObjectFile/Dwarf/DwarfFileName.cs @@ -4,27 +4,30 @@ using System.IO; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public sealed class DwarfFileName { - public sealed class DwarfFileName + public DwarfFileName(string name) { - public string Name { get; set; } + Name = name; + } + + 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 (string.IsNullOrEmpty(Name)) return ""; - 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 daad1a9..0aef1b4 100644 --- a/src/LibObjectFile/Dwarf/DwarfHelper.cs +++ b/src/LibObjectFile/Dwarf/DwarfHelper.cs @@ -2,113 +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 System.Text; using System.Numerics; +using System.Text; + +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 5f727a1..2cc0b75 100644 --- a/src/LibObjectFile/Dwarf/DwarfInfoSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfInfoSection.cs @@ -1,105 +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 LibObjectFile.Collections; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public sealed class DwarfInfoSection : DwarfRelocatableSection { - public sealed class DwarfInfoSection : DwarfRelocatableSection - { - private readonly List _units; + private readonly ObjectList _units; - public DwarfInfoSection() - { - _units = new List(); - } + public DwarfInfoSection() + { + _units = new ObjectList(this); + } - public IReadOnlyList Units => _units; + public ObjectList Units => _units; - public void AddUnit(DwarfUnit unit) + public override void Read(DwarfReader reader) + { + var addressRangeTable = reader.File.AddressRangeTable; + + while (reader.Position < reader.Length) { - _units.Add(this, unit); - } + // 7.5 Format of Debugging Information + // - Each such contribution consists of a compilation unit header - public void RemoveUnit(DwarfUnit unit) - { - _units.Remove(this, unit); - } + var startOffset = Position; - public DwarfUnit RemoveUnitAt(int index) - { - return _units.RemoveAt(this, index); - } + reader.ClearResolveAttributeReferenceWithinCompilationUnit(); - protected override void Read(DwarfReader reader) - { - var addressRangeTable = reader.File.AddressRangeTable; - - while (reader.Offset < reader.Length) + var cu = DwarfUnit.ReadInstance(reader, out var offsetEndOfUnit); + if (cu == null) { - // 7.5 Format of Debugging Information - // - Each such contribution consists of a compilation unit header - - var startOffset = Offset; - - reader.ClearResolveAttributeReferenceWithinCompilationUnit(); - - var cu = DwarfUnit.ReadInstance(reader, out var offsetEndOfUnit); - if (cu == null) - { - reader.Offset = offsetEndOfUnit; - continue; - } + reader.Position = offsetEndOfUnit; + continue; + } - reader.CurrentUnit = cu; + reader.CurrentUnit = cu; - // Link AddressRangeTable to Unit - if (addressRangeTable.DebugInfoOffset == cu.Offset) - { - addressRangeTable.Unit = cu; - } - - AddUnit(cu); + // Link AddressRangeTable to Unit + if (addressRangeTable.DebugInfoOffset == cu.Position) + { + addressRangeTable.Unit = cu; } - - reader.ResolveAttributeReferenceWithinSection(); + + _units.Add(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) + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + var offset = Position; + foreach (var unit in Units) { - var offset = Offset; - foreach (var unit in Units) - { - layoutContext.CurrentUnit = unit; - unit.Offset = offset; - unit.UpdateLayoutInternal(layoutContext); - offset += unit.Size; - } - Size = offset - Offset; + context.CurrentUnit = unit; + unit.Position = offset; + unit.UpdateLayout(context); + 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(Offset == writer.Offset); - foreach (var unit in _units) - { - writer.CurrentUnit = unit; - unit.WriteInternal(writer); - } - writer.CurrentUnit = null; - Debug.Assert(Size == writer.Offset - Offset); + 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 511fd57..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 66977b6..fca11a2 100644 --- a/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfLayoutContext.cs @@ -2,25 +2,35 @@ // 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 LibObjectFile.Diagnostics; + +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 e6965b7..f16152f 100644 --- a/src/LibObjectFile/Dwarf/DwarfLine.cs +++ b/src/LibObjectFile/Dwarf/DwarfLine.cs @@ -1,184 +1,187 @@ -// 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.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(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}"; - } - 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) - { - } + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + } + + 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 185f5d3..f772f93 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineProgramTable.cs @@ -1,4 +1,3 @@ - // Copyright (c) Alexandre Mutel. All 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,1371 +5,1368 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Text; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; -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 ObjectList _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 ObjectList(this); + _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 ObjectList LineSequences => _lineSequences; - public List FileNames { get; } + public override void Read(DwarfReader reader) + { + var log = reader.DebugLog; + var startOfSection = reader.Position; + + reader.OffsetToLineProgramTable.Add(startOfSection, this); - public IReadOnlyList LineSequences => _lineSequences; + var unitLength = reader.ReadUnitLength(); + Is64BitEncoding = reader.Is64BitEncoding; + AddressSize = reader.AddressSize; + var startPosition = reader.Position; + var version = reader.ReadU16(); - public void AddLineSequence(DwarfLineSequence line) + 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.Offset; - - reader.OffsetToLineProgramTable.Add(startOfSection, this); - - var unitLength = reader.ReadUnitLength(); - Is64BitEncoding = reader.Is64BitEncoding; - AddressSize = reader.AddressSize; - var startPosition = reader.Offset; - 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.Offset; - 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.Offset; - 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 = 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.Offset = reader.Offset; - 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}; - - while (true) + if (log != null) { - var currentLength = reader.Offset - 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.Lines.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.Offset:x8}]"); - } - - var opcode = reader.ReadU8(); - switch (opcode) + break; + case DwarfNative.DW_LNS_advance_pc: { - case DwarfNative.DW_LNS_copy: - currentSequence.Add(state.Clone()); - state.Offset = reader.Offset; - 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.Lines.Add(state.Clone()); + currentSequence.Size = reader.Position - currentSequence.Position; + _lineSequences.Add(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}]"); + log.Write($" Special opcode {adjusted_opcode}: advance Address by {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.Offset; - var endOffset = reader.Offset + sizeOfExtended; - bool hasValidOpCode = true; - if (reader.Offset < 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.Offset - currentSequence.Offset; - AddLineSequence(currentSequence); - - currentSequence = new DwarfLineSequence() {Offset = reader.Offset}; - - state.Offset = reader.Offset; - 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() {Name = fileName}; - debugFileName.Directory = fileDirectoryIndex == 0 || fileDirectoryIndex >= directories.Count ? null : directories[fileDirectoryIndex - 1]; - debugFileName.Time = fileTime; - debugFileName.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.Offset != endOffset) - { - reader.Diagnostics.Warning(DiagnosticId.DWARF_WRN_InvalidExtendedOpCodeLength, $"Invalid length {sizeOfExtended} at offset 0x{lengthOffset:x}"); - } - - reader.Offset = 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(); - } - - 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.Lines.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.Offset = reader.Offset; - 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) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidNullFileNameEntry, $"Invalid null {nameof(FileNames)} entry at [{i}] for {this}"); - } - else + var line = lines[i]; + var deltaAddress = (long)line.Address - (long)previousAddress; + if (deltaAddress < 0) { - if (fileName.Name == null) - { - diagnostics.Error(DiagnosticId.DWARF_ERR_InvalidFileName, $"Invalid null filename for {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}"); } - } + 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; + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + 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 - sizeOf++; - - if (Version >= 4) - { - // maximum_operations_per_instruction - sizeOf++; - } - - // 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(); - - foreach (var fileName in FileNames) - { - uint dirIndex = 0; - if (fileName.Directory != null) - { - var directoryName = fileName.Directory; - RecordDirectory(directoryName, ref sizeOf, out dirIndex); - } - - _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; + // minimum_instruction_length + sizeOf++; - HeaderLength = sizeOf - headerLengthStart; + if (Version >= 4) + { + // maximum_operations_per_instruction + sizeOf++; + } - LayoutDebugLineOpCodes(ref sizeOf, OpCodeBase); + // default_is_stmt + // line_base + // line_range + // opcode_base + sizeOf += 4; - Size = sizeOf; + // StandardOpCodeLengths + foreach (var opcodeLength in _standardOpCodeLengths) + { + sizeOf += DwarfHelper.SizeOfULEB128(opcodeLength); } - private void RecordDirectory(string directoryName, ref ulong sizeOf, out uint dirIndex) + // Write directory names + _directoryNameToIndex.Clear(); + _directoryNames.Clear(); + _fileNameToIndex.Clear(); + + foreach (var fileName in FileNames) { - if (!_directoryNameToIndex.TryGetValue(directoryName, out dirIndex)) + uint dirIndex = 0; + if (fileName.Directory != null) { - dirIndex = (uint) _directoryNames.Count + 1; - _directoryNameToIndex.Add(directoryName, dirIndex); - sizeOf += (ulong) Encoding.UTF8.GetByteCount(directoryName) + 1; - _directoryNames.Add(directoryName); + var directoryName = fileName.Directory; + RecordDirectory(directoryName, ref sizeOf, out dirIndex); } - } - protected override void Write(DwarfWriter writer) - { - var startOffset = writer.Offset; + _fileNameToIndex.Add(fileName, (uint)_fileNameToIndex.Count + 1); - writer.Is64BitEncoding = Is64BitEncoding; - writer.WriteUnitLength(Size - DwarfHelper.SizeOfUnitLength(Is64BitEncoding)); - - writer.WriteU16(Version); - writer.WriteUIntFromEncoding(HeaderLength); + 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; - var startOfHeader = writer.Offset; + HeaderLength = sizeOf - headerLengthStart; - writer.WriteU8(MinimumInstructionLength); + LayoutDebugLineOpCodes(ref sizeOf, OpCodeBase); - if (Version >= 4) - { - writer.WriteU8(MaximumOperationsPerInstruction); - } + Size = sizeOf; + } - // default_is_stmt - writer.WriteU8(1); + 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); + } + } - // line_base - writer.WriteI8(LineBase); + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; - // line_range - writer.WriteU8(LineRange); + writer.Is64BitEncoding = Is64BitEncoding; + writer.WriteUnitLength(Size - DwarfHelper.SizeOfUnitLength(Is64BitEncoding)); + + writer.WriteU16(Version); + writer.WriteUIntFromEncoding(HeaderLength); - // opcode_base - writer.WriteU8(OpCodeBase); + var startOfHeader = writer.Position; - // standard_opcode_lengths - foreach (var opcodeLength in StandardOpCodeLengths) - { - writer.WriteULEB128(opcodeLength); - } + writer.WriteU8(MinimumInstructionLength); - // Write directory names - foreach (var directoryName in _directoryNames) - { - writer.WriteStringUTF8NullTerminated(directoryName); - } - // empty string - writer.WriteU8(0); + if (Version >= 4) + { + writer.WriteU8(MaximumOperationsPerInstruction); + } - // Write filenames - foreach (var fileName in FileNames) - { - writer.WriteStringUTF8NullTerminated(fileName.Name); + // default_is_stmt + writer.WriteU8(1); - uint directoryIndex = 0; - if (fileName.Directory != null) - { - directoryIndex = _directoryNameToIndex[fileName.Directory]; - } + // line_base + writer.WriteI8(LineBase); - writer.WriteULEB128(directoryIndex); - writer.WriteULEB128(fileName.Time); - writer.WriteULEB128(fileName.Size); - } - // empty string - writer.WriteU8(0); + // line_range + writer.WriteU8(LineRange); - var headSizeWritten = writer.Offset - startOfHeader; - Debug.Assert(HeaderLength == headSizeWritten, $"Expected Header Length: {HeaderLength} != Written Header Length: {headSizeWritten}"); - - WriteDebugLineOpCodes(writer, OpCodeBase); + // opcode_base + writer.WriteU8(OpCodeBase); - Debug.Assert(Size == writer.Offset - startOffset, $"Expected Size: {Size} != Written Size: {writer.Offset - startOffset}"); + // standard_opcode_lengths + foreach (var opcodeLength in StandardOpCodeLengths) + { + writer.WriteULEB128(opcodeLength); } - private void WriteDebugLineOpCodes(DwarfWriter writer, uint opCodeBase) + // Write directory names + foreach (var directoryName in _directoryNames) { - var previousLineState = new DwarfLineState(); - var firstFile = FileNames.Count > 0 ? FileNames[0] : null; - previousLineState.Reset(firstFile, true); - var initialState = previousLineState; + writer.WriteStringUTF8NullTerminated(directoryName); + } + // empty string + writer.WriteU8(0); - uint maxDeltaAddressPerSpecialCode; - byte maxOperationAdvance = (byte) ((255 - OpCodeBase) / LineRange); - if (Version >= 4) - { - maxDeltaAddressPerSpecialCode = (uint)maxOperationAdvance / MaximumOperationsPerInstruction; - } - else + // Write filenames + foreach (var fileName in FileNames) + { + writer.WriteStringUTF8NullTerminated(fileName.Name); + + uint directoryIndex = 0; + if (fileName.Directory != null) { - maxDeltaAddressPerSpecialCode = maxOperationAdvance; + directoryIndex = _directoryNameToIndex[fileName.Directory]; } - maxDeltaAddressPerSpecialCode *= MinimumInstructionLength; - - bool hasSetAddress; - foreach (var lineSequence in _lineSequences) - { - var lines = lineSequence.Lines; + writer.WriteULEB128(directoryIndex); + writer.WriteULEB128(fileName.Time); + writer.WriteULEB128(fileName.Size); + } + // empty string + writer.WriteU8(0); - hasSetAddress = false; + var headSizeWritten = writer.Position - startOfHeader; + Debug.Assert(HeaderLength == headSizeWritten, $"Expected Header Length: {HeaderLength} != Written Header Length: {headSizeWritten}"); + + WriteDebugLineOpCodes(writer, OpCodeBase); - for (var lineIndex = 0; lineIndex < lines.Count; lineIndex++) - { - 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.Offset == writer.Offset, $"Expected Debug Line Offset: {debugLine.Offset} != Written Offset: {writer.Offset}"); - - // DW_LNS_set_column - if (deltaColumn != 0) - { - writer.WriteU8(DwarfNative.DW_LNS_set_column); - writer.WriteULEB128(debugLine.Column); - } + 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; + 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; - // 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); - } - } + bool hasSetAddress; - // 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; - } + foreach (var lineSequence in _lineSequences) + { + var lines = lineSequence.Lines; - // DW_LNS_set_basic_block - if (isBasicBlockChanged && debugLine.IsBasicBlock) - { - writer.WriteU8(DwarfNative.DW_LNS_set_basic_block); - } + hasSetAddress = false; - // DW_LNS_set_prologue_end - if (isPrologueEndChanged && debugLine.IsPrologueEnd) - { - writer.WriteU8(DwarfNative.DW_LNS_set_prologue_end); - } + for (var lineIndex = 0; lineIndex < lines.Count; lineIndex++) + { + 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); + } - // DW_LNS_set_epilogue_begin - if (isEpilogueBeginChanged && debugLine.IsEpilogueBegin) + // DW_LNS_set_file or DW_LNE_define_file + if (fileNameChanged) + { + var fileName = debugLine.File; + if (fileName is null) { - writer.WriteU8(DwarfNative.DW_LNS_set_epilogue_begin); + throw new InvalidOperationException("File name cannot be null"); } - // DW_LNS_set_isa - if (isaChanged) + // DW_LNS_set_file + if (_fileNameToIndex.TryGetValue(fileName, out var fileIndex)) { - writer.WriteU8(DwarfNative.DW_LNS_set_isa); - writer.WriteULEB128(debugLine.Isa); + writer.WriteU8(DwarfNative.DW_LNS_set_file); + writer.WriteULEB128(fileIndex); } - - // DW_LNE_set_discriminator - if (isDiscriminatorChanged) + else { + // DW_LNE_define_file writer.WriteU8(0); - writer.WriteULEB128(1 + DwarfHelper.SizeOfULEB128(debugLine.Discriminator)); - writer.WriteU8(DwarfNative.DW_LNE_set_discriminator); - writer.WriteULEB128(debugLine.Discriminator); - } + uint dirIndex = fileName.Directory != null && _directoryNameToIndex.ContainsKey(fileName.Directory) ? _directoryNameToIndex[fileName.Directory] : 0; - // DW_LNS_negate_stmt - if (isStatementChanged) - { - writer.WriteU8(DwarfNative.DW_LNS_negate_stmt); + 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); } + } - bool isEndOfSequence = lineIndex + 1 == lines.Count; - bool canEncodeSpecial = !isEndOfSequence; - bool canEncodeLineInSpecialCode = canEncodeSpecial && deltaLine >= LineBase && deltaLine < LineBase + LineRange; + // 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; + } - bool operationAdvancedEncoded = false; + // DW_LNS_set_basic_block + if (isBasicBlockChanged && debugLine.IsBasicBlock) + { + writer.WriteU8(DwarfNative.DW_LNS_set_basic_block); + } - // 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_set_prologue_end + if (isPrologueEndChanged && debugLine.IsPrologueEnd) + { + writer.WriteU8(DwarfNative.DW_LNS_set_prologue_end); + } - // 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_advance_line); - writer.WriteILEB128(deltaLine); - deltaLine = 0; - } - } + // DW_LNS_set_epilogue_begin + if (isEpilogueBeginChanged && debugLine.IsEpilogueBegin) + { + writer.WriteU8(DwarfNative.DW_LNS_set_epilogue_begin); + } + // DW_LNS_set_isa + if (isaChanged) + { + writer.WriteU8(DwarfNative.DW_LNS_set_isa); + writer.WriteULEB128(debugLine.Isa); + } - if (deltaAddress > maxDeltaAddressPerSpecialCode && deltaAddress <= (2U * maxDeltaAddressPerSpecialCode)) - { - ulong deltaAddressSpecialOpCode255; + // 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); + } - if (Version >= 4) - { - deltaAddressSpecialOpCode255 = (((ulong) previousLineState.OperationIndex + maxOperationAdvance) / MaximumOperationsPerInstruction); - deltaOperationIndex = debugLine.OperationIndex - (byte) ((previousLineState.OperationIndex + maxOperationAdvance) % MaximumOperationsPerInstruction); - } - else - { - deltaAddressSpecialOpCode255 = maxOperationAdvance; - deltaOperationIndex = 0; - } + // DW_LNS_negate_stmt + if (isStatementChanged) + { + writer.WriteU8(DwarfNative.DW_LNS_negate_stmt); + } - Debug.Assert(deltaAddressSpecialOpCode255 * MinimumInstructionLength < deltaAddress); - deltaAddress -= deltaAddressSpecialOpCode255 * MinimumInstructionLength; + bool isEndOfSequence = lineIndex + 1 == lines.Count; + bool canEncodeSpecial = !isEndOfSequence; + bool canEncodeLineInSpecialCode = canEncodeSpecial && deltaLine >= LineBase && deltaLine < LineBase + LineRange; - writer.WriteU8(DwarfNative.DW_LNS_const_add_pc); - } + bool operationAdvancedEncoded = false; - var operation_advance = deltaAddress * MaximumOperationsPerInstruction / MinimumInstructionLength + debugLine.OperationIndex; + // 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; + } - bool canEncodeAddressInSpecialCode = false; - ulong opcode = 256; - if (canEncodeSpecial && (operation_advance > 0 || deltaOperationIndex != 0 || deltaLine != 0)) + // DW_LNS_advance_line + // In case we can't encode the line advance via special code + if (!canEncodeLineInSpecialCode) + { + if (deltaLine != 0) { - opcode = operation_advance * LineRange + opCodeBase + (ulong) (deltaLine - LineBase); - if (opcode > 255) - { - if (deltaLine != 0) - { - opcode = opCodeBase + (ulong) (deltaLine - LineBase); - } - } - else - { - canEncodeAddressInSpecialCode = true; - } + writer.WriteU8(DwarfNative.DW_LNS_advance_line); + writer.WriteILEB128(deltaLine); + deltaLine = 0; } + } + - if (!operationAdvancedEncoded && !canEncodeAddressInSpecialCode) + if (deltaAddress > maxDeltaAddressPerSpecialCode && deltaAddress <= (2U * maxDeltaAddressPerSpecialCode)) + { + ulong deltaAddressSpecialOpCode255; + + if (Version >= 4) { - if (deltaAddress > 0 || deltaOperationIndex != 0) - { - writer.WriteU8(DwarfNative.DW_LNS_advance_pc); - writer.WriteULEB128(operation_advance); - } + deltaAddressSpecialOpCode255 = (((ulong) previousLineState.OperationIndex + maxOperationAdvance) / MaximumOperationsPerInstruction); + deltaOperationIndex = debugLine.OperationIndex - (byte) ((previousLineState.OperationIndex + maxOperationAdvance) % MaximumOperationsPerInstruction); } - - // Special opcode - if (opcode <= 255) + else { - writer.WriteU8((byte) opcode); - debugLineState.SpecialReset(); - hasGeneratedRow = true; + deltaAddressSpecialOpCode255 = maxOperationAdvance; + deltaOperationIndex = 0; } - if (isEndOfSequence) - { - writer.WriteU8(0); - writer.WriteULEB128(1); - writer.WriteU8(DwarfNative.DW_LNE_end_sequence); + Debug.Assert(deltaAddressSpecialOpCode255 * MinimumInstructionLength < deltaAddress); + deltaAddress -= deltaAddressSpecialOpCode255 * MinimumInstructionLength; - hasGeneratedRow = true; + writer.WriteU8(DwarfNative.DW_LNS_const_add_pc); + } + + var operation_advance = deltaAddress * MaximumOperationsPerInstruction / MinimumInstructionLength + debugLine.OperationIndex; - hasSetAddress = false; - previousLineState = initialState; - previousLineState.Reset(firstFile, true); + 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) + { + if (deltaLine != 0) + { + opcode = opCodeBase + (ulong) (deltaLine - LineBase); + } } else { - previousLineState = debugLineState; + canEncodeAddressInSpecialCode = true; } + } - if (!hasGeneratedRow) + if (!operationAdvancedEncoded && !canEncodeAddressInSpecialCode) + { + if (deltaAddress > 0 || deltaOperationIndex != 0) { - writer.WriteU8(DwarfNative.DW_LNS_copy); + writer.WriteU8(DwarfNative.DW_LNS_advance_pc); + writer.WriteULEB128(operation_advance); } + } + + // Special opcode + if (opcode <= 255) + { + writer.WriteU8((byte) opcode); + debugLineState.SpecialReset(); + hasGeneratedRow = true; + } + + if (isEndOfSequence) + { + writer.WriteU8(0); + writer.WriteULEB128(1); + writer.WriteU8(DwarfNative.DW_LNE_end_sequence); + + hasGeneratedRow = true; - Debug.Assert(debugLine.Size == writer.Offset - debugLine.Offset, $"Expected Debug Line Size: {debugLine.Size} != Written Size: {writer.Offset - debugLine.Offset}"); + hasSetAddress = false; + previousLineState = initialState; + previousLineState.Reset(firstFile, true); } + else + { + previousLineState = debugLineState; + } + + 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; - - uint maxDeltaAddressPerSpecialCode; - byte maxOperationAdvance = (byte)((255 - OpCodeBase) / LineRange); - if (Version >= 4) - { - maxDeltaAddressPerSpecialCode = (uint)maxOperationAdvance / MaximumOperationsPerInstruction; - } - else - { - maxDeltaAddressPerSpecialCode = maxOperationAdvance; - } - maxDeltaAddressPerSpecialCode *= MinimumInstructionLength; + maxDeltaAddressPerSpecialCode = (uint)maxOperationAdvance / MaximumOperationsPerInstruction; + } + else + { + maxDeltaAddressPerSpecialCode = maxOperationAdvance; + } + maxDeltaAddressPerSpecialCode *= MinimumInstructionLength; - bool hasSetAddress; + bool hasSetAddress; - foreach (var lineSequence in _lineSequences) - { - var lines = lineSequence.Lines; + foreach (var lineSequence in _lineSequences) + { + var lines = lineSequence.Lines; - lineSequence.Offset = Offset + sizeOf; - hasSetAddress = false; + lineSequence.Position = Position + sizeOf; + hasSetAddress = false; - for (var lineIndex = 0; lineIndex < lines.Count; lineIndex++) + for (var lineIndex = 0; lineIndex < lines.Count; lineIndex++) + { + 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 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.Offset = Offset + 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)); - } - - // DW_LNS_set_file or DW_LNE_define_file - if (fileNameChanged) - { - var fileName = debugLine.File; + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_column); + sizeOf += DwarfHelper.SizeOfULEB128(debugLine.Column); //writer.WriteLEB128(debugLine.Column)); + } - // 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; - } - } + // DW_LNS_set_file or DW_LNE_define_file + if (fileNameChanged) + { + var fileName = debugLine.File; - // DW_LNS_copy - if (isBasicBlockChanged && !debugLine.IsBasicBlock || - isPrologueEndChanged && !debugLine.IsPrologueEnd || - isEpilogueBeginChanged && !debugLine.IsEpilogueBegin) + if (fileName is null) { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_copy); - isDiscriminatorChanged = debugLine.Discriminator != 0; - hasGeneratedRow = true; + throw new InvalidOperationException("File name cannot be null"); } - // DW_LNS_set_basic_block - if (isBasicBlockChanged && debugLine.IsBasicBlock) + // DW_LNS_set_file + if (_fileNameToIndex.TryGetValue(fileName, out var fileIndex)) { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_basic_block); + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_file); + sizeOf += DwarfHelper.SizeOfULEB128(fileIndex); // writer.WriteLEB128(fileIndex); } - - // DW_LNS_set_prologue_end - if (isPrologueEndChanged && debugLine.IsPrologueEnd) + else { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_prologue_end); - } + // DW_LNE_define_file + sizeOf += 1; // writer.WriteU8(0); + uint dirIndex = fileName.Directory != null && _directoryNameToIndex.ContainsKey(fileName.Directory) ? _directoryNameToIndex[fileName.Directory] : 0; - // DW_LNS_set_epilogue_begin - if (isEpilogueBeginChanged && debugLine.IsEpilogueBegin) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_epilogue_begin); - } + 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_isa - if (isaChanged) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_isa); - sizeOf += DwarfHelper.SizeOfULEB128(debugLine.Isa); // writer.WriteLEB128(debugLine.Isa); + sizeOf += DwarfHelper.SizeOfULEB128(sizeOfInlineFileName); + sizeOf += sizeOfInlineFileName; } + } - // 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_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_negate_stmt - if (isStatementChanged) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_negate_stmt); - } + // DW_LNS_set_basic_block + if (isBasicBlockChanged && debugLine.IsBasicBlock) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_basic_block); + } - bool isEndOfSequence = lineIndex + 1 == lines.Count; - bool canEncodeSpecial = !isEndOfSequence; - bool canEncodeLineInSpecialCode = canEncodeSpecial && deltaLine >= LineBase && deltaLine < LineBase + LineRange; - bool operationAdvancedEncoded = false; + // DW_LNS_set_prologue_end + if (isPrologueEndChanged && debugLine.IsPrologueEnd) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_prologue_end); + } - 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; + // DW_LNS_set_epilogue_begin + if (isEpilogueBeginChanged && debugLine.IsEpilogueBegin) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_epilogue_begin); + } - if (Version >= 4) - { - deltaAddressSpecialOpCode255 = (((ulong) previousLineState.OperationIndex + maxOperationAdvance) / MaximumOperationsPerInstruction); - deltaOperationIndex = debugLine.OperationIndex - (byte) ((previousLineState.OperationIndex + maxOperationAdvance) % MaximumOperationsPerInstruction); - } - else - { - deltaAddressSpecialOpCode255 = maxOperationAdvance; - deltaOperationIndex = 0; - } + // DW_LNS_set_isa + if (isaChanged) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_set_isa); + sizeOf += DwarfHelper.SizeOfULEB128(debugLine.Isa); // writer.WriteLEB128(debugLine.Isa); + } - Debug.Assert(deltaAddressSpecialOpCode255 * MinimumInstructionLength < deltaAddress); - deltaAddress -= deltaAddressSpecialOpCode255 * MinimumInstructionLength; + // 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); + } - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_const_add_pc); - } + // DW_LNS_negate_stmt + if (isStatementChanged) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_negate_stmt); + } - // DW_LNS_advance_line - if (!canEncodeLineInSpecialCode) - { - if (deltaLine != 0) - { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_advance_line); - sizeOf += DwarfHelper.SizeOfILEB128(deltaLine); // writer.WriteSignedLEB128(deltaLine); - deltaLine = 0; - } - } + bool isEndOfSequence = lineIndex + 1 == lines.Count; + bool canEncodeSpecial = !isEndOfSequence; + bool canEncodeLineInSpecialCode = canEncodeSpecial && deltaLine >= LineBase && deltaLine < LineBase + LineRange; + bool operationAdvancedEncoded = false; - var operation_advance = deltaAddress * MaximumOperationsPerInstruction / MinimumInstructionLength + debugLine.OperationIndex; + 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; - bool canEncodeAddress = false; - ulong opcode = 256; - if (canEncodeSpecial && (operation_advance > 0 || deltaOperationIndex > 0 || deltaLine != 0)) + if (Version >= 4) { - opcode = operation_advance * LineRange + opCodeBase + (ulong) (deltaLine - LineBase); - if (opcode > 255) - { - if (deltaLine != 0) - { - opcode = opCodeBase + (ulong) (deltaLine - LineBase); - } - } - else - { - canEncodeAddress = true; - } + deltaAddressSpecialOpCode255 = (((ulong) previousLineState.OperationIndex + maxOperationAdvance) / MaximumOperationsPerInstruction); + deltaOperationIndex = debugLine.OperationIndex - (byte) ((previousLineState.OperationIndex + maxOperationAdvance) % MaximumOperationsPerInstruction); } - - 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); - } + deltaAddressSpecialOpCode255 = maxOperationAdvance; + deltaOperationIndex = 0; } - // Special opcode - if (opcode <= 255) + Debug.Assert(deltaAddressSpecialOpCode255 * MinimumInstructionLength < deltaAddress); + deltaAddress -= deltaAddressSpecialOpCode255 * MinimumInstructionLength; + + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_const_add_pc); + } + + // DW_LNS_advance_line + if (!canEncodeLineInSpecialCode) + { + if (deltaLine != 0) { - sizeOf += 1; // writer.WriteU8((byte)opcode); - debugLineState.SpecialReset(); - hasGeneratedRow = true; + 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; - if (isEndOfSequence) + 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) { - 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; + if (deltaLine != 0) + { + opcode = opCodeBase + (ulong) (deltaLine - LineBase); + } } else { - previousLineState = debugLineState; + canEncodeAddress = true; } + } - if (!hasGeneratedRow) + if (!operationAdvancedEncoded && !canEncodeAddress) + { + if (deltaAddress > 0 || deltaOperationIndex > 0) { - sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_copy); + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_advance_pc); + sizeOf += DwarfHelper.SizeOfULEB128(operation_advance); // writer.WriteLEB128(operation_advance); } + } + + // Special opcode + if (opcode <= 255) + { + sizeOf += 1; // writer.WriteU8((byte)opcode); + debugLineState.SpecialReset(); + hasGeneratedRow = true; + } + + 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 = Offset + sizeOf - debugLine.Offset; + if (!hasGeneratedRow) + { + sizeOf += 1; // writer.WriteU8(DwarfNative.DW_LNS_copy); } - lineSequence.Size = Offset + sizeOf - lineSequence.Offset; + + 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 a259852..a81ef42 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSection.cs @@ -1,90 +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.Collections.Generic; using System.Diagnostics; +using System.Text; +using LibObjectFile.Collections; -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; + private readonly ObjectList _tables; - public DwarfLineSection() - { - _tables = new List(); - } + public DwarfLineSection() + { + _tables = new ObjectList(this); + } - public IReadOnlyList LineTables => _tables; + public ObjectList LineTables => _tables; - public void AddLineProgramTable(DwarfLineProgramTable line) + public override void Read(DwarfReader reader) + { + while (reader.Position < reader.Length) { - _tables.Add(this, line); + var programTable = new DwarfLineProgramTable(); + programTable.Position = reader.Position; + programTable.Read(reader); + _tables.Add(programTable); } + } - public void RemoveLineProgramTable(DwarfLineProgramTable line) + public override void Verify(DwarfVerifyContext context) + { + foreach (var dwarfLineProgramTable in _tables) { - _tables.Remove(this, line); + dwarfLineProgramTable.Verify(context); } + } - public DwarfLineProgramTable RemoveLineProgramTableAt(int index) - { - return _tables.RemoveAt(this, index); - } + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + ulong sizeOf = 0; - protected override void Read(DwarfReader reader) + foreach (var dwarfLineProgramTable in _tables) { - while (reader.Offset < reader.Length) - { - var programTable = new DwarfLineProgramTable(); - programTable.Offset = reader.Offset; - programTable.ReadInternal(reader); - AddLineProgramTable(programTable); - } + dwarfLineProgramTable.Position = Position + sizeOf; + dwarfLineProgramTable.UpdateLayout(context); + sizeOf += dwarfLineProgramTable.Size; } + Size = sizeOf; + } - public override void Verify(DiagnosticBag diagnostics) - { - base.Verify(diagnostics); - - foreach (var dwarfLineProgramTable in _tables) - { - dwarfLineProgramTable.Verify(diagnostics); - } - } + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; - protected override void UpdateLayout(DwarfLayoutContext layoutContext) + foreach (var dwarfLineProgramTable in _tables) { - ulong sizeOf = 0; - - foreach (var dwarfLineProgramTable in _tables) - { - dwarfLineProgramTable.Offset = Offset + sizeOf; - dwarfLineProgramTable.UpdateLayoutInternal(layoutContext); - sizeOf += dwarfLineProgramTable.Size; - } - Size = sizeOf; + dwarfLineProgramTable.Write(writer); } - protected override void Write(DwarfWriter writer) - { - var startOffset = writer.Offset; - - 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() - { - return $"Section .debug_line, Entries: {_tables.Count}"; - } + protected override bool PrintMembers(StringBuilder builder) + { + 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/DwarfLineSequence.cs b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs index 8846b71..361cd9b 100644 --- a/src/LibObjectFile/Dwarf/DwarfLineSequence.cs +++ b/src/LibObjectFile/Dwarf/DwarfLineSequence.cs @@ -1,71 +1,61 @@ -// 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; using System.Diagnostics; +using LibObjectFile.Collections; -namespace LibObjectFile.Dwarf -{ - /// - /// A sequence of - /// - [DebuggerDisplay("Count = {Lines.Count,nq}")] - public class DwarfLineSequence : DwarfObject, IEnumerable - { - private readonly List _lines; +namespace LibObjectFile.Dwarf; - public DwarfLineSequence() - { - _lines = new List(); - } +/// +/// A sequence of +/// +[DebuggerDisplay("Count = {Lines.Count,nq}")] +public class DwarfLineSequence : DwarfObject, IEnumerable +{ + private readonly ObjectList _lines; - public IReadOnlyList Lines => _lines; + public DwarfLineSequence() + { + _lines = new ObjectList(this); + } - public void Add(DwarfLine line) - { - _lines.Add(this, line); - } + public ObjectList Lines => _lines; - public void Remove(DwarfLine line) - { - _lines.Remove(this, line); - } + public void Add(DwarfLine line) + { + _lines.Add(line); + } - public DwarfLine RemoveAt(int index) - { - return _lines.RemoveAt(this, index); - } - - protected override void UpdateLayout(DwarfLayoutContext layoutContext) - { - // This is implemented in DwarfLineSection - } + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + // 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 aebdab8..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 e6a9f46..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 d7ab162..b6c1934 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationList.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationList.cs @@ -1,107 +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.Text; using System.Collections.Generic; +using System.Text; +using LibObjectFile.Collections; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +public class DwarfLocationList : DwarfContainer { + private readonly ObjectList _locationListEntries; - public class DwarfLocationList : DwarfContainer + public DwarfLocationList() { - private readonly List _locationListEntries; + _locationListEntries = new ObjectList(this); + } - public DwarfLocationList() - { - _locationListEntries = new List(); - } + public ObjectList LocationListEntries => _locationListEntries; - public IReadOnlyList LocationListEntries => _locationListEntries; + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + var endOffset = Position; - public void AddLocationListEntry(DwarfLocationListEntry locationListEntry) + foreach (var locationListEntry in _locationListEntries) { - _locationListEntries.Add(this, locationListEntry); + locationListEntry.Position = endOffset; + locationListEntry.UpdateLayout(context); + endOffset += locationListEntry.Size; } - public void RemoveLocationList(DwarfLocationListEntry locationListEntry) - { - _locationListEntries.Remove(this, locationListEntry); - } + // End of list + endOffset += 2 * DwarfHelper.SizeOfUInt(context.CurrentUnit!.AddressSize); - public DwarfLocationListEntry RemoveLocationListEntryAt(int index) - { - return _locationListEntries.RemoveAt(this, index); - } + Size = endOffset - Position; + } - protected override void UpdateLayout(DwarfLayoutContext layoutContext) + public override void Read(DwarfReader reader) + { + reader.OffsetToLocationList.Add(reader.Position, this); + + while (reader.Position < reader.Length) { - var endOffset = Offset; + var locationListEntry = new DwarfLocationListEntry(); + locationListEntry.Read(reader); - foreach (var locationListEntry in _locationListEntries) + if (locationListEntry.Start == 0 && locationListEntry.End == 0) { - locationListEntry.Offset = endOffset; - locationListEntry.UpdateLayoutInternal(layoutContext); - endOffset += locationListEntry.Size; + // End of list + return; } - // End of list - endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.AddressSize); - - Size = endOffset - Offset; + _locationListEntries.Add(locationListEntry); } + } - protected override void Read(DwarfReader reader) + public override void Write(DwarfWriter writer) + { + foreach (var locationListEntry in _locationListEntries) { - reader.OffsetToLocationList.Add(reader.Offset, this); - - while (reader.Offset < reader.Length) - { - var locationListEntry = new DwarfLocationListEntry(); - locationListEntry.ReadInternal(reader); - - if (locationListEntry.Start == 0 && locationListEntry.End == 0) - { - // End of list - return; - } - - _locationListEntries.Add(locationListEntry); - } + locationListEntry.Write(writer); } - protected override void Write(DwarfWriter writer) + // End of list + writer.WriteUInt(0); + writer.WriteUInt(0); + } + + protected override bool PrintMembers(StringBuilder builder) + { + for (int i = 0; i < _locationListEntries.Count; i++) { - foreach (var locationListEntry in _locationListEntries) + if (i == 3) { - locationListEntry.WriteInternal(writer); + builder.Append(", ..."); + break; } - - // End of list - writer.WriteUInt(0); - writer.WriteUInt(0); - } - - public override string ToString() - { - var builder = new StringBuilder(); - - for (int i = 0; i < _locationListEntries.Count; i++) + 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 829acb2..561b513 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationListEntry.cs @@ -1,85 +1,87 @@ -// 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.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 = Offset; + Expression = new DwarfExpression(); + Expression.ReadInternal(reader, inLocationSection: true); + } - endOffset += 2 * DwarfHelper.SizeOfUInt(layoutContext.CurrentUnit.AddressSize); - if (Expression != null) - { - Expression.Offset = endOffset; - Expression.UpdateLayoutInternal(layoutContext, inLocationSection: true); - endOffset += Expression.Size; - } + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + var endOffset = Position; - Size = endOffset - Offset; + endOffset += 2 * DwarfHelper.SizeOfUInt(context.CurrentUnit!.AddressSize); + if (Expression != null) + { + Expression.Position = endOffset; + Expression.UpdateLayout(context, 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 e2057af..1ca11bc 100644 --- a/src/LibObjectFile/Dwarf/DwarfLocationSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfLocationSection.cs @@ -1,89 +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.Collections.Generic; using System.Diagnostics; +using System.Text; +using LibObjectFile.Collections; -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 ObjectList _locationLists; + + public DwarfLocationSection() { - private readonly List _locationLists; + _locationLists = new ObjectList(this); - public DwarfLocationSection() - { - _locationLists = new List(); - } + } - public IReadOnlyList LocationLists => _locationLists; + public ObjectList LocationLists => _locationLists; - public void AddLocationList(DwarfLocationList locationList) + public override void Read(DwarfReader reader) + { + while (reader.Position < reader.Length) { - _locationLists.Add(this, locationList); + var locationList = new DwarfLocationList(); + locationList.Position = reader.Position; + locationList.Read(reader); + _locationLists.Add(locationList); } + } - public void RemoveLocationList(DwarfLocationList locationList) + public override void Verify(DwarfVerifyContext context) + { + foreach (var locationList in _locationLists) { - _locationLists.Remove(this, locationList); + locationList.Verify(context); } + } - public DwarfLocationList RemoveLineProgramTableAt(int index) - { - return _locationLists.RemoveAt(this, index); - } + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + ulong sizeOf = 0; - protected override void Read(DwarfReader reader) + foreach (var locationList in _locationLists) { - while (reader.Offset < reader.Length) - { - var locationList = new DwarfLocationList(); - locationList.Offset = reader.Offset; - locationList.ReadInternal(reader); - AddLocationList(locationList); - } + locationList.Position = Position + sizeOf; + locationList.UpdateLayout(context); + sizeOf += locationList.Size; } + Size = sizeOf; + } - public override void Verify(DiagnosticBag diagnostics) - { - base.Verify(diagnostics); - - foreach (var locationList in _locationLists) - { - locationList.Verify(diagnostics); - } - } + public override void Write(DwarfWriter writer) + { + var startOffset = writer.Position; - protected override void UpdateLayout(DwarfLayoutContext layoutContext) + foreach (var locationList in _locationLists) { - ulong sizeOf = 0; - - foreach (var locationList in _locationLists) - { - locationList.Offset = Offset + sizeOf; - locationList.UpdateLayoutInternal(layoutContext); - sizeOf += locationList.Size; - } - Size = sizeOf; + locationList.Write(writer); } - protected override void Write(DwarfWriter writer) - { - var startOffset = writer.Offset; - - 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() - { - return $"Section .debug_loc, Entries: {_locationLists.Count}"; - } + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"Section .debug_loc, Entries: {_locationLists.Count}, "); + base.PrintMembers(builder); + return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/Dwarf/DwarfObject.cs b/src/LibObjectFile/Dwarf/DwarfObject.cs index a66f7b8..9044976 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() + public DwarfFile? GetParentFile() { - var check = (ObjectFileNodeBase)this; + var check = (ObjectElement?)this; while (check != null) { if (check is DwarfFile dwarfFile) return dwarfFile; @@ -20,9 +20,9 @@ public DwarfFile GetParentFile() return null; } - public DwarfUnit GetParentUnit() + public DwarfUnit? GetParentUnit() { - var check = (ObjectFileNodeBase)this; + var check = (ObjectElement?)this; while (check != null) { if (check is DwarfUnit dwarfUnit) return dwarfUnit; @@ -31,9 +31,9 @@ public DwarfUnit GetParentUnit() return null; } - public DwarfSection GetParentSection() + public DwarfSection? GetParentSection() { - var check = (ObjectFileNodeBase)this; + var check = (ObjectElement?)this; while (check != null) { if (check is DwarfSection dwarfSection) return dwarfSection; @@ -43,9 +43,9 @@ public DwarfSection GetParentSection() } } -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(ObjectElement parent) { if (!(parent is TContainer)) { @@ -53,38 +53,14 @@ protected override void ValidateParent(ObjectFileNodeBase parent) } } - /// /// Gets the containing . Might be null if this section or segment /// 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; } - - 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 241d89b..b1547c2 100644 --- a/src/LibObjectFile/Dwarf/DwarfOperation.cs +++ b/src/LibObjectFile/Dwarf/DwarfOperation.cs @@ -1,1172 +1,1172 @@ -// 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 LibObjectFile.Diagnostics; -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) { - Offset = reader.Offset; - 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: + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + var endOffset = Position; + // 1 byte per opcode + endOffset += 1; + + switch (Kind.Value) + { + case DwarfOperationKind.Addr: + endOffset += DwarfHelper.SizeOfUInt(context.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(context.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; + context.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 + { + context.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 (context.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(context.CurrentUnit.AddressSize); + } + else + { + endOffset += DwarfHelper.SizeOfUInt(context.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(context); + endOffset += DwarfHelper.SizeOfULEB128(expr.Size); + } + else { - Operand1.U64 = ReadEncodedValue(reader, kind, out var sizeOfEncodedValue); - Operand2.U64 = sizeOfEncodedValue; - break; + context.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(context); + endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, context.CurrentUnit!.AddressSize); + break; } - // Store the size of the current op - Size = reader.Offset - Offset; + 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(context); + 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(context); + break; + } + + case DwarfOperationKind.Convert: + case DwarfOperationKind.GNUConvert: + case DwarfOperationKind.Reinterpret: + case DwarfOperationKind.GNUReinterpret: + endOffset += SizeOfDIEReference(context); + break; + + case DwarfOperationKind.GNUEncodedAddr: + endOffset += SizeOfEncodedValue(Kind, Operand1.U64, (byte)Operand2.U64, context.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 = Offset; - // 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.Offset = 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 - Offset; + 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.Offset < Offset) + 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.Offset); + 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 = Offset; - Debug.Assert(startOpOffset == Offset); + 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)Offset + 2 - (long)((DwarfOperation)Operand0).Offset)); - 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).Offset); - break; - - case DwarfOperationKind.Call4: - writer.WriteU32((uint)((DwarfDIE)Operand0).Offset); - break; - - case DwarfOperationKind.CallRef: - writer.WriteUInt(((DwarfDIE)Operand0).Offset); - 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).Offset; - // 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).Offset); - 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).Offset); - 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).Offset); - break; - } - - case DwarfOperationKind.Convert: - case DwarfOperationKind.GNUConvert: - case DwarfOperationKind.Reinterpret: - case DwarfOperationKind.GNUReinterpret: - writer.WriteULEB128(((DwarfDIE)Operand0).Offset); - 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.Offset - 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 7f91c06..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 a1b1eb5..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.Offset})"); + 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.Offset: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($" 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.Offset: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.Offset: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.Offset: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 306939b..6de940a 100644 --- a/src/LibObjectFile/Dwarf/DwarfReader.cs +++ b/src/LibObjectFile/Dwarf/DwarfReader.cs @@ -4,167 +4,170 @@ using System.Collections.Generic; using System.Diagnostics; +using LibObjectFile.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(); - } + KeepOriginalStreamForSubStreams = 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 KeepOriginalStreamForSubStreams { 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) + internal void PushLineProgramTable(DwarfLineProgramTable lineTable) + { + var dieWithLineProgramTable = CurrentDIE; + if (_stackWithLineProgramTable.Count > 0 && ReferenceEquals(_stackWithLineProgramTable.Peek(), dieWithLineProgramTable)) { - _registeredDIEPerCompilationUnit.Add(die.Offset - CurrentUnit.Offset, die); - _registeredDIEPerSection.Add(die.Offset, die); - _stack.Push(die); + return; } - internal void PushLineProgramTable(DwarfLineProgramTable lineTable) + if (dieWithLineProgramTable != null) { - var dieWithLineProgramTable = CurrentDIE; - if (_stackWithLineProgramTable.Count > 0 && ReferenceEquals(_stackWithLineProgramTable.Peek(), dieWithLineProgramTable)) - { - return; - } - _stackWithLineProgramTable.Push(dieWithLineProgramTable); dieWithLineProgramTable.CurrentLineProgramTable = lineTable; } + } - internal void PopDIE() + internal void PopDIE() + { + var die = _stack.Pop(); + if (die.CurrentLineProgramTable != null) { - var die = _stack.Pop(); - if (die.CurrentLineProgramTable != null) - { - var dieWithProgramLineTable = _stackWithLineProgramTable.Pop(); - Debug.Assert(ReferenceEquals(die, dieWithProgramLineTable)); - dieWithProgramLineTable.CurrentLineProgramTable = null; - } + var dieWithProgramLineTable = _stackWithLineProgramTable.Pop(); + Debug.Assert(ReferenceEquals(die, dieWithProgramLineTable)); + dieWithProgramLineTable.CurrentLineProgramTable = null; } + } - internal void ClearResolveAttributeReferenceWithinCompilationUnit() - { - _registeredDIEPerCompilationUnit.Clear(); - _unresolvedDIECompilationUnitReference.Clear(); - } + 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)) { - if (_registeredDIEPerSection.TryGetValue(dieRef.Offset, out var die)) + dieRef.Resolved = die; + dieRef.Resolver(ref dieRef); + } + else + { + 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 049ebd6..b369cfe 100644 --- a/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs +++ b/src/LibObjectFile/Dwarf/DwarfReaderWriter.cs @@ -3,171 +3,169 @@ // See the license.txt file in the project root for more information. using System; -using System.IO; +using LibObjectFile.Diagnostics; -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(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 97d3bf3..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 0908472..3d09422 100644 --- a/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs +++ b/src/LibObjectFile/Dwarf/DwarfRelocatableSection.cs @@ -4,63 +4,61 @@ using System; using System.Collections.Generic; -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 b2f33bf..38e0d04 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(ObjectElement 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 3f92cef..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..16d8579 100644 --- a/src/LibObjectFile/Dwarf/DwarfStreamExtensions.cs +++ b/src/LibObjectFile/Dwarf/DwarfStreamExtensions.cs @@ -4,94 +4,94 @@ using System; using System.IO; +using LibObjectFile.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 f2a9abf..6a41620 100644 --- a/src/LibObjectFile/Dwarf/DwarfStringTable.cs +++ b/src/LibObjectFile/Dwarf/DwarfStringTable.cs @@ -1,89 +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; using System.Collections.Generic; -using System.Diagnostics; using System.IO; +using LibObjectFile.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) - { - Stream = stream ?? throw new ArgumentNullException(nameof(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.Offset - Offset; - } + 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); + } + + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + 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 5681d85..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 03bfa44..102e284 100644 --- a/src/LibObjectFile/Dwarf/DwarfUnit.cs +++ b/src/LibObjectFile/Dwarf/DwarfUnit.cs @@ -1,241 +1,240 @@ -// 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 LibObjectFile.Diagnostics; -namespace LibObjectFile.Dwarf +namespace LibObjectFile.Dwarf; + +/// +/// Base class for a Dwarf Unit. +/// +public abstract class DwarfUnit : DwarfContainer { + private ObjectFileElementHolder _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 => AttachChild(this, value, ref _root, true); - } + 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 => _root.Set(this, value); + } + + /// + /// 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(ObjectElement parent) + { + if (!(parent is DwarfSection)) { - var offset = this.Offset; + throw new ArgumentException($"Parent must inherit from type {nameof(DwarfSection)}"); + } + } - // 1. unit_length - offset += DwarfHelper.SizeOfUnitLength(Is64BitEncoding); + protected override void UpdateLayoutCore(DwarfLayoutContext context) + { + 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(); + context.File.AbbreviationTable.Abbreviations.Add(Abbreviation); - Root.UpdateAbbreviationItem(layoutContext); + Root.UpdateAbbreviationItem(context); - DebugAbbreviationOffset = Abbreviation.Offset; + DebugAbbreviationOffset = Abbreviation.Position; - Root.Offset = offset; - Root.UpdateLayoutInternal(layoutContext); - offset += Root.Size; - } - - Size = offset - Offset; - UnitLength = offset - offsetAfterUnitLength; + Root.Position = offset; + Root.UpdateLayout(context); + 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.Offset == DebugAbbreviationOffset) - { - Abbreviation = abbreviation; - break; - } + Abbreviation = abbreviation; + break; } - - Root = DwarfDIE.ReadInstance(reader); - - reader.ResolveAttributeReferenceWithinCompilationUnit(); - - Size = reader.Offset - Offset; } - internal static DwarfUnit ReadInstance(DwarfReader reader, out ulong offsetEndOfUnit) - { - var startOffset = reader.Offset; + Root = DwarfDIE.ReadInstance(reader); - DwarfUnit unit = null; + reader.ResolveAttributeReferenceWithinCompilationUnit(); - // 1. unit_length - var unit_length = reader.ReadUnitLength(); + Size = reader.Position - Position; + } - offsetEndOfUnit = (ulong)reader.Offset + 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.Offset = 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.Offset; - Debug.Assert(Offset == writer.Offset); + 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.Offset; + 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.Offset - startOffset); - Debug.Assert(UnitLength == writer.Offset - 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 07a6b8f..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 d12d32f..7e83855 100644 --- a/src/LibObjectFile/Dwarf/DwarfWriter.cs +++ b/src/LibObjectFile/Dwarf/DwarfWriter.cs @@ -3,43 +3,43 @@ // See the license.txt file in the project root for more information. using System; +using LibObjectFile.Diagnostics; -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 KeepOriginalStreamForSubStreams => 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(Offset, 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..c1a3001 100644 --- a/src/LibObjectFile/Dwarf/DwarfWriterContext.cs +++ b/src/LibObjectFile/Dwarf/DwarfWriterContext.cs @@ -3,24 +3,22 @@ // See the license.txt file in the project root for more information. 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 2b2cb73..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 ca8607a..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.Offset; - 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 70347e2..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 4ae30ce..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 7d5f4b0..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 fcc6cd3..a523c6e 100644 --- a/src/LibObjectFile/Elf/ElfObject.cs +++ b/src/LibObjectFile/Elf/ElfObject.cs @@ -7,28 +7,31 @@ 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(ObjectElement 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 + 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..3105366 100644 --- a/src/LibObjectFile/Elf/ElfObjectFile.cs +++ b/src/LibObjectFile/Elf/ElfObjectFile.cs @@ -1,12 +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 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; @@ -16,10 +18,10 @@ 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; + private ElfSectionHeaderStringTable? _sectionHeaderStringTable; private readonly List _segments; public const int IdentSizeInBytes = ElfNative.EI_NIDENT; @@ -119,12 +121,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 . @@ -140,7 +142,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 @@ -165,14 +167,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) { @@ -187,13 +202,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); } } @@ -210,11 +225,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; @@ -240,14 +257,14 @@ public override unsafe void UpdateLayout(DiagnosticBag diagnostics) } var align = section.Alignment == 0 ? 1 : section.Alignment; - offset = AlignHelper.AlignToUpper(offset, align); - section.Offset = offset; + offset = AlignHelper.AlignUp(offset, align); + 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; } @@ -279,7 +296,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}"); @@ -293,7 +310,7 @@ public override 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; } @@ -310,7 +327,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); } } @@ -331,7 +348,7 @@ public void AddSegment(ElfSegment segment) } segment.Parent = this; - segment.Index = (uint)_segments.Count; + segment.Index = _segments.Count; _segments.Add(segment); } @@ -350,7 +367,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; @@ -376,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++) @@ -414,7 +431,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) @@ -453,7 +470,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) @@ -470,7 +487,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]; @@ -516,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; @@ -654,7 +671,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 +704,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 +721,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)); @@ -774,4 +791,8 @@ private static int CompareStreamIndexAndIndex(ElfSection left, ElfSection right) if (delta != 0) return delta; return left.Index.CompareTo(right.Index); } + + protected override void UpdateLayoutCore(ElfVisitorContext context) + { + } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs b/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs index 34eb6ce..095328a 100644 --- a/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs +++ b/src/LibObjectFile/Elf/ElfObjectFileExtensions.cs @@ -3,81 +3,81 @@ // See the license.txt file in the project root for more information. using System; +using LibObjectFile.Diagnostics; -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/ElfOffsetCalculationMode.cs b/src/LibObjectFile/Elf/ElfOffsetCalculationMode.cs new file mode 100644 index 0000000..a9f2ca3 --- /dev/null +++ b/src/LibObjectFile/Elf/ElfOffsetCalculationMode.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.Elf; + +/// +/// Defines the way a value is calculated. +/// +public enum ElfOffsetCalculationMode +{ + /// + /// The associated value is automatically calculated by the system. + /// + Auto, + + /// + /// The associated value is set manually. + /// + Manual, +} \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfPrinter.cs b/src/LibObjectFile/Elf/ElfPrinter.cs index f6666b3..4e9a1c7 100644 --- a/src/LibObjectFile/Elf/ElfPrinter.cs +++ b/src/LibObjectFile/Elf/ElfPrinter.cs @@ -3,815 +3,812 @@ // 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 +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.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(@"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.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}"); - } - - 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.Offset: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).Offset >= segment.Offset + && 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.Offset - segment.Offset + && (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.Offset - segment.Offset - + 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.Offset > segment.Offset + && ((section.VirtualAddress - segment.VirtualAddress + + GetSectionSize(section, segment)) - && (section.Offset - segment.Offset + <= 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..18a466a 100644 --- a/src/LibObjectFile/Elf/ElfReader.cs +++ b/src/LibObjectFile/Elf/ElfReader.cs @@ -3,58 +3,55 @@ // See the license.txt file in the project root for more information. using System; -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 KeepOriginalStreamForSubStreams => 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 ea9f764..39fa1ec 100644 --- a/src/LibObjectFile/Elf/ElfReaderOptions.cs +++ b/src/LibObjectFile/Elf/ElfReaderOptions.cs @@ -2,46 +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 +using LibObjectFile.Diagnostics; + +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 64a4b4b..d877f43 100644 --- a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs +++ b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs @@ -5,855 +5,856 @@ using System; using System.Collections.Generic; using System.IO; -using static System.Collections.Specialized.BitVector32; +using LibObjectFile.Diagnostics; -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)), - Offset =_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)), - Offset = _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.Offset = _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.Offset = _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) + if (_hasValidSectionStringTable) + { + Stream.Position = (long)ObjectFile.SectionHeaderStringTable!.Position; + ObjectFile.SectionHeaderStringTable.Read(this); + } + + 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.Offset; - 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.Offset; - 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() - { - Offset = Layout.OffsetOfProgramHeaderTable, - }; + Position = Layout.OffsetOfProgramHeaderTable, + }; - // Add the shadow section ElfProgramHeaderTable - ObjectFile.InsertSectionAt(1, programHeaderTable); - programHeaderTable.UpdateLayout(Diagnostics); + // Add the shadow section ElfProgramHeaderTable + ObjectFile.InsertSectionAt(1, programHeaderTable); + programHeaderTable.UpdateLayout(context); - if (programHeaderTable.Size > 0) + if (programHeaderTable.Size > 0) + { + fileParts.Insert(new ElfFilePart(programHeaderTable)); + } + } + + // 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.Offset > lastOffset) + var otherSection = orderedSections[i + 1]; + if (otherSection.Position < section.Position + section.Size) { - if (section.Offset > lastOffset) - { - // Create parts for the segment - fileParts.CreateParts(lastOffset + 1, section.Offset - 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.Offset + 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.Offset < section.Offset + 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}]"); - } + // 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.OffsetCalculationMode = ElfOffsetCalculationMode.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.Offset + 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.Offset + section.Size - 1; - if (segment.Offset == section.Offset && 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.Offset; - - // 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, - Offset = part.StartOffset, - Size = part.EndOffset - part.StartOffset + 1 - }; - shadowCount++; - - Stream.Position = (long)shadowSection.Offset; - 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.Offset + 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.Offset + section.Size - 1; - if (segment.Offset >= section.Offset && segment.Offset <= 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; + 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) - { - endSection = nextSection; - 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 + if (segmentEndOffset >= nextSection.Position && segmentEndOffset <= sectionEndOffset) { - segment.Range = new ElfSegmentRange(beginSection, segment.Offset - beginSection.Offset, endSection, (long)(segmentEndOffset - endSection.Offset)); + 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.OffsetCalculationMode = ElfOffsetCalculationMode.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.Offset.CompareTo(right.Offset); - 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 cbe385f..b27c344 100644 --- a/src/LibObjectFile/Elf/ElfSection.cs +++ b/src/LibObjectFile/Elf/ElfSection.cs @@ -4,195 +4,178 @@ using System; using System.Diagnostics; +using System.Text; +using LibObjectFile.Diagnostics; -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(ObjectElement 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}`, "); + base.PrintMembers(builder); + 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..fc39e07 100644 --- a/src/LibObjectFile/Elf/ElfSectionExtension.cs +++ b/src/LibObjectFile/Elf/ElfSectionExtension.cs @@ -3,152 +3,150 @@ // See the license.txt file in the project root for more information. 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 new InvalidOperationException($"Invalid Enum {sectionSpecialType.GetType()}.{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 2ffc79f..867ea2f 100644 --- a/src/LibObjectFile/Elf/ElfSectionLink.cs +++ b/src/LibObjectFile/Elf/ElfSectionLink.cs @@ -3,145 +3,146 @@ // See the license.txt file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; +using LibObjectFile.Diagnostics; -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, 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 57a809a..a363d50 100644 --- a/src/LibObjectFile/Elf/ElfSegment.cs +++ b/src/LibObjectFile/Elf/ElfSegment.cs @@ -2,139 +2,150 @@ // 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; +using System.Numerics; +using System.Text; +using LibObjectFile.Diagnostics; -namespace LibObjectFile.Elf +namespace LibObjectFile.Elf; + +/// +/// Defines a segment or program header. +/// +public sealed class ElfSegment : ElfObject { + public ElfOffsetCalculationMode OffsetCalculationMode { 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; } + + protected override void UpdateLayoutCore(ElfVisitorContext context) + { + var diagnostics = context.Diagnostics; + + if (OffsetCalculationMode == ElfOffsetCalculationMode.Auto) + { + Position = Range.Offset; + } + + if (Range.IsEmpty) { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + //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 (!BitOperations.IsPow2(alignment)) { - Offset = 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) { - 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.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}] "); + base.PrintMembers(builder); + return true; } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfSegmentFlags.cs b/src/LibObjectFile/Elf/ElfSegmentFlags.cs index ccdf795..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 e33ffb4..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.Offset + 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.Offset - BeginSection.Offset; - 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 == 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 88fdba7..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 435febb..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..5c09394 --- /dev/null +++ b/src/LibObjectFile/Elf/ElfVisitorContext.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. + +using LibObjectFile.Diagnostics; + +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..14d9efe 100644 --- a/src/LibObjectFile/Elf/ElfWriter.cs +++ b/src/LibObjectFile/Elf/ElfWriter.cs @@ -3,50 +3,47 @@ // See the license.txt file in the project root for more information. using System; -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 KeepOriginalStreamForSubStreams => 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 de3c122..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.Offset); - 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.Offset); - 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.Offset); - 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 eb276b9..fe0d240 100644 --- a/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfAlignedShadowSection.cs @@ -4,65 +4,63 @@ using System; using System.Buffers; +using LibObjectFile.Diagnostics; 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) + protected override void UpdateLayoutCore(ElfVisitorContext context) + { + var nextSectionOffset = AlignHelper.AlignUp(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(Offset, UpperAlignment); - Size = nextSectionOffset - Offset; - 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 db864d9..a8d1640 100644 --- a/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs @@ -4,79 +4,75 @@ using System; using System.IO; +using LibObjectFile.Diagnostics; -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) + protected override void UpdateLayoutCore(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 e0d8435..5898788 100644 --- a/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfBinaryShadowSection.cs @@ -2,38 +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.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; - } + protected override void UpdateLayoutCore(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 27d614c..e98b64c 100644 --- a/src/LibObjectFile/Elf/Sections/ElfCustomNote.cs +++ b/src/LibObjectFile/Elf/Sections/ElfCustomNote.cs @@ -6,102 +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 string Name { get; } + + /// + /// Gets or sets the associated descriptor data. + /// + public Stream? Descriptor { get; set; } + + /// + /// Gets or sets the type of this note. + /// + public ElfNoteTypeEx Type { get; } + + public override string GetName() { - /// - /// Gets or sets the name of this note. - /// - public string Name { 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; set; } - - public override string GetName() - { - return Name; - } + 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..7b4c743 100644 --- a/src/LibObjectFile/Elf/Sections/ElfGnuNoteABITag.cs +++ b/src/LibObjectFile/Elf/Sections/ElfGnuNoteABITag.cs @@ -2,82 +2,81 @@ // 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 +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 7976c74..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 7f79618..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. @@ -6,170 +6,162 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; +using LibObjectFile.Diagnostics; 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); + protected override unsafe void UpdateLayoutCore(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.AlignUp(size, 4); - size += (ulong)elfNote.GetDescriptorSize(); - size = AlignHelper.AlignToUpper(size, 4); + size += (ulong)elfNote.GetDescriptorSize(); + size = AlignHelper.AlignUp(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; + ElfNote? note = null; - if (reader.Options.TryCreateNote != null) - { - note = reader.Options.TryCreateNote(name, type); - } - - return note ?? new ElfCustomNote() - { - Name = name, - Type = 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 2baa9cf..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 89f92a9..f82ea06 100644 --- a/src/LibObjectFile/Elf/Sections/ElfNullSection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfNullSection.cs @@ -2,44 +2,32 @@ // 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 +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 || - Offset != 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) - { - } + 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 f1f7754..05b6efb 100644 --- a/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfProgramHeaderTable.cs @@ -2,92 +2,87 @@ // 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; -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; + protected override void UpdateLayoutCore(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.Offset); - 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.Offset); - 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 194d7af..f1c8e51 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationTable.cs @@ -4,311 +4,308 @@ using System; using System.Collections.Generic; -using System.IO; +using LibObjectFile.Diagnostics; -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)); + protected override unsafe void UpdateLayoutCore(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 0b13a44..4820b81 100644 --- a/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs +++ b/src/LibObjectFile/Elf/Sections/ElfRelocationTableExtensions.cs @@ -3,233 +3,231 @@ // See the license.txt file in the project root for more information. using System; -using System.Collections.Generic; using System.IO; -using LibObjectFile.Dwarf; +using LibObjectFile.IO; -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 da37e22..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 807e42f..e6c47a2 100644 --- a/src/LibObjectFile/Elf/Sections/ElfStringTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfStringTable.cs @@ -7,316 +7,314 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Text; using System.Linq; +using System.Text; + +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; } + } + + protected override void UpdateLayoutCore(ElfVisitorContext context) + { + if (_reservedStrings.Count > 0) FlushReservedStrings(); + Size = (ulong)_table.Length; + } + + 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 UpdateLayout(DiagnosticBag diagnostics) + 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); } + } + + internal void FlushReservedStrings() + { + // TODO: Use CollectionsMarshal.AsSpan + string[] reservedStrings = _reservedStrings.ToArray(); + + // Pre-sort the string based on their matching suffix + MultiKeySort(reservedStrings, 0); - protected override void Read(ElfReader reader) + // 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 object && !_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) + { + (input[l], input[i]) = (input[i], input[l]); + l++; i++; + } + else 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); + 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 void MultiKeySortLargeInput(Span input, int pos) + static bool MultiKeySortSmallInput(Span input, int pos) + { + if (input.Length <= 1) + return true; + + // 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; - } + text = string.Empty; + return true; + } - if (_reservedStrings.Count > 0) FlushReservedStrings(); + if (_reservedStrings.Count > 0) FlushReservedStrings(); - if (_mapIndexToString.TryGetValue(index, out text)) - { - return true; - } - - 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 1cf390b..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 cd1c8f6..637e649 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs @@ -4,283 +4,282 @@ using System; using System.Collections.Generic; +using LibObjectFile.Diagnostics; -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)); - } + 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)); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs index 483c529..3e4f4d9 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs @@ -5,97 +5,101 @@ 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) + 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) { - // 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); + return; + } - for (int i = 0; i < _entries.Count; i++) + for (int i = 0; i < _entries.Count; i++) + { + var entry = _entries[i]; + if (entry != 0) { - 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}]"); + 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; - } + // Update the link in symbol table + var symbolTableEntry = symbolTable.Entries[i]; + symbolTableEntry.Section = resolvedLink; + symbolTable.Entries[i] = symbolTableEntry; } } + } - public override void Verify(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)) { - 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)) - { - return; - } + return; } + } - public override unsafe void UpdateLayout(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + 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); - // 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); + int numberOfEntries = 0; - int numberOfEntries = 0; + if (symbolTable is not null) + { for (int i = 0; i < symbolTable.Entries.Count; i++) { if (symbolTable.Entries[i].Section.Section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) @@ -103,10 +107,13 @@ public override unsafe void UpdateLayout(DiagnosticBag diagnostics) numberOfEntries = i + 1; } } + } - _entries.Capacity = numberOfEntries; - _entries.Clear(); + _entries.Capacity = numberOfEntries; + _entries.Clear(); + if (symbolTable is not null) + { for (int i = 0; i < numberOfEntries; i++) { var section = symbolTable.Entries[i].Section.Section; @@ -119,8 +126,8 @@ public override unsafe void UpdateLayout(DiagnosticBag diagnostics) _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/IO/StreamExtensions.cs b/src/LibObjectFile/IO/StreamExtensions.cs new file mode 100644 index 0000000..b5ca792 --- /dev/null +++ b/src/LibObjectFile/IO/StreamExtensions.cs @@ -0,0 +1,353 @@ +// Copyright (c) Alexandre Mutel. All 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.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; + +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(); + 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) + { + // TODO: Optimize this by reading a block of bytes + 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); + } + } + + /// + /// 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)).Slice(0, sizeToRead)); + } + 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/IO/SubStream.cs b/src/LibObjectFile/IO/SubStream.cs new file mode 100644 index 0000000..b73fc32 --- /dev/null +++ b/src/LibObjectFile/IO/SubStream.cs @@ -0,0 +1,236 @@ +// Copyright (c) Alexandre Mutel. All 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.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace LibObjectFile.IO; + +/// +/// Defines a stream as a slice of another existing stream. +/// +public class SubStream : Stream +{ + private Stream _baseStream; + private readonly long _length; + private readonly long _basePosition; + private long _localPosition; + + 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."); + 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 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) + { + ThrowIfDisposed(); + + long remaining = _length - _localPosition; + if (remaining <= 0) return 0; + if (remaining < buffer.Length) buffer = buffer.Slice(0, (int)remaining); + + _baseStream.Seek(_basePosition + _localPosition, SeekOrigin.Begin); + int read = _baseStream.Read(buffer); + _localPosition += read; + + return read; + } + + public override int ReadByte() + { + ThrowIfDisposed(); + + if (_localPosition >= _length) return -1; + + _baseStream.Seek(_basePosition + _localPosition, SeekOrigin.Begin); + 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.Seek(_basePosition + _localPosition, SeekOrigin.Begin); + 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 + { + ThrowIfDisposed(); + return _localPosition; + } + set => Seek(value, SeekOrigin.Begin); + } + 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) + { + 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}"); + + // Check that we can seek on the origin stream + _baseStream.Seek(_basePosition + newPosition, SeekOrigin.Begin); + _localPosition = newPosition; + + return newPosition; + } + + public override void SetLength(long value) + { + ThrowIfDisposed(); + throw new NotSupportedException("This stream does not support setting the length"); + } + + public override void Flush() + { + ThrowIfDisposed(); _baseStream.Flush(); + } + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + if (_baseStream != Null) + { + try + { + _baseStream.Dispose(); + } + catch + { + // ignored + } + _baseStream = Null; + } + } + } + + 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.Seek(_basePosition + _localPosition, SeekOrigin.Begin); + _baseStream.Write(buffer.Slice(0, maxLength)); + _localPosition += maxLength; + if (isOverLength) + { + ThrowCannotWriteOutside(); + } + } + + public override void WriteByte(byte value) + { + ThrowIfDisposed(); + if (_localPosition >= _length) ThrowCannotWriteOutside(); + + _baseStream.Seek(_basePosition + _localPosition, SeekOrigin.Begin); + _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.Seek(_basePosition + _localPosition, SeekOrigin.Begin); + 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 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/LibObjectFile.csproj b/src/LibObjectFile/LibObjectFile.csproj index 1778701..eec2cd9 100644 --- a/src/LibObjectFile/LibObjectFile.csproj +++ b/src/LibObjectFile/LibObjectFile.csproj @@ -1,8 +1,9 @@ - + net8.0 true + enable @@ -30,11 +31,14 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + 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/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 new file mode 100644 index 0000000..26b6537 --- /dev/null +++ b/src/LibObjectFile/ObjectFileElement.cs @@ -0,0 +1,134 @@ +// Copyright (c) Alexandre Mutel. All 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 ObjectFileElement : ObjectElement +{ + + 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 ulong Size { get; set; } + + /// + /// Checks if the specified offset is contained by 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 position) + { + return position >= Position && position < Position + Size; + } + + public bool Contains(ulong position, uint size) + { + return position >= Position && position + size <= Position + Size; + } + + /// + /// Checks this instance contains either the beginning or the end of the specified section or segment. + /// + /// The specified section or segment. + /// 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); + return Contains((ulong)element.Position) || element.Size != 0 && Contains((ulong)(element.Position + element.Size - 1)); + } + + 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; + } +} + +/// +/// 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 +{ + /// + /// Updates the layout of this element. + /// + /// The layout context. + public virtual void UpdateLayout(TLayoutContext context) + { + UpdateLayoutCore(context); + } + + /// + /// Updates the layout of this element. + /// + /// The layout context. + protected virtual void UpdateLayoutCore(TLayoutContext context) + { + } + + /// + /// 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) + { + } +} \ 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; + } + } +} diff --git a/src/LibObjectFile/ObjectFileException.cs b/src/LibObjectFile/ObjectFileException.cs index 10f0b58..142b763 100644 --- a/src/LibObjectFile/ObjectFileException.cs +++ b/src/LibObjectFile/ObjectFileException.cs @@ -3,25 +3,25 @@ // See the license.txt file in the project root for more information. using System; +using LibObjectFile.Diagnostics; -namespace LibObjectFile +namespace LibObjectFile; + +/// +/// An exception used when diagnostics error are happening during read/write. +/// +public sealed 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 deleted file mode 100644 index 2338d31..0000000 --- a/src/LibObjectFile/ObjectFileExtensions.cs +++ /dev/null @@ -1,141 +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 : ObjectFileNodeBase where TParent: ObjectFileNodeBase - { - 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 = (uint)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 - { - 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 = (uint)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 - { - 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 = (uint)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 : ObjectFileNodeBase where TParent : ObjectFileNodeBase - { - 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 : ObjectFileNodeBase where TParent : ObjectFileNodeBase - { - 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 b355305..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 layout of this node. - /// - /// The diagnostics. - public abstract void UpdateLayout(DiagnosticBag diagnostics); -} \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileNodeBase.cs b/src/LibObjectFile/ObjectFileNodeBase.cs deleted file mode 100644 index ac981e5..0000000 --- a/src/LibObjectFile/ObjectFileNodeBase.cs +++ /dev/null @@ -1,115 +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; - -public abstract class ObjectFileNodeBase -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private ObjectFileNodeBase _parent; - - /// - /// Gets or sets the offset of this section or segment in the parent . - /// - public ulong Offset { get; set; } - - /// - /// Gets the containing parent. - /// - public ObjectFileNodeBase Parent - { - get => _parent; - - internal set - { - if (value == null) - { - _parent = null; - } - else - { - ValidateParent(value); - } - - _parent = value; - } - } - - protected virtual void ValidateParent(ObjectFileNodeBase parent) - { - } - - /// - /// Index within the containing list in the - /// - public uint Index { get; internal set; } - - /// - /// 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. - /// - /// 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) - { - return offset >= Offset && offset < Offset + Size; - } - - /// - /// 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. - public bool Contains(ObjectFileNodeBase node) - { - if (node == null) throw new ArgumentNullException(nameof(node)); - return Contains((ulong)node.Offset) || node.Size != 0 && Contains((ulong)(node.Offset + node.Size - 1)); - } - - /// - /// Verifies the integrity of this file. - /// - /// The result of the diagnostics - public DiagnosticBag Verify() - { - var diagnostics = new DiagnosticBag(); - Verify(diagnostics); - return diagnostics; - } - - /// - /// Verifies the integrity of this file. - /// - /// A DiagnosticBag instance to receive the diagnostics. - public virtual void Verify(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - } - - protected static void AttachChild(TParent parent, T child, ref T field, bool allowNull) where T : ObjectFileNodeBase where TParent : ObjectFileNodeBase - { - if (parent == null) throw new ArgumentNullException(nameof(parent)); - if (!allowNull && child == null) throw new ArgumentNullException(nameof(child)); - - if (field != 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; - } - } -} \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileReaderWriter.cs b/src/LibObjectFile/ObjectFileReaderWriter.cs index 3e6ff92..01ec1ac 100644 --- a/src/LibObjectFile/ObjectFileReaderWriter.cs +++ b/src/LibObjectFile/ObjectFileReaderWriter.cs @@ -1,341 +1,268 @@ -// 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.Buffers.Binary; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Text; -using LibObjectFile.Utils; +using LibObjectFile.Diagnostics; +using LibObjectFile.IO; -namespace LibObjectFile +namespace LibObjectFile; + +/// +/// Base class used for reading / writing an object file to/from a stream. +/// +public abstract class ObjectFileReaderWriter : VisitorContextBase { - /// - /// Base class used for reading / writing an object file to/from a stream. - /// - public abstract class ObjectFileReaderWriter + private Stream _stream; + private readonly byte[] _zeroBuffer = new byte[1024]; + + internal ObjectFileReaderWriter(ObjectFileElement file, Stream stream) : this(file, stream, new DiagnosticBag()) { - private Stream _stream; + } - protected ObjectFileReaderWriter(Stream stream) : this(stream, new DiagnosticBag()) - { - } + internal ObjectFileReaderWriter(ObjectFileElement file, Stream stream, DiagnosticBag diagnostics) : base(file, diagnostics) + { + _stream = stream; + IsLittleEndian = true; + } - protected ObjectFileReaderWriter(Stream stream, DiagnosticBag diagnostics) - { - Stream = stream; - Diagnostics = diagnostics; - IsLittleEndian = true; - } + /// + /// Gets or sets stream of the object file. + /// + public Stream Stream + { + get => _stream; + set => _stream = value; + } - /// - /// Gets or sets stream of the object file. - /// - public Stream Stream - { - get => _stream; - set => _stream = value; - } + public ulong Position + { + get => (ulong) Stream.Position; + set => Stream.Seek((long)value, SeekOrigin.Begin); + } - public ulong Offset - { - get => (ulong) Stream.Position; - set => Stream.Position = (long) value; - } + public ulong Length + { + get => (ulong) Stream.Length; + } - public ulong Length - { - get => (ulong) Stream.Length; - } + /// + /// Gets a boolean indicating if this reader is operating in read-only mode. + /// + public abstract bool KeepOriginalStreamForSubStreams { get; } - /// - /// The diagnostics while read/writing this object file. - /// - public DiagnosticBag Diagnostics { get; protected set; } + public bool IsLittleEndian { get; protected set; } - /// - /// Gets a boolean indicating if this reader is operating in read-only mode. - /// - public abstract bool IsReadOnly { get; } + /// + /// 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 bool IsLittleEndian { get; protected set; } + public int Read(Span buffer) => Stream.Read(buffer); - public TextWriter Log { get; set; } + public void ReadExactly(Span buffer) => Stream.ReadExactly(buffer); - /// - /// 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) - { - return Stream.Read(buffer, offset, count); - } - - /// - /// 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)); - - 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; - } + /// + /// 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 + => Stream.TryReadData(sizeToRead, out data); - /// - /// 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) - { - var stream = ReadAsSliceStream(size); - Stream.Position += stream.Length; - return stream; - } - - return ReadAsMemoryStream(size); - } + /// + /// 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) + => Stream.ReadAsStream(size, Diagnostics, KeepOriginalStreamForSubStreams); + + /// + /// 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 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) + /// + /// Writes count bytes with zero. + /// + /// The number of bytes to write with zero. + public void WriteZero(int count) + { + while (count > 0) { - Stream.Write(buffer, offset, count); + var size = Math.Min(count, _zeroBuffer.Length); + Stream.Write(_zeroBuffer, 0, size); + count -= size; } + } - /// - /// 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 + /// + /// Writes to the and current position from the specified buffer. + /// + /// 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. + /// + /// 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)); - - inputStream.Position = 0; - 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; - - Stream.Write(buffer, 0, sizeRead); - size -= (ulong)sizeRead; - } - - inputStream.Position = 0; - if (size != 0) - { - throw new InvalidOperationException("Unable to write stream entirely"); - } - } - - private SliceStream ReadAsSliceStream(ulong size) + /// + /// 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); + + while (size != 0) { - 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 SliceStream(Stream, position, (long)size); + 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; } - private MemoryStream ReadAsMemoryStream(ulong size) + inputStream.Seek(0, SeekOrigin.Begin); + if (size != 0) { - var memoryStream = new MemoryStream((int)size); - if (size == 0) return memoryStream; - - 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; - } - } - - if (size != 0) - { - Diagnostics.Error(DiagnosticId.CMN_ERR_UnexpectedEndOfFile, $"Unexpected end of file. Expecting to read {size} bytes at offset {Stream.Position}"); - } - - return memoryStream; + throw new InvalidOperationException("Unable to write stream entirely"); } } } \ No newline at end of file diff --git a/src/LibObjectFile/ObjectFileStreamExtensions.cs b/src/LibObjectFile/ObjectFileStreamExtensions.cs deleted file mode 100644 index 027abd2..0000000 --- a/src/LibObjectFile/ObjectFileStreamExtensions.cs +++ /dev/null @@ -1,234 +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; -using LibObjectFile.Utils; - -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/PE/DataDirectory/PEArchitectureDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEArchitectureDirectory.cs new file mode 100644 index 0000000..a6f782e --- /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(PELayoutContext 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/PEBaseRelocation.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs new file mode 100644 index 0000000..da28d2b --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocation.cs @@ -0,0 +1,49 @@ +// Copyright (c) Alexandre Mutel. All 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; + +namespace LibObjectFile.PE; + +/// +/// A base relocation in a Portable Executable (PE) image. +/// +[DebuggerDisplay("{ToString(),nq}")] +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 (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 OffsetInBlock => (ushort)(_value & VirtualOffsetMask); + + 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 new file mode 100644 index 0000000..07d3ef8 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationBlock.cs @@ -0,0 +1,205 @@ +// Copyright (c) Alexandre Mutel. All 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; +using System.Text; + +namespace LibObjectFile.PE; + +/// +/// A block of base relocations for a page. +/// +[DebuggerDisplay("{ToString(),nq}")] +public sealed class PEBaseRelocationBlock : PESectionData +{ + /// + /// Initializes a new instance of the class. + /// + public PEBaseRelocationBlock() + { + } + + /// + public override bool HasChildren => false; + + /// + /// 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 Relocations { get; } = new(); + + protected override unsafe void UpdateLayoutCore(PELayoutContext context) + { + 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++; + } + + Size = (uint)(sizeof(ImageBaseRelocation) + count * sizeof(PEBaseRelocation)); + } + + /// + /// 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) + { + if (relocation.Type != PEBaseRelocationType.Dir64) + { + throw new InvalidOperationException($"The base relocation type {relocation.Type} not supported. Only Dir64 is supported for this method."); + } + + var vaOfReloc = SectionLink.RVA() + relocation.OffsetInBlock; + + if (!file.TryFindByRVA(vaOfReloc, out var container)) + { + throw new InvalidOperationException($"Unable to find the section data containing the virtual address {vaOfReloc}"); + } + + var rvo = vaOfReloc - container!.RVA; + + if (file.IsPE32) + { + VA32 va32 = default; + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref va32, 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 va32; + } + else + { + VA64 va64 = default; + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref va64, 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 va64; + } + } + + public override unsafe void Read(PEImageReader reader) + { + reader.Position = Position; + + 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; + } + + 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; + } + + + 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); + } + + 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)); + } + } + + public override void Verify(PEVerifyContext context) + { + context.VerifyObject(SectionLink.Container, this, nameof(SectionLink), false); + } + + 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}"); + } + } + + 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/PEBaseRelocationDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.cs new file mode 100644 index 0000000..fb7a891 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationDirectory.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 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) + { + } + + /// + protected override uint ComputeHeaderSize(PELayoutContext context) => 0; + + /// + public override void Read(PEImageReader reader) + { + reader.Position = Position; + var size = (long)Size; + + while (size > 0) + { + var block = new PEBaseRelocationBlock() + { + Position = reader.Position + }; + + block.Read(reader); + + size -= (uint)block.Size; + + // Add the block to the content + 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/PEBaseRelocationType.cs b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationType.cs new file mode 100644 index 0000000..2473ee4 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBaseRelocationType.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 PEBaseRelocationType : 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/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/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..1d1817a --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable32.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 System.Collections.Generic; +using System.Runtime.InteropServices; +using LibObjectFile.Utils; + +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); + + 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 new file mode 100644 index 0000000..0441aac --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportAddressTable64.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 LibObjectFile.Utils; +using System; +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); + + 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/PEBoundImportDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs new file mode 100644 index 0000000..f8cd559 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectory.cs @@ -0,0 +1,195 @@ +// Copyright (c) Alexandre Mutel. All 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; +using LibObjectFile.Collections; + +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 unsafe uint ComputeHeaderSize(PELayoutContext context) + { + // Last raw entry is zero + var size = (uint)sizeof(RawPEBoundImportDirectory); + var entries = CollectionsMarshal.AsSpan(Entries); + foreach (var entry in entries) + { + size += entry.Size; + } + + return size; + } + + /// + public override unsafe void Read(PEImageReader reader) + { + 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) + { + using var tempSpan = TempSpan.Create(rawEntry.NumberOfModuleForwarderRefs, out var spanForwarderRef); + var span = tempSpan.AsBytes; + + read = reader.Read(span); + if (read != span.Length) + { + 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; + } + + 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.TryFindByRVA(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.TryFindByRVA(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) + { + 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); + } + + 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/PEBoundImportDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEBoundImportDirectoryEntry.cs new file mode 100644 index 0000000..66edea8 --- /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 new file mode 100644 index 0000000..80c72e8 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEClrMetadata.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 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) => 0; + + /// + public override void Read(PEImageReader reader) + { + } + + /// + public override void Write(PEImageWriter writer) + { + } +} diff --git a/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs new file mode 100644 index 0000000..1642c5b --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PECompositeSectionData.cs @@ -0,0 +1,178 @@ +// Copyright (c) Alexandre Mutel. All 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; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; +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, IEnumerable +{ + 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; } + + 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); + } + } + } + + public override void Verify(PEVerifyContext context) + { + foreach (var data in Content) + { + data.Verify(context); + } + } + + protected sealed override void UpdateLayoutCore(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) + { + 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); + + if (table is PECompositeSectionData compositeSectionData) + { + compositeSectionData.WriteHeaderAndContent(writer); + } + else + { + 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); + } + } + } + + protected abstract uint ComputeHeaderSize(PELayoutContext context); + + 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; + foreach (var table in Content) + { + table.UpdateRVA(va); + 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/PEDataDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.cs new file mode 100644 index 0000000..144088b --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectory.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. + +using System; + +namespace LibObjectFile.PE; + +public abstract class PEDataDirectory : PECompositeSectionData +{ + protected PEDataDirectory(PEDataDirectoryKind kind) + { + Kind = kind; + } + + public PEDataDirectoryKind Kind { get; } + + protected override void ValidateParent(ObjectElement parent) + { + //if (parent is not PESection) + //{ + // throw new ArgumentException($"Invalid parent type [{parent?.GetType()}] for [{GetType()}]"); + //} + } + + /// + /// Factory method to create a new instance of based on the kind. + /// + /// The kind of PE directory entry. + /// A PE directory entry. + internal static PEDataDirectory Create(PEDataDirectoryKind kind, bool is32Bits) + { + return kind switch + { + PEDataDirectoryKind.Export => new PEExportDirectory(), + PEDataDirectoryKind.Import => new PEImportDirectory(), + PEDataDirectoryKind.Resource => new PEResourceDirectory(), + PEDataDirectoryKind.Exception => new PEExceptionDirectory(), + PEDataDirectoryKind.BaseRelocation => new PEBaseRelocationDirectory(), + PEDataDirectoryKind.Debug => new PEDebugDirectory(), + PEDataDirectoryKind.Architecture => new PEArchitectureDirectory(), + PEDataDirectoryKind.GlobalPointer => new PEGlobalPointerDirectory(), + 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(), + PEDataDirectoryKind.ClrMetadata => new PEClrMetadata(), + _ => new PEUnknownDirectory((int)kind) + }; + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEDataDirectoryKind.cs b/src/LibObjectFile/PE/DataDirectory/PEDataDirectoryKind.cs new file mode 100644 index 0000000..869a38e --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDataDirectoryKind.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 PEDataDirectoryKind : ushort +{ + /// + /// Export Directory + /// + Export = 0, + /// + /// Import Directory + /// + Import = 1, + /// + /// Resource Directory + /// + Resource = 2, + /// + /// Exception Directory + /// + Exception = 3, + /// + /// Security/Certificate Directory + /// + SecurityCertificate = 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/DataDirectory/PEDebugDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs new file mode 100644 index 0000000..dc3dd48 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectory.cs @@ -0,0 +1,199 @@ +// Copyright (c) Alexandre Mutel. All 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.Runtime.InteropServices; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; +using LibObjectFile.PE.Internal; + +namespace LibObjectFile.PE; + +public sealed class PEDebugDirectory : PEDataDirectory +{ + public PEDebugDirectory() : base(PEDataDirectoryKind.Debug) + { + Entries = new(); + } + + public List Entries { get; } + + public override unsafe void Read(PEImageReader reader) + { + var size = (int)Size; + + var entryCount = size / sizeof(RawImageDebugDirectory); + + // Scope the pooled span to ensure it is returned to the pool as soon as possible + { + using var tempSpan = TempSpan.Create(entryCount, out var entries); + Span span = tempSpan; + + 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 rawEntry = entries[i]; + var entry = new PEDebugDirectoryEntry + { + Characteristics = rawEntry.Characteristics, + MajorVersion = rawEntry.MajorVersion, + MinorVersion = rawEntry.MinorVersion, + TimeDateStamp = rawEntry.TimeDateStamp, + Type = rawEntry.Type, + }; + + + if (rawEntry.AddressOfRawData != 0) + { + 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; + } + + 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 (rawEntry.PointerToRawData != 0) + { + + var extraData = new PEDebugStreamExtraData + { + Position = (RVA)(uint)rawEntry.PointerToRawData, + Size = rawEntry.SizeOfData + }; + + entry.ExtraData = extraData; + } + else + { + Debug.Assert(rawEntry.SizeOfData == 0); + } + + Entries.Add(entry); + } + } + + // Read the data associated with the debug directory entries + foreach (var entry in Entries) + { + if (entry.SectionData is not null) + { + entry.SectionData.Read(reader); + } + else if (entry.ExtraData is not null) + { + entry.ExtraData.Read(reader); + } + } + + HeaderSize = ComputeHeaderSize(reader); + } + + internal override IEnumerable CollectImplicitSectionDataList() + { + foreach (var entry in Entries) + { + if (entry.SectionData is not null) + { + yield return entry.SectionData; + } + else if (entry.ExtraData is not null) + { + yield return entry.ExtraData; + } + } + } + + public override void Write(PEImageWriter writer) + { + var entries = CollectionsMarshal.AsSpan(Entries); + using var tempSpan = TempSpan.Create(entries.Length, out var rawEntries); + + 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; + rawEntry.TimeDateStamp = entry.TimeDateStamp; + rawEntry.Type = entry.Type; + + if (entry.SectionData is not null) + { + rawEntry.SizeOfData = (uint)entry.SectionData.Size; + rawEntry.AddressOfRawData = entry.SectionData.RVA; + rawEntry.PointerToRawData = (uint)entry.SectionData.Position; + } + else if (entry.ExtraData is not null) + { + rawEntry.SizeOfData = (uint)entry.ExtraData.Size; + rawEntry.AddressOfRawData = 0; + rawEntry.PointerToRawData = (uint)entry.ExtraData.Position; + } + else + { + rawEntry.SizeOfData = 0; + rawEntry.AddressOfRawData = 0; + rawEntry.PointerToRawData = 0; + } + + rawEntries[i] = rawEntry; + } + + writer.Write(tempSpan); + } + + 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/PEDebugDirectoryEntry.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.cs new file mode 100644 index 0000000..aad3b5e --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugDirectoryEntry.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.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 +{ + private PEObjectBase? _data; + + /// + /// 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 associated section data to this debug entry. + /// + /// + /// 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; + } + + /// + /// Gets or sets the associated blob data outside a section (e.g. in or ). + /// + /// + /// This is set when the data is located outside a section. Otherwise might be set. + /// + public PEExtraData? ExtraData + { + get => _data as PEExtraData; + set => _data = value; + } + + public override string ToString() => $"{nameof(PEDebugDirectoryEntry)} {Type} {_data}"; +} \ 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/PEDebugSectionData.cs b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionData.cs new file mode 100644 index 0000000..d366726 --- /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..a91c6de --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDebugSectionDataRSDS.cs @@ -0,0 +1,139 @@ +// Copyright (c) Alexandre Mutel. All 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.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// Represents a RSDS debug data. +/// +[DebuggerDisplay("{ToString(),nq}")] +public sealed class PEDebugSectionDataRSDS : PEDebugSectionData +{ + private const uint Signature = 0x53445352; + + /// + /// Initializes a new instance of the class. + /// + public PEDebugSectionDataRSDS() + { + Guid = Guid.Empty; + Age = 0; + PdbPath = string.Empty; + ExtraData = []; + } + + /// + /// 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; } + + /// + /// Gets or sets the extra data after the PDB path. + /// + public byte[] ExtraData { get; set; } + + public override unsafe void Read(PEImageReader reader) + { + var size = (int)Size; + using var tempSpan = TempSpan.Create(size, out var span); + + reader.Position = Position; + 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 != Signature) + { + 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)); + + // Remaining + pdbPath = pdbPath.Slice(indexOfZero + 1); + if (pdbPath.Length > 0) + { + ExtraData = pdbPath.ToArray(); + } + + var newSize = CalculateSize(); + + 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 = span.Slice(written + 1); + if (ExtraData.Length > 0) + { + ExtraData.CopyTo(span); + } + + writer.Write(tempSpan.AsBytes); + } + + protected override void UpdateLayoutCore(PELayoutContext context) + { + Size = CalculateSize(); + } + + private unsafe uint CalculateSize() + => (uint)(sizeof(uint) + sizeof(Guid) + sizeof(uint) + Encoding.UTF8.GetByteCount(PdbPath) + 1 + ExtraData.Length); + + /// + 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/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/IObjectFileNodeLink.cs b/src/LibObjectFile/PE/DataDirectory/PEDelayImportAddressTable.cs similarity index 57% rename from src/LibObjectFile/IObjectFileNodeLink.cs rename to src/LibObjectFile/PE/DataDirectory/PEDelayImportAddressTable.cs index 570138e..70e1d3d 100644 --- a/src/LibObjectFile/IObjectFileNodeLink.cs +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportAddressTable.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.PE; + +public sealed class PEDelayImportAddressTable : PEImportAddressTable { - public interface IObjectFileNodeLink + public PEDelayImportAddressTable() { - ulong GetRelativeOffset(); - - ObjectFileNodeBase GetObjectFileNode(); } } \ 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..6542a49 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectory.cs @@ -0,0 +1,237 @@ +// Copyright (c) Alexandre Mutel. All 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; +using LibObjectFile.Collections; + +namespace LibObjectFile.PE; + +public sealed class PEDelayImportDirectory : PEDataDirectory +{ + public PEDelayImportDirectory() : base(PEDataDirectoryKind.DelayImport) + { + Entries = new List(); + } + + public List Entries { get; } + + public override void Read(PEImageReader reader) + { + 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.TryFindSectionByRVA(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.TryFindSectionByRVA(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 delayImportAddressTable = is32Bits ? new PEBoundImportAddressTable32() : new PEBoundImportAddressTable64(); + delayImportAddressTable.Position = (uint)importDelayLoadImportAddressTablePositionInFile; + + Entries.Add( + + new PEDelayImportDirectoryEntry( + new PEAsciiStringLink(null, (RVO)(uint)rawEntry.NameRVA), + new PEModuleHandleLink(null, (RVO)(uint)rawEntry.ModuleHandleRVA), + delayImportAddressTable, + new PEImportLookupTable() + { + Position = (uint)importDelayLoadImportNameTablePositionInFile, + } + ) + { + Attributes = rawEntry.Attributes, + BoundImportAddressTableLink = new PESectionDataLink(null, (RVO)(uint)rawEntry.BoundDelayLoadImportAddressTableRVA), + UnloadDelayInformationTableLink = new PESectionDataLink(null, (RVO)(uint)rawEntry.UnloadDelayLoadImportAddressTableRVA) + } + ); + } + + // 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); + } + } + internal override IEnumerable CollectImplicitSectionDataList() + { + foreach (var entry in Entries) + { + yield return entry.DelayImportAddressTable; + yield return entry.DelayImportNameTable; + } + } + + internal override void Bind(PEImageReader reader) + { + 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.TryFindByRVA(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); + + // 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.TryFindSectionByRVA(rva, out var moduleSection)) + { + diagnostics.Error(DiagnosticId.PE_ERR_DelayImportDirectoryInvalidModuleHandleRVA, $"Unable to find the section data for ModuleHandleRVA {rva}"); + return; + } + + entry.ModuleHandle = new PEModuleHandleLink(moduleSection, rva - moduleSection.RVA); + + entry.DelayImportNameTable.FunctionTable.Bind(reader, false); + + + if (entry.BoundImportAddressTableLink.RVO != 0) + { + rva = (RVA)(uint)entry.BoundImportAddressTableLink.RVO; + if (!peFile.TryFindByRVA(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); + } + + if (entry.UnloadDelayInformationTableLink.RVO != 0) + { + rva = (RVA)(uint)entry.UnloadDelayInformationTableLink.RVO; + if (!peFile.TryFindByRVA(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); + } + } + } + + protected override uint ComputeHeaderSize(PELayoutContext context) => CalculateSize(); + + + private unsafe uint CalculateSize() + { + return Entries.Count == 0 ? 0 : (uint)(((Entries.Count + 1) * sizeof(RawDelayLoadDescriptor))); + } + + + public override unsafe void Write(PEImageWriter writer) + { + var entries = CollectionsMarshal.AsSpan(Entries); + using var tempSpan = TempSpan.Create(entries.Length + 1, out var rawEntries); + + 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.BoundImportAddressTableLink.RVA(); + rawEntry.UnloadDelayLoadImportAddressTableRVA = entry.UnloadDelayInformationTableLink.RVA(); + rawEntry.TimeDateStamp = 0; + + rawEntries[i] = rawEntry; + } + + // Write the null entry + rawEntries[entries.Length] = default; + + 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 new file mode 100644 index 0000000..c72e3ad --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDelayImportDirectoryEntry.cs @@ -0,0 +1,41 @@ +// Copyright (c) Alexandre Mutel. All 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 uint Attributes { get; set; } + + public PEAsciiStringLink DllName { get; set; } + + public PEModuleHandleLink ModuleHandle { get; set; } + + public PEBoundImportAddressTable DelayImportAddressTable { get; set; } + + public PEImportLookupTable DelayImportNameTable { get; set; } + + 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/PEDirectoryTable.cs b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs new file mode 100644 index 0000000..8d0c025 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEDirectoryTable.cs @@ -0,0 +1,280 @@ +// Copyright (c) Alexandre Mutel. All 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 LibObjectFile.Collections; +using LibObjectFile.PE.Internal; + +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 +{ + private PEObjectBase?[] _entries; + private int _count; + + internal PEDirectoryTable() + { + _entries = []; + } + + /// + /// Clear the directory + /// + public void Clear() + { + Array.Clear(_entries); + } + + /// + /// 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]; + } + + internal set + { + if (index < 0 || index >= _count) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + _entries[index] = value; + } + } + + /// + /// Gets the directory entry of the specified kind. Must be within the bounds of . + /// + /// 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]; + } + + internal set + { + int index = (int)(ushort)kind; + if (index < 0 || index >= _count) + { + throw new ArgumentOutOfRangeException(nameof(kind)); + } + _entries[(int)kind] = value; + } + } + + /// + /// 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. + /// + public PEExportDirectory? Export + { + get => (PEExportDirectory?)this[PEDataDirectoryKind.Export]; + } + + /// + /// Gets the import directory information from the PE file. + /// + public PEImportDirectory? Import + { + get => (PEImportDirectory?)this[PEDataDirectoryKind.Import]; + } + + /// + /// Gets the resource directory information from the PE file. + /// + public PEResourceDirectory? Resource + { + get => (PEResourceDirectory?)this[PEDataDirectoryKind.Resource]; + } + + /// + /// Gets the exception directory information from the PE file. + /// + public PEExceptionDirectory? Exception + { + get => (PEExceptionDirectory?)this[PEDataDirectoryKind.Exception]; + } + + /// + /// Gets the certificate/security directory information from the PE file. + /// + public PESecurityCertificateDirectory? Certificate + { + get => (PESecurityCertificateDirectory?)this[PEDataDirectoryKind.SecurityCertificate]; + } + + /// + /// Gets the base relocation directory information from the PE file. + /// + public PEBaseRelocationDirectory? BaseRelocation + { + get => (PEBaseRelocationDirectory?)this[PEDataDirectoryKind.BaseRelocation]; + } + + /// + /// Gets the debug directory information from the PE file. + /// + public PEDebugDirectory? Debug + { + get => (PEDebugDirectory?)this[PEDataDirectoryKind.Debug]; + } + + /// + /// Gets the architecture-specific directory information from the PE file. + /// + public PEArchitectureDirectory? Architecture + { + get => (PEArchitectureDirectory?)this[PEDataDirectoryKind.Architecture]; + } + + /// + /// Gets the global pointer directory information from the PE file. + /// + public PEGlobalPointerDirectory? GlobalPointer + { + get => (PEGlobalPointerDirectory?)this[PEDataDirectoryKind.GlobalPointer]; + } + + /// + /// Gets the TLS (Thread Local Storage) directory information from the PE file. + /// + public PETlsDirectory? Tls + { + get => (PETlsDirectory?)this[PEDataDirectoryKind.Tls]; + } + + /// + /// Gets the load configuration directory information from the PE file. + /// + public PELoadConfigDirectory? LoadConfig + { + get => (PELoadConfigDirectory?)this[PEDataDirectoryKind.LoadConfig]; + } + + /// + /// Gets the bound import directory information from the PE file. + /// + public PEBoundImportDirectory? BoundImport + { + get => (PEBoundImportDirectory?)this[PEDataDirectoryKind.BoundImport]; + } + + /// + /// Gets the delay import directory information from the PE file. + /// + public PEDelayImportDirectory? DelayImport + { + get => (PEDelayImportDirectory?)this[PEDataDirectoryKind.DelayImport]; + } + + /// + /// Gets the import address table directory information from the PE file. + /// + public PEImportAddressTableDirectory? ImportAddressTableDirectory + { + get => (PEImportAddressTableDirectory?)this[PEDataDirectoryKind.ImportAddressTable]; + } + + /// + /// Gets the CLR metadata directory information from the PE file, if present. + /// + public PEClrMetadata? ClrMetadata + { + get => (PEClrMetadata?)this[PEDataDirectoryKind.ClrMetadata]; + } + + internal void Set(PESecurityCertificateDirectory? directory) => Set(PEDataDirectoryKind.SecurityCertificate, directory); + + internal void Set(PEDataDirectoryKind kind, PEObjectBase? directory) => Set((int)kind, directory); + + internal void Set(int index, PEObjectBase? directory) + { + if (index >= Count) + { + throw new ArgumentOutOfRangeException(nameof(index), $"The directory entry only accepts {Count} entries. Set the count explicitly to allow more entries."); + } + + _entries[index] = 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++) + { + var entry = _entries[i]; + if (entry is not null) + { + 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)); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs new file mode 100644 index 0000000..7a7fe83 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionDirectory.cs @@ -0,0 +1,312 @@ +// Copyright (c) Alexandre Mutel. All 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.Collections; +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(PELayoutContext 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; + { + using var tempSpan = TempSpan.Create((int)size, out var span); + 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; + } + } + + 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.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; + } + + 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.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; + } + + 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.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; + } + + 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.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; + } + + 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) + { + 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; + } + } + + 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); + + for (var i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + var entryX86 = (PEExceptionFunctionEntryX86)entry; + 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) + { + using var tempSpan = TempSpan.Create(Entries.Count, out var span); + + for (var i = 0; i < Entries.Count; i++) + { + 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) + { + 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..294e011 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntry.cs @@ -0,0 +1,30 @@ +// Copyright (c) Alexandre Mutel. All 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; } + + 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/PEExceptionFunctionEntryArm.cs b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryArm.cs new file mode 100644 index 0000000..428b483 --- /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..fffad18 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExceptionFunctionEntryX86.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; + +/// +/// 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()}"; + } + + /// + 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/PEExportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.cs new file mode 100644 index 0000000..d0af0dc --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExportAddressTable.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.Collections.Generic; +using System.Runtime.InteropServices; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; + +namespace LibObjectFile.PE; + +public sealed class PEExportAddressTable : PESectionData +{ + public PEExportAddressTable() + { + } + + public PEExportAddressTable(int count) + { + CollectionsMarshal.SetCount(Values, count); + } + + public override bool HasChildren => false; + + public List Values { get; } = new(); + + protected override unsafe void UpdateLayoutCore(PELayoutContext context) + { + Size = (ulong)(Values.Count * sizeof(RVA)); + } + + public override unsafe void Read(PEImageReader reader) + { + using var tempSpan = TempSpan.Create(Values.Count, out var spanRva); + var span = tempSpan.AsBytes; + + reader.Position = Position; + 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++) + { + var rva = spanRva[i]; + if (rva == 0) + { + Values[i] = new PEExportFunctionEntry(); + continue; + } + + 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; + } + + var found = section.TryFindSectionDataByRVA(rva, out var sectionData); + + if (section.Name == PESectionName.EData) + { + 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)); + } + else + { + if (found) + { + Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(sectionData, rva - sectionData!.RVA)); + } + else + { + Values[i] = new PEExportFunctionEntry(new PEFunctionAddressLink(section, rva - section.RVA)); + } + } + } + } + + public override void Write(PEImageWriter writer) + { + 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.IsEmpty ? default : value.IsForwarderRVA ? value.ForwarderRVA.RVA() : value.ExportRVA.RVA(); + } + + writer.Write(tempSpan); + } +} \ 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..4643279 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExportDirectory.cs @@ -0,0 +1,214 @@ +// Copyright (c) Alexandre Mutel. All 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; } + + 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.TryFindSectionByRVA(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 + // 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); + + // Not sure this one happen + if (exportDirectory.AddressOfFunctions != 0) + { + 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; + } + ExportFunctionAddressTable = new PEExportAddressTable((int)exportDirectory.NumberOfFunctions) + { + Position = sectionAddressOfFunctions.Position + exportDirectory.AddressOfFunctions - sectionAddressOfFunctions.RVA, + Size = (ulong)(exportDirectory.NumberOfFunctions * sizeof(RVA)) + }; + } + + // AddressOfNames can be 0 + if (exportDirectory.AddressOfNames != 0) + { + 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; + } + + ExportNameTable = new PEExportNameTable((int)exportDirectory.NumberOfNames) + { + Position = sectionAddressOfNames.Position + exportDirectory.AddressOfNames - sectionAddressOfNames.RVA, + Size = (ulong)(exportDirectory.NumberOfNames * sizeof(RVA)) + }; + } + + // AddressOfNames can be 0 + if (exportDirectory.AddressOfNameOrdinals != 0) + { + 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; + } + + 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); + } + + internal override void Bind(PEImageReader reader) + { + var peFile = reader.File; + var diagnostics = reader.Diagnostics; + + 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; + } + + var streamSectionData = container as PEStreamSectionData; + if (streamSectionData is null) + { + 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 - streamSectionData.RVA); + + 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) + { + var exportDirectory = new RawImageExportDirectory + { + TimeDateStamp = (uint)(TimeStamp - DateTime.UnixEpoch).TotalSeconds, + MajorVersion = MajorVersion, + MinorVersion = MinorVersion, + Base = OrdinalBase, + Name = NameLink.RVA(), + 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) + }; + + writer.Write(exportDirectory); + } + + 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) + { + 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 + 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..7a704e0 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExportFunctionEntry.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. + +namespace LibObjectFile.PE; + +#pragma warning disable CS0649 +public readonly struct PEExportFunctionEntry +{ + private readonly PEObject? _container; + private readonly uint _offset; + private readonly bool _isForwarderRVA; + + public PEExportFunctionEntry(PEFunctionAddressLink exportRVA) + { + _container = exportRVA.Container; + _offset = exportRVA.RVO; + _isForwarderRVA = false; + } + + public PEExportFunctionEntry(PEAsciiStringLink forwarderRVA) + { + _container = forwarderRVA.Container; + _offset = forwarderRVA.RVO; + _isForwarderRVA = true; + } + + 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() => 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 new file mode 100644 index 0000000..a0fc161 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExportNameTable.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.Collections.Generic; +using System.Runtime.InteropServices; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; + +namespace LibObjectFile.PE; + +public sealed class PEExportNameTable : PESectionData +{ + public PEExportNameTable() + { + } + + public PEExportNameTable(int count) + { + CollectionsMarshal.SetCount(Values, count); + } + + public override bool HasChildren => false; + + public List Values { get; } = new(); + + protected override unsafe void UpdateLayoutCore(PELayoutContext context) + { + Size = (ulong)(Values.Count * sizeof(RVA)); + } + + public override unsafe void Read(PEImageReader reader) + { + reader.Position = Position; + using var tempSpan = TempSpan.Create(Values.Count, out var spanRva); + var span = tempSpan.AsBytes; + + int read = reader.Read(span); + if (read != span.Length) + { + 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.TryFindByRVA(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); + } + } + + public override void Write(PEImageWriter writer) + { + 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); + } + + 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/PEExportOrdinalTable.cs b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.cs new file mode 100644 index 0000000..367e5da --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEExportOrdinalTable.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.Collections.Generic; +using System.Runtime.InteropServices; +using LibObjectFile.Diagnostics; + +namespace LibObjectFile.PE; + +public sealed class PEExportOrdinalTable : PESectionData +{ + public PEExportOrdinalTable() + { + } + + internal PEExportOrdinalTable(int count) + { + CollectionsMarshal.SetCount(Values, count); + } + + public override bool HasChildren => false; + + public List Values { get; } = new(); + + + protected override void UpdateLayoutCore(PELayoutContext 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; + } + } + + 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/PEGlobalPointerDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PEGlobalPointerDirectory.cs new file mode 100644 index 0000000..e0f4b9e --- /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(PELayoutContext 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/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/PEImportAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs new file mode 100644 index 0000000..fe3250a --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTable.cs @@ -0,0 +1,48 @@ +// Copyright (c) Alexandre Mutel. All 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, IEnumerable +{ + internal readonly PEImportFunctionTable FunctionTable; + + public PEImportAddressTable() + { + FunctionTable = new PEImportFunctionTable(); + } + + public override bool HasChildren => false; + + public List Entries => FunctionTable.Entries; + + protected override void UpdateLayoutCore(PELayoutContext context) + { + Size = FunctionTable.CalculateSize(context); + } + + public override void Read(PEImageReader reader) + { + FunctionTable.Read(reader, Position); + UpdateLayout(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(); + + public override void Verify(PEVerifyContext context) + { + FunctionTable.Verify(context, this); + } +} \ 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..a502935 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEImportAddressTableDirectory.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; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using LibObjectFile.Collections; + +namespace LibObjectFile.PE; + +public sealed class PEImportAddressTableDirectory : PEDataDirectory +{ + public PEImportAddressTableDirectory() : base(PEDataDirectoryKind.ImportAddressTable) + { + } + + public override void Read(PEImageReader reader) + { + } + + public override void Write(PEImageWriter writer) + { + } + + 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 new file mode 100644 index 0000000..6fdc4cf --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectory.cs @@ -0,0 +1,195 @@ +// Copyright (c) Alexandre Mutel. All 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.Runtime.InteropServices; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; +using LibObjectFile.PE.Internal; + +namespace LibObjectFile.PE; + +public sealed class PEImportDirectory : PEDataDirectory +{ + private readonly List _entries; + + public PEImportDirectory() : base(PEDataDirectoryKind.Import) + { + _entries = new(); + } + + public List Entries => _entries; + + public override void Read(PEImageReader reader) + { + var diagnostics = reader.Diagnostics; + + reader.Position = Position; + + // Read Import Directory Entries + RawImportDirectoryEntry 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; + } + + // TODO: handle bound imports through entry.TimeDateStamp + + // Check for null entry (last entry in the import directory) + 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.File.TryFindSectionByRVA(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 = section.Position + rawEntry.ImportAddressTableRVA - section.RVA; + + // Find the section data for the ImportLookupTableRVA + 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; + } + + // Calculate its position within the original stream + 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, (RVO)(uint)rawEntry.NameRVA), // Store the RVA as a fake RVO until we bind it in the Bind phase + // ImportAddressTable + new PEImportAddressTable() + { + Position = importLookupAddressTablePositionInFile + }, + // ImportLookupTable + new PEImportLookupTable() + { + Position = importLookupTablePositionInFile + } + ) + { + TimeDateStamp = rawEntry.TimeDateStamp, + ForwarderChain = rawEntry.ForwarderChain + } + ); + } + + // 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.ImportAddressTable.Read(reader); + entry.ImportLookupTable.Read(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; + rawEntry.TimeDateStamp = entry.TimeDateStamp; + rawEntry.ForwarderChain = entry.ForwarderChain; + writer.Write(rawEntry); + } + + // Null entry + rawEntry = default; + writer.Write(rawEntry); + } + + protected override unsafe uint ComputeHeaderSize(PELayoutContext context) => CalculateSize(); + + internal override IEnumerable CollectImplicitSectionDataList() + { + foreach (var entry in _entries) + { + yield return entry.ImportAddressTable; + yield return entry.ImportLookupTable; + } + } + + internal override void Bind(PEImageReader reader) + { + var peFile = reader.File; + var diagnostics = reader.Diagnostics; + + var entries = CollectionsMarshal.AsSpan(_entries); + foreach (ref var entry in entries) + { + // The RVO is actually an RVA until we bind it here + var va = (RVA)(uint)entry.ImportDllNameLink.RVO; + if (!peFile.TryFindByRVA(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.RVA), + entry.ImportAddressTable, + entry.ImportLookupTable) + { + TimeDateStamp = entry.TimeDateStamp, + ForwarderChain = entry.ForwarderChain + }; + } + + + foreach (var entry in Entries) + { + entry.ImportAddressTable.FunctionTable.Bind(reader, true); + entry.ImportLookupTable.FunctionTable.Bind(reader, false); + } + } + + 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))); + } +} \ 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..32c65de --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEImportDirectoryEntry.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. + +namespace LibObjectFile.PE; + +public sealed class PEImportDirectoryEntry +{ + public PEImportDirectoryEntry(PEAsciiStringLink importDllNameLink, PEImportAddressTable importAddressTable, PEImportLookupTable importLookupTable) + { + ImportDllNameLink = importDllNameLink; + ImportAddressTable = importAddressTable; + ImportLookupTable = importLookupTable; + } + + public PEAsciiStringLink ImportDllNameLink { get; set; } + + 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; } + + 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 new file mode 100644 index 0000000..9d8721c --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionEntry.cs @@ -0,0 +1,94 @@ +// Copyright (c) Alexandre Mutel. All 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 PEStreamSectionData? _peSectionData; + private readonly ulong _offset; + private readonly bool _isLongOffset; + + /// + /// Initializes a new instance of the class by name. + /// + /// The name of the import. + public PEImportFunctionEntry(PEImportHintNameLink 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. + /// + /// The ordinal of the import. + 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 || 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 && !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); + + + 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 new file mode 100644 index 0000000..756b323 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEImportFunctionTable.cs @@ -0,0 +1,193 @@ +// Copyright (c) Alexandre Mutel. All 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.Collections; +using LibObjectFile.Diagnostics; +using LibObjectFile.PE.Internal; + +namespace LibObjectFile.PE; + +internal readonly struct PEImportFunctionTable() +{ + public List Entries { get; } = new(); + + public unsafe ulong CalculateSize(PEVisitorContext context) + { + var peFile = context.File; + // +1 for the null terminator + return Entries.Count == 0 ? 0 : (ulong)((Entries.Count + 1) * (peFile.IsPE32 ? sizeof(RawImportFunctionEntry32) : sizeof(RawImportFunctionEntry64))); + } + + public void Read(PEImageReader reader, ulong position) + { + var peFile = reader.File; + reader.Position = position; + + if (peFile.IsPE32) + { + Read32(reader); + } + else + { + Read64(reader); + } + } + + public void Bind(PEImageReader reader, bool allowOutOfRange) + { + var peFile = reader.File; + var diagnostics = reader.Diagnostics; + + var entries = CollectionsMarshal.AsSpan(Entries); + for (var i = 0; i < entries.Length; i++) + { + 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.TryFindByRVA(va, out var container)) + { + 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; + 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 PEImportHintNameLink(streamSectionData, va - container.RVA)); + } + } + } + + private unsafe void Read32(PEImageReader reader) + { + RawImportFunctionEntry32 entry = default; + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref entry, 1)); + while (true) + { + 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 PEImportHintNameLink(PEStreamSectionData.Empty, entry.HintNameTableRVA)) + ); + } + } + + private unsafe void Read64(PEImageReader reader) + { + RawImportFunctionEntry64 entry = default; + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref entry, 1)); + while (true) + { + 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) + : entry.HintNameTableRVA > uint.MaxValue ? new PEImportFunctionEntry(entry.HintNameTableRVA) : new PEImportFunctionEntry(new PEImportHintNameLink(PEStreamSectionData.Empty, (uint)entry.HintNameTableRVA)) + ); + } + } + + public void Write(PEImageWriter writer) + { + if (writer.PEFile.IsPE32) + { + Write32(writer); + } + else + { + Write64(writer); + } + } + + private unsafe void Write32(PEImageWriter writer) + { + using var tempSpan = TempSpan.Create(Entries.Count + 1, out var span); + + 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); + } + + // Last entry is null terminator + span[^1] = default; + + writer.Write(tempSpan); + } + + private unsafe void Write64(PEImageWriter writer) + { + using var tempSpan = TempSpan.Create(Entries.Count + 1, out var span); + for (var i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + 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; + + 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 new file mode 100644 index 0000000..15f206f --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEImportLookupTable.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; +using System.Collections.Generic; +using LibObjectFile.Diagnostics; + +namespace LibObjectFile.PE; + +public sealed class PEImportLookupTable : PESectionData, IEnumerable +{ + internal readonly PEImportFunctionTable FunctionTable; + + public PEImportLookupTable() + { + FunctionTable = new PEImportFunctionTable(); + } + + public override bool HasChildren => false; + + public List Entries => FunctionTable.Entries; + + protected override void UpdateLayoutCore(PELayoutContext context) + { + Size = FunctionTable.CalculateSize(context); + } + + public override void Read(PEImageReader reader) + { + FunctionTable.Read(reader, Position); + UpdateLayout(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(); + + public override void Verify(PEVerifyContext context) + { + FunctionTable.Verify(context, this); + } +} \ 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 new file mode 100644 index 0000000..aa371bd --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory.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 LibObjectFile.Diagnostics; +using System; +using System.Runtime.InteropServices; +using LibObjectFile.Utils; + +namespace LibObjectFile.PE; + +/// +/// Represents the Load Configuration Directory for a PE file. +/// +public abstract class PELoadConfigDirectory : PERawDataDirectory +{ + /// + /// Initializes a new instance of the class. + /// + private protected PELoadConfigDirectory(int minSize) : base(PEDataDirectoryKind.LoadConfig, minSize) + { + } + + /// + /// Change the recorded size of the Load Configuration Directory. + /// + /// + public void SetConfigSize(uint size) + { + SetRawDataSize(size); + + // Write back the configuration size + 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 new file mode 100644 index 0000000..7701302 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory32.cs @@ -0,0 +1,327 @@ +// Copyright (c) Alexandre Mutel. All 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.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +/// +/// Represents the Load Configuration Directory for a PE32 file. +/// +public class PELoadConfigDirectory32 : PELoadConfigDirectory +{ + /// + /// Initializes a new instance of the class. + /// + public unsafe PELoadConfigDirectory32() : base(sizeof(PELoadConfigDirectoryData32)) + { + SetConfigSize((uint)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 + { + [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 new file mode 100644 index 0000000..0e09070 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PELoadConfigDirectory64.cs @@ -0,0 +1,330 @@ +// Copyright (c) Alexandre Mutel. All 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; +using System.Runtime.InteropServices; + +namespace LibObjectFile.PE; + +/// +/// Represents the Load Configuration Directory for a PE64 file. +/// +public class PELoadConfigDirectory64 : PELoadConfigDirectory +{ + /// + /// Initializes a new instance of the class. + /// + public unsafe PELoadConfigDirectory64() : base(sizeof(PELoadConfigDirectoryData64)) + { + // ReSharper disable once VirtualMemberCallInConstructor + SetConfigSize((uint)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 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => 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..2c7e20f --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PERawDataDirectory.cs @@ -0,0 +1,105 @@ +// Copyright (c) Alexandre Mutel. All 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]; + RawDataSize = (uint)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 uint RawDataSize { get; private set; } + + /// + /// Sets the config size. + /// + /// The new config size. + /// + /// The underlying buffer will be resized if necessary. + /// + public virtual void SetRawDataSize(uint value) + { + 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((int)value); + span.Fill(0); + } + + RawDataSize = value; + } + + /// + public sealed 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; + } + + RawDataSize = (uint)size; + + HeaderSize = ComputeHeaderSize(reader); + } + + /// + public sealed override void Write(PEImageWriter writer) + { + var rawDataSize = RawDataSize; + var span = _rawData.AsSpan().Slice(0, (int)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 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/PEResourceData.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceData.cs new file mode 100644 index 0000000..505d096 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceData.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; + +/// +/// A section data that contains a resource data. +/// +public sealed class PEResourceData : PEStreamSectionData +{ + 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 new file mode 100644 index 0000000..3127978 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDataEntry.cs @@ -0,0 +1,137 @@ +// Copyright (c) Alexandre Mutel. All 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; +using LibObjectFile.Diagnostics; +using LibObjectFile.PE.Internal; +using LibObjectFile.Utils; + +namespace LibObjectFile.PE; + +/// +/// Represents a resource data entry in a PE file. +/// +public sealed class PEResourceDataEntry : PEResourceEntry +{ + internal PEResourceDataEntry() + { + Data = null!; + } + + /// + /// Initializes a new instance of the class. + /// + public PEResourceDataEntry(PEResourceData data) + { + Data = data; + } + + /// + /// Initializes a new instance of the class. + /// + public PEResourceDataEntry(Encoding? codePage, PEResourceData data) + { + CodePage = codePage; + Data = data; + } + + /// + /// 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 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)) + { + builder.Append(", "); + } + + builder.Append($"{nameof(CodePage)} = {CodePage?.EncodingName}, {nameof(Data)} = {Data}"); + return true; + } + + 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; + + reader.Position = Position; + Size = (uint)sizeof(RawImageResourceDataEntry); + + 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; + } + + CodePage = rawDataEntry.CodePage != 0 ? Encoding.GetEncoding((int)rawDataEntry.CodePage) : null; + Reserved = rawDataEntry.Reserved; + + var peFile = context.Reader.File; + 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; + } + + var resourceDataPosition = (uint)(section.Position + rawDataEntry.OffsetToData - section.RVA); + + if (!context.TryFindResourceDataByPosition(resourceDataPosition, out var resourceData)) + { + 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) + { + var rawDataEntry = new RawImageResourceDataEntry + { + OffsetToData = Data.RVA, + Size = (uint)Data.Size, + CodePage = (uint)(CodePage?.CodePage ?? 0), + Reserved = Reserved, + }; + + 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 new file mode 100644 index 0000000..7e7f7d0 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectory.cs @@ -0,0 +1,126 @@ +// Copyright (c) Alexandre Mutel. All 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.Collections.Generic; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace LibObjectFile.PE; + +/// +/// Represents a resource directory in a Portable Executable (PE) file. +/// +public sealed class PEResourceDirectory : PEDataDirectory +{ + private List? _tempResourceStrings; + private List? _tempResourceEntries; + + /// + /// 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(PELayoutContext context) + { + Root.UpdateLayout(context); + return (uint)Root.Size; + } + + /// + public override void Read(PEImageReader reader) + { + reader.Position = Position; + Root.Position = Position; + + _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) + foreach (var resourceEntry in _tempResourceEntries) + { + if (resourceEntry is not PEResourceDataEntry dataEntry) + { + continue; + } + + var resourceData = dataEntry.Data; + + // 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); + } + + HeaderSize = ComputeHeaderSize(reader); + } + + public override void Verify(PEVerifyContext context) + { + Root.Verify(context); + base.Verify(context); + } + + internal override IEnumerable CollectImplicitSectionDataList() + { + 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) + { + var resourceData = new HashSet(); + + foreach (var data in _tempResourceEntries) + { + yield return data; + + // 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; + } + } + + // We clear the list after being used - as this method is called once and we don't want to hold a reference + _tempResourceEntries.Clear(); + _tempResourceEntries = null; + } + } + + /// + 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 new file mode 100644 index 0000000..b7414f8 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceDirectoryEntry.cs @@ -0,0 +1,264 @@ +// Copyright (c) Alexandre Mutel. All 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.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using LibObjectFile.Collections; + +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 +{ + /// + /// Initializes a new instance of the class. + /// + public PEResourceDirectoryEntry() + { + ByNames = new(); + 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. + /// + public DateTime TimeDateStamp { get; set; } + + /// + /// Gets or sets the major version of the resource directory entry. + /// + public ushort MajorVersion { get; set; } + + /// + /// Gets or sets the minor version of the resource directory entry. + /// + public ushort MinorVersion { get; set; } + + /// + /// Gets the list of resource entries within the directory. + /// + public List ByNames { get; } + + /// + /// Gets the list of resource entries within the directory. + /// + public List ByIds { get; } + + internal override unsafe void Read(in ReaderContext context) + { + var reader = context.Reader; + + reader.Position = Position; + if (!reader.TryReadData(sizeof(RawImageResourceDirectory), out var data)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidResourceDirectoryEntry, $"Invalid resource directory at position {reader.Position}"); + return; + } + + Characteristics = data.Characteristics; + TimeDateStamp = DateTime.UnixEpoch.AddSeconds(data.TimeDateStamp); + MajorVersion = data.MajorVersion; + 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; + } + + // Read all entries + for (int i = 0; i < data.NumberOfNamedEntries + data.NumberOfIdEntries; i++) + { + var entry = spanEntries[i]; + ReadEntry(context, entry); + + if (reader.Diagnostics.HasErrors) + { + return; + } + } + + // Update the size + Size = reader.Position - Position; + + Debug.Assert(Size == CalculateSize()); + + // Read all strings (they should follow the directory) + var byNames = CollectionsMarshal.AsSpan(ByNames); + foreach (ref var item in byNames) + { + // 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); + } + + // Read the entry content + var byIds = CollectionsMarshal.AsSpan(ByIds); + foreach (ref var item in byIds) + { + context.Entries.Add(item.Entry); + item.Entry.Read(context); + } + } + + public override unsafe void Write(PEImageWriter writer) + { + var size = Size; + Debug.Assert(size == CalculateSize()); + var directory = (PEResourceDirectory)Parent!; + + // 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)); + + 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 = directory.Position; + + 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); + if (entry.Entry is PEResourceDirectoryEntry) + { + rawEntry.OffsetToDataOrDirectoryEntry |= IMAGE_RESOURCE_DATA_IS_DIRECTORY; + } + } + + 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 = (uint)(entry.Entry.Position - directoryPosition); + if (entry.Entry is PEResourceDirectoryEntry) + { + rawEntry.OffsetToDataOrDirectoryEntry |= IMAGE_RESOURCE_DATA_IS_DIRECTORY; + } + } + + writer.Write(span); + } + } + + 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; + + PEResourceEntry entry = isDirectory ? new PEResourceDirectoryEntry() : new PEResourceDataEntry(); + entry.Position = directory.Position + offset; + + if ((rawEntry.NameOrId & IMAGE_RESOURCE_NAME_IS_STRING) != 0) + { + 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)) + { + 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)); + } + } + + 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; + size += (uint)sizeof(RawImageResourceDirectory); + size += (uint)(ByNames.Count + ByIds.Count) * (uint)sizeof(RawImageResourceDirectoryEntry); + + return size; + } + + protected override bool PrintMembers(StringBuilder builder) + { + if (base.PrintMembers(builder)) + { + builder.Append(", "); + } + + builder.Append($"ByNames[{ByNames.Count}], ByIds[{ByIds.Count}] , TimeDateStamp = {TimeDateStamp}, MajorVersion = {MajorVersion}, MinorVersion = {MinorVersion}"); + + return true; + } + + 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/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..9292de7 --- /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(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 new file mode 100644 index 0000000..c88796c --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceEntry.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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace LibObjectFile.PE; + +/// +/// Represents an abstract base class for a PE resource entry. +/// +public abstract class PEResourceEntry : PESectionData +{ + public sealed override bool HasChildren => false; + + internal abstract void Read(in ReaderContext context); + + 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) + { + 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/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/DataDirectory/PEResourceString.cs b/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs new file mode 100644 index 0000000..f756821 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEResourceString.cs @@ -0,0 +1,96 @@ +// Copyright (c) Alexandre Mutel. All 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) + { + reader.Position = Position; + var length = reader.ReadU16(); + using var tempSpan = TempSpan.Create(length, out var span); + 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 = CalculateSize(); + } + + /// + public override void Write(PEImageWriter writer) + { + // TODO: validate string length <= ushort.MaxValue + writer.WriteU16((ushort)Text.Length); + writer.Write(MemoryMarshal.Cast(Text.AsSpan())); + } + + protected override void UpdateLayoutCore(PELayoutContext context) + { + Size = CalculateSize(); + } + + private uint CalculateSize() => (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; + } + + /// + 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/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/PESecurityCertificate.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificate.cs new file mode 100644 index 0000000..3e18d7d --- /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/PESecurityCertificateDirectory.cs b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs new file mode 100644 index 0000000..da171d9 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PESecurityCertificateDirectory.cs @@ -0,0 +1,146 @@ +// Copyright (c) Alexandre Mutel. All 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 PESecurityCertificateDirectory : PEExtraData +{ + /// + /// Initializes a new instance of the class. + /// + public PESecurityCertificateDirectory() + { + 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, + Data = reader.ReadAsStream(header.Length - (uint)sizeof(RawCertificateHeader)) + }; + + 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 = CalculateSize(); + Debug.Assert(computedSize == Size); + } + + private unsafe uint CalculateSize() + { + var size = 0u; + foreach (var certificate in Certificates) + { + size += (uint)sizeof(RawCertificateHeader); // CertificateSize + Version + Type + size += (uint)certificate.Data.Length; + size = AlignHelper.AlignUp(size, 8U); + } + + return size; + } + + protected override void UpdateLayoutCore(PELayoutContext context) + { + Size = CalculateSize(); + } + + public override unsafe void Write(PEImageWriter writer) + { + 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 + { +#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/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/PEThunkAddressTable.cs b/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs new file mode 100644 index 0000000..63b02eb --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PEThunkAddressTable.cs @@ -0,0 +1,113 @@ +// Copyright (c) Alexandre Mutel. All 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) + { + Is32Bits = is32Bits; + } + + public override bool HasChildren => false; + + 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) + { + var span = CollectionsMarshal.AsSpan(entries); + var bytes = MemoryMarshal.AsBytes(span); + writer.Write(bytes); + writer.WriteU32(0); + } + + private protected void Write64(PEImageWriter writer, List entries) + { + var span = CollectionsMarshal.AsSpan(entries); + var bytes = MemoryMarshal.AsBytes(span); + writer.Write(bytes); + writer.WriteU64(0); + } + + protected sealed override unsafe void UpdateLayoutCore(PELayoutContext context) + { + Size = (uint)((Count + 1) * (Is32Bits ? sizeof(VA32) : sizeof(VA64))); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000..ca6f770 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory.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 the Thread Local Storage (TLS) directory in a PE file. +/// +public abstract class PETlsDirectory : PERawDataDirectory +{ + /// + /// Initializes a new instance of the class. + /// + private protected PETlsDirectory(int minSize) : base(PEDataDirectoryKind.Tls, minSize) + { + } +} \ 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..a3b8820 --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory32.cs @@ -0,0 +1,108 @@ +// Copyright (c) Alexandre Mutel. All 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 the 32-bit Thread Local Storage (TLS) directory in a PE file. +/// +public sealed class PETlsDirectory32 : PETlsDirectory +{ + /// + /// Initializes a new instance of the class. + /// + public unsafe PETlsDirectory32() : base(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 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); + } + + public override unsafe void SetRawDataSize(uint value) + { + 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 new file mode 100644 index 0000000..efe921b --- /dev/null +++ b/src/LibObjectFile/PE/DataDirectory/PETlsDirectory64.cs @@ -0,0 +1,108 @@ +// Copyright (c) Alexandre Mutel. All 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 the 64-bit Thread Local Storage (TLS) directory in a PE file. +/// +public sealed class PETlsDirectory64 : PETlsDirectory +{ + /// + /// Initializes a new instance of the class. + /// + public unsafe PETlsDirectory64() : base(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 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(RawData)); + } + + public override unsafe void SetRawDataSize(uint value) + { + ArgumentOutOfRangeException.ThrowIfLessThan(value, (uint)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/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/IPELink.cs b/src/LibObjectFile/PE/IPELink.cs new file mode 100644 index 0000000..e410584 --- /dev/null +++ b/src/LibObjectFile/PE/IPELink.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; + +// ReSharper disable once InconsistentNaming + + +public interface IPELink; + +public interface IPELink : IPELink where TObject: PEObjectBase +{ + public TObject? Container { get; } + + public RVO RVO { get; } +} + + +public interface IPELink : IPELink where TObject : PEObjectBase +{ + 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 new file mode 100644 index 0000000..6d9bae4 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawDelayLoadDescriptor.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.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 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. + /// + 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/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 diff --git a/src/LibObjectFile/PE/Internal/RawImageDataDirectory.cs b/src/LibObjectFile/PE/Internal/RawImageDataDirectory.cs new file mode 100644 index 0000000..31aeb80 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageDataDirectory.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.Internal; + +#pragma warning disable CS0649 +/// +/// Data directory entry in the optional header of a Portable Executable (PE) file. +/// +[StructLayout(LayoutKind.Sequential, Pack = 4)] +internal struct RawImageDataDirectory +{ + /// + /// The relative virtual address of the data directory. + /// + public RVA RVA; + + /// + /// The size of the data directory, in bytes. + /// + public uint Size; +} \ No newline at end of file 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/RawImageDebugDirectory.cs b/src/LibObjectFile/PE/Internal/RawImageDebugDirectory.cs new file mode 100644 index 0000000..dc9d9d1 --- /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/Internal/RawImageOptionalHeader32.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.cs new file mode 100644 index 0000000..93ce0e8 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader32.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. + +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; + + // In case of a PE Header with zero directories + 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 new file mode 100644 index 0000000..514baf7 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageOptionalHeader64.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.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; + + // In case of a PE Header with zero directories + public static unsafe int MinimumSize => sizeof(RawImageOptionalHeader64) - sizeof(RawImageDataDirectoryArray); +} diff --git a/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase32.cs b/src/LibObjectFile/PE/Internal/RawImageOptionalHeaderBase32.cs new file mode 100644 index 0000000..bcb04df --- /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 RVA 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..4cd0c18 --- /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 PEOptionalHeaderMagic 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 RVA AddressOfEntryPoint; + + /// + /// The address relative to the image base of the beginning of the code section. + /// + public RVA 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..25c50c7 --- /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 RawImageDataDirectoryArray 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/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/Internal/RawImageSectionHeader.cs b/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs new file mode 100644 index 0000000..7372845 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImageSectionHeader.cs @@ -0,0 +1,114 @@ +// Copyright (c) Alexandre Mutel. All 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.Reflection.PortableExecutable; +using System.Runtime.InteropServices; +using System.Text; +using static System.Collections.Specialized.BitVector32; + +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); + } + } + + 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. + /// + 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 RVA RVA; + + /// + /// 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/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..b82fceb --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry32.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 readonly struct RawImportFunctionEntry32 +{ + private readonly uint _hintNameTableRVA; + + public RawImportFunctionEntry32(uint hintNameTableRVA) + { + _hintNameTableRVA = hintNameTableRVA; + } + + public uint HintNameTableRVA => IsImportByOrdinal ? 0U : _hintNameTableRVA; + + public bool IsNull => _hintNameTableRVA == 0; + + 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..8647393 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawImportFunctionEntry64.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 readonly struct RawImportFunctionEntry64 +{ + private readonly ulong _hintNameTableRVA; + + public RawImportFunctionEntry64(ulong hintNameTableRVA) + { + _hintNameTableRVA = hintNameTableRVA; + } + + public ulong HintNameTableRVA => IsImportByOrdinal ? 0U : _hintNameTableRVA; + + public bool IsNull => _hintNameTableRVA == 0; + + 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/Internal/RawPEBoundImportDirectory.cs b/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.cs new file mode 100644 index 0000000..5e64618 --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawPEBoundImportDirectory.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 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..d141c1f --- /dev/null +++ b/src/LibObjectFile/PE/Internal/RawPEBoundImportForwarderRef.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 RawPEBoundImportForwarderRef +{ + public uint TimeDateStamp; + public ushort OffsetModuleName; + public ushort Reserved; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PE.cd b/src/LibObjectFile/PE/PE.cd new file mode 100644 index 0000000..8e5f927 --- /dev/null +++ b/src/LibObjectFile/PE/PE.cd @@ -0,0 +1,414 @@ + + + + + + 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/PEAsciiStringLink.cs b/src/LibObjectFile/PE/PEAsciiStringLink.cs new file mode 100644 index 0000000..a4376ac --- /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. + +using System.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// A link to a null terminated ASCII string in a . +/// +[DebuggerDisplay("{ToString(),nq}")] +public readonly record struct PEAsciiStringLink(PEStreamSectionData? Container, RVO RVO) : IPELink +{ + /// + public override string ToString() => this.ToDisplayTextWithRVA(); + + /// + /// Resolves this link to a string. + /// + /// The string resolved. + public string? Resolve() => Container?.ReadAsciiString(RVO); +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PECoffHeader.cs b/src/LibObjectFile/PE/PECoffHeader.cs new file mode 100644 index 0000000..cb727f7 --- /dev/null +++ b/src/LibObjectFile/PE/PECoffHeader.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.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 PECoffHeader +{ + /// + /// The machine type that the file is intended for. + /// + public System.Reflection.PortableExecutable.Machine Machine; + + internal ushort _NumberOfSections; + + /// + /// The number of sections in the file. + /// + /// + /// 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. + /// + /// 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. + /// + /// 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. + /// + /// + /// 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. + /// + public System.Reflection.PortableExecutable.Characteristics Characteristics; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEDosHeader.cs b/src/LibObjectFile/PE/PEDosHeader.cs new file mode 100644 index 0000000..071ddf7 --- /dev/null +++ b/src/LibObjectFile/PE/PEDosHeader.cs @@ -0,0 +1,135 @@ +// Copyright (c) Alexandre Mutel. All 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; + +#pragma warning disable CS0649 +/// +/// Represents the DOS header of a PE file. +/// +[StructLayout(LayoutKind.Sequential, Pack = 2)] +public unsafe struct PEDosHeader +{ + /// + /// Magic number. (Original DOS field is `e_magic`) + /// + public PEDosMagic 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]; + + internal uint _FileAddressPEHeader; + + /// + /// File address of new exe header. (Original DOS field is `e_lfanew`) + /// + /// 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/PEDosMagic.cs b/src/LibObjectFile/PE/PEDosMagic.cs new file mode 100644 index 0000000..cefff80 --- /dev/null +++ b/src/LibObjectFile/PE/PEDosMagic.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 PEDosMagic : 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/PEExtraData.cs b/src/LibObjectFile/PE/PEExtraData.cs new file mode 100644 index 0000000..cec783c --- /dev/null +++ b/src/LibObjectFile/PE/PEExtraData.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; + +/// +/// Base class for extra data in a PE file accessible via . +/// +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.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.Read.cs b/src/LibObjectFile/PE/PEFile.Read.cs new file mode 100644 index 0000000..e9f0361 --- /dev/null +++ b/src/LibObjectFile/PE/PEFile.Read.cs @@ -0,0 +1,746 @@ +// Copyright (c) Alexandre Mutel. All 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.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection.PortableExecutable; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; +using LibObjectFile.PE.Internal; +using LibObjectFile.Utils; + +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; + diagnostics.EnableStackTrace = options.EnableStackTrace; + peFile.Read(reader); + + return !reader.Diagnostics.HasErrors; + } + + 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()) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosHeaderSize, "Invalid DOS header"); + return; + } + + if (DosHeader.Magic != PEDosMagic.DOS) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidDosHeaderMagic, "Invalid DOS header"); + return; + } + + var pePosition = DosHeader.FileAddressPEHeader; + + if (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 + { + // Read the DOS stub + var dosStubSize = DosHeader.SizeOfParagraphsHeader * 16; + + if (dosStubSize + sizeof(PEDosHeader) > 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; + } + + _dosStub = dosStub; + } + else + { + _dosStub = []; + } + + var dosHeaderAndStubSize = sizeof(PEDosHeader) + 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(PESignature); + read = reader.Read(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref signature, 1))); + if (read != sizeof(PESignature)) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidPESignature, "Invalid PE signature"); + return; + } + + 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(PECoffHeader); + read = reader.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 positionAfterCoffHeader = reader.Position; + + // Cannot be smaller than the magic + if (CoffHeader.SizeOfOptionalHeader < sizeof(PEOptionalHeaderMagic)) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidOptionalHeaderSize, $"Invalid optional header size {CoffHeader.SizeOfOptionalHeader}"); + return; + } + + var magic = (PEOptionalHeaderMagic)reader.ReadU16(); + + int minimumSizeOfOptionalHeaderToRead = 0; + + // Process known PE32/PE32+ headers + if (magic == PEOptionalHeaderMagic.PE32) + { + minimumSizeOfOptionalHeaderToRead = RawImageOptionalHeader32.MinimumSize; + } + else if (magic == PEOptionalHeaderMagic.PE32Plus) + { + minimumSizeOfOptionalHeaderToRead = RawImageOptionalHeader64.MinimumSize; + } + else + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidOptionalHeaderMagic, $"Invalid optional header PE magic 0x{(uint)magic:X8}"); + return; + } + + Span optionalHeader = stackalloc byte[minimumSizeOfOptionalHeaderToRead]; + MemoryMarshal.Write(optionalHeader, (ushort)magic); + + // We have already read the magic number + 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(PEOptionalHeaderMagic)); + + Debug.Assert(Unsafe.SizeOf() == 224); + Debug.Assert(Unsafe.SizeOf() == 240); + + // Process known PE32/PE32+ headers + if (magic == PEOptionalHeaderMagic.PE32) + { + var optionalHeader32 = new RawImageOptionalHeader32(); + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref optionalHeader32, 1)); + if (span.Length > optionalHeader.Length) + { + span = span.Slice(0, optionalHeader.Length); + } + + optionalHeader.CopyTo(span); + + OptionalHeader.OptionalHeaderCommonPart1 = optionalHeader32.Common; + OptionalHeader.OptionalHeaderBase32 = optionalHeader32.Base32; + OptionalHeader.OptionalHeaderCommonPart2 = optionalHeader32.Common2; + OptionalHeader.OptionalHeaderSize32 = optionalHeader32.Size32; + OptionalHeader.OptionalHeaderCommonPart3 = optionalHeader32.Common3; + OptionalHeader.SyncPE32ToPE32Plus(); + } + 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) + { + span = span.Slice(0, optionalHeader.Length); + } + + optionalHeader.CopyTo(span); + + OptionalHeader.OptionalHeaderCommonPart1 = optionalHeader64.Common; + OptionalHeader.OptionalHeaderBase64 = optionalHeader64.Base64; + OptionalHeader.OptionalHeaderCommonPart2 = optionalHeader64.Common2; + OptionalHeader.OptionalHeaderSize64 = optionalHeader64.Size64; + OptionalHeader.OptionalHeaderCommonPart3 = optionalHeader64.Common3; + } + + // Read Directory headers + 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.OptionalHeaderCommonPart3.NumberOfRvaAndSizes; + + 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.OptionalHeaderCommonPart3.NumberOfRvaAndSizes} at position {reader.Position}"); + return; + } + } + + // Read sections + reader.Position = positionAfterCoffHeader + CoffHeader.SizeOfOptionalHeader; + + Debug.Assert(Unsafe.SizeOf() == 40); + + using var pooledSpanSections = TempSpan.Create((int)CoffHeader.NumberOfSections, out var rawSections); + read = reader.Read(pooledSpanSections.AsBytes); + + if (read != pooledSpanSections.AsBytes.Length) + { + diagnostics.Error(DiagnosticId.PE_ERR_InvalidSectionHeadersSize, "Invalid section headers"); + return; + } + + // Read all sections and directories + ReadSectionsAndDirectories(reader, rawSections, rawDirectories); + + // Set the size to the full size of the file + Size = reader.Length; + } + + private void ReadSectionsAndDirectories(PEImageReader reader, ReadOnlySpan rawSectionHeaders, ReadOnlySpan rawDirectories) + { + _sections.Clear(); + + uint positionAfterHeaders = (uint)reader.Position; + uint positionFirstSection = positionAfterHeaders; + + // Load any data stored before the sections + if (rawSectionHeaders.Length > 0) + { + positionFirstSection = rawSectionHeaders[0].PointerToRawData; + } + + uint positionAfterLastSection = positionFirstSection; + + // Create sections + foreach (var rawSection in rawSectionHeaders) + { + // We don't validate the name + var section = new PESection( new PESectionName(rawSection.NameAsString, false), rawSection.RVA) + { + Position = rawSection.PointerToRawData, + Size = rawSection.SizeOfRawData, + Characteristics = rawSection.Characteristics, + }; + + // 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(section); + } + + // A list that contains all the section data that we have created (e.g. from directories, import tables...) + var sectionDataList = new List(); + var extraDataList = new List(); + + // Create directories and find the section for each directory + var maxNumberOfDirectory = (int)Math.Min(OptionalHeader.OptionalHeaderCommonPart3.NumberOfRvaAndSizes, 15); + for (int i = 0; i < maxNumberOfDirectory && i < rawDirectories.Length; i++) + { + var directoryEntry = rawDirectories[i]; + if (directoryEntry.RVA == 0 || directoryEntry.Size == 0) + { + continue; + } + + var kind = (PEDataDirectoryKind)i; + if (kind == PEDataDirectoryKind.SecurityCertificate) + { + var directory = new PESecurityCertificateDirectory + { + // The PE certificate directory is a special case as it is not a standard directory. It doesn't use RVA but the position in the file + Position = (uint)directoryEntry.RVA, + Size = directoryEntry.Size + }; + + Directories.Set(directory); + + // The PESecurityDirectory is a special case as it doesn't use RVA but the position in the file. It belongs after the sections to the extra data + extraDataList.Add(directory); + + directory.Read(reader); + } + else + { + + if (!TryFindSectionByRVA(directoryEntry.RVA, directoryEntry.Size, out var peSection)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidDataDirectorySection, $"Unable to find the section for the DataDirectory {(PEDataDirectoryKind)i} at virtual address {directoryEntry.RVA}, size {directoryEntry.Size}"); + continue; + } + + var directory = PEDataDirectory.Create((PEDataDirectoryKind)i, IsPE32); + var offsetInSection = directoryEntry.RVA - peSection.RVA; + directory.Position = peSection.Position + offsetInSection; + + directory.Size = directoryEntry.Size; + + Directories.Set(directory.Kind, directory); + + sectionDataList.Add(directory); + + // Read the content of the directory + directory.Read(reader); + } + + if (reader.Diagnostics.HasErrors) + { + return; + } + } + + // Get all implicit section data from directories after registering directories + for (var i = 0; i < Directories.Count; i++) + { + var entry = Directories[i]; + if (entry is not PEDataDirectory directory) + { + continue; + } + + // Add all implicit data + foreach (var implicitData in directory.CollectImplicitSectionDataList()) + { + if (implicitData is PESectionData sectionData) + { + sectionDataList.Add(sectionData); + } + else if (implicitData is PEExtraData extraData) + { + extraDataList.Add(extraData); + } + } + } + + // Process all extra data list + foreach(var extraData in extraDataList) + { + if (extraData.Position < positionFirstSection) + { + ExtraDataBeforeSections.Add(extraData); + } + else if (extraData.Position >= 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 + foreach (var vObj in sectionDataList) + { + if (!TryFindByPosition((uint)vObj.Position, out var container)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Unable to find a container for {vObj} at position {vObj.Position}"); + return; + } + + ObjectList 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, $"Invalid container {container} for {vObj} at position {vObj.Position}"); + return; + } + + int insertIndex = 0; + for (; insertIndex < dataParts.Count; insertIndex++) + { + var sectionData = dataParts[insertIndex]; + if (vObj.Position < sectionData.Position) + { + break; + } + } + + dataParts.Insert(insertIndex, vObj); + } + + // Make sure that we have a proper stream for all directories + for (var i = 0; i < Directories.Count; i++) + { + var entry = Directories[i]; + if (entry is PEDataDirectory directory) + { + FillDirectoryWithStreams(reader, directory); + } + } + + // Create all remaining extra data section data (before and after sections) + if (positionFirstSection > 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++) + { + var section = _sections[i]; + FillSectionDataWithMissingStreams(reader, section, section.Content, (uint)section.Position, (uint)section.Size, rawSectionHeaders[i].VirtualSize); + + var previousSize = rawSectionHeaders[i].SizeOfRawData; + + section.UpdateLayout(reader); + + var newSize = section.Size; + if (newSize != previousSize) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid size for section {section} at {section.Position} with calculated size 0x{section.Size:X} != size from disk 0x{previousSize:X}"); + return; + } + } + + // Final binding of directories with the actual section data + for (var i = 0; i < Directories.Count; i++) + { + var entry = Directories[i]; + + if (entry is PEDataDirectory directory) + { + Debug.Assert(directory.RVA == rawDirectories[i].RVA); + directory.Bind(reader); + } + } + + if (OptionalHeader.OptionalHeaderCommonPart1.BaseOfCode != 0) + { + // Find the section code + if (!this.TryFindSectionByRVA(OptionalHeader.OptionalHeaderCommonPart1.BaseOfCode, out var codeSection)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidBaseOfCode, $"Unable to find the section for BaseOfCode {OptionalHeader.OptionalHeaderCommonPart1.BaseOfCode}"); + return; + } + + OptionalHeader.BaseOfCode = codeSection; + } + + + // Find the entry address point + if (OptionalHeader.OptionalHeaderCommonPart1.AddressOfEntryPoint != 0) + { + if (!this.TryFindByRVA(OptionalHeader.OptionalHeaderCommonPart1.AddressOfEntryPoint, out var entryPointContainer)) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidAddressOfEntryPoint, $"Unable to find the section for AddressOfEntryPoint {OptionalHeader.OptionalHeaderCommonPart1.AddressOfEntryPoint}"); + return; + } + + if (entryPointContainer is not PESectionData entryPointSectionData) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidAddressOfEntryPoint, $"Invalid section for AddressOfEntryPoint {OptionalHeader.OptionalHeaderCommonPart1.AddressOfEntryPoint}. The object found is not a section data but {entryPointContainer}"); + return; + } + + OptionalHeader.AddressOfEntryPoint = new(entryPointSectionData, OptionalHeader.OptionalHeaderCommonPart1.AddressOfEntryPoint - entryPointSectionData.RVA); + } + + // Validate the VirtualAddress of the directories + for (int i = 0; i < maxNumberOfDirectory && i < rawDirectories.Length; i++) + { + var directoryEntry = rawDirectories[i]; + if (directoryEntry.RVA == 0 || directoryEntry.Size == 0) + { + continue; + } + + // We have the guarantee that the directory is not null + var directory = Directories[(PEDataDirectoryKind)i]!; + + if (directory is PEDataDirectory peDataDirectory) + { + if (peDataDirectory.RVA != directoryEntry.RVA) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid virtual address for directory {peDataDirectory.Kind} at {peDataDirectory.RVA} != {directoryEntry.RVA}"); + } + } + else if (directory is PESecurityCertificateDirectory peSecurityDirectory) + { + if (peSecurityDirectory.Position != directoryEntry.RVA) + { + reader.Diagnostics.Error(DiagnosticId.PE_ERR_InvalidInternalState, $"Invalid position for security directory at {peSecurityDirectory.Position} != {directoryEntry.RVA}"); + } + } + } + } + + /// + /// 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 parent, ObjectList dataParts, uint startPosition, uint totalSize, uint? totalVirtualSizeOpt) + { + var currentPosition = startPosition; + var endPosition = startPosition + totalSize; + + var listOrderedByPosition = dataParts.UnsafeList; + + // Early exit if we don't have any data + if (totalSize == 0 && listOrderedByPosition.Count == 0) + { + 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; + } + + 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) + { + var size = (uint)(data.Position - currentPosition); + imageReader.Position = currentPosition; + + var sectionData = new PEStreamSectionData(imageReader.ReadAsStream(size)) + { + Position = currentPosition, + Parent = parent, + }; + + 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; + + uint paddingSize = 0; + + if (totalVirtualSizeOpt.HasValue && totalVirtualSizeOpt.Value < totalSize) + { + paddingSize = totalSize - totalVirtualSizeOpt.Value; + } + + 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 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++) + { + var data = listOrderedByPosition[i]; + data.Index = i; + } + } + + private static void FillExtraDataWithMissingStreams(PEImageReader imageReader, PEObjectBase parent, ObjectList list, 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 = 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++) + { + var data = listOrderedByPosition[i]; + if (currentPosition < data.Position) + { + var size = data.Position - currentPosition; + imageReader.Position = currentPosition; + + var extraData = new PEStreamExtraData(imageReader.ReadAsStream(size)) + { + Position = currentPosition, + Parent = parent, + }; + + listOrderedByPosition.Insert(i, extraData); + currentPosition = data.Position; + i++; + } + else if (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 < endPosition) + { + var size = endPosition - currentPosition; + imageReader.Position = currentPosition; + var extraData = new PEStreamExtraData(imageReader.ReadAsStream(size)) + { + Position = currentPosition, + Parent = parent, + }; + + listOrderedByPosition.Add(extraData); + } + else if (currentPosition > endPosition) + { + 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++) + { + var data = listOrderedByPosition[i]; + data.Index = i; + } + } + + private static void FillDirectoryWithStreams(PEImageReader imageReader, PEDataDirectory directory) + { + 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.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 new file mode 100644 index 0000000..6cc4ba3 --- /dev/null +++ b/src/LibObjectFile/PE/PEFile.Write.cs @@ -0,0 +1,311 @@ +// Copyright (c) Alexandre Mutel. All 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.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; + +partial class PEFile +{ + /// + /// Writes this PE file to the specified stream. + /// + /// The stream to write to. + public void Write(Stream stream, PEImageWriterOptions? options = null) + { + if (!TryWrite(stream, out var diagnostics, options)) + { + 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, PEImageWriterOptions? options = null) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); + + var peWriter = new PEImageWriter(this, stream, options ?? PEImageWriterOptions.Default); + diagnostics = peWriter.Diagnostics; + + diagnostics.EnableStackTrace = peWriter.Options.EnableStackTrace; + + if (peWriter.Options.EnableChecksum && stream is not MemoryStream) + { + diagnostics.Error(DiagnosticId.PE_ERR_ChecksumNotSupported, "Checksum is only supported for MemoryStream"); + return false; + } + + // 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; + } + + public override unsafe void Write(PEImageWriter writer) + { + // 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 + writer.Write(DosHeader); + position += (uint)sizeof(PEDosHeader); + + // 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(PESignature.PE); + position += sizeof(PESignature); // PE00 header + + // COFF header + writer.Write(CoffHeader); + position += (uint)sizeof(PECoffHeader); + + // Update OptionalHeader + OptionalHeader.OptionalHeaderCommonPart1.AddressOfEntryPoint = OptionalHeader.AddressOfEntryPoint.RVA(); + OptionalHeader.OptionalHeaderCommonPart1.BaseOfCode = OptionalHeader.BaseOfCode?.RVA ?? 0; + + var optionalHeaderPosition = position; + + if (IsPE32) + { + RawImageOptionalHeader32 header32; + // Some properties are only set in PE32Plus so we copy them back in the PE32 header + OptionalHeader.SyncPE32PlusToPE32(); + header32.Common = OptionalHeader.OptionalHeaderCommonPart1; + header32.Base32 = OptionalHeader.OptionalHeaderBase32; + header32.Common2 = OptionalHeader.OptionalHeaderCommonPart2; + header32.Size32 = OptionalHeader.OptionalHeaderSize32; + header32.Common3 = OptionalHeader.OptionalHeaderCommonPart3; + var span = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref header32, 1)).Slice(0, RawImageOptionalHeader32.MinimumSize); + writer.Write(span); + position += (uint)span.Length; + } + else + { + RawImageOptionalHeader64 header64; + header64.Common = OptionalHeader.OptionalHeaderCommonPart1; + header64.Base64 = OptionalHeader.OptionalHeaderBase64; + header64.Common2 = OptionalHeader.OptionalHeaderCommonPart2; + header64.Size64 = OptionalHeader.OptionalHeaderSize64; + header64.Common3 = OptionalHeader.OptionalHeaderCommonPart3; + 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); + + // Write Section Headers + RawImageSectionHeader sectionHeader = default; + foreach (var section in _sections) + { + sectionHeader.SetName(section.Name); + 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) + { + Debug.Assert(position == extraData.Position); + 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]; + + 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}"); + return; + } + + if (data is PEDataDirectory directory) + { + directory.WriteHeaderAndContent(writer); + } + else + { + data.Write(writer); + } + + position += (uint)data.Size; + } + + // 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); + } + + // 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}"); + } + + 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; + } + } +} diff --git a/src/LibObjectFile/PE/PEFile.cs b/src/LibObjectFile/PE/PEFile.cs new file mode 100644 index 0000000..9ba9f9b --- /dev/null +++ b/src/LibObjectFile/PE/PEFile.cs @@ -0,0 +1,357 @@ +// Copyright (c) Alexandre Mutel. All 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.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; + +/// +/// A Portable Executable file that can be read, modified and written. +/// +public sealed partial class PEFile : PEObjectBase +{ + private byte[] _dosStub = []; + private Stream? _dosStubExtra; + private readonly ObjectList _sections; + + static PEFile() + { + // Required for resolving code pages + 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) + { + _sections = new(this); + Directories = new(); + ExtraDataBeforeSections = new(this); + ExtraDataAfterSections = new(this); + + DosHeader = PEDosHeader.Default; + DosStub = PEDosHeader.DefaultDosStub.ToArray(); + + CoffHeader = new() + { + Machine = Machine.Amd64, + TimeDateStamp = 0, + Characteristics = Characteristics.ExecutableImage | Characteristics.LargeAddressAware + }; + + 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()); + } + + /// + /// Internal constructor used to create a PEFile without initializing the DOS header. + /// + internal PEFile(bool unused) + { + _sections = new(this); + Directories = new(); + ExtraDataBeforeSections = new(this); + ExtraDataAfterSections = new(this); + + OptionalHeader = new PEOptionalHeader(this); + } + + /// + /// Gets the DOS header. + /// + public PEDosHeader 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 PECoffHeader CoffHeader; + + /// + /// Gets the optional header. + /// + public PEOptionalHeader OptionalHeader { get; } + + /// + /// Gets a boolean indicating whether this instance is a PE32 image. + /// + public bool IsPE32 => OptionalHeader.Magic == PEOptionalHeaderMagic.PE32; + + /// + /// Gets a boolean indicating whether this instance is a PE32+ image. + /// + public bool IsPE32Plus => OptionalHeader.Magic == PEOptionalHeaderMagic.PE32Plus; + + /// + /// Gets the directories. + /// + /// + /// Directories must be added/removed from or . + /// + public PEDirectoryTable Directories { get; } + + /// + /// Gets the data present before the sections in the file. + /// + public ObjectList ExtraDataBeforeSections { get; } + + /// + /// Gets the sections. + /// + public ObjectList Sections => _sections; + + /// + /// Gets the data present after the sections in the file (e.g ) + /// + public ObjectList ExtraDataAfterSections { get; } + + /// + /// 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, 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; + } + + 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 TryFindSectionByRVA(RVA rva, uint size, [NotNullWhen(true)] out PESection? section) + => _sections.TryFindByRVA(rva, size, out section); + + public bool TryFindByRVA(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); + 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.UnsafeList); + foreach (var trySection in sections) + { + if (trySection.Name == name) + { + section = trySection; + return true; + } + } + + section = null; + 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 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 && TryFindByRVA(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 && TryFindByRVA(rva, out result)) + { + offset = rva - result.RVA; + return true; + } + + result = null; + offset = 0; + 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; + } + } + } + } + + 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 PEOptionalHeaderMagic.PE32: + builder.Append("PE32"); + break; + case PEOptionalHeaderMagic.PE32Plus: + builder.Append("PE32+"); + break; + } + + builder.Append($"Directories[{Directories.Count}], Sections[{Sections.Count}]"); + return true; + } +} diff --git a/src/LibObjectFile/PE/PEFunctionAddressLink.cs b/src/LibObjectFile/PE/PEFunctionAddressLink.cs new file mode 100644 index 0000000..74ba7c8 --- /dev/null +++ b/src/LibObjectFile/PE/PEFunctionAddressLink.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. + +using System.Diagnostics; + +namespace LibObjectFile.PE; + +#pragma warning disable CS0649 +[DebuggerDisplay("{ToString(),nq}")] +public readonly record struct PEFunctionAddressLink(PEObject? Container, RVO RVO) : IPELink +{ + public override string ToString() => Container is not null ? $"{Container}, Offset = {RVO}" : $""; +} \ 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..5ea4a77 --- /dev/null +++ b/src/LibObjectFile/PE/PEImageReader.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.IO; + +namespace LibObjectFile.PE; + +public sealed class PEImageReader : ObjectFileReaderWriter +{ + internal PEImageReader(PEFile file, Stream stream, PEImageReaderOptions readerOptions) : base(file, stream) + { + Options = readerOptions; + LayoutContext = new PELayoutContext(File, Diagnostics, true); + } + + public new PEFile File => (PEFile)base.File; + + public PEImageReaderOptions Options { get; } + + public override bool KeepOriginalStreamForSubStreams => Options.UseSubStream; + + public PELayoutContext LayoutContext { get; } + + + public static implicit operator PELayoutContext(PEImageReader reader) => reader.LayoutContext; +} \ 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..e5a76f9 --- /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. + +namespace LibObjectFile.PE; + +public class PEImageReaderOptions +{ + public bool UseSubStream { get; init; } + + 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 new file mode 100644 index 0000000..bd95031 --- /dev/null +++ b/src/LibObjectFile/PE/PEImageWriter.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.IO; + +namespace LibObjectFile.PE; + +public sealed class PEImageWriter : ObjectFileReaderWriter +{ + 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/LibObjectFile/PE/PEImportHintName.cs b/src/LibObjectFile/PE/PEImportHintName.cs new file mode 100644 index 0000000..3a3f0a0 --- /dev/null +++ b/src/LibObjectFile/PE/PEImportHintName.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; + +/// +/// 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..58d1178 --- /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. + +using System.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// A link to a PE Import Hint Name in a . +/// +[DebuggerDisplay("{ToString(),nq}")] +public readonly record struct PEImportHintNameLink(PEStreamSectionData? Container, RVO RVO) : IPELink +{ + /// + public override string ToString() => this.ToDisplayTextWithRVA(); + + /// + /// Resolves this link to a PE Import Hint Name. + /// + /// The PE Import Hint Name resolved. + 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 new file mode 100644 index 0000000..cfc1fff --- /dev/null +++ b/src/LibObjectFile/PE/PEModuleHandleLink.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. + +using System.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// A link to a module handle. +/// +[DebuggerDisplay("{ToString(),nq}")] +public readonly record struct PEModuleHandleLink(PESection? Container, RVO RVO) : IPELink +{ + /// + public override string ToString() => this.ToDisplayTextWithRVA(); +} \ 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..a975b8d --- /dev/null +++ b/src/LibObjectFile/PE/PEObject.cs @@ -0,0 +1,221 @@ +// Copyright (c) Alexandre Mutel. All 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; +using LibObjectFile.Collections; + +namespace LibObjectFile.PE; + +/// +/// Base class for all Portable Executable (PE) objects that have a virtual address. +/// +public abstract class PEObject : PEObjectBase +{ + private uint _customVirtualSize; + private bool _useCustomVirtualSize; + + protected PEObject() + { + } + + /// + /// 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 uint VirtualSize => _useCustomVirtualSize ? _customVirtualSize : (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, [NotNullWhen(true)] out PEObject? result) + { + if (ContainsVirtual(rva)) + { + if (HasChildren && 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, [NotNullWhen(true)] out PEObject? result) + { + throw new NotImplementedException("This method must be implemented by PEVirtualObject with children"); + } + + internal void UpdateRVA(RVA rva) + { + RVA = rva; + if (HasChildren) + { + 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}, "); + base.PrintMembers(builder); + return true; + } + + 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 context) + { + var section = context.CurrentSection; + + if (section is null) + { + // Update the layout normally + base.UpdateLayout(context); + } + 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(context); + + // 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; + objectList = new ObjectList(parent, null, SectionDataAdded, null, SectionDataRemoved, null, SectionDataUpdated); + return objectList; + + void SectionDataAdded(ObjectElement vParent, TPEObject item) + { + // ReSharper disable once AccessToModifiedClosure + UpdateSectionDataRVA((PEObject)vParent, objectList, item.Index); + } + + void SectionDataRemoved(ObjectElement vParent, int index, TPEObject item) + { + // ReSharper disable once AccessToModifiedClosure + UpdateSectionDataRVA((PEObject)vParent, objectList, index); + } + + 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) + { + 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..3f215b3 --- /dev/null +++ b/src/LibObjectFile/PE/PEObjectBase.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.Diagnostics.CodeAnalysis; + +namespace LibObjectFile.PE; + +/// +/// Base class for all Portable Executable (PE) objects. +/// +[DebuggerDisplay("{ToString(),nq}")] +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(); + + /// + /// 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}"); + } + + 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. + /// + /// 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/RelocationSize.cs b/src/LibObjectFile/PE/PEObjectBaseExtensions.cs similarity index 60% rename from src/LibObjectFile/RelocationSize.cs rename to src/LibObjectFile/PE/PEObjectBaseExtensions.cs index 78cd8e5..34d37cb 100644 --- a/src/LibObjectFile/RelocationSize.cs +++ b/src/LibObjectFile/PE/PEObjectBaseExtensions.cs @@ -2,13 +2,4 @@ // 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 +namespace LibObjectFile.PE; diff --git a/src/LibObjectFile/PE/PEObjectExtensions.cs b/src/LibObjectFile/PE/PEObjectExtensions.cs new file mode 100644 index 0000000..73a8310 --- /dev/null +++ b/src/LibObjectFile/PE/PEObjectExtensions.cs @@ -0,0 +1,139 @@ +// Copyright (c) Alexandre Mutel. All 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 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. + /// + /// 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 TryFindByRVA(this ObjectList list, RVA virtualAddress, uint size, [NotNullWhen(true)] out TPEObject? item) + where TPEObject : PEObject + { + // 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.RVA) + { + 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 TryFindByRVA(this ObjectList list, RVA virtualAddress, bool recurse, [NotNullWhen(true)] out PEObject? item) + where TPEObject : PEObject + { + // 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)) + { + if (recurse && trySectionData.TryFindByRVA(virtualAddress, out var virtualItem)) + { + item = virtualItem; + } + else + { + item = trySectionData; + } + return true; + } + + if (virtualAddress > trySectionData.RVA) + { + low = mid + 1; + } + else + { + high = mid - 1; + } + } + + item = null; + 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/PEOptionalHeader.cs b/src/LibObjectFile/PE/PEOptionalHeader.cs new file mode 100644 index 0000000..088aaf5 --- /dev/null +++ b/src/LibObjectFile/PE/PEOptionalHeader.cs @@ -0,0 +1,387 @@ +// Copyright (c) Alexandre Mutel. All 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 System.Reflection.PortableExecutable; +using LibObjectFile.PE.Internal; + +namespace LibObjectFile.PE; + +public sealed class PEOptionalHeader +{ + internal RawImageOptionalHeaderCommonPart1 OptionalHeaderCommonPart1; + internal RawImageOptionalHeaderBase32 OptionalHeaderBase32; + internal RawImageOptionalHeaderBase64 OptionalHeaderBase64; + internal RawImageOptionalHeaderCommonPart2 OptionalHeaderCommonPart2; + internal RawImageOptionalHeaderSize32 OptionalHeaderSize32; + internal RawImageOptionalHeaderSize64 OptionalHeaderSize64; + internal RawImageOptionalHeaderCommonPart3 OptionalHeaderCommonPart3; + + 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; + OptionalHeaderBase64 = default; + OptionalHeaderCommonPart2 = default; + OptionalHeaderSize32 = default; + OptionalHeaderSize64 = default; + OptionalHeaderCommonPart3 = default; + + // Setup some fields to some default values + OptionalHeaderCommonPart1.Magic = magic; + OptionalHeaderCommonPart1.MajorLinkerVersion = 1; + + 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.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. + /// + /// + /// This value cannot be changed and must be set at construction time. + /// + public PEOptionalHeaderMagic Magic + { + get => OptionalHeaderCommonPart1.Magic; + } + + /// + /// 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; + } + + /// + /// The size of the initialized data section, in bytes. + /// + public uint SizeOfInitializedData + { + get => OptionalHeaderCommonPart1.SizeOfInitializedData; + } + + /// + /// The size of the uninitialized data section, in bytes. + /// + public uint SizeOfUninitializedData + { + get => OptionalHeaderCommonPart1.SizeOfUninitializedData; + } + + /// + /// The address of the entry point relative to the image base when the executable starts. + /// + public PESectionDataLink AddressOfEntryPoint + { + get => _entryPointLink; + set => _entryPointLink = value; + } + + /// + /// The address relative to the image base of the beginning of the code section. + /// + public PESection? BaseOfCode + { + get + { + return _baseOfCode; + } + set + { + _baseOfCode = value; + OptionalHeaderCommonPart1.BaseOfCode = _baseOfCode?.RVA ?? 0; + } + } + + /// + /// The address relative to the image base of the beginning of the data section. + /// + /// + /// Only valid for PE32. + /// + public RVA 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. + /// + /// + /// In order to change the ImageBase use + /// + public ulong ImageBase + { + get => OptionalHeaderBase64.ImageBase; + } + + /// + /// The alignment of sections in memory, in bytes. + /// + public uint SectionAlignment + { + get => OptionalHeaderCommonPart2.SectionAlignment; + 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; + } + } + + /// + /// The alignment of the raw data of sections in the image file, in bytes. + /// + public uint FileAlignment + { + get => OptionalHeaderCommonPart2.FileAlignment; + 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; + } + } + + /// + /// 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; + } + + /// + /// The combined size of all headers (DOS, PE, section headers), rounded up to a multiple of FileAlignment. + /// + public uint SizeOfHeaders + { + get => OptionalHeaderCommonPart2.SizeOfHeaders; + } + + /// + /// 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 => (uint)_peFile.Directories.Count; + } + + 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; + } + + 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/PEOptionalHeaderMagic.cs b/src/LibObjectFile/PE/PEOptionalHeaderMagic.cs new file mode 100644 index 0000000..d64f749 --- /dev/null +++ b/src/LibObjectFile/PE/PEOptionalHeaderMagic.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 PEOptionalHeaderMagic : ushort +{ + /// + /// PE32 + /// + PE32 = 0x10b, + + /// + /// PE32+ + /// + PE32Plus = 0x20b, +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEPrinter.cs b/src/LibObjectFile/PE/PEPrinter.cs new file mode 100644 index 0000000..bb68224 --- /dev/null +++ b/src/LibObjectFile/PE/PEPrinter.cs @@ -0,0 +1,765 @@ +// Copyright (c) Alexandre Mutel. All 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.IO; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace LibObjectFile.PE; + +public static class PEPrinter +{ + public static void Print(this PEFile file, TextWriter writer) + { + ArgumentNullException.ThrowIfNull(file); + ArgumentNullException.ThrowIfNull(writer); + + var indenter = new TextWriterIndenter(writer); + + PrintHeaders(file, ref indenter); + PrintDataDirectories(file, ref indenter); + PrintSections(file, ref indenter); + } + + private static void PrintHeaders(PEFile file, ref TextWriterIndenter writer) + { + PrintDosHeader(file, ref writer); + PrintDosStub(file, ref writer); + PrintCoffHeader(file, ref writer); + PrintOptionalHeader(file, ref writer); + } + + private static unsafe void PrintDosHeader(PEFile file, ref TextWriterIndenter writer) + { + const int indent = -26; + writer.WriteLine("DOS Header"); + writer.Indent(); + { + 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(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(); + } + + private static void PrintDosStub(PEFile file, ref TextWriterIndenter writer) + { + const int indent = -26; + writer.WriteLine("DOS Stub"); + writer.Indent(); + { + writer.WriteLine($"{nameof(file.DosStub),indent} = {file.DosStub.Length} bytes"); + } + writer.Unindent(); + writer.WriteLine(); + } + + private static void PrintCoffHeader(PEFile file, ref TextWriterIndenter writer) + { + const int indent = -26; + + writer.WriteLine("COFF Header"); + writer.Indent(); + { + 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(); + } + + private static void PrintOptionalHeader(PEFile file, ref TextWriterIndenter writer) + { + const int indent = -26; + writer.WriteLine("Optional Header"); + writer.Indent(); + { + 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} = {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}"); + 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(); + } + + private static void PrintDataDirectories(PEFile file, ref TextWriterIndenter writer) + { + if (file.Directories.Count == 0) return; + + writer.WriteLine("Data Directories"); + writer.Indent(); + for(int i = 0; i < file.Directories.Count; i++) + { + var kind = (PEDataDirectoryKind)i; + var directory = file.Directories[kind]; + writer.WriteLine(directory is null + ? $"[{i:00}] = null" + : $"[{i:00}] = {PEDescribe(directory)}"); + } + writer.Unindent(); + writer.WriteLine(); + } + + 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.WriteLine(heading); + writer.WriteLine($"[{i:00}] {section.Name,8} {PEDescribe(section)}, Characteristics = 0x{(uint)section.Characteristics:X8} ({section.Characteristics})"); + writer.WriteLine(); + if (section.Content.Count > 0) + { + foreach (var data in section.Content) + { + writer.Indent(); + PrintSectionData(file, data, ref writer); + writer.Unindent(); + } + } + } + writer.Unindent(); + } + + private static void PrintSectionData(PEFile file, PESectionData data, ref TextWriterIndenter writer) + { + writer.WriteLine($"[{data.Index:00}] {PEDescribe(data)}"); + writer.Indent(); + switch (data) + { + case PEBaseRelocationDirectory peBaseRelocationDirectory: + Print(peBaseRelocationDirectory, ref writer); + break; + case PEBaseRelocationBlock baseRelocationBlock: + Print(baseRelocationBlock, ref writer); + break; + case PEBoundImportDirectory peBoundImportDirectory: + Print(peBoundImportDirectory, ref writer); + break; + case PEClrMetadata peClrMetadata: + Print(peClrMetadata, ref writer); + break; + case PEArchitectureDirectory peArchitectureDirectory: + Print(peArchitectureDirectory, ref writer); + break; + case PEDebugDirectory peDebugDirectory: + Print(peDebugDirectory, ref writer); + break; + case PEDelayImportDirectory peDelayImportDirectory: + Print(peDelayImportDirectory, ref writer); + break; + case PEExceptionDirectory peExceptionDirectory: + Print(peExceptionDirectory, ref writer); + break; + case PEExportDirectory peExportDirectory: + Print(peExportDirectory, ref writer); + break; + case PEGlobalPointerDirectory peGlobalPointerDirectory: + Print(peGlobalPointerDirectory, ref writer); + break; + case PEImportAddressTableDirectory peImportAddressTableDirectory: + Print(peImportAddressTableDirectory, ref writer); + break; + case PEImportDirectory peImportDirectory: + Print(peImportDirectory, ref writer); + break; + case PELoadConfigDirectory32 peLoadConfigDirectory: + Print(peLoadConfigDirectory, ref writer); + break; + case PELoadConfigDirectory64 peLoadConfigDirectory: + Print(peLoadConfigDirectory, ref writer); + break; + case PEResourceDirectory peResourceDirectory: + Print(peResourceDirectory, ref writer); + break; + case PETlsDirectory32 peTlsDirectory32: + Print(peTlsDirectory32, ref writer); + break; + case PETlsDirectory64 peTlsDirectory64: + Print(peTlsDirectory64, ref writer); + break; + case PEBoundImportAddressTable32 peBoundImportAddressTable32: + Print(peBoundImportAddressTable32, ref writer); + break; + case PEBoundImportAddressTable64 peBoundImportAddressTable64: + Print(peBoundImportAddressTable64, ref writer); + break; + case PEDelayImportAddressTable peDelayImportAddressTable: + Print(peDelayImportAddressTable, ref writer); + break; + case PEExportAddressTable peExportAddressTable: + Print(peExportAddressTable, ref writer); + break; + case PEExportNameTable peExportNameTable: + Print(peExportNameTable, ref writer); + break; + case PEExportOrdinalTable peExportOrdinalTable: + Print(peExportOrdinalTable, ref writer); + break; + case PEImportAddressTable peImportAddressTable: + Print(peImportAddressTable, ref writer); + break; + case PEImportLookupTable peImportLookupTable: + Print(peImportLookupTable, ref writer); + break; + case PEStreamSectionData peStreamSectionData: + Print(peStreamSectionData, ref writer); + break; + case PEDebugSectionDataRSDS peDebugSectionDataRSDS: + Print(peDebugSectionDataRSDS, ref writer); + break; + case PEResourceEntry peResourceEntry: + Print(peResourceEntry, ref writer); + break; + default: + writer.WriteLine($"Unsupported section data {data}"); + break; + } + + if (data is PEDataDirectory directory && directory.Content.Count > 0) + { + foreach (var content in directory.Content) + { + PrintSectionData(file, content, ref writer); + } + } + + writer.Unindent(); + writer.WriteLine(); + } + + 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(PEBaseRelocationDirectory data, ref TextWriterIndenter writer) + { + } + + 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 reloc = block.Relocations[i]; + var relocRVA = block.GetRVA(reloc); + var offsetInPage = relocRVA - pageRVA; + + + var section = block.SectionLink.Container!; + section.TryFindSectionDataByRVA(relocRVA, out var sectionData); + + + 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) + { + foreach (var entry in data.Entries) + { + 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.Unindent(); + } + } + + private static void Print(PEClrMetadata data, ref TextWriterIndenter writer) + { + } + + private static void Print(PEArchitectureDirectory 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(PEDelayImportDirectory data, ref TextWriterIndenter writer) + { + for (var i = 0; i < data.Entries.Count; i++) + { + 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 {dirEntry.BoundImportAddressTableLink}"); + writer.WriteLine($"[{i}] UnloadDelayInformationTable {dirEntry.UnloadDelayInformationTableLink}"); + writer.WriteLine(); + } + } + + 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(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(PEGlobalPointerDirectory data, ref TextWriterIndenter writer) + { + } + + private static void Print(PEImportAddressTableDirectory 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(PELoadConfigDirectory32 data, ref TextWriterIndenter 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(PELoadConfigDirectory64 data, ref TextWriterIndenter 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(PEResourceDirectory data, ref TextWriterIndenter writer) + { + writer.Indent(); + Print(data.Root, ref writer); + writer.Unindent(); + } + + private static void Print(PEResourceEntry data, ref TextWriterIndenter writer) + { + switch (data) + { + case PEResourceDataEntry resourceFile: + writer.WriteLine($"> CodePage = {resourceFile.CodePage?.EncodingName ?? "null"}, Data = {resourceFile.Data}"); + break; + case PEResourceDirectoryEntry dir: + writer.WriteLine($"> ByNames[{dir.ByNames.Count}], ByIds[{dir.ByIds.Count}] , TimeDateStamp = {dir.TimeDateStamp}, Version = {dir.MajorVersion}.{dir.MinorVersion}"); + writer.Indent(); + + 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++) + { + var entry = dir.ByIds[i]; + writer.WriteLine($"[{i}] Id = {entry.Id}, Entry = {entry.Entry}"); + } + + writer.Unindent(); + break; + default: + throw new ArgumentOutOfRangeException(nameof(data)); + } + } + + 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(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(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(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(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.RVA()} ({PELink(entry.ForwarderRVA.Container)})"); + } + else + { + writer.WriteLine($"[{i}] Exported RVA = {entry.ExportRVA.RVA()} ({PELink(entry.ExportRVA.Container)})"); + } + } + } + + 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(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(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(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(PEStreamSectionData data, ref TextWriterIndenter 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 if (peObjectBase is not null) + { + return $"{peObjectBase.GetType().Name,indent} Position = 0x{peObjectBase.Position:X8}, Size = 0x{peObjectBase.Size:X8}"; + } + else + { + 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}"; + } + 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 ""; + } + } +} \ 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..9d95e4c --- /dev/null +++ b/src/LibObjectFile/PE/PESection.cs @@ -0,0 +1,245 @@ +// Copyright (c) Alexandre Mutel. All 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.IO; +using System.Reflection.PortableExecutable; +using System.Text; +using LibObjectFile.Collections; +using LibObjectFile.Diagnostics; +using LibObjectFile.Utils; + +namespace LibObjectFile.PE; + +/// +/// Defines a section in a Portable Executable (PE) image. +/// +public sealed class PESection : PEObject +{ + private readonly ObjectList _content; + private PESectionVirtualSizeMode _virtualSizeMode; + + public PESection(PESectionName name, RVA rva) + { + Name = name; + RVA = rva; + _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); + } + + /// + /// Gets the name of this section. + /// + public PESectionName Name { get; } + + public override bool HasChildren => true; + + /// + /// 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 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; + + /// + public override uint GetRequiredSizeAlignment(PEFile file) => file.OptionalHeader.FileAlignment; + + /// + /// 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 TryFindSectionDataByRVA(RVA virtualAddress, [NotNullWhen(true)] out PESectionData? sectionData) + { + var result = _content.TryFindByRVA(virtualAddress, true, out var sectionObj); + sectionData = sectionObj as PESectionData; + return result && sectionData is not null; + } + + /// + protected override void UpdateLayoutCore(PELayoutContext context) + { + context.CurrentSection = this; + try + { + var peFile = context.File; + + var va = RVA; + 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) + { + data.Position = position; + } + + data.UpdateLayout(context); + + var dataSize = AlignHelper.AlignUp((uint)data.Size, data.GetRequiredSizeAlignment(peFile)); + va += dataSize; + position += dataSize; + size += dataSize; + } + + if (_virtualSizeMode == PESectionVirtualSizeMode.Auto) + { + SetCustomVirtualSize(size); + } + + 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; + } + } + + public override void Verify(PEVerifyContext context) + { + foreach (var data in Content) + { + data.Verify(context); + } + } + + public override void Read(PEImageReader reader) + { + throw new NotImplementedException(); + } + + /// + public override void Write(PEImageWriter writer) + { + throw new NotImplementedException(); + } + + /// + protected override bool PrintMembers(StringBuilder builder) + { + builder.Append($"{Name} "); + base.PrintMembers(builder); + builder.Append($", Content[{Content.Count}]"); + return true; + } + + 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? + } + + protected override void ValidateParent(ObjectElement 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. + /// + /// 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 + }; + } +} \ 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..70993b9 --- /dev/null +++ b/src/LibObjectFile/PE/PESectionData.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 LibObjectFile.Utils; +using System; +using System.Runtime.CompilerServices; +using System.Text; + +namespace LibObjectFile.PE; + +/// +/// Base class for data contained in a . +/// +public abstract class PESectionData : PEObject +{ + protected PESectionData() + { + } + + protected override void ValidateParent(ObjectElement 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/PESectionDataExtensions.cs b/src/LibObjectFile/PE/PESectionDataExtensions.cs new file mode 100644 index 0000000..d58e506 --- /dev/null +++ b/src/LibObjectFile/PE/PESectionDataExtensions.cs @@ -0,0 +1,180 @@ +// Copyright (c) Alexandre Mutel. All 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.Security.Cryptography; +using System.Text; +using LibObjectFile.Collections; + +namespace LibObjectFile.PE; + +public static class PESectionDataExtensions +{ + 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(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(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++; + } + + using var tempSpan = TempSpan.Create(maxLength + 1 + (isHint ? 2 : 0), out var span); + span.Clear(); + + var text = span; + + if (isHint) + { + MemoryMarshal.Write(span, hint); + text = span.Slice(2); + } + + int actualLength = Encoding.ASCII.GetBytes(value, text); + text[actualLength] = 0; + if ((actualLength & 1) != 0) + { + actualLength++; + } + + if (isHint) + { + actualLength += 2; + } + + stream.Write(span.Slice(0, actualLength + 1)); + } + + 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 = stream.Read(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))); + } + + if (charBuffer is null) + { + throw new EndOfStreamException(); + } + + 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/PESectionDataLink.cs b/src/LibObjectFile/PE/PESectionDataLink.cs new file mode 100644 index 0000000..6193770 --- /dev/null +++ b/src/LibObjectFile/PE/PESectionDataLink.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. + +using System.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// A link to a section data. +/// +[DebuggerDisplay("{ToString(),nq}")] +public readonly record struct PESectionDataLink(PESectionData? Container, RVO RVO) : IPELink +{ + 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 new file mode 100644 index 0000000..de985c0 --- /dev/null +++ b/src/LibObjectFile/PE/PESectionLink.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. + +using System.Diagnostics; + +namespace LibObjectFile.PE; + +/// +/// A link to a section +/// +[DebuggerDisplay("{ToString(),nq}")] +public readonly record struct PESectionLink(PESection? Container, RVO RVO) : IPELink +{ + public override string ToString() => this.ToDisplayTextWithRVA(); +} \ 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..fab3579 --- /dev/null +++ b/src/LibObjectFile/PE/PESectionName.Defaults.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. + +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 new file mode 100644 index 0000000..56c5df1 --- /dev/null +++ b/src/LibObjectFile/PE/PESectionName.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.Text; +using System.Xml.Linq; + +namespace LibObjectFile.PE; + +/// +/// A section name in a Portable Executable (PE) image. +/// +public readonly partial 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; + + 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. + /// + /// + /// + /// > + /// 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 diff --git a/src/LibObjectFile/PE/PESignature.cs b/src/LibObjectFile/PE/PESignature.cs new file mode 100644 index 0000000..2ad6f00 --- /dev/null +++ b/src/LibObjectFile/PE/PESignature.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 PESignature : uint +{ + /// + /// PE00 + /// + PE = 0x00004550, +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEStreamExtraData.cs b/src/LibObjectFile/PE/PEStreamExtraData.cs new file mode 100644 index 0000000..193111a --- /dev/null +++ b/src/LibObjectFile/PE/PEStreamExtraData.cs @@ -0,0 +1,66 @@ +// Copyright (c) Alexandre Mutel. All 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 class PEStreamExtraData : PEExtraData +{ + private Stream _stream; + + /// + /// Initializes a new instance of the class. + /// + public PEStreamExtraData() + { + _stream = Stream.Null; + } + + /// + /// Initializes a new instance of the class. + /// + /// The data stream. + public PEStreamExtraData(Stream stream) + { + ArgumentNullException.ThrowIfNull(stream); + _stream = stream; + Size = (uint)stream.Length; + } + + /// + /// Gets or sets the data stream. + /// + public Stream Stream + { + get => _stream; + set + { + ArgumentNullException.ThrowIfNull(value); + _stream = value; + Size = (uint)value.Length; + } + } + + protected override void UpdateLayoutCore(PELayoutContext context) + { + Size = (uint)Stream.Length; + } + + public override void Read(PEImageReader reader) + { + reader.Position = Position; + Stream = reader.ReadAsStream(Size); + } + + public override void Write(PEImageWriter writer) + { + Stream.Position = 0; + Stream.CopyTo(writer.Stream); + } +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEStreamSectionData.cs b/src/LibObjectFile/PE/PEStreamSectionData.cs new file mode 100644 index 0000000..8f77966 --- /dev/null +++ b/src/LibObjectFile/PE/PEStreamSectionData.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; +using System.IO; + +namespace LibObjectFile.PE; + +/// +/// Gets a stream section data in a Portable Executable (PE) image. +/// +public class PEStreamSectionData : PESectionData +{ + private Stream _stream; + private uint _requiredPositionAlignment; + private uint _requiredSizeAlignment; + + internal static PEStreamSectionData Empty = new(System.IO.Stream.Null); + + /// + /// Initializes a new instance of the class. + /// + public PEStreamSectionData() : this(new MemoryStream()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream containing the data of this section data. + public PEStreamSectionData(Stream stream) + { + ArgumentNullException.ThrowIfNull(stream); + _stream = stream; + Size = (ulong)stream.Length; + _requiredPositionAlignment = 1; + _requiredSizeAlignment = 1; + } + + public override bool HasChildren => false; + + /// + /// Gets the stream containing the data of this section data. + /// + public Stream Stream + { + get => _stream; + set + { + ArgumentNullException.ThrowIfNull(value); + _stream = value; + Size = (ulong)value.Length; + } + } + + /// + /// 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; + } + } + + protected override void UpdateLayoutCore(PELayoutContext context) + { + Size = (ulong)Stream.Length; + } + + public override void Read(PEImageReader reader) + { + reader.Position = Position; + Stream = reader.ReadAsStream(Size); + } + + 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); + } + + public override uint GetRequiredPositionAlignment(PEFile file) => _requiredPositionAlignment; + + public override uint GetRequiredSizeAlignment(PEFile file) => _requiredSizeAlignment; +} \ No newline at end of file diff --git a/src/LibObjectFile/PE/PEVisitorContext.cs b/src/LibObjectFile/PE/PEVisitorContext.cs new file mode 100644 index 0000000..8180cd7 --- /dev/null +++ b/src/LibObjectFile/PE/PEVisitorContext.cs @@ -0,0 +1,55 @@ +// Copyright (c) Alexandre Mutel. All 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; + +namespace LibObjectFile.PE; + +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; } + + 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 diff --git a/src/LibObjectFile/PE/RVA.cs b/src/LibObjectFile/PE/RVA.cs new file mode 100644 index 0000000..cb1908b --- /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() => $"0x{Value:X}"; +} diff --git a/src/LibObjectFile/PE/RVO.cs b/src/LibObjectFile/PE/RVO.cs new file mode 100644 index 0000000..f222881 --- /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 a 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 diff --git a/src/LibObjectFile/PE/VA32.cs b/src/LibObjectFile/PE/VA32.cs new file mode 100644 index 0000000..92073a4 --- /dev/null +++ b/src/LibObjectFile/PE/VA32.cs @@ -0,0 +1,36 @@ +// Copyright (c) Alexandre Mutel. All 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 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; + + /// + /// 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..c8c7f1a --- /dev/null +++ b/src/LibObjectFile/PE/VA64.cs @@ -0,0 +1,36 @@ +// Copyright (c) Alexandre Mutel. All 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 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; + + /// + /// 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/Utils/AlignHelper.cs b/src/LibObjectFile/Utils/AlignHelper.cs index e2daf53..b24cfde 100644 --- a/src/LibObjectFile/Utils/AlignHelper.cs +++ b/src/LibObjectFile/Utils/AlignHelper.cs @@ -1,41 +1,48 @@ -// 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.Numerics; using System.Runtime.CompilerServices; -namespace LibObjectFile.Utils +namespace LibObjectFile.Utils; + +/// +/// Helper class to perform alignment. +/// +public static class AlignHelper { /// - /// Helper class to perform alignment. + /// Aligns a value to the required alignment. /// - public static class AlignHelper + /// The value to align. + /// The alignment. + /// The value aligned or unchanged it is was already aligned. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong AlignUp(ulong value, 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; - } - - /// - /// 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"); + 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; + } + /// + /// 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; - } + var nextValue = ((value + align - 1) / align) * align; + return nextValue; } + + } \ 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..9e24f64 --- /dev/null +++ b/src/LibObjectFile/Utils/DataUtils.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. + +using System; + +namespace LibObjectFile.Utils; + +internal static class DataUtils +{ + public static int ReadAt(ReadOnlySpan source, uint offset, Span destination) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, (uint)source.Length); + + var lengthToCopy = Math.Min(source.Length - (int)offset, destination.Length); + source.Slice((int)offset, lengthToCopy).CopyTo(destination); + + return lengthToCopy; + } + + public static unsafe void WriteAt(Span destination, uint offset, ReadOnlySpan source) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, (uint)destination.Length); + + if (source.Length > (destination.Length - (int)offset)) + { + 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 diff --git a/src/LibObjectFile/Utils/SliceStream.cs b/src/LibObjectFile/Utils/SliceStream.cs deleted file mode 100644 index c5c9765..0000000 --- a/src/LibObjectFile/Utils/SliceStream.cs +++ /dev/null @@ -1,148 +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.IO; - -namespace LibObjectFile.Utils -{ - /// - /// 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) - { - 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(); - - long remaining = _length - _localPosition; - if (remaining <= 0) return 0; - if (remaining < count) count = (int)remaining; - - _baseStream.Position = _basePosition + _localPosition; - int read = _baseStream.Read(buffer, offset, count); - _localPosition += read; - - 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 - { - get { ThrowIfDisposed(); return _baseStream.CanSeek; } - } - public override long Position - { - get - { - ThrowIfDisposed(); - return _localPosition; - } - set => Seek(value, SeekOrigin.Begin); - } - 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}"); - - // Check that we can seek on the origin stream - _baseStream.Position = _basePosition + newPosition; - _localPosition = newPosition; - - return newPosition; - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Flush() - { - ThrowIfDisposed(); _baseStream.Flush(); - } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - if (_baseStream != null) - { - try - { - _baseStream.Dispose(); - } - catch - { - // ignored - } - _baseStream = null; - } - } - } - public override void Write(byte[] buffer, int offset, int count) - { - 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"); - } - } - } -} diff --git a/src/LibObjectFile/Utils/ThrowHelper.cs b/src/LibObjectFile/Utils/ThrowHelper.cs deleted file mode 100644 index 7382392..0000000 --- a/src/LibObjectFile/Utils/ThrowHelper.cs +++ /dev/null @@ -1,84 +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.IO; -using System.Text; - -namespace LibObjectFile.Utils -{ - /// - /// 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 - { - /// - /// 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)) - { - 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, out string text) - { - text = null; - var buffer = ArrayPool.Shared.Rent((int)128); - int textLength = 0; - try - { - while (true) - { - // TODO: not efficient to read byte by byte - int nextByte = stream.ReadByte(); - if (nextByte < 0) - { - return false; - } - - 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; - } - - 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 deleted file mode 100644 index 70aa41a..0000000 --- a/src/LibObjectFile/ValueKind.cs +++ /dev/null @@ -1,22 +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 the way a value is calculated. - /// - public enum ValueKind - { - /// - /// The associated value is automatically calculated by the system. - /// - Auto, - - /// - /// 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..af8acec --- /dev/null +++ b/src/LibObjectFile/VisitorContextBase.cs @@ -0,0 +1,38 @@ +// Copyright (c) Alexandre Mutel. All 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.IO; + +namespace 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; + + public TextWriter? DebugLog { get; set; } +} + +/// +/// 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 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) { 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..192b364 --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeConsole2Win64/NativeConsole2Win64.vcxproj @@ -0,0 +1,148 @@ + + + + + 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 + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + NativeLibraryWin64.dll + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + NativeLibraryWin64.dll + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + NativeLibraryWin64.dll + true + + + + + + + + {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..17fdbf8 --- /dev/null +++ b/src/native/Win64/NativeProjects/NativeProjects.sln @@ -0,0 +1,61 @@ + +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 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RawNativeConsoleWin64", "RawNativeConsoleWin64\RawNativeConsoleWin64.vcxproj", "{269227A8-1C63-4EB1-88CF-5C80B7C61F0F}" +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 + {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 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {46F09478-29CA-4093-831B-DCAF8ECB4A49} + EndGlobalSection +EndGlobal diff --git a/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.cpp b/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.cpp new file mode 100644 index 0000000..b9dd07c --- /dev/null +++ b/src/native/Win64/NativeProjects/RawNativeConsoleWin64/RawNativeConsoleWin64.cpp @@ -0,0 +1,6 @@ +#include + +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 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 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 @@ - - + +