sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 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 |
sewardj | e95d03f | 2014-11-06 20:23:22 +0000 | [diff] [blame] | 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. |
sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 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. |
sewardj | e95d03f | 2014-11-06 20:23:22 +0000 | [diff] [blame] | 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. |
sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 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 | |
sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 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 | |
sewardj | e95d03f | 2014-11-06 20:23:22 +0000 | [diff] [blame] | 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 | |
sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 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; |
sewardj | e95d03f | 2014-11-06 20:23:22 +0000 | [diff] [blame] | 403 | struct segment_command_64* seg__pagezero = NULL; |
sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 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; |
sewardj | 627c649 | 2011-05-10 08:42:14 +0000 | [diff] [blame] | 429 | case LC_DYSYMTAB: |
| 430 | if (DEBUGPRINTING) |
| 431 | printf("LC_DYSYMTAB"); |
| 432 | break; |
sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 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: |
sewardj | e95d03f | 2014-11-06 20:23:22 +0000 | [diff] [blame] | 442 | if (DEBUGPRINTING) |
sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 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; |
sewardj | e95d03f | 2014-11-06 20:23:22 +0000 | [diff] [blame] | 480 | if (0 == strcmp(seg->segname, "__PAGEZERO")) |
| 481 | seg__pagezero = seg; |
sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 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" ); |
sewardj | e95d03f | 2014-11-06 20:23:22 +0000 | [diff] [blame] | 527 | goto maybe_mash_pagezero; |
sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 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 */ |
sewardj | e95d03f | 2014-11-06 20:23:22 +0000 | [diff] [blame] | 548 | goto maybe_mash_pagezero; |
sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 549 | } |
| 550 | |
| 551 | /* out of options */ |
| 552 | fail("no __UNIXSTACK found and no usable __LINKEDIT found; " |
| 553 | "out of options."); |
| 554 | /* NOTREACHED */ |
| 555 | |
sewardj | e95d03f | 2014-11-06 20:23:22 +0000 | [diff] [blame] | 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: |
sewardj | 08f5a27 | 2011-04-06 11:17:16 +0000 | [diff] [blame] | 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 | */ |