1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package elf
6
7import (
8	"bytes"
9	"compress/gzip"
10	"compress/zlib"
11	"debug/dwarf"
12	"encoding/binary"
13	"errors"
14	"fmt"
15	"io"
16	"math/rand"
17	"net"
18	"os"
19	"path"
20	"reflect"
21	"runtime"
22	"slices"
23	"strings"
24	"testing"
25)
26
27type fileTest struct {
28	file     string
29	hdr      FileHeader
30	sections []SectionHeader
31	progs    []ProgHeader
32	needed   []string
33	symbols  []Symbol
34}
35
36var fileTests = []fileTest{
37	{
38		"testdata/gcc-386-freebsd-exec",
39		FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386, 0x80483cc},
40		[]SectionHeader{
41			{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
42			{".interp", SHT_PROGBITS, SHF_ALLOC, 0x80480d4, 0xd4, 0x15, 0x0, 0x0, 0x1, 0x0, 0x15},
43			{".hash", SHT_HASH, SHF_ALLOC, 0x80480ec, 0xec, 0x90, 0x3, 0x0, 0x4, 0x4, 0x90},
44			{".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x804817c, 0x17c, 0x110, 0x4, 0x1, 0x4, 0x10, 0x110},
45			{".dynstr", SHT_STRTAB, SHF_ALLOC, 0x804828c, 0x28c, 0xbb, 0x0, 0x0, 0x1, 0x0, 0xbb},
46			{".rel.plt", SHT_REL, SHF_ALLOC, 0x8048348, 0x348, 0x20, 0x3, 0x7, 0x4, 0x8, 0x20},
47			{".init", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x8048368, 0x368, 0x11, 0x0, 0x0, 0x4, 0x0, 0x11},
48			{".plt", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x804837c, 0x37c, 0x50, 0x0, 0x0, 0x4, 0x4, 0x50},
49			{".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x80483cc, 0x3cc, 0x180, 0x0, 0x0, 0x4, 0x0, 0x180},
50			{".fini", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x804854c, 0x54c, 0xc, 0x0, 0x0, 0x4, 0x0, 0xc},
51			{".rodata", SHT_PROGBITS, SHF_ALLOC, 0x8048558, 0x558, 0xa3, 0x0, 0x0, 0x1, 0x0, 0xa3},
52			{".data", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80495fc, 0x5fc, 0xc, 0x0, 0x0, 0x4, 0x0, 0xc},
53			{".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x8049608, 0x608, 0x4, 0x0, 0x0, 0x4, 0x0, 0x4},
54			{".dynamic", SHT_DYNAMIC, SHF_WRITE + SHF_ALLOC, 0x804960c, 0x60c, 0x98, 0x4, 0x0, 0x4, 0x8, 0x98},
55			{".ctors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496a4, 0x6a4, 0x8, 0x0, 0x0, 0x4, 0x0, 0x8},
56			{".dtors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496ac, 0x6ac, 0x8, 0x0, 0x0, 0x4, 0x0, 0x8},
57			{".jcr", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496b4, 0x6b4, 0x4, 0x0, 0x0, 0x4, 0x0, 0x4},
58			{".got", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x80496b8, 0x6b8, 0x1c, 0x0, 0x0, 0x4, 0x4, 0x1c},
59			{".bss", SHT_NOBITS, SHF_WRITE + SHF_ALLOC, 0x80496d4, 0x6d4, 0x20, 0x0, 0x0, 0x4, 0x0, 0x20},
60			{".comment", SHT_PROGBITS, 0x0, 0x0, 0x6d4, 0x12d, 0x0, 0x0, 0x1, 0x0, 0x12d},
61			{".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x801, 0x20, 0x0, 0x0, 0x1, 0x0, 0x20},
62			{".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0x821, 0x1b, 0x0, 0x0, 0x1, 0x0, 0x1b},
63			{".debug_info", SHT_PROGBITS, 0x0, 0x0, 0x83c, 0x11d, 0x0, 0x0, 0x1, 0x0, 0x11d},
64			{".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0x959, 0x41, 0x0, 0x0, 0x1, 0x0, 0x41},
65			{".debug_line", SHT_PROGBITS, 0x0, 0x0, 0x99a, 0x35, 0x0, 0x0, 0x1, 0x0, 0x35},
66			{".debug_frame", SHT_PROGBITS, 0x0, 0x0, 0x9d0, 0x30, 0x0, 0x0, 0x4, 0x0, 0x30},
67			{".debug_str", SHT_PROGBITS, 0x0, 0x0, 0xa00, 0xd, 0x0, 0x0, 0x1, 0x0, 0xd},
68			{".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xa0d, 0xf8, 0x0, 0x0, 0x1, 0x0, 0xf8},
69			{".symtab", SHT_SYMTAB, 0x0, 0x0, 0xfb8, 0x4b0, 0x1d, 0x38, 0x4, 0x10, 0x4b0},
70			{".strtab", SHT_STRTAB, 0x0, 0x0, 0x1468, 0x206, 0x0, 0x0, 0x1, 0x0, 0x206},
71		},
72		[]ProgHeader{
73			{PT_PHDR, PF_R + PF_X, 0x34, 0x8048034, 0x8048034, 0xa0, 0xa0, 0x4},
74			{PT_INTERP, PF_R, 0xd4, 0x80480d4, 0x80480d4, 0x15, 0x15, 0x1},
75			{PT_LOAD, PF_R + PF_X, 0x0, 0x8048000, 0x8048000, 0x5fb, 0x5fb, 0x1000},
76			{PT_LOAD, PF_R + PF_W, 0x5fc, 0x80495fc, 0x80495fc, 0xd8, 0xf8, 0x1000},
77			{PT_DYNAMIC, PF_R + PF_W, 0x60c, 0x804960c, 0x804960c, 0x98, 0x98, 0x4},
78		},
79		[]string{"libc.so.6"},
80		[]Symbol{
81			{"", 3, 0, 1, 134512852, 0, "", ""},
82			{"", 3, 0, 2, 134512876, 0, "", ""},
83			{"", 3, 0, 3, 134513020, 0, "", ""},
84			{"", 3, 0, 4, 134513292, 0, "", ""},
85			{"", 3, 0, 5, 134513480, 0, "", ""},
86			{"", 3, 0, 6, 134513512, 0, "", ""},
87			{"", 3, 0, 7, 134513532, 0, "", ""},
88			{"", 3, 0, 8, 134513612, 0, "", ""},
89			{"", 3, 0, 9, 134513996, 0, "", ""},
90			{"", 3, 0, 10, 134514008, 0, "", ""},
91			{"", 3, 0, 11, 134518268, 0, "", ""},
92			{"", 3, 0, 12, 134518280, 0, "", ""},
93			{"", 3, 0, 13, 134518284, 0, "", ""},
94			{"", 3, 0, 14, 134518436, 0, "", ""},
95			{"", 3, 0, 15, 134518444, 0, "", ""},
96			{"", 3, 0, 16, 134518452, 0, "", ""},
97			{"", 3, 0, 17, 134518456, 0, "", ""},
98			{"", 3, 0, 18, 134518484, 0, "", ""},
99			{"", 3, 0, 19, 0, 0, "", ""},
100			{"", 3, 0, 20, 0, 0, "", ""},
101			{"", 3, 0, 21, 0, 0, "", ""},
102			{"", 3, 0, 22, 0, 0, "", ""},
103			{"", 3, 0, 23, 0, 0, "", ""},
104			{"", 3, 0, 24, 0, 0, "", ""},
105			{"", 3, 0, 25, 0, 0, "", ""},
106			{"", 3, 0, 26, 0, 0, "", ""},
107			{"", 3, 0, 27, 0, 0, "", ""},
108			{"", 3, 0, 28, 0, 0, "", ""},
109			{"", 3, 0, 29, 0, 0, "", ""},
110			{"crt1.c", 4, 0, 65521, 0, 0, "", ""},
111			{"/usr/src/lib/csu/i386-elf/crti.S", 4, 0, 65521, 0, 0, "", ""},
112			{"<command line>", 4, 0, 65521, 0, 0, "", ""},
113			{"<built-in>", 4, 0, 65521, 0, 0, "", ""},
114			{"/usr/src/lib/csu/i386-elf/crti.S", 4, 0, 65521, 0, 0, "", ""},
115			{"crtstuff.c", 4, 0, 65521, 0, 0, "", ""},
116			{"__CTOR_LIST__", 1, 0, 14, 134518436, 0, "", ""},
117			{"__DTOR_LIST__", 1, 0, 15, 134518444, 0, "", ""},
118			{"__EH_FRAME_BEGIN__", 1, 0, 12, 134518280, 0, "", ""},
119			{"__JCR_LIST__", 1, 0, 16, 134518452, 0, "", ""},
120			{"p.0", 1, 0, 11, 134518276, 0, "", ""},
121			{"completed.1", 1, 0, 18, 134518484, 1, "", ""},
122			{"__do_global_dtors_aux", 2, 0, 8, 134513760, 0, "", ""},
123			{"object.2", 1, 0, 18, 134518488, 24, "", ""},
124			{"frame_dummy", 2, 0, 8, 134513836, 0, "", ""},
125			{"crtstuff.c", 4, 0, 65521, 0, 0, "", ""},
126			{"__CTOR_END__", 1, 0, 14, 134518440, 0, "", ""},
127			{"__DTOR_END__", 1, 0, 15, 134518448, 0, "", ""},
128			{"__FRAME_END__", 1, 0, 12, 134518280, 0, "", ""},
129			{"__JCR_END__", 1, 0, 16, 134518452, 0, "", ""},
130			{"__do_global_ctors_aux", 2, 0, 8, 134513960, 0, "", ""},
131			{"/usr/src/lib/csu/i386-elf/crtn.S", 4, 0, 65521, 0, 0, "", ""},
132			{"<command line>", 4, 0, 65521, 0, 0, "", ""},
133			{"<built-in>", 4, 0, 65521, 0, 0, "", ""},
134			{"/usr/src/lib/csu/i386-elf/crtn.S", 4, 0, 65521, 0, 0, "", ""},
135			{"hello.c", 4, 0, 65521, 0, 0, "", ""},
136			{"printf", 18, 0, 0, 0, 44, "", ""},
137			{"_DYNAMIC", 17, 0, 65521, 134518284, 0, "", ""},
138			{"__dso_handle", 17, 2, 11, 134518272, 0, "", ""},
139			{"_init", 18, 0, 6, 134513512, 0, "", ""},
140			{"environ", 17, 0, 18, 134518512, 4, "", ""},
141			{"__deregister_frame_info", 32, 0, 0, 0, 0, "", ""},
142			{"__progname", 17, 0, 11, 134518268, 4, "", ""},
143			{"_start", 18, 0, 8, 134513612, 145, "", ""},
144			{"__bss_start", 16, 0, 65521, 134518484, 0, "", ""},
145			{"main", 18, 0, 8, 134513912, 46, "", ""},
146			{"_init_tls", 18, 0, 0, 0, 5, "", ""},
147			{"_fini", 18, 0, 9, 134513996, 0, "", ""},
148			{"atexit", 18, 0, 0, 0, 43, "", ""},
149			{"_edata", 16, 0, 65521, 134518484, 0, "", ""},
150			{"_GLOBAL_OFFSET_TABLE_", 17, 0, 65521, 134518456, 0, "", ""},
151			{"_end", 16, 0, 65521, 134518516, 0, "", ""},
152			{"exit", 18, 0, 0, 0, 68, "", ""},
153			{"_Jv_RegisterClasses", 32, 0, 0, 0, 0, "", ""},
154			{"__register_frame_info", 32, 0, 0, 0, 0, "", ""},
155		},
156	},
157	{
158		"testdata/gcc-amd64-linux-exec",
159		FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64, 0x4003e0},
160		[]SectionHeader{
161			{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
162			{".interp", SHT_PROGBITS, SHF_ALLOC, 0x400200, 0x200, 0x1c, 0x0, 0x0, 0x1, 0x0, 0x1c},
163			{".note.ABI-tag", SHT_NOTE, SHF_ALLOC, 0x40021c, 0x21c, 0x20, 0x0, 0x0, 0x4, 0x0, 0x20},
164			{".hash", SHT_HASH, SHF_ALLOC, 0x400240, 0x240, 0x24, 0x5, 0x0, 0x8, 0x4, 0x24},
165			{".gnu.hash", SHT_LOOS + 268435446, SHF_ALLOC, 0x400268, 0x268, 0x1c, 0x5, 0x0, 0x8, 0x0, 0x1c},
166			{".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x400288, 0x288, 0x60, 0x6, 0x1, 0x8, 0x18, 0x60},
167			{".dynstr", SHT_STRTAB, SHF_ALLOC, 0x4002e8, 0x2e8, 0x3d, 0x0, 0x0, 0x1, 0x0, 0x3d},
168			{".gnu.version", SHT_HIOS, SHF_ALLOC, 0x400326, 0x326, 0x8, 0x5, 0x0, 0x2, 0x2, 0x8},
169			{".gnu.version_r", SHT_LOOS + 268435454, SHF_ALLOC, 0x400330, 0x330, 0x20, 0x6, 0x1, 0x8, 0x0, 0x20},
170			{".rela.dyn", SHT_RELA, SHF_ALLOC, 0x400350, 0x350, 0x18, 0x5, 0x0, 0x8, 0x18, 0x18},
171			{".rela.plt", SHT_RELA, SHF_ALLOC, 0x400368, 0x368, 0x30, 0x5, 0xc, 0x8, 0x18, 0x30},
172			{".init", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x400398, 0x398, 0x18, 0x0, 0x0, 0x4, 0x0, 0x18},
173			{".plt", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x4003b0, 0x3b0, 0x30, 0x0, 0x0, 0x4, 0x10, 0x30},
174			{".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x4003e0, 0x3e0, 0x1b4, 0x0, 0x0, 0x10, 0x0, 0x1b4},
175			{".fini", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x400594, 0x594, 0xe, 0x0, 0x0, 0x4, 0x0, 0xe},
176			{".rodata", SHT_PROGBITS, SHF_ALLOC, 0x4005a4, 0x5a4, 0x11, 0x0, 0x0, 0x4, 0x0, 0x11},
177			{".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, 0x4005b8, 0x5b8, 0x24, 0x0, 0x0, 0x4, 0x0, 0x24},
178			{".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x4005e0, 0x5e0, 0xa4, 0x0, 0x0, 0x8, 0x0, 0xa4},
179			{".ctors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600688, 0x688, 0x10, 0x0, 0x0, 0x8, 0x0, 0x10},
180			{".dtors", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600698, 0x698, 0x10, 0x0, 0x0, 0x8, 0x0, 0x10},
181			{".jcr", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x6006a8, 0x6a8, 0x8, 0x0, 0x0, 0x8, 0x0, 0x8},
182			{".dynamic", SHT_DYNAMIC, SHF_WRITE + SHF_ALLOC, 0x6006b0, 0x6b0, 0x1a0, 0x6, 0x0, 0x8, 0x10, 0x1a0},
183			{".got", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600850, 0x850, 0x8, 0x0, 0x0, 0x8, 0x8, 0x8},
184			{".got.plt", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600858, 0x858, 0x28, 0x0, 0x0, 0x8, 0x8, 0x28},
185			{".data", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x600880, 0x880, 0x18, 0x0, 0x0, 0x8, 0x0, 0x18},
186			{".bss", SHT_NOBITS, SHF_WRITE + SHF_ALLOC, 0x600898, 0x898, 0x8, 0x0, 0x0, 0x4, 0x0, 0x8},
187			{".comment", SHT_PROGBITS, 0x0, 0x0, 0x898, 0x126, 0x0, 0x0, 0x1, 0x0, 0x126},
188			{".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x9c0, 0x90, 0x0, 0x0, 0x10, 0x0, 0x90},
189			{".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0xa50, 0x25, 0x0, 0x0, 0x1, 0x0, 0x25},
190			{".debug_info", SHT_PROGBITS, 0x0, 0x0, 0xa75, 0x1a7, 0x0, 0x0, 0x1, 0x0, 0x1a7},
191			{".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0xc1c, 0x6f, 0x0, 0x0, 0x1, 0x0, 0x6f},
192			{".debug_line", SHT_PROGBITS, 0x0, 0x0, 0xc8b, 0x13f, 0x0, 0x0, 0x1, 0x0, 0x13f},
193			{".debug_str", SHT_PROGBITS, SHF_MERGE + SHF_STRINGS, 0x0, 0xdca, 0xb1, 0x0, 0x0, 0x1, 0x1, 0xb1},
194			{".debug_ranges", SHT_PROGBITS, 0x0, 0x0, 0xe80, 0x90, 0x0, 0x0, 0x10, 0x0, 0x90},
195			{".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xf10, 0x149, 0x0, 0x0, 0x1, 0x0, 0x149},
196			{".symtab", SHT_SYMTAB, 0x0, 0x0, 0x19a0, 0x6f0, 0x24, 0x39, 0x8, 0x18, 0x6f0},
197			{".strtab", SHT_STRTAB, 0x0, 0x0, 0x2090, 0x1fc, 0x0, 0x0, 0x1, 0x0, 0x1fc},
198		},
199		[]ProgHeader{
200			{PT_PHDR, PF_R + PF_X, 0x40, 0x400040, 0x400040, 0x1c0, 0x1c0, 0x8},
201			{PT_INTERP, PF_R, 0x200, 0x400200, 0x400200, 0x1c, 0x1c, 1},
202			{PT_LOAD, PF_R + PF_X, 0x0, 0x400000, 0x400000, 0x684, 0x684, 0x200000},
203			{PT_LOAD, PF_R + PF_W, 0x688, 0x600688, 0x600688, 0x210, 0x218, 0x200000},
204			{PT_DYNAMIC, PF_R + PF_W, 0x6b0, 0x6006b0, 0x6006b0, 0x1a0, 0x1a0, 0x8},
205			{PT_NOTE, PF_R, 0x21c, 0x40021c, 0x40021c, 0x20, 0x20, 0x4},
206			{PT_LOOS + 0x474E550, PF_R, 0x5b8, 0x4005b8, 0x4005b8, 0x24, 0x24, 0x4},
207			{PT_LOOS + 0x474E551, PF_R + PF_W, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8},
208		},
209		[]string{"libc.so.6"},
210		[]Symbol{
211			{"", 3, 0, 1, 4194816, 0, "", ""},
212			{"", 3, 0, 2, 4194844, 0, "", ""},
213			{"", 3, 0, 3, 4194880, 0, "", ""},
214			{"", 3, 0, 4, 4194920, 0, "", ""},
215			{"", 3, 0, 5, 4194952, 0, "", ""},
216			{"", 3, 0, 6, 4195048, 0, "", ""},
217			{"", 3, 0, 7, 4195110, 0, "", ""},
218			{"", 3, 0, 8, 4195120, 0, "", ""},
219			{"", 3, 0, 9, 4195152, 0, "", ""},
220			{"", 3, 0, 10, 4195176, 0, "", ""},
221			{"", 3, 0, 11, 4195224, 0, "", ""},
222			{"", 3, 0, 12, 4195248, 0, "", ""},
223			{"", 3, 0, 13, 4195296, 0, "", ""},
224			{"", 3, 0, 14, 4195732, 0, "", ""},
225			{"", 3, 0, 15, 4195748, 0, "", ""},
226			{"", 3, 0, 16, 4195768, 0, "", ""},
227			{"", 3, 0, 17, 4195808, 0, "", ""},
228			{"", 3, 0, 18, 6293128, 0, "", ""},
229			{"", 3, 0, 19, 6293144, 0, "", ""},
230			{"", 3, 0, 20, 6293160, 0, "", ""},
231			{"", 3, 0, 21, 6293168, 0, "", ""},
232			{"", 3, 0, 22, 6293584, 0, "", ""},
233			{"", 3, 0, 23, 6293592, 0, "", ""},
234			{"", 3, 0, 24, 6293632, 0, "", ""},
235			{"", 3, 0, 25, 6293656, 0, "", ""},
236			{"", 3, 0, 26, 0, 0, "", ""},
237			{"", 3, 0, 27, 0, 0, "", ""},
238			{"", 3, 0, 28, 0, 0, "", ""},
239			{"", 3, 0, 29, 0, 0, "", ""},
240			{"", 3, 0, 30, 0, 0, "", ""},
241			{"", 3, 0, 31, 0, 0, "", ""},
242			{"", 3, 0, 32, 0, 0, "", ""},
243			{"", 3, 0, 33, 0, 0, "", ""},
244			{"init.c", 4, 0, 65521, 0, 0, "", ""},
245			{"initfini.c", 4, 0, 65521, 0, 0, "", ""},
246			{"call_gmon_start", 2, 0, 13, 4195340, 0, "", ""},
247			{"crtstuff.c", 4, 0, 65521, 0, 0, "", ""},
248			{"__CTOR_LIST__", 1, 0, 18, 6293128, 0, "", ""},
249			{"__DTOR_LIST__", 1, 0, 19, 6293144, 0, "", ""},
250			{"__JCR_LIST__", 1, 0, 20, 6293160, 0, "", ""},
251			{"__do_global_dtors_aux", 2, 0, 13, 4195376, 0, "", ""},
252			{"completed.6183", 1, 0, 25, 6293656, 1, "", ""},
253			{"p.6181", 1, 0, 24, 6293648, 0, "", ""},
254			{"frame_dummy", 2, 0, 13, 4195440, 0, "", ""},
255			{"crtstuff.c", 4, 0, 65521, 0, 0, "", ""},
256			{"__CTOR_END__", 1, 0, 18, 6293136, 0, "", ""},
257			{"__DTOR_END__", 1, 0, 19, 6293152, 0, "", ""},
258			{"__FRAME_END__", 1, 0, 17, 4195968, 0, "", ""},
259			{"__JCR_END__", 1, 0, 20, 6293160, 0, "", ""},
260			{"__do_global_ctors_aux", 2, 0, 13, 4195680, 0, "", ""},
261			{"initfini.c", 4, 0, 65521, 0, 0, "", ""},
262			{"hello.c", 4, 0, 65521, 0, 0, "", ""},
263			{"_GLOBAL_OFFSET_TABLE_", 1, 2, 23, 6293592, 0, "", ""},
264			{"__init_array_end", 0, 2, 18, 6293124, 0, "", ""},
265			{"__init_array_start", 0, 2, 18, 6293124, 0, "", ""},
266			{"_DYNAMIC", 1, 2, 21, 6293168, 0, "", ""},
267			{"data_start", 32, 0, 24, 6293632, 0, "", ""},
268			{"__libc_csu_fini", 18, 0, 13, 4195520, 2, "", ""},
269			{"_start", 18, 0, 13, 4195296, 0, "", ""},
270			{"__gmon_start__", 32, 0, 0, 0, 0, "", ""},
271			{"_Jv_RegisterClasses", 32, 0, 0, 0, 0, "", ""},
272			{"puts@@GLIBC_2.2.5", 18, 0, 0, 0, 396, "", ""},
273			{"_fini", 18, 0, 14, 4195732, 0, "", ""},
274			{"__libc_start_main@@GLIBC_2.2.5", 18, 0, 0, 0, 450, "", ""},
275			{"_IO_stdin_used", 17, 0, 15, 4195748, 4, "", ""},
276			{"__data_start", 16, 0, 24, 6293632, 0, "", ""},
277			{"__dso_handle", 17, 2, 24, 6293640, 0, "", ""},
278			{"__libc_csu_init", 18, 0, 13, 4195536, 137, "", ""},
279			{"__bss_start", 16, 0, 65521, 6293656, 0, "", ""},
280			{"_end", 16, 0, 65521, 6293664, 0, "", ""},
281			{"_edata", 16, 0, 65521, 6293656, 0, "", ""},
282			{"main", 18, 0, 13, 4195480, 27, "", ""},
283			{"_init", 18, 0, 11, 4195224, 0, "", ""},
284		},
285	},
286	{
287		"testdata/hello-world-core.gz",
288		FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0x0, binary.LittleEndian, ET_CORE, EM_X86_64, 0x0},
289		[]SectionHeader{},
290		[]ProgHeader{
291			{Type: PT_NOTE, Flags: 0x0, Off: 0x3f8, Vaddr: 0x0, Paddr: 0x0, Filesz: 0x8ac, Memsz: 0x0, Align: 0x0},
292			{Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x1000, Vaddr: 0x400000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1000, Align: 0x1000},
293			{Type: PT_LOAD, Flags: PF_R, Off: 0x1000, Vaddr: 0x401000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
294			{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x2000, Vaddr: 0x402000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
295			{Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3000, Vaddr: 0x7f54078b8000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1b5000, Align: 0x1000},
296			{Type: PT_LOAD, Flags: 0x0, Off: 0x3000, Vaddr: 0x7f5407a6d000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1ff000, Align: 0x1000},
297			{Type: PT_LOAD, Flags: PF_R, Off: 0x3000, Vaddr: 0x7f5407c6c000, Paddr: 0x0, Filesz: 0x4000, Memsz: 0x4000, Align: 0x1000},
298			{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x7000, Vaddr: 0x7f5407c70000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000},
299			{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x9000, Vaddr: 0x7f5407c72000, Paddr: 0x0, Filesz: 0x5000, Memsz: 0x5000, Align: 0x1000},
300			{Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0xe000, Vaddr: 0x7f5407c77000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x22000, Align: 0x1000},
301			{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0xe000, Vaddr: 0x7f5407e81000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000},
302			{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x11000, Vaddr: 0x7f5407e96000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000},
303			{Type: PT_LOAD, Flags: PF_R, Off: 0x14000, Vaddr: 0x7f5407e99000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
304			{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x15000, Vaddr: 0x7f5407e9a000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000},
305			{Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x17000, Vaddr: 0x7fff79972000, Paddr: 0x0, Filesz: 0x23000, Memsz: 0x23000, Align: 0x1000},
306			{Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3a000, Vaddr: 0x7fff799f8000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
307			{Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3b000, Vaddr: 0xffffffffff600000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
308		},
309		nil,
310		nil,
311	},
312	{
313		"testdata/compressed-32.obj",
314		FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0x0, binary.LittleEndian, ET_REL, EM_386, 0x0},
315		[]SectionHeader{
316			{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
317			{".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0x0, 0x34, 0x17, 0x0, 0x0, 0x1, 0x0, 0x17},
318			{".rel.text", SHT_REL, SHF_INFO_LINK, 0x0, 0x3dc, 0x10, 0x13, 0x1, 0x4, 0x8, 0x10},
319			{".data", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC, 0x0, 0x4b, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0},
320			{".bss", SHT_NOBITS, SHF_WRITE | SHF_ALLOC, 0x0, 0x4b, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0},
321			{".rodata", SHT_PROGBITS, SHF_ALLOC, 0x0, 0x4b, 0xd, 0x0, 0x0, 0x1, 0x0, 0xd},
322			{".debug_info", SHT_PROGBITS, SHF_COMPRESSED, 0x0, 0x58, 0xb4, 0x0, 0x0, 0x1, 0x0, 0x84},
323			{".rel.debug_info", SHT_REL, SHF_INFO_LINK, 0x0, 0x3ec, 0xa0, 0x13, 0x6, 0x4, 0x8, 0xa0},
324			{".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0xdc, 0x5a, 0x0, 0x0, 0x1, 0x0, 0x5a},
325			{".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x136, 0x20, 0x0, 0x0, 0x1, 0x0, 0x20},
326			{".rel.debug_aranges", SHT_REL, SHF_INFO_LINK, 0x0, 0x48c, 0x10, 0x13, 0x9, 0x4, 0x8, 0x10},
327			{".debug_line", SHT_PROGBITS, 0x0, 0x0, 0x156, 0x5c, 0x0, 0x0, 0x1, 0x0, 0x5c},
328			{".rel.debug_line", SHT_REL, SHF_INFO_LINK, 0x0, 0x49c, 0x8, 0x13, 0xb, 0x4, 0x8, 0x8},
329			{".debug_str", SHT_PROGBITS, SHF_MERGE | SHF_STRINGS | SHF_COMPRESSED, 0x0, 0x1b2, 0x10f, 0x0, 0x0, 0x1, 0x1, 0xb3},
330			{".comment", SHT_PROGBITS, SHF_MERGE | SHF_STRINGS, 0x0, 0x265, 0x2a, 0x0, 0x0, 0x1, 0x1, 0x2a},
331			{".note.GNU-stack", SHT_PROGBITS, 0x0, 0x0, 0x28f, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0},
332			{".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x0, 0x290, 0x38, 0x0, 0x0, 0x4, 0x0, 0x38},
333			{".rel.eh_frame", SHT_REL, SHF_INFO_LINK, 0x0, 0x4a4, 0x8, 0x13, 0x10, 0x4, 0x8, 0x8},
334			{".shstrtab", SHT_STRTAB, 0x0, 0x0, 0x4ac, 0xab, 0x0, 0x0, 0x1, 0x0, 0xab},
335			{".symtab", SHT_SYMTAB, 0x0, 0x0, 0x2c8, 0x100, 0x14, 0xe, 0x4, 0x10, 0x100},
336			{".strtab", SHT_STRTAB, 0x0, 0x0, 0x3c8, 0x13, 0x0, 0x0, 0x1, 0x0, 0x13},
337		},
338		[]ProgHeader{},
339		nil,
340		[]Symbol{
341			{"hello.c", 4, 0, 65521, 0, 0, "", ""},
342			{"", 3, 0, 1, 0, 0, "", ""},
343			{"", 3, 0, 3, 0, 0, "", ""},
344			{"", 3, 0, 4, 0, 0, "", ""},
345			{"", 3, 0, 5, 0, 0, "", ""},
346			{"", 3, 0, 6, 0, 0, "", ""},
347			{"", 3, 0, 8, 0, 0, "", ""},
348			{"", 3, 0, 9, 0, 0, "", ""},
349			{"", 3, 0, 11, 0, 0, "", ""},
350			{"", 3, 0, 13, 0, 0, "", ""},
351			{"", 3, 0, 15, 0, 0, "", ""},
352			{"", 3, 0, 16, 0, 0, "", ""},
353			{"", 3, 0, 14, 0, 0, "", ""},
354			{"main", 18, 0, 1, 0, 23, "", ""},
355			{"puts", 16, 0, 0, 0, 0, "", ""},
356		},
357	},
358	{
359		"testdata/compressed-64.obj",
360		FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0x0, binary.LittleEndian, ET_REL, EM_X86_64, 0x0},
361		[]SectionHeader{
362			{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
363			{".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0x0, 0x40, 0x1b, 0x0, 0x0, 0x1, 0x0, 0x1b},
364			{".rela.text", SHT_RELA, SHF_INFO_LINK, 0x0, 0x488, 0x30, 0x13, 0x1, 0x8, 0x18, 0x30},
365			{".data", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC, 0x0, 0x5b, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0},
366			{".bss", SHT_NOBITS, SHF_WRITE | SHF_ALLOC, 0x0, 0x5b, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0},
367			{".rodata", SHT_PROGBITS, SHF_ALLOC, 0x0, 0x5b, 0xd, 0x0, 0x0, 0x1, 0x0, 0xd},
368			{".debug_info", SHT_PROGBITS, SHF_COMPRESSED, 0x0, 0x68, 0xba, 0x0, 0x0, 0x1, 0x0, 0x72},
369			{".rela.debug_info", SHT_RELA, SHF_INFO_LINK, 0x0, 0x4b8, 0x1c8, 0x13, 0x6, 0x8, 0x18, 0x1c8},
370			{".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0xda, 0x5c, 0x0, 0x0, 0x1, 0x0, 0x5c},
371			{".debug_aranges", SHT_PROGBITS, SHF_COMPRESSED, 0x0, 0x136, 0x30, 0x0, 0x0, 0x1, 0x0, 0x2f},
372			{".rela.debug_aranges", SHT_RELA, SHF_INFO_LINK, 0x0, 0x680, 0x30, 0x13, 0x9, 0x8, 0x18, 0x30},
373			{".debug_line", SHT_PROGBITS, 0x0, 0x0, 0x165, 0x60, 0x0, 0x0, 0x1, 0x0, 0x60},
374			{".rela.debug_line", SHT_RELA, SHF_INFO_LINK, 0x0, 0x6b0, 0x18, 0x13, 0xb, 0x8, 0x18, 0x18},
375			{".debug_str", SHT_PROGBITS, SHF_MERGE | SHF_STRINGS | SHF_COMPRESSED, 0x0, 0x1c5, 0x104, 0x0, 0x0, 0x1, 0x1, 0xc3},
376			{".comment", SHT_PROGBITS, SHF_MERGE | SHF_STRINGS, 0x0, 0x288, 0x2a, 0x0, 0x0, 0x1, 0x1, 0x2a},
377			{".note.GNU-stack", SHT_PROGBITS, 0x0, 0x0, 0x2b2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0},
378			{".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x0, 0x2b8, 0x38, 0x0, 0x0, 0x8, 0x0, 0x38},
379			{".rela.eh_frame", SHT_RELA, SHF_INFO_LINK, 0x0, 0x6c8, 0x18, 0x13, 0x10, 0x8, 0x18, 0x18},
380			{".shstrtab", SHT_STRTAB, 0x0, 0x0, 0x6e0, 0xb0, 0x0, 0x0, 0x1, 0x0, 0xb0},
381			{".symtab", SHT_SYMTAB, 0x0, 0x0, 0x2f0, 0x180, 0x14, 0xe, 0x8, 0x18, 0x180},
382			{".strtab", SHT_STRTAB, 0x0, 0x0, 0x470, 0x13, 0x0, 0x0, 0x1, 0x0, 0x13},
383		},
384		[]ProgHeader{},
385		nil,
386		[]Symbol{
387			{"hello.c", 4, 0, 65521, 0, 0, "", ""},
388			{"", 3, 0, 1, 0, 0, "", ""},
389			{"", 3, 0, 3, 0, 0, "", ""},
390			{"", 3, 0, 4, 0, 0, "", ""},
391			{"", 3, 0, 5, 0, 0, "", ""},
392			{"", 3, 0, 6, 0, 0, "", ""},
393			{"", 3, 0, 8, 0, 0, "", ""},
394			{"", 3, 0, 9, 0, 0, "", ""},
395			{"", 3, 0, 11, 0, 0, "", ""},
396			{"", 3, 0, 13, 0, 0, "", ""},
397			{"", 3, 0, 15, 0, 0, "", ""},
398			{"", 3, 0, 16, 0, 0, "", ""},
399			{"", 3, 0, 14, 0, 0, "", ""},
400			{"main", 18, 0, 1, 0, 27, "", ""},
401			{"puts", 16, 0, 0, 0, 0, "", ""},
402		},
403	},
404	{
405		"testdata/go-relocation-test-gcc620-sparc64.obj",
406		FileHeader{Class: ELFCLASS64, Data: ELFDATA2MSB, Version: EV_CURRENT, OSABI: ELFOSABI_NONE, ABIVersion: 0x0, ByteOrder: binary.BigEndian, Type: ET_REL, Machine: EM_SPARCV9, Entry: 0x0},
407		[]SectionHeader{
408			{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
409			{".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR, 0x0, 0x40, 0x2c, 0x0, 0x0, 0x4, 0x0, 0x2c},
410			{".rela.text", SHT_RELA, SHF_INFO_LINK, 0x0, 0xa58, 0x48, 0x13, 0x1, 0x8, 0x18, 0x48},
411			{".data", SHT_PROGBITS, SHF_WRITE + SHF_ALLOC, 0x0, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0},
412			{".bss", SHT_NOBITS, SHF_WRITE + SHF_ALLOC, 0x0, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0},
413			{".rodata", SHT_PROGBITS, SHF_ALLOC, 0x0, 0x70, 0xd, 0x0, 0x0, 0x8, 0x0, 0xd},
414			{".debug_info", SHT_PROGBITS, 0x0, 0x0, 0x7d, 0x346, 0x0, 0x0, 0x1, 0x0, 0x346},
415			{".rela.debug_info", SHT_RELA, SHF_INFO_LINK, 0x0, 0xaa0, 0x630, 0x13, 0x6, 0x8, 0x18, 0x630},
416			{".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0x3c3, 0xf1, 0x0, 0x0, 0x1, 0x0, 0xf1},
417			{".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x4b4, 0x30, 0x0, 0x0, 0x1, 0x0, 0x30},
418			{".rela.debug_aranges", SHT_RELA, SHF_INFO_LINK, 0x0, 0x10d0, 0x30, 0x13, 0x9, 0x8, 0x18, 0x30},
419			{".debug_line", SHT_PROGBITS, 0x0, 0x0, 0x4e4, 0xd3, 0x0, 0x0, 0x1, 0x0, 0xd3},
420			{".rela.debug_line", SHT_RELA, SHF_INFO_LINK, 0x0, 0x1100, 0x18, 0x13, 0xb, 0x8, 0x18, 0x18},
421			{".debug_str", SHT_PROGBITS, SHF_MERGE + SHF_STRINGS, 0x0, 0x5b7, 0x2a3, 0x0, 0x0, 0x1, 0x1, 0x2a3},
422			{".comment", SHT_PROGBITS, SHF_MERGE + SHF_STRINGS, 0x0, 0x85a, 0x2e, 0x0, 0x0, 0x1, 0x1, 0x2e},
423			{".note.GNU-stack", SHT_PROGBITS, 0x0, 0x0, 0x888, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0},
424			{".debug_frame", SHT_PROGBITS, 0x0, 0x0, 0x888, 0x38, 0x0, 0x0, 0x8, 0x0, 0x38},
425			{".rela.debug_frame", SHT_RELA, SHF_INFO_LINK, 0x0, 0x1118, 0x30, 0x13, 0x10, 0x8, 0x18, 0x30},
426			{".shstrtab", SHT_STRTAB, 0x0, 0x0, 0x1148, 0xb3, 0x0, 0x0, 0x1, 0x0, 0xb3},
427			{".symtab", SHT_SYMTAB, 0x0, 0x0, 0x8c0, 0x180, 0x14, 0xe, 0x8, 0x18, 0x180},
428			{".strtab", SHT_STRTAB, 0x0, 0x0, 0xa40, 0x13, 0x0, 0x0, 0x1, 0x0, 0x13},
429		},
430		[]ProgHeader{},
431		nil,
432		[]Symbol{
433			{"hello.c", 4, 0, 65521, 0, 0, "", ""},
434			{"", 3, 0, 1, 0, 0, "", ""},
435			{"", 3, 0, 3, 0, 0, "", ""},
436			{"", 3, 0, 4, 0, 0, "", ""},
437			{"", 3, 0, 5, 0, 0, "", ""},
438			{"", 3, 0, 6, 0, 0, "", ""},
439			{"", 3, 0, 8, 0, 0, "", ""},
440			{"", 3, 0, 9, 0, 0, "", ""},
441			{"", 3, 0, 11, 0, 0, "", ""},
442			{"", 3, 0, 13, 0, 0, "", ""},
443			{"", 3, 0, 15, 0, 0, "", ""},
444			{"", 3, 0, 16, 0, 0, "", ""},
445			{"", 3, 0, 14, 0, 0, "", ""},
446			{"main", 18, 0, 1, 0, 44, "", ""},
447			{"puts", 16, 0, 0, 0, 0, "", ""},
448		},
449	},
450}
451
452func TestOpen(t *testing.T) {
453	for i := range fileTests {
454		tt := &fileTests[i]
455
456		var f *File
457		var err error
458		if path.Ext(tt.file) == ".gz" {
459			var r io.ReaderAt
460			if r, err = decompress(tt.file); err == nil {
461				f, err = NewFile(r)
462			}
463		} else {
464			f, err = Open(tt.file)
465		}
466		if err != nil {
467			t.Errorf("cannot open file %s: %v", tt.file, err)
468			continue
469		}
470		defer f.Close()
471		if f.FileHeader != tt.hdr {
472			t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr)
473			continue
474		}
475		for i, s := range f.Sections {
476			if i >= len(tt.sections) {
477				break
478			}
479			sh := tt.sections[i]
480			if s.SectionHeader != sh {
481				t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, s.SectionHeader, sh)
482			}
483		}
484		for i, p := range f.Progs {
485			if i >= len(tt.progs) {
486				break
487			}
488			ph := tt.progs[i]
489			if p.ProgHeader != ph {
490				t.Errorf("open %s, program %d:\n\thave %#v\n\twant %#v\n", tt.file, i, p.ProgHeader, ph)
491			}
492		}
493		tn := len(tt.sections)
494		fn := len(f.Sections)
495		if tn != fn {
496			t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn)
497		}
498		tn = len(tt.progs)
499		fn = len(f.Progs)
500		if tn != fn {
501			t.Errorf("open %s: len(Progs) = %d, want %d", tt.file, fn, tn)
502		}
503		tl := tt.needed
504		fl, err := f.ImportedLibraries()
505		if err != nil {
506			t.Error(err)
507		}
508		if !reflect.DeepEqual(tl, fl) {
509			t.Errorf("open %s: DT_NEEDED = %v, want %v", tt.file, tl, fl)
510		}
511		symbols, err := f.Symbols()
512		if tt.symbols == nil {
513			if !errors.Is(err, ErrNoSymbols) {
514				t.Errorf("open %s: Symbols() expected ErrNoSymbols, have nil", tt.file)
515			}
516			if symbols != nil {
517				t.Errorf("open %s: Symbols() expected no symbols, have %v", tt.file, symbols)
518			}
519		} else {
520			if err != nil {
521				t.Errorf("open %s: Symbols() unexpected error %v", tt.file, err)
522			}
523			if !slices.Equal(symbols, tt.symbols) {
524				t.Errorf("open %s: Symbols() = %v, want %v", tt.file, symbols, tt.symbols)
525			}
526		}
527	}
528}
529
530// elf.NewFile requires io.ReaderAt, which compress/gzip cannot
531// provide. Decompress the file to a bytes.Reader.
532func decompress(gz string) (io.ReaderAt, error) {
533	in, err := os.Open(gz)
534	if err != nil {
535		return nil, err
536	}
537	defer in.Close()
538	r, err := gzip.NewReader(in)
539	if err != nil {
540		return nil, err
541	}
542	var out bytes.Buffer
543	_, err = io.Copy(&out, r)
544	return bytes.NewReader(out.Bytes()), err
545}
546
547type relocationTestEntry struct {
548	entryNumber int
549	entry       *dwarf.Entry
550	pcRanges    [][2]uint64
551}
552
553type relocationTest struct {
554	file    string
555	entries []relocationTestEntry
556}
557
558var relocationTests = []relocationTest{
559	{
560		"testdata/go-relocation-test-gcc441-x86-64.obj",
561		[]relocationTestEntry{
562			{
563				entry: &dwarf.Entry{
564					Offset:   0xb,
565					Tag:      dwarf.TagCompileUnit,
566					Children: true,
567					Field: []dwarf.Field{
568						{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1", Class: dwarf.ClassString},
569						{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant},
570						{Attr: dwarf.AttrName, Val: "go-relocation-test.c", Class: dwarf.ClassString},
571						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
572						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
573						{Attr: dwarf.AttrHighpc, Val: uint64(0x6), Class: dwarf.ClassAddress},
574						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
575					},
576				},
577				pcRanges: [][2]uint64{{0x0, 0x6}},
578			},
579		},
580	},
581	{
582		"testdata/go-relocation-test-gcc441-x86.obj",
583		[]relocationTestEntry{
584			{
585				entry: &dwarf.Entry{
586					Offset:   0xb,
587					Tag:      dwarf.TagCompileUnit,
588					Children: true,
589					Field: []dwarf.Field{
590						{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1", Class: dwarf.ClassString},
591						{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant},
592						{Attr: dwarf.AttrName, Val: "t.c", Class: dwarf.ClassString},
593						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
594						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
595						{Attr: dwarf.AttrHighpc, Val: uint64(0x5), Class: dwarf.ClassAddress},
596						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
597					},
598				},
599				pcRanges: [][2]uint64{{0x0, 0x5}},
600			},
601		},
602	},
603	{
604		"testdata/go-relocation-test-gcc424-x86-64.obj",
605		[]relocationTestEntry{
606			{
607				entry: &dwarf.Entry{
608					Offset:   0xb,
609					Tag:      dwarf.TagCompileUnit,
610					Children: true,
611					Field: []dwarf.Field{
612						{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)", Class: dwarf.ClassString},
613						{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant},
614						{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c", Class: dwarf.ClassString},
615						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
616						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
617						{Attr: dwarf.AttrHighpc, Val: uint64(0x6), Class: dwarf.ClassAddress},
618						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
619					},
620				},
621				pcRanges: [][2]uint64{{0x0, 0x6}},
622			},
623		},
624	},
625	{
626		"testdata/go-relocation-test-gcc482-aarch64.obj",
627		[]relocationTestEntry{
628			{
629				entry: &dwarf.Entry{
630					Offset:   0xb,
631					Tag:      dwarf.TagCompileUnit,
632					Children: true,
633					Field: []dwarf.Field{
634						{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -g -fstack-protector", Class: dwarf.ClassString},
635						{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant},
636						{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482.c", Class: dwarf.ClassString},
637						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
638						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
639						{Attr: dwarf.AttrHighpc, Val: int64(0x24), Class: dwarf.ClassConstant},
640						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
641					},
642				},
643				pcRanges: [][2]uint64{{0x0, 0x24}},
644			},
645		},
646	},
647	{
648		"testdata/go-relocation-test-gcc492-arm.obj",
649		[]relocationTestEntry{
650			{
651				entry: &dwarf.Entry{
652					Offset:   0xb,
653					Tag:      dwarf.TagCompileUnit,
654					Children: true,
655					Field: []dwarf.Field{
656						{Attr: dwarf.AttrProducer, Val: "GNU C 4.9.2 20141224 (prerelease) -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -mtls-dialect=gnu -g", Class: dwarf.ClassString},
657						{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant},
658						{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc492.c", Class: dwarf.ClassString},
659						{Attr: dwarf.AttrCompDir, Val: "/root/go/src/debug/elf/testdata", Class: dwarf.ClassString},
660						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
661						{Attr: dwarf.AttrHighpc, Val: int64(0x28), Class: dwarf.ClassConstant},
662						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
663					},
664				},
665				pcRanges: [][2]uint64{{0x0, 0x28}},
666			},
667		},
668	},
669	{
670		"testdata/go-relocation-test-clang-arm.obj",
671		[]relocationTestEntry{
672			{
673				entry: &dwarf.Entry{
674					Offset:   0xb,
675					Tag:      dwarf.TagCompileUnit,
676					Children: true,
677					Field: []dwarf.Field{
678						{Attr: dwarf.AttrProducer, Val: "Debian clang version 3.5.0-10 (tags/RELEASE_350/final) (based on LLVM 3.5.0)", Class: dwarf.ClassString},
679						{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
680						{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
681						{Attr: dwarf.AttrStmtList, Val: int64(0x0), Class: dwarf.ClassLinePtr},
682						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
683						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
684						{Attr: dwarf.AttrHighpc, Val: int64(0x30), Class: dwarf.ClassConstant},
685					},
686				},
687				pcRanges: [][2]uint64{{0x0, 0x30}},
688			},
689		},
690	},
691	{
692		"testdata/go-relocation-test-gcc5-ppc.obj",
693		[]relocationTestEntry{
694			{
695				entry: &dwarf.Entry{
696					Offset:   0xb,
697					Tag:      dwarf.TagCompileUnit,
698					Children: true,
699					Field: []dwarf.Field{
700						{Attr: dwarf.AttrProducer, Val: "GNU C11 5.0.0 20150116 (experimental) -Asystem=linux -Asystem=unix -Asystem=posix -g", Class: dwarf.ClassString},
701						{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
702						{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc5-ppc.c", Class: dwarf.ClassString},
703						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
704						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
705						{Attr: dwarf.AttrHighpc, Val: int64(0x44), Class: dwarf.ClassConstant},
706						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
707					},
708				},
709				pcRanges: [][2]uint64{{0x0, 0x44}},
710			},
711		},
712	},
713	{
714		"testdata/go-relocation-test-gcc482-ppc64le.obj",
715		[]relocationTestEntry{
716			{
717				entry: &dwarf.Entry{
718					Offset:   0xb,
719					Tag:      dwarf.TagCompileUnit,
720					Children: true,
721					Field: []dwarf.Field{
722						{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -Asystem=linux -Asystem=unix -Asystem=posix -msecure-plt -mtune=power8 -mcpu=power7 -gdwarf-2 -fstack-protector", Class: dwarf.ClassString},
723						{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant},
724						{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482-ppc64le.c", Class: dwarf.ClassString},
725						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
726						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
727						{Attr: dwarf.AttrHighpc, Val: uint64(0x24), Class: dwarf.ClassAddress},
728						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
729					},
730				},
731				pcRanges: [][2]uint64{{0x0, 0x24}},
732			},
733		},
734	},
735	{
736		"testdata/go-relocation-test-gcc492-mips64.obj",
737		[]relocationTestEntry{
738			{
739				entry: &dwarf.Entry{
740					Offset:   0xb,
741					Tag:      dwarf.TagCompileUnit,
742					Children: true,
743					Field: []dwarf.Field{
744						{Attr: dwarf.AttrProducer, Val: "GNU C 4.9.2 -meb -mabi=64 -march=mips3 -mtune=mips64 -mllsc -mno-shared -g", Class: dwarf.ClassString},
745						{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant},
746						{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
747						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
748						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
749						{Attr: dwarf.AttrHighpc, Val: int64(0x64), Class: dwarf.ClassConstant},
750						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
751					},
752				},
753				pcRanges: [][2]uint64{{0x0, 0x64}},
754			},
755		},
756	},
757	{
758		"testdata/go-relocation-test-gcc531-s390x.obj",
759		[]relocationTestEntry{
760			{
761				entry: &dwarf.Entry{
762					Offset:   0xb,
763					Tag:      dwarf.TagCompileUnit,
764					Children: true,
765					Field: []dwarf.Field{
766						{Attr: dwarf.AttrProducer, Val: "GNU C11 5.3.1 20160316 -march=zEC12 -m64 -mzarch -g -fstack-protector-strong", Class: dwarf.ClassString},
767						{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
768						{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
769						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
770						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
771						{Attr: dwarf.AttrHighpc, Val: int64(0x3a), Class: dwarf.ClassConstant},
772						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
773					},
774				},
775				pcRanges: [][2]uint64{{0x0, 0x3a}},
776			},
777		},
778	},
779	{
780		"testdata/go-relocation-test-gcc620-sparc64.obj",
781		[]relocationTestEntry{
782			{
783				entry: &dwarf.Entry{
784					Offset:   0xb,
785					Tag:      dwarf.TagCompileUnit,
786					Children: true,
787					Field: []dwarf.Field{
788						{Attr: dwarf.AttrProducer, Val: "GNU C11 6.2.0 20160914 -mcpu=v9 -g -fstack-protector-strong", Class: dwarf.ClassString},
789						{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
790						{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
791						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
792						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
793						{Attr: dwarf.AttrHighpc, Val: int64(0x2c), Class: dwarf.ClassConstant},
794						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
795					},
796				},
797				pcRanges: [][2]uint64{{0x0, 0x2c}},
798			},
799		},
800	},
801	{
802		"testdata/go-relocation-test-gcc492-mipsle.obj",
803		[]relocationTestEntry{
804			{
805				entry: &dwarf.Entry{
806					Offset:   0xb,
807					Tag:      dwarf.TagCompileUnit,
808					Children: true,
809					Field: []dwarf.Field{
810						{Attr: dwarf.AttrProducer, Val: "GNU C 4.9.2 -mel -march=mips2 -mtune=mips32 -mllsc -mno-shared -mabi=32 -g", Class: dwarf.ClassString},
811						{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant},
812						{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
813						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
814						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
815						{Attr: dwarf.AttrHighpc, Val: int64(0x58), Class: dwarf.ClassConstant},
816						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
817					},
818				},
819				pcRanges: [][2]uint64{{0x0, 0x58}},
820			},
821		},
822	},
823	{
824		"testdata/go-relocation-test-gcc540-mips.obj",
825		[]relocationTestEntry{
826			{
827				entry: &dwarf.Entry{
828					Offset:   0xb,
829					Tag:      dwarf.TagCompileUnit,
830					Children: true,
831					Field: []dwarf.Field{
832						{Attr: dwarf.AttrProducer, Val: "GNU C11 5.4.0 20160609 -meb -mips32 -mtune=mips32r2 -mfpxx -mllsc -mno-shared -mabi=32 -g -gdwarf-2", Class: dwarf.ClassString},
833						{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
834						{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
835						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
836						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
837						{Attr: dwarf.AttrHighpc, Val: uint64(0x5c), Class: dwarf.ClassAddress},
838						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
839					},
840				},
841				pcRanges: [][2]uint64{{0x0, 0x5c}},
842			},
843		},
844	},
845	{
846		"testdata/go-relocation-test-gcc493-mips64le.obj",
847		[]relocationTestEntry{
848			{
849				entry: &dwarf.Entry{
850					Offset:   0xb,
851					Tag:      dwarf.TagCompileUnit,
852					Children: true,
853					Field: []dwarf.Field{
854						{Attr: dwarf.AttrProducer, Val: "GNU C 4.9.3 -mel -mabi=64 -mllsc -mno-shared -g -fstack-protector-strong", Class: dwarf.ClassString},
855						{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant},
856						{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
857						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
858						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
859						{Attr: dwarf.AttrHighpc, Val: int64(0x64), Class: dwarf.ClassConstant},
860						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
861					},
862				},
863				pcRanges: [][2]uint64{{0x0, 0x64}},
864			},
865		},
866	},
867	{
868		"testdata/go-relocation-test-gcc720-riscv64.obj",
869		[]relocationTestEntry{
870			{
871				entry: &dwarf.Entry{
872					Offset:   0xb,
873					Tag:      dwarf.TagCompileUnit,
874					Children: true,
875					Field: []dwarf.Field{
876						{Attr: dwarf.AttrProducer, Val: "GNU C11 7.2.0 -march=rv64imafdc -mabi=lp64d -g -gdwarf-2", Class: dwarf.ClassString},
877						{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
878						{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
879						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
880						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
881						{Attr: dwarf.AttrHighpc, Val: uint64(0x2c), Class: dwarf.ClassAddress},
882						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
883					},
884				},
885				pcRanges: [][2]uint64{{0x0, 0x2c}},
886			},
887		},
888	},
889	{
890		"testdata/go-relocation-test-clang-x86.obj",
891		[]relocationTestEntry{
892			{
893				entry: &dwarf.Entry{
894					Offset:   0xb,
895					Tag:      dwarf.TagCompileUnit,
896					Children: true,
897					Field: []dwarf.Field{
898						{Attr: dwarf.AttrProducer, Val: "clang version google3-trunk (trunk r209387)", Class: dwarf.ClassString},
899						{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
900						{Attr: dwarf.AttrName, Val: "go-relocation-test-clang.c", Class: dwarf.ClassString},
901						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
902						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
903					},
904				},
905			},
906		},
907	},
908	{
909		"testdata/gcc-amd64-openbsd-debug-with-rela.obj",
910		[]relocationTestEntry{
911			{
912				entryNumber: 203,
913				entry: &dwarf.Entry{
914					Offset:   0xc62,
915					Tag:      dwarf.TagMember,
916					Children: false,
917					Field: []dwarf.Field{
918						{Attr: dwarf.AttrName, Val: "it_interval", Class: dwarf.ClassString},
919						{Attr: dwarf.AttrDeclFile, Val: int64(7), Class: dwarf.ClassConstant},
920						{Attr: dwarf.AttrDeclLine, Val: int64(236), Class: dwarf.ClassConstant},
921						{Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f), Class: dwarf.ClassReference},
922						{Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x0}, Class: dwarf.ClassExprLoc},
923					},
924				},
925			},
926			{
927				entryNumber: 204,
928				entry: &dwarf.Entry{
929					Offset:   0xc70,
930					Tag:      dwarf.TagMember,
931					Children: false,
932					Field: []dwarf.Field{
933						{Attr: dwarf.AttrName, Val: "it_value", Class: dwarf.ClassString},
934						{Attr: dwarf.AttrDeclFile, Val: int64(7), Class: dwarf.ClassConstant},
935						{Attr: dwarf.AttrDeclLine, Val: int64(237), Class: dwarf.ClassConstant},
936						{Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f), Class: dwarf.ClassReference},
937						{Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x10}, Class: dwarf.ClassExprLoc},
938					},
939				},
940			},
941		},
942	},
943	{
944		"testdata/go-relocation-test-gcc930-ranges-no-rela-x86-64",
945		[]relocationTestEntry{
946			{
947				entry: &dwarf.Entry{
948					Offset:   0xb,
949					Tag:      dwarf.TagCompileUnit,
950					Children: true,
951					Field: []dwarf.Field{
952						{Attr: dwarf.AttrProducer, Val: "GNU C17 9.3.0 -mtune=generic -march=x86-64 -g -fno-asynchronous-unwind-tables", Class: dwarf.ClassString},
953						{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
954						{Attr: dwarf.AttrName, Val: "multiple-code-sections.c", Class: dwarf.ClassString},
955						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
956						{Attr: dwarf.AttrRanges, Val: int64(0), Class: dwarf.ClassRangeListPtr},
957						{Attr: dwarf.AttrLowpc, Val: uint64(0), Class: dwarf.ClassAddress},
958						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
959					},
960				},
961				pcRanges: [][2]uint64{
962					{0x765, 0x777},
963					{0x7e1, 0x7ec},
964				},
965			},
966		},
967	},
968	{
969		"testdata/go-relocation-test-gcc930-ranges-with-rela-x86-64",
970		[]relocationTestEntry{
971			{
972				entry: &dwarf.Entry{
973					Offset:   0xb,
974					Tag:      dwarf.TagCompileUnit,
975					Children: true,
976					Field: []dwarf.Field{
977						{Attr: dwarf.AttrProducer, Val: "GNU C17 9.3.0 -mtune=generic -march=x86-64 -g -fno-asynchronous-unwind-tables", Class: dwarf.ClassString},
978						{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
979						{Attr: dwarf.AttrName, Val: "multiple-code-sections.c", Class: dwarf.ClassString},
980						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
981						{Attr: dwarf.AttrRanges, Val: int64(0), Class: dwarf.ClassRangeListPtr},
982						{Attr: dwarf.AttrLowpc, Val: uint64(0), Class: dwarf.ClassAddress},
983						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
984					},
985				},
986				pcRanges: [][2]uint64{
987					{0x765, 0x777},
988					{0x7e1, 0x7ec},
989				},
990			},
991		},
992	},
993}
994
995func TestDWARFRelocations(t *testing.T) {
996	for _, test := range relocationTests {
997		test := test
998		t.Run(test.file, func(t *testing.T) {
999			t.Parallel()
1000			f, err := Open(test.file)
1001			if err != nil {
1002				t.Fatal(err)
1003			}
1004			dwarf, err := f.DWARF()
1005			if err != nil {
1006				t.Fatal(err)
1007			}
1008			reader := dwarf.Reader()
1009			idx := 0
1010			for _, testEntry := range test.entries {
1011				if testEntry.entryNumber < idx {
1012					t.Fatalf("internal test error: %d < %d", testEntry.entryNumber, idx)
1013				}
1014				for ; idx < testEntry.entryNumber; idx++ {
1015					entry, err := reader.Next()
1016					if entry == nil || err != nil {
1017						t.Fatalf("Failed to skip to entry %d: %v", testEntry.entryNumber, err)
1018					}
1019				}
1020				entry, err := reader.Next()
1021				idx++
1022				if err != nil {
1023					t.Fatal(err)
1024				}
1025				if !reflect.DeepEqual(testEntry.entry, entry) {
1026					t.Errorf("entry %d mismatch: got:%#v want:%#v", testEntry.entryNumber, entry, testEntry.entry)
1027				}
1028				pcRanges, err := dwarf.Ranges(entry)
1029				if err != nil {
1030					t.Fatal(err)
1031				}
1032				if !reflect.DeepEqual(testEntry.pcRanges, pcRanges) {
1033					t.Errorf("entry %d: PC range mismatch: got:%#v want:%#v", testEntry.entryNumber, pcRanges, testEntry.pcRanges)
1034				}
1035			}
1036		})
1037	}
1038}
1039
1040func TestCompressedDWARF(t *testing.T) {
1041	// Test file built with GCC 4.8.4 and as 2.24 using:
1042	// gcc -Wa,--compress-debug-sections -g -c -o zdebug-test-gcc484-x86-64.obj hello.c
1043	f, err := Open("testdata/zdebug-test-gcc484-x86-64.obj")
1044	if err != nil {
1045		t.Fatal(err)
1046	}
1047	dwarf, err := f.DWARF()
1048	if err != nil {
1049		t.Fatal(err)
1050	}
1051	reader := dwarf.Reader()
1052	n := 0
1053	for {
1054		entry, err := reader.Next()
1055		if err != nil {
1056			t.Fatal(err)
1057		}
1058		if entry == nil {
1059			break
1060		}
1061		n++
1062	}
1063	if n != 18 {
1064		t.Fatalf("want %d DWARF entries, got %d", 18, n)
1065	}
1066}
1067
1068func TestCompressedSection(t *testing.T) {
1069	// Test files built with gcc -g -S hello.c and assembled with
1070	// --compress-debug-sections=zlib-gabi.
1071	f, err := Open("testdata/compressed-64.obj")
1072	if err != nil {
1073		t.Fatal(err)
1074	}
1075	sec := f.Section(".debug_info")
1076	wantData := []byte{
1077		182, 0, 0, 0, 4, 0, 0, 0, 0, 0, 8, 1, 0, 0, 0, 0,
1078		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1079		0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 7,
1080		0, 0, 0, 0, 2, 1, 8, 0, 0, 0, 0, 2, 2, 7, 0, 0,
1081		0, 0, 2, 4, 7, 0, 0, 0, 0, 2, 1, 6, 0, 0, 0, 0,
1082		2, 2, 5, 0, 0, 0, 0, 3, 4, 5, 105, 110, 116, 0, 2, 8,
1083		5, 0, 0, 0, 0, 2, 8, 7, 0, 0, 0, 0, 4, 8, 114, 0,
1084		0, 0, 2, 1, 6, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 4,
1085		0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0,
1086		1, 156, 179, 0, 0, 0, 6, 0, 0, 0, 0, 1, 4, 87, 0, 0,
1087		0, 2, 145, 108, 6, 0, 0, 0, 0, 1, 4, 179, 0, 0, 0, 2,
1088		145, 96, 0, 4, 8, 108, 0, 0, 0, 0,
1089	}
1090
1091	// Test Data method.
1092	b, err := sec.Data()
1093	if err != nil {
1094		t.Fatal(err)
1095	}
1096	if !bytes.Equal(wantData, b) {
1097		t.Fatalf("want data %x, got %x", wantData, b)
1098	}
1099
1100	// Test Open method and seeking.
1101	buf, have, count := make([]byte, len(b)), make([]bool, len(b)), 0
1102	sf := sec.Open()
1103	if got, err := sf.Seek(0, io.SeekEnd); got != int64(len(b)) || err != nil {
1104		t.Fatalf("want seek end %d, got %d error %v", len(b), got, err)
1105	}
1106	if n, err := sf.Read(buf); n != 0 || err != io.EOF {
1107		t.Fatalf("want EOF with 0 bytes, got %v with %d bytes", err, n)
1108	}
1109	pos := int64(len(buf))
1110	for count < len(buf) {
1111		// Construct random seek arguments.
1112		whence := rand.Intn(3)
1113		target := rand.Int63n(int64(len(buf)))
1114		var offset int64
1115		switch whence {
1116		case io.SeekStart:
1117			offset = target
1118		case io.SeekCurrent:
1119			offset = target - pos
1120		case io.SeekEnd:
1121			offset = target - int64(len(buf))
1122		}
1123		pos, err = sf.Seek(offset, whence)
1124		if err != nil {
1125			t.Fatal(err)
1126		}
1127		if pos != target {
1128			t.Fatalf("want position %d, got %d", target, pos)
1129		}
1130
1131		// Read data from the new position.
1132		end := pos + 16
1133		if end > int64(len(buf)) {
1134			end = int64(len(buf))
1135		}
1136		n, err := io.ReadFull(sf, buf[pos:end])
1137		if err != nil {
1138			t.Fatal(err)
1139		}
1140		for i := 0; i < n; i++ {
1141			if !have[pos] {
1142				have[pos] = true
1143				count++
1144			}
1145			pos++
1146		}
1147	}
1148	if !bytes.Equal(wantData, buf) {
1149		t.Fatalf("want data %x, got %x", wantData, buf)
1150	}
1151}
1152
1153func TestNoSectionOverlaps(t *testing.T) {
1154	// Ensure cmd/link outputs sections without overlaps.
1155	switch runtime.GOOS {
1156	case "aix", "android", "darwin", "ios", "js", "plan9", "windows", "wasip1":
1157		t.Skipf("cmd/link doesn't produce ELF binaries on %s", runtime.GOOS)
1158	}
1159	_ = net.ResolveIPAddr // force dynamic linkage
1160	f, err := Open(os.Args[0])
1161	if err != nil {
1162		t.Error(err)
1163		return
1164	}
1165	for i, si := range f.Sections {
1166		sih := si.SectionHeader
1167		if sih.Type == SHT_NOBITS {
1168			continue
1169		}
1170		// checking for overlap in file
1171		for j, sj := range f.Sections {
1172			sjh := sj.SectionHeader
1173			if i == j || sjh.Type == SHT_NOBITS || sih.Offset == sjh.Offset && sih.FileSize == 0 {
1174				continue
1175			}
1176			if sih.Offset >= sjh.Offset && sih.Offset < sjh.Offset+sjh.FileSize {
1177				t.Errorf("ld produced ELF with section offset %s within %s: 0x%x <= 0x%x..0x%x < 0x%x",
1178					sih.Name, sjh.Name, sjh.Offset, sih.Offset, sih.Offset+sih.FileSize, sjh.Offset+sjh.FileSize)
1179			}
1180		}
1181
1182		if sih.Flags&SHF_ALLOC == 0 {
1183			continue
1184		}
1185
1186		// checking for overlap in address space
1187		for j, sj := range f.Sections {
1188			sjh := sj.SectionHeader
1189			if i == j || sjh.Flags&SHF_ALLOC == 0 || sjh.Type == SHT_NOBITS ||
1190				sih.Addr == sjh.Addr && sih.Size == 0 {
1191				continue
1192			}
1193			if sih.Addr >= sjh.Addr && sih.Addr < sjh.Addr+sjh.Size {
1194				t.Errorf("ld produced ELF with section address %s within %s: 0x%x <= 0x%x..0x%x < 0x%x",
1195					sih.Name, sjh.Name, sjh.Addr, sih.Addr, sih.Addr+sih.Size, sjh.Addr+sjh.Size)
1196			}
1197		}
1198	}
1199}
1200
1201func TestNobitsSection(t *testing.T) {
1202	const testdata = "testdata/gcc-amd64-linux-exec"
1203	f, err := Open(testdata)
1204	if err != nil {
1205		t.Fatalf("could not read %s: %v", testdata, err)
1206	}
1207	defer f.Close()
1208
1209	wantError := "unexpected read from SHT_NOBITS section"
1210	bss := f.Section(".bss")
1211
1212	_, err = bss.Data()
1213	if err == nil || err.Error() != wantError {
1214		t.Fatalf("bss.Data() got error %q, want error %q", err, wantError)
1215	}
1216
1217	r := bss.Open()
1218	p := make([]byte, 1)
1219	_, err = r.Read(p)
1220	if err == nil || err.Error() != wantError {
1221		t.Fatalf("r.Read(p) got error %q, want error %q", err, wantError)
1222	}
1223}
1224
1225// TestLargeNumberOfSections tests the case that a file has greater than or
1226// equal to 65280 (0xff00) sections.
1227func TestLargeNumberOfSections(t *testing.T) {
1228	// A file with >= 0xff00 sections is too big, so we will construct it on the
1229	// fly. The original file "y.o" is generated by these commands:
1230	// 1. generate "y.c":
1231	//   for i in `seq 1 65288`; do
1232	//     printf -v x "%04x" i;
1233	//     echo "int var_$x __attribute__((section(\"section_$x\"))) = $i;"
1234	//   done > y.c
1235	// 2. compile: gcc -c y.c -m32
1236	//
1237	// $readelf -h y.o
1238	// ELF Header:
1239	//   Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
1240	//   Class:                             ELF32
1241	//   Data:                              2's complement, little endian
1242	//   Version:                           1 (current)
1243	//   OS/ABI:                            UNIX - System V
1244	//   ABI Version:                       0
1245	//   Type:                              REL (Relocatable file)
1246	//   Machine:                           Intel 80386
1247	//   Version:                           0x1
1248	//   Entry point address:               0x0
1249	//   Start of program headers:          0 (bytes into file)
1250	//   Start of section headers:          3003468 (bytes into file)
1251	//   Flags:                             0x0
1252	//   Size of this header:               52 (bytes)
1253	//   Size of program headers:           0 (bytes)
1254	//   Number of program headers:         0
1255	//   Size of section headers:           40 (bytes)
1256	//   Number of section headers:         0 (65298)
1257	//   Section header string table index: 65535 (65297)
1258	//
1259	// $readelf -S y.o
1260	// There are 65298 section headers, starting at offset 0x2dd44c:
1261	// Section Headers:
1262	//   [Nr]    Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
1263	//   [    0]                   NULL            00000000 000000 00ff12 00     65297   0  0
1264	//   [    1] .text             PROGBITS        00000000 000034 000000 00  AX  0   0  1
1265	//   [    2] .data             PROGBITS        00000000 000034 000000 00  WA  0   0  1
1266	//   [    3] .bss              NOBITS          00000000 000034 000000 00  WA  0   0  1
1267	//   [    4] section_0001      PROGBITS        00000000 000034 000004 00  WA  0   0  4
1268	//   [    5] section_0002      PROGBITS        00000000 000038 000004 00  WA  0   0  4
1269	//   [ section_0003 ~ section_ff06 truncated ]
1270	//   [65290] section_ff07      PROGBITS        00000000 03fc4c 000004 00  WA  0   0  4
1271	//   [65291] section_ff08      PROGBITS        00000000 03fc50 000004 00  WA  0   0  4
1272	//   [65292] .comment          PROGBITS        00000000 03fc54 000027 01  MS  0   0  1
1273	//   [65293] .note.GNU-stack   PROGBITS        00000000 03fc7b 000000 00      0   0  1
1274	//   [65294] .symtab           SYMTAB          00000000 03fc7c 0ff0a0 10     65296   2  4
1275	//   [65295] .symtab_shndx     SYMTAB SECTION  00000000 13ed1c 03fc28 04     65294   0  4
1276	//   [65296] .strtab           STRTAB          00000000 17e944 08f74d 00      0   0  1
1277	//   [65297] .shstrtab         STRTAB          00000000 20e091 0cf3bb 00      0   0  1
1278
1279	var buf bytes.Buffer
1280
1281	{
1282		buf.Grow(0x55AF1C) // 3003468 + 40 * 65298
1283
1284		h := Header32{
1285			Ident:     [16]byte{0x7F, 'E', 'L', 'F', 0x01, 0x01, 0x01},
1286			Type:      1,
1287			Machine:   3,
1288			Version:   1,
1289			Shoff:     0x2DD44C,
1290			Ehsize:    0x34,
1291			Shentsize: 0x28,
1292			Shnum:     0,
1293			Shstrndx:  0xFFFF,
1294		}
1295		binary.Write(&buf, binary.LittleEndian, h)
1296
1297		// Zero out sections [1]~[65294].
1298		buf.Write(bytes.Repeat([]byte{0}, 0x13ED1C-binary.Size(h)))
1299
1300		// Write section [65295]. Section [65295] are all zeros except for the
1301		// last 48 bytes.
1302		buf.Write(bytes.Repeat([]byte{0}, 0x03FC28-12*4))
1303		for i := 0; i < 12; i++ {
1304			binary.Write(&buf, binary.LittleEndian, uint32(0xFF00|i))
1305		}
1306
1307		// Write section [65296].
1308		buf.Write([]byte{0})
1309		buf.Write([]byte("y.c\x00"))
1310		for i := 1; i <= 65288; i++ {
1311			// var_0001 ~ var_ff08
1312			name := fmt.Sprintf("var_%04x", i)
1313			buf.Write([]byte(name))
1314			buf.Write([]byte{0})
1315		}
1316
1317		// Write section [65297].
1318		buf.Write([]byte{0})
1319		buf.Write([]byte(".symtab\x00"))
1320		buf.Write([]byte(".strtab\x00"))
1321		buf.Write([]byte(".shstrtab\x00"))
1322		buf.Write([]byte(".text\x00"))
1323		buf.Write([]byte(".data\x00"))
1324		buf.Write([]byte(".bss\x00"))
1325		for i := 1; i <= 65288; i++ {
1326			// s_0001 ~ s_ff08
1327			name := fmt.Sprintf("section_%04x", i)
1328			buf.Write([]byte(name))
1329			buf.Write([]byte{0})
1330		}
1331		buf.Write([]byte(".comment\x00"))
1332		buf.Write([]byte(".note.GNU-stack\x00"))
1333		buf.Write([]byte(".symtab_shndx\x00"))
1334
1335		// Write section header table.
1336		// NULL
1337		binary.Write(&buf, binary.LittleEndian, Section32{Name: 0, Size: 0xFF12, Link: 0xFF11})
1338		// .text
1339		binary.Write(&buf, binary.LittleEndian, Section32{
1340			Name:      0x1B,
1341			Type:      uint32(SHT_PROGBITS),
1342			Flags:     uint32(SHF_ALLOC | SHF_EXECINSTR),
1343			Off:       0x34,
1344			Addralign: 0x01,
1345		})
1346		// .data
1347		binary.Write(&buf, binary.LittleEndian, Section32{
1348			Name:      0x21,
1349			Type:      uint32(SHT_PROGBITS),
1350			Flags:     uint32(SHF_WRITE | SHF_ALLOC),
1351			Off:       0x34,
1352			Addralign: 0x01,
1353		})
1354		// .bss
1355		binary.Write(&buf, binary.LittleEndian, Section32{
1356			Name:      0x27,
1357			Type:      uint32(SHT_NOBITS),
1358			Flags:     uint32(SHF_WRITE | SHF_ALLOC),
1359			Off:       0x34,
1360			Addralign: 0x01,
1361		})
1362		// s_1 ~ s_65537
1363		for i := 0; i < 65288; i++ {
1364			s := Section32{
1365				Name:      uint32(0x2C + i*13),
1366				Type:      uint32(SHT_PROGBITS),
1367				Flags:     uint32(SHF_WRITE | SHF_ALLOC),
1368				Off:       uint32(0x34 + i*4),
1369				Size:      0x04,
1370				Addralign: 0x04,
1371			}
1372			binary.Write(&buf, binary.LittleEndian, s)
1373		}
1374		// .comment
1375		binary.Write(&buf, binary.LittleEndian, Section32{
1376			Name:      0x0CF394,
1377			Type:      uint32(SHT_PROGBITS),
1378			Flags:     uint32(SHF_MERGE | SHF_STRINGS),
1379			Off:       0x03FC54,
1380			Size:      0x27,
1381			Addralign: 0x01,
1382			Entsize:   0x01,
1383		})
1384		// .note.GNU-stack
1385		binary.Write(&buf, binary.LittleEndian, Section32{
1386			Name:      0x0CF39D,
1387			Type:      uint32(SHT_PROGBITS),
1388			Off:       0x03FC7B,
1389			Addralign: 0x01,
1390		})
1391		// .symtab
1392		binary.Write(&buf, binary.LittleEndian, Section32{
1393			Name:      0x01,
1394			Type:      uint32(SHT_SYMTAB),
1395			Off:       0x03FC7C,
1396			Size:      0x0FF0A0,
1397			Link:      0xFF10,
1398			Info:      0x02,
1399			Addralign: 0x04,
1400			Entsize:   0x10,
1401		})
1402		// .symtab_shndx
1403		binary.Write(&buf, binary.LittleEndian, Section32{
1404			Name:      0x0CF3AD,
1405			Type:      uint32(SHT_SYMTAB_SHNDX),
1406			Off:       0x13ED1C,
1407			Size:      0x03FC28,
1408			Link:      0xFF0E,
1409			Addralign: 0x04,
1410			Entsize:   0x04,
1411		})
1412		// .strtab
1413		binary.Write(&buf, binary.LittleEndian, Section32{
1414			Name:      0x09,
1415			Type:      uint32(SHT_STRTAB),
1416			Off:       0x17E944,
1417			Size:      0x08F74D,
1418			Addralign: 0x01,
1419		})
1420		// .shstrtab
1421		binary.Write(&buf, binary.LittleEndian, Section32{
1422			Name:      0x11,
1423			Type:      uint32(SHT_STRTAB),
1424			Off:       0x20E091,
1425			Size:      0x0CF3BB,
1426			Addralign: 0x01,
1427		})
1428	}
1429
1430	data := buf.Bytes()
1431
1432	f, err := NewFile(bytes.NewReader(data))
1433	if err != nil {
1434		t.Errorf("cannot create file from data: %v", err)
1435	}
1436	defer f.Close()
1437
1438	wantFileHeader := FileHeader{
1439		Class:     ELFCLASS32,
1440		Data:      ELFDATA2LSB,
1441		Version:   EV_CURRENT,
1442		OSABI:     ELFOSABI_NONE,
1443		ByteOrder: binary.LittleEndian,
1444		Type:      ET_REL,
1445		Machine:   EM_386,
1446	}
1447	if f.FileHeader != wantFileHeader {
1448		t.Errorf("\nhave %#v\nwant %#v\n", f.FileHeader, wantFileHeader)
1449	}
1450
1451	wantSectionNum := 65298
1452	if len(f.Sections) != wantSectionNum {
1453		t.Errorf("len(Sections) = %d, want %d", len(f.Sections), wantSectionNum)
1454	}
1455
1456	wantSectionHeader := SectionHeader{
1457		Name:      "section_0007",
1458		Type:      SHT_PROGBITS,
1459		Flags:     SHF_WRITE + SHF_ALLOC,
1460		Offset:    0x4c,
1461		Size:      0x4,
1462		Addralign: 0x4,
1463		FileSize:  0x4,
1464	}
1465	if f.Sections[10].SectionHeader != wantSectionHeader {
1466		t.Errorf("\nhave %#v\nwant %#v\n", f.Sections[10].SectionHeader, wantSectionHeader)
1467	}
1468}
1469
1470func TestIssue10996(t *testing.T) {
1471	data := []byte("\u007fELF\x02\x01\x010000000000000" +
1472		"\x010000000000000000000" +
1473		"\x00\x00\x00\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00" +
1474		"0000")
1475	_, err := NewFile(bytes.NewReader(data))
1476	if err == nil {
1477		t.Fatalf("opening invalid ELF file unexpectedly succeeded")
1478	}
1479}
1480
1481func TestDynValue(t *testing.T) {
1482	const testdata = "testdata/gcc-amd64-linux-exec"
1483	f, err := Open(testdata)
1484	if err != nil {
1485		t.Fatalf("could not read %s: %v", testdata, err)
1486	}
1487	defer f.Close()
1488
1489	vals, err := f.DynValue(DT_VERNEEDNUM)
1490	if err != nil {
1491		t.Fatalf("DynValue(DT_VERNEEDNUM): got unexpected error %v", err)
1492	}
1493
1494	if len(vals) != 1 || vals[0] != 1 {
1495		t.Errorf("DynValue(DT_VERNEEDNUM): got %v, want [1]", vals)
1496	}
1497}
1498
1499func TestIssue59208(t *testing.T) {
1500	// corrupted dwarf data should raise invalid dwarf data instead of invalid zlib
1501	const orig = "testdata/compressed-64.obj"
1502	f, err := Open(orig)
1503	if err != nil {
1504		t.Fatal(err)
1505	}
1506	sec := f.Section(".debug_info")
1507
1508	data, err := os.ReadFile(orig)
1509	if err != nil {
1510		t.Fatal(err)
1511	}
1512
1513	dn := make([]byte, len(data))
1514	zoffset := sec.Offset + uint64(sec.compressionOffset)
1515	copy(dn, data[:zoffset])
1516
1517	ozd, err := sec.Data()
1518	if err != nil {
1519		t.Fatal(err)
1520	}
1521	buf := bytes.NewBuffer(nil)
1522	wr := zlib.NewWriter(buf)
1523	// corrupt origin data same as COMPRESS_ZLIB
1524	copy(ozd, []byte{1, 0, 0, 0})
1525	wr.Write(ozd)
1526	wr.Close()
1527
1528	copy(dn[zoffset:], buf.Bytes())
1529	copy(dn[sec.Offset+sec.FileSize:], data[sec.Offset+sec.FileSize:])
1530
1531	nf, err := NewFile(bytes.NewReader(dn))
1532	if err != nil {
1533		t.Error(err)
1534	}
1535
1536	const want = "decoding dwarf section info"
1537	_, err = nf.DWARF()
1538	if err == nil || !strings.Contains(err.Error(), want) {
1539		t.Errorf("DWARF = %v; want %q", err, want)
1540	}
1541}
1542
1543func BenchmarkSymbols64(b *testing.B) {
1544	const testdata = "testdata/gcc-amd64-linux-exec"
1545	f, err := Open(testdata)
1546	if err != nil {
1547		b.Fatalf("could not read %s: %v", testdata, err)
1548	}
1549	defer f.Close()
1550	b.ResetTimer()
1551	for i := 0; i < b.N; i++ {
1552		symbols, err := f.Symbols()
1553		if err != nil {
1554			b.Fatalf("Symbols(): got unexpected error %v", err)
1555		}
1556		if len(symbols) != 73 {
1557			b.Errorf("\nhave %d symbols\nwant %d symbols\n", len(symbols), 73)
1558		}
1559	}
1560}
1561
1562func BenchmarkSymbols32(b *testing.B) {
1563	const testdata = "testdata/gcc-386-freebsd-exec"
1564	f, err := Open(testdata)
1565	if err != nil {
1566		b.Fatalf("could not read %s: %v", testdata, err)
1567	}
1568	defer f.Close()
1569	b.ResetTimer()
1570	for i := 0; i < b.N; i++ {
1571		symbols, err := f.Symbols()
1572		if err != nil {
1573			b.Fatalf("Symbols(): got unexpected error %v", err)
1574		}
1575		if len(symbols) != 74 {
1576			b.Errorf("\nhave %d symbols\nwant %d symbols\n", len(symbols), 74)
1577		}
1578	}
1579}
1580