Ticket #30462: upstream_58577.diff

File upstream_58577.diff, 21.0 KB (added by barry.allard@…, 13 years ago)

Upstream "proposed patch (21.05 KB, patch)" http://bugsfiles.kde.org/attachment.cgi?id=58577

  • coregrind/link_tool_exe_darwin.in

     
    160160    }
    161161}
    162162
    163 #print "link_tool_exe_darwin: $cmd\n";
     163print "link_tool_exe_darwin: $cmd\n";
    164164
    165 
    166165# Execute the command:
    167166my $r = system("$cmd");
    168167
    169 if ($r == 0) {
    170     exit 0;
    171 } else {
    172     exit 1;
     168if ($r != 0) {
     169   exit 1;
    173170}
     171
     172
     173# and now kludge the tool exe
     174# see bug 267997
     175
     176$cmd = "../coregrind/fixup_macho_loadcmds";
     177$cmd = "$cmd $stack_addr_str $stack_size_str $outname";
     178
     179print "link_tool_exe_darwin: $cmd\n";
     180
     181my $r = system("$cmd");
     182
     183if ($r != 0) {
     184   exit 1;
     185}
     186
     187
     188
     189
     190exit 0;
  • coregrind/fixup_macho_loadcmds.c

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

     
    441441
    442442install-exec-local: install-noinst_PROGRAMS install-noinst_DSYMS
    443443
     444#----------------------------------------------------------------------------
     445# Darwin linker kludges
     446#----------------------------------------------------------------------------
     447
     448if VGCONF_OS_IS_DARWIN
     449
     450BUILT_SOURCES += fixup_macho_loadcmds
     451fixup_macho_loadcmds: fixup_macho_loadcmds.c
     452        $(CC) -g -Wall -o fixup_macho_loadcmds fixup_macho_loadcmds.c
     453
     454CLEANFILES += fixup_macho_loadcmds
     455
     456endif
     457
     458EXTRA_DIST += fixup_macho_loadcmds.c