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