1/* 2 * Copyright (c) 2006-2008, 2012 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28#include <unistd.h> 29#include <string.h> 30 31#include <stdio.h> 32#include <fcntl.h> 33 34#include <Kernel/mach/vm_types.h> 35#include <Kernel/mach/vm_param.h> 36 37#include <mach-o/swap.h> 38 39#include <sys/types.h> 40#include <sys/stat.h> 41#include <sys/mman.h> 42 43#include "macho_util.h" 44 45static boolean_t macho_swap_32(u_char *file); 46static boolean_t macho_swap_64(u_char *file); 47 48static boolean_t macho_unswap_32(u_char *file); 49static boolean_t macho_unswap_64(u_char *file); 50 51/******************************************************************************* 52* 53*******************************************************************************/ 54macho_seek_result macho_find_symbol( 55 const void * file_start, 56 const void * file_end, 57 const char * name, 58 uint8_t * nlist_type, 59 const void ** symbol_address) 60{ 61 macho_seek_result result = macho_seek_result_not_found; 62 macho_seek_result symtab_result = macho_seek_result_not_found; 63 uint8_t swap = 0; 64 char sixtyfourbit = 0; 65 struct symtab_command * symtab = NULL; 66 struct nlist * syms_address; 67 struct nlist_64 * syms_address_64; 68 const void * string_list; 69 char * symbol_name; 70 unsigned int symtab_offset; 71 unsigned int str_offset; 72 unsigned int num_syms; 73 unsigned int syms_bytes; 74 unsigned int sym_index; 75 76 if (symbol_address) { 77 *symbol_address = 0; 78 } 79 80 symtab_result = macho_find_symtab(file_start, file_end, &symtab); 81 if (symtab_result != macho_seek_result_found) { 82 goto finish; 83 } 84 85 if (ISSWAPPEDMACHO(MAGIC32(file_start))) { 86 swap = 1; 87 } 88 if (ISMACHO64(MAGIC32(file_start))) { 89 sixtyfourbit = 1; 90 } 91 92 symtab_offset = CondSwapInt32(swap, symtab->symoff); 93 str_offset = CondSwapInt32(swap, symtab->stroff); 94 num_syms = CondSwapInt32(swap, symtab->nsyms); 95 96 syms_address = (struct nlist *)(file_start + symtab_offset); 97 syms_address_64 = (struct nlist_64 *)(file_start + symtab_offset); 98 99 string_list = file_start + str_offset; 100 if (sixtyfourbit) { 101 syms_bytes = num_syms * sizeof(struct nlist_64); 102 } else { 103 syms_bytes = num_syms * sizeof(struct nlist); 104 } 105 106 if ((char *)syms_address + syms_bytes > (char *)file_end) { 107 result = macho_seek_result_error; 108 goto finish; 109 } 110 111 for (sym_index = 0; sym_index < num_syms; sym_index++) { 112 struct nlist * seekptr; 113 struct nlist_64 * seekptr_64; 114 uint32_t string_index; 115 uint8_t n_type; 116 uint8_t n_sect; 117 uint64_t n_value; 118 119 if (sixtyfourbit) { 120 seekptr_64 = &syms_address_64[sym_index]; 121 string_index = CondSwapInt32(swap, seekptr_64->n_un.n_strx); 122 n_type = seekptr_64->n_type; 123 n_sect = seekptr_64->n_sect; 124 n_value = CondSwapInt64(swap, seekptr_64->n_value); 125 } else { 126 seekptr = &syms_address[sym_index]; 127 string_index = CondSwapInt32(swap, seekptr->n_un.n_strx); 128 n_type = seekptr->n_type; 129 n_sect = seekptr->n_sect; 130 n_value = (uint64_t)CondSwapInt32(swap, seekptr->n_value); 131 } 132 133 if (string_index == 0 || n_type & N_STAB) { 134 continue; 135 } 136 symbol_name = (char *)(string_list + string_index); 137 138 if (strcmp(name, symbol_name) == 0) { 139 140 if (nlist_type) { 141 *nlist_type = n_type; 142 } 143 switch (n_type & N_TYPE) { 144 case N_SECT: 145 { 146 void * v_sect_info = macho_find_section_numbered( 147 file_start, file_end, n_sect); 148 149 150 if (!v_sect_info) { 151 break; // out of the switch 152 } 153 154 if (symbol_address) { 155 if (sixtyfourbit) { 156 struct section_64 * sect_info_64 = 157 (struct section_64 *)v_sect_info; 158 159 // this isn't right for 64bit? compare below 160 size_t reloffset = (n_value - 161 CondSwapInt64(swap, sect_info_64->addr)); 162 163 *symbol_address = file_start; 164 *symbol_address += CondSwapInt32(swap, 165 sect_info_64->offset); 166 *symbol_address += reloffset; 167 } else { 168 struct section * sect_info = 169 (struct section *)v_sect_info; 170 171 size_t reloffset = (n_value - 172 CondSwapInt32(swap, sect_info->addr)); 173 174 *symbol_address = file_start; 175 *symbol_address += CondSwapInt32(swap, 176 sect_info->offset); 177 *symbol_address += reloffset; 178 } 179 } 180 result = macho_seek_result_found; 181 goto finish; 182 } 183 break; 184 185 case N_UNDF: 186 result = macho_seek_result_found_no_value; 187 goto finish; 188 break; 189 190 case N_ABS: 191 result = macho_seek_result_found_no_value; 192 goto finish; 193 break; 194 195 /* We don't chase indirect symbols as they can be external. 196 */ 197 case N_INDR: 198 result = macho_seek_result_found_no_value; 199 goto finish; 200 break; 201 202 default: 203 goto finish; 204 break; 205 } 206 } 207 } 208 209finish: 210 return result; 211} 212 213/******************************************************************************* 214* 215*******************************************************************************/ 216typedef struct { 217 struct symtab_command * symtab; 218} _symtab_scan; 219 220static macho_seek_result __macho_lc_is_symtab( 221 struct load_command * lc_cmd, 222 const void * file_end, 223 uint8_t swap, 224 void * user_data); 225 226/******************************************************************************/ 227 228macho_seek_result macho_find_symtab( 229 const void * file_start, 230 const void * file_end, 231 struct symtab_command ** symtab) 232{ 233 macho_seek_result result = macho_seek_result_not_found; 234 _symtab_scan sym_data; 235 236 bzero(&sym_data, sizeof(sym_data)); 237 238 if (symtab) { 239 *symtab = NULL; 240 } 241 242 result = macho_scan_load_commands(file_start, 243 file_end, &__macho_lc_is_symtab, &sym_data); 244 245 if (result == macho_seek_result_found) { 246 if (symtab) { 247 *symtab = sym_data.symtab; 248 } 249 } 250 251 return result; 252} 253 254/******************************************************************************/ 255 256static macho_seek_result __macho_lc_is_symtab( 257 struct load_command * lc_cmd, 258 const void * file_end, 259 uint8_t swap, 260 void * user_data) 261{ 262 macho_seek_result result = macho_seek_result_not_found; 263 _symtab_scan * sym_data = (_symtab_scan *)user_data; 264 uint32_t cmd; 265 266 if ((void *)(lc_cmd + sizeof(struct load_command)) > file_end) { 267 result = macho_seek_result_error; 268 goto finish; 269 } 270 271 cmd = CondSwapInt32(swap, lc_cmd->cmd); 272 273 if (cmd == LC_SYMTAB) { 274 uint32_t cmd_size = CondSwapInt32(swap, lc_cmd->cmdsize); 275 276 if ((cmd_size != sizeof(struct symtab_command)) || 277 ((void *)(lc_cmd + sizeof(struct symtab_command)) > file_end)) { 278 result = macho_seek_result_error; 279 goto finish; 280 } 281 sym_data->symtab = (struct symtab_command *)lc_cmd; 282 result = macho_seek_result_found; 283 goto finish; 284 } 285 286finish: 287 return result; 288} 289 290/******************************************************************************* 291 * 292 *******************************************************************************/ 293typedef struct { 294 struct dysymtab_command * dysymtab; 295} _dysymtab_scan; 296 297static macho_seek_result __macho_lc_is_dysymtab( 298 struct load_command * lc_cmd, 299 const void * file_end, 300 uint8_t swap, 301 void * user_data); 302 303/******************************************************************************/ 304 305macho_seek_result macho_find_dysymtab( 306 const void * file_start, 307 const void * file_end, 308 struct dysymtab_command ** dysymtab) 309{ 310 macho_seek_result result = macho_seek_result_not_found; 311 _dysymtab_scan dysym_data; 312 313 bzero(&dysym_data, sizeof(dysym_data)); 314 315 if (dysymtab) { 316 *dysymtab = NULL; 317 } 318 319 result = macho_scan_load_commands(file_start, 320 file_end, &__macho_lc_is_dysymtab, &dysym_data); 321 322 if (result == macho_seek_result_found) { 323 if (dysymtab) { 324 *dysymtab = dysym_data.dysymtab; 325 } 326 } 327 328 return result; 329} 330 331/******************************************************************************/ 332 333static macho_seek_result __macho_lc_is_dysymtab( 334 struct load_command * lc_cmd, 335 const void * file_end, 336 uint8_t swap, 337 void * user_data) 338{ 339 macho_seek_result result = macho_seek_result_not_found; 340 _dysymtab_scan * dysym_data = (_dysymtab_scan *)user_data; 341 uint32_t cmd; 342 343 if ((void *)(lc_cmd + sizeof(struct load_command)) > file_end) { 344 result = macho_seek_result_error; 345 goto finish; 346 } 347 348 cmd = CondSwapInt32(swap, lc_cmd->cmd); 349 350 if (cmd == LC_DYSYMTAB) { 351 uint32_t cmd_size = CondSwapInt32(swap, lc_cmd->cmdsize); 352 353 if ((cmd_size != sizeof(struct dysymtab_command)) || 354 ((void *)(lc_cmd + sizeof(struct dysymtab_command)) > file_end)) { 355 result = macho_seek_result_error; 356 goto finish; 357 } 358 dysym_data->dysymtab = (struct dysymtab_command *)lc_cmd; 359 result = macho_seek_result_found; 360 goto finish; 361 } 362 363finish: 364 return result; 365} 366 367/******************************************************************************* 368* macho_find_uuid() 369* 370* Returns a pointer to the UUID bytes. 371*******************************************************************************/ 372struct _uuid_scan { 373 unsigned int uuid_size; 374 char * uuid; 375}; 376 377macho_seek_result __uuid_callback( 378 struct load_command * load_command, 379 const void * file_end, 380 uint8_t swap __unused, 381 void * user_data) 382{ 383 struct _uuid_scan * uuid_stuff = (struct _uuid_scan *)user_data; 384 if (load_command->cmd == LC_UUID) { 385 struct uuid_command * uuid_command = (struct uuid_command *)load_command; 386 if (((void *)load_command + load_command->cmdsize) > file_end) { 387 return macho_seek_result_error; 388 } 389 uuid_stuff->uuid_size = sizeof(uuid_command->uuid); 390 uuid_stuff->uuid = (char *)uuid_command->uuid; 391 return macho_seek_result_found; 392 } 393 return macho_seek_result_not_found; 394} 395 396macho_seek_result macho_find_uuid( 397 const void * file_start, 398 const void * file_end, 399 char **uuid) 400{ 401 macho_seek_result result; 402 struct _uuid_scan seek_uuid; 403 404 result = macho_scan_load_commands( 405 file_start, file_end, 406 __uuid_callback, (const void **)&seek_uuid); 407 if (result == macho_seek_result_found && uuid) { 408 *uuid = seek_uuid.uuid; 409 } 410 411 return result; 412} 413 414/******************************************************************************* 415* macho_find_section_numbered() 416* 417* Returns a pointer to a section in a mach-o file based on its global index 418* (which starts at 1, not zero!). The section number is typically garnered from 419* some other mach-o struct, such as a symtab entry. Returns NULL if the numbered 420* section can't be found. 421*******************************************************************************/ 422typedef struct { 423 char sixtyfourbit; 424 uint8_t sect_num; 425 uint8_t sect_counter; 426 void * sect_info; // struct section or section_64 depending 427} _sect_scan; 428 429static macho_seek_result __macho_sect_in_lc( 430 struct load_command * lc_cmd, 431 const void * file_end, 432 uint8_t swap, 433 void * user_data); 434 435/******************************************************************************/ 436 437void * macho_find_section_numbered( 438 const void * file_start, 439 const void * file_end, 440 uint8_t sect_num) 441{ 442 _sect_scan sect_data; 443 444 bzero(§_data, sizeof(sect_data)); 445 446 sect_data.sect_num = sect_num; 447 448 if (ISMACHO64(MAGIC32(file_start))) { 449 sect_data.sixtyfourbit = 1; 450 } 451 452 if (macho_seek_result_found == macho_scan_load_commands( 453 file_start, file_end, &__macho_sect_in_lc, §_data)) { 454 455 return sect_data.sect_info; 456 } 457 458 return NULL; 459} 460 461/******************************************************************************/ 462 463static macho_seek_result __macho_sect_in_lc( 464 struct load_command * lc_cmd, 465 const void * file_end, 466 uint8_t swap, 467 void * user_data) 468{ 469 macho_seek_result result = macho_seek_result_not_found; 470 _sect_scan * sect_data = (_sect_scan *)user_data; 471 uint32_t cmd; 472 473 if (sect_data->sect_counter > sect_data->sect_num) { 474 result = macho_seek_result_stop; 475 goto finish; 476 } 477 478 if ((void *)(lc_cmd + sizeof(struct load_command)) > file_end) { 479 result = macho_seek_result_error; 480 goto finish; 481 } 482 483 cmd = CondSwapInt32(swap, lc_cmd->cmd); 484 485 if (cmd == LC_SEGMENT_64) { 486 struct segment_command_64 * seg_cmd = 487 (struct segment_command_64 *)lc_cmd; 488 uint32_t cmd_size; 489 uint32_t num_sects; 490 uint32_t sects_size; 491 struct section_64 * seek_sect; 492 uint32_t sect_index; 493 494 cmd_size = CondSwapInt32(swap, seg_cmd->cmdsize); 495 num_sects = CondSwapInt32(swap, seg_cmd->nsects); 496 sects_size = num_sects * sizeof(*seek_sect); 497 498 if (cmd_size != (sizeof(*seg_cmd) + sects_size)) { 499 result = macho_seek_result_error; 500 goto finish; 501 } 502 503 if (((void *)lc_cmd + cmd_size) > file_end) { 504 result = macho_seek_result_error; 505 goto finish; 506 } 507 508 for (sect_index = 0; sect_index < num_sects; sect_index++) { 509 510 seek_sect = (struct section_64 *)((void *)lc_cmd + 511 sizeof(*seg_cmd) + 512 (sect_index * sizeof(*seek_sect))); 513 514 sect_data->sect_counter++; 515 516 if (sect_data->sect_counter == sect_data->sect_num) { 517 sect_data->sect_info = seek_sect; 518 result = macho_seek_result_found; 519 goto finish; 520 } 521 } 522 } else if (cmd == LC_SEGMENT) { 523 struct segment_command * seg_cmd = (struct segment_command *)lc_cmd; 524 uint32_t cmd_size; 525 uint32_t num_sects; 526 uint32_t sects_size; 527 struct section * seek_sect; 528 uint32_t sect_index; 529 530 cmd_size = CondSwapInt32(swap, seg_cmd->cmdsize); 531 num_sects = CondSwapInt32(swap, seg_cmd->nsects); 532 sects_size = num_sects * sizeof(*seek_sect); 533 534 if (cmd_size != (sizeof(*seg_cmd) + sects_size)) { 535 result = macho_seek_result_error; 536 goto finish; 537 } 538 539 if (((void *)lc_cmd + cmd_size) > file_end) { 540 result = macho_seek_result_error; 541 goto finish; 542 } 543 544 for (sect_index = 0; sect_index < num_sects; sect_index++) { 545 546 seek_sect = (struct section *)((void *)lc_cmd + 547 sizeof(*seg_cmd) + 548 (sect_index * sizeof(*seek_sect))); 549 550 sect_data->sect_counter++; 551 552 if (sect_data->sect_counter == sect_data->sect_num) { 553 sect_data->sect_info = seek_sect; 554 result = macho_seek_result_found; 555 goto finish; 556 } 557 } 558 } 559 560finish: 561 return result; 562} 563 564/******************************************************************************* 565 * macho_find_source_version() 566 * 567 * Returns the contents of the version field of an LC_SOURCE_VERSION load 568 * command. 569 *******************************************************************************/ 570static macho_seek_result __source_version_callback( 571 struct load_command * load_command, 572 const void * file_end, 573 uint8_t swap __unused, 574 void * user_data) 575{ 576 uint64_t *versionPtr = (uint64_t *) user_data; 577 578 if (load_command->cmd == LC_SOURCE_VERSION) { 579 struct source_version_command * sv_command = (struct source_version_command *)load_command; 580 if (((void *)load_command + load_command->cmdsize) > file_end) { 581 return macho_seek_result_error; 582 } 583 *versionPtr = sv_command->version; 584 return macho_seek_result_found; 585 } 586 return macho_seek_result_not_found; 587} 588 589macho_seek_result macho_find_source_version( 590 const void * file_start, 591 const void * file_end, 592 uint64_t * version) 593{ 594 macho_seek_result result; 595 596 *version = 0; 597 result = macho_scan_load_commands(file_start, file_end, __source_version_callback, version); 598 return result; 599} 600 601/******************************************************************************* 602* 603*******************************************************************************/ 604#define CMDSIZE_MULT_32 (4) 605#define CMDSIZE_MULT_64 (8) 606 607macho_seek_result macho_scan_load_commands( 608 const void * file_start, 609 const void * file_end, 610 macho_lc_callback lc_callback, 611 void * user_data) 612{ 613 macho_seek_result result = macho_seek_result_not_found; 614 struct mach_header * mach_header = (struct mach_header *)file_start; 615 616 uint8_t swap = 0; 617 uint32_t cmdsize_mult = CMDSIZE_MULT_32; 618 619 uint32_t num_cmds; 620 uint32_t sizeofcmds; 621 char * cmds_end; 622 623 uint32_t cmd_index; 624 struct load_command * load_commands; 625 struct load_command * seek_lc; 626 627 switch (MAGIC32(file_start)) { 628 case MH_MAGIC_64: 629 cmdsize_mult = CMDSIZE_MULT_64; 630 break; 631 case MH_CIGAM_64: 632 cmdsize_mult = CMDSIZE_MULT_64; 633 swap = 1; 634 break; 635 case MH_CIGAM: 636 swap = 1; 637 break; 638 case MH_MAGIC: 639 break; 640 default: 641 result = macho_seek_result_error; 642 goto finish; 643 break; 644 } 645 646 if (cmdsize_mult == CMDSIZE_MULT_64) { 647 load_commands = (struct load_command *) 648 (file_start + sizeof(struct mach_header_64)); 649 } else { 650 load_commands = (struct load_command *) 651 (file_start + sizeof(struct mach_header)); 652 } 653 654 if (file_start >= file_end || (((void *)load_commands) > file_end)) { 655 result = macho_seek_result_error; 656 goto finish; 657 } 658 659 num_cmds = CondSwapInt32(swap, mach_header->ncmds); 660 sizeofcmds = CondSwapInt32(swap, mach_header->sizeofcmds); 661 cmds_end = (char *)load_commands + sizeofcmds; 662 663 if (cmds_end > (char *)file_end) { 664 result = macho_seek_result_error; 665 goto finish; 666 } 667 668 seek_lc = load_commands; 669 670 for (cmd_index = 0; cmd_index < num_cmds; cmd_index++) { 671 uint32_t cmd_size; 672 char * lc_end; 673 674 cmd_size = CondSwapInt32(swap, seek_lc->cmdsize); 675 lc_end = (char *)seek_lc + cmd_size; 676 677 if ((cmd_size % cmdsize_mult != 0) || (lc_end > cmds_end)) { 678 result = macho_seek_result_error; 679 goto finish; 680 } 681 682 result = lc_callback(seek_lc, file_end, swap, user_data); 683 684 switch (result) { 685 case macho_seek_result_not_found: 686 /* Not found, keep scanning. */ 687 break; 688 689 case macho_seek_result_stop: 690 /* Definitely found that it isn't there. */ 691 result = macho_seek_result_not_found; 692 goto finish; 693 break; 694 695 case macho_seek_result_found: 696 /* Found it! */ 697 goto finish; 698 break; 699 700 default: 701 /* Error, fall through default case. */ 702 result = macho_seek_result_error; 703 goto finish; 704 break; 705 } 706 707 seek_lc = (struct load_command *)((char *)seek_lc + cmd_size); 708 } 709 710finish: 711 return result; 712} 713 714/******************************************************************************* 715*******************************************************************************/ 716boolean_t macho_swap( 717 u_char * file) 718{ 719 boolean_t result = FALSE; 720 struct mach_header *hdr = (struct mach_header *) file; 721 722 if (hdr->magic == MH_CIGAM) { 723 result = macho_swap_32(file); 724 } else if (hdr->magic == MH_CIGAM_64) { 725 result = macho_swap_64(file); 726 } 727 728 return result; 729} 730 731/******************************************************************************* 732*******************************************************************************/ 733static boolean_t macho_swap_32( 734 u_char * file) 735{ 736 boolean_t result = FALSE; 737 struct mach_header *hdr = (struct mach_header *) file; 738 struct load_command *lc = (struct load_command *) &hdr[1]; 739 struct segment_command *seg = NULL; 740 u_long offset = 0; 741 u_int cmd = 0; 742 u_int cmdsize = 0; 743 u_int i = 0; 744 745 if (!hdr || hdr->magic != MH_CIGAM) goto finish; 746 747 swap_mach_header(hdr, NXHostByteOrder()); 748 749 offset = sizeof(*hdr); 750 for (i = 0; i < hdr->ncmds; ++i) { 751 lc = (struct load_command *) (file + offset); 752 753 cmd = OSSwapInt32(lc->cmd); 754 cmdsize = OSSwapInt32(lc->cmdsize); 755 offset += cmdsize; 756 757 if (cmd == LC_SEGMENT) { 758 seg = (struct segment_command *) lc; 759 swap_segment_command(seg, NXHostByteOrder()); 760 } else { 761 swap_load_command(lc, NXHostByteOrder()); 762 } 763 } 764 765 result = TRUE; 766finish: 767 return result; 768} 769 770/******************************************************************************* 771*******************************************************************************/ 772boolean_t macho_swap_64( 773 u_char * file) 774{ 775 boolean_t result = FALSE; 776 struct mach_header_64 *hdr = (struct mach_header_64 *) file; 777 struct load_command *lc = (struct load_command *) &hdr[1]; 778 struct segment_command_64 *seg = NULL; 779 u_long offset = 0; 780 u_int cmd = 0; 781 u_int cmdsize = 0; 782 u_int i = 0; 783 784 if (!hdr || hdr->magic != MH_CIGAM_64) goto finish; 785 786 swap_mach_header_64(hdr, NXHostByteOrder()); 787 788 offset = sizeof(*hdr); 789 for (i = 0; i < hdr->ncmds; ++i) { 790 lc = (struct load_command *) (file + offset); 791 792 cmd = OSSwapInt32(lc->cmd); 793 cmdsize = OSSwapInt32(lc->cmdsize); 794 offset += cmdsize; 795 796 if (cmd == LC_SEGMENT_64) { 797 seg = (struct segment_command_64 *) lc; 798 swap_segment_command_64(seg, NXHostByteOrder()); 799 } else { 800 swap_load_command(lc, NXHostByteOrder()); 801 } 802 } 803 804 result = TRUE; 805finish: 806 return result; 807} 808 809/******************************************************************************* 810*******************************************************************************/ 811boolean_t macho_unswap( 812 u_char * file) 813{ 814 boolean_t result = FALSE; 815 struct mach_header *hdr = (struct mach_header *) file; 816 817 if (hdr->magic == MH_MAGIC) { 818 result = macho_unswap_32(file); 819 } else if (hdr->magic == MH_MAGIC_64) { 820 result = macho_unswap_64(file); 821 } 822 823 return result; 824} 825 826/******************************************************************************* 827*******************************************************************************/ 828boolean_t macho_unswap_32( 829 u_char * file) 830{ 831 boolean_t result = FALSE; 832 enum NXByteOrder order = 0; 833 struct mach_header *hdr = (struct mach_header *) file; 834 struct load_command *lc = (struct load_command *) &hdr[1]; 835 struct segment_command *seg = NULL; 836 u_long offset = 0; 837 u_int i = 0; 838 839 if (NXHostByteOrder() == NX_LittleEndian) { 840 order = NX_BigEndian; 841 } else { 842 order = NX_LittleEndian; 843 } 844 845 if (!hdr || hdr->magic != MH_MAGIC) goto finish; 846 847 offset = sizeof(*hdr); 848 for (i = 0; i < hdr->ncmds; ++i) { 849 lc = (struct load_command *) (file + offset); 850 offset += lc->cmdsize; 851 852 if (lc->cmd == LC_SEGMENT) { 853 seg = (struct segment_command *) lc; 854 swap_segment_command(seg, order); 855 } else { 856 swap_load_command(lc, order); 857 } 858 859 } 860 861 swap_mach_header(hdr, order); 862 863 result = TRUE; 864finish: 865 return result; 866} 867 868/******************************************************************************* 869*******************************************************************************/ 870boolean_t macho_unswap_64( 871 u_char * file) 872{ 873 boolean_t result = FALSE; 874 enum NXByteOrder order = 0; 875 struct mach_header_64 *hdr = (struct mach_header_64 *) file; 876 struct load_command *lc = (struct load_command *) &hdr[1]; 877 struct segment_command_64 *seg = NULL; 878 u_long offset = 0; 879 u_int i = 0; 880 881 if (NXHostByteOrder() == NX_LittleEndian) { 882 order = NX_BigEndian; 883 } else { 884 order = NX_LittleEndian; 885 } 886 887 if (!hdr || hdr->magic != MH_MAGIC_64) goto finish; 888 889 offset = sizeof(*hdr); 890 for (i = 0; i < hdr->ncmds; ++i) { 891 lc = (struct load_command *) (file + offset); 892 offset += lc->cmdsize; 893 894 if (lc->cmd == LC_SEGMENT_64) { 895 seg = (struct segment_command_64 *) lc; 896 swap_segment_command_64(seg, order); 897 } else { 898 swap_load_command(lc, order); 899 } 900 } 901 902 swap_mach_header_64(hdr, order); 903 904 result = TRUE; 905finish: 906 return result; 907} 908 909/******************************************************************************* 910*******************************************************************************/ 911struct segment_command * macho_get_segment_by_name( 912 struct mach_header * mach_header, 913 const char * segname) 914{ 915 struct segment_command *segment = NULL; 916 struct load_command *lc = NULL; 917 u_char *base = (u_char *) mach_header; 918 size_t offset = sizeof(*mach_header); 919 u_int i = 0; 920 921 if (mach_header->magic != MH_MAGIC) goto finish; 922 923 for (i = 0; i < mach_header->ncmds; ++i) { 924 lc = (struct load_command *) (base + offset); 925 926 if (lc->cmd == LC_SEGMENT) { 927 segment = (struct segment_command *) lc; 928 if (!strncmp(segment->segname, segname, sizeof(segment->segname))) { 929 break; 930 } 931 segment = NULL; 932 } 933 934 offset += lc->cmdsize; 935 } 936 937finish: 938 return segment; 939} 940 941/******************************************************************************* 942*******************************************************************************/ 943struct segment_command_64 * macho_get_segment_by_name_64( 944 struct mach_header_64 * mach_header, 945 const char * segname) 946{ 947 struct segment_command_64 *segment = NULL; 948 struct load_command *lc = NULL; 949 u_char *base = (u_char *) mach_header; 950 size_t offset = sizeof(*mach_header); 951 u_int i = 0; 952 953 if (mach_header->magic != MH_MAGIC_64) goto finish; 954 955 for (i = 0; i < mach_header->ncmds; ++i) { 956 lc = (struct load_command *) (base + offset); 957 958 if (lc->cmd == LC_SEGMENT_64) { 959 segment = (struct segment_command_64 *) lc; 960 if (!strncmp(segment->segname, segname, sizeof(segment->segname))) { 961 break; 962 } 963 segment = NULL; 964 } 965 966 offset += lc->cmdsize; 967 } 968 969finish: 970 return segment; 971} 972 973/******************************************************************************* 974*******************************************************************************/ 975struct section * macho_get_section_by_name( 976 struct mach_header * mach_header, 977 const char * segname, 978 const char * sectname) 979{ 980 struct segment_command *segment = NULL; 981 struct section *section = NULL; 982 u_int i = 0; 983 984 if (mach_header->magic != MH_MAGIC) goto finish; 985 986 segment = macho_get_segment_by_name(mach_header, segname); 987 if (!segment) goto finish; 988 989 section = (struct section *) (&segment[1]); 990 for (i = 0; i < segment->nsects; ++i, ++section) { 991 if (!strncmp(section->sectname, sectname, sizeof(section->sectname))) { 992 break; 993 } 994 } 995 996 if (i == segment->nsects) { 997 section = NULL; 998 } 999 1000finish: 1001 return section; 1002} 1003 1004/******************************************************************************* 1005*******************************************************************************/ 1006struct section_64 * macho_get_section_by_name_64( 1007 struct mach_header_64 * mach_header, 1008 const char * segname, 1009 const char * sectname) 1010{ 1011 struct segment_command_64 *segment = NULL; 1012 struct section_64 *section = NULL; 1013 u_int i = 0; 1014 1015 if (mach_header->magic != MH_MAGIC_64) goto finish; 1016 1017 segment = macho_get_segment_by_name_64(mach_header, segname); 1018 if (!segment) goto finish; 1019 1020 section = (struct section_64 *) (&segment[1]); 1021 for (i = 0; i < segment->nsects; ++i, ++section) { 1022 if (!strncmp(section->sectname, sectname, sizeof(section->sectname))) { 1023 break; 1024 } 1025 } 1026 1027 if (i == segment->nsects) { 1028 section = NULL; 1029 } 1030 1031finish: 1032 return section; 1033} 1034 1035/******************************************************************************* 1036*******************************************************************************/ 1037boolean_t macho_remove_linkedit(u_char *macho, u_long *linkedit_size) 1038{ 1039 boolean_t result = FALSE; 1040 struct mach_header *mach_hdr; 1041 struct mach_header_64 *mach_hdr64; 1042 u_char *src, *dst; 1043 uint32_t ncmds, cmdsize; 1044 boolean_t swap = FALSE; 1045 u_int i; 1046 1047 swap = macho_swap(macho); 1048 1049 mach_hdr = (struct mach_header *) macho; 1050 mach_hdr64 = (struct mach_header_64 *) macho; 1051 1052 /* Find the start of the load commands */ 1053 1054 if (mach_hdr->magic == MH_MAGIC) { 1055 src = dst = macho + sizeof(*mach_hdr); 1056 ncmds = mach_hdr->ncmds; 1057 } else if (mach_hdr->magic == MH_MAGIC_64) { 1058 src = dst = macho + sizeof(*mach_hdr64); 1059 ncmds = mach_hdr64->ncmds; 1060 } else { 1061 goto finish; 1062 } 1063 1064 /* Remove any LINKEDIT-related load commands */ 1065 1066 for (i = 0; i < ncmds; ++i, src += cmdsize) { 1067 struct load_command * lc = (struct load_command *) src; 1068 struct segment_command *seg = (struct segment_command *) src; 1069 struct segment_command_64 *seg64 = (struct segment_command_64 *) src; 1070 boolean_t strip = FALSE; 1071 1072 cmdsize = lc->cmdsize; 1073 1074 /* We delete the LINKEDIT segment and any symtab load commands */ 1075 1076 switch (lc->cmd) { 1077 case LC_SEGMENT: 1078 if (!strncmp(seg->segname, SEG_LINKEDIT, sizeof(SEG_LINKEDIT) - 1)) { 1079 strip = TRUE; 1080 *linkedit_size = seg->vmsize; 1081 } 1082 break; 1083 case LC_SEGMENT_64: 1084 if (!strncmp(seg64->segname, SEG_LINKEDIT, sizeof(SEG_LINKEDIT) - 1)) { 1085 strip = TRUE; 1086 *linkedit_size = seg64->vmsize; 1087 } 1088 break; 1089 case LC_SYMTAB: 1090 case LC_DYSYMTAB: 1091 strip = TRUE; 1092 break; 1093 } 1094 1095 if (strip) { 1096 if (mach_hdr->magic == MH_MAGIC) { 1097 mach_hdr->ncmds--; 1098 mach_hdr->sizeofcmds -= cmdsize; 1099 } else { 1100 mach_hdr64->ncmds--; 1101 mach_hdr64->sizeofcmds -= cmdsize; 1102 } 1103 bzero(src, lc->cmdsize); 1104 } else { 1105 memmove(dst, src, cmdsize); 1106 dst += cmdsize; 1107 } 1108 } 1109 1110 result = TRUE; 1111finish: 1112 if (swap) macho_unswap(macho); 1113 return result; 1114} 1115 1116/******************************************************************************* 1117 * remove the symbol table and string table from the LINKEDIT segment, leaving * 1118 * any relocation data within the segment. * 1119*******************************************************************************/ 1120boolean_t macho_trim_linkedit( 1121 u_char *macho, 1122 u_long *amount_trimmed) 1123{ 1124 boolean_t result = FALSE; 1125 struct mach_header *mach_hdr; 1126 struct mach_header_64 *mach_hdr64; 1127 u_char *src, *dst; 1128 uint32_t ncmds, cmdsize; 1129 boolean_t swap = FALSE; 1130 boolean_t is32bit = FALSE; 1131 u_int i; 1132 u_char *linkedit_segment = NULL; 1133 struct symtab_command *symtab = NULL; 1134 struct dysymtab_command *dysymtab = NULL; 1135 1136 *amount_trimmed = 0; /* initialize */ 1137 1138 swap = macho_swap(macho); 1139 1140 mach_hdr = (struct mach_header *) macho; 1141 mach_hdr64 = (struct mach_header_64 *) macho; 1142 1143 /* Find the start of the load commands */ 1144 1145 if (mach_hdr->magic == MH_MAGIC) { 1146 src = dst = macho + sizeof(*mach_hdr); 1147 ncmds = mach_hdr->ncmds; 1148 is32bit = TRUE; 1149 } else if (mach_hdr->magic == MH_MAGIC_64) { 1150 src = dst = macho + sizeof(*mach_hdr64); 1151 ncmds = mach_hdr64->ncmds; 1152 is32bit = FALSE; 1153 } else { 1154 goto finish; 1155 } 1156 1157 /* find any LINKEDIT-related load commands */ 1158 1159 for (i = 0; i < ncmds; ++i, src += cmdsize) { 1160 struct load_command * lc = (struct load_command *) src; 1161 struct segment_command *seg = (struct segment_command *) src; 1162 struct segment_command_64 *seg64 = (struct segment_command_64 *) src; 1163 1164 cmdsize = lc->cmdsize; 1165 1166 /* First, identify the load commands of interest */ 1167 switch (lc->cmd) { 1168 case LC_SEGMENT: 1169 if (!strncmp(seg->segname, SEG_LINKEDIT, sizeof(SEG_LINKEDIT) - 1)) { 1170 linkedit_segment = src; 1171 } 1172 break; 1173 1174 case LC_SEGMENT_64: 1175 if (!strncmp(seg64->segname, SEG_LINKEDIT, sizeof(SEG_LINKEDIT) - 1)) { 1176 linkedit_segment = src; 1177 } 1178 break; 1179 1180 case LC_SYMTAB: 1181 symtab = (struct symtab_command *) src; 1182 break; 1183 1184 case LC_DYSYMTAB: 1185 dysymtab = (struct dysymtab_command *) src; 1186 break; 1187 } 1188 } 1189 1190 /* was a LINKEDIT segment found? (it damned well better be there!) */ 1191 if (linkedit_segment == NULL) 1192 goto finish; /* yowza! */ 1193 1194 /* if no DYSYMTAB command was found, just remove the entire LINKEDIT segment */ 1195 if (dysymtab == NULL) { 1196 if (swap) macho_unswap(macho); 1197 return (macho_remove_linkedit(macho, amount_trimmed)); 1198 } 1199 else { 1200 1201 /* Calculate size of symbol table (including strings): 1202 * # of symbols * sizeof (nlist | nlist_64)... 1203 * + size of string table... 1204 * aligned to 8-byte boundary 1205 */ 1206 u_long symtab_size = (((symtab->nsyms 1207 * (is32bit ? sizeof(struct nlist) : sizeof(struct nlist_64))) 1208 + symtab->strsize) + 7 ) & ~7; 1209 1210 /* calculate size of relocation entries */ 1211 u_long reloc_size = dysymtab->nlocrel * sizeof(struct relocation_info); 1212 1213 /* cache old vmsize */ 1214 u_long old_vmsize = 1215 (is32bit 1216 ? ((struct segment_command *) linkedit_segment)->vmsize 1217 : ((struct segment_command_64 *) linkedit_segment)->vmsize); 1218 1219 /* calculate new segment size after removal of symtab/stringtab data */ 1220 u_long new_vmsize = round_page(reloc_size); 1221 1222 /* If the relocation entries are positioned within the LINKEDIT segment AFTER 1223 * the symbol table, those entries must be moved within the segment. Otherwise, 1224 * the segment can simply be truncated to remove the symbol table. 1225 */ 1226 if (symtab->symoff < dysymtab->locreloff) { 1227 /* move them up within the segment, overwriting the existing symbol table */ 1228 memmove(macho + symtab->symoff, macho + dysymtab->locreloff, reloc_size); 1229 1230 /* update the header field */ 1231 dysymtab->locreloff = symtab->symoff; 1232 1233 /* clear now-unused data within the segment */ 1234 bzero(macho + dysymtab->locreloff + reloc_size, symtab_size); 1235 } 1236 else { 1237 /* symtab/stringtab entries are located after the relocation entries 1238 * in the segment. Therefore, we just have to truncate the segment 1239 * appropriately 1240 */ 1241 bzero(macho + symtab->symoff, symtab_size); /* wipe any existing data */ 1242 } 1243 1244 /* update LINKEDIT segment command with new size */ 1245 if (is32bit) { 1246 ((struct segment_command *) linkedit_segment)->vmsize = 1247 ((struct segment_command *) linkedit_segment)->filesize = new_vmsize; 1248 } 1249 else { 1250 ((struct segment_command_64 *) linkedit_segment)->vmsize = 1251 ((struct segment_command_64 *) linkedit_segment)->filesize = new_vmsize; 1252 } 1253 1254 /* notify caller of # of bytes removed from segment */ 1255 *amount_trimmed = old_vmsize - new_vmsize; 1256 } 1257 1258 /* now that the LINKEDIT segment contents have been adjusted properly, we must 1259 * remove the actual SYMTAB load command from the header 1260 */ 1261 src = dst; /* reset for second pass through header */ 1262 for (i = 0; i < ncmds; ++i, src += cmdsize) { 1263 struct load_command * lc = (struct load_command *) src; 1264 1265 cmdsize = lc->cmdsize; 1266 1267 if (lc->cmd == LC_SYMTAB) { 1268 if (is32bit) { 1269 mach_hdr->ncmds--; 1270 mach_hdr->sizeofcmds -= cmdsize; 1271 } else { 1272 mach_hdr64->ncmds--; 1273 mach_hdr64->sizeofcmds -= cmdsize; 1274 } 1275 bzero(src, lc->cmdsize); /* zap the SYMTAB command */ 1276 } 1277 else { 1278 /* move remaining load commands up within the header */ 1279 if (dst != src) { 1280 memmove(dst, src, cmdsize); 1281 } 1282 dst += cmdsize; 1283 } 1284 } 1285 1286 result = TRUE; 1287finish: 1288 if (swap) macho_unswap(macho); 1289 return result; 1290} 1291