diff --git a/readme.md b/readme.md index 50c09fc..c491ae8 100644 --- a/readme.md +++ b/readme.md @@ -49,6 +49,7 @@ elf.Write(outStream); - Symbol Table - Relocation Table: supported I386, X86_64, ARM and AARCH64 relocations (others can be exposed by adding some mappings) - Note Table + - Dynamic Linking Table (`SHT_DYNAMIC`) - Other sections fallback to `ElfCustomSection` - Program headers with or without sections - Print with `readelf` similar output diff --git a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs index fe22ac4..9b3511a 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -438,6 +438,131 @@ public async Task SimpleProgramHeaderAndCodeSectionAndSymbolSectionAndRelocation } + [TestMethod] + public async Task SimpleDynamicSection() + { + var elf = new ElfFile(ElfArch.X86_64); + + var dynstr = new ElfStringTable() { Name = ".dynstr" }; + elf.Add(dynstr); + + var dynamic = new ElfDynamicLinkingTable() { Link = dynstr }; + dynamic.AddNeededLibrary("libc.so.6"); + dynamic.AddNeededLibrary("libm.so.6"); + dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Flags1, Value = 0x08000001 }); + dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Null }); + elf.Add(dynamic); + + elf.Add(new ElfSectionHeaderStringTable()); + elf.Add(new ElfSectionHeaderTable()); + + // The read helpers resolve DT_NEEDED offsets through the linked .dynstr. + CollectionAssert.AreEqual(new[] { "libc.so.6", "libm.so.6" }, dynamic.GetNeededLibraries().ToList()); + + await AssertReadElf(elf, "test_dynamic.elf"); + + // Round-trip the entries and confirm the resolved library names survive write -> read. + var memoryStream = new MemoryStream(); + elf.Write(memoryStream); + memoryStream.Position = 0; + var roundTrip = ElfFile.Read(memoryStream); + var roundTripDynamic = roundTrip.Sections.OfType().Single(); + Assert.AreEqual(dynamic.Entries.Count, roundTripDynamic.Entries.Count); + CollectionAssert.AreEqual(new[] { "libc.so.6", "libm.so.6" }, roundTripDynamic.GetNeededLibraries().ToList()); + } + + [TestMethod] + public void DynamicSectionStreamRoundTrip() + { + var elf = new ElfFile(ElfArch.X86_64); + var dynstr = new ElfStringTable() { Name = ".dynstr" }; + elf.Add(dynstr); + + var dynamic = new ElfDynamicLinkingTable() { Link = dynstr }; + dynamic.AddNeededLibrary("libc.so.6"); + dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Flags1, Value = 0x08000001 }); + dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Null }); + elf.Add(dynamic); + + // Serialize just the .dynamic entries to a standalone stream (64-bit -> 16 bytes each). + var stream = new MemoryStream(); + dynamic.Write(stream); + Assert.AreEqual(dynamic.Entries.Count * 16, stream.Length); + + // Read them back into a fresh table attached to another file and confirm value equality. + var elf2 = new ElfFile(ElfArch.X86_64); + var dynamic2 = new ElfDynamicLinkingTable(); + elf2.Add(dynamic2); + stream.Position = 0; + dynamic2.Read(stream); + + CollectionAssert.AreEqual(dynamic.Entries, dynamic2.Entries); + } + + [TestMethod] + public void DynamicSection32Bit() + { + var elf = new ElfFile(ElfArch.I386); + var dynstr = new ElfStringTable() { Name = ".dynstr" }; + elf.Add(dynstr); + + var dynamic = new ElfDynamicLinkingTable() { Link = dynstr }; + dynamic.AddNeededLibrary("libc.so.6"); + dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Flags, Value = 0x8 }); // DF_BIND_NOW + dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Null }); + elf.Add(dynamic); + + // The printer uses 8-digit tag hex for 32-bit files. + var writer = new StringWriter(); + ElfPrinter.PrintDynamicSections(elf, writer); + var text = writer.ToString(); + StringAssert.Contains(text, " 0x00000001 (NEEDED)"); + StringAssert.Contains(text, "Shared library: [libc.so.6]"); + StringAssert.Contains(text, " 0x0000001e (FLAGS)"); + StringAssert.Contains(text, "BIND_NOW"); + + // The Read32/Write32 path uses 8-byte Elf32_Dyn records. + var stream = new MemoryStream(); + dynamic.Write(stream); + Assert.AreEqual(dynamic.Entries.Count * 8, stream.Length); + + var elf2 = new ElfFile(ElfArch.I386); + var dynamic2 = new ElfDynamicLinkingTable(); + elf2.Add(dynamic2); + stream.Position = 0; + dynamic2.Read(stream); + + CollectionAssert.AreEqual(dynamic.Entries, dynamic2.Entries); + } + + [TestMethod] + public void SectionHeader64BitFieldsNotTruncated() + { + // Regression: the 64-bit section header writer cast sh_addr/sh_offset/sh_size/etc. to uint, + // truncating any value >= 4 GiB. A NOBITS section declares a huge address/size with no file bytes. + var elf = new ElfFile(ElfArch.X86_64); + + var bss = new ElfNoBitsSection + { + Name = ".hugebss", + VirtualAddress = 0x1_0000_2000UL, // > 4 GiB + Size = 0x2_0000_1000UL, // > 4 GiB + VirtualAddressAlignment = 0x1000, + }; + elf.Add(bss); + elf.Add(new ElfSectionHeaderStringTable()); + elf.Add(new ElfSectionHeaderTable()); + + var stream = new MemoryStream(); + elf.Write(stream); + stream.Position = 0; + var roundTrip = ElfFile.Read(stream); + + var bss2 = roundTrip.Sections.OfType().Single(); + Assert.AreEqual(0x1_0000_2000UL, bss2.VirtualAddress); + Assert.AreEqual(0x2_0000_1000UL, bss2.Size); + } + [TestMethod] public async Task TestAlignedSection() { diff --git a/src/LibObjectFile.Tests/Verified/ElfSimpleTests.SimpleDynamicSection.verified.txt b/src/LibObjectFile.Tests/Verified/ElfSimpleTests.SimpleDynamicSection.verified.txt new file mode 100644 index 0000000..64b427d --- /dev/null +++ b/src/LibObjectFile.Tests/Verified/ElfSimpleTests.SimpleDynamicSection.verified.txt @@ -0,0 +1,48 @@ +ELF Header: + Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 + Class: ELF64 + Data: 2's complement, little endian + Version: 1 (current) + OS/ABI: UNIX - System V + ABI Version: 0 + Type: REL (Relocatable file) + Machine: Advanced Micro Devices X86-64 + Version: 0x1 + Entry point address: 0x0 + Start of program headers: 0 (bytes into file) + Start of section headers: 0 (bytes into file) + Flags: 0x0 + Size of this header: 0 (bytes) + Size of program headers: 0 (bytes) + Number of program headers: 0 + Size of section headers: 0 (bytes) + Number of section headers: 4 + Section header string table index: 3 + +Section Headers: + [Nr] Name Type Address Off Size ES Flg Lk Inf Al + [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 + [ 1] .dynstr STRTAB 0000000000000000 000000 000000 00 0 0 0 + [ 2] .dynamic DYNAMIC 0000000000000000 000000 000000 10 A 1 0 0 + [ 3] .shstrtab STRTAB 0000000000000000 000000 000000 00 0 0 0 +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) + +There are no section groups in this file. + +There are no program headers in this file. + +Dynamic section at offset 0x0 contains 4 entries: + Tag Type Name/Value + 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] + 0x0000000000000001 (NEEDED) Shared library: [libm.so.6] + 0x000000006ffffffb (FLAGS_1) Flags: NOW PIE + 0x0000000000000000 (NULL) 0x0 + +There are no relocations in this file. +No processor specific unwind information to decode + +No version information found in this file. diff --git a/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=helloworld.verified.txt b/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=helloworld.verified.txt index e66a256..86e6fed 100644 --- a/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=helloworld.verified.txt +++ b/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=helloworld.verified.txt @@ -92,7 +92,35 @@ Program Headers: 11 12 .init_array .fini_array .dynamic .got -There is no dynamic section in this file. +Dynamic section at offset 0x2dc8 contains 27 entries: + Tag Type Name/Value + 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] + 0x000000000000000c (INIT) 0x1000 + 0x000000000000000d (FINI) 0x1174 + 0x0000000000000019 (INIT_ARRAY) 0x3db8 + 0x000000000000001b (INIT_ARRAYSZ) 8 (bytes) + 0x000000000000001a (FINI_ARRAY) 0x3dc0 + 0x000000000000001c (FINI_ARRAYSZ) 8 (bytes) + 0x000000006ffffef5 (GNU_HASH) 0x3b0 + 0x0000000000000005 (STRTAB) 0x480 + 0x0000000000000006 (SYMTAB) 0x3d8 + 0x000000000000000a (STRSZ) 141 (bytes) + 0x000000000000000b (SYMENT) 24 (bytes) + 0x0000000000000015 (DEBUG) 0x0 + 0x0000000000000003 (PLTGOT) 0x3fb8 + 0x0000000000000002 (PLTRELSZ) 24 (bytes) + 0x0000000000000014 (PLTREL) RELA + 0x0000000000000017 (JMPREL) 0x610 + 0x0000000000000007 (RELA) 0x550 + 0x0000000000000008 (RELASZ) 192 (bytes) + 0x0000000000000009 (RELAENT) 24 (bytes) + 0x000000000000001e (FLAGS) BIND_NOW + 0x000000006ffffffb (FLAGS_1) Flags: NOW PIE + 0x000000006ffffffe (VERNEED) 0x520 + 0x000000006fffffff (VERNEEDNUM) 1 + 0x000000006ffffff0 (VERSYM) 0x50e + 0x000000006ffffff9 (RELACOUNT) 3 + 0x0000000000000000 (NULL) 0x0 Relocation section '.rela.dyn' at offset 0x550 contains 8 entries: Offset Info Type Symbol's Value Symbol's Name + Addend diff --git a/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=helloworld_debug.verified.txt b/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=helloworld_debug.verified.txt index 600ecf5..98b13bd 100644 --- a/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=helloworld_debug.verified.txt +++ b/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=helloworld_debug.verified.txt @@ -97,7 +97,35 @@ Program Headers: 11 12 .init_array .fini_array .dynamic .got -There is no dynamic section in this file. +Dynamic section at offset 0x2dc8 contains 27 entries: + Tag Type Name/Value + 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] + 0x000000000000000c (INIT) 0x1000 + 0x000000000000000d (FINI) 0x1174 + 0x0000000000000019 (INIT_ARRAY) 0x3db8 + 0x000000000000001b (INIT_ARRAYSZ) 8 (bytes) + 0x000000000000001a (FINI_ARRAY) 0x3dc0 + 0x000000000000001c (FINI_ARRAYSZ) 8 (bytes) + 0x000000006ffffef5 (GNU_HASH) 0x3b0 + 0x0000000000000005 (STRTAB) 0x480 + 0x0000000000000006 (SYMTAB) 0x3d8 + 0x000000000000000a (STRSZ) 141 (bytes) + 0x000000000000000b (SYMENT) 24 (bytes) + 0x0000000000000015 (DEBUG) 0x0 + 0x0000000000000003 (PLTGOT) 0x3fb8 + 0x0000000000000002 (PLTRELSZ) 24 (bytes) + 0x0000000000000014 (PLTREL) RELA + 0x0000000000000017 (JMPREL) 0x610 + 0x0000000000000007 (RELA) 0x550 + 0x0000000000000008 (RELASZ) 192 (bytes) + 0x0000000000000009 (RELAENT) 24 (bytes) + 0x000000000000001e (FLAGS) BIND_NOW + 0x000000006ffffffb (FLAGS_1) Flags: NOW PIE + 0x000000006ffffffe (VERNEED) 0x520 + 0x000000006fffffff (VERNEEDNUM) 1 + 0x000000006ffffff0 (VERSYM) 0x50e + 0x000000006ffffff9 (RELACOUNT) 3 + 0x0000000000000000 (NULL) 0x0 Relocation section '.rela.dyn' at offset 0x550 contains 8 entries: Offset Info Type Symbol's Value Symbol's Name + Addend diff --git a/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=lib_debug.so.verified.txt b/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=lib_debug.so.verified.txt index e8419d5..2dd86df 100644 --- a/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=lib_debug.so.verified.txt +++ b/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=lib_debug.so.verified.txt @@ -87,7 +87,25 @@ Program Headers: 09 10 .init_array .fini_array .dynamic .got -There is no dynamic section in this file. +Dynamic section at offset 0x2e90 contains 17 entries: + Tag Type Name/Value + 0x000000000000000c (INIT) 0x1000 + 0x000000000000000d (FINI) 0x1130 + 0x0000000000000019 (INIT_ARRAY) 0x3e80 + 0x000000000000001b (INIT_ARRAYSZ) 8 (bytes) + 0x000000000000001a (FINI_ARRAY) 0x3e88 + 0x000000000000001c (FINI_ARRAYSZ) 8 (bytes) + 0x000000006ffffef5 (GNU_HASH) 0x2f0 + 0x0000000000000005 (STRTAB) 0x3c0 + 0x0000000000000006 (SYMTAB) 0x318 + 0x000000000000000a (STRSZ) 122 (bytes) + 0x000000000000000b (SYMENT) 24 (bytes) + 0x0000000000000003 (PLTGOT) 0x4000 + 0x0000000000000007 (RELA) 0x440 + 0x0000000000000008 (RELASZ) 168 (bytes) + 0x0000000000000009 (RELAENT) 24 (bytes) + 0x000000006ffffff9 (RELACOUNT) 3 + 0x0000000000000000 (NULL) 0x0 Relocation section '.rela.dyn' at offset 0x440 contains 7 entries: Offset Info Type Symbol's Value Symbol's Name + Addend diff --git a/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=libstdc++.so.verified.txt b/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=libstdc++.so.verified.txt index 09b8027..aee262b 100644 --- a/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=libstdc++.so.verified.txt +++ b/src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=libstdc++.so.verified.txt @@ -93,7 +93,38 @@ Program Headers: 10 11 .init_array .fini_array .data.rel.ro .dynamic .got -There is no dynamic section in this file. +Dynamic section at offset 0x223c90 contains 30 entries: + Tag Type Name/Value + 0x0000000000000001 (NEEDED) Shared library: [libm.so.6] + 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] + 0x0000000000000001 (NEEDED) Shared library: [ld-linux-x86-64.so.2] + 0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1] + 0x000000000000000e (SONAME) Library soname: [libstdc++.so.6] + 0x000000000000000c (INIT) 0x9a000 + 0x000000000000000d (FINI) 0x1aafc4 + 0x0000000000000019 (INIT_ARRAY) 0x21b880 + 0x000000000000001b (INIT_ARRAYSZ) 120 (bytes) + 0x000000000000001a (FINI_ARRAY) 0x21b8f8 + 0x000000000000001c (FINI_ARRAYSZ) 8 (bytes) + 0x000000006ffffef5 (GNU_HASH) 0x328 + 0x0000000000000005 (STRTAB) 0x2d310 + 0x0000000000000006 (SYMTAB) 0x90a0 + 0x000000000000000a (STRSZ) 302456 (bytes) + 0x000000000000000b (SYMENT) 24 (bytes) + 0x0000000000000003 (PLTGOT) 0x226000 + 0x0000000000000002 (PLTRELSZ) 25032 (bytes) + 0x0000000000000014 (PLTREL) RELA + 0x0000000000000017 (JMPREL) 0x92f78 + 0x0000000000000007 (RELA) 0x7a8e8 + 0x0000000000000008 (RELASZ) 99984 (bytes) + 0x0000000000000009 (RELAENT) 24 (bytes) + 0x000000006ffffffc (VERDEF) 0x7a0c0 + 0x000000006ffffffd (VERDEFNUM) 48 + 0x000000006ffffffe (VERNEED) 0x7a758 + 0x000000006fffffff (VERNEEDNUM) 4 + 0x000000006ffffff0 (VERSYM) 0x77088 + 0x000000006ffffff9 (RELACOUNT) 899 + 0x0000000000000000 (NULL) 0x0 Relocation section '.rela.dyn' at offset 0x7a8e8 contains 4166 entries: Offset Info Type Symbol's Value Symbol's Name + Addend diff --git a/src/LibObjectFile/Elf/ElfPrinter.cs b/src/LibObjectFile/Elf/ElfPrinter.cs index 27b4650..069a1f6 100644 --- a/src/LibObjectFile/Elf/ElfPrinter.cs +++ b/src/LibObjectFile/Elf/ElfPrinter.cs @@ -538,9 +538,253 @@ offsets within the segment. */ public static void PrintDynamicSections(ElfFile elf, TextWriter writer) { - writer.WriteLine(); - writer.WriteLine("There is no dynamic section in this file."); - // TODO + if (elf == null) throw new ArgumentNullException(nameof(elf)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); + + bool is32 = elf.FileClass == ElfFileClass.Is32; + bool foundDynamic = false; + + foreach (var section in elf.Sections) + { + if (section is not ElfDynamicLinkingTable dynamic) continue; + foundDynamic = true; + + // readelf lists entries up to and including the first DT_NULL terminator, ignoring any + // trailing padding entries the section may reserve for later editing. + int count = dynamic.Entries.Count; + for (int i = 0; i < dynamic.Entries.Count; i++) + { + if (dynamic.Entries[i].TagType == ElfDynamicTag.Null) + { + count = i + 1; + break; + } + } + + writer.WriteLine(); + writer.WriteLine(count == 1 + ? $"Dynamic section at offset 0x{dynamic.Position:x} contains {count} entry:" + : $"Dynamic section at offset 0x{dynamic.Position:x} contains {count} entries:"); + writer.WriteLine(" Tag Type Name/Value"); + + for (int i = 0; i < count; i++) + { + var entry = dynamic.Entries[i]; + var tagHex = is32 ? $"0x{(uint)entry.Tag:x8}" : $"0x{(ulong)entry.Tag:x16}"; + var typeField = $" ({GetElfDynamicTagName(entry.Tag)})"; + writer.WriteLine($" {tagHex}{typeField,-22}{GetElfDynamicValue(dynamic, entry)}"); + } + } + + if (!foundDynamic) + { + writer.WriteLine(); + writer.WriteLine("There is no dynamic section in this file."); + } + } + + private static string GetElfDynamicValue(ElfDynamicLinkingTable dynamic, ElfDynamic entry) + { + var tag = entry.TagType; + var value = entry.Value; + + if (ElfDynamicLinkingTable.IsStringValueTag(tag) && dynamic.TryGetString(entry, out var name)) + { + return tag switch + { + ElfDynamicTag.Needed => $"Shared library: [{name}]", + ElfDynamicTag.SoName => $"Library soname: [{name}]", + ElfDynamicTag.RPath => $"Library rpath: [{name}]", + ElfDynamicTag.RunPath => $"Library runpath: [{name}]", + ElfDynamicTag.Config => $"Configuration file: [{name}]", + ElfDynamicTag.DepAudit => $"Dependency audit library: [{name}]", + ElfDynamicTag.Audit => $"Audit library: [{name}]", + _ => $"0x{value:x}", + }; + } + + switch (tag) + { + case ElfDynamicTag.PltRelSz: + case ElfDynamicTag.RelaSz: + case ElfDynamicTag.RelaEnt: + case ElfDynamicTag.StrSz: + case ElfDynamicTag.SymEnt: + case ElfDynamicTag.RelSz: + case ElfDynamicTag.RelEnt: + case ElfDynamicTag.InitArraySz: + case ElfDynamicTag.FiniArraySz: + case ElfDynamicTag.PreInitArraySz: + case ElfDynamicTag.RelrSz: + case ElfDynamicTag.RelrEnt: + case ElfDynamicTag.GnuConflictSz: + case ElfDynamicTag.GnuLibListSz: + case ElfDynamicTag.PltPadSz: + case ElfDynamicTag.MoveSz: + case ElfDynamicTag.MoveEnt: + case ElfDynamicTag.SymInSz: + case ElfDynamicTag.SymInEnt: + return $"{value} (bytes)"; + + case ElfDynamicTag.RelaCount: + case ElfDynamicTag.RelCount: + case ElfDynamicTag.VerDefNum: + case ElfDynamicTag.VerNeedNum: + return value.ToString(); + + case ElfDynamicTag.PltRel: + return value == (ulong)ElfNative.DT_RELA ? "RELA" : value == (ulong)ElfNative.DT_REL ? "REL" : $"0x{value:x}"; + + case ElfDynamicTag.Flags: + return GetElfDynamicFlags(value); + + case ElfDynamicTag.Flags1: + return $"Flags:{GetElfDynamicFlags1(value)}"; + + default: + return $"0x{value:x}"; + } + } + + private static string GetElfDynamicFlags(ulong flags) + { + // DF_* flags. Not exposed by ElfNative, so the bit values are inlined here. + (ulong Bit, string Name)[] known = + [ + (0x1, "ORIGIN"), + (0x2, "SYMBOLIC"), + (0x4, "TEXTREL"), + (0x8, "BIND_NOW"), + (0x10, "STATIC_TLS"), + ]; + + var builder = new StringBuilder(); + foreach (var (bit, flagName) in known) + { + if ((flags & bit) == 0) continue; + if (builder.Length > 0) builder.Append(' '); + builder.Append(flagName); + flags &= ~bit; + } + + if (flags != 0) + { + if (builder.Length > 0) builder.Append(' '); + builder.Append($"0x{flags:x}"); + } + + return builder.ToString(); + } + + private static string GetElfDynamicFlags1(ulong flags) + { + // DF_1_* flags, in readelf order. Each match is emitted as " NAME" so the caller can prefix "Flags:". + (ulong Bit, string Name)[] known = + [ + (0x00000001, "NOW"), (0x00000002, "GLOBAL"), (0x00000004, "GROUP"), (0x00000008, "NODELETE"), + (0x00000010, "LOADFLTR"), (0x00000020, "INITFIRST"), (0x00000040, "NOOPEN"), (0x00000080, "ORIGIN"), + (0x00000100, "DIRECT"), (0x00000200, "TRANS"), (0x00000400, "INTERPOSE"), (0x00000800, "NODEFLIB"), + (0x00001000, "NODUMP"), (0x00002000, "CONFALT"), (0x00004000, "ENDFILTEE"), (0x00008000, "DISPRELDNE"), + (0x00010000, "DISPRELPND"), (0x00020000, "NODIRECT"), (0x00040000, "IGNMULDEF"), (0x00080000, "NOKSYMS"), + (0x00100000, "NOHDR"), (0x00200000, "EDITED"), (0x00400000, "NORELOC"), (0x00800000, "SYMINTPOSE"), + (0x01000000, "GLOBAUDIT"), (0x02000000, "SINGLETON"), (0x04000000, "STUB"), (0x08000000, "PIE"), + (0x10000000, "KMOD"), (0x20000000, "WEAKFILTER"), (0x40000000, "NOCOMMON"), + ]; + + var builder = new StringBuilder(); + foreach (var (bit, flagName) in known) + { + if ((flags & bit) == 0) continue; + builder.Append(' '); + builder.Append(flagName); + flags &= ~bit; + } + + if (flags != 0) + { + builder.Append($" 0x{flags:x}"); + } + + return builder.ToString(); + } + + private static string GetElfDynamicTagName(long tag) + { + switch ((ElfDynamicTag)tag) + { + case ElfDynamicTag.Null: return "NULL"; + case ElfDynamicTag.Needed: return "NEEDED"; + case ElfDynamicTag.PltRelSz: return "PLTRELSZ"; + case ElfDynamicTag.PltGot: return "PLTGOT"; + case ElfDynamicTag.Hash: return "HASH"; + case ElfDynamicTag.StrTab: return "STRTAB"; + case ElfDynamicTag.SymTab: return "SYMTAB"; + case ElfDynamicTag.Rela: return "RELA"; + case ElfDynamicTag.RelaSz: return "RELASZ"; + case ElfDynamicTag.RelaEnt: return "RELAENT"; + case ElfDynamicTag.StrSz: return "STRSZ"; + case ElfDynamicTag.SymEnt: return "SYMENT"; + case ElfDynamicTag.Init: return "INIT"; + case ElfDynamicTag.Fini: return "FINI"; + case ElfDynamicTag.SoName: return "SONAME"; + case ElfDynamicTag.RPath: return "RPATH"; + case ElfDynamicTag.Symbolic: return "SYMBOLIC"; + case ElfDynamicTag.Rel: return "REL"; + case ElfDynamicTag.RelSz: return "RELSZ"; + case ElfDynamicTag.RelEnt: return "RELENT"; + case ElfDynamicTag.PltRel: return "PLTREL"; + case ElfDynamicTag.Debug: return "DEBUG"; + case ElfDynamicTag.TextRel: return "TEXTREL"; + case ElfDynamicTag.JmpRel: return "JMPREL"; + case ElfDynamicTag.BindNow: return "BIND_NOW"; + case ElfDynamicTag.InitArray: return "INIT_ARRAY"; + case ElfDynamicTag.FiniArray: return "FINI_ARRAY"; + case ElfDynamicTag.InitArraySz: return "INIT_ARRAYSZ"; + case ElfDynamicTag.FiniArraySz: return "FINI_ARRAYSZ"; + case ElfDynamicTag.RunPath: return "RUNPATH"; + case ElfDynamicTag.Flags: return "FLAGS"; + // DT_ENCODING (32) aliases DT_PREINIT_ARRAY; readelf reports 32 as PREINIT_ARRAY. + case ElfDynamicTag.PreInitArray: return "PREINIT_ARRAY"; + case ElfDynamicTag.PreInitArraySz: return "PREINIT_ARRAYSZ"; + case ElfDynamicTag.SymtabShndx: return "SYMTAB_SHNDX"; + case ElfDynamicTag.RelrSz: return "RELRSZ"; + case ElfDynamicTag.Relr: return "RELR"; + case ElfDynamicTag.RelrEnt: return "RELRENT"; + case ElfDynamicTag.GnuPrelinked: return "GNU_PRELINKED"; + case ElfDynamicTag.GnuConflictSz: return "GNU_CONFLICTSZ"; + case ElfDynamicTag.GnuLibListSz: return "GNU_LIBLISTSZ"; + case ElfDynamicTag.Checksum: return "CHECKSUM"; + case ElfDynamicTag.PltPadSz: return "PLTPADSZ"; + case ElfDynamicTag.MoveEnt: return "MOVEENT"; + case ElfDynamicTag.MoveSz: return "MOVESZ"; + case ElfDynamicTag.Feature1: return "FEATURE_1"; + case ElfDynamicTag.PosFlag1: return "POSFLAG_1"; + case ElfDynamicTag.SymInSz: return "SYMINSZ"; + case ElfDynamicTag.SymInEnt: return "SYMINENT"; + case ElfDynamicTag.GnuHash: return "GNU_HASH"; + case ElfDynamicTag.TlsDescPlt: return "TLSDESC_PLT"; + case ElfDynamicTag.TlsDescGot: return "TLSDESC_GOT"; + case ElfDynamicTag.GnuConflict: return "GNU_CONFLICT"; + case ElfDynamicTag.GnuLibList: return "GNU_LIBLIST"; + case ElfDynamicTag.Config: return "CONFIG"; + case ElfDynamicTag.DepAudit: return "DEPAUDIT"; + case ElfDynamicTag.Audit: return "AUDIT"; + case ElfDynamicTag.PltPad: return "PLTPAD"; + case ElfDynamicTag.MoveTab: return "MOVETAB"; + case ElfDynamicTag.SymInfo: return "SYMINFO"; + case ElfDynamicTag.VerSym: return "VERSYM"; + case ElfDynamicTag.RelaCount: return "RELACOUNT"; + case ElfDynamicTag.RelCount: return "RELCOUNT"; + case ElfDynamicTag.Flags1: return "FLAGS_1"; + case ElfDynamicTag.VerDef: return "VERDEF"; + case ElfDynamicTag.VerDefNum: return "VERDEFNUM"; + case ElfDynamicTag.VerNeed: return "VERNEED"; + case ElfDynamicTag.VerNeedNum: return "VERNEEDNUM"; + default: + if (tag >= ElfNative.DT_LOOS && tag <= ElfNative.DT_HIOS) return $""; + if (tag >= ElfNative.DT_LOPROC && tag <= ElfNative.DT_HIPROC) return $""; + return $": 0x{tag:x}"; + } } private static string GetElfSegmentFlags(ElfSegmentFlags flags) diff --git a/src/LibObjectFile/Elf/ElfSectionHeaderTable.cs b/src/LibObjectFile/Elf/ElfSectionHeaderTable.cs index dd17f04..8dc0ae6 100644 --- a/src/LibObjectFile/Elf/ElfSectionHeaderTable.cs +++ b/src/LibObjectFile/Elf/ElfSectionHeaderTable.cs @@ -133,6 +133,7 @@ private static ElfSection CreateElfSection(ElfReader reader, uint sectionIndex, ElfSectionType.Note => new ElfNoteTable(), ElfSectionType.SymbolTableSectionHeaderIndices => new ElfSymbolTableSectionHeaderIndices(), ElfSectionType.NoBits => new ElfNoBitsSection(), + ElfSectionType.DynamicLinking => new ElfDynamicLinkingTable(), _ => new ElfStreamSection(sectionType) }; } @@ -215,21 +216,23 @@ private static unsafe void EncodeSectionTableEntry64(ElfWriter writer, ElfSectio writer.Encode(out rawSection.sh_name, section.Name.Index); writer.Encode(out rawSection.sh_type, (uint)section.Type); writer.Encode(out rawSection.sh_flags, (uint)section.Flags); - writer.Encode(out rawSection.sh_addr, (uint)section.VirtualAddress); - writer.Encode(out rawSection.sh_offset, (uint)section.Position); + // sh_addr/sh_offset/sh_size/sh_addralign/sh_entsize are 64-bit in Elf64_Shdr. Casting to + // uint here truncated any value >= 4 GiB (high load addresses, large NOBITS sections). + writer.Encode(out rawSection.sh_addr, section.VirtualAddress); + writer.Encode(out rawSection.sh_offset, section.Position); if (section.SectionIndex == 0 && writer.File.Sections.Count >= ElfNative.SHN_LORESERVE) { - writer.Encode(out rawSection.sh_size, (uint)writer.File.Sections.Count); + writer.Encode(out rawSection.sh_size, (ulong)writer.File.Sections.Count); var shstrSectionIndex = (uint)(writer.File.SectionHeaderStringTable?.SectionIndex ?? 0); writer.Encode(out rawSection.sh_link, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? shstrSectionIndex : 0); } else { - writer.Encode(out rawSection.sh_size, (uint)section.Size); + writer.Encode(out rawSection.sh_size, section.Size); writer.Encode(out rawSection.sh_link, (uint)section.Link.GetIndex()); } writer.Encode(out rawSection.sh_info, (uint)section.Info.GetIndex()); - writer.Encode(out rawSection.sh_addralign, (uint)section.VirtualAddressAlignment); - writer.Encode(out rawSection.sh_entsize, (uint)section.TableEntrySize); + writer.Encode(out rawSection.sh_addralign, section.VirtualAddressAlignment); + writer.Encode(out rawSection.sh_entsize, section.TableEntrySize); } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfDynamic.cs b/src/LibObjectFile/Elf/Sections/ElfDynamic.cs new file mode 100644 index 0000000..554e435 --- /dev/null +++ b/src/LibObjectFile/Elf/Sections/ElfDynamic.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.Elf +{ + /// + /// Dynamic table entry for ELF. + /// + public record struct ElfDynamic + { + /// + /// Type of dynamic table entry. + /// + public long Tag { get; set; } + + /// + /// Integer value of entry. + /// + public ulong Value { get; set; } + + /// + /// Pointer value of entry. + /// + public ulong Pointer => Value; + + /// + /// Dynamic Tag + /// + public ElfDynamicTag TagType => (ElfDynamicTag)Tag; + } +} \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfDynamicLinkingTable.cs b/src/LibObjectFile/Elf/Sections/ElfDynamicLinkingTable.cs new file mode 100644 index 0000000..81ea52f --- /dev/null +++ b/src/LibObjectFile/Elf/Sections/ElfDynamicLinkingTable.cs @@ -0,0 +1,284 @@ +// Copyright (c) Alexandre Mutel. All rights 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; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using LibObjectFile.Diagnostics; +using LibObjectFile.IO; + +namespace LibObjectFile.Elf +{ + public class ElfDynamicLinkingTable : ElfSection + { + private bool _is32; + + public List Entries { get; } + + public ElfDynamicLinkingTable() : base(ElfSectionType.DynamicLinking) + { + Name = ElfSectionSpecialType.Dynamic.GetDefaultName(); + Flags = ElfSectionSpecialType.Dynamic.GetSectionFlags(); + + Entries = []; + } + + /// + /// Gets the dynamic string table (.dynstr) referenced by , + /// or null when the link does not point to an . Tags such as + /// store an offset into this table rather than the string itself. + /// + public ElfStringTable? StringTable => Link.Section as ElfStringTable; + + /// + /// Returns true when the value of an entry with the given is an offset + /// into the linked string table () rather than an address, size, or flag set. + /// + public static bool IsStringValueTag(ElfDynamicTag tag) => tag switch + { + ElfDynamicTag.Needed + or ElfDynamicTag.SoName + or ElfDynamicTag.RPath + or ElfDynamicTag.RunPath + or ElfDynamicTag.Config + or ElfDynamicTag.DepAudit + or ElfDynamicTag.Audit => true, + _ => false, + }; + + /// + /// Resolves the string referenced by a string-valued entry (e.g. ) + /// through the linked string table. Returns false when the entry is not string-valued or the + /// linked string table is missing. + /// + public bool TryGetString(ElfDynamic entry, out string value) + { + if (IsStringValueTag(entry.TagType) && StringTable is { } stringTable) + { + return stringTable.TryGetString((uint)entry.Value, out value); + } + + value = string.Empty; + return false; + } + + /// + /// Enumerates the shared library names referenced by entries, + /// resolved through the linked string table. Yields nothing when the string table is missing. + /// + public IEnumerable GetNeededLibraries() + { + if (StringTable is not { } stringTable) + { + yield break; + } + + foreach (var entry in Entries) + { + if (entry.TagType == ElfDynamicTag.Needed && stringTable.TryGetString((uint)entry.Value, out var name)) + { + yield return name; + } + } + } + + /// + /// Appends a entry for , adding the + /// name to the linked string table. The entry is inserted before any trailing + /// terminator so the table stays valid. + /// + /// is not an . + public void AddNeededLibrary(string libraryName) + { + ArgumentNullException.ThrowIfNull(libraryName); + var stringTable = StringTable ?? throw new InvalidOperationException($"The {nameof(Link)} of this dynamic section must be set to an {nameof(ElfStringTable)} before adding a needed library"); + + var offset = stringTable.GetOrCreateString(libraryName); + + int insertIndex = Entries.Count; + while (insertIndex > 0 && Entries[insertIndex - 1].TagType == ElfDynamicTag.Null) + { + insertIndex--; + } + + Entries.Insert(insertIndex, new ElfDynamic { Tag = (long)ElfDynamicTag.Needed, Value = offset }); + } + + /// + /// Serializes as raw Elf32_Dyn/Elf64_Dyn records to , + /// using the file class and byte order of the parent . Standalone counterpart to the + /// full-file write path, mirroring at the section level. + /// + /// The section is not attached to an . + public void Write(Stream stream) + { + ArgumentNullException.ThrowIfNull(stream); + var elf = Parent ?? throw new InvalidOperationException($"The {nameof(ElfDynamicLinkingTable)} must be attached to an {nameof(ElfFile)} to determine the file class and byte order"); + + var writer = ElfWriter.Create(elf, stream); + Write(writer); + } + + /// + /// Replaces by reading raw Elf32_Dyn/Elf64_Dyn records from the whole of + /// , using the file class and byte order of the parent . + /// Standalone counterpart to the full-file read path, mirroring . + /// + /// The section is not attached to an . + public void Read(Stream stream) + { + ArgumentNullException.ThrowIfNull(stream); + var elf = Parent ?? throw new InvalidOperationException($"The {nameof(ElfDynamicLinkingTable)} must be attached to an {nameof(ElfFile)} to determine the file class and byte order"); + + var reader = ElfReader.Create(elf, stream, new ElfReaderOptions()); + reader.Position = 0; + + var entrySize = _is32 ? Unsafe.SizeOf() : Unsafe.SizeOf(); + var numberOfEntries = (int)((ulong)stream.Length / (ulong)entrySize); + + Entries.Clear(); + CollectionsMarshal.SetCount(Entries, numberOfEntries); + + if (_is32) + Read32(reader, numberOfEntries); + else + Read64(reader, numberOfEntries); + } + + protected override void UpdateLayoutCore(ElfVisitorContext context) + { + base.UpdateLayoutCore(context); + + var numberOfEntries = Entries.Count; + + base.Size = (ulong)numberOfEntries * base.TableEntrySize; + } + + public override void Read(ElfReader reader) + { + reader.Position = Position; + Entries.Clear(); + + var numberOfEntries = (int)(base.Size / base.TableEntrySize); + var entries = Entries; + CollectionsMarshal.SetCount(entries, numberOfEntries); + + if (_is32) + Read32(reader, numberOfEntries); + else + Read64(reader, numberOfEntries); + } + + private void Read32(ElfReader reader, int numberOfEntries) + { + using var batch = new BatchDataReader(reader.Stream, numberOfEntries); + var span = CollectionsMarshal.AsSpan(Entries); + ref var entry = ref MemoryMarshal.GetReference(span); + while (batch.HasNext()) + { + ref var dyn = ref batch.Read(); + + entry.Tag = reader.Decode(dyn.d_tag); + entry.Value = reader.Decode(dyn.d_un.d_val); + + entry = ref Unsafe.Add(ref entry, 1); + } + } + + private void Read64(ElfReader reader, int numberOfEntries) + { + using var batch = new BatchDataReader(reader.Stream, numberOfEntries); + var span = CollectionsMarshal.AsSpan(Entries); + ref var entry = ref MemoryMarshal.GetReference(span); + while (batch.HasNext()) + { + ref var dyn = ref batch.Read(); + + entry.Tag = reader.Decode(dyn.d_tag); + entry.Value = reader.Decode(dyn.d_un.d_val); + + entry = ref Unsafe.Add(ref entry, 1); + } + } + + public override void Write(ElfWriter writer) + { + if (_is32) + Write32(writer); + else + Write64(writer); + } + + private void Write32(ElfWriter writer) + { + var entries = CollectionsMarshal.AsSpan(Entries); + using var batch = new BatchDataWriter(writer.Stream, entries.Length); + var dyn = new ElfNative.Elf32_Dyn(); + for (int i = 0; i < entries.Length; i++) + { + ref var entry = ref entries[i]; + + writer.Encode(out dyn.d_tag, (int)entry.Tag); + writer.Encode(out dyn.d_un.d_val, (uint)entry.Value); + + batch.Write(dyn); + } + } + + private void Write64(ElfWriter writer) + { + var entries = CollectionsMarshal.AsSpan(Entries); + using var batch = new BatchDataWriter(writer.Stream, entries.Length); + var dyn = new ElfNative.Elf64_Dyn(); + for (int i = 0; i < entries.Length; i++) + { + ref var entry = ref entries[i]; + + writer.Encode(out dyn.d_tag, entry.Tag); + writer.Encode(out dyn.d_un.d_val, entry.Value); + + batch.Write(dyn); + } + } + + protected override unsafe void ValidateParent(ObjectElement parent) + { + base.ValidateParent(parent); + + var elf = (ElfFile)parent; + _is32 = elf.FileClass == ElfFileClass.Is32; + + BaseTableEntrySize = (uint)(_is32 ? sizeof(ElfNative.Elf32_Dyn) : sizeof(ElfNative.Elf64_Dyn)); + AdditionalTableEntrySize = 0; + } + + internal override unsafe void InitializeEntrySizeFromRead(DiagnosticBag diagnostics, ulong entrySize, bool is32) + { + _is32 = is32; + + if (is32) + { + if (entrySize != (ulong)sizeof(ElfNative.Elf32_Dyn)) + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionEntrySize, $"Invalid size [{entrySize}] for dynamic entry. Expecting to be equal to [{sizeof(ElfNative.Elf32_Dyn)}] bytes."); + else + { + BaseTableEntrySize = (uint)sizeof(ElfNative.Elf32_Dyn); + AdditionalTableEntrySize = (uint)(entrySize - AdditionalTableEntrySize); + } + } + else + { + if (entrySize != (ulong)sizeof(ElfNative.Elf64_Dyn)) + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionEntrySize, $"Invalid size [{entrySize}] for dynamic entry. Expecting to be equal to [{sizeof(ElfNative.Elf64_Dyn)}] bytes."); + else + { + BaseTableEntrySize = (uint)sizeof(ElfNative.Elf64_Dyn); + AdditionalTableEntrySize = (uint)(entrySize - AdditionalTableEntrySize); + } + } + } + } +} \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfDynamicTag.cs b/src/LibObjectFile/Elf/Sections/ElfDynamicTag.cs new file mode 100644 index 0000000..822d317 --- /dev/null +++ b/src/LibObjectFile/Elf/Sections/ElfDynamicTag.cs @@ -0,0 +1,372 @@ +// Copyright (c) Alexandre Mutel. All 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 +{ + /// + /// Dynamic table entry tags. + /// + public enum ElfDynamicTag : uint + { + /// + /// Null + /// + Null = ElfNative.DT_NULL, + + /// + /// String table offset of needed library. + /// + Needed = ElfNative.DT_NEEDED, + + /// + /// Size of relocation entries in PLT. + /// + PltRelSz = ElfNative.DT_PLTRELSZ, + + /// + /// Address associated with linkage table. + /// + PltGot = ElfNative.DT_PLTGOT, + + /// + /// Address of symbolic hash table. + /// + Hash = ElfNative.DT_HASH, + + /// + /// Address of dynamic string table. + /// + StrTab = ElfNative.DT_STRTAB, + + /// + /// Address of dynamic symbol table. + /// + SymTab = ElfNative.DT_SYMTAB, + + /// + /// Address of relocation table (Rela entries). + /// + Rela = ElfNative.DT_RELA, + + /// + /// Size of Rela relocation table. + /// + RelaSz = ElfNative.DT_RELASZ, + + /// + /// Size of a Rela relocation entry. + /// + RelaEnt = ElfNative.DT_RELAENT, + + /// + /// Total size of the string table. + /// + StrSz = ElfNative.DT_STRSZ, + + /// + /// Size of a symbol table entry. + /// + SymEnt = ElfNative.DT_SYMENT, + + /// + /// Address of initialization function. + /// + Init = ElfNative.DT_INIT, + + /// + /// Address of termination function. + /// + Fini = ElfNative.DT_FINI, + + /// + /// String table offset of a shared object's name. + /// + SoName = ElfNative.DT_SONAME, + + /// + /// String table offset of library search path. + /// + RPath = ElfNative.DT_RPATH, + + /// + /// Changes symbol resolution algorithm. + /// + Symbolic = ElfNative.DT_SYMBOLIC, + + /// + /// Address of relocation table (Rel entries). + /// + Rel = ElfNative.DT_REL, + + /// + /// Size of Rel relocation table. + /// + RelSz = ElfNative.DT_RELSZ, + + /// + /// Size of a Rel relocation entry. + /// + RelEnt = ElfNative.DT_RELENT, + + /// + /// Type of relocation entry used for linking. + /// + PltRel = ElfNative.DT_PLTREL, + + /// + /// Reserved for debugger. + /// + Debug = ElfNative.DT_DEBUG, + + /// + /// Relocations exist for non-writable segments. + /// + TextRel = ElfNative.DT_TEXTREL, + + /// + /// Address of relocations associated with PLT. + /// + JmpRel = ElfNative.DT_JMPREL, + + /// + /// Process all relocations before execution. + /// + BindNow = ElfNative.DT_BIND_NOW, + + /// + /// Pointer to array of initialization functions. + /// + InitArray = ElfNative.DT_INIT_ARRAY, + + /// + /// Pointer to array of termination functions. + /// + FiniArray = ElfNative.DT_FINI_ARRAY, + + /// + /// Size of . + /// + InitArraySz = ElfNative.DT_INIT_ARRAYSZ, + + /// + /// Size of . + /// + FiniArraySz = ElfNative.DT_FINI_ARRAYSZ, + + /// + /// String table offset of library search path. + /// + RunPath = ElfNative.DT_RUNPATH, + + /// + /// Flags. + /// + Flags = ElfNative.DT_FLAGS, + + /// + /// Values from here to DT_LOOS follow the rules for the interpretation of the d_un union. + /// + Encoding = ElfNative.DT_ENCODING, + + /// + /// Pointer to array of preinit functions. + /// + PreInitArray = ElfNative.DT_PREINIT_ARRAY, + + /// + /// Size of the DT_PREINIT_ARRAY array. + /// + PreInitArraySz = ElfNative.DT_PREINIT_ARRAYSZ, + + /// + /// Address of the section header index table for the dynamic symbol table. + /// + SymtabShndx = ElfNative.DT_SYMTAB_SHNDX, + + /// + /// Total size of the RELR relative relocation table. + /// + RelrSz = ElfNative.DT_RELRSZ, + + /// + /// Address of the RELR relative relocation table. + /// + Relr = ElfNative.DT_RELR, + + /// + /// Size of a RELR relative relocation entry. + /// + RelrEnt = ElfNative.DT_RELRENT, + + /// + /// Prelinking timestamp (GNU extension). + /// + GnuPrelinked = ElfNative.DT_GNU_PRELINKED, + + /// + /// Size of the conflict section (GNU extension). + /// + GnuConflictSz = ElfNative.DT_GNU_CONFLICTSZ, + + /// + /// Size of the library list (GNU extension). + /// + GnuLibListSz = ElfNative.DT_GNU_LIBLISTSZ, + + /// + /// Checksum of the shared object (GNU extension). + /// + Checksum = ElfNative.DT_CHECKSUM, + + /// + /// Size of the PLT padding. + /// + PltPadSz = ElfNative.DT_PLTPADSZ, + + /// + /// Size of a move table entry. + /// + MoveEnt = ElfNative.DT_MOVEENT, + + /// + /// Total size of the move table. + /// + MoveSz = ElfNative.DT_MOVESZ, + + /// + /// Feature selection flags (DTF_*). + /// + Feature1 = ElfNative.DT_FEATURE_1, + + /// + /// Flags for the entry immediately following this one. + /// + PosFlag1 = ElfNative.DT_POSFLAG_1, + + /// + /// Size of the syminfo table. + /// + SymInSz = ElfNative.DT_SYMINSZ, + + /// + /// Size of a syminfo table entry. + /// + SymInEnt = ElfNative.DT_SYMINENT, + + /// + /// Address of the GNU-style symbol hash table. + /// + GnuHash = ElfNative.DT_GNU_HASH, + + /// + /// Address of the PLT stub for TLS descriptors. + /// + TlsDescPlt = ElfNative.DT_TLSDESC_PLT, + + /// + /// Address of the GOT entry for TLS descriptors. + /// + TlsDescGot = ElfNative.DT_TLSDESC_GOT, + + /// + /// Address of the conflict section (GNU extension). + /// + GnuConflict = ElfNative.DT_GNU_CONFLICT, + + /// + /// Address of the library list (GNU extension). + /// + GnuLibList = ElfNative.DT_GNU_LIBLIST, + + /// + /// String table offset of a configuration file. + /// + Config = ElfNative.DT_CONFIG, + + /// + /// String table offset of the dependency audit library list. + /// + DepAudit = ElfNative.DT_DEPAUDIT, + + /// + /// String table offset of the audit library list. + /// + Audit = ElfNative.DT_AUDIT, + + /// + /// Address of the PLT padding. + /// + PltPad = ElfNative.DT_PLTPAD, + + /// + /// Address of the move table. + /// + MoveTab = ElfNative.DT_MOVETAB, + + /// + /// Address of the syminfo table. + /// + SymInfo = ElfNative.DT_SYMINFO, + + /// + /// Address of the symbol version table (.gnu.version). + /// + VerSym = ElfNative.DT_VERSYM, + + /// + /// Number of RELATIVE entries at the start of the Rela relocation table. + /// + RelaCount = ElfNative.DT_RELACOUNT, + + /// + /// Number of RELATIVE entries at the start of the Rel relocation table. + /// + RelCount = ElfNative.DT_RELCOUNT, + + /// + /// State flags (DF_1_*). + /// + Flags1 = ElfNative.DT_FLAGS_1, + + /// + /// Address of the version definition table (.gnu.version_d). + /// + VerDef = ElfNative.DT_VERDEF, + + /// + /// Number of entries in the version definition table. + /// + VerDefNum = ElfNative.DT_VERDEFNUM, + + /// + /// Address of the version needs table (.gnu.version_r). + /// + VerNeed = ElfNative.DT_VERNEED, + + /// + /// Number of entries in the version needs table. + /// + VerNeedNum = ElfNative.DT_VERNEEDNUM, + + /// + /// Start of environment specific tags. + /// + LoOs = ElfNative.DT_LOOS, + + /// + /// End of environment specific tags. + /// + HiOS = ElfNative.DT_HIOS, + + /// + /// Start of processor specific tags. + /// + LoProc = ElfNative.DT_LOPROC, + + /// + /// End of processor specific tags. + /// + HiProc = ElfNative.DT_HIPROC + } +} \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfStringTable.cs b/src/LibObjectFile/Elf/Sections/ElfStringTable.cs index a54d9e0..0b9a181 100644 --- a/src/LibObjectFile/Elf/Sections/ElfStringTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfStringTable.cs @@ -92,6 +92,23 @@ private ElfString Create(string? text) return new(index); } + /// + /// Adds a string to the table (or returns the offset of an existing copy) and + /// returns its byte offset. Lets callers append entries such as a new + /// DT_NEEDED library name whose dynamic entry stores a raw string offset. + /// + public uint GetOrCreateString(string text) + { + if (string.IsNullOrEmpty(text)) return 0; + + if (!_mapStringToIndex.TryGetValue(text, out uint index)) + { + index = CreateIndex(text); + } + + return index; + } + public bool TryResolve(ElfString name, out ElfString resolvedName) { string text = name.Value; @@ -127,7 +144,7 @@ public ElfString Resolve(ElfString name) return newName; } - internal bool TryGetString(uint index, out string text) + public bool TryGetString(uint index, out string text) { if (index == 0) {