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