Skip to content

Commit 0b89af9

Browse files
authored
Merge pull request #48 from ProjectSynchro/elf-dynamic-support
Add ELF dynamic linking table (SHT_DYNAMIC) support
2 parents 4a7830b + fa50da0 commit 0b89af9

13 files changed

Lines changed: 1245 additions & 14 deletions

‎readme.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ elf.Write(outStream);
4949
- Symbol Table
5050
- Relocation Table: supported I386, X86_64, ARM and AARCH64 relocations (others can be exposed by adding some mappings)
5151
- Note Table
52+
- Dynamic Linking Table (`SHT_DYNAMIC`)
5253
- Other sections fallback to `ElfCustomSection`
5354
- Program headers with or without sections
5455
- Print with `readelf` similar output

‎src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs‎

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,131 @@ public async Task SimpleProgramHeaderAndCodeSectionAndSymbolSectionAndRelocation
438438
}
439439

440440

441+
[TestMethod]
442+
public async Task SimpleDynamicSection()
443+
{
444+
var elf = new ElfFile(ElfArch.X86_64);
445+
446+
var dynstr = new ElfStringTable() { Name = ".dynstr" };
447+
elf.Add(dynstr);
448+
449+
var dynamic = new ElfDynamicLinkingTable() { Link = dynstr };
450+
dynamic.AddNeededLibrary("libc.so.6");
451+
dynamic.AddNeededLibrary("libm.so.6");
452+
dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Flags1, Value = 0x08000001 });
453+
dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Null });
454+
elf.Add(dynamic);
455+
456+
elf.Add(new ElfSectionHeaderStringTable());
457+
elf.Add(new ElfSectionHeaderTable());
458+
459+
// The read helpers resolve DT_NEEDED offsets through the linked .dynstr.
460+
CollectionAssert.AreEqual(new[] { "libc.so.6", "libm.so.6" }, dynamic.GetNeededLibraries().ToList());
461+
462+
await AssertReadElf(elf, "test_dynamic.elf");
463+
464+
// Round-trip the entries and confirm the resolved library names survive write -> read.
465+
var memoryStream = new MemoryStream();
466+
elf.Write(memoryStream);
467+
memoryStream.Position = 0;
468+
var roundTrip = ElfFile.Read(memoryStream);
469+
var roundTripDynamic = roundTrip.Sections.OfType<ElfDynamicLinkingTable>().Single();
470+
Assert.AreEqual(dynamic.Entries.Count, roundTripDynamic.Entries.Count);
471+
CollectionAssert.AreEqual(new[] { "libc.so.6", "libm.so.6" }, roundTripDynamic.GetNeededLibraries().ToList());
472+
}
473+
474+
[TestMethod]
475+
public void DynamicSectionStreamRoundTrip()
476+
{
477+
var elf = new ElfFile(ElfArch.X86_64);
478+
var dynstr = new ElfStringTable() { Name = ".dynstr" };
479+
elf.Add(dynstr);
480+
481+
var dynamic = new ElfDynamicLinkingTable() { Link = dynstr };
482+
dynamic.AddNeededLibrary("libc.so.6");
483+
dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Flags1, Value = 0x08000001 });
484+
dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Null });
485+
elf.Add(dynamic);
486+
487+
// Serialize just the .dynamic entries to a standalone stream (64-bit -> 16 bytes each).
488+
var stream = new MemoryStream();
489+
dynamic.Write(stream);
490+
Assert.AreEqual(dynamic.Entries.Count * 16, stream.Length);
491+
492+
// Read them back into a fresh table attached to another file and confirm value equality.
493+
var elf2 = new ElfFile(ElfArch.X86_64);
494+
var dynamic2 = new ElfDynamicLinkingTable();
495+
elf2.Add(dynamic2);
496+
stream.Position = 0;
497+
dynamic2.Read(stream);
498+
499+
CollectionAssert.AreEqual(dynamic.Entries, dynamic2.Entries);
500+
}
501+
502+
[TestMethod]
503+
public void DynamicSection32Bit()
504+
{
505+
var elf = new ElfFile(ElfArch.I386);
506+
var dynstr = new ElfStringTable() { Name = ".dynstr" };
507+
elf.Add(dynstr);
508+
509+
var dynamic = new ElfDynamicLinkingTable() { Link = dynstr };
510+
dynamic.AddNeededLibrary("libc.so.6");
511+
dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Flags, Value = 0x8 }); // DF_BIND_NOW
512+
dynamic.Entries.Add(new ElfDynamic { Tag = (long)ElfDynamicTag.Null });
513+
elf.Add(dynamic);
514+
515+
// The printer uses 8-digit tag hex for 32-bit files.
516+
var writer = new StringWriter();
517+
ElfPrinter.PrintDynamicSections(elf, writer);
518+
var text = writer.ToString();
519+
StringAssert.Contains(text, " 0x00000001 (NEEDED)");
520+
StringAssert.Contains(text, "Shared library: [libc.so.6]");
521+
StringAssert.Contains(text, " 0x0000001e (FLAGS)");
522+
StringAssert.Contains(text, "BIND_NOW");
523+
524+
// The Read32/Write32 path uses 8-byte Elf32_Dyn records.
525+
var stream = new MemoryStream();
526+
dynamic.Write(stream);
527+
Assert.AreEqual(dynamic.Entries.Count * 8, stream.Length);
528+
529+
var elf2 = new ElfFile(ElfArch.I386);
530+
var dynamic2 = new ElfDynamicLinkingTable();
531+
elf2.Add(dynamic2);
532+
stream.Position = 0;
533+
dynamic2.Read(stream);
534+
535+
CollectionAssert.AreEqual(dynamic.Entries, dynamic2.Entries);
536+
}
537+
538+
[TestMethod]
539+
public void SectionHeader64BitFieldsNotTruncated()
540+
{
541+
// Regression: the 64-bit section header writer cast sh_addr/sh_offset/sh_size/etc. to uint,
542+
// truncating any value >= 4 GiB. A NOBITS section declares a huge address/size with no file bytes.
543+
var elf = new ElfFile(ElfArch.X86_64);
544+
545+
var bss = new ElfNoBitsSection
546+
{
547+
Name = ".hugebss",
548+
VirtualAddress = 0x1_0000_2000UL, // > 4 GiB
549+
Size = 0x2_0000_1000UL, // > 4 GiB
550+
VirtualAddressAlignment = 0x1000,
551+
};
552+
elf.Add(bss);
553+
elf.Add(new ElfSectionHeaderStringTable());
554+
elf.Add(new ElfSectionHeaderTable());
555+
556+
var stream = new MemoryStream();
557+
elf.Write(stream);
558+
stream.Position = 0;
559+
var roundTrip = ElfFile.Read(stream);
560+
561+
var bss2 = roundTrip.Sections.OfType<ElfNoBitsSection>().Single();
562+
Assert.AreEqual(0x1_0000_2000UL, bss2.VirtualAddress);
563+
Assert.AreEqual(0x2_0000_1000UL, bss2.Size);
564+
}
565+
441566
[TestMethod]
442567
public async Task TestAlignedSection()
443568
{
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
ELF Header:
2+
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
3+
Class: ELF64
4+
Data: 2's complement, little endian
5+
Version: 1 (current)
6+
OS/ABI: UNIX - System V
7+
ABI Version: 0
8+
Type: REL (Relocatable file)
9+
Machine: Advanced Micro Devices X86-64
10+
Version: 0x1
11+
Entry point address: 0x0
12+
Start of program headers: 0 (bytes into file)
13+
Start of section headers: 0 (bytes into file)
14+
Flags: 0x0
15+
Size of this header: 0 (bytes)
16+
Size of program headers: 0 (bytes)
17+
Number of program headers: 0
18+
Size of section headers: 0 (bytes)
19+
Number of section headers: 4
20+
Section header string table index: 3
21+
22+
Section Headers:
23+
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
24+
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
25+
[ 1] .dynstr STRTAB 0000000000000000 000000 000000 00 0 0 0
26+
[ 2] .dynamic DYNAMIC 0000000000000000 000000 000000 10 A 1 0 0
27+
[ 3] .shstrtab STRTAB 0000000000000000 000000 000000 00 0 0 0
28+
Key to Flags:
29+
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
30+
L (link order), O (extra OS processing required), G (group), T (TLS),
31+
C (compressed), x (unknown), o (OS specific), E (exclude),
32+
D (mbind), l (large), p (processor specific)
33+
34+
There are no section groups in this file.
35+
36+
There are no program headers in this file.
37+
38+
Dynamic section at offset 0x0 contains 4 entries:
39+
Tag Type Name/Value
40+
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
41+
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
42+
0x000000006ffffffb (FLAGS_1) Flags: NOW PIE
43+
0x0000000000000000 (NULL) 0x0
44+
45+
There are no relocations in this file.
46+
No processor specific unwind information to decode
47+
48+
No version information found in this file.

‎src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=helloworld.verified.txt‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,35 @@ Program Headers:
9292
11
9393
12 .init_array .fini_array .dynamic .got
9494

95-
There is no dynamic section in this file.
95+
Dynamic section at offset 0x2dc8 contains 27 entries:
96+
Tag Type Name/Value
97+
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
98+
0x000000000000000c (INIT) 0x1000
99+
0x000000000000000d (FINI) 0x1174
100+
0x0000000000000019 (INIT_ARRAY) 0x3db8
101+
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
102+
0x000000000000001a (FINI_ARRAY) 0x3dc0
103+
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
104+
0x000000006ffffef5 (GNU_HASH) 0x3b0
105+
0x0000000000000005 (STRTAB) 0x480
106+
0x0000000000000006 (SYMTAB) 0x3d8
107+
0x000000000000000a (STRSZ) 141 (bytes)
108+
0x000000000000000b (SYMENT) 24 (bytes)
109+
0x0000000000000015 (DEBUG) 0x0
110+
0x0000000000000003 (PLTGOT) 0x3fb8
111+
0x0000000000000002 (PLTRELSZ) 24 (bytes)
112+
0x0000000000000014 (PLTREL) RELA
113+
0x0000000000000017 (JMPREL) 0x610
114+
0x0000000000000007 (RELA) 0x550
115+
0x0000000000000008 (RELASZ) 192 (bytes)
116+
0x0000000000000009 (RELAENT) 24 (bytes)
117+
0x000000000000001e (FLAGS) BIND_NOW
118+
0x000000006ffffffb (FLAGS_1) Flags: NOW PIE
119+
0x000000006ffffffe (VERNEED) 0x520
120+
0x000000006fffffff (VERNEEDNUM) 1
121+
0x000000006ffffff0 (VERSYM) 0x50e
122+
0x000000006ffffff9 (RELACOUNT) 3
123+
0x0000000000000000 (NULL) 0x0
96124

97125
Relocation section '.rela.dyn' at offset 0x550 contains 8 entries:
98126
Offset Info Type Symbol's Value Symbol's Name + Addend

‎src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=helloworld_debug.verified.txt‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,35 @@ Program Headers:
9797
11
9898
12 .init_array .fini_array .dynamic .got
9999

100-
There is no dynamic section in this file.
100+
Dynamic section at offset 0x2dc8 contains 27 entries:
101+
Tag Type Name/Value
102+
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
103+
0x000000000000000c (INIT) 0x1000
104+
0x000000000000000d (FINI) 0x1174
105+
0x0000000000000019 (INIT_ARRAY) 0x3db8
106+
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
107+
0x000000000000001a (FINI_ARRAY) 0x3dc0
108+
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
109+
0x000000006ffffef5 (GNU_HASH) 0x3b0
110+
0x0000000000000005 (STRTAB) 0x480
111+
0x0000000000000006 (SYMTAB) 0x3d8
112+
0x000000000000000a (STRSZ) 141 (bytes)
113+
0x000000000000000b (SYMENT) 24 (bytes)
114+
0x0000000000000015 (DEBUG) 0x0
115+
0x0000000000000003 (PLTGOT) 0x3fb8
116+
0x0000000000000002 (PLTRELSZ) 24 (bytes)
117+
0x0000000000000014 (PLTREL) RELA
118+
0x0000000000000017 (JMPREL) 0x610
119+
0x0000000000000007 (RELA) 0x550
120+
0x0000000000000008 (RELASZ) 192 (bytes)
121+
0x0000000000000009 (RELAENT) 24 (bytes)
122+
0x000000000000001e (FLAGS) BIND_NOW
123+
0x000000006ffffffb (FLAGS_1) Flags: NOW PIE
124+
0x000000006ffffffe (VERNEED) 0x520
125+
0x000000006fffffff (VERNEEDNUM) 1
126+
0x000000006ffffff0 (VERSYM) 0x50e
127+
0x000000006ffffff9 (RELACOUNT) 3
128+
0x0000000000000000 (NULL) 0x0
101129

102130
Relocation section '.rela.dyn' at offset 0x550 contains 8 entries:
103131
Offset Info Type Symbol's Value Symbol's Name + Addend

‎src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=lib_debug.so.verified.txt‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,25 @@ Program Headers:
8787
09
8888
10 .init_array .fini_array .dynamic .got
8989

90-
There is no dynamic section in this file.
90+
Dynamic section at offset 0x2e90 contains 17 entries:
91+
Tag Type Name/Value
92+
0x000000000000000c (INIT) 0x1000
93+
0x000000000000000d (FINI) 0x1130
94+
0x0000000000000019 (INIT_ARRAY) 0x3e80
95+
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
96+
0x000000000000001a (FINI_ARRAY) 0x3e88
97+
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
98+
0x000000006ffffef5 (GNU_HASH) 0x2f0
99+
0x0000000000000005 (STRTAB) 0x3c0
100+
0x0000000000000006 (SYMTAB) 0x318
101+
0x000000000000000a (STRSZ) 122 (bytes)
102+
0x000000000000000b (SYMENT) 24 (bytes)
103+
0x0000000000000003 (PLTGOT) 0x4000
104+
0x0000000000000007 (RELA) 0x440
105+
0x0000000000000008 (RELASZ) 168 (bytes)
106+
0x0000000000000009 (RELAENT) 24 (bytes)
107+
0x000000006ffffff9 (RELACOUNT) 3
108+
0x0000000000000000 (NULL) 0x0
91109

92110
Relocation section '.rela.dyn' at offset 0x440 contains 7 entries:
93111
Offset Info Type Symbol's Value Symbol's Name + Addend

‎src/LibObjectFile.Tests/Verified/ElfSimpleTests.TestElf_name=libstdc++.so.verified.txt‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,38 @@ Program Headers:
9393
10
9494
11 .init_array .fini_array .data.rel.ro .dynamic .got
9595

96-
There is no dynamic section in this file.
96+
Dynamic section at offset 0x223c90 contains 30 entries:
97+
Tag Type Name/Value
98+
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
99+
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
100+
0x0000000000000001 (NEEDED) Shared library: [ld-linux-x86-64.so.2]
101+
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
102+
0x000000000000000e (SONAME) Library soname: [libstdc++.so.6]
103+
0x000000000000000c (INIT) 0x9a000
104+
0x000000000000000d (FINI) 0x1aafc4
105+
0x0000000000000019 (INIT_ARRAY) 0x21b880
106+
0x000000000000001b (INIT_ARRAYSZ) 120 (bytes)
107+
0x000000000000001a (FINI_ARRAY) 0x21b8f8
108+
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
109+
0x000000006ffffef5 (GNU_HASH) 0x328
110+
0x0000000000000005 (STRTAB) 0x2d310
111+
0x0000000000000006 (SYMTAB) 0x90a0
112+
0x000000000000000a (STRSZ) 302456 (bytes)
113+
0x000000000000000b (SYMENT) 24 (bytes)
114+
0x0000000000000003 (PLTGOT) 0x226000
115+
0x0000000000000002 (PLTRELSZ) 25032 (bytes)
116+
0x0000000000000014 (PLTREL) RELA
117+
0x0000000000000017 (JMPREL) 0x92f78
118+
0x0000000000000007 (RELA) 0x7a8e8
119+
0x0000000000000008 (RELASZ) 99984 (bytes)
120+
0x0000000000000009 (RELAENT) 24 (bytes)
121+
0x000000006ffffffc (VERDEF) 0x7a0c0
122+
0x000000006ffffffd (VERDEFNUM) 48
123+
0x000000006ffffffe (VERNEED) 0x7a758
124+
0x000000006fffffff (VERNEEDNUM) 4
125+
0x000000006ffffff0 (VERSYM) 0x77088
126+
0x000000006ffffff9 (RELACOUNT) 899
127+
0x0000000000000000 (NULL) 0x0
97128

98129
Relocation section '.rela.dyn' at offset 0x7a8e8 contains 4166 entries:
99130
Offset Info Type Symbol's Value Symbol's Name + Addend

0 commit comments

Comments
 (0)