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