[81375] trunk/dports/devel/valgrind

raimue at macports.org raimue at macports.org
Fri Jul 29 18:07:50 PDT 2011


Revision: 81375
          http://trac.macports.org/changeset/81375
Author:   raimue at macports.org
Date:     2011-07-29 18:07:50 -0700 (Fri, 29 Jul 2011)
Log Message:
-----------
devel/valgrind:
Backport Xcode 4 compatibility, closes #28572

Modified Paths:
--------------
    trunk/dports/devel/valgrind/Portfile

Added Paths:
-----------
    trunk/dports/devel/valgrind/files/
    trunk/dports/devel/valgrind/files/patch-xcode4.diff

Modified: trunk/dports/devel/valgrind/Portfile
===================================================================
--- trunk/dports/devel/valgrind/Portfile	2011-07-30 01:03:45 UTC (rev 81374)
+++ trunk/dports/devel/valgrind/Portfile	2011-07-30 01:07:50 UTC (rev 81375)
@@ -5,6 +5,7 @@
 name            valgrind
 conflicts       valgrind-devel
 version         3.6.1
+revision        1
 categories      devel
 platforms       darwin
 maintainers     raimue
@@ -33,6 +34,8 @@
 # Ignore trace reports about boost, Qt and OpenMP
 # as they are only used for tests
 
+patchfiles      patch-xcode4.diff
+
 configure.args  --mandir=${prefix}/share/man \
                 --without-mpicc
 

Added: trunk/dports/devel/valgrind/files/patch-xcode4.diff
===================================================================
--- trunk/dports/devel/valgrind/files/patch-xcode4.diff	                        (rev 0)
+++ trunk/dports/devel/valgrind/files/patch-xcode4.diff	2011-07-30 01:07:50 UTC (rev 81375)
@@ -0,0 +1,1350 @@
+Index: coregrind/link_tool_exe_darwin.in
+===================================================================
+--- coregrind/link_tool_exe_darwin.in	(revision 11685)
++++ coregrind/link_tool_exe_darwin.in	(revision 11686)
+@@ -160,14 +160,31 @@
+     }
+ }
+ 
+-#print "link_tool_exe_darwin: $cmd\n";
+-
++print "link_tool_exe_darwin: $cmd\n";
+ 
+ # Execute the command:
+ my $r = system("$cmd");
+ 
+-if ($r == 0) {
+-    exit 0;
+-} else {
+-    exit 1;
++if ($r != 0) {
++   exit 1;
+ }
++
++
++# and now kludge the tool exe
++# see bug 267997
++
++$cmd = "../coregrind/fixup_macho_loadcmds";
++$cmd = "$cmd $stack_addr_str $stack_size_str $outname";
++
++print "link_tool_exe_darwin: $cmd\n";
++
++my $r = system("$cmd");
++
++if ($r != 0) {
++   exit 1;
++}
++
++
++
++
++exit 0;
+Index: coregrind/fixup_macho_loadcmds.c
+===================================================================
+--- coregrind/fixup_macho_loadcmds.c	(revision 0)
++++ coregrind/fixup_macho_loadcmds.c	(revision 11686)
+@@ -0,0 +1,605 @@
++
++/* Derived from Valgrind sources, coregrind/m_debuginfo/readmacho.c.
++   GPL 2+ therefore.
++
++   Can be compiled as either a 32- or 64-bit program (doesn't matter).
++*/
++
++/* 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.
++
++   The tracking bug is https://bugs.kde.org/show_bug.cgi?id=267997
++
++   The bug causes 64-bit tool executables to segfault at startup,
++   because:
++
++   Comparing the MachO load commands vs a (working) tool executable
++   that was created by Xcode 3.2.x, it appears that the new linker has
++   partially ignored the build system's request to place the tool
++   executable's stack at a non standard location.  The build system
++   tells the linker "-stack_addr 0x134000000 -stack_size 0x800000".
++
++   With the Xcode 3.2 linker those flags produce two results:
++
++   (1) A load command to allocate the stack at the said location:
++          Load command 3
++                cmd LC_SEGMENT_64
++            cmdsize 72
++            segname __UNIXSTACK
++             vmaddr 0x0000000133800000
++             vmsize 0x0000000000800000
++            fileoff 2285568
++           filesize 0
++            maxprot 0x00000007
++           initprot 0x00000003
++             nsects 0
++              flags 0x0
++
++   (2) A request (in LC_UNIXTHREAD) to set %rsp to the correct value
++       at process startup, 0x134000000.
++
++   With Xcode 4.0.1, (1) is missing but (2) is still present.  The
++   tool executable therefore starts up with %rsp pointing to unmapped
++   memory and faults almost instantly.
++
++   The workaround implemented by this program is documented in comment
++   8 of bug 267997, viz:
++
++   One really sick workaround is to observe that the executables
++   contain a redundant MachO load command:
++
++      Load command 2
++            cmd LC_SEGMENT_64
++        cmdsize 72
++        segname __LINKEDIT
++         vmaddr 0x0000000138dea000
++         vmsize 0x00000000000ad000
++        fileoff 2658304
++       filesize 705632
++        maxprot 0x00000007
++       initprot 0x00000001
++         nsects 0
++          flags 0x0
++
++   The described section presumably contains information intended for
++   the dynamic linker, but is irrelevant because this is a statically
++   linked executable.  Hence it might be possible to postprocess the
++   executables after linking, to overwrite this entry with the
++   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.
++*/
++
++#define DEBUGPRINTING 0
++
++#include <assert.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/mman.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include <fcntl.h>
++
++
++#undef PLAT_x86_darwin
++#undef PLAT_amd64_darwin
++
++#if defined(__APPLE__) && defined(__i386__)
++#  define PLAT_x86_darwin 1
++#elif defined(__APPLE__) && defined(__x86_64__)
++#  define PLAT_amd64_darwin 1
++#else
++#  error "Can't be compiled on this platform"
++#endif
++
++#include <mach-o/loader.h>
++#include <mach-o/nlist.h>
++#include <mach-o/fat.h>
++#include <mach/i386/thread_status.h>
++
++
++typedef  unsigned char   UChar;
++typedef    signed char   Char;
++typedef           char   HChar; /* signfulness depends on host */
++
++typedef  unsigned int    UInt;
++typedef    signed int    Int;
++
++typedef  unsigned char   Bool;
++#define  True   ((Bool)1)
++#define  False  ((Bool)0)
++
++typedef  unsigned long   UWord;
++
++typedef  UWord           SizeT;
++typedef  UWord           Addr;
++
++typedef  unsigned long long int   ULong;
++typedef    signed long long int   Long;
++
++
++
++__attribute__((noreturn))
++void fail ( HChar* msg )
++{
++   fprintf(stderr, "fixup_macho_loadcmds: fail: %s\n", msg);
++   exit(1);
++}
++
++
++/*------------------------------------------------------------*/
++/*---                                                      ---*/
++/*--- Mach-O file mapping/unmapping helpers                ---*/
++/*---                                                      ---*/
++/*------------------------------------------------------------*/
++
++typedef
++   struct {
++      /* These two describe the entire mapped-in ("primary") image,
++         fat headers, kitchen sink, whatnot: the entire file.  The
++         image is mapped into img[0 .. img_szB-1]. */
++      UChar* img;
++      SizeT  img_szB;
++      /* These two describe the Mach-O object of interest, which is
++         presumably somewhere inside the primary image.
++         map_image_aboard() below, which generates this info, will
++         carefully check that the macho_ fields denote a section of
++         memory that falls entirely inside img[0 .. img_szB-1]. */
++      UChar* macho_img;
++      SizeT  macho_img_szB;
++   }
++   ImageInfo;
++
++
++Bool is_macho_object_file( const void* buf, SizeT szB )
++{
++   /* (JRS: the Mach-O headers might not be in this mapped data,
++      because we only mapped a page for this initial check,
++      or at least not very much, and what's at the start of the file
++      is in general a so-called fat header.  The Mach-O object we're
++      interested in could be arbitrarily far along the image, and so
++      we can't assume its header will fall within this page.) */
++
++   /* But we can say that either it's a fat object, in which case it
++      begins with a fat header, or it's unadorned Mach-O, in which
++      case it starts with a normal header.  At least do what checks we
++      can to establish whether or not we're looking at something
++      sane. */
++
++   const struct fat_header*  fh_be = buf;
++   const struct mach_header_64* mh    = buf;
++
++   assert(buf);
++   if (szB < sizeof(struct fat_header))
++      return False;
++   if (ntohl(fh_be->magic) == FAT_MAGIC)
++      return True;
++
++   if (szB < sizeof(struct mach_header_64))
++      return False;
++   if (mh->magic == MH_MAGIC_64)
++      return True;
++
++   return False;
++}
++
++
++/* Unmap an image mapped in by map_image_aboard. */
++static void unmap_image ( /*MOD*/ImageInfo* ii )
++{
++   Int r;
++   assert(ii->img);
++   assert(ii->img_szB > 0);
++   r = munmap( ii->img, ii->img_szB );
++   /* Do we care if this fails?  I suppose so; it would indicate
++      some fairly serious snafu with the mapping of the file. */
++   assert( !r );
++   memset(ii, 0, sizeof(*ii));
++}
++
++
++/* Map a given fat or thin object aboard, find the thin part if
++   necessary, do some checks, and write details of both the fat and
++   thin parts into *ii.  Returns 32 (and leaves the file unmapped) if
++   the thin part is a 32 bit file.  Returns 64 if it's a 64 bit file.
++   Does not return on failure.  Guarantees to return pointers to a
++   valid(ish) Mach-O image if it succeeds. */
++static Int map_image_aboard ( /*OUT*/ImageInfo* ii, HChar* filename )
++{
++   memset(ii, 0, sizeof(*ii));
++
++   /* First off, try to map the thing in. */
++   { SizeT  size;
++     Int r, fd;
++     struct stat stat_buf;
++
++     r = stat(filename, &stat_buf);
++     if (r)
++        fail("Can't stat image (to determine its size)?!");
++     size = stat_buf.st_size;
++
++     fd = open(filename, O_RDWR, 0);
++     if (fd == -1)
++        fail("Can't open image for possible modification!");
++     if (DEBUGPRINTING)
++        printf("size %lu fd %d\n", size, fd);
++     void* v = mmap ( NULL, size, PROT_READ|PROT_WRITE,
++                                  MAP_FILE|MAP_SHARED, fd, 0 );
++     if (v == MAP_FAILED) {
++        perror("mmap failed");
++        fail("Can't mmap image for possible modification!");
++     }
++
++     close(fd);
++
++     ii->img     = (UChar*)v;
++     ii->img_szB = size;
++   }
++
++   /* Now it's mapped in and we have .img and .img_szB set.  Look for
++      the embedded Mach-O object.  If not findable, unmap and fail. */
++   { struct fat_header*  fh_be;
++     struct fat_header   fh;
++     struct mach_header_64* mh;
++     
++     // Assume initially that we have a thin image, and update
++     // these if it turns out to be fat.
++     ii->macho_img     = ii->img;
++     ii->macho_img_szB = ii->img_szB;
++
++     // Check for fat header.
++     if (ii->img_szB < sizeof(struct fat_header))
++        fail("Invalid Mach-O file (0 too small).");
++
++     // Fat header is always BIG-ENDIAN
++     fh_be = (struct fat_header *)ii->img;
++     fh.magic = ntohl(fh_be->magic);
++     fh.nfat_arch = ntohl(fh_be->nfat_arch);
++     if (fh.magic == FAT_MAGIC) {
++        // Look for a good architecture.
++        struct fat_arch *arch_be;
++        struct fat_arch arch;
++        Int f;
++        if (ii->img_szB < sizeof(struct fat_header)
++                          + fh.nfat_arch * sizeof(struct fat_arch))
++           fail("Invalid Mach-O file (1 too small).");
++
++        for (f = 0, arch_be = (struct fat_arch *)(fh_be+1); 
++             f < fh.nfat_arch;
++             f++, arch_be++) {
++           Int cputype;
++#          if defined(PLAT_x86_darwin)
++           cputype = CPU_TYPE_X86;
++#          elif defined(PLAT_amd64_darwin)
++           cputype = CPU_TYPE_X86_64;
++#          else
++#            error "unknown architecture"
++#          endif
++           arch.cputype    = ntohl(arch_be->cputype);
++           arch.cpusubtype = ntohl(arch_be->cpusubtype);
++           arch.offset     = ntohl(arch_be->offset);
++           arch.size       = ntohl(arch_be->size);
++           if (arch.cputype == cputype) {
++              if (ii->img_szB < arch.offset + arch.size)
++                 fail("Invalid Mach-O file (2 too small).");
++              ii->macho_img     = ii->img + arch.offset;
++              ii->macho_img_szB = arch.size;
++              break;
++           }
++        }
++        if (f == fh.nfat_arch)
++           fail("No acceptable architecture found in fat file.");
++     }
++
++     /* Sanity check what we found. */
++
++     /* assured by logic above */
++     assert(ii->img_szB >= sizeof(struct fat_header));
++
++     if (ii->macho_img_szB < sizeof(struct mach_header_64))
++        fail("Invalid Mach-O file (3 too small).");
++
++     if (ii->macho_img_szB > ii->img_szB)
++        fail("Invalid Mach-O file (thin bigger than fat).");
++
++     if (ii->macho_img >= ii->img
++         && ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB) {
++        /* thin entirely within fat, as expected */
++     } else {
++        fail("Invalid Mach-O file (thin not inside fat).");
++     }
++
++     mh = (struct mach_header_64 *)ii->macho_img;
++     if (mh->magic == MH_MAGIC) {
++        assert(ii->img);
++        assert(ii->macho_img);
++        assert(ii->img_szB > 0);
++        assert(ii->macho_img_szB > 0);
++        assert(ii->macho_img >= ii->img);
++        assert(ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB);
++        return 32;
++     }
++     if (mh->magic != MH_MAGIC_64)
++        fail("Invalid Mach-O file (bad magic).");
++
++     if (ii->macho_img_szB < sizeof(struct mach_header_64) + mh->sizeofcmds)
++        fail("Invalid Mach-O file (4 too small).");
++   }
++
++   assert(ii->img);
++   assert(ii->macho_img);
++   assert(ii->img_szB > 0);
++   assert(ii->macho_img_szB > 0);
++   assert(ii->macho_img >= ii->img);
++   assert(ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB);
++   return 64;
++}
++
++
++/*------------------------------------------------------------*/
++/*---                                                      ---*/
++/*--- Mach-O top-level processing                          ---*/
++/*---                                                      ---*/
++/*------------------------------------------------------------*/
++
++void modify_macho_loadcmds ( HChar* filename,
++                             ULong  expected_stack_start,
++                             ULong  expected_stack_size )
++{
++   ImageInfo ii;
++   memset(&ii, 0, sizeof(ii));
++
++   Int size = map_image_aboard( &ii, filename );
++   if (size == 32) {
++      fprintf(stderr, "fixup_macho_loadcmds:   Is 32-bit MachO file;"
++              " no modifications needed.\n");
++      goto out;
++   }
++
++   assert(size == 64);
++
++   assert(ii.macho_img != NULL && ii.macho_img_szB > 0);
++
++   /* Poke around in the Mach-O header, to find some important
++      stuff.
++      * the location of the __UNIXSTACK load command, if any
++      * the location of the __LINKEDIT load command, if any
++      * the initial RSP value as stated in the LC_UNIXTHREAD
++   */
++
++   /* The collected data */
++   ULong init_rsp = 0;
++   Bool  have_rsp = False;
++   struct segment_command_64* seg__unixstack = NULL;
++   struct segment_command_64* seg__linkedit  = NULL;
++
++   /* Loop over the load commands and fill in the above 4 variables. */
++
++   { struct mach_header_64 *mh = (struct mach_header_64 *)ii.macho_img;
++      struct load_command *cmd;
++      Int c;
++
++      for (c = 0, cmd = (struct load_command *)(mh+1);
++           c < mh->ncmds;
++           c++, cmd = (struct load_command *)(cmd->cmdsize
++                                              + (unsigned long)cmd)) {
++         if (DEBUGPRINTING)
++            printf("load cmd: offset %4lu   size %3d   kind %2d = ",
++                   (unsigned long)((UChar*)cmd - (UChar*)ii.macho_img),
++                   cmd->cmdsize, cmd->cmd);
++
++         switch (cmd->cmd) {
++            case LC_SEGMENT_64:
++               if (DEBUGPRINTING)
++                  printf("LC_SEGMENT_64");
++               break;
++            case LC_SYMTAB:
++               if (DEBUGPRINTING)
++                  printf("LC_SYMTAB");
++               break;
++            case LC_UUID:
++               if (DEBUGPRINTING)
++                  printf("LC_UUID");
++               break;
++            case LC_UNIXTHREAD:
++               if (DEBUGPRINTING)
++                  printf("LC_UNIXTHREAD");
++               break;
++            default:
++                  printf("???");
++               fail("unexpected load command in Mach header");
++            break;
++         }
++         if (DEBUGPRINTING)
++            printf("\n");
++
++         /* Note what the stated initial RSP value is, so we can
++            check it is as expected. */
++         if (cmd->cmd == LC_UNIXTHREAD) {
++            struct thread_command* tcmd = (struct thread_command*)cmd;
++            UInt* w32s = (UInt*)( (UChar*)tcmd + sizeof(*tcmd) );
++            if (DEBUGPRINTING)
++               printf("UnixThread: flavor %u = ", w32s[0]);
++            if (w32s[0] == x86_THREAD_STATE64 && !have_rsp) {
++               if (DEBUGPRINTING)
++                  printf("x86_THREAD_STATE64\n");
++               x86_thread_state64_t* state64
++                  = (x86_thread_state64_t*)(&w32s[2]);
++               have_rsp = True;
++               init_rsp = state64->__rsp;
++               if (DEBUGPRINTING)
++                  printf("rsp = 0x%llx\n", init_rsp);
++            } else {
++               if (DEBUGPRINTING)
++                  printf("???");
++            }
++            if (DEBUGPRINTING)
++               printf("\n");
++         }
++
++         if (cmd->cmd == LC_SEGMENT_64) {
++            struct segment_command_64 *seg = (struct segment_command_64 *)cmd;
++            if (0 == strcmp(seg->segname, "__LINKEDIT"))
++               seg__linkedit = seg;
++            if (0 == strcmp(seg->segname, "__UNIXSTACK"))
++               seg__unixstack = seg;
++         }
++
++      }
++   }
++
++   /*
++      Actions are then as follows:
++
++      * (always) check the RSP value is as expected, and abort if not
++
++      * if there's a UNIXSTACK load command, check it is as expected.
++        If not abort, if yes, do nothing more.
++
++      * (so there's no UNIXSTACK load command).  if there's a LINKEDIT
++        load command, check if it is minimally usable (has 0 for
++        nsects and flags).  If yes, convert it to a UNIXSTACK load
++        command.  If there is none, or is unusable, then we're out of
++        options and have to abort.
++   */
++   if (!have_rsp)
++      fail("Can't find / check initial RSP setting");
++   if (init_rsp != expected_stack_start + expected_stack_size)
++      fail("Initial RSP value not as expected");
++
++   fprintf(stderr, "fixup_macho_loadcmds:   "
++                   "initial RSP is as expected (0x%llx)\n",
++                   expected_stack_start + expected_stack_size );
++
++   if (seg__unixstack) {
++      struct segment_command_64 *seg = seg__unixstack;
++      if (seg->vmaddr != expected_stack_start)
++         fail("has __UNIXSTACK, but wrong ::vmaddr");
++      if (seg->vmsize != expected_stack_size)
++         fail("has __UNIXSTACK, but wrong ::vmsize");
++      if (seg->maxprot != 7)
++         fail("has __UNIXSTACK, but wrong ::maxprot (should be 7)");
++      if (seg->initprot != 3)
++         fail("has __UNIXSTACK, but wrong ::initprot (should be 3)");
++      if (seg->nsects != 0)
++         fail("has __UNIXSTACK, but wrong ::nsects (should be 0)");
++      if (seg->flags != 0)
++         fail("has __UNIXSTACK, but wrong ::flags (should be 0)");
++      /* looks ok */
++      fprintf(stderr, "fixup_macho_loadcmds:   "
++              "acceptable __UNIXSTACK present; no modifications.\n" );
++      goto out;
++   }
++
++   if (seg__linkedit) {
++      struct segment_command_64 *seg = seg__linkedit;
++      if (seg->nsects != 0)
++         fail("has __LINKEDIT, but wrong ::nsects (should be 0)");
++      if (seg->flags != 0)
++         fail("has __LINKEDIT, but wrong ::flags (should be 0)");
++      fprintf(stderr, "fixup_macho_loadcmds:   "
++              "no __UNIXSTACK present.\n" );
++      fprintf(stderr, "fixup_macho_loadcmds:   "
++              "converting __LINKEDIT to __UNIXSTACK.\n" );
++      strcpy(seg->segname, "__UNIXSTACK");
++      seg->vmaddr   = expected_stack_start;
++      seg->vmsize   = expected_stack_size;
++      seg->fileoff  = 0;
++      seg->filesize = 0;
++      seg->maxprot  = 7;
++      seg->initprot = 3;
++      /* success */
++      goto out;
++   }
++
++   /* out of options */
++   fail("no __UNIXSTACK found and no usable __LINKEDIT found; "
++        "out of options.");
++   /* NOTREACHED */
++
++  out:
++   if (ii.img)
++      unmap_image(&ii);
++}
++
++
++static Bool is_plausible_tool_exe_name ( HChar* nm )
++{
++   HChar* p;
++   if (!nm)
++      return False;
++
++   // Does it end with this string?
++   p = strstr(nm, "-x86-darwin");
++   if (p && 0 == strcmp(p, "-x86-darwin"))
++      return True;
++
++   p = strstr(nm, "-amd64-darwin");
++   if (p && 0 == strcmp(p, "-amd64-darwin"))
++      return True;
++
++   return False;
++}
++
++
++int main ( int argc, char** argv )
++{
++   Int   r;
++   ULong req_stack_addr = 0;
++   ULong req_stack_size = 0;
++
++   if (argc != 4)
++      fail("args: -stack_addr-arg -stack_size-arg "
++           "name-of-tool-executable-to-modify"); 
++
++   r= sscanf(argv[1], "0x%llx", &req_stack_addr);
++   if (r != 1) fail("invalid stack_addr arg");
++
++   r= sscanf(argv[2], "0x%llx", &req_stack_size);
++   if (r != 1) fail("invalid stack_size arg");
++
++   fprintf(stderr, "fixup_macho_loadcmds: "
++           "requested stack_addr (top) 0x%llx, "
++           "stack_size 0x%llx\n", req_stack_addr, req_stack_size );
++
++   if (!is_plausible_tool_exe_name(argv[3]))
++      fail("implausible tool exe name -- not of the form *-{x86,amd64}-darwin");
++
++   fprintf(stderr, "fixup_macho_loadcmds: examining tool exe: %s\n", 
++           argv[3] );
++   modify_macho_loadcmds( argv[3], req_stack_addr - req_stack_size,
++                          req_stack_size );
++
++   return 0;
++}
++
++/*
++      cmd LC_SEGMENT_64
++  cmdsize 72
++  segname __LINKEDIT
++   vmaddr 0x0000000138dea000
++   vmsize 0x00000000000ad000
++  fileoff 2658304
++ filesize 705632
++  maxprot 0x00000007
++ initprot 0x00000001
++   nsects 0
++    flags 0x0
++*/
++
++/*
++      cmd LC_SEGMENT_64
++  cmdsize 72
++  segname __UNIXSTACK
++   vmaddr 0x0000000133800000
++   vmsize 0x0000000000800000
++  fileoff 2498560
++ filesize 0
++  maxprot 0x00000007
++ initprot 0x00000003
++   nsects 0
++    flags 0x0
++*/
+Index: coregrind/Makefile.am
+===================================================================
+--- coregrind/Makefile.am	(revision 11685)
++++ coregrind/Makefile.am	(revision 11686)
+@@ -441,3 +441,18 @@
+ 
+ install-exec-local: install-noinst_PROGRAMS install-noinst_DSYMS
+ 
++#----------------------------------------------------------------------------
++# Darwin linker kludges
++#----------------------------------------------------------------------------
++
++if VGCONF_OS_IS_DARWIN
++
++BUILT_SOURCES += fixup_macho_loadcmds
++fixup_macho_loadcmds: fixup_macho_loadcmds.c
++	$(CC) -g -Wall -o fixup_macho_loadcmds fixup_macho_loadcmds.c
++
++CLEANFILES += fixup_macho_loadcmds
++
++endif
++
++EXTRA_DIST += fixup_macho_loadcmds.c
+Index: coregrind/link_tool_exe_darwin.in
+===================================================================
+--- coregrind/link_tool_exe_darwin.in	(revision 11685)
++++ coregrind/link_tool_exe_darwin.in	(revision 11686)
+@@ -160,14 +160,31 @@
+     }
+ }
+ 
+-#print "link_tool_exe_darwin: $cmd\n";
+-
++print "link_tool_exe_darwin: $cmd\n";
+ 
+ # Execute the command:
+ my $r = system("$cmd");
+ 
+-if ($r == 0) {
+-    exit 0;
+-} else {
+-    exit 1;
++if ($r != 0) {
++   exit 1;
+ }
++
++
++# and now kludge the tool exe
++# see bug 267997
++
++$cmd = "../coregrind/fixup_macho_loadcmds";
++$cmd = "$cmd $stack_addr_str $stack_size_str $outname";
++
++print "link_tool_exe_darwin: $cmd\n";
++
++my $r = system("$cmd");
++
++if ($r != 0) {
++   exit 1;
++}
++
++
++
++
++exit 0;
+Index: coregrind/fixup_macho_loadcmds.c
+===================================================================
+--- coregrind/fixup_macho_loadcmds.c	(revision 0)
++++ coregrind/fixup_macho_loadcmds.c	(revision 11686)
+@@ -0,0 +1,605 @@
++
++/* Derived from Valgrind sources, coregrind/m_debuginfo/readmacho.c.
++   GPL 2+ therefore.
++
++   Can be compiled as either a 32- or 64-bit program (doesn't matter).
++*/
++
++/* 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.
++
++   The tracking bug is https://bugs.kde.org/show_bug.cgi?id=267997
++
++   The bug causes 64-bit tool executables to segfault at startup,
++   because:
++
++   Comparing the MachO load commands vs a (working) tool executable
++   that was created by Xcode 3.2.x, it appears that the new linker has
++   partially ignored the build system's request to place the tool
++   executable's stack at a non standard location.  The build system
++   tells the linker "-stack_addr 0x134000000 -stack_size 0x800000".
++
++   With the Xcode 3.2 linker those flags produce two results:
++
++   (1) A load command to allocate the stack at the said location:
++          Load command 3
++                cmd LC_SEGMENT_64
++            cmdsize 72
++            segname __UNIXSTACK
++             vmaddr 0x0000000133800000
++             vmsize 0x0000000000800000
++            fileoff 2285568
++           filesize 0
++            maxprot 0x00000007
++           initprot 0x00000003
++             nsects 0
++              flags 0x0
++
++   (2) A request (in LC_UNIXTHREAD) to set %rsp to the correct value
++       at process startup, 0x134000000.
++
++   With Xcode 4.0.1, (1) is missing but (2) is still present.  The
++   tool executable therefore starts up with %rsp pointing to unmapped
++   memory and faults almost instantly.
++
++   The workaround implemented by this program is documented in comment
++   8 of bug 267997, viz:
++
++   One really sick workaround is to observe that the executables
++   contain a redundant MachO load command:
++
++      Load command 2
++            cmd LC_SEGMENT_64
++        cmdsize 72
++        segname __LINKEDIT
++         vmaddr 0x0000000138dea000
++         vmsize 0x00000000000ad000
++        fileoff 2658304
++       filesize 705632
++        maxprot 0x00000007
++       initprot 0x00000001
++         nsects 0
++          flags 0x0
++
++   The described section presumably contains information intended for
++   the dynamic linker, but is irrelevant because this is a statically
++   linked executable.  Hence it might be possible to postprocess the
++   executables after linking, to overwrite this entry with the
++   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.
++*/
++
++#define DEBUGPRINTING 0
++
++#include <assert.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/mman.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include <fcntl.h>
++
++
++#undef PLAT_x86_darwin
++#undef PLAT_amd64_darwin
++
++#if defined(__APPLE__) && defined(__i386__)
++#  define PLAT_x86_darwin 1
++#elif defined(__APPLE__) && defined(__x86_64__)
++#  define PLAT_amd64_darwin 1
++#else
++#  error "Can't be compiled on this platform"
++#endif
++
++#include <mach-o/loader.h>
++#include <mach-o/nlist.h>
++#include <mach-o/fat.h>
++#include <mach/i386/thread_status.h>
++
++
++typedef  unsigned char   UChar;
++typedef    signed char   Char;
++typedef           char   HChar; /* signfulness depends on host */
++
++typedef  unsigned int    UInt;
++typedef    signed int    Int;
++
++typedef  unsigned char   Bool;
++#define  True   ((Bool)1)
++#define  False  ((Bool)0)
++
++typedef  unsigned long   UWord;
++
++typedef  UWord           SizeT;
++typedef  UWord           Addr;
++
++typedef  unsigned long long int   ULong;
++typedef    signed long long int   Long;
++
++
++
++__attribute__((noreturn))
++void fail ( HChar* msg )
++{
++   fprintf(stderr, "fixup_macho_loadcmds: fail: %s\n", msg);
++   exit(1);
++}
++
++
++/*------------------------------------------------------------*/
++/*---                                                      ---*/
++/*--- Mach-O file mapping/unmapping helpers                ---*/
++/*---                                                      ---*/
++/*------------------------------------------------------------*/
++
++typedef
++   struct {
++      /* These two describe the entire mapped-in ("primary") image,
++         fat headers, kitchen sink, whatnot: the entire file.  The
++         image is mapped into img[0 .. img_szB-1]. */
++      UChar* img;
++      SizeT  img_szB;
++      /* These two describe the Mach-O object of interest, which is
++         presumably somewhere inside the primary image.
++         map_image_aboard() below, which generates this info, will
++         carefully check that the macho_ fields denote a section of
++         memory that falls entirely inside img[0 .. img_szB-1]. */
++      UChar* macho_img;
++      SizeT  macho_img_szB;
++   }
++   ImageInfo;
++
++
++Bool is_macho_object_file( const void* buf, SizeT szB )
++{
++   /* (JRS: the Mach-O headers might not be in this mapped data,
++      because we only mapped a page for this initial check,
++      or at least not very much, and what's at the start of the file
++      is in general a so-called fat header.  The Mach-O object we're
++      interested in could be arbitrarily far along the image, and so
++      we can't assume its header will fall within this page.) */
++
++   /* But we can say that either it's a fat object, in which case it
++      begins with a fat header, or it's unadorned Mach-O, in which
++      case it starts with a normal header.  At least do what checks we
++      can to establish whether or not we're looking at something
++      sane. */
++
++   const struct fat_header*  fh_be = buf;
++   const struct mach_header_64* mh    = buf;
++
++   assert(buf);
++   if (szB < sizeof(struct fat_header))
++      return False;
++   if (ntohl(fh_be->magic) == FAT_MAGIC)
++      return True;
++
++   if (szB < sizeof(struct mach_header_64))
++      return False;
++   if (mh->magic == MH_MAGIC_64)
++      return True;
++
++   return False;
++}
++
++
++/* Unmap an image mapped in by map_image_aboard. */
++static void unmap_image ( /*MOD*/ImageInfo* ii )
++{
++   Int r;
++   assert(ii->img);
++   assert(ii->img_szB > 0);
++   r = munmap( ii->img, ii->img_szB );
++   /* Do we care if this fails?  I suppose so; it would indicate
++      some fairly serious snafu with the mapping of the file. */
++   assert( !r );
++   memset(ii, 0, sizeof(*ii));
++}
++
++
++/* Map a given fat or thin object aboard, find the thin part if
++   necessary, do some checks, and write details of both the fat and
++   thin parts into *ii.  Returns 32 (and leaves the file unmapped) if
++   the thin part is a 32 bit file.  Returns 64 if it's a 64 bit file.
++   Does not return on failure.  Guarantees to return pointers to a
++   valid(ish) Mach-O image if it succeeds. */
++static Int map_image_aboard ( /*OUT*/ImageInfo* ii, HChar* filename )
++{
++   memset(ii, 0, sizeof(*ii));
++
++   /* First off, try to map the thing in. */
++   { SizeT  size;
++     Int r, fd;
++     struct stat stat_buf;
++
++     r = stat(filename, &stat_buf);
++     if (r)
++        fail("Can't stat image (to determine its size)?!");
++     size = stat_buf.st_size;
++
++     fd = open(filename, O_RDWR, 0);
++     if (fd == -1)
++        fail("Can't open image for possible modification!");
++     if (DEBUGPRINTING)
++        printf("size %lu fd %d\n", size, fd);
++     void* v = mmap ( NULL, size, PROT_READ|PROT_WRITE,
++                                  MAP_FILE|MAP_SHARED, fd, 0 );
++     if (v == MAP_FAILED) {
++        perror("mmap failed");
++        fail("Can't mmap image for possible modification!");
++     }
++
++     close(fd);
++
++     ii->img     = (UChar*)v;
++     ii->img_szB = size;
++   }
++
++   /* Now it's mapped in and we have .img and .img_szB set.  Look for
++      the embedded Mach-O object.  If not findable, unmap and fail. */
++   { struct fat_header*  fh_be;
++     struct fat_header   fh;
++     struct mach_header_64* mh;
++     
++     // Assume initially that we have a thin image, and update
++     // these if it turns out to be fat.
++     ii->macho_img     = ii->img;
++     ii->macho_img_szB = ii->img_szB;
++
++     // Check for fat header.
++     if (ii->img_szB < sizeof(struct fat_header))
++        fail("Invalid Mach-O file (0 too small).");
++
++     // Fat header is always BIG-ENDIAN
++     fh_be = (struct fat_header *)ii->img;
++     fh.magic = ntohl(fh_be->magic);
++     fh.nfat_arch = ntohl(fh_be->nfat_arch);
++     if (fh.magic == FAT_MAGIC) {
++        // Look for a good architecture.
++        struct fat_arch *arch_be;
++        struct fat_arch arch;
++        Int f;
++        if (ii->img_szB < sizeof(struct fat_header)
++                          + fh.nfat_arch * sizeof(struct fat_arch))
++           fail("Invalid Mach-O file (1 too small).");
++
++        for (f = 0, arch_be = (struct fat_arch *)(fh_be+1); 
++             f < fh.nfat_arch;
++             f++, arch_be++) {
++           Int cputype;
++#          if defined(PLAT_x86_darwin)
++           cputype = CPU_TYPE_X86;
++#          elif defined(PLAT_amd64_darwin)
++           cputype = CPU_TYPE_X86_64;
++#          else
++#            error "unknown architecture"
++#          endif
++           arch.cputype    = ntohl(arch_be->cputype);
++           arch.cpusubtype = ntohl(arch_be->cpusubtype);
++           arch.offset     = ntohl(arch_be->offset);
++           arch.size       = ntohl(arch_be->size);
++           if (arch.cputype == cputype) {
++              if (ii->img_szB < arch.offset + arch.size)
++                 fail("Invalid Mach-O file (2 too small).");
++              ii->macho_img     = ii->img + arch.offset;
++              ii->macho_img_szB = arch.size;
++              break;
++           }
++        }
++        if (f == fh.nfat_arch)
++           fail("No acceptable architecture found in fat file.");
++     }
++
++     /* Sanity check what we found. */
++
++     /* assured by logic above */
++     assert(ii->img_szB >= sizeof(struct fat_header));
++
++     if (ii->macho_img_szB < sizeof(struct mach_header_64))
++        fail("Invalid Mach-O file (3 too small).");
++
++     if (ii->macho_img_szB > ii->img_szB)
++        fail("Invalid Mach-O file (thin bigger than fat).");
++
++     if (ii->macho_img >= ii->img
++         && ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB) {
++        /* thin entirely within fat, as expected */
++     } else {
++        fail("Invalid Mach-O file (thin not inside fat).");
++     }
++
++     mh = (struct mach_header_64 *)ii->macho_img;
++     if (mh->magic == MH_MAGIC) {
++        assert(ii->img);
++        assert(ii->macho_img);
++        assert(ii->img_szB > 0);
++        assert(ii->macho_img_szB > 0);
++        assert(ii->macho_img >= ii->img);
++        assert(ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB);
++        return 32;
++     }
++     if (mh->magic != MH_MAGIC_64)
++        fail("Invalid Mach-O file (bad magic).");
++
++     if (ii->macho_img_szB < sizeof(struct mach_header_64) + mh->sizeofcmds)
++        fail("Invalid Mach-O file (4 too small).");
++   }
++
++   assert(ii->img);
++   assert(ii->macho_img);
++   assert(ii->img_szB > 0);
++   assert(ii->macho_img_szB > 0);
++   assert(ii->macho_img >= ii->img);
++   assert(ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB);
++   return 64;
++}
++
++
++/*------------------------------------------------------------*/
++/*---                                                      ---*/
++/*--- Mach-O top-level processing                          ---*/
++/*---                                                      ---*/
++/*------------------------------------------------------------*/
++
++void modify_macho_loadcmds ( HChar* filename,
++                             ULong  expected_stack_start,
++                             ULong  expected_stack_size )
++{
++   ImageInfo ii;
++   memset(&ii, 0, sizeof(ii));
++
++   Int size = map_image_aboard( &ii, filename );
++   if (size == 32) {
++      fprintf(stderr, "fixup_macho_loadcmds:   Is 32-bit MachO file;"
++              " no modifications needed.\n");
++      goto out;
++   }
++
++   assert(size == 64);
++
++   assert(ii.macho_img != NULL && ii.macho_img_szB > 0);
++
++   /* Poke around in the Mach-O header, to find some important
++      stuff.
++      * the location of the __UNIXSTACK load command, if any
++      * the location of the __LINKEDIT load command, if any
++      * the initial RSP value as stated in the LC_UNIXTHREAD
++   */
++
++   /* The collected data */
++   ULong init_rsp = 0;
++   Bool  have_rsp = False;
++   struct segment_command_64* seg__unixstack = NULL;
++   struct segment_command_64* seg__linkedit  = NULL;
++
++   /* Loop over the load commands and fill in the above 4 variables. */
++
++   { struct mach_header_64 *mh = (struct mach_header_64 *)ii.macho_img;
++      struct load_command *cmd;
++      Int c;
++
++      for (c = 0, cmd = (struct load_command *)(mh+1);
++           c < mh->ncmds;
++           c++, cmd = (struct load_command *)(cmd->cmdsize
++                                              + (unsigned long)cmd)) {
++         if (DEBUGPRINTING)
++            printf("load cmd: offset %4lu   size %3d   kind %2d = ",
++                   (unsigned long)((UChar*)cmd - (UChar*)ii.macho_img),
++                   cmd->cmdsize, cmd->cmd);
++
++         switch (cmd->cmd) {
++            case LC_SEGMENT_64:
++               if (DEBUGPRINTING)
++                  printf("LC_SEGMENT_64");
++               break;
++            case LC_SYMTAB:
++               if (DEBUGPRINTING)
++                  printf("LC_SYMTAB");
++               break;
++            case LC_UUID:
++               if (DEBUGPRINTING)
++                  printf("LC_UUID");
++               break;
++            case LC_UNIXTHREAD:
++               if (DEBUGPRINTING)
++                  printf("LC_UNIXTHREAD");
++               break;
++            default:
++                  printf("???");
++               fail("unexpected load command in Mach header");
++            break;
++         }
++         if (DEBUGPRINTING)
++            printf("\n");
++
++         /* Note what the stated initial RSP value is, so we can
++            check it is as expected. */
++         if (cmd->cmd == LC_UNIXTHREAD) {
++            struct thread_command* tcmd = (struct thread_command*)cmd;
++            UInt* w32s = (UInt*)( (UChar*)tcmd + sizeof(*tcmd) );
++            if (DEBUGPRINTING)
++               printf("UnixThread: flavor %u = ", w32s[0]);
++            if (w32s[0] == x86_THREAD_STATE64 && !have_rsp) {
++               if (DEBUGPRINTING)
++                  printf("x86_THREAD_STATE64\n");
++               x86_thread_state64_t* state64
++                  = (x86_thread_state64_t*)(&w32s[2]);
++               have_rsp = True;
++               init_rsp = state64->__rsp;
++               if (DEBUGPRINTING)
++                  printf("rsp = 0x%llx\n", init_rsp);
++            } else {
++               if (DEBUGPRINTING)
++                  printf("???");
++            }
++            if (DEBUGPRINTING)
++               printf("\n");
++         }
++
++         if (cmd->cmd == LC_SEGMENT_64) {
++            struct segment_command_64 *seg = (struct segment_command_64 *)cmd;
++            if (0 == strcmp(seg->segname, "__LINKEDIT"))
++               seg__linkedit = seg;
++            if (0 == strcmp(seg->segname, "__UNIXSTACK"))
++               seg__unixstack = seg;
++         }
++
++      }
++   }
++
++   /*
++      Actions are then as follows:
++
++      * (always) check the RSP value is as expected, and abort if not
++
++      * if there's a UNIXSTACK load command, check it is as expected.
++        If not abort, if yes, do nothing more.
++
++      * (so there's no UNIXSTACK load command).  if there's a LINKEDIT
++        load command, check if it is minimally usable (has 0 for
++        nsects and flags).  If yes, convert it to a UNIXSTACK load
++        command.  If there is none, or is unusable, then we're out of
++        options and have to abort.
++   */
++   if (!have_rsp)
++      fail("Can't find / check initial RSP setting");
++   if (init_rsp != expected_stack_start + expected_stack_size)
++      fail("Initial RSP value not as expected");
++
++   fprintf(stderr, "fixup_macho_loadcmds:   "
++                   "initial RSP is as expected (0x%llx)\n",
++                   expected_stack_start + expected_stack_size );
++
++   if (seg__unixstack) {
++      struct segment_command_64 *seg = seg__unixstack;
++      if (seg->vmaddr != expected_stack_start)
++         fail("has __UNIXSTACK, but wrong ::vmaddr");
++      if (seg->vmsize != expected_stack_size)
++         fail("has __UNIXSTACK, but wrong ::vmsize");
++      if (seg->maxprot != 7)
++         fail("has __UNIXSTACK, but wrong ::maxprot (should be 7)");
++      if (seg->initprot != 3)
++         fail("has __UNIXSTACK, but wrong ::initprot (should be 3)");
++      if (seg->nsects != 0)
++         fail("has __UNIXSTACK, but wrong ::nsects (should be 0)");
++      if (seg->flags != 0)
++         fail("has __UNIXSTACK, but wrong ::flags (should be 0)");
++      /* looks ok */
++      fprintf(stderr, "fixup_macho_loadcmds:   "
++              "acceptable __UNIXSTACK present; no modifications.\n" );
++      goto out;
++   }
++
++   if (seg__linkedit) {
++      struct segment_command_64 *seg = seg__linkedit;
++      if (seg->nsects != 0)
++         fail("has __LINKEDIT, but wrong ::nsects (should be 0)");
++      if (seg->flags != 0)
++         fail("has __LINKEDIT, but wrong ::flags (should be 0)");
++      fprintf(stderr, "fixup_macho_loadcmds:   "
++              "no __UNIXSTACK present.\n" );
++      fprintf(stderr, "fixup_macho_loadcmds:   "
++              "converting __LINKEDIT to __UNIXSTACK.\n" );
++      strcpy(seg->segname, "__UNIXSTACK");
++      seg->vmaddr   = expected_stack_start;
++      seg->vmsize   = expected_stack_size;
++      seg->fileoff  = 0;
++      seg->filesize = 0;
++      seg->maxprot  = 7;
++      seg->initprot = 3;
++      /* success */
++      goto out;
++   }
++
++   /* out of options */
++   fail("no __UNIXSTACK found and no usable __LINKEDIT found; "
++        "out of options.");
++   /* NOTREACHED */
++
++  out:
++   if (ii.img)
++      unmap_image(&ii);
++}
++
++
++static Bool is_plausible_tool_exe_name ( HChar* nm )
++{
++   HChar* p;
++   if (!nm)
++      return False;
++
++   // Does it end with this string?
++   p = strstr(nm, "-x86-darwin");
++   if (p && 0 == strcmp(p, "-x86-darwin"))
++      return True;
++
++   p = strstr(nm, "-amd64-darwin");
++   if (p && 0 == strcmp(p, "-amd64-darwin"))
++      return True;
++
++   return False;
++}
++
++
++int main ( int argc, char** argv )
++{
++   Int   r;
++   ULong req_stack_addr = 0;
++   ULong req_stack_size = 0;
++
++   if (argc != 4)
++      fail("args: -stack_addr-arg -stack_size-arg "
++           "name-of-tool-executable-to-modify"); 
++
++   r= sscanf(argv[1], "0x%llx", &req_stack_addr);
++   if (r != 1) fail("invalid stack_addr arg");
++
++   r= sscanf(argv[2], "0x%llx", &req_stack_size);
++   if (r != 1) fail("invalid stack_size arg");
++
++   fprintf(stderr, "fixup_macho_loadcmds: "
++           "requested stack_addr (top) 0x%llx, "
++           "stack_size 0x%llx\n", req_stack_addr, req_stack_size );
++
++   if (!is_plausible_tool_exe_name(argv[3]))
++      fail("implausible tool exe name -- not of the form *-{x86,amd64}-darwin");
++
++   fprintf(stderr, "fixup_macho_loadcmds: examining tool exe: %s\n", 
++           argv[3] );
++   modify_macho_loadcmds( argv[3], req_stack_addr - req_stack_size,
++                          req_stack_size );
++
++   return 0;
++}
++
++/*
++      cmd LC_SEGMENT_64
++  cmdsize 72
++  segname __LINKEDIT
++   vmaddr 0x0000000138dea000
++   vmsize 0x00000000000ad000
++  fileoff 2658304
++ filesize 705632
++  maxprot 0x00000007
++ initprot 0x00000001
++   nsects 0
++    flags 0x0
++*/
++
++/*
++      cmd LC_SEGMENT_64
++  cmdsize 72
++  segname __UNIXSTACK
++   vmaddr 0x0000000133800000
++   vmsize 0x0000000000800000
++  fileoff 2498560
++ filesize 0
++  maxprot 0x00000007
++ initprot 0x00000003
++   nsects 0
++    flags 0x0
++*/
+Index: coregrind/Makefile.am
+===================================================================
+--- coregrind/Makefile.am	(revision 11685)
++++ coregrind/Makefile.am	(revision 11686)
+@@ -441,3 +441,18 @@
+ 
+ install-exec-local: install-noinst_PROGRAMS install-noinst_DSYMS
+ 
++#----------------------------------------------------------------------------
++# Darwin linker kludges
++#----------------------------------------------------------------------------
++
++if VGCONF_OS_IS_DARWIN
++
++BUILT_SOURCES += fixup_macho_loadcmds
++fixup_macho_loadcmds: fixup_macho_loadcmds.c
++	$(CC) -g -Wall -o fixup_macho_loadcmds fixup_macho_loadcmds.c
++
++CLEANFILES += fixup_macho_loadcmds
++
++endif
++
++EXTRA_DIST += fixup_macho_loadcmds.c
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macports-changes/attachments/20110729/5ef77b99/attachment-0001.html>


More information about the macports-changes mailing list