108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/* Derived from Valgrind sources, coregrind/m_debuginfo/readmacho.c.
308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   GPL 2+ therefore.
408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   Can be compiled as either a 32- or 64-bit program (doesn't matter).
608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj*/
708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/* What does this program do?  In short it postprocesses tool
9e95d03fff3786ef9462a45cdd6717b0793630377sewardj   executables on MacOSX, after linking using /usr/bin/ld.
10e95d03fff3786ef9462a45cdd6717b0793630377sewardj
11e95d03fff3786ef9462a45cdd6717b0793630377sewardj   This is to deal with two separate and entirely unrelated problems.
12e95d03fff3786ef9462a45cdd6717b0793630377sewardj   Problem (1) is a bug in the linker in Xcode 4.0.0.  Problem (2) is
13e95d03fff3786ef9462a45cdd6717b0793630377sewardj   much newer and concerns linking 64-bit tool executables for
14e95d03fff3786ef9462a45cdd6717b0793630377sewardj   Yosemite (10.10).
15e95d03fff3786ef9462a45cdd6717b0793630377sewardj
16e95d03fff3786ef9462a45cdd6717b0793630377sewardj   --- Problem (1) ------------------------------------------------
17e95d03fff3786ef9462a45cdd6717b0793630377sewardj
18e95d03fff3786ef9462a45cdd6717b0793630377sewardj   This is a bug in the linker on Xcode 4.0.0 and Xcode 4.0.1.  Xcode
19e95d03fff3786ef9462a45cdd6717b0793630377sewardj   versions prior to 4.0.0 are unaffected.
2008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
2108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   The tracking bug is https://bugs.kde.org/show_bug.cgi?id=267997
2208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
2308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   The bug causes 64-bit tool executables to segfault at startup,
2408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   because:
2508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
2608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   Comparing the MachO load commands vs a (working) tool executable
2708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   that was created by Xcode 3.2.x, it appears that the new linker has
2808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   partially ignored the build system's request to place the tool
2908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   executable's stack at a non standard location.  The build system
3008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   tells the linker "-stack_addr 0x134000000 -stack_size 0x800000".
3108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
3208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   With the Xcode 3.2 linker those flags produce two results:
3308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
3408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   (1) A load command to allocate the stack at the said location:
3508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj          Load command 3
3608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                cmd LC_SEGMENT_64
3708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            cmdsize 72
3808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            segname __UNIXSTACK
3908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj             vmaddr 0x0000000133800000
4008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj             vmsize 0x0000000000800000
4108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            fileoff 2285568
4208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           filesize 0
4308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            maxprot 0x00000007
4408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           initprot 0x00000003
4508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj             nsects 0
4608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj              flags 0x0
4708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
4808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   (2) A request (in LC_UNIXTHREAD) to set %rsp to the correct value
4908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj       at process startup, 0x134000000.
5008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
5108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   With Xcode 4.0.1, (1) is missing but (2) is still present.  The
5208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   tool executable therefore starts up with %rsp pointing to unmapped
5308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   memory and faults almost instantly.
5408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
5508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   The workaround implemented by this program is documented in comment
5608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   8 of bug 267997, viz:
5708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
5808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   One really sick workaround is to observe that the executables
5908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   contain a redundant MachO load command:
6008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
6108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      Load command 2
6208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            cmd LC_SEGMENT_64
6308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        cmdsize 72
6408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        segname __LINKEDIT
6508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         vmaddr 0x0000000138dea000
6608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         vmsize 0x00000000000ad000
6708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        fileoff 2658304
6808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj       filesize 705632
6908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        maxprot 0x00000007
7008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj       initprot 0x00000001
7108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         nsects 0
7208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj          flags 0x0
7308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
7408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   The described section presumably contains information intended for
7508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   the dynamic linker, but is irrelevant because this is a statically
7608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   linked executable.  Hence it might be possible to postprocess the
7708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   executables after linking, to overwrite this entry with the
7808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   information that would have been in the missing __UNIXSTACK entry.
7908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   I tried this by hand (with a binary editor) earlier and got
8008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   something that worked.
81e95d03fff3786ef9462a45cdd6717b0793630377sewardj
82e95d03fff3786ef9462a45cdd6717b0793630377sewardj   --- Problem (2) ------------------------------------------------
83e95d03fff3786ef9462a45cdd6717b0793630377sewardj
84e95d03fff3786ef9462a45cdd6717b0793630377sewardj   On MacOSX 10.10 (Yosemite), the kernel requires all valid
85e95d03fff3786ef9462a45cdd6717b0793630377sewardj   executables to have a __PAGEZERO section with SVMA of zero and size
86e95d03fff3786ef9462a45cdd6717b0793630377sewardj   of at least one page.  However, our tool executables have a
87e95d03fff3786ef9462a45cdd6717b0793630377sewardj   __PAGEZERO section with SVMA set to the requested Valgrind load
88e95d03fff3786ef9462a45cdd6717b0793630377sewardj   address (typically 0x1'3800'0000).  And the kernel won't start
89e95d03fff3786ef9462a45cdd6717b0793630377sewardj   those.  So we take the opportunity to "fix" this by setting the
90e95d03fff3786ef9462a45cdd6717b0793630377sewardj   SVMA to zero.  Seems to work and have no obvious bad side effects.
9108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj*/
9208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
9308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#define DEBUGPRINTING 0
9408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
9508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <assert.h>
9608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <stdlib.h>
9708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <stdio.h>
9808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <string.h>
9908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <sys/mman.h>
10008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <sys/stat.h>
10108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <unistd.h>
10208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <fcntl.h>
10308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
10408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#undef PLAT_x86_darwin
10508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#undef PLAT_amd64_darwin
10608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
10708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#if defined(__APPLE__) && defined(__i386__)
10808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#  define PLAT_x86_darwin 1
10908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#elif defined(__APPLE__) && defined(__x86_64__)
11008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#  define PLAT_amd64_darwin 1
11108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#else
11208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#  error "Can't be compiled on this platform"
11308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#endif
11408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
11508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <mach-o/loader.h>
11608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <mach-o/nlist.h>
11708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <mach-o/fat.h>
11808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#include <mach/i386/thread_status.h>
11908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
120e95d03fff3786ef9462a45cdd6717b0793630377sewardj/* Get hold of DARWIN_VERS, and check it has a sane value. */
121e95d03fff3786ef9462a45cdd6717b0793630377sewardj#include "config.h"
122e95d03fff3786ef9462a45cdd6717b0793630377sewardj#if DARWIN_VERS != DARWIN_10_5 && DARWIN_VERS != DARWIN_10_6 \
123e95d03fff3786ef9462a45cdd6717b0793630377sewardj    && DARWIN_VERS != DARWIN_10_7 && DARWIN_VERS != DARWIN_10_8 \
124e95d03fff3786ef9462a45cdd6717b0793630377sewardj    && DARWIN_VERS != DARWIN_10_9 && DARWIN_VERS != DARWIN_10_10
125e95d03fff3786ef9462a45cdd6717b0793630377sewardj#  error "Unknown DARWIN_VERS value.  This file only compiles on Darwin."
126e95d03fff3786ef9462a45cdd6717b0793630377sewardj#endif
127e95d03fff3786ef9462a45cdd6717b0793630377sewardj
12808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
12908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef  unsigned char   UChar;
13008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef    signed char   Char;
13108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef           char   HChar; /* signfulness depends on host */
13208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
13308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef  unsigned int    UInt;
13408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef    signed int    Int;
13508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
13608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef  unsigned char   Bool;
13708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#define  True   ((Bool)1)
13808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#define  False  ((Bool)0)
13908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
14008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef  unsigned long   UWord;
14108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
14208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef  UWord           SizeT;
14308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef  UWord           Addr;
14408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
14508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef  unsigned long long int   ULong;
14608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef    signed long long int   Long;
14708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
14808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
14908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
15008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj__attribute__((noreturn))
15108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjvoid fail ( HChar* msg )
15208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj{
15308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   fprintf(stderr, "fixup_macho_loadcmds: fail: %s\n", msg);
15408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   exit(1);
15508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj}
15608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
15708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
15808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*------------------------------------------------------------*/
15908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*---                                                      ---*/
16008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*--- Mach-O file mapping/unmapping helpers                ---*/
16108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*---                                                      ---*/
16208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*------------------------------------------------------------*/
16308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
16408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjtypedef
16508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   struct {
16608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      /* These two describe the entire mapped-in ("primary") image,
16708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         fat headers, kitchen sink, whatnot: the entire file.  The
16808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         image is mapped into img[0 .. img_szB-1]. */
16908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      UChar* img;
17008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      SizeT  img_szB;
17108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      /* These two describe the Mach-O object of interest, which is
17208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         presumably somewhere inside the primary image.
17308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         map_image_aboard() below, which generates this info, will
17408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         carefully check that the macho_ fields denote a section of
17508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         memory that falls entirely inside img[0 .. img_szB-1]. */
17608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      UChar* macho_img;
17708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      SizeT  macho_img_szB;
17808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   }
17908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   ImageInfo;
18008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
18108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
18208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjBool is_macho_object_file( const void* buf, SizeT szB )
18308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj{
18408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   /* (JRS: the Mach-O headers might not be in this mapped data,
18508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      because we only mapped a page for this initial check,
18608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      or at least not very much, and what's at the start of the file
18708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      is in general a so-called fat header.  The Mach-O object we're
18808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      interested in could be arbitrarily far along the image, and so
18908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      we can't assume its header will fall within this page.) */
19008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
19108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   /* But we can say that either it's a fat object, in which case it
19208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      begins with a fat header, or it's unadorned Mach-O, in which
19308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      case it starts with a normal header.  At least do what checks we
19408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      can to establish whether or not we're looking at something
19508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      sane. */
19608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
19708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   const struct fat_header*  fh_be = buf;
19808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   const struct mach_header_64* mh    = buf;
19908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
20008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert(buf);
20108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (szB < sizeof(struct fat_header))
20208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      return False;
20308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (ntohl(fh_be->magic) == FAT_MAGIC)
20408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      return True;
20508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
20608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (szB < sizeof(struct mach_header_64))
20708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      return False;
20808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (mh->magic == MH_MAGIC_64)
20908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      return True;
21008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
21108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   return False;
21208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj}
21308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
21408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
21508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/* Unmap an image mapped in by map_image_aboard. */
21608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjstatic void unmap_image ( /*MOD*/ImageInfo* ii )
21708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj{
21808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   Int r;
21908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert(ii->img);
22008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert(ii->img_szB > 0);
22108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   r = munmap( ii->img, ii->img_szB );
22208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   /* Do we care if this fails?  I suppose so; it would indicate
22308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      some fairly serious snafu with the mapping of the file. */
22408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert( !r );
22508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   memset(ii, 0, sizeof(*ii));
22608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj}
22708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
22808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
22908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/* Map a given fat or thin object aboard, find the thin part if
23008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   necessary, do some checks, and write details of both the fat and
23108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   thin parts into *ii.  Returns 32 (and leaves the file unmapped) if
23208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   the thin part is a 32 bit file.  Returns 64 if it's a 64 bit file.
23308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   Does not return on failure.  Guarantees to return pointers to a
23408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   valid(ish) Mach-O image if it succeeds. */
23508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjstatic Int map_image_aboard ( /*OUT*/ImageInfo* ii, HChar* filename )
23608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj{
23708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   memset(ii, 0, sizeof(*ii));
23808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
23908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   /* First off, try to map the thing in. */
24008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   { SizeT  size;
24108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     Int r, fd;
24208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     struct stat stat_buf;
24308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
24408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     r = stat(filename, &stat_buf);
24508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (r)
24608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        fail("Can't stat image (to determine its size)?!");
24708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     size = stat_buf.st_size;
24808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
24908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     fd = open(filename, O_RDWR, 0);
25008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (fd == -1)
25108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        fail("Can't open image for possible modification!");
25208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (DEBUGPRINTING)
25308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        printf("size %lu fd %d\n", size, fd);
25408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     void* v = mmap ( NULL, size, PROT_READ|PROT_WRITE,
25508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                                  MAP_FILE|MAP_SHARED, fd, 0 );
25608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (v == MAP_FAILED) {
25708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        perror("mmap failed");
25808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        fail("Can't mmap image for possible modification!");
25908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     }
26008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
26108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     close(fd);
26208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
26308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     ii->img     = (UChar*)v;
26408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     ii->img_szB = size;
26508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   }
26608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
26708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   /* Now it's mapped in and we have .img and .img_szB set.  Look for
26808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      the embedded Mach-O object.  If not findable, unmap and fail. */
26908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   { struct fat_header*  fh_be;
27008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     struct fat_header   fh;
27108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     struct mach_header_64* mh;
27208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
27308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     // Assume initially that we have a thin image, and update
27408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     // these if it turns out to be fat.
27508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     ii->macho_img     = ii->img;
27608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     ii->macho_img_szB = ii->img_szB;
27708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
27808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     // Check for fat header.
27908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (ii->img_szB < sizeof(struct fat_header))
28008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        fail("Invalid Mach-O file (0 too small).");
28108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
28208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     // Fat header is always BIG-ENDIAN
28308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     fh_be = (struct fat_header *)ii->img;
28408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     fh.magic = ntohl(fh_be->magic);
28508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     fh.nfat_arch = ntohl(fh_be->nfat_arch);
28608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (fh.magic == FAT_MAGIC) {
28708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        // Look for a good architecture.
28808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        struct fat_arch *arch_be;
28908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        struct fat_arch arch;
29008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        Int f;
29108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        if (ii->img_szB < sizeof(struct fat_header)
29208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                          + fh.nfat_arch * sizeof(struct fat_arch))
29308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           fail("Invalid Mach-O file (1 too small).");
29408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
29508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        for (f = 0, arch_be = (struct fat_arch *)(fh_be+1);
29608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj             f < fh.nfat_arch;
29708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj             f++, arch_be++) {
29808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           Int cputype;
29908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#          if defined(PLAT_x86_darwin)
30008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           cputype = CPU_TYPE_X86;
30108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#          elif defined(PLAT_amd64_darwin)
30208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           cputype = CPU_TYPE_X86_64;
30308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#          else
30408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#            error "unknown architecture"
30508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj#          endif
30608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           arch.cputype    = ntohl(arch_be->cputype);
30708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           arch.cpusubtype = ntohl(arch_be->cpusubtype);
30808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           arch.offset     = ntohl(arch_be->offset);
30908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           arch.size       = ntohl(arch_be->size);
31008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           if (arch.cputype == cputype) {
31108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj              if (ii->img_szB < arch.offset + arch.size)
31208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                 fail("Invalid Mach-O file (2 too small).");
31308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj              ii->macho_img     = ii->img + arch.offset;
31408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj              ii->macho_img_szB = arch.size;
31508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj              break;
31608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           }
31708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        }
31808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        if (f == fh.nfat_arch)
31908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           fail("No acceptable architecture found in fat file.");
32008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     }
32108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
32208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     /* Sanity check what we found. */
32308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
32408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     /* assured by logic above */
32508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     assert(ii->img_szB >= sizeof(struct fat_header));
32608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
32708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (ii->macho_img_szB < sizeof(struct mach_header_64))
32808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        fail("Invalid Mach-O file (3 too small).");
32908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
33008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (ii->macho_img_szB > ii->img_szB)
33108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        fail("Invalid Mach-O file (thin bigger than fat).");
33208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
33308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (ii->macho_img >= ii->img
33408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         && ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB) {
33508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        /* thin entirely within fat, as expected */
33608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     } else {
33708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        fail("Invalid Mach-O file (thin not inside fat).");
33808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     }
33908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
34008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     mh = (struct mach_header_64 *)ii->macho_img;
34108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (mh->magic == MH_MAGIC) {
34208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        assert(ii->img);
34308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        assert(ii->macho_img);
34408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        assert(ii->img_szB > 0);
34508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        assert(ii->macho_img_szB > 0);
34608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        assert(ii->macho_img >= ii->img);
34708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        assert(ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB);
34808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        return 32;
34908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     }
35008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (mh->magic != MH_MAGIC_64)
35108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        fail("Invalid Mach-O file (bad magic).");
35208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
35308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj     if (ii->macho_img_szB < sizeof(struct mach_header_64) + mh->sizeofcmds)
35408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        fail("Invalid Mach-O file (4 too small).");
35508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   }
35608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
35708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert(ii->img);
35808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert(ii->macho_img);
35908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert(ii->img_szB > 0);
36008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert(ii->macho_img_szB > 0);
36108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert(ii->macho_img >= ii->img);
36208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert(ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB);
36308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   return 64;
36408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj}
36508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
36608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
36708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*------------------------------------------------------------*/
36808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*---                                                      ---*/
36908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*--- Mach-O top-level processing                          ---*/
37008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*---                                                      ---*/
37108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*------------------------------------------------------------*/
37208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
37308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjvoid modify_macho_loadcmds ( HChar* filename,
37408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                             ULong  expected_stack_start,
37508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                             ULong  expected_stack_size )
37608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj{
37708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   ImageInfo ii;
37808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   memset(&ii, 0, sizeof(ii));
37908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
38008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   Int size = map_image_aboard( &ii, filename );
38108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (size == 32) {
38208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      fprintf(stderr, "fixup_macho_loadcmds:   Is 32-bit MachO file;"
38308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj              " no modifications needed.\n");
38408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      goto out;
38508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   }
38608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
38708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert(size == 64);
38808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
38908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   assert(ii.macho_img != NULL && ii.macho_img_szB > 0);
39008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
39108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   /* Poke around in the Mach-O header, to find some important
39208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      stuff.
39308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      * the location of the __UNIXSTACK load command, if any
39408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      * the location of the __LINKEDIT load command, if any
39508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      * the initial RSP value as stated in the LC_UNIXTHREAD
39608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   */
39708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
39808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   /* The collected data */
39908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   ULong init_rsp = 0;
40008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   Bool  have_rsp = False;
40108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   struct segment_command_64* seg__unixstack = NULL;
40208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   struct segment_command_64* seg__linkedit  = NULL;
403e95d03fff3786ef9462a45cdd6717b0793630377sewardj   struct segment_command_64* seg__pagezero  = NULL;
40408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
40508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   /* Loop over the load commands and fill in the above 4 variables. */
40608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
40708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   { struct mach_header_64 *mh = (struct mach_header_64 *)ii.macho_img;
40808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      struct load_command *cmd;
40908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      Int c;
41008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
41108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      for (c = 0, cmd = (struct load_command *)(mh+1);
41208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           c < mh->ncmds;
41308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           c++, cmd = (struct load_command *)(cmd->cmdsize
41408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                                              + (unsigned long)cmd)) {
41508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         if (DEBUGPRINTING)
41608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            printf("load cmd: offset %4lu   size %3d   kind %2d = ",
41708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                   (unsigned long)((UChar*)cmd - (UChar*)ii.macho_img),
41808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                   cmd->cmdsize, cmd->cmd);
41908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
42008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         switch (cmd->cmd) {
42108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            case LC_SEGMENT_64:
42208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               if (DEBUGPRINTING)
42308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                  printf("LC_SEGMENT_64");
42408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               break;
42508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            case LC_SYMTAB:
42608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               if (DEBUGPRINTING)
42708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                  printf("LC_SYMTAB");
42808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               break;
429627c649161bb457d7681625a8ea98929acee4abasewardj            case LC_DYSYMTAB:
430627c649161bb457d7681625a8ea98929acee4abasewardj               if (DEBUGPRINTING)
431627c649161bb457d7681625a8ea98929acee4abasewardj                  printf("LC_DYSYMTAB");
432627c649161bb457d7681625a8ea98929acee4abasewardj               break;
43308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            case LC_UUID:
43408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               if (DEBUGPRINTING)
43508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                  printf("LC_UUID");
43608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               break;
43708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            case LC_UNIXTHREAD:
43808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               if (DEBUGPRINTING)
43908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                  printf("LC_UNIXTHREAD");
44008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               break;
44108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            default:
442e95d03fff3786ef9462a45cdd6717b0793630377sewardj               if (DEBUGPRINTING)
44308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                  printf("???");
44408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               fail("unexpected load command in Mach header");
44508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            break;
44608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         }
44708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         if (DEBUGPRINTING)
44808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            printf("\n");
44908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
45008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         /* Note what the stated initial RSP value is, so we can
45108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            check it is as expected. */
45208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         if (cmd->cmd == LC_UNIXTHREAD) {
45308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            struct thread_command* tcmd = (struct thread_command*)cmd;
45408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            UInt* w32s = (UInt*)( (UChar*)tcmd + sizeof(*tcmd) );
45508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            if (DEBUGPRINTING)
45608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               printf("UnixThread: flavor %u = ", w32s[0]);
45708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            if (w32s[0] == x86_THREAD_STATE64 && !have_rsp) {
45808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               if (DEBUGPRINTING)
45908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                  printf("x86_THREAD_STATE64\n");
46008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               x86_thread_state64_t* state64
46108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                  = (x86_thread_state64_t*)(&w32s[2]);
46208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               have_rsp = True;
46308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               init_rsp = state64->__rsp;
46408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               if (DEBUGPRINTING)
46508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                  printf("rsp = 0x%llx\n", init_rsp);
46608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            } else {
46708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               if (DEBUGPRINTING)
46808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                  printf("???");
46908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            }
47008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            if (DEBUGPRINTING)
47108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               printf("\n");
47208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         }
47308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
47408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         if (cmd->cmd == LC_SEGMENT_64) {
47508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            struct segment_command_64 *seg = (struct segment_command_64 *)cmd;
47608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            if (0 == strcmp(seg->segname, "__LINKEDIT"))
47708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               seg__linkedit = seg;
47808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj            if (0 == strcmp(seg->segname, "__UNIXSTACK"))
47908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj               seg__unixstack = seg;
480e95d03fff3786ef9462a45cdd6717b0793630377sewardj            if (0 == strcmp(seg->segname, "__PAGEZERO"))
481e95d03fff3786ef9462a45cdd6717b0793630377sewardj               seg__pagezero = seg;
48208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         }
48308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
48408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      }
48508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   }
48608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
48708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   /*
48808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      Actions are then as follows:
48908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
49008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      * (always) check the RSP value is as expected, and abort if not
49108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
49208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      * if there's a UNIXSTACK load command, check it is as expected.
49308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        If not abort, if yes, do nothing more.
49408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
49508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      * (so there's no UNIXSTACK load command).  if there's a LINKEDIT
49608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        load command, check if it is minimally usable (has 0 for
49708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        nsects and flags).  If yes, convert it to a UNIXSTACK load
49808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        command.  If there is none, or is unusable, then we're out of
49908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        options and have to abort.
50008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   */
50108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (!have_rsp)
50208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      fail("Can't find / check initial RSP setting");
50308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (init_rsp != expected_stack_start + expected_stack_size)
50408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      fail("Initial RSP value not as expected");
50508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
50608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   fprintf(stderr, "fixup_macho_loadcmds:   "
50708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                   "initial RSP is as expected (0x%llx)\n",
50808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                   expected_stack_start + expected_stack_size );
50908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
51008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (seg__unixstack) {
51108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      struct segment_command_64 *seg = seg__unixstack;
51208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      if (seg->vmaddr != expected_stack_start)
51308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         fail("has __UNIXSTACK, but wrong ::vmaddr");
51408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      if (seg->vmsize != expected_stack_size)
51508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         fail("has __UNIXSTACK, but wrong ::vmsize");
51608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      if (seg->maxprot != 7)
51708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         fail("has __UNIXSTACK, but wrong ::maxprot (should be 7)");
51808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      if (seg->initprot != 3)
51908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         fail("has __UNIXSTACK, but wrong ::initprot (should be 3)");
52008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      if (seg->nsects != 0)
52108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         fail("has __UNIXSTACK, but wrong ::nsects (should be 0)");
52208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      if (seg->flags != 0)
52308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         fail("has __UNIXSTACK, but wrong ::flags (should be 0)");
52408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      /* looks ok */
52508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      fprintf(stderr, "fixup_macho_loadcmds:   "
52608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj              "acceptable __UNIXSTACK present; no modifications.\n" );
527e95d03fff3786ef9462a45cdd6717b0793630377sewardj      goto maybe_mash_pagezero;
52808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   }
52908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
53008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (seg__linkedit) {
53108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      struct segment_command_64 *seg = seg__linkedit;
53208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      if (seg->nsects != 0)
53308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         fail("has __LINKEDIT, but wrong ::nsects (should be 0)");
53408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      if (seg->flags != 0)
53508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj         fail("has __LINKEDIT, but wrong ::flags (should be 0)");
53608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      fprintf(stderr, "fixup_macho_loadcmds:   "
53708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj              "no __UNIXSTACK present.\n" );
53808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      fprintf(stderr, "fixup_macho_loadcmds:   "
53908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj              "converting __LINKEDIT to __UNIXSTACK.\n" );
54008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      strcpy(seg->segname, "__UNIXSTACK");
54108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      seg->vmaddr   = expected_stack_start;
54208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      seg->vmsize   = expected_stack_size;
54308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      seg->fileoff  = 0;
54408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      seg->filesize = 0;
54508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      seg->maxprot  = 7;
54608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      seg->initprot = 3;
54708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      /* success */
548e95d03fff3786ef9462a45cdd6717b0793630377sewardj      goto maybe_mash_pagezero;
54908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   }
55008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
55108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   /* out of options */
55208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   fail("no __UNIXSTACK found and no usable __LINKEDIT found; "
55308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj        "out of options.");
55408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   /* NOTREACHED */
55508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
556e95d03fff3786ef9462a45cdd6717b0793630377sewardj  maybe_mash_pagezero:
557e95d03fff3786ef9462a45cdd6717b0793630377sewardj   /* Deal with Problem (2) as documented above. */
558e95d03fff3786ef9462a45cdd6717b0793630377sewardj#  if DARWIN_VERS == DARWIN_10_10
559e95d03fff3786ef9462a45cdd6717b0793630377sewardj   assert(size == 64);
560e95d03fff3786ef9462a45cdd6717b0793630377sewardj   if (!seg__pagezero) {
561e95d03fff3786ef9462a45cdd6717b0793630377sewardj      fail("Can't find __PAGEZERO to modify; can't continue.");
562e95d03fff3786ef9462a45cdd6717b0793630377sewardj   }
563e95d03fff3786ef9462a45cdd6717b0793630377sewardj   fprintf(stderr, "fixup_macho_loadcmds:   "
564e95d03fff3786ef9462a45cdd6717b0793630377sewardj           "changing __PAGEZERO.vmaddr from %p to 0x0.\n",
565e95d03fff3786ef9462a45cdd6717b0793630377sewardj           (void*)seg__pagezero->vmaddr);
566e95d03fff3786ef9462a45cdd6717b0793630377sewardj   seg__pagezero->vmaddr = 0;
567e95d03fff3786ef9462a45cdd6717b0793630377sewardj#  endif
568e95d03fff3786ef9462a45cdd6717b0793630377sewardj
569e95d03fff3786ef9462a45cdd6717b0793630377sewardj  out:
57008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (ii.img)
57108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      unmap_image(&ii);
57208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj}
57308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
57408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
57508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjstatic Bool is_plausible_tool_exe_name ( HChar* nm )
57608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj{
57708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   HChar* p;
57808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (!nm)
57908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      return False;
58008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
58108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   // Does it end with this string?
58208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   p = strstr(nm, "-x86-darwin");
58308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (p && 0 == strcmp(p, "-x86-darwin"))
58408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      return True;
58508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
58608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   p = strstr(nm, "-amd64-darwin");
58708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (p && 0 == strcmp(p, "-amd64-darwin"))
58808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      return True;
58908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
59008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   return False;
59108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj}
59208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
59308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
59408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardjint main ( int argc, char** argv )
59508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj{
59608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   Int   r;
59708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   ULong req_stack_addr = 0;
59808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   ULong req_stack_size = 0;
59908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
60008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (argc != 4)
60108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      fail("args: -stack_addr-arg -stack_size-arg "
60208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           "name-of-tool-executable-to-modify");
60308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
60408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   r= sscanf(argv[1], "0x%llx", &req_stack_addr);
60508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (r != 1) fail("invalid stack_addr arg");
60608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
60708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   r= sscanf(argv[2], "0x%llx", &req_stack_size);
60808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (r != 1) fail("invalid stack_size arg");
60908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
61008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   fprintf(stderr, "fixup_macho_loadcmds: "
61108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           "requested stack_addr (top) 0x%llx, "
61208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           "stack_size 0x%llx\n", req_stack_addr, req_stack_size );
61308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
61408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   if (!is_plausible_tool_exe_name(argv[3]))
61508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      fail("implausible tool exe name -- not of the form *-{x86,amd64}-darwin");
61608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
61708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   fprintf(stderr, "fixup_macho_loadcmds: examining tool exe: %s\n",
61808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj           argv[3] );
61908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   modify_macho_loadcmds( argv[3], req_stack_addr - req_stack_size,
62008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj                          req_stack_size );
62108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
62208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   return 0;
62308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj}
62408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
62508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*
62608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      cmd LC_SEGMENT_64
62708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj  cmdsize 72
62808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj  segname __LINKEDIT
62908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   vmaddr 0x0000000138dea000
63008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   vmsize 0x00000000000ad000
63108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj  fileoff 2658304
63208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj filesize 705632
63308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj  maxprot 0x00000007
63408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj initprot 0x00000001
63508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   nsects 0
63608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj    flags 0x0
63708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj*/
63808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj
63908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj/*
64008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj      cmd LC_SEGMENT_64
64108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj  cmdsize 72
64208f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj  segname __UNIXSTACK
64308f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   vmaddr 0x0000000133800000
64408f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   vmsize 0x0000000000800000
64508f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj  fileoff 2498560
64608f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj filesize 0
64708f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj  maxprot 0x00000007
64808f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj initprot 0x00000003
64908f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj   nsects 0
65008f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj    flags 0x0
65108f5a27676dedc7197a8b81f80ebc83d1e311af9sewardj*/
652