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