blob: d66ad20abef5960c91cfde4573e70b9613a31691 [file] [log] [blame]
sewardj08f5a272011-04-06 11:17:16 +00001
2/* Derived from Valgrind sources, coregrind/m_debuginfo/readmacho.c.
3 GPL 2+ therefore.
4
5 Can be compiled as either a 32- or 64-bit program (doesn't matter).
6*/
7
8/* What does this program do? In short it postprocesses tool
sewardje95d03f2014-11-06 20:23:22 +00009 executables on MacOSX, after linking using /usr/bin/ld.
10
11 This is to deal with two separate and entirely unrelated problems.
12 Problem (1) is a bug in the linker in Xcode 4.0.0. Problem (2) is
13 much newer and concerns linking 64-bit tool executables for
14 Yosemite (10.10).
15
16 --- Problem (1) ------------------------------------------------
17
18 This is a bug in the linker on Xcode 4.0.0 and Xcode 4.0.1. Xcode
19 versions prior to 4.0.0 are unaffected.
sewardj08f5a272011-04-06 11:17:16 +000020
21 The tracking bug is https://bugs.kde.org/show_bug.cgi?id=267997
22
23 The bug causes 64-bit tool executables to segfault at startup,
24 because:
25
26 Comparing the MachO load commands vs a (working) tool executable
27 that was created by Xcode 3.2.x, it appears that the new linker has
28 partially ignored the build system's request to place the tool
29 executable's stack at a non standard location. The build system
30 tells the linker "-stack_addr 0x134000000 -stack_size 0x800000".
31
32 With the Xcode 3.2 linker those flags produce two results:
33
34 (1) A load command to allocate the stack at the said location:
35 Load command 3
36 cmd LC_SEGMENT_64
37 cmdsize 72
38 segname __UNIXSTACK
39 vmaddr 0x0000000133800000
40 vmsize 0x0000000000800000
41 fileoff 2285568
42 filesize 0
43 maxprot 0x00000007
44 initprot 0x00000003
45 nsects 0
46 flags 0x0
47
48 (2) A request (in LC_UNIXTHREAD) to set %rsp to the correct value
49 at process startup, 0x134000000.
50
51 With Xcode 4.0.1, (1) is missing but (2) is still present. The
52 tool executable therefore starts up with %rsp pointing to unmapped
53 memory and faults almost instantly.
54
55 The workaround implemented by this program is documented in comment
56 8 of bug 267997, viz:
57
58 One really sick workaround is to observe that the executables
59 contain a redundant MachO load command:
60
61 Load command 2
62 cmd LC_SEGMENT_64
63 cmdsize 72
64 segname __LINKEDIT
65 vmaddr 0x0000000138dea000
66 vmsize 0x00000000000ad000
67 fileoff 2658304
68 filesize 705632
69 maxprot 0x00000007
70 initprot 0x00000001
71 nsects 0
72 flags 0x0
73
74 The described section presumably contains information intended for
75 the dynamic linker, but is irrelevant because this is a statically
76 linked executable. Hence it might be possible to postprocess the
77 executables after linking, to overwrite this entry with the
78 information that would have been in the missing __UNIXSTACK entry.
79 I tried this by hand (with a binary editor) earlier and got
80 something that worked.
sewardje95d03f2014-11-06 20:23:22 +000081
82 --- Problem (2) ------------------------------------------------
83
84 On MacOSX 10.10 (Yosemite), the kernel requires all valid
85 executables to have a __PAGEZERO section with SVMA of zero and size
86 of at least one page. However, our tool executables have a
87 __PAGEZERO section with SVMA set to the requested Valgrind load
88 address (typically 0x1'3800'0000). And the kernel won't start
89 those. So we take the opportunity to "fix" this by setting the
90 SVMA to zero. Seems to work and have no obvious bad side effects.
sewardj08f5a272011-04-06 11:17:16 +000091*/
92
93#define DEBUGPRINTING 0
94
95#include <assert.h>
96#include <stdlib.h>
97#include <stdio.h>
98#include <string.h>
99#include <sys/mman.h>
100#include <sys/stat.h>
101#include <unistd.h>
102#include <fcntl.h>
103
sewardj08f5a272011-04-06 11:17:16 +0000104#undef PLAT_x86_darwin
105#undef PLAT_amd64_darwin
106
107#if defined(__APPLE__) && defined(__i386__)
108# define PLAT_x86_darwin 1
109#elif defined(__APPLE__) && defined(__x86_64__)
110# define PLAT_amd64_darwin 1
111#else
112# error "Can't be compiled on this platform"
113#endif
114
115#include <mach-o/loader.h>
116#include <mach-o/nlist.h>
117#include <mach-o/fat.h>
118#include <mach/i386/thread_status.h>
119
sewardje95d03f2014-11-06 20:23:22 +0000120/* Get hold of DARWIN_VERS, and check it has a sane value. */
121#include "config.h"
122#if DARWIN_VERS != DARWIN_10_5 && DARWIN_VERS != DARWIN_10_6 \
123 && DARWIN_VERS != DARWIN_10_7 && DARWIN_VERS != DARWIN_10_8 \
124 && DARWIN_VERS != DARWIN_10_9 && DARWIN_VERS != DARWIN_10_10
125# error "Unknown DARWIN_VERS value. This file only compiles on Darwin."
126#endif
127
sewardj08f5a272011-04-06 11:17:16 +0000128
129typedef unsigned char UChar;
130typedef signed char Char;
131typedef char HChar; /* signfulness depends on host */
132
133typedef unsigned int UInt;
134typedef signed int Int;
135
136typedef unsigned char Bool;
137#define True ((Bool)1)
138#define False ((Bool)0)
139
140typedef unsigned long UWord;
141
142typedef UWord SizeT;
143typedef UWord Addr;
144
145typedef unsigned long long int ULong;
146typedef signed long long int Long;
147
148
149
150__attribute__((noreturn))
151void fail ( HChar* msg )
152{
153 fprintf(stderr, "fixup_macho_loadcmds: fail: %s\n", msg);
154 exit(1);
155}
156
157
158/*------------------------------------------------------------*/
159/*--- ---*/
160/*--- Mach-O file mapping/unmapping helpers ---*/
161/*--- ---*/
162/*------------------------------------------------------------*/
163
164typedef
165 struct {
166 /* These two describe the entire mapped-in ("primary") image,
167 fat headers, kitchen sink, whatnot: the entire file. The
168 image is mapped into img[0 .. img_szB-1]. */
169 UChar* img;
170 SizeT img_szB;
171 /* These two describe the Mach-O object of interest, which is
172 presumably somewhere inside the primary image.
173 map_image_aboard() below, which generates this info, will
174 carefully check that the macho_ fields denote a section of
175 memory that falls entirely inside img[0 .. img_szB-1]. */
176 UChar* macho_img;
177 SizeT macho_img_szB;
178 }
179 ImageInfo;
180
181
182Bool is_macho_object_file( const void* buf, SizeT szB )
183{
184 /* (JRS: the Mach-O headers might not be in this mapped data,
185 because we only mapped a page for this initial check,
186 or at least not very much, and what's at the start of the file
187 is in general a so-called fat header. The Mach-O object we're
188 interested in could be arbitrarily far along the image, and so
189 we can't assume its header will fall within this page.) */
190
191 /* But we can say that either it's a fat object, in which case it
192 begins with a fat header, or it's unadorned Mach-O, in which
193 case it starts with a normal header. At least do what checks we
194 can to establish whether or not we're looking at something
195 sane. */
196
197 const struct fat_header* fh_be = buf;
198 const struct mach_header_64* mh = buf;
199
200 assert(buf);
201 if (szB < sizeof(struct fat_header))
202 return False;
203 if (ntohl(fh_be->magic) == FAT_MAGIC)
204 return True;
205
206 if (szB < sizeof(struct mach_header_64))
207 return False;
208 if (mh->magic == MH_MAGIC_64)
209 return True;
210
211 return False;
212}
213
214
215/* Unmap an image mapped in by map_image_aboard. */
216static void unmap_image ( /*MOD*/ImageInfo* ii )
217{
218 Int r;
219 assert(ii->img);
220 assert(ii->img_szB > 0);
221 r = munmap( ii->img, ii->img_szB );
222 /* Do we care if this fails? I suppose so; it would indicate
223 some fairly serious snafu with the mapping of the file. */
224 assert( !r );
225 memset(ii, 0, sizeof(*ii));
226}
227
228
229/* Map a given fat or thin object aboard, find the thin part if
230 necessary, do some checks, and write details of both the fat and
231 thin parts into *ii. Returns 32 (and leaves the file unmapped) if
232 the thin part is a 32 bit file. Returns 64 if it's a 64 bit file.
233 Does not return on failure. Guarantees to return pointers to a
234 valid(ish) Mach-O image if it succeeds. */
235static Int map_image_aboard ( /*OUT*/ImageInfo* ii, HChar* filename )
236{
237 memset(ii, 0, sizeof(*ii));
238
239 /* First off, try to map the thing in. */
240 { SizeT size;
241 Int r, fd;
242 struct stat stat_buf;
243
244 r = stat(filename, &stat_buf);
245 if (r)
246 fail("Can't stat image (to determine its size)?!");
247 size = stat_buf.st_size;
248
249 fd = open(filename, O_RDWR, 0);
250 if (fd == -1)
251 fail("Can't open image for possible modification!");
252 if (DEBUGPRINTING)
253 printf("size %lu fd %d\n", size, fd);
254 void* v = mmap ( NULL, size, PROT_READ|PROT_WRITE,
255 MAP_FILE|MAP_SHARED, fd, 0 );
256 if (v == MAP_FAILED) {
257 perror("mmap failed");
258 fail("Can't mmap image for possible modification!");
259 }
260
261 close(fd);
262
263 ii->img = (UChar*)v;
264 ii->img_szB = size;
265 }
266
267 /* Now it's mapped in and we have .img and .img_szB set. Look for
268 the embedded Mach-O object. If not findable, unmap and fail. */
269 { struct fat_header* fh_be;
270 struct fat_header fh;
271 struct mach_header_64* mh;
272
273 // Assume initially that we have a thin image, and update
274 // these if it turns out to be fat.
275 ii->macho_img = ii->img;
276 ii->macho_img_szB = ii->img_szB;
277
278 // Check for fat header.
279 if (ii->img_szB < sizeof(struct fat_header))
280 fail("Invalid Mach-O file (0 too small).");
281
282 // Fat header is always BIG-ENDIAN
283 fh_be = (struct fat_header *)ii->img;
284 fh.magic = ntohl(fh_be->magic);
285 fh.nfat_arch = ntohl(fh_be->nfat_arch);
286 if (fh.magic == FAT_MAGIC) {
287 // Look for a good architecture.
288 struct fat_arch *arch_be;
289 struct fat_arch arch;
290 Int f;
291 if (ii->img_szB < sizeof(struct fat_header)
292 + fh.nfat_arch * sizeof(struct fat_arch))
293 fail("Invalid Mach-O file (1 too small).");
294
295 for (f = 0, arch_be = (struct fat_arch *)(fh_be+1);
296 f < fh.nfat_arch;
297 f++, arch_be++) {
298 Int cputype;
299# if defined(PLAT_x86_darwin)
300 cputype = CPU_TYPE_X86;
301# elif defined(PLAT_amd64_darwin)
302 cputype = CPU_TYPE_X86_64;
303# else
304# error "unknown architecture"
305# endif
306 arch.cputype = ntohl(arch_be->cputype);
307 arch.cpusubtype = ntohl(arch_be->cpusubtype);
308 arch.offset = ntohl(arch_be->offset);
309 arch.size = ntohl(arch_be->size);
310 if (arch.cputype == cputype) {
311 if (ii->img_szB < arch.offset + arch.size)
312 fail("Invalid Mach-O file (2 too small).");
313 ii->macho_img = ii->img + arch.offset;
314 ii->macho_img_szB = arch.size;
315 break;
316 }
317 }
318 if (f == fh.nfat_arch)
319 fail("No acceptable architecture found in fat file.");
320 }
321
322 /* Sanity check what we found. */
323
324 /* assured by logic above */
325 assert(ii->img_szB >= sizeof(struct fat_header));
326
327 if (ii->macho_img_szB < sizeof(struct mach_header_64))
328 fail("Invalid Mach-O file (3 too small).");
329
330 if (ii->macho_img_szB > ii->img_szB)
331 fail("Invalid Mach-O file (thin bigger than fat).");
332
333 if (ii->macho_img >= ii->img
334 && ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB) {
335 /* thin entirely within fat, as expected */
336 } else {
337 fail("Invalid Mach-O file (thin not inside fat).");
338 }
339
340 mh = (struct mach_header_64 *)ii->macho_img;
341 if (mh->magic == MH_MAGIC) {
342 assert(ii->img);
343 assert(ii->macho_img);
344 assert(ii->img_szB > 0);
345 assert(ii->macho_img_szB > 0);
346 assert(ii->macho_img >= ii->img);
347 assert(ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB);
348 return 32;
349 }
350 if (mh->magic != MH_MAGIC_64)
351 fail("Invalid Mach-O file (bad magic).");
352
353 if (ii->macho_img_szB < sizeof(struct mach_header_64) + mh->sizeofcmds)
354 fail("Invalid Mach-O file (4 too small).");
355 }
356
357 assert(ii->img);
358 assert(ii->macho_img);
359 assert(ii->img_szB > 0);
360 assert(ii->macho_img_szB > 0);
361 assert(ii->macho_img >= ii->img);
362 assert(ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB);
363 return 64;
364}
365
366
367/*------------------------------------------------------------*/
368/*--- ---*/
369/*--- Mach-O top-level processing ---*/
370/*--- ---*/
371/*------------------------------------------------------------*/
372
373void modify_macho_loadcmds ( HChar* filename,
374 ULong expected_stack_start,
375 ULong expected_stack_size )
376{
377 ImageInfo ii;
378 memset(&ii, 0, sizeof(ii));
379
380 Int size = map_image_aboard( &ii, filename );
381 if (size == 32) {
382 fprintf(stderr, "fixup_macho_loadcmds: Is 32-bit MachO file;"
383 " no modifications needed.\n");
384 goto out;
385 }
386
387 assert(size == 64);
388
389 assert(ii.macho_img != NULL && ii.macho_img_szB > 0);
390
391 /* Poke around in the Mach-O header, to find some important
392 stuff.
393 * the location of the __UNIXSTACK load command, if any
394 * the location of the __LINKEDIT load command, if any
395 * the initial RSP value as stated in the LC_UNIXTHREAD
396 */
397
398 /* The collected data */
399 ULong init_rsp = 0;
400 Bool have_rsp = False;
401 struct segment_command_64* seg__unixstack = NULL;
402 struct segment_command_64* seg__linkedit = NULL;
sewardje95d03f2014-11-06 20:23:22 +0000403 struct segment_command_64* seg__pagezero = NULL;
sewardj08f5a272011-04-06 11:17:16 +0000404
405 /* Loop over the load commands and fill in the above 4 variables. */
406
407 { struct mach_header_64 *mh = (struct mach_header_64 *)ii.macho_img;
408 struct load_command *cmd;
409 Int c;
410
411 for (c = 0, cmd = (struct load_command *)(mh+1);
412 c < mh->ncmds;
413 c++, cmd = (struct load_command *)(cmd->cmdsize
414 + (unsigned long)cmd)) {
415 if (DEBUGPRINTING)
416 printf("load cmd: offset %4lu size %3d kind %2d = ",
417 (unsigned long)((UChar*)cmd - (UChar*)ii.macho_img),
418 cmd->cmdsize, cmd->cmd);
419
420 switch (cmd->cmd) {
421 case LC_SEGMENT_64:
422 if (DEBUGPRINTING)
423 printf("LC_SEGMENT_64");
424 break;
425 case LC_SYMTAB:
426 if (DEBUGPRINTING)
427 printf("LC_SYMTAB");
428 break;
sewardj627c6492011-05-10 08:42:14 +0000429 case LC_DYSYMTAB:
430 if (DEBUGPRINTING)
431 printf("LC_DYSYMTAB");
432 break;
sewardj08f5a272011-04-06 11:17:16 +0000433 case LC_UUID:
434 if (DEBUGPRINTING)
435 printf("LC_UUID");
436 break;
437 case LC_UNIXTHREAD:
438 if (DEBUGPRINTING)
439 printf("LC_UNIXTHREAD");
440 break;
441 default:
sewardje95d03f2014-11-06 20:23:22 +0000442 if (DEBUGPRINTING)
sewardj08f5a272011-04-06 11:17:16 +0000443 printf("???");
444 fail("unexpected load command in Mach header");
445 break;
446 }
447 if (DEBUGPRINTING)
448 printf("\n");
449
450 /* Note what the stated initial RSP value is, so we can
451 check it is as expected. */
452 if (cmd->cmd == LC_UNIXTHREAD) {
453 struct thread_command* tcmd = (struct thread_command*)cmd;
454 UInt* w32s = (UInt*)( (UChar*)tcmd + sizeof(*tcmd) );
455 if (DEBUGPRINTING)
456 printf("UnixThread: flavor %u = ", w32s[0]);
457 if (w32s[0] == x86_THREAD_STATE64 && !have_rsp) {
458 if (DEBUGPRINTING)
459 printf("x86_THREAD_STATE64\n");
460 x86_thread_state64_t* state64
461 = (x86_thread_state64_t*)(&w32s[2]);
462 have_rsp = True;
463 init_rsp = state64->__rsp;
464 if (DEBUGPRINTING)
465 printf("rsp = 0x%llx\n", init_rsp);
466 } else {
467 if (DEBUGPRINTING)
468 printf("???");
469 }
470 if (DEBUGPRINTING)
471 printf("\n");
472 }
473
474 if (cmd->cmd == LC_SEGMENT_64) {
475 struct segment_command_64 *seg = (struct segment_command_64 *)cmd;
476 if (0 == strcmp(seg->segname, "__LINKEDIT"))
477 seg__linkedit = seg;
478 if (0 == strcmp(seg->segname, "__UNIXSTACK"))
479 seg__unixstack = seg;
sewardje95d03f2014-11-06 20:23:22 +0000480 if (0 == strcmp(seg->segname, "__PAGEZERO"))
481 seg__pagezero = seg;
sewardj08f5a272011-04-06 11:17:16 +0000482 }
483
484 }
485 }
486
487 /*
488 Actions are then as follows:
489
490 * (always) check the RSP value is as expected, and abort if not
491
492 * if there's a UNIXSTACK load command, check it is as expected.
493 If not abort, if yes, do nothing more.
494
495 * (so there's no UNIXSTACK load command). if there's a LINKEDIT
496 load command, check if it is minimally usable (has 0 for
497 nsects and flags). If yes, convert it to a UNIXSTACK load
498 command. If there is none, or is unusable, then we're out of
499 options and have to abort.
500 */
501 if (!have_rsp)
502 fail("Can't find / check initial RSP setting");
503 if (init_rsp != expected_stack_start + expected_stack_size)
504 fail("Initial RSP value not as expected");
505
506 fprintf(stderr, "fixup_macho_loadcmds: "
507 "initial RSP is as expected (0x%llx)\n",
508 expected_stack_start + expected_stack_size );
509
510 if (seg__unixstack) {
511 struct segment_command_64 *seg = seg__unixstack;
512 if (seg->vmaddr != expected_stack_start)
513 fail("has __UNIXSTACK, but wrong ::vmaddr");
514 if (seg->vmsize != expected_stack_size)
515 fail("has __UNIXSTACK, but wrong ::vmsize");
516 if (seg->maxprot != 7)
517 fail("has __UNIXSTACK, but wrong ::maxprot (should be 7)");
518 if (seg->initprot != 3)
519 fail("has __UNIXSTACK, but wrong ::initprot (should be 3)");
520 if (seg->nsects != 0)
521 fail("has __UNIXSTACK, but wrong ::nsects (should be 0)");
522 if (seg->flags != 0)
523 fail("has __UNIXSTACK, but wrong ::flags (should be 0)");
524 /* looks ok */
525 fprintf(stderr, "fixup_macho_loadcmds: "
526 "acceptable __UNIXSTACK present; no modifications.\n" );
sewardje95d03f2014-11-06 20:23:22 +0000527 goto maybe_mash_pagezero;
sewardj08f5a272011-04-06 11:17:16 +0000528 }
529
530 if (seg__linkedit) {
531 struct segment_command_64 *seg = seg__linkedit;
532 if (seg->nsects != 0)
533 fail("has __LINKEDIT, but wrong ::nsects (should be 0)");
534 if (seg->flags != 0)
535 fail("has __LINKEDIT, but wrong ::flags (should be 0)");
536 fprintf(stderr, "fixup_macho_loadcmds: "
537 "no __UNIXSTACK present.\n" );
538 fprintf(stderr, "fixup_macho_loadcmds: "
539 "converting __LINKEDIT to __UNIXSTACK.\n" );
540 strcpy(seg->segname, "__UNIXSTACK");
541 seg->vmaddr = expected_stack_start;
542 seg->vmsize = expected_stack_size;
543 seg->fileoff = 0;
544 seg->filesize = 0;
545 seg->maxprot = 7;
546 seg->initprot = 3;
547 /* success */
sewardje95d03f2014-11-06 20:23:22 +0000548 goto maybe_mash_pagezero;
sewardj08f5a272011-04-06 11:17:16 +0000549 }
550
551 /* out of options */
552 fail("no __UNIXSTACK found and no usable __LINKEDIT found; "
553 "out of options.");
554 /* NOTREACHED */
555
sewardje95d03f2014-11-06 20:23:22 +0000556 maybe_mash_pagezero:
557 /* Deal with Problem (2) as documented above. */
558# if DARWIN_VERS == DARWIN_10_10
559 assert(size == 64);
560 if (!seg__pagezero) {
561 fail("Can't find __PAGEZERO to modify; can't continue.");
562 }
563 fprintf(stderr, "fixup_macho_loadcmds: "
564 "changing __PAGEZERO.vmaddr from %p to 0x0.\n",
565 (void*)seg__pagezero->vmaddr);
566 seg__pagezero->vmaddr = 0;
567# endif
568
569 out:
sewardj08f5a272011-04-06 11:17:16 +0000570 if (ii.img)
571 unmap_image(&ii);
572}
573
574
575static Bool is_plausible_tool_exe_name ( HChar* nm )
576{
577 HChar* p;
578 if (!nm)
579 return False;
580
581 // Does it end with this string?
582 p = strstr(nm, "-x86-darwin");
583 if (p && 0 == strcmp(p, "-x86-darwin"))
584 return True;
585
586 p = strstr(nm, "-amd64-darwin");
587 if (p && 0 == strcmp(p, "-amd64-darwin"))
588 return True;
589
590 return False;
591}
592
593
594int main ( int argc, char** argv )
595{
596 Int r;
597 ULong req_stack_addr = 0;
598 ULong req_stack_size = 0;
599
600 if (argc != 4)
601 fail("args: -stack_addr-arg -stack_size-arg "
602 "name-of-tool-executable-to-modify");
603
604 r= sscanf(argv[1], "0x%llx", &req_stack_addr);
605 if (r != 1) fail("invalid stack_addr arg");
606
607 r= sscanf(argv[2], "0x%llx", &req_stack_size);
608 if (r != 1) fail("invalid stack_size arg");
609
610 fprintf(stderr, "fixup_macho_loadcmds: "
611 "requested stack_addr (top) 0x%llx, "
612 "stack_size 0x%llx\n", req_stack_addr, req_stack_size );
613
614 if (!is_plausible_tool_exe_name(argv[3]))
615 fail("implausible tool exe name -- not of the form *-{x86,amd64}-darwin");
616
617 fprintf(stderr, "fixup_macho_loadcmds: examining tool exe: %s\n",
618 argv[3] );
619 modify_macho_loadcmds( argv[3], req_stack_addr - req_stack_size,
620 req_stack_size );
621
622 return 0;
623}
624
625/*
626 cmd LC_SEGMENT_64
627 cmdsize 72
628 segname __LINKEDIT
629 vmaddr 0x0000000138dea000
630 vmsize 0x00000000000ad000
631 fileoff 2658304
632 filesize 705632
633 maxprot 0x00000007
634 initprot 0x00000001
635 nsects 0
636 flags 0x0
637*/
638
639/*
640 cmd LC_SEGMENT_64
641 cmdsize 72
642 segname __UNIXSTACK
643 vmaddr 0x0000000133800000
644 vmsize 0x0000000000800000
645 fileoff 2498560
646 filesize 0
647 maxprot 0x00000007
648 initprot 0x00000003
649 nsects 0
650 flags 0x0
651*/