Source file
src/debug/macho/file_test.go
1
2
3
4
5 package macho
6
7 import (
8 "bytes"
9 "internal/obscuretestdata"
10 "io"
11 "reflect"
12 "slices"
13 "testing"
14 )
15
16 type fileTest struct {
17 file string
18 hdr FileHeader
19 loads []any
20 sections []*SectionHeader
21 relocations map[string][]Reloc
22 importedSyms []string
23 }
24
25 var fileTests = []fileTest{
26 {
27 "testdata/gcc-386-darwin-exec.base64",
28 FileHeader{0xfeedface, Cpu386, 0x3, 0x2, 0xc, 0x3c0, 0x85},
29 []any{
30 &SegmentHeader{LoadCmdSegment, 0x38, "__PAGEZERO", 0x0, 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
31 &SegmentHeader{LoadCmdSegment, 0xc0, "__TEXT", 0x1000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x2, 0x0},
32 &SegmentHeader{LoadCmdSegment, 0xc0, "__DATA", 0x2000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x2, 0x0},
33 &SegmentHeader{LoadCmdSegment, 0x7c, "__IMPORT", 0x3000, 0x1000, 0x2000, 0x1000, 0x7, 0x7, 0x1, 0x0},
34 &SegmentHeader{LoadCmdSegment, 0x38, "__LINKEDIT", 0x4000, 0x1000, 0x3000, 0x12c, 0x7, 0x1, 0x0, 0x0},
35 nil,
36 nil,
37 nil,
38 nil,
39 nil,
40 &Dylib{nil, "/usr/lib/libgcc_s.1.dylib", 0x2, 0x10000, 0x10000},
41 &Dylib{nil, "/usr/lib/libSystem.B.dylib", 0x2, 0x6f0104, 0x10000},
42 },
43 []*SectionHeader{
44 {"__text", "__TEXT", 0x1f68, 0x88, 0xf68, 0x2, 0x0, 0x0, 0x80000400},
45 {"__cstring", "__TEXT", 0x1ff0, 0xd, 0xff0, 0x0, 0x0, 0x0, 0x2},
46 {"__data", "__DATA", 0x2000, 0x14, 0x1000, 0x2, 0x0, 0x0, 0x0},
47 {"__dyld", "__DATA", 0x2014, 0x1c, 0x1014, 0x2, 0x0, 0x0, 0x0},
48 {"__jump_table", "__IMPORT", 0x3000, 0xa, 0x2000, 0x6, 0x0, 0x0, 0x4000008},
49 },
50 nil,
51 nil,
52 },
53 {
54 "testdata/gcc-amd64-darwin-exec.base64",
55 FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0x2, 0xb, 0x568, 0x85},
56 []any{
57 &SegmentHeader{LoadCmdSegment64, 0x48, "__PAGEZERO", 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
58 &SegmentHeader{LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x5, 0x0},
59 &SegmentHeader{LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x3, 0x0},
60 &SegmentHeader{LoadCmdSegment64, 0x48, "__LINKEDIT", 0x100002000, 0x1000, 0x2000, 0x140, 0x7, 0x1, 0x0, 0x0},
61 nil,
62 nil,
63 nil,
64 nil,
65 nil,
66 &Dylib{nil, "/usr/lib/libgcc_s.1.dylib", 0x2, 0x10000, 0x10000},
67 &Dylib{nil, "/usr/lib/libSystem.B.dylib", 0x2, 0x6f0104, 0x10000},
68 },
69 []*SectionHeader{
70 {"__text", "__TEXT", 0x100000f14, 0x6d, 0xf14, 0x2, 0x0, 0x0, 0x80000400},
71 {"__symbol_stub1", "__TEXT", 0x100000f81, 0xc, 0xf81, 0x0, 0x0, 0x0, 0x80000408},
72 {"__stub_helper", "__TEXT", 0x100000f90, 0x18, 0xf90, 0x2, 0x0, 0x0, 0x0},
73 {"__cstring", "__TEXT", 0x100000fa8, 0xd, 0xfa8, 0x0, 0x0, 0x0, 0x2},
74 {"__eh_frame", "__TEXT", 0x100000fb8, 0x48, 0xfb8, 0x3, 0x0, 0x0, 0x6000000b},
75 {"__data", "__DATA", 0x100001000, 0x1c, 0x1000, 0x3, 0x0, 0x0, 0x0},
76 {"__dyld", "__DATA", 0x100001020, 0x38, 0x1020, 0x3, 0x0, 0x0, 0x0},
77 {"__la_symbol_ptr", "__DATA", 0x100001058, 0x10, 0x1058, 0x2, 0x0, 0x0, 0x7},
78 },
79 nil,
80 nil,
81 },
82 {
83 "testdata/gcc-amd64-darwin-exec-debug.base64",
84 FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0xa, 0x4, 0x5a0, 0},
85 []any{
86 nil,
87 &SegmentHeader{LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x0, 0x7, 0x5, 0x5, 0x0},
88 &SegmentHeader{LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x0, 0x0, 0x7, 0x3, 0x3, 0x0},
89 &SegmentHeader{LoadCmdSegment64, 0x278, "__DWARF", 0x100002000, 0x1000, 0x1000, 0x1bc, 0x7, 0x3, 0x7, 0x0},
90 },
91 []*SectionHeader{
92 {"__text", "__TEXT", 0x100000f14, 0x0, 0x0, 0x2, 0x0, 0x0, 0x80000400},
93 {"__symbol_stub1", "__TEXT", 0x100000f81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000408},
94 {"__stub_helper", "__TEXT", 0x100000f90, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0},
95 {"__cstring", "__TEXT", 0x100000fa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
96 {"__eh_frame", "__TEXT", 0x100000fb8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x6000000b},
97 {"__data", "__DATA", 0x100001000, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0},
98 {"__dyld", "__DATA", 0x100001020, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0},
99 {"__la_symbol_ptr", "__DATA", 0x100001058, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7},
100 {"__debug_abbrev", "__DWARF", 0x100002000, 0x36, 0x1000, 0x0, 0x0, 0x0, 0x0},
101 {"__debug_aranges", "__DWARF", 0x100002036, 0x30, 0x1036, 0x0, 0x0, 0x0, 0x0},
102 {"__debug_frame", "__DWARF", 0x100002066, 0x40, 0x1066, 0x0, 0x0, 0x0, 0x0},
103 {"__debug_info", "__DWARF", 0x1000020a6, 0x54, 0x10a6, 0x0, 0x0, 0x0, 0x0},
104 {"__debug_line", "__DWARF", 0x1000020fa, 0x47, 0x10fa, 0x0, 0x0, 0x0, 0x0},
105 {"__debug_pubnames", "__DWARF", 0x100002141, 0x1b, 0x1141, 0x0, 0x0, 0x0, 0x0},
106 {"__debug_str", "__DWARF", 0x10000215c, 0x60, 0x115c, 0x0, 0x0, 0x0, 0x0},
107 },
108 nil,
109 nil,
110 },
111 {
112 "testdata/clang-386-darwin-exec-with-rpath.base64",
113 FileHeader{0xfeedface, Cpu386, 0x3, 0x2, 0x10, 0x42c, 0x1200085},
114 []any{
115 nil,
116 nil,
117 nil,
118 nil,
119 nil,
120 nil,
121 nil,
122 nil,
123 nil,
124 nil,
125 nil,
126 nil,
127 nil,
128 &Rpath{nil, "/my/rpath"},
129 nil,
130 nil,
131 },
132 nil,
133 nil,
134 nil,
135 },
136 {
137 "testdata/clang-amd64-darwin-exec-with-rpath.base64",
138 FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0x2, 0x10, 0x4c8, 0x200085},
139 []any{
140 nil,
141 nil,
142 nil,
143 nil,
144 nil,
145 nil,
146 nil,
147 nil,
148 nil,
149 nil,
150 nil,
151 nil,
152 nil,
153 &Rpath{nil, "/my/rpath"},
154 nil,
155 nil,
156 },
157 nil,
158 nil,
159 nil,
160 },
161 {
162 "testdata/clang-386-darwin.obj.base64",
163 FileHeader{0xfeedface, Cpu386, 0x3, 0x1, 0x4, 0x138, 0x2000},
164 nil,
165 nil,
166 map[string][]Reloc{
167 "__text": {
168 {
169 Addr: 0x1d,
170 Type: uint8(GENERIC_RELOC_VANILLA),
171 Len: 2,
172 Pcrel: true,
173 Extern: true,
174 Value: 1,
175 Scattered: false,
176 },
177 {
178 Addr: 0xe,
179 Type: uint8(GENERIC_RELOC_LOCAL_SECTDIFF),
180 Len: 2,
181 Pcrel: false,
182 Value: 0x2d,
183 Scattered: true,
184 },
185 {
186 Addr: 0x0,
187 Type: uint8(GENERIC_RELOC_PAIR),
188 Len: 2,
189 Pcrel: false,
190 Value: 0xb,
191 Scattered: true,
192 },
193 },
194 },
195 nil,
196 },
197 {
198 "testdata/clang-amd64-darwin.obj.base64",
199 FileHeader{0xfeedfacf, CpuAmd64, 0x3, 0x1, 0x4, 0x200, 0x2000},
200 nil,
201 nil,
202 map[string][]Reloc{
203 "__text": {
204 {
205 Addr: 0x19,
206 Type: uint8(X86_64_RELOC_BRANCH),
207 Len: 2,
208 Pcrel: true,
209 Extern: true,
210 Value: 1,
211 },
212 {
213 Addr: 0xb,
214 Type: uint8(X86_64_RELOC_SIGNED),
215 Len: 2,
216 Pcrel: true,
217 Extern: false,
218 Value: 2,
219 },
220 },
221 "__compact_unwind": {
222 {
223 Addr: 0x0,
224 Type: uint8(X86_64_RELOC_UNSIGNED),
225 Len: 3,
226 Pcrel: false,
227 Extern: false,
228 Value: 1,
229 },
230 },
231 },
232 []string{"_printf"},
233 },
234 {
235 "testdata/clang-amd64-darwin-ld-r.obj.base64",
236 FileHeader{0xfeedfacf, CpuAmd64, 0x3, 0x1, 0x4, 0x1c0, 0x2000},
237 nil,
238 nil,
239 nil,
240 []string{"_printf"},
241 },
242 }
243
244 func readerAtFromObscured(name string) (io.ReaderAt, error) {
245 b, err := obscuretestdata.ReadFile(name)
246 if err != nil {
247 return nil, err
248 }
249 return bytes.NewReader(b), nil
250 }
251
252 func openObscured(name string) (*File, error) {
253 ra, err := readerAtFromObscured(name)
254 if err != nil {
255 return nil, err
256 }
257 ff, err := NewFile(ra)
258 if err != nil {
259 return nil, err
260 }
261 return ff, nil
262 }
263
264 func openFatObscured(name string) (*FatFile, error) {
265 ra, err := readerAtFromObscured(name)
266 if err != nil {
267 return nil, err
268 }
269 ff, err := NewFatFile(ra)
270 if err != nil {
271 return nil, err
272 }
273 return ff, nil
274 }
275
276 func TestOpen(t *testing.T) {
277 for i := range fileTests {
278 tt := &fileTests[i]
279
280
281
282
283
284 f, err := openObscured(tt.file)
285 if err != nil {
286 t.Error(err)
287 continue
288 }
289 if !reflect.DeepEqual(f.FileHeader, tt.hdr) {
290 t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr)
291 continue
292 }
293 for i, l := range f.Loads {
294 if len(l.Raw()) < 8 {
295 t.Errorf("open %s, command %d:\n\tload command %T don't have enough data\n", tt.file, i, l)
296 }
297 }
298 if tt.loads != nil {
299 for i, l := range f.Loads {
300 if i >= len(tt.loads) {
301 break
302 }
303
304 want := tt.loads[i]
305 if want == nil {
306 continue
307 }
308
309 switch l := l.(type) {
310 case *Segment:
311 have := &l.SegmentHeader
312 if !reflect.DeepEqual(have, want) {
313 t.Errorf("open %s, command %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
314 }
315 case *Dylib:
316 have := l
317 have.LoadBytes = nil
318 if !reflect.DeepEqual(have, want) {
319 t.Errorf("open %s, command %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
320 }
321 case *Rpath:
322 have := l
323 have.LoadBytes = nil
324 if !reflect.DeepEqual(have, want) {
325 t.Errorf("open %s, command %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
326 }
327 default:
328 t.Errorf("open %s, command %d: unknown load command\n\thave %#v\n\twant %#v\n", tt.file, i, l, want)
329 }
330 }
331 tn := len(tt.loads)
332 fn := len(f.Loads)
333 if tn != fn {
334 t.Errorf("open %s: len(Loads) = %d, want %d", tt.file, fn, tn)
335 }
336 }
337
338 if tt.sections != nil {
339 for i, sh := range f.Sections {
340 if i >= len(tt.sections) {
341 break
342 }
343 have := &sh.SectionHeader
344 want := tt.sections[i]
345 if !reflect.DeepEqual(have, want) {
346 t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
347 }
348 }
349 tn := len(tt.sections)
350 fn := len(f.Sections)
351 if tn != fn {
352 t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn)
353 }
354 }
355
356 if tt.relocations != nil {
357 for i, sh := range f.Sections {
358 have := sh.Relocs
359 want := tt.relocations[sh.Name]
360 if !reflect.DeepEqual(have, want) {
361 t.Errorf("open %s, relocations in section %d (%s):\n\thave %#v\n\twant %#v\n", tt.file, i, sh.Name, have, want)
362 }
363 }
364 }
365
366 if tt.importedSyms != nil {
367 ss, err := f.ImportedSymbols()
368 if err != nil {
369 t.Errorf("open %s: fail to read imported symbols: %v", tt.file, err)
370 }
371 want := tt.importedSyms
372 if !slices.Equal(ss, want) {
373 t.Errorf("open %s: imported symbols differ:\n\thave %v\n\twant %v", tt.file, ss, want)
374 }
375 }
376 }
377 }
378
379 func TestOpenFailure(t *testing.T) {
380 filename := "file.go"
381 _, err := Open(filename)
382 if err == nil {
383 t.Errorf("open %s: succeeded unexpectedly", filename)
384 }
385 }
386
387 func TestOpenFat(t *testing.T) {
388 ff, err := openFatObscured("testdata/fat-gcc-386-amd64-darwin-exec.base64")
389 if err != nil {
390 t.Fatal(err)
391 }
392
393 if ff.Magic != MagicFat {
394 t.Errorf("OpenFat: got magic number %#x, want %#x", ff.Magic, MagicFat)
395 }
396 if len(ff.Arches) != 2 {
397 t.Errorf("OpenFat: got %d architectures, want 2", len(ff.Arches))
398 }
399
400 for i := range ff.Arches {
401 arch := &ff.Arches[i]
402 ftArch := &fileTests[i]
403
404 if arch.Cpu != ftArch.hdr.Cpu || arch.SubCpu != ftArch.hdr.SubCpu {
405 t.Errorf("OpenFat: architecture #%d got cpu=%#x subtype=%#x, expected cpu=%#x, subtype=%#x", i, arch.Cpu, arch.SubCpu, ftArch.hdr.Cpu, ftArch.hdr.SubCpu)
406 }
407
408 if !reflect.DeepEqual(arch.FileHeader, ftArch.hdr) {
409 t.Errorf("OpenFat header:\n\tgot %#v\n\twant %#v\n", arch.FileHeader, ftArch.hdr)
410 }
411 }
412 }
413
414 func TestOpenFatFailure(t *testing.T) {
415 filename := "file.go"
416 if _, err := OpenFat(filename); err == nil {
417 t.Errorf("OpenFat %s: succeeded unexpectedly", filename)
418 }
419
420 filename = "testdata/gcc-386-darwin-exec.base64"
421 ff, err := openFatObscured(filename)
422 if err != ErrNotFat {
423 t.Errorf("OpenFat %s: got %v, want ErrNotFat", filename, err)
424 }
425 if ff != nil {
426 t.Errorf("OpenFat %s: got %v, want nil", filename, ff)
427 }
428 }
429
430 func TestRelocTypeString(t *testing.T) {
431 if X86_64_RELOC_BRANCH.String() != "X86_64_RELOC_BRANCH" {
432 t.Errorf("got %v, want %v", X86_64_RELOC_BRANCH.String(), "X86_64_RELOC_BRANCH")
433 }
434 if X86_64_RELOC_BRANCH.GoString() != "macho.X86_64_RELOC_BRANCH" {
435 t.Errorf("got %v, want %v", X86_64_RELOC_BRANCH.GoString(), "macho.X86_64_RELOC_BRANCH")
436 }
437 }
438
439 func TestTypeString(t *testing.T) {
440 if TypeExec.String() != "Exec" {
441 t.Errorf("got %v, want %v", TypeExec.String(), "Exec")
442 }
443 if TypeExec.GoString() != "macho.Exec" {
444 t.Errorf("got %v, want %v", TypeExec.GoString(), "macho.Exec")
445 }
446 }
447
448 func TestOpenBadDysymCmd(t *testing.T) {
449 _, err := openObscured("testdata/gcc-amd64-darwin-exec-with-bad-dysym.base64")
450 if err == nil {
451 t.Fatal("openObscured did not fail when opening a file with an invalid dynamic symbol table command")
452 }
453 }
454
View as plain text