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 | |
5 | package macho |
6 | |
7 | import ( |
8 | "reflect" |
9 | "strings" |
10 | "testing" |
11 | ) |
12 | |
13 | type fileTest struct { |
14 | file string |
15 | hdr FileHeader |
16 | loads []interface{} |
17 | sections []*SectionHeader |
18 | relocations map[string][]Reloc |
19 | } |
20 | |
21 | var 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 | |
225 | func 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 | |
313 | func 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 | |
321 | func 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 | |
348 | func 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 | |
373 | func 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 | |
382 | func 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 |
Members