Revision: 81375 http://trac.macports.org/changeset/81375 Author: raimue@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
participants (1)
-
raimue@macports.org