On MacOS X 10.10, when postprocessing tool executables, set the SVMA
of __PAGEZERO to zero.  Without this, the 10.10 kernel refuses to
start these executables.  Based on investigations and a
proof-of-concept implementation by Rhys Kidd (rhyskidd@gmail.com).
Part of bug 339045.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@14697 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am
index 7fb2618..81245e2 100644
--- a/coregrind/Makefile.am
+++ b/coregrind/Makefile.am
@@ -642,7 +642,7 @@
 
 BUILT_SOURCES += fixup_macho_loadcmds
 fixup_macho_loadcmds: fixup_macho_loadcmds.c
-	$(CC) -g -Wall -o $@ $<
+	$(CC) -I $(top_srcdir) -g -Wall -o $@ $<
 
 CLEANFILES += fixup_macho_loadcmds
 
diff --git a/coregrind/fixup_macho_loadcmds.c b/coregrind/fixup_macho_loadcmds.c
index 77524f8..d66ad20 100644
--- a/coregrind/fixup_macho_loadcmds.c
+++ b/coregrind/fixup_macho_loadcmds.c
@@ -6,9 +6,17 @@
 */
 
 /* What does this program do?  In short it postprocesses tool
-   executables on MacOSX, after linking using /usr/bin/ld.  This is so
-   as to work around a bug in the linker on Xcode 4.0.0 and Xcode
-   4.0.1.  Xcode versions prior to 4.0.0 are unaffected.
+   executables on MacOSX, after linking using /usr/bin/ld.
+
+   This is to deal with two separate and entirely unrelated problems.
+   Problem (1) is a bug in the linker in Xcode 4.0.0.  Problem (2) is
+   much newer and concerns linking 64-bit tool executables for
+   Yosemite (10.10).
+
+   --- Problem (1) ------------------------------------------------
+
+   This is a bug in the linker on Xcode 4.0.0 and Xcode 4.0.1.  Xcode
+   versions prior to 4.0.0 are unaffected.
 
    The tracking bug is https://bugs.kde.org/show_bug.cgi?id=267997
 
@@ -70,6 +78,16 @@
    information that would have been in the missing __UNIXSTACK entry.
    I tried this by hand (with a binary editor) earlier and got
    something that worked.
+
+   --- Problem (2) ------------------------------------------------
+
+   On MacOSX 10.10 (Yosemite), the kernel requires all valid
+   executables to have a __PAGEZERO section with SVMA of zero and size
+   of at least one page.  However, our tool executables have a
+   __PAGEZERO section with SVMA set to the requested Valgrind load
+   address (typically 0x1'3800'0000).  And the kernel won't start
+   those.  So we take the opportunity to "fix" this by setting the
+   SVMA to zero.  Seems to work and have no obvious bad side effects.
 */
 
 #define DEBUGPRINTING 0
@@ -83,7 +101,6 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-
 #undef PLAT_x86_darwin
 #undef PLAT_amd64_darwin
 
@@ -100,6 +117,14 @@
 #include <mach-o/fat.h>
 #include <mach/i386/thread_status.h>
 
+/* Get hold of DARWIN_VERS, and check it has a sane value. */
+#include "config.h"
+#if DARWIN_VERS != DARWIN_10_5 && DARWIN_VERS != DARWIN_10_6 \
+    && DARWIN_VERS != DARWIN_10_7 && DARWIN_VERS != DARWIN_10_8 \
+    && DARWIN_VERS != DARWIN_10_9 && DARWIN_VERS != DARWIN_10_10
+#  error "Unknown DARWIN_VERS value.  This file only compiles on Darwin."
+#endif
+
 
 typedef  unsigned char   UChar;
 typedef    signed char   Char;
@@ -375,6 +400,7 @@
    Bool  have_rsp = False;
    struct segment_command_64* seg__unixstack = NULL;
    struct segment_command_64* seg__linkedit  = NULL;
+   struct segment_command_64* seg__pagezero  = NULL;
 
    /* Loop over the load commands and fill in the above 4 variables. */
 
@@ -413,6 +439,7 @@
                   printf("LC_UNIXTHREAD");
                break;
             default:
+               if (DEBUGPRINTING)
                   printf("???");
                fail("unexpected load command in Mach header");
             break;
@@ -450,6 +477,8 @@
                seg__linkedit = seg;
             if (0 == strcmp(seg->segname, "__UNIXSTACK"))
                seg__unixstack = seg;
+            if (0 == strcmp(seg->segname, "__PAGEZERO"))
+               seg__pagezero = seg;
          }
 
       }
@@ -495,7 +524,7 @@
       /* looks ok */
       fprintf(stderr, "fixup_macho_loadcmds:   "
               "acceptable __UNIXSTACK present; no modifications.\n" );
-      goto out;
+      goto maybe_mash_pagezero;
    }
 
    if (seg__linkedit) {
@@ -516,7 +545,7 @@
       seg->maxprot  = 7;
       seg->initprot = 3;
       /* success */
-      goto out;
+      goto maybe_mash_pagezero;
    }
 
    /* out of options */
@@ -524,7 +553,20 @@
         "out of options.");
    /* NOTREACHED */
 
-  out:
+  maybe_mash_pagezero:
+   /* Deal with Problem (2) as documented above. */
+#  if DARWIN_VERS == DARWIN_10_10
+   assert(size == 64);
+   if (!seg__pagezero) {
+      fail("Can't find __PAGEZERO to modify; can't continue.");
+   }
+   fprintf(stderr, "fixup_macho_loadcmds:   "
+           "changing __PAGEZERO.vmaddr from %p to 0x0.\n",
+           (void*)seg__pagezero->vmaddr);
+   seg__pagezero->vmaddr = 0;
+#  endif
+
+  out:   
    if (ii.img)
       unmap_image(&ii);
 }