1
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
9   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.
20
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.
81
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.
91*/
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
104#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
120/* 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
128
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;
403   struct segment_command_64* seg__pagezero  = NULL;
404
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;
429            case LC_DYSYMTAB:
430               if (DEBUGPRINTING)
431                  printf("LC_DYSYMTAB");
432               break;
433            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:
442               if (DEBUGPRINTING)
443                  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;
480            if (0 == strcmp(seg->segname, "__PAGEZERO"))
481               seg__pagezero = seg;
482         }
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" );
527      goto maybe_mash_pagezero;
528   }
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 */
548      goto maybe_mash_pagezero;
549   }
550
551   /* out of options */
552   fail("no __UNIXSTACK found and no usable __LINKEDIT found; "
553        "out of options.");
554   /* NOTREACHED */
555
556  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:
570   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*/
652