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    && DARWIN_VERS != DARWIN_10_11 && DARWIN_VERS != DARWIN_10_12
126#  error "Unknown DARWIN_VERS value.  This file only compiles on Darwin."
127#endif
128
129
130typedef  unsigned char   UChar;
131typedef    signed char   Char;
132typedef           char   HChar; /* signfulness depends on host */
133
134typedef  unsigned int    UInt;
135typedef    signed int    Int;
136
137typedef  unsigned char   Bool;
138#define  True   ((Bool)1)
139#define  False  ((Bool)0)
140
141typedef  unsigned long   UWord;
142
143typedef  UWord           SizeT;
144typedef  UWord           Addr;
145
146typedef  unsigned long long int   ULong;
147typedef    signed long long int   Long;
148
149
150
151__attribute__((noreturn))
152void fail ( HChar* msg )
153{
154   fprintf(stderr, "fixup_macho_loadcmds: fail: %s\n", msg);
155   exit(1);
156}
157
158
159/*------------------------------------------------------------*/
160/*---                                                      ---*/
161/*--- Mach-O file mapping/unmapping helpers                ---*/
162/*---                                                      ---*/
163/*------------------------------------------------------------*/
164
165typedef
166   struct {
167      /* These two describe the entire mapped-in ("primary") image,
168         fat headers, kitchen sink, whatnot: the entire file.  The
169         image is mapped into img[0 .. img_szB-1]. */
170      UChar* img;
171      SizeT  img_szB;
172      /* These two describe the Mach-O object of interest, which is
173         presumably somewhere inside the primary image.
174         map_image_aboard() below, which generates this info, will
175         carefully check that the macho_ fields denote a section of
176         memory that falls entirely inside img[0 .. img_szB-1]. */
177      UChar* macho_img;
178      SizeT  macho_img_szB;
179   }
180   ImageInfo;
181
182
183Bool is_macho_object_file( const void* buf, SizeT szB )
184{
185   /* (JRS: the Mach-O headers might not be in this mapped data,
186      because we only mapped a page for this initial check,
187      or at least not very much, and what's at the start of the file
188      is in general a so-called fat header.  The Mach-O object we're
189      interested in could be arbitrarily far along the image, and so
190      we can't assume its header will fall within this page.) */
191
192   /* But we can say that either it's a fat object, in which case it
193      begins with a fat header, or it's unadorned Mach-O, in which
194      case it starts with a normal header.  At least do what checks we
195      can to establish whether or not we're looking at something
196      sane. */
197
198   const struct fat_header*  fh_be = buf;
199   const struct mach_header_64* mh    = buf;
200
201   assert(buf);
202   if (szB < sizeof(struct fat_header))
203      return False;
204   if (ntohl(fh_be->magic) == FAT_MAGIC)
205      return True;
206
207   if (szB < sizeof(struct mach_header_64))
208      return False;
209   if (mh->magic == MH_MAGIC_64)
210      return True;
211
212   return False;
213}
214
215
216/* Unmap an image mapped in by map_image_aboard. */
217static void unmap_image ( /*MOD*/ImageInfo* ii )
218{
219   Int r;
220   assert(ii->img);
221   assert(ii->img_szB > 0);
222   r = munmap( ii->img, ii->img_szB );
223   /* Do we care if this fails?  I suppose so; it would indicate
224      some fairly serious snafu with the mapping of the file. */
225   assert( !r );
226   memset(ii, 0, sizeof(*ii));
227}
228
229
230/* Map a given fat or thin object aboard, find the thin part if
231   necessary, do some checks, and write details of both the fat and
232   thin parts into *ii.  Returns 32 (and leaves the file unmapped) if
233   the thin part is a 32 bit file.  Returns 64 if it's a 64 bit file.
234   Does not return on failure.  Guarantees to return pointers to a
235   valid(ish) Mach-O image if it succeeds. */
236static Int map_image_aboard ( /*OUT*/ImageInfo* ii, HChar* filename )
237{
238   memset(ii, 0, sizeof(*ii));
239
240   /* First off, try to map the thing in. */
241   { SizeT  size;
242     Int r, fd;
243     struct stat stat_buf;
244
245     r = stat(filename, &stat_buf);
246     if (r)
247        fail("Can't stat image (to determine its size)?!");
248     size = stat_buf.st_size;
249
250     fd = open(filename, O_RDWR, 0);
251     if (fd == -1)
252        fail("Can't open image for possible modification!");
253     if (DEBUGPRINTING)
254        printf("size %lu fd %d\n", size, fd);
255     void* v = mmap ( NULL, size, PROT_READ|PROT_WRITE,
256                                  MAP_FILE|MAP_SHARED, fd, 0 );
257     if (v == MAP_FAILED) {
258        perror("mmap failed");
259        fail("Can't mmap image for possible modification!");
260     }
261
262     close(fd);
263
264     ii->img     = (UChar*)v;
265     ii->img_szB = size;
266   }
267
268   /* Now it's mapped in and we have .img and .img_szB set.  Look for
269      the embedded Mach-O object.  If not findable, unmap and fail. */
270   { struct fat_header*  fh_be;
271     struct fat_header   fh;
272     struct mach_header_64* mh;
273
274     // Assume initially that we have a thin image, and update
275     // these if it turns out to be fat.
276     ii->macho_img     = ii->img;
277     ii->macho_img_szB = ii->img_szB;
278
279     // Check for fat header.
280     if (ii->img_szB < sizeof(struct fat_header))
281        fail("Invalid Mach-O file (0 too small).");
282
283     // Fat header is always BIG-ENDIAN
284     fh_be = (struct fat_header *)ii->img;
285     fh.magic = ntohl(fh_be->magic);
286     fh.nfat_arch = ntohl(fh_be->nfat_arch);
287     if (fh.magic == FAT_MAGIC) {
288        // Look for a good architecture.
289        struct fat_arch *arch_be;
290        struct fat_arch arch;
291        Int f;
292        if (ii->img_szB < sizeof(struct fat_header)
293                          + fh.nfat_arch * sizeof(struct fat_arch))
294           fail("Invalid Mach-O file (1 too small).");
295
296        for (f = 0, arch_be = (struct fat_arch *)(fh_be+1);
297             f < fh.nfat_arch;
298             f++, arch_be++) {
299           Int cputype;
300#          if defined(PLAT_x86_darwin)
301           cputype = CPU_TYPE_X86;
302#          elif defined(PLAT_amd64_darwin)
303           cputype = CPU_TYPE_X86_64;
304#          else
305#            error "unknown architecture"
306#          endif
307           arch.cputype    = ntohl(arch_be->cputype);
308           arch.cpusubtype = ntohl(arch_be->cpusubtype);
309           arch.offset     = ntohl(arch_be->offset);
310           arch.size       = ntohl(arch_be->size);
311           if (arch.cputype == cputype) {
312              if (ii->img_szB < arch.offset + arch.size)
313                 fail("Invalid Mach-O file (2 too small).");
314              ii->macho_img     = ii->img + arch.offset;
315              ii->macho_img_szB = arch.size;
316              break;
317           }
318        }
319        if (f == fh.nfat_arch)
320           fail("No acceptable architecture found in fat file.");
321     }
322
323     /* Sanity check what we found. */
324
325     /* assured by logic above */
326     assert(ii->img_szB >= sizeof(struct fat_header));
327
328     if (ii->macho_img_szB < sizeof(struct mach_header_64))
329        fail("Invalid Mach-O file (3 too small).");
330
331     if (ii->macho_img_szB > ii->img_szB)
332        fail("Invalid Mach-O file (thin bigger than fat).");
333
334     if (ii->macho_img >= ii->img
335         && ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB) {
336        /* thin entirely within fat, as expected */
337     } else {
338        fail("Invalid Mach-O file (thin not inside fat).");
339     }
340
341     mh = (struct mach_header_64 *)ii->macho_img;
342     if (mh->magic == MH_MAGIC) {
343        assert(ii->img);
344        assert(ii->macho_img);
345        assert(ii->img_szB > 0);
346        assert(ii->macho_img_szB > 0);
347        assert(ii->macho_img >= ii->img);
348        assert(ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB);
349        return 32;
350     }
351     if (mh->magic != MH_MAGIC_64)
352        fail("Invalid Mach-O file (bad magic).");
353
354     if (ii->macho_img_szB < sizeof(struct mach_header_64) + mh->sizeofcmds)
355        fail("Invalid Mach-O file (4 too small).");
356   }
357
358   assert(ii->img);
359   assert(ii->macho_img);
360   assert(ii->img_szB > 0);
361   assert(ii->macho_img_szB > 0);
362   assert(ii->macho_img >= ii->img);
363   assert(ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB);
364   return 64;
365}
366
367
368/*------------------------------------------------------------*/
369/*---                                                      ---*/
370/*--- Mach-O top-level processing                          ---*/
371/*---                                                      ---*/
372/*------------------------------------------------------------*/
373
374void modify_macho_loadcmds ( HChar* filename,
375                             ULong  expected_stack_start,
376                             ULong  expected_stack_size )
377{
378   ImageInfo ii;
379   memset(&ii, 0, sizeof(ii));
380
381   Int size = map_image_aboard( &ii, filename );
382   if (size == 32) {
383      fprintf(stderr, "fixup_macho_loadcmds:   Is 32-bit MachO file;"
384              " no modifications needed.\n");
385      goto out;
386   }
387
388   assert(size == 64);
389
390   assert(ii.macho_img != NULL && ii.macho_img_szB > 0);
391
392   /* Poke around in the Mach-O header, to find some important
393      stuff.
394      * the location of the __UNIXSTACK load command, if any
395      * the location of the __LINKEDIT load command, if any
396      * the initial RSP value as stated in the LC_UNIXTHREAD
397   */
398
399   /* The collected data */
400   ULong init_rsp = 0;
401   Bool  have_rsp = False;
402   struct segment_command_64* seg__unixstack = NULL;
403   struct segment_command_64* seg__linkedit  = NULL;
404   struct segment_command_64* seg__pagezero  = NULL;
405
406   /* Loop over the load commands and fill in the above 4 variables. */
407
408   { struct mach_header_64 *mh = (struct mach_header_64 *)ii.macho_img;
409      struct load_command *cmd;
410      Int c;
411
412      for (c = 0, cmd = (struct load_command *)(mh+1);
413           c < mh->ncmds;
414           c++, cmd = (struct load_command *)(cmd->cmdsize
415                                              + (unsigned long)cmd)) {
416         if (DEBUGPRINTING)
417            printf("load cmd: offset %4lu   size %3d   kind %2d = ",
418                   (unsigned long)((UChar*)cmd - (UChar*)ii.macho_img),
419                   cmd->cmdsize, cmd->cmd);
420
421         switch (cmd->cmd) {
422            case LC_SEGMENT_64:
423               if (DEBUGPRINTING)
424                  printf("LC_SEGMENT_64");
425               break;
426            case LC_SYMTAB:
427               if (DEBUGPRINTING)
428                  printf("LC_SYMTAB");
429               break;
430            case LC_DYSYMTAB:
431               if (DEBUGPRINTING)
432                  printf("LC_DYSYMTAB");
433               break;
434            case LC_UUID:
435               if (DEBUGPRINTING)
436                  printf("LC_UUID");
437               break;
438            case LC_UNIXTHREAD:
439               if (DEBUGPRINTING)
440                  printf("LC_UNIXTHREAD");
441               break;
442            default:
443               if (DEBUGPRINTING)
444                  printf("???");
445               fail("unexpected load command in Mach header");
446            break;
447         }
448         if (DEBUGPRINTING)
449            printf("\n");
450
451         /* Note what the stated initial RSP value is, so we can
452            check it is as expected. */
453         if (cmd->cmd == LC_UNIXTHREAD) {
454            struct thread_command* tcmd = (struct thread_command*)cmd;
455            UInt* w32s = (UInt*)( (UChar*)tcmd + sizeof(*tcmd) );
456            if (DEBUGPRINTING)
457               printf("UnixThread: flavor %u = ", w32s[0]);
458            if (w32s[0] == x86_THREAD_STATE64 && !have_rsp) {
459               if (DEBUGPRINTING)
460                  printf("x86_THREAD_STATE64\n");
461               x86_thread_state64_t* state64
462                  = (x86_thread_state64_t*)(&w32s[2]);
463               have_rsp = True;
464               init_rsp = state64->__rsp;
465               if (DEBUGPRINTING)
466                  printf("rsp = 0x%llx\n", init_rsp);
467            } else {
468               if (DEBUGPRINTING)
469                  printf("???");
470            }
471            if (DEBUGPRINTING)
472               printf("\n");
473         }
474
475         if (cmd->cmd == LC_SEGMENT_64) {
476            struct segment_command_64 *seg = (struct segment_command_64 *)cmd;
477            if (0 == strcmp(seg->segname, "__LINKEDIT"))
478               seg__linkedit = seg;
479            if (0 == strcmp(seg->segname, "__UNIXSTACK"))
480               seg__unixstack = seg;
481            if (0 == strcmp(seg->segname, "__PAGEZERO"))
482               seg__pagezero = seg;
483         }
484
485      }
486   }
487
488   /*
489      Actions are then as follows:
490
491      * (always) check the RSP value is as expected, and abort if not
492
493      * if there's a UNIXSTACK load command, check it is as expected.
494        If not abort, if yes, do nothing more.
495
496      * (so there's no UNIXSTACK load command).  if there's a LINKEDIT
497        load command, check if it is minimally usable (has 0 for
498        nsects and flags).  If yes, convert it to a UNIXSTACK load
499        command.  If there is none, or is unusable, then we're out of
500        options and have to abort.
501   */
502   if (!have_rsp)
503      fail("Can't find / check initial RSP setting");
504   if (init_rsp != expected_stack_start + expected_stack_size)
505      fail("Initial RSP value not as expected");
506
507   fprintf(stderr, "fixup_macho_loadcmds:   "
508                   "initial RSP is as expected (0x%llx)\n",
509                   expected_stack_start + expected_stack_size );
510
511   if (seg__unixstack) {
512      struct segment_command_64 *seg = seg__unixstack;
513      if (seg->vmaddr != expected_stack_start)
514         fail("has __UNIXSTACK, but wrong ::vmaddr");
515      if (seg->vmsize != expected_stack_size)
516         fail("has __UNIXSTACK, but wrong ::vmsize");
517      if (seg->maxprot != 7)
518         fail("has __UNIXSTACK, but wrong ::maxprot (should be 7)");
519      if (seg->initprot != 3)
520         fail("has __UNIXSTACK, but wrong ::initprot (should be 3)");
521      if (seg->nsects != 0)
522         fail("has __UNIXSTACK, but wrong ::nsects (should be 0)");
523      if (seg->flags != 0)
524         fail("has __UNIXSTACK, but wrong ::flags (should be 0)");
525      /* looks ok */
526      fprintf(stderr, "fixup_macho_loadcmds:   "
527              "acceptable __UNIXSTACK present; no modifications.\n" );
528      goto maybe_mash_pagezero;
529   }
530
531   if (seg__linkedit) {
532      struct segment_command_64 *seg = seg__linkedit;
533      if (seg->nsects != 0)
534         fail("has __LINKEDIT, but wrong ::nsects (should be 0)");
535      if (seg->flags != 0)
536         fail("has __LINKEDIT, but wrong ::flags (should be 0)");
537      fprintf(stderr, "fixup_macho_loadcmds:   "
538              "no __UNIXSTACK present.\n" );
539      fprintf(stderr, "fixup_macho_loadcmds:   "
540              "converting __LINKEDIT to __UNIXSTACK.\n" );
541      strcpy(seg->segname, "__UNIXSTACK");
542      seg->vmaddr   = expected_stack_start;
543      seg->vmsize   = expected_stack_size;
544      seg->fileoff  = 0;
545      seg->filesize = 0;
546      seg->maxprot  = 7;
547      seg->initprot = 3;
548      /* success */
549      goto maybe_mash_pagezero;
550   }
551
552   /* out of options */
553   fail("no __UNIXSTACK found and no usable __LINKEDIT found; "
554        "out of options.");
555   /* NOTREACHED */
556
557  maybe_mash_pagezero:
558   /* Deal with Problem (2) as documented above. */
559#  if DARWIN_VERS >= DARWIN_10_10
560   assert(size == 64);
561   if (!seg__pagezero) {
562      fail("Can't find __PAGEZERO to modify; can't continue.");
563   }
564   fprintf(stderr, "fixup_macho_loadcmds:   "
565           "changing __PAGEZERO.vmaddr from %p to 0x0.\n",
566           (void*)seg__pagezero->vmaddr);
567   seg__pagezero->vmaddr = 0;
568#  endif
569
570  out:
571   if (ii.img)
572      unmap_image(&ii);
573}
574
575
576static Bool is_plausible_tool_exe_name ( HChar* nm )
577{
578   HChar* p;
579   if (!nm)
580      return False;
581
582   // Does it end with this string?
583   p = strstr(nm, "-x86-darwin");
584   if (p && 0 == strcmp(p, "-x86-darwin"))
585      return True;
586
587   p = strstr(nm, "-amd64-darwin");
588   if (p && 0 == strcmp(p, "-amd64-darwin"))
589      return True;
590
591   return False;
592}
593
594
595int main ( int argc, char** argv )
596{
597   Int   r;
598   ULong req_stack_addr = 0;
599   ULong req_stack_size = 0;
600
601   if (argc != 4)
602      fail("args: -stack_addr-arg -stack_size-arg "
603           "name-of-tool-executable-to-modify");
604
605   r= sscanf(argv[1], "0x%llx", &req_stack_addr);
606   if (r != 1) fail("invalid stack_addr arg");
607
608   r= sscanf(argv[2], "0x%llx", &req_stack_size);
609   if (r != 1) fail("invalid stack_size arg");
610
611   fprintf(stderr, "fixup_macho_loadcmds: "
612           "requested stack_addr (top) 0x%llx, "
613           "stack_size 0x%llx\n", req_stack_addr, req_stack_size );
614
615   if (!is_plausible_tool_exe_name(argv[3]))
616      fail("implausible tool exe name -- not of the form *-{x86,amd64}-darwin");
617
618   fprintf(stderr, "fixup_macho_loadcmds: examining tool exe: %s\n",
619           argv[3] );
620   modify_macho_loadcmds( argv[3], req_stack_addr - req_stack_size,
621                          req_stack_size );
622
623   return 0;
624}
625
626/*
627      cmd LC_SEGMENT_64
628  cmdsize 72
629  segname __LINKEDIT
630   vmaddr 0x0000000138dea000
631   vmsize 0x00000000000ad000
632  fileoff 2658304
633 filesize 705632
634  maxprot 0x00000007
635 initprot 0x00000001
636   nsects 0
637    flags 0x0
638*/
639
640/*
641      cmd LC_SEGMENT_64
642  cmdsize 72
643  segname __UNIXSTACK
644   vmaddr 0x0000000133800000
645   vmsize 0x0000000000800000
646  fileoff 2498560
647 filesize 0
648  maxprot 0x00000007
649 initprot 0x00000003
650   nsects 0
651    flags 0x0
652*/
653