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