1 /* simple-object-mach-o.c -- routines to manipulate Mach-O object files. 2 Copyright 2010, 2011, 2013 Free Software Foundation, Inc. 3 Written by Ian Lance Taylor, Google. 4 5 This program is free software; you can redistribute it and/or modify it 6 under the terms of the GNU General Public License as published by the 7 Free Software Foundation; either version 2, or (at your option) any 8 later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, 51 Franklin Street - Fifth Floor, 18 Boston, MA 02110-1301, USA. */ 19 20 #include "config.h" 21 #include "libiberty.h" 22 #include "simple-object.h" 23 24 #include <stddef.h> 25 26 #ifdef HAVE_STDLIB_H 27 #include <stdlib.h> 28 #endif 29 30 #ifdef HAVE_STDINT_H 31 #include <stdint.h> 32 #endif 33 34 #ifdef HAVE_STRING_H 35 #include <string.h> 36 #endif 37 38 #ifdef HAVE_INTTYPES_H 39 #include <inttypes.h> 40 #endif 41 42 #include "simple-object-common.h" 43 44 /* Mach-O structures and constants. */ 45 46 /* Mach-O header (32-bit version). */ 47 48 struct mach_o_header_32 49 { 50 unsigned char magic[4]; /* Magic number. */ 51 unsigned char cputype[4]; /* CPU that this object is for. */ 52 unsigned char cpusubtype[4]; /* CPU subtype. */ 53 unsigned char filetype[4]; /* Type of file. */ 54 unsigned char ncmds[4]; /* Number of load commands. */ 55 unsigned char sizeofcmds[4]; /* Total size of load commands. */ 56 unsigned char flags[4]; /* Flags for special featues. */ 57 }; 58 59 /* Mach-O header (64-bit version). */ 60 61 struct mach_o_header_64 62 { 63 unsigned char magic[4]; /* Magic number. */ 64 unsigned char cputype[4]; /* CPU that this object is for. */ 65 unsigned char cpusubtype[4]; /* CPU subtype. */ 66 unsigned char filetype[4]; /* Type of file. */ 67 unsigned char ncmds[4]; /* Number of load commands. */ 68 unsigned char sizeofcmds[4]; /* Total size of load commands. */ 69 unsigned char flags[4]; /* Flags for special featues. */ 70 unsigned char reserved[4]; /* Reserved. Duh. */ 71 }; 72 73 /* For magic field in header. */ 74 75 #define MACH_O_MH_MAGIC 0xfeedface 76 #define MACH_O_MH_MAGIC_64 0xfeedfacf 77 78 /* For filetype field in header. */ 79 80 #define MACH_O_MH_OBJECT 0x01 81 82 /* A Mach-O file is a list of load commands. This is the header of a 83 load command. */ 84 85 struct mach_o_load_command 86 { 87 unsigned char cmd[4]; /* The type of load command. */ 88 unsigned char cmdsize[4]; /* Size in bytes of entire command. */ 89 }; 90 91 /* For cmd field in load command. */ 92 93 #define MACH_O_LC_SEGMENT 0x01 94 #define MACH_O_LC_SEGMENT_64 0x19 95 96 /* LC_SEGMENT load command. */ 97 98 struct mach_o_segment_command_32 99 { 100 unsigned char cmd[4]; /* The type of load command (LC_SEGMENT). */ 101 unsigned char cmdsize[4]; /* Size in bytes of entire command. */ 102 unsigned char segname[16]; /* Name of this segment. */ 103 unsigned char vmaddr[4]; /* Virtual memory address of this segment. */ 104 unsigned char vmsize[4]; /* Size there, in bytes. */ 105 unsigned char fileoff[4]; /* Offset in bytes of the data to be mapped. */ 106 unsigned char filesize[4]; /* Size in bytes on disk. */ 107 unsigned char maxprot[4]; /* Maximum permitted vmem protection. */ 108 unsigned char initprot[4]; /* Initial vmem protection. */ 109 unsigned char nsects[4]; /* Number of sections in this segment. */ 110 unsigned char flags[4]; /* Flags that affect the loading. */ 111 }; 112 113 /* LC_SEGMENT_64 load command. */ 114 115 struct mach_o_segment_command_64 116 { 117 unsigned char cmd[4]; /* The type of load command (LC_SEGMENT_64). */ 118 unsigned char cmdsize[4]; /* Size in bytes of entire command. */ 119 unsigned char segname[16]; /* Name of this segment. */ 120 unsigned char vmaddr[8]; /* Virtual memory address of this segment. */ 121 unsigned char vmsize[8]; /* Size there, in bytes. */ 122 unsigned char fileoff[8]; /* Offset in bytes of the data to be mapped. */ 123 unsigned char filesize[8]; /* Size in bytes on disk. */ 124 unsigned char maxprot[4]; /* Maximum permitted vmem protection. */ 125 unsigned char initprot[4]; /* Initial vmem protection. */ 126 unsigned char nsects[4]; /* Number of sections in this segment. */ 127 unsigned char flags[4]; /* Flags that affect the loading. */ 128 }; 129 130 /* 32-bit section header. */ 131 132 struct mach_o_section_32 133 { 134 unsigned char sectname[16]; /* Section name. */ 135 unsigned char segname[16]; /* Segment that the section belongs to. */ 136 unsigned char addr[4]; /* Address of this section in memory. */ 137 unsigned char size[4]; /* Size in bytes of this section. */ 138 unsigned char offset[4]; /* File offset of this section. */ 139 unsigned char align[4]; /* log2 of this section's alignment. */ 140 unsigned char reloff[4]; /* File offset of this section's relocs. */ 141 unsigned char nreloc[4]; /* Number of relocs for this section. */ 142 unsigned char flags[4]; /* Section flags/attributes. */ 143 unsigned char reserved1[4]; 144 unsigned char reserved2[4]; 145 }; 146 147 /* 64-bit section header. */ 148 149 struct mach_o_section_64 150 { 151 unsigned char sectname[16]; /* Section name. */ 152 unsigned char segname[16]; /* Segment that the section belongs to. */ 153 unsigned char addr[8]; /* Address of this section in memory. */ 154 unsigned char size[8]; /* Size in bytes of this section. */ 155 unsigned char offset[4]; /* File offset of this section. */ 156 unsigned char align[4]; /* log2 of this section's alignment. */ 157 unsigned char reloff[4]; /* File offset of this section's relocs. */ 158 unsigned char nreloc[4]; /* Number of relocs for this section. */ 159 unsigned char flags[4]; /* Section flags/attributes. */ 160 unsigned char reserved1[4]; 161 unsigned char reserved2[4]; 162 unsigned char reserved3[4]; 163 }; 164 165 /* Flags for Mach-O sections. */ 166 167 #define MACH_O_S_ATTR_DEBUG 0x02000000 168 169 /* The length of a segment or section name. */ 170 171 #define MACH_O_NAME_LEN (16) 172 173 /* A GNU specific extension for long section names. */ 174 175 #define GNU_SECTION_NAMES "__section_names" 176 177 /* A GNU-specific extension to wrap multiple sections using three 178 mach-o sections within a given segment. The section '__wrapper_sects' 179 is subdivided according to the index '__wrapper_index' and each sub 180 sect is named according to the names supplied in '__wrapper_names'. */ 181 182 #define GNU_WRAPPER_SECTS "__wrapper_sects" 183 #define GNU_WRAPPER_INDEX "__wrapper_index" 184 #define GNU_WRAPPER_NAMES "__wrapper_names" 185 186 /* Private data for an simple_object_read. */ 187 188 struct simple_object_mach_o_read 189 { 190 /* User specified segment name. */ 191 char *segment_name; 192 /* Magic number. */ 193 unsigned int magic; 194 /* Whether this file is big-endian. */ 195 int is_big_endian; 196 /* CPU type from header. */ 197 unsigned int cputype; 198 /* CPU subtype from header. */ 199 unsigned int cpusubtype; 200 /* Number of commands, from header. */ 201 unsigned int ncmds; 202 /* Flags from header. */ 203 unsigned int flags; 204 /* Reserved field from header, only used on 64-bit. */ 205 unsigned int reserved; 206 }; 207 208 /* Private data for an simple_object_attributes. */ 209 210 struct simple_object_mach_o_attributes 211 { 212 /* Magic number. */ 213 unsigned int magic; 214 /* Whether this file is big-endian. */ 215 int is_big_endian; 216 /* CPU type from header. */ 217 unsigned int cputype; 218 /* CPU subtype from header. */ 219 unsigned int cpusubtype; 220 /* Flags from header. */ 221 unsigned int flags; 222 /* Reserved field from header, only used on 64-bit. */ 223 unsigned int reserved; 224 }; 225 226 /* See if we have a Mach-O MH_OBJECT file: 227 228 A standard MH_OBJECT (from as) will have three load commands: 229 0 - LC_SEGMENT/LC_SEGMENT64 230 1 - LC_SYMTAB 231 2 - LC_DYSYMTAB 232 233 The LC_SEGMENT/LC_SEGMENT64 will introduce a single anonymous segment 234 containing all the sections. 235 236 Files written by simple-object will have only the segment command 237 (no symbol tables). */ 238 239 static void * 240 simple_object_mach_o_match ( 241 unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN], 242 int descriptor, 243 off_t offset, 244 const char *segment_name, 245 const char **errmsg, 246 int *err) 247 { 248 unsigned int magic; 249 int is_big_endian; 250 unsigned int (*fetch_32) (const unsigned char *); 251 unsigned int filetype; 252 struct simple_object_mach_o_read *omr; 253 unsigned char buf[sizeof (struct mach_o_header_64)]; 254 unsigned char *b; 255 256 magic = simple_object_fetch_big_32 (header); 257 if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64) 258 is_big_endian = 1; 259 else 260 { 261 magic = simple_object_fetch_little_32 (header); 262 if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64) 263 is_big_endian = 0; 264 else 265 { 266 *errmsg = NULL; 267 *err = 0; 268 return NULL; 269 } 270 } 271 272 #ifndef UNSIGNED_64BIT_TYPE 273 if (magic == MACH_O_MH_MAGIC_64) 274 { 275 *errmsg = "64-bit Mach-O objects not supported"; 276 *err = 0; 277 return NULL; 278 } 279 #endif 280 281 /* We require the user to provide a segment name. This is 282 unfortunate but I don't see any good choices here. */ 283 284 if (segment_name == NULL) 285 { 286 *errmsg = "Mach-O file found but no segment name specified"; 287 *err = 0; 288 return NULL; 289 } 290 291 if (strlen (segment_name) > MACH_O_NAME_LEN) 292 { 293 *errmsg = "Mach-O segment name too long"; 294 *err = 0; 295 return NULL; 296 } 297 298 /* The 32-bit and 64-bit headers are similar enough that we can use 299 the same code. */ 300 301 fetch_32 = (is_big_endian 302 ? simple_object_fetch_big_32 303 : simple_object_fetch_little_32); 304 305 if (!simple_object_internal_read (descriptor, offset, buf, 306 (magic == MACH_O_MH_MAGIC 307 ? sizeof (struct mach_o_header_32) 308 : sizeof (struct mach_o_header_64)), 309 errmsg, err)) 310 return NULL; 311 312 b = &buf[0]; 313 314 filetype = (*fetch_32) (b + offsetof (struct mach_o_header_32, filetype)); 315 if (filetype != MACH_O_MH_OBJECT) 316 { 317 *errmsg = "Mach-O file is not object file"; 318 *err = 0; 319 return NULL; 320 } 321 322 omr = XNEW (struct simple_object_mach_o_read); 323 omr->segment_name = xstrdup (segment_name); 324 omr->magic = magic; 325 omr->is_big_endian = is_big_endian; 326 omr->cputype = (*fetch_32) (b + offsetof (struct mach_o_header_32, cputype)); 327 omr->cpusubtype = (*fetch_32) (b 328 + offsetof (struct mach_o_header_32, 329 cpusubtype)); 330 omr->ncmds = (*fetch_32) (b + offsetof (struct mach_o_header_32, ncmds)); 331 omr->flags = (*fetch_32) (b + offsetof (struct mach_o_header_32, flags)); 332 if (magic == MACH_O_MH_MAGIC) 333 omr->reserved = 0; 334 else 335 omr->reserved = (*fetch_32) (b 336 + offsetof (struct mach_o_header_64, 337 reserved)); 338 339 return (void *) omr; 340 } 341 342 /* Get the file offset and size from a section header. */ 343 344 static void 345 simple_object_mach_o_section_info (int is_big_endian, int is_32, 346 const unsigned char *sechdr, off_t *offset, 347 size_t *size) 348 { 349 unsigned int (*fetch_32) (const unsigned char *); 350 ulong_type (*fetch_64) (const unsigned char *); 351 352 fetch_32 = (is_big_endian 353 ? simple_object_fetch_big_32 354 : simple_object_fetch_little_32); 355 356 fetch_64 = NULL; 357 #ifdef UNSIGNED_64BIT_TYPE 358 fetch_64 = (is_big_endian 359 ? simple_object_fetch_big_64 360 : simple_object_fetch_little_64); 361 #endif 362 363 if (is_32) 364 { 365 *offset = fetch_32 (sechdr 366 + offsetof (struct mach_o_section_32, offset)); 367 *size = fetch_32 (sechdr 368 + offsetof (struct mach_o_section_32, size)); 369 } 370 else 371 { 372 *offset = fetch_32 (sechdr 373 + offsetof (struct mach_o_section_64, offset)); 374 *size = fetch_64 (sechdr 375 + offsetof (struct mach_o_section_64, size)); 376 } 377 } 378 379 /* Handle a segment in a Mach-O Object file. 380 381 This will callback to the function pfn for each "section found" the meaning 382 of which depends on gnu extensions to mach-o: 383 384 If we find mach-o sections (with the segment name as specified) which also 385 contain: a 'sects' wrapper, an index, and a name table, we expand this into 386 as many sections as are specified in the index. In this case, there will 387 be a callback for each of these. 388 389 We will also allow an extension that permits long names (more than 16 390 characters) to be used with mach-o. In this case, the section name has 391 a specific format embedding an index into a name table, and the file must 392 contain such name table. 393 394 Return 1 if we should continue, 0 if the caller should return. */ 395 396 #define SOMO_SECTS_PRESENT 0x01 397 #define SOMO_INDEX_PRESENT 0x02 398 #define SOMO_NAMES_PRESENT 0x04 399 #define SOMO_LONGN_PRESENT 0x08 400 #define SOMO_WRAPPING (SOMO_SECTS_PRESENT | SOMO_INDEX_PRESENT \ 401 | SOMO_NAMES_PRESENT) 402 403 static int 404 simple_object_mach_o_segment (simple_object_read *sobj, off_t offset, 405 const unsigned char *segbuf, 406 int (*pfn) (void *, const char *, off_t offset, 407 off_t length), 408 void *data, 409 const char **errmsg, int *err) 410 { 411 struct simple_object_mach_o_read *omr = 412 (struct simple_object_mach_o_read *) sobj->data; 413 unsigned int (*fetch_32) (const unsigned char *); 414 int is_32; 415 size_t seghdrsize; 416 size_t sechdrsize; 417 size_t segname_offset; 418 size_t sectname_offset; 419 unsigned int nsects; 420 unsigned char *secdata; 421 unsigned int i; 422 unsigned int gnu_sections_found; 423 unsigned int strtab_index; 424 unsigned int index_index; 425 unsigned int nametab_index; 426 unsigned int sections_index; 427 char *strtab; 428 char *nametab; 429 unsigned char *index; 430 size_t strtab_size; 431 size_t nametab_size; 432 size_t index_size; 433 unsigned int n_wrapped_sects; 434 size_t wrapper_sect_size; 435 off_t wrapper_sect_offset = 0; 436 437 fetch_32 = (omr->is_big_endian 438 ? simple_object_fetch_big_32 439 : simple_object_fetch_little_32); 440 441 is_32 = omr->magic == MACH_O_MH_MAGIC; 442 443 if (is_32) 444 { 445 seghdrsize = sizeof (struct mach_o_segment_command_32); 446 sechdrsize = sizeof (struct mach_o_section_32); 447 segname_offset = offsetof (struct mach_o_section_32, segname); 448 sectname_offset = offsetof (struct mach_o_section_32, sectname); 449 nsects = (*fetch_32) (segbuf 450 + offsetof (struct mach_o_segment_command_32, 451 nsects)); 452 } 453 else 454 { 455 seghdrsize = sizeof (struct mach_o_segment_command_64); 456 sechdrsize = sizeof (struct mach_o_section_64); 457 segname_offset = offsetof (struct mach_o_section_64, segname); 458 sectname_offset = offsetof (struct mach_o_section_64, sectname); 459 nsects = (*fetch_32) (segbuf 460 + offsetof (struct mach_o_segment_command_64, 461 nsects)); 462 } 463 464 /* Fetch the section headers from the segment command. */ 465 466 secdata = XNEWVEC (unsigned char, nsects * sechdrsize); 467 if (!simple_object_internal_read (sobj->descriptor, offset + seghdrsize, 468 secdata, nsects * sechdrsize, errmsg, err)) 469 { 470 XDELETEVEC (secdata); 471 return 0; 472 } 473 474 /* Scan for special sections that signal GNU extensions to the format. */ 475 476 gnu_sections_found = 0; 477 index_index = nsects; 478 sections_index = nsects; 479 strtab_index = nsects; 480 nametab_index = nsects; 481 for (i = 0; i < nsects; ++i) 482 { 483 size_t nameoff; 484 485 nameoff = i * sechdrsize + segname_offset; 486 if (strcmp ((char *) secdata + nameoff, omr->segment_name) != 0) 487 continue; 488 489 nameoff = i * sechdrsize + sectname_offset; 490 if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_NAMES) == 0) 491 { 492 nametab_index = i; 493 gnu_sections_found |= SOMO_NAMES_PRESENT; 494 } 495 else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_INDEX) == 0) 496 { 497 index_index = i; 498 gnu_sections_found |= SOMO_INDEX_PRESENT; 499 } 500 else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_SECTS) == 0) 501 { 502 sections_index = i; 503 gnu_sections_found |= SOMO_SECTS_PRESENT; 504 } 505 else if (strcmp ((char *) secdata + nameoff, GNU_SECTION_NAMES) == 0) 506 { 507 strtab_index = i; 508 gnu_sections_found |= SOMO_LONGN_PRESENT; 509 } 510 } 511 512 /* If any of the special wrapper section components is present, then 513 they all should be. */ 514 515 if ((gnu_sections_found & SOMO_WRAPPING) != 0) 516 { 517 off_t nametab_offset; 518 off_t index_offset; 519 520 if ((gnu_sections_found & SOMO_WRAPPING) != SOMO_WRAPPING) 521 { 522 *errmsg = "GNU Mach-o section wrapper: required section missing"; 523 *err = 0; /* No useful errno. */ 524 XDELETEVEC (secdata); 525 return 0; 526 } 527 528 /* Fetch the name table. */ 529 530 simple_object_mach_o_section_info (omr->is_big_endian, is_32, 531 secdata + nametab_index * sechdrsize, 532 &nametab_offset, &nametab_size); 533 nametab = XNEWVEC (char, nametab_size); 534 if (!simple_object_internal_read (sobj->descriptor, 535 sobj->offset + nametab_offset, 536 (unsigned char *) nametab, nametab_size, 537 errmsg, err)) 538 { 539 XDELETEVEC (nametab); 540 XDELETEVEC (secdata); 541 return 0; 542 } 543 544 /* Fetch the index. */ 545 546 simple_object_mach_o_section_info (omr->is_big_endian, is_32, 547 secdata + index_index * sechdrsize, 548 &index_offset, &index_size); 549 index = XNEWVEC (unsigned char, index_size); 550 if (!simple_object_internal_read (sobj->descriptor, 551 sobj->offset + index_offset, 552 index, index_size, 553 errmsg, err)) 554 { 555 XDELETEVEC (index); 556 XDELETEVEC (nametab); 557 XDELETEVEC (secdata); 558 return 0; 559 } 560 561 /* The index contains 4 unsigned ints per sub-section: 562 sub-section offset/length, sub-section name/length. 563 We fix this for both 32 and 64 bit mach-o for now, since 564 other fields limit the maximum size of an object to 4G. */ 565 n_wrapped_sects = index_size / 16; 566 567 /* Get the parameters for the wrapper too. */ 568 simple_object_mach_o_section_info (omr->is_big_endian, is_32, 569 secdata + sections_index * sechdrsize, 570 &wrapper_sect_offset, 571 &wrapper_sect_size); 572 } 573 else 574 { 575 index = NULL; 576 index_size = 0; 577 nametab = NULL; 578 nametab_size = 0; 579 n_wrapped_sects = 0; 580 } 581 582 /* If we have a long names section, fetch it. */ 583 584 if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0) 585 { 586 off_t strtab_offset; 587 588 simple_object_mach_o_section_info (omr->is_big_endian, is_32, 589 secdata + strtab_index * sechdrsize, 590 &strtab_offset, &strtab_size); 591 strtab = XNEWVEC (char, strtab_size); 592 if (!simple_object_internal_read (sobj->descriptor, 593 sobj->offset + strtab_offset, 594 (unsigned char *) strtab, strtab_size, 595 errmsg, err)) 596 { 597 XDELETEVEC (strtab); 598 XDELETEVEC (index); 599 XDELETEVEC (nametab); 600 XDELETEVEC (secdata); 601 return 0; 602 } 603 } 604 else 605 { 606 strtab = NULL; 607 strtab_size = 0; 608 strtab_index = nsects; 609 } 610 611 /* Process the sections. */ 612 613 for (i = 0; i < nsects; ++i) 614 { 615 const unsigned char *sechdr; 616 char namebuf[MACH_O_NAME_LEN * 2 + 2]; 617 char *name; 618 off_t secoffset; 619 size_t secsize; 620 int l; 621 622 sechdr = secdata + i * sechdrsize; 623 624 /* We've already processed the long section names. */ 625 626 if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0 627 && i == strtab_index) 628 continue; 629 630 /* We only act on the segment named. */ 631 632 if (strcmp ((char *) sechdr + segname_offset, omr->segment_name) != 0) 633 continue; 634 635 /* Process sections associated with the wrapper. */ 636 637 if ((gnu_sections_found & SOMO_WRAPPING) != 0) 638 { 639 if (i == nametab_index || i == index_index) 640 continue; 641 642 if (i == sections_index) 643 { 644 unsigned int j; 645 for (j = 0; j < n_wrapped_sects; ++j) 646 { 647 unsigned int subsect_offset, subsect_length, name_offset; 648 subsect_offset = (*fetch_32) (index + 16 * j); 649 subsect_length = (*fetch_32) (index + 16 * j + 4); 650 name_offset = (*fetch_32) (index + 16 * j + 8); 651 /* We don't need the name_length yet. */ 652 653 secoffset = wrapper_sect_offset + subsect_offset; 654 secsize = subsect_length; 655 name = nametab + name_offset; 656 657 if (!(*pfn) (data, name, secoffset, secsize)) 658 { 659 *errmsg = NULL; 660 *err = 0; 661 XDELETEVEC (index); 662 XDELETEVEC (nametab); 663 XDELETEVEC (strtab); 664 XDELETEVEC (secdata); 665 return 0; 666 } 667 } 668 continue; 669 } 670 } 671 672 if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0) 673 { 674 memcpy (namebuf, sechdr + sectname_offset, MACH_O_NAME_LEN); 675 namebuf[MACH_O_NAME_LEN] = '\0'; 676 677 name = &namebuf[0]; 678 if (strtab != NULL && name[0] == '_' && name[1] == '_') 679 { 680 unsigned long stringoffset; 681 682 if (sscanf (name + 2, "%08lX", &stringoffset) == 1) 683 { 684 if (stringoffset >= strtab_size) 685 { 686 *errmsg = "section name offset out of range"; 687 *err = 0; 688 XDELETEVEC (index); 689 XDELETEVEC (nametab); 690 XDELETEVEC (strtab); 691 XDELETEVEC (secdata); 692 return 0; 693 } 694 695 name = strtab + stringoffset; 696 } 697 } 698 } 699 else 700 { 701 /* Otherwise, make a name like __segment,__section as per the 702 convention in mach-o asm. */ 703 name = &namebuf[0]; 704 memcpy (namebuf, (char *) sechdr + segname_offset, MACH_O_NAME_LEN); 705 namebuf[MACH_O_NAME_LEN] = '\0'; 706 l = strlen (namebuf); 707 namebuf[l] = ','; 708 memcpy (namebuf + l + 1, (char *) sechdr + sectname_offset, 709 MACH_O_NAME_LEN); 710 namebuf[l + 1 + MACH_O_NAME_LEN] = '\0'; 711 } 712 713 simple_object_mach_o_section_info (omr->is_big_endian, is_32, sechdr, 714 &secoffset, &secsize); 715 716 if (!(*pfn) (data, name, secoffset, secsize)) 717 { 718 *errmsg = NULL; 719 *err = 0; 720 XDELETEVEC (index); 721 XDELETEVEC (nametab); 722 XDELETEVEC (strtab); 723 XDELETEVEC (secdata); 724 return 0; 725 } 726 } 727 728 XDELETEVEC (index); 729 XDELETEVEC (nametab); 730 XDELETEVEC (strtab); 731 XDELETEVEC (secdata); 732 733 return 1; 734 } 735 736 /* Find all sections in a Mach-O file. */ 737 738 static const char * 739 simple_object_mach_o_find_sections (simple_object_read *sobj, 740 int (*pfn) (void *, const char *, 741 off_t offset, off_t length), 742 void *data, 743 int *err) 744 { 745 struct simple_object_mach_o_read *omr = 746 (struct simple_object_mach_o_read *) sobj->data; 747 off_t offset; 748 size_t seghdrsize; 749 unsigned int (*fetch_32) (const unsigned char *); 750 const char *errmsg; 751 unsigned int i; 752 753 if (omr->magic == MACH_O_MH_MAGIC) 754 { 755 offset = sizeof (struct mach_o_header_32); 756 seghdrsize = sizeof (struct mach_o_segment_command_32); 757 } 758 else 759 { 760 offset = sizeof (struct mach_o_header_64); 761 seghdrsize = sizeof (struct mach_o_segment_command_64); 762 } 763 764 fetch_32 = (omr->is_big_endian 765 ? simple_object_fetch_big_32 766 : simple_object_fetch_little_32); 767 768 for (i = 0; i < omr->ncmds; ++i) 769 { 770 unsigned char loadbuf[sizeof (struct mach_o_load_command)]; 771 unsigned int cmd; 772 unsigned int cmdsize; 773 774 if (!simple_object_internal_read (sobj->descriptor, 775 sobj->offset + offset, 776 loadbuf, 777 sizeof (struct mach_o_load_command), 778 &errmsg, err)) 779 return errmsg; 780 781 cmd = (*fetch_32) (loadbuf + offsetof (struct mach_o_load_command, cmd)); 782 cmdsize = (*fetch_32) (loadbuf 783 + offsetof (struct mach_o_load_command, cmdsize)); 784 785 if (cmd == MACH_O_LC_SEGMENT || cmd == MACH_O_LC_SEGMENT_64) 786 { 787 unsigned char segbuf[sizeof (struct mach_o_segment_command_64)]; 788 int r; 789 790 if (!simple_object_internal_read (sobj->descriptor, 791 sobj->offset + offset, 792 segbuf, seghdrsize, &errmsg, err)) 793 return errmsg; 794 795 r = simple_object_mach_o_segment (sobj, offset, segbuf, pfn, 796 data, &errmsg, err); 797 if (!r) 798 return errmsg; 799 } 800 801 offset += cmdsize; 802 } 803 804 return NULL; 805 } 806 807 /* Fetch the attributes for an simple_object_read. */ 808 809 static void * 810 simple_object_mach_o_fetch_attributes (simple_object_read *sobj, 811 const char **errmsg ATTRIBUTE_UNUSED, 812 int *err ATTRIBUTE_UNUSED) 813 { 814 struct simple_object_mach_o_read *omr = 815 (struct simple_object_mach_o_read *) sobj->data; 816 struct simple_object_mach_o_attributes *ret; 817 818 ret = XNEW (struct simple_object_mach_o_attributes); 819 ret->magic = omr->magic; 820 ret->is_big_endian = omr->is_big_endian; 821 ret->cputype = omr->cputype; 822 ret->cpusubtype = omr->cpusubtype; 823 ret->flags = omr->flags; 824 ret->reserved = omr->reserved; 825 return ret; 826 } 827 828 /* Release the private data for an simple_object_read. */ 829 830 static void 831 simple_object_mach_o_release_read (void *data) 832 { 833 struct simple_object_mach_o_read *omr = 834 (struct simple_object_mach_o_read *) data; 835 836 free (omr->segment_name); 837 XDELETE (omr); 838 } 839 840 /* Compare two attributes structures. */ 841 842 static const char * 843 simple_object_mach_o_attributes_merge (void *todata, void *fromdata, int *err) 844 { 845 struct simple_object_mach_o_attributes *to = 846 (struct simple_object_mach_o_attributes *) todata; 847 struct simple_object_mach_o_attributes *from = 848 (struct simple_object_mach_o_attributes *) fromdata; 849 850 if (to->magic != from->magic 851 || to->is_big_endian != from->is_big_endian 852 || to->cputype != from->cputype) 853 { 854 *err = 0; 855 return "Mach-O object format mismatch"; 856 } 857 return NULL; 858 } 859 860 /* Release the private data for an attributes structure. */ 861 862 static void 863 simple_object_mach_o_release_attributes (void *data) 864 { 865 XDELETE (data); 866 } 867 868 /* Prepare to write out a file. */ 869 870 static void * 871 simple_object_mach_o_start_write (void *attributes_data, 872 const char **errmsg ATTRIBUTE_UNUSED, 873 int *err ATTRIBUTE_UNUSED) 874 { 875 struct simple_object_mach_o_attributes *attrs = 876 (struct simple_object_mach_o_attributes *) attributes_data; 877 struct simple_object_mach_o_attributes *ret; 878 879 /* We're just going to record the attributes, but we need to make a 880 copy because the user may delete them. */ 881 ret = XNEW (struct simple_object_mach_o_attributes); 882 *ret = *attrs; 883 return ret; 884 } 885 886 /* Write out the header of a Mach-O file. */ 887 888 static int 889 simple_object_mach_o_write_header (simple_object_write *sobj, int descriptor, 890 size_t nsects, const char **errmsg, 891 int *err) 892 { 893 struct simple_object_mach_o_attributes *attrs = 894 (struct simple_object_mach_o_attributes *) sobj->data; 895 void (*set_32) (unsigned char *, unsigned int); 896 unsigned char hdrbuf[sizeof (struct mach_o_header_64)]; 897 unsigned char *hdr; 898 size_t wrsize; 899 900 set_32 = (attrs->is_big_endian 901 ? simple_object_set_big_32 902 : simple_object_set_little_32); 903 904 memset (hdrbuf, 0, sizeof hdrbuf); 905 906 /* The 32-bit and 64-bit headers start out the same. */ 907 908 hdr = &hdrbuf[0]; 909 set_32 (hdr + offsetof (struct mach_o_header_32, magic), attrs->magic); 910 set_32 (hdr + offsetof (struct mach_o_header_32, cputype), attrs->cputype); 911 set_32 (hdr + offsetof (struct mach_o_header_32, cpusubtype), 912 attrs->cpusubtype); 913 set_32 (hdr + offsetof (struct mach_o_header_32, filetype), MACH_O_MH_OBJECT); 914 set_32 (hdr + offsetof (struct mach_o_header_32, ncmds), 1); 915 set_32 (hdr + offsetof (struct mach_o_header_32, flags), attrs->flags); 916 if (attrs->magic == MACH_O_MH_MAGIC) 917 { 918 wrsize = sizeof (struct mach_o_header_32); 919 set_32 (hdr + offsetof (struct mach_o_header_32, sizeofcmds), 920 (sizeof (struct mach_o_segment_command_32) 921 + nsects * sizeof (struct mach_o_section_32))); 922 } 923 else 924 { 925 set_32 (hdr + offsetof (struct mach_o_header_64, sizeofcmds), 926 (sizeof (struct mach_o_segment_command_64) 927 + nsects * sizeof (struct mach_o_section_64))); 928 set_32 (hdr + offsetof (struct mach_o_header_64, reserved), 929 attrs->reserved); 930 wrsize = sizeof (struct mach_o_header_64); 931 } 932 933 return simple_object_internal_write (descriptor, 0, hdrbuf, wrsize, 934 errmsg, err); 935 } 936 937 /* Write a Mach-O section header. */ 938 939 static int 940 simple_object_mach_o_write_section_header (simple_object_write *sobj, 941 int descriptor, 942 size_t sechdr_offset, 943 const char *name, const char *segn, 944 size_t secaddr, size_t secsize, 945 size_t offset, unsigned int align, 946 const char **errmsg, int *err) 947 { 948 struct simple_object_mach_o_attributes *attrs = 949 (struct simple_object_mach_o_attributes *) sobj->data; 950 void (*set_32) (unsigned char *, unsigned int); 951 unsigned char hdrbuf[sizeof (struct mach_o_section_64)]; 952 unsigned char *hdr; 953 size_t sechdrsize; 954 955 set_32 = (attrs->is_big_endian 956 ? simple_object_set_big_32 957 : simple_object_set_little_32); 958 959 memset (hdrbuf, 0, sizeof hdrbuf); 960 961 hdr = &hdrbuf[0]; 962 if (attrs->magic == MACH_O_MH_MAGIC) 963 { 964 strncpy ((char *) hdr + offsetof (struct mach_o_section_32, sectname), 965 name, MACH_O_NAME_LEN); 966 strncpy ((char *) hdr + offsetof (struct mach_o_section_32, segname), 967 segn, MACH_O_NAME_LEN); 968 set_32 (hdr + offsetof (struct mach_o_section_32, addr), secaddr); 969 set_32 (hdr + offsetof (struct mach_o_section_32, size), secsize); 970 set_32 (hdr + offsetof (struct mach_o_section_32, offset), offset); 971 set_32 (hdr + offsetof (struct mach_o_section_32, align), align); 972 /* reloff left as zero. */ 973 /* nreloc left as zero. */ 974 set_32 (hdr + offsetof (struct mach_o_section_32, flags), 975 MACH_O_S_ATTR_DEBUG); 976 /* reserved1 left as zero. */ 977 /* reserved2 left as zero. */ 978 sechdrsize = sizeof (struct mach_o_section_32); 979 } 980 else 981 { 982 #ifdef UNSIGNED_64BIT_TYPE 983 void (*set_64) (unsigned char *, ulong_type); 984 985 set_64 = (attrs->is_big_endian 986 ? simple_object_set_big_64 987 : simple_object_set_little_64); 988 989 strncpy ((char *) hdr + offsetof (struct mach_o_section_64, sectname), 990 name, MACH_O_NAME_LEN); 991 strncpy ((char *) hdr + offsetof (struct mach_o_section_64, segname), 992 segn, MACH_O_NAME_LEN); 993 set_64 (hdr + offsetof (struct mach_o_section_64, addr), secaddr); 994 set_64 (hdr + offsetof (struct mach_o_section_64, size), secsize); 995 set_32 (hdr + offsetof (struct mach_o_section_64, offset), offset); 996 set_32 (hdr + offsetof (struct mach_o_section_64, align), align); 997 /* reloff left as zero. */ 998 /* nreloc left as zero. */ 999 set_32 (hdr + offsetof (struct mach_o_section_64, flags), 1000 MACH_O_S_ATTR_DEBUG); 1001 /* reserved1 left as zero. */ 1002 /* reserved2 left as zero. */ 1003 /* reserved3 left as zero. */ 1004 #endif 1005 sechdrsize = sizeof (struct mach_o_section_64); 1006 } 1007 1008 return simple_object_internal_write (descriptor, sechdr_offset, hdr, 1009 sechdrsize, errmsg, err); 1010 } 1011 1012 /* Write out the single (anonymous) segment containing the sections of a Mach-O 1013 Object file. 1014 1015 As a GNU extension to mach-o, when the caller specifies a segment name in 1016 sobj->segment_name, all the sections passed will be output under a single 1017 mach-o section header. The caller's sections are indexed within this 1018 'wrapper' section by a table stored in a second mach-o section. Finally, 1019 arbitrary length section names are permitted by the extension and these are 1020 stored in a table in a third mach-o section. 1021 1022 Note that this is only likely to make any sense for the __GNU_LTO segment 1023 at present. 1024 1025 If the wrapper extension is not in force, we assume that the section name 1026 is in the form __SEGMENT_NAME,__section_name as per Mach-O asm. */ 1027 1028 static int 1029 simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, 1030 size_t *nsects, const char **errmsg, 1031 int *err) 1032 { 1033 struct simple_object_mach_o_attributes *attrs = 1034 (struct simple_object_mach_o_attributes *) sobj->data; 1035 void (*set_32) (unsigned char *, unsigned int); 1036 size_t hdrsize; 1037 size_t seghdrsize; 1038 size_t sechdrsize; 1039 size_t cmdsize; 1040 size_t offset; 1041 size_t sechdr_offset; 1042 size_t secaddr; 1043 unsigned int name_offset; 1044 simple_object_write_section *section; 1045 unsigned char hdrbuf[sizeof (struct mach_o_segment_command_64)]; 1046 unsigned char *hdr; 1047 size_t nsects_in; 1048 unsigned int *index; 1049 char *snames; 1050 unsigned int sect; 1051 1052 set_32 = (attrs->is_big_endian 1053 ? simple_object_set_big_32 1054 : simple_object_set_little_32); 1055 1056 /* Write out the sections first. */ 1057 1058 if (attrs->magic == MACH_O_MH_MAGIC) 1059 { 1060 hdrsize = sizeof (struct mach_o_header_32); 1061 seghdrsize = sizeof (struct mach_o_segment_command_32); 1062 sechdrsize = sizeof (struct mach_o_section_32); 1063 } 1064 else 1065 { 1066 hdrsize = sizeof (struct mach_o_header_64); 1067 seghdrsize = sizeof (struct mach_o_segment_command_64); 1068 sechdrsize = sizeof (struct mach_o_section_64); 1069 } 1070 1071 name_offset = 0; 1072 *nsects = nsects_in = 0; 1073 1074 /* Count the number of sections we start with. */ 1075 1076 for (section = sobj->sections; section != NULL; section = section->next) 1077 nsects_in++; 1078 1079 if (sobj->segment_name != NULL) 1080 { 1081 /* We will only write 3 sections: wrapped data, index and names. */ 1082 1083 *nsects = 3; 1084 1085 /* The index has four entries per wrapped section: 1086 Section Offset, length, Name offset, length. 1087 Where the offsets are based at the start of the wrapper and name 1088 sections respectively. 1089 The values are stored as 32 bit int for both 32 and 64 bit mach-o 1090 since the size of a mach-o MH_OBJECT cannot exceed 4G owing to 1091 other constraints. */ 1092 1093 index = XNEWVEC (unsigned int, nsects_in * 4); 1094 1095 /* We now need to figure out the size of the names section. This just 1096 stores the names as null-terminated c strings, packed without any 1097 alignment padding. */ 1098 1099 for (section = sobj->sections, sect = 0; section != NULL; 1100 section = section->next, sect++) 1101 { 1102 index[sect*4+2] = name_offset; 1103 index[sect*4+3] = strlen (section->name) + 1; 1104 name_offset += strlen (section->name) + 1; 1105 } 1106 snames = XNEWVEC (char, name_offset); 1107 } 1108 else 1109 { 1110 *nsects = nsects_in; 1111 index = NULL; 1112 snames = NULL; 1113 } 1114 1115 sechdr_offset = hdrsize + seghdrsize; 1116 cmdsize = seghdrsize + *nsects * sechdrsize; 1117 offset = hdrsize + cmdsize; 1118 secaddr = 0; 1119 1120 for (section = sobj->sections, sect = 0; 1121 section != NULL; section = section->next, sect++) 1122 { 1123 size_t mask; 1124 size_t new_offset; 1125 size_t secsize; 1126 struct simple_object_write_section_buffer *buffer; 1127 1128 mask = (1U << section->align) - 1; 1129 new_offset = offset + mask; 1130 new_offset &= ~ mask; 1131 while (new_offset > offset) 1132 { 1133 unsigned char zeroes[16]; 1134 size_t write; 1135 1136 memset (zeroes, 0, sizeof zeroes); 1137 write = new_offset - offset; 1138 if (write > sizeof zeroes) 1139 write = sizeof zeroes; 1140 if (!simple_object_internal_write (descriptor, offset, zeroes, write, 1141 errmsg, err)) 1142 return 0; 1143 offset += write; 1144 } 1145 1146 secsize = 0; 1147 for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) 1148 { 1149 if (!simple_object_internal_write (descriptor, offset + secsize, 1150 ((const unsigned char *) 1151 buffer->buffer), 1152 buffer->size, errmsg, err)) 1153 return 0; 1154 secsize += buffer->size; 1155 } 1156 1157 if (sobj->segment_name != NULL) 1158 { 1159 index[sect*4+0] = (unsigned int) offset; 1160 index[sect*4+1] = secsize; 1161 /* Stash the section name in our table. */ 1162 memcpy (snames + index[sect * 4 + 2], section->name, 1163 index[sect * 4 + 3]); 1164 } 1165 else 1166 { 1167 char namebuf[MACH_O_NAME_LEN + 1]; 1168 char segnbuf[MACH_O_NAME_LEN + 1]; 1169 char *comma; 1170 1171 /* Try to extract segment,section from the input name. */ 1172 1173 memset (namebuf, 0, sizeof namebuf); 1174 memset (segnbuf, 0, sizeof segnbuf); 1175 comma = strchr (section->name, ','); 1176 if (comma != NULL) 1177 { 1178 int len = comma - section->name; 1179 len = len > MACH_O_NAME_LEN ? MACH_O_NAME_LEN : len; 1180 strncpy (namebuf, section->name, len); 1181 strncpy (segnbuf, comma + 1, MACH_O_NAME_LEN); 1182 } 1183 else /* just try to copy the name, leave segment blank. */ 1184 strncpy (namebuf, section->name, MACH_O_NAME_LEN); 1185 1186 if (!simple_object_mach_o_write_section_header (sobj, descriptor, 1187 sechdr_offset, 1188 namebuf, segnbuf, 1189 secaddr, secsize, 1190 offset, 1191 section->align, 1192 errmsg, err)) 1193 return 0; 1194 sechdr_offset += sechdrsize; 1195 } 1196 1197 offset += secsize; 1198 secaddr += secsize; 1199 } 1200 1201 if (sobj->segment_name != NULL) 1202 { 1203 size_t secsize; 1204 unsigned int i; 1205 1206 /* Write the section header for the wrapper. */ 1207 /* Account for any initial aligment - which becomes the alignment for this 1208 created section. */ 1209 1210 secsize = (offset - index[0]); 1211 if (!simple_object_mach_o_write_section_header (sobj, descriptor, 1212 sechdr_offset, 1213 GNU_WRAPPER_SECTS, 1214 sobj->segment_name, 1215 0 /*secaddr*/, 1216 secsize, index[0], 1217 sobj->sections->align, 1218 errmsg, err)) 1219 return 0; 1220 1221 /* Subtract the wrapper section start from the begining of each sub 1222 section. */ 1223 1224 for (i = 1; i < nsects_in; ++i) 1225 index[4 * i] -= index[0]; 1226 index[0] = 0; 1227 1228 sechdr_offset += sechdrsize; 1229 1230 /* Write out the section names. 1231 ... the header ... 1232 name_offset contains the length of the section. It is not aligned. */ 1233 1234 if (!simple_object_mach_o_write_section_header (sobj, descriptor, 1235 sechdr_offset, 1236 GNU_WRAPPER_NAMES, 1237 sobj->segment_name, 1238 0 /*secaddr*/, 1239 name_offset, 1240 offset, 1241 0, errmsg, err)) 1242 return 0; 1243 1244 /* ... and the content.. */ 1245 if (!simple_object_internal_write (descriptor, offset, 1246 (const unsigned char *) snames, 1247 name_offset, errmsg, err)) 1248 return 0; 1249 1250 sechdr_offset += sechdrsize; 1251 secaddr += name_offset; 1252 offset += name_offset; 1253 1254 /* Now do the index, we'll align this to 4 bytes although the read code 1255 will handle unaligned. */ 1256 1257 offset += 3; 1258 offset &= ~0x03; 1259 if (!simple_object_mach_o_write_section_header (sobj, descriptor, 1260 sechdr_offset, 1261 GNU_WRAPPER_INDEX, 1262 sobj->segment_name, 1263 0 /*secaddr*/, 1264 nsects_in * 16, 1265 offset, 1266 2, errmsg, err)) 1267 return 0; 1268 1269 /* ... and the content.. */ 1270 if (!simple_object_internal_write (descriptor, offset, 1271 (const unsigned char *) index, 1272 nsects_in*16, errmsg, err)) 1273 return 0; 1274 1275 XDELETEVEC (index); 1276 XDELETEVEC (snames); 1277 } 1278 1279 /* Write out the segment header. */ 1280 1281 memset (hdrbuf, 0, sizeof hdrbuf); 1282 1283 hdr = &hdrbuf[0]; 1284 if (attrs->magic == MACH_O_MH_MAGIC) 1285 { 1286 set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmd), 1287 MACH_O_LC_SEGMENT); 1288 set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmdsize), 1289 cmdsize); 1290 /* MH_OBJECTS have a single, anonymous, segment - so the segment name 1291 is left empty. */ 1292 /* vmaddr left as zero. */ 1293 /* vmsize left as zero. */ 1294 set_32 (hdr + offsetof (struct mach_o_segment_command_32, fileoff), 1295 hdrsize + cmdsize); 1296 set_32 (hdr + offsetof (struct mach_o_segment_command_32, filesize), 1297 offset - (hdrsize + cmdsize)); 1298 /* maxprot left as zero. */ 1299 /* initprot left as zero. */ 1300 set_32 (hdr + offsetof (struct mach_o_segment_command_32, nsects), 1301 *nsects); 1302 /* flags left as zero. */ 1303 } 1304 else 1305 { 1306 #ifdef UNSIGNED_64BIT_TYPE 1307 void (*set_64) (unsigned char *, ulong_type); 1308 1309 set_64 = (attrs->is_big_endian 1310 ? simple_object_set_big_64 1311 : simple_object_set_little_64); 1312 1313 set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmd), 1314 MACH_O_LC_SEGMENT); 1315 set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmdsize), 1316 cmdsize); 1317 /* MH_OBJECTS have a single, anonymous, segment - so the segment name 1318 is left empty. */ 1319 /* vmaddr left as zero. */ 1320 /* vmsize left as zero. */ 1321 set_64 (hdr + offsetof (struct mach_o_segment_command_64, fileoff), 1322 hdrsize + cmdsize); 1323 set_64 (hdr + offsetof (struct mach_o_segment_command_64, filesize), 1324 offset - (hdrsize + cmdsize)); 1325 /* maxprot left as zero. */ 1326 /* initprot left as zero. */ 1327 set_32 (hdr + offsetof (struct mach_o_segment_command_64, nsects), 1328 *nsects); 1329 /* flags left as zero. */ 1330 #endif 1331 } 1332 1333 return simple_object_internal_write (descriptor, hdrsize, hdr, seghdrsize, 1334 errmsg, err); 1335 } 1336 1337 /* Write out a complete Mach-O file. */ 1338 1339 static const char * 1340 simple_object_mach_o_write_to_file (simple_object_write *sobj, int descriptor, 1341 int *err) 1342 { 1343 size_t nsects = 0; 1344 const char *errmsg; 1345 1346 if (!simple_object_mach_o_write_segment (sobj, descriptor, &nsects, 1347 &errmsg, err)) 1348 return errmsg; 1349 1350 if (!simple_object_mach_o_write_header (sobj, descriptor, nsects, 1351 &errmsg, err)) 1352 return errmsg; 1353 1354 return NULL; 1355 } 1356 1357 /* Release the private data for an simple_object_write structure. */ 1358 1359 static void 1360 simple_object_mach_o_release_write (void *data) 1361 { 1362 XDELETE (data); 1363 } 1364 1365 /* The Mach-O functions. */ 1366 1367 const struct simple_object_functions simple_object_mach_o_functions = 1368 { 1369 simple_object_mach_o_match, 1370 simple_object_mach_o_find_sections, 1371 simple_object_mach_o_fetch_attributes, 1372 simple_object_mach_o_release_read, 1373 simple_object_mach_o_attributes_merge, 1374 simple_object_mach_o_release_attributes, 1375 simple_object_mach_o_start_write, 1376 simple_object_mach_o_write_to_file, 1377 simple_object_mach_o_release_write 1378 }; 1379