|  | /* simple-object-mach-o.c -- routines to manipulate Mach-O object files. | 
|  | Copyright (C) 2010-2021 Free Software Foundation, Inc. | 
|  | Written by Ian Lance Taylor, Google. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify it | 
|  | under the terms of the GNU General Public License as published by the | 
|  | Free Software Foundation; either version 2, or (at your option) any | 
|  | later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program; if not, write to the Free Software | 
|  | Foundation, 51 Franklin Street - Fifth Floor, | 
|  | Boston, MA 02110-1301, USA.  */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "libiberty.h" | 
|  | #include "simple-object.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #ifdef HAVE_STDLIB_H | 
|  | #include <stdlib.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef HAVE_STDINT_H | 
|  | #include <stdint.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef HAVE_STRING_H | 
|  | #include <string.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef HAVE_INTTYPES_H | 
|  | #include <inttypes.h> | 
|  | #endif | 
|  |  | 
|  | #include "simple-object-common.h" | 
|  |  | 
|  | /* Mach-O structures and constants.  */ | 
|  |  | 
|  | /* Mach-O header (32-bit version).  */ | 
|  |  | 
|  | struct mach_o_header_32 | 
|  | { | 
|  | unsigned char magic[4];	/* Magic number.  */ | 
|  | unsigned char cputype[4];	/* CPU that this object is for.  */ | 
|  | unsigned char cpusubtype[4];	/* CPU subtype.  */ | 
|  | unsigned char filetype[4];	/* Type of file.  */ | 
|  | unsigned char ncmds[4];	/* Number of load commands.  */ | 
|  | unsigned char sizeofcmds[4];	/* Total size of load commands.  */ | 
|  | unsigned char flags[4];	/* Flags for special featues.  */ | 
|  | }; | 
|  |  | 
|  | /* Mach-O header (64-bit version).  */ | 
|  |  | 
|  | struct mach_o_header_64 | 
|  | { | 
|  | unsigned char magic[4];	/* Magic number.  */ | 
|  | unsigned char cputype[4];	/* CPU that this object is for.  */ | 
|  | unsigned char cpusubtype[4];	/* CPU subtype.  */ | 
|  | unsigned char filetype[4];	/* Type of file.  */ | 
|  | unsigned char ncmds[4];	/* Number of load commands.  */ | 
|  | unsigned char sizeofcmds[4];	/* Total size of load commands.  */ | 
|  | unsigned char flags[4];	/* Flags for special featues.  */ | 
|  | unsigned char reserved[4];	/* Reserved.  Duh.  */ | 
|  | }; | 
|  |  | 
|  | /* For magic field in header.  */ | 
|  |  | 
|  | #define MACH_O_MH_MAGIC			0xfeedface | 
|  | #define MACH_O_MH_MAGIC_64		0xfeedfacf | 
|  |  | 
|  | /* For filetype field in header.  */ | 
|  |  | 
|  | #define MACH_O_MH_OBJECT		0x01 | 
|  |  | 
|  | /* A Mach-O file is a list of load commands.  This is the header of a | 
|  | load command.  */ | 
|  |  | 
|  | struct mach_o_load_command | 
|  | { | 
|  | unsigned char cmd[4];		/* The type of load command.  */ | 
|  | unsigned char cmdsize[4];	/* Size in bytes of entire command.  */ | 
|  | }; | 
|  |  | 
|  | /* For cmd field in load command.   */ | 
|  |  | 
|  | #define MACH_O_LC_SEGMENT		0x01 | 
|  | #define MACH_O_LC_SEGMENT_64		0x19 | 
|  |  | 
|  | /* LC_SEGMENT load command.  */ | 
|  |  | 
|  | struct mach_o_segment_command_32 | 
|  | { | 
|  | unsigned char cmd[4];		/* The type of load command (LC_SEGMENT).  */ | 
|  | unsigned char cmdsize[4];	/* Size in bytes of entire command.  */ | 
|  | unsigned char segname[16];	/* Name of this segment.  */ | 
|  | unsigned char vmaddr[4];	/* Virtual memory address of this segment.  */ | 
|  | unsigned char vmsize[4];	/* Size there, in bytes.  */ | 
|  | unsigned char fileoff[4];	/* Offset in bytes of the data to be mapped.  */ | 
|  | unsigned char filesize[4];	/* Size in bytes on disk.  */ | 
|  | unsigned char maxprot[4];	/* Maximum permitted vmem protection.  */ | 
|  | unsigned char initprot[4];	/* Initial vmem protection.  */ | 
|  | unsigned char nsects[4];	/* Number of sections in this segment.  */ | 
|  | unsigned char flags[4];	/* Flags that affect the loading.  */ | 
|  | }; | 
|  |  | 
|  | /* LC_SEGMENT_64 load command.  */ | 
|  |  | 
|  | struct mach_o_segment_command_64 | 
|  | { | 
|  | unsigned char cmd[4];		/* The type of load command (LC_SEGMENT_64).  */ | 
|  | unsigned char cmdsize[4];	/* Size in bytes of entire command.  */ | 
|  | unsigned char segname[16];	/* Name of this segment.  */ | 
|  | unsigned char vmaddr[8];	/* Virtual memory address of this segment.  */ | 
|  | unsigned char vmsize[8];	/* Size there, in bytes.  */ | 
|  | unsigned char fileoff[8];	/* Offset in bytes of the data to be mapped.  */ | 
|  | unsigned char filesize[8];	/* Size in bytes on disk.  */ | 
|  | unsigned char maxprot[4];	/* Maximum permitted vmem protection.  */ | 
|  | unsigned char initprot[4];	/* Initial vmem protection.  */ | 
|  | unsigned char nsects[4];	/* Number of sections in this segment.  */ | 
|  | unsigned char flags[4];	/* Flags that affect the loading.  */ | 
|  | }; | 
|  |  | 
|  | /* 32-bit section header.  */ | 
|  |  | 
|  | struct mach_o_section_32 | 
|  | { | 
|  | unsigned char sectname[16];	/* Section name.  */ | 
|  | unsigned char segname[16];	/* Segment that the section belongs to.  */ | 
|  | unsigned char addr[4];	/* Address of this section in memory.  */ | 
|  | unsigned char size[4];	/* Size in bytes of this section.  */ | 
|  | unsigned char offset[4];	/* File offset of this section.  */ | 
|  | unsigned char align[4];	/* log2 of this section's alignment.  */ | 
|  | unsigned char reloff[4];	/* File offset of this section's relocs.  */ | 
|  | unsigned char nreloc[4];	/* Number of relocs for this section.  */ | 
|  | unsigned char flags[4];	/* Section flags/attributes.  */ | 
|  | unsigned char reserved1[4]; | 
|  | unsigned char reserved2[4]; | 
|  | }; | 
|  |  | 
|  | /* 64-bit section header.  */ | 
|  |  | 
|  | struct mach_o_section_64 | 
|  | { | 
|  | unsigned char sectname[16];	/* Section name.  */ | 
|  | unsigned char segname[16];	/* Segment that the section belongs to.  */ | 
|  | unsigned char addr[8];	/* Address of this section in memory.  */ | 
|  | unsigned char size[8];	/* Size in bytes of this section.  */ | 
|  | unsigned char offset[4];	/* File offset of this section.  */ | 
|  | unsigned char align[4];	/* log2 of this section's alignment.  */ | 
|  | unsigned char reloff[4];	/* File offset of this section's relocs.  */ | 
|  | unsigned char nreloc[4];	/* Number of relocs for this section.  */ | 
|  | unsigned char flags[4];	/* Section flags/attributes.  */ | 
|  | unsigned char reserved1[4]; | 
|  | unsigned char reserved2[4]; | 
|  | unsigned char reserved3[4]; | 
|  | }; | 
|  |  | 
|  | /* Flags for Mach-O sections.  */ | 
|  |  | 
|  | #define MACH_O_S_ATTR_DEBUG			0x02000000 | 
|  |  | 
|  | /* The length of a segment or section name.  */ | 
|  |  | 
|  | #define MACH_O_NAME_LEN (16) | 
|  |  | 
|  | /* A GNU specific extension for long section names.  */ | 
|  |  | 
|  | #define GNU_SECTION_NAMES "__section_names" | 
|  |  | 
|  | /* A GNU-specific extension to wrap multiple sections using three | 
|  | mach-o sections within a given segment.  The section '__wrapper_sects' | 
|  | is subdivided according to the index '__wrapper_index' and each sub | 
|  | sect is named according to the names supplied in '__wrapper_names'.  */ | 
|  |  | 
|  | #define GNU_WRAPPER_SECTS "__wrapper_sects" | 
|  | #define GNU_WRAPPER_INDEX "__wrapper_index" | 
|  | #define GNU_WRAPPER_NAMES "__wrapper_names" | 
|  |  | 
|  | /* Private data for an simple_object_read.  */ | 
|  |  | 
|  | struct simple_object_mach_o_read | 
|  | { | 
|  | /* User specified segment name.  */ | 
|  | char *segment_name; | 
|  | /* Magic number.  */ | 
|  | unsigned int magic; | 
|  | /* Whether this file is big-endian.  */ | 
|  | int is_big_endian; | 
|  | /* CPU type from header.  */ | 
|  | unsigned int cputype; | 
|  | /* CPU subtype from header.  */ | 
|  | unsigned int cpusubtype; | 
|  | /* Number of commands, from header.  */ | 
|  | unsigned int ncmds; | 
|  | /* Flags from header.  */ | 
|  | unsigned int flags; | 
|  | /* Reserved field from header, only used on 64-bit.  */ | 
|  | unsigned int reserved; | 
|  | }; | 
|  |  | 
|  | /* Private data for an simple_object_attributes.  */ | 
|  |  | 
|  | struct simple_object_mach_o_attributes | 
|  | { | 
|  | /* Magic number.  */ | 
|  | unsigned int magic; | 
|  | /* Whether this file is big-endian.  */ | 
|  | int is_big_endian; | 
|  | /* CPU type from header.  */ | 
|  | unsigned int cputype; | 
|  | /* CPU subtype from header.  */ | 
|  | unsigned int cpusubtype; | 
|  | /* Flags from header.  */ | 
|  | unsigned int flags; | 
|  | /* Reserved field from header, only used on 64-bit.  */ | 
|  | unsigned int reserved; | 
|  | }; | 
|  |  | 
|  | /* See if we have a Mach-O MH_OBJECT file: | 
|  |  | 
|  | A standard MH_OBJECT (from as) will have three load commands: | 
|  | 0 - LC_SEGMENT/LC_SEGMENT64 | 
|  | 1 - LC_SYMTAB | 
|  | 2 - LC_DYSYMTAB | 
|  |  | 
|  | The LC_SEGMENT/LC_SEGMENT64 will introduce a single anonymous segment | 
|  | containing all the sections. | 
|  |  | 
|  | Files written by simple-object will have only the segment command | 
|  | (no symbol tables).  */ | 
|  |  | 
|  | static void * | 
|  | simple_object_mach_o_match ( | 
|  | unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN], | 
|  | int descriptor, | 
|  | off_t offset, | 
|  | const char *segment_name, | 
|  | const char **errmsg, | 
|  | int *err) | 
|  | { | 
|  | unsigned int magic; | 
|  | int is_big_endian; | 
|  | unsigned int (*fetch_32) (const unsigned char *); | 
|  | unsigned int filetype; | 
|  | struct simple_object_mach_o_read *omr; | 
|  | unsigned char buf[sizeof (struct mach_o_header_64)]; | 
|  | unsigned char *b; | 
|  |  | 
|  | magic = simple_object_fetch_big_32 (header); | 
|  | if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64) | 
|  | is_big_endian = 1; | 
|  | else | 
|  | { | 
|  | magic = simple_object_fetch_little_32 (header); | 
|  | if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64) | 
|  | is_big_endian = 0; | 
|  | else | 
|  | { | 
|  | *errmsg = NULL; | 
|  | *err = 0; | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifndef UNSIGNED_64BIT_TYPE | 
|  | if (magic == MACH_O_MH_MAGIC_64) | 
|  | { | 
|  | *errmsg = "64-bit Mach-O objects not supported"; | 
|  | *err = 0; | 
|  | return NULL; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* We require the user to provide a segment name.  This is | 
|  | unfortunate but I don't see any good choices here.  */ | 
|  |  | 
|  | if (segment_name == NULL) | 
|  | { | 
|  | *errmsg = "Mach-O file found but no segment name specified"; | 
|  | *err = 0; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | if (strlen (segment_name) > MACH_O_NAME_LEN) | 
|  | { | 
|  | *errmsg = "Mach-O segment name too long"; | 
|  | *err = 0; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* The 32-bit and 64-bit headers are similar enough that we can use | 
|  | the same code.  */ | 
|  |  | 
|  | fetch_32 = (is_big_endian | 
|  | ? simple_object_fetch_big_32 | 
|  | : simple_object_fetch_little_32); | 
|  |  | 
|  | if (!simple_object_internal_read (descriptor, offset, buf, | 
|  | (magic == MACH_O_MH_MAGIC | 
|  | ? sizeof (struct mach_o_header_32) | 
|  | : sizeof (struct mach_o_header_64)), | 
|  | errmsg, err)) | 
|  | return NULL; | 
|  |  | 
|  | b = &buf[0]; | 
|  |  | 
|  | filetype = (*fetch_32) (b + offsetof (struct mach_o_header_32, filetype)); | 
|  | if (filetype != MACH_O_MH_OBJECT) | 
|  | { | 
|  | *errmsg = "Mach-O file is not object file"; | 
|  | *err = 0; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | omr = XNEW (struct simple_object_mach_o_read); | 
|  | omr->segment_name = xstrdup (segment_name); | 
|  | omr->magic = magic; | 
|  | omr->is_big_endian = is_big_endian; | 
|  | omr->cputype = (*fetch_32) (b + offsetof (struct mach_o_header_32, cputype)); | 
|  | omr->cpusubtype = (*fetch_32) (b | 
|  | + offsetof (struct mach_o_header_32, | 
|  | cpusubtype)); | 
|  | omr->ncmds = (*fetch_32) (b + offsetof (struct mach_o_header_32, ncmds)); | 
|  | omr->flags = (*fetch_32) (b + offsetof (struct mach_o_header_32, flags)); | 
|  | if (magic == MACH_O_MH_MAGIC) | 
|  | omr->reserved = 0; | 
|  | else | 
|  | omr->reserved = (*fetch_32) (b | 
|  | + offsetof (struct mach_o_header_64, | 
|  | reserved)); | 
|  |  | 
|  | return (void *) omr; | 
|  | } | 
|  |  | 
|  | /* Get the file offset and size from a section header.  */ | 
|  |  | 
|  | static void | 
|  | simple_object_mach_o_section_info (int is_big_endian, int is_32, | 
|  | const unsigned char *sechdr, off_t *offset, | 
|  | size_t *size) | 
|  | { | 
|  | unsigned int (*fetch_32) (const unsigned char *); | 
|  | ulong_type (*fetch_64) (const unsigned char *); | 
|  |  | 
|  | fetch_32 = (is_big_endian | 
|  | ? simple_object_fetch_big_32 | 
|  | : simple_object_fetch_little_32); | 
|  |  | 
|  | fetch_64 = NULL; | 
|  | #ifdef UNSIGNED_64BIT_TYPE | 
|  | fetch_64 = (is_big_endian | 
|  | ? simple_object_fetch_big_64 | 
|  | : simple_object_fetch_little_64); | 
|  | #endif | 
|  |  | 
|  | if (is_32) | 
|  | { | 
|  | *offset = fetch_32 (sechdr | 
|  | + offsetof (struct mach_o_section_32, offset)); | 
|  | *size = fetch_32 (sechdr | 
|  | + offsetof (struct mach_o_section_32, size)); | 
|  | } | 
|  | else | 
|  | { | 
|  | *offset = fetch_32 (sechdr | 
|  | + offsetof (struct mach_o_section_64, offset)); | 
|  | *size = fetch_64 (sechdr | 
|  | + offsetof (struct mach_o_section_64, size)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Handle a segment in a Mach-O Object file. | 
|  |  | 
|  | This will callback to the function pfn for each "section found" the meaning | 
|  | of which depends on gnu extensions to mach-o: | 
|  |  | 
|  | If we find mach-o sections (with the segment name as specified) which also | 
|  | contain: a 'sects' wrapper, an index, and a  name table, we expand this into | 
|  | as many sections as are specified in the index.  In this case, there will | 
|  | be a callback for each of these. | 
|  |  | 
|  | We will also allow an extension that permits long names (more than 16 | 
|  | characters) to be used with mach-o.  In this case, the section name has | 
|  | a specific format embedding an index into a name table, and the file must | 
|  | contain such name table. | 
|  |  | 
|  | Return 1 if we should continue, 0 if the caller should return.  */ | 
|  |  | 
|  | #define SOMO_SECTS_PRESENT 0x01 | 
|  | #define SOMO_INDEX_PRESENT 0x02 | 
|  | #define SOMO_NAMES_PRESENT 0x04 | 
|  | #define SOMO_LONGN_PRESENT 0x08 | 
|  | #define SOMO_WRAPPING (SOMO_SECTS_PRESENT | SOMO_INDEX_PRESENT \ | 
|  | | SOMO_NAMES_PRESENT) | 
|  |  | 
|  | static int | 
|  | simple_object_mach_o_segment (simple_object_read *sobj, off_t offset, | 
|  | const unsigned char *segbuf, | 
|  | int (*pfn) (void *, const char *, off_t offset, | 
|  | off_t length), | 
|  | void *data, | 
|  | const char **errmsg, int *err) | 
|  | { | 
|  | struct simple_object_mach_o_read *omr = | 
|  | (struct simple_object_mach_o_read *) sobj->data; | 
|  | unsigned int (*fetch_32) (const unsigned char *); | 
|  | int is_32; | 
|  | size_t seghdrsize; | 
|  | size_t sechdrsize; | 
|  | size_t segname_offset; | 
|  | size_t sectname_offset; | 
|  | unsigned int nsects; | 
|  | unsigned char *secdata; | 
|  | unsigned int i; | 
|  | unsigned int gnu_sections_found; | 
|  | unsigned int strtab_index; | 
|  | unsigned int index_index; | 
|  | unsigned int nametab_index; | 
|  | unsigned int sections_index; | 
|  | char *strtab; | 
|  | char *nametab; | 
|  | unsigned char *index; | 
|  | size_t strtab_size; | 
|  | size_t nametab_size; | 
|  | size_t index_size; | 
|  | unsigned int n_wrapped_sects; | 
|  | size_t wrapper_sect_size; | 
|  | off_t wrapper_sect_offset = 0; | 
|  |  | 
|  | fetch_32 = (omr->is_big_endian | 
|  | ? simple_object_fetch_big_32 | 
|  | : simple_object_fetch_little_32); | 
|  |  | 
|  | is_32 = omr->magic == MACH_O_MH_MAGIC; | 
|  |  | 
|  | if (is_32) | 
|  | { | 
|  | seghdrsize = sizeof (struct mach_o_segment_command_32); | 
|  | sechdrsize = sizeof (struct mach_o_section_32); | 
|  | segname_offset = offsetof (struct mach_o_section_32, segname); | 
|  | sectname_offset = offsetof (struct mach_o_section_32, sectname); | 
|  | nsects = (*fetch_32) (segbuf | 
|  | + offsetof (struct mach_o_segment_command_32, | 
|  | nsects)); | 
|  | } | 
|  | else | 
|  | { | 
|  | seghdrsize = sizeof (struct mach_o_segment_command_64); | 
|  | sechdrsize = sizeof (struct mach_o_section_64); | 
|  | segname_offset = offsetof (struct mach_o_section_64, segname); | 
|  | sectname_offset = offsetof (struct mach_o_section_64, sectname); | 
|  | nsects = (*fetch_32) (segbuf | 
|  | + offsetof (struct mach_o_segment_command_64, | 
|  | nsects)); | 
|  | } | 
|  |  | 
|  | /* Fetch the section headers from the segment command.  */ | 
|  |  | 
|  | secdata = XNEWVEC (unsigned char, nsects * sechdrsize); | 
|  | if (!simple_object_internal_read (sobj->descriptor, offset + seghdrsize, | 
|  | secdata, nsects * sechdrsize, errmsg, err)) | 
|  | { | 
|  | XDELETEVEC (secdata); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Scan for special sections that signal GNU extensions to the format.  */ | 
|  |  | 
|  | gnu_sections_found = 0; | 
|  | index_index = nsects; | 
|  | sections_index = nsects; | 
|  | strtab_index = nsects; | 
|  | nametab_index = nsects; | 
|  | for (i = 0; i < nsects; ++i) | 
|  | { | 
|  | size_t nameoff; | 
|  |  | 
|  | nameoff = i * sechdrsize + segname_offset; | 
|  | if (strcmp ((char *) secdata + nameoff, omr->segment_name) != 0) | 
|  | continue; | 
|  |  | 
|  | nameoff = i * sechdrsize + sectname_offset; | 
|  | if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_NAMES) == 0) | 
|  | { | 
|  | nametab_index = i; | 
|  | gnu_sections_found |= SOMO_NAMES_PRESENT; | 
|  | } | 
|  | else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_INDEX) == 0) | 
|  | { | 
|  | index_index = i; | 
|  | gnu_sections_found |= SOMO_INDEX_PRESENT; | 
|  | } | 
|  | else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_SECTS) == 0) | 
|  | { | 
|  | sections_index = i; | 
|  | gnu_sections_found |= SOMO_SECTS_PRESENT; | 
|  | } | 
|  | else if (strcmp ((char *) secdata + nameoff, GNU_SECTION_NAMES) == 0) | 
|  | { | 
|  | strtab_index = i; | 
|  | gnu_sections_found |= SOMO_LONGN_PRESENT; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If any of the special wrapper section components is present, then | 
|  | they all should be.  */ | 
|  |  | 
|  | if ((gnu_sections_found & SOMO_WRAPPING) != 0) | 
|  | { | 
|  | off_t nametab_offset; | 
|  | off_t index_offset; | 
|  |  | 
|  | if ((gnu_sections_found & SOMO_WRAPPING) != SOMO_WRAPPING) | 
|  | { | 
|  | *errmsg = "GNU Mach-o section wrapper: required section missing"; | 
|  | *err = 0; /* No useful errno.  */ | 
|  | XDELETEVEC (secdata); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Fetch the name table.  */ | 
|  |  | 
|  | simple_object_mach_o_section_info (omr->is_big_endian, is_32, | 
|  | secdata + nametab_index * sechdrsize, | 
|  | &nametab_offset, &nametab_size); | 
|  | nametab = XNEWVEC (char, nametab_size); | 
|  | if (!simple_object_internal_read (sobj->descriptor, | 
|  | sobj->offset + nametab_offset, | 
|  | (unsigned char *) nametab, nametab_size, | 
|  | errmsg, err)) | 
|  | { | 
|  | XDELETEVEC (nametab); | 
|  | XDELETEVEC (secdata); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Fetch the index.  */ | 
|  |  | 
|  | simple_object_mach_o_section_info (omr->is_big_endian, is_32, | 
|  | secdata + index_index * sechdrsize, | 
|  | &index_offset, &index_size); | 
|  | index = XNEWVEC (unsigned char, index_size); | 
|  | if (!simple_object_internal_read (sobj->descriptor, | 
|  | sobj->offset + index_offset, | 
|  | index, index_size, | 
|  | errmsg, err)) | 
|  | { | 
|  | XDELETEVEC (index); | 
|  | XDELETEVEC (nametab); | 
|  | XDELETEVEC (secdata); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* The index contains 4 unsigned ints per sub-section: | 
|  | sub-section offset/length, sub-section name/length. | 
|  | We fix this for both 32 and 64 bit mach-o for now, since | 
|  | other fields limit the maximum size of an object to 4G.  */ | 
|  | n_wrapped_sects = index_size / 16; | 
|  |  | 
|  | /* Get the parameters for the wrapper too.  */ | 
|  | simple_object_mach_o_section_info (omr->is_big_endian, is_32, | 
|  | secdata + sections_index * sechdrsize, | 
|  | &wrapper_sect_offset, | 
|  | &wrapper_sect_size); | 
|  | } | 
|  | else | 
|  | { | 
|  | index = NULL; | 
|  | index_size = 0; | 
|  | nametab = NULL; | 
|  | nametab_size = 0; | 
|  | n_wrapped_sects = 0; | 
|  | } | 
|  |  | 
|  | /* If we have a long names section, fetch it.  */ | 
|  |  | 
|  | if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0) | 
|  | { | 
|  | off_t strtab_offset; | 
|  |  | 
|  | simple_object_mach_o_section_info (omr->is_big_endian, is_32, | 
|  | secdata + strtab_index * sechdrsize, | 
|  | &strtab_offset, &strtab_size); | 
|  | strtab = XNEWVEC (char, strtab_size); | 
|  | if (!simple_object_internal_read (sobj->descriptor, | 
|  | sobj->offset + strtab_offset, | 
|  | (unsigned char *) strtab, strtab_size, | 
|  | errmsg, err)) | 
|  | { | 
|  | XDELETEVEC (strtab); | 
|  | XDELETEVEC (index); | 
|  | XDELETEVEC (nametab); | 
|  | XDELETEVEC (secdata); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | strtab = NULL; | 
|  | strtab_size = 0; | 
|  | strtab_index = nsects; | 
|  | } | 
|  |  | 
|  | /* Process the sections.  */ | 
|  |  | 
|  | for (i = 0; i < nsects; ++i) | 
|  | { | 
|  | const unsigned char *sechdr; | 
|  | char namebuf[MACH_O_NAME_LEN * 2 + 2]; | 
|  | char *name; | 
|  | off_t secoffset; | 
|  | size_t secsize; | 
|  | int l; | 
|  |  | 
|  | sechdr = secdata + i * sechdrsize; | 
|  |  | 
|  | /* We've already processed the long section names.  */ | 
|  |  | 
|  | if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0 | 
|  | && i == strtab_index) | 
|  | continue; | 
|  |  | 
|  | /* We only act on the segment named.  */ | 
|  |  | 
|  | if (strcmp ((char *) sechdr + segname_offset, omr->segment_name) != 0) | 
|  | continue; | 
|  |  | 
|  | /* Process sections associated with the wrapper.  */ | 
|  |  | 
|  | if ((gnu_sections_found & SOMO_WRAPPING) != 0) | 
|  | { | 
|  | if (i == nametab_index || i == index_index) | 
|  | continue; | 
|  |  | 
|  | if (i == sections_index) | 
|  | { | 
|  | unsigned int j; | 
|  | for (j = 0; j < n_wrapped_sects; ++j) | 
|  | { | 
|  | unsigned int subsect_offset, subsect_length, name_offset; | 
|  | subsect_offset = (*fetch_32) (index + 16 * j); | 
|  | subsect_length = (*fetch_32) (index + 16 * j + 4); | 
|  | name_offset = (*fetch_32) (index + 16 * j + 8); | 
|  | /* We don't need the name_length yet.  */ | 
|  |  | 
|  | secoffset = wrapper_sect_offset + subsect_offset; | 
|  | secsize = subsect_length; | 
|  | name = nametab + name_offset; | 
|  |  | 
|  | if (!(*pfn) (data, name, secoffset, secsize)) | 
|  | { | 
|  | *errmsg = NULL; | 
|  | *err = 0; | 
|  | XDELETEVEC (index); | 
|  | XDELETEVEC (nametab); | 
|  | XDELETEVEC (strtab); | 
|  | XDELETEVEC (secdata); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0) | 
|  | { | 
|  | memcpy (namebuf, sechdr + sectname_offset, MACH_O_NAME_LEN); | 
|  | namebuf[MACH_O_NAME_LEN] = '\0'; | 
|  |  | 
|  | name = &namebuf[0]; | 
|  | if (strtab != NULL && name[0] == '_' && name[1] == '_') | 
|  | { | 
|  | unsigned long stringoffset; | 
|  |  | 
|  | if (sscanf (name + 2, "%08lX", &stringoffset) == 1) | 
|  | { | 
|  | if (stringoffset >= strtab_size) | 
|  | { | 
|  | *errmsg = "section name offset out of range"; | 
|  | *err = 0; | 
|  | XDELETEVEC (index); | 
|  | XDELETEVEC (nametab); | 
|  | XDELETEVEC (strtab); | 
|  | XDELETEVEC (secdata); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | name = strtab + stringoffset; | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Otherwise, make a name like __segment,__section as per the | 
|  | convention in mach-o asm.  */ | 
|  | name = &namebuf[0]; | 
|  | memcpy (namebuf, (char *) sechdr + segname_offset, MACH_O_NAME_LEN); | 
|  | namebuf[MACH_O_NAME_LEN] = '\0'; | 
|  | l = strlen (namebuf); | 
|  | namebuf[l] = ','; | 
|  | memcpy (namebuf + l + 1, (char *) sechdr + sectname_offset, | 
|  | MACH_O_NAME_LEN); | 
|  | namebuf[l + 1 + MACH_O_NAME_LEN] = '\0'; | 
|  | } | 
|  |  | 
|  | simple_object_mach_o_section_info (omr->is_big_endian, is_32, sechdr, | 
|  | &secoffset, &secsize); | 
|  |  | 
|  | if (!(*pfn) (data, name, secoffset, secsize)) | 
|  | { | 
|  | *errmsg = NULL; | 
|  | *err = 0; | 
|  | XDELETEVEC (index); | 
|  | XDELETEVEC (nametab); | 
|  | XDELETEVEC (strtab); | 
|  | XDELETEVEC (secdata); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | XDELETEVEC (index); | 
|  | XDELETEVEC (nametab); | 
|  | XDELETEVEC (strtab); | 
|  | XDELETEVEC (secdata); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Find all sections in a Mach-O file.  */ | 
|  |  | 
|  | static const char * | 
|  | simple_object_mach_o_find_sections (simple_object_read *sobj, | 
|  | int (*pfn) (void *, const char *, | 
|  | off_t offset, off_t length), | 
|  | void *data, | 
|  | int *err) | 
|  | { | 
|  | struct simple_object_mach_o_read *omr = | 
|  | (struct simple_object_mach_o_read *) sobj->data; | 
|  | off_t offset; | 
|  | size_t seghdrsize; | 
|  | unsigned int (*fetch_32) (const unsigned char *); | 
|  | const char *errmsg; | 
|  | unsigned int i; | 
|  |  | 
|  | if (omr->magic == MACH_O_MH_MAGIC) | 
|  | { | 
|  | offset = sizeof (struct mach_o_header_32); | 
|  | seghdrsize = sizeof (struct mach_o_segment_command_32); | 
|  | } | 
|  | else | 
|  | { | 
|  | offset = sizeof (struct mach_o_header_64); | 
|  | seghdrsize = sizeof (struct mach_o_segment_command_64); | 
|  | } | 
|  |  | 
|  | fetch_32 = (omr->is_big_endian | 
|  | ? simple_object_fetch_big_32 | 
|  | : simple_object_fetch_little_32); | 
|  |  | 
|  | for (i = 0; i < omr->ncmds; ++i) | 
|  | { | 
|  | unsigned char loadbuf[sizeof (struct mach_o_load_command)]; | 
|  | unsigned int cmd; | 
|  | unsigned int cmdsize; | 
|  |  | 
|  | if (!simple_object_internal_read (sobj->descriptor, | 
|  | sobj->offset + offset, | 
|  | loadbuf, | 
|  | sizeof (struct mach_o_load_command), | 
|  | &errmsg, err)) | 
|  | return errmsg; | 
|  |  | 
|  | cmd = (*fetch_32) (loadbuf + offsetof (struct mach_o_load_command, cmd)); | 
|  | cmdsize = (*fetch_32) (loadbuf | 
|  | + offsetof (struct mach_o_load_command, cmdsize)); | 
|  |  | 
|  | if (cmd == MACH_O_LC_SEGMENT || cmd == MACH_O_LC_SEGMENT_64) | 
|  | { | 
|  | unsigned char segbuf[sizeof (struct mach_o_segment_command_64)]; | 
|  | int r; | 
|  |  | 
|  | if (!simple_object_internal_read (sobj->descriptor, | 
|  | sobj->offset + offset, | 
|  | segbuf, seghdrsize, &errmsg, err)) | 
|  | return errmsg; | 
|  |  | 
|  | r = simple_object_mach_o_segment (sobj, offset, segbuf, pfn, | 
|  | data, &errmsg, err); | 
|  | if (!r) | 
|  | return errmsg; | 
|  | } | 
|  |  | 
|  | offset += cmdsize; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Fetch the attributes for an simple_object_read.  */ | 
|  |  | 
|  | static void * | 
|  | simple_object_mach_o_fetch_attributes (simple_object_read *sobj, | 
|  | const char **errmsg ATTRIBUTE_UNUSED, | 
|  | int *err ATTRIBUTE_UNUSED) | 
|  | { | 
|  | struct simple_object_mach_o_read *omr = | 
|  | (struct simple_object_mach_o_read *) sobj->data; | 
|  | struct simple_object_mach_o_attributes *ret; | 
|  |  | 
|  | ret = XNEW (struct simple_object_mach_o_attributes); | 
|  | ret->magic = omr->magic; | 
|  | ret->is_big_endian = omr->is_big_endian; | 
|  | ret->cputype = omr->cputype; | 
|  | ret->cpusubtype = omr->cpusubtype; | 
|  | ret->flags = omr->flags; | 
|  | ret->reserved = omr->reserved; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Release the private data for an simple_object_read.  */ | 
|  |  | 
|  | static void | 
|  | simple_object_mach_o_release_read (void *data) | 
|  | { | 
|  | struct simple_object_mach_o_read *omr = | 
|  | (struct simple_object_mach_o_read *) data; | 
|  |  | 
|  | free (omr->segment_name); | 
|  | XDELETE (omr); | 
|  | } | 
|  |  | 
|  | /* Compare two attributes structures.  */ | 
|  |  | 
|  | static const char * | 
|  | simple_object_mach_o_attributes_merge (void *todata, void *fromdata, int *err) | 
|  | { | 
|  | struct simple_object_mach_o_attributes *to = | 
|  | (struct simple_object_mach_o_attributes *) todata; | 
|  | struct simple_object_mach_o_attributes *from = | 
|  | (struct simple_object_mach_o_attributes *) fromdata; | 
|  |  | 
|  | if (to->magic != from->magic | 
|  | || to->is_big_endian != from->is_big_endian | 
|  | || to->cputype != from->cputype) | 
|  | { | 
|  | *err = 0; | 
|  | return "Mach-O object format mismatch"; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Release the private data for an attributes structure.  */ | 
|  |  | 
|  | static void | 
|  | simple_object_mach_o_release_attributes (void *data) | 
|  | { | 
|  | XDELETE (data); | 
|  | } | 
|  |  | 
|  | /* Prepare to write out a file.  */ | 
|  |  | 
|  | static void * | 
|  | simple_object_mach_o_start_write (void *attributes_data, | 
|  | const char **errmsg ATTRIBUTE_UNUSED, | 
|  | int *err ATTRIBUTE_UNUSED) | 
|  | { | 
|  | struct simple_object_mach_o_attributes *attrs = | 
|  | (struct simple_object_mach_o_attributes *) attributes_data; | 
|  | struct simple_object_mach_o_attributes *ret; | 
|  |  | 
|  | /* We're just going to record the attributes, but we need to make a | 
|  | copy because the user may delete them.  */ | 
|  | ret = XNEW (struct simple_object_mach_o_attributes); | 
|  | *ret = *attrs; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Write out the header of a Mach-O file.  */ | 
|  |  | 
|  | static int | 
|  | simple_object_mach_o_write_header (simple_object_write *sobj, int descriptor, | 
|  | size_t nsects, const char **errmsg, | 
|  | int *err) | 
|  | { | 
|  | struct simple_object_mach_o_attributes *attrs = | 
|  | (struct simple_object_mach_o_attributes *) sobj->data; | 
|  | void (*set_32) (unsigned char *, unsigned int); | 
|  | unsigned char hdrbuf[sizeof (struct mach_o_header_64)]; | 
|  | unsigned char *hdr; | 
|  | size_t wrsize; | 
|  |  | 
|  | set_32 = (attrs->is_big_endian | 
|  | ? simple_object_set_big_32 | 
|  | : simple_object_set_little_32); | 
|  |  | 
|  | memset (hdrbuf, 0, sizeof hdrbuf); | 
|  |  | 
|  | /* The 32-bit and 64-bit headers start out the same.  */ | 
|  |  | 
|  | hdr = &hdrbuf[0]; | 
|  | set_32 (hdr + offsetof (struct mach_o_header_32, magic), attrs->magic); | 
|  | set_32 (hdr + offsetof (struct mach_o_header_32, cputype), attrs->cputype); | 
|  | set_32 (hdr + offsetof (struct mach_o_header_32, cpusubtype), | 
|  | attrs->cpusubtype); | 
|  | set_32 (hdr + offsetof (struct mach_o_header_32, filetype), MACH_O_MH_OBJECT); | 
|  | set_32 (hdr + offsetof (struct mach_o_header_32, ncmds), 1); | 
|  | set_32 (hdr + offsetof (struct mach_o_header_32, flags), attrs->flags); | 
|  | if (attrs->magic == MACH_O_MH_MAGIC) | 
|  | { | 
|  | wrsize = sizeof (struct mach_o_header_32); | 
|  | set_32 (hdr + offsetof (struct mach_o_header_32, sizeofcmds), | 
|  | (sizeof (struct mach_o_segment_command_32) | 
|  | + nsects * sizeof (struct mach_o_section_32))); | 
|  | } | 
|  | else | 
|  | { | 
|  | set_32 (hdr + offsetof (struct mach_o_header_64, sizeofcmds), | 
|  | (sizeof (struct mach_o_segment_command_64) | 
|  | + nsects * sizeof (struct mach_o_section_64))); | 
|  | set_32 (hdr + offsetof (struct mach_o_header_64, reserved), | 
|  | attrs->reserved); | 
|  | wrsize = sizeof (struct mach_o_header_64); | 
|  | } | 
|  |  | 
|  | return simple_object_internal_write (descriptor, 0, hdrbuf, wrsize, | 
|  | errmsg, err); | 
|  | } | 
|  |  | 
|  | /* Write a Mach-O section header.  */ | 
|  |  | 
|  | static int | 
|  | simple_object_mach_o_write_section_header (simple_object_write *sobj, | 
|  | int descriptor, | 
|  | size_t sechdr_offset, | 
|  | const char *name, const char *segn, | 
|  | size_t secaddr, size_t secsize, | 
|  | size_t offset, unsigned int align, | 
|  | const char **errmsg, int *err) | 
|  | { | 
|  | struct simple_object_mach_o_attributes *attrs = | 
|  | (struct simple_object_mach_o_attributes *) sobj->data; | 
|  | void (*set_32) (unsigned char *, unsigned int); | 
|  | unsigned char hdrbuf[sizeof (struct mach_o_section_64)]; | 
|  | unsigned char *hdr; | 
|  | size_t sechdrsize; | 
|  |  | 
|  | set_32 = (attrs->is_big_endian | 
|  | ? simple_object_set_big_32 | 
|  | : simple_object_set_little_32); | 
|  |  | 
|  | memset (hdrbuf, 0, sizeof hdrbuf); | 
|  |  | 
|  | hdr = &hdrbuf[0]; | 
|  | if (attrs->magic == MACH_O_MH_MAGIC) | 
|  | { | 
|  | strncpy ((char *) hdr + offsetof (struct mach_o_section_32, sectname), | 
|  | name, MACH_O_NAME_LEN); | 
|  | strncpy ((char *) hdr + offsetof (struct mach_o_section_32, segname), | 
|  | segn, MACH_O_NAME_LEN); | 
|  | set_32 (hdr + offsetof (struct mach_o_section_32, addr), secaddr); | 
|  | set_32 (hdr + offsetof (struct mach_o_section_32, size), secsize); | 
|  | set_32 (hdr + offsetof (struct mach_o_section_32, offset), offset); | 
|  | set_32 (hdr + offsetof (struct mach_o_section_32, align), align); | 
|  | /* reloff left as zero.  */ | 
|  | /* nreloc left as zero.  */ | 
|  | set_32 (hdr + offsetof (struct mach_o_section_32, flags), | 
|  | MACH_O_S_ATTR_DEBUG); | 
|  | /* reserved1 left as zero.  */ | 
|  | /* reserved2 left as zero.  */ | 
|  | sechdrsize = sizeof (struct mach_o_section_32); | 
|  | } | 
|  | else | 
|  | { | 
|  | #ifdef UNSIGNED_64BIT_TYPE | 
|  | void (*set_64) (unsigned char *, ulong_type); | 
|  |  | 
|  | set_64 = (attrs->is_big_endian | 
|  | ? simple_object_set_big_64 | 
|  | : simple_object_set_little_64); | 
|  |  | 
|  | strncpy ((char *) hdr + offsetof (struct mach_o_section_64, sectname), | 
|  | name, MACH_O_NAME_LEN); | 
|  | strncpy ((char *) hdr + offsetof (struct mach_o_section_64, segname), | 
|  | segn, MACH_O_NAME_LEN); | 
|  | set_64 (hdr + offsetof (struct mach_o_section_64, addr), secaddr); | 
|  | set_64 (hdr + offsetof (struct mach_o_section_64, size), secsize); | 
|  | set_32 (hdr + offsetof (struct mach_o_section_64, offset), offset); | 
|  | set_32 (hdr + offsetof (struct mach_o_section_64, align), align); | 
|  | /* reloff left as zero.  */ | 
|  | /* nreloc left as zero.  */ | 
|  | set_32 (hdr + offsetof (struct mach_o_section_64, flags), | 
|  | MACH_O_S_ATTR_DEBUG); | 
|  | /* reserved1 left as zero.  */ | 
|  | /* reserved2 left as zero.  */ | 
|  | /* reserved3 left as zero.  */ | 
|  | #endif | 
|  | sechdrsize = sizeof (struct mach_o_section_64); | 
|  | } | 
|  |  | 
|  | return simple_object_internal_write (descriptor, sechdr_offset, hdr, | 
|  | sechdrsize, errmsg, err); | 
|  | } | 
|  |  | 
|  | /* Write out the single (anonymous) segment containing the sections of a Mach-O | 
|  | Object file. | 
|  |  | 
|  | As a GNU extension to mach-o, when the caller specifies a segment name in | 
|  | sobj->segment_name, all the sections passed will be output under a single | 
|  | mach-o section header.  The caller's sections are indexed within this | 
|  | 'wrapper' section by a table stored in a second mach-o section.  Finally, | 
|  | arbitrary length section names are permitted by the extension and these are | 
|  | stored in a table in a third mach-o section. | 
|  |  | 
|  | Note that this is only likely to make any sense for the __GNU_LTO segment | 
|  | at present. | 
|  |  | 
|  | If the wrapper extension is not in force, we assume that the section name | 
|  | is in the form __SEGMENT_NAME,__section_name as per Mach-O asm.  */ | 
|  |  | 
|  | static int | 
|  | simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, | 
|  | size_t *nsects, const char **errmsg, | 
|  | int *err) | 
|  | { | 
|  | struct simple_object_mach_o_attributes *attrs = | 
|  | (struct simple_object_mach_o_attributes *) sobj->data; | 
|  | void (*set_32) (unsigned char *, unsigned int); | 
|  | size_t hdrsize; | 
|  | size_t seghdrsize; | 
|  | size_t sechdrsize; | 
|  | size_t cmdsize; | 
|  | size_t offset; | 
|  | size_t sechdr_offset; | 
|  | size_t secaddr; | 
|  | unsigned int name_offset; | 
|  | simple_object_write_section *section; | 
|  | unsigned char hdrbuf[sizeof (struct mach_o_segment_command_64)]; | 
|  | unsigned char *hdr; | 
|  | size_t nsects_in; | 
|  | unsigned int *index; | 
|  | char *snames; | 
|  | unsigned int sect; | 
|  |  | 
|  | set_32 = (attrs->is_big_endian | 
|  | ? simple_object_set_big_32 | 
|  | : simple_object_set_little_32); | 
|  |  | 
|  | /* Write out the sections first.  */ | 
|  |  | 
|  | if (attrs->magic == MACH_O_MH_MAGIC) | 
|  | { | 
|  | hdrsize = sizeof (struct mach_o_header_32); | 
|  | seghdrsize = sizeof (struct mach_o_segment_command_32); | 
|  | sechdrsize = sizeof (struct mach_o_section_32); | 
|  | } | 
|  | else | 
|  | { | 
|  | hdrsize = sizeof (struct mach_o_header_64); | 
|  | seghdrsize = sizeof (struct mach_o_segment_command_64); | 
|  | sechdrsize = sizeof (struct mach_o_section_64); | 
|  | } | 
|  |  | 
|  | name_offset = 0; | 
|  | *nsects = nsects_in = 0; | 
|  |  | 
|  | /* Count the number of sections we start with.  */ | 
|  |  | 
|  | for (section = sobj->sections; section != NULL; section = section->next) | 
|  | nsects_in++; | 
|  |  | 
|  | if (sobj->segment_name != NULL) | 
|  | { | 
|  | /* We will only write 3 sections: wrapped data, index and names.  */ | 
|  |  | 
|  | *nsects = 3; | 
|  |  | 
|  | /* The index has four entries per wrapped section: | 
|  | Section Offset, length,  Name offset, length. | 
|  | Where the offsets are based at the start of the wrapper and name | 
|  | sections respectively. | 
|  | The values are stored as 32 bit int for both 32 and 64 bit mach-o | 
|  | since the size of a mach-o MH_OBJECT cannot exceed 4G owing to | 
|  | other constraints.  */ | 
|  |  | 
|  | index = XNEWVEC (unsigned int, nsects_in * 4); | 
|  |  | 
|  | /* We now need to figure out the size of the names section.  This just | 
|  | stores the names as null-terminated c strings, packed without any | 
|  | alignment padding.  */ | 
|  |  | 
|  | for (section = sobj->sections, sect = 0; section != NULL; | 
|  | section = section->next, sect++) | 
|  | { | 
|  | index[sect*4+2] = name_offset; | 
|  | index[sect*4+3] = strlen (section->name) + 1; | 
|  | name_offset += strlen (section->name) + 1; | 
|  | } | 
|  | snames = XNEWVEC (char, name_offset); | 
|  | } | 
|  | else | 
|  | { | 
|  | *nsects = nsects_in; | 
|  | index = NULL; | 
|  | snames = NULL; | 
|  | } | 
|  |  | 
|  | sechdr_offset = hdrsize + seghdrsize; | 
|  | cmdsize = seghdrsize + *nsects * sechdrsize; | 
|  | offset = hdrsize + cmdsize; | 
|  | secaddr = 0; | 
|  |  | 
|  | for (section = sobj->sections, sect = 0; | 
|  | section != NULL; section = section->next, sect++) | 
|  | { | 
|  | size_t mask; | 
|  | size_t new_offset; | 
|  | size_t secsize; | 
|  | struct simple_object_write_section_buffer *buffer; | 
|  |  | 
|  | mask = (1U << section->align) - 1; | 
|  | new_offset = offset + mask; | 
|  | new_offset &= ~ mask; | 
|  | while (new_offset > offset) | 
|  | { | 
|  | unsigned char zeroes[16]; | 
|  | size_t write; | 
|  |  | 
|  | memset (zeroes, 0, sizeof zeroes); | 
|  | write = new_offset - offset; | 
|  | if (write > sizeof zeroes) | 
|  | write = sizeof zeroes; | 
|  | if (!simple_object_internal_write (descriptor, offset, zeroes, write, | 
|  | errmsg, err)) | 
|  | return 0; | 
|  | offset += write; | 
|  | } | 
|  |  | 
|  | secsize = 0; | 
|  | for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) | 
|  | { | 
|  | if (!simple_object_internal_write (descriptor, offset + secsize, | 
|  | ((const unsigned char *) | 
|  | buffer->buffer), | 
|  | buffer->size, errmsg, err)) | 
|  | return 0; | 
|  | secsize += buffer->size; | 
|  | } | 
|  |  | 
|  | if (sobj->segment_name != NULL) | 
|  | { | 
|  | index[sect*4+0] = (unsigned int) offset; | 
|  | index[sect*4+1] = secsize; | 
|  | /* Stash the section name in our table.  */ | 
|  | memcpy (snames + index[sect * 4 + 2], section->name, | 
|  | index[sect * 4 + 3]); | 
|  | } | 
|  | else | 
|  | { | 
|  | char namebuf[MACH_O_NAME_LEN + 1]; | 
|  | char segnbuf[MACH_O_NAME_LEN + 1]; | 
|  | char *comma; | 
|  |  | 
|  | /* Try to extract segment,section from the input name.  */ | 
|  |  | 
|  | memset (namebuf, 0, sizeof namebuf); | 
|  | memset (segnbuf, 0, sizeof segnbuf); | 
|  | comma = strchr (section->name, ','); | 
|  | if (comma != NULL) | 
|  | { | 
|  | int len = comma - section->name; | 
|  | len = len > MACH_O_NAME_LEN ? MACH_O_NAME_LEN : len; | 
|  | strncpy (namebuf, section->name, len); | 
|  | strncpy (segnbuf, comma + 1, MACH_O_NAME_LEN); | 
|  | } | 
|  | else /* just try to copy the name, leave segment blank.  */ | 
|  | strncpy (namebuf, section->name, MACH_O_NAME_LEN); | 
|  |  | 
|  | if (!simple_object_mach_o_write_section_header (sobj, descriptor, | 
|  | sechdr_offset, | 
|  | namebuf, segnbuf, | 
|  | secaddr, secsize, | 
|  | offset, | 
|  | section->align, | 
|  | errmsg, err)) | 
|  | return 0; | 
|  | sechdr_offset += sechdrsize; | 
|  | } | 
|  |  | 
|  | offset += secsize; | 
|  | secaddr += secsize; | 
|  | } | 
|  |  | 
|  | if (sobj->segment_name != NULL) | 
|  | { | 
|  | size_t secsize; | 
|  | unsigned int i; | 
|  |  | 
|  | /* Write the section header for the wrapper.  */ | 
|  | /* Account for any initial aligment - which becomes the alignment for this | 
|  | created section.  */ | 
|  |  | 
|  | secsize = (offset - index[0]); | 
|  | if (!simple_object_mach_o_write_section_header (sobj, descriptor, | 
|  | sechdr_offset, | 
|  | GNU_WRAPPER_SECTS, | 
|  | sobj->segment_name, | 
|  | 0 /*secaddr*/, | 
|  | secsize, index[0], | 
|  | sobj->sections->align, | 
|  | errmsg, err)) | 
|  | return 0; | 
|  |  | 
|  | /* Subtract the wrapper section start from the begining of each sub | 
|  | section.  */ | 
|  |  | 
|  | for (i = 1; i < nsects_in; ++i) | 
|  | index[4 * i] -= index[0]; | 
|  | index[0] = 0; | 
|  |  | 
|  | sechdr_offset += sechdrsize; | 
|  |  | 
|  | /* Write out the section names. | 
|  | ... the header ... | 
|  | name_offset contains the length of the section.  It is not aligned.  */ | 
|  |  | 
|  | if (!simple_object_mach_o_write_section_header (sobj, descriptor, | 
|  | sechdr_offset, | 
|  | GNU_WRAPPER_NAMES, | 
|  | sobj->segment_name, | 
|  | 0 /*secaddr*/, | 
|  | name_offset, | 
|  | offset, | 
|  | 0, errmsg, err)) | 
|  | return 0; | 
|  |  | 
|  | /* ... and the content.. */ | 
|  | if (!simple_object_internal_write (descriptor, offset, | 
|  | (const unsigned char *) snames, | 
|  | name_offset, errmsg, err)) | 
|  | return 0; | 
|  |  | 
|  | sechdr_offset += sechdrsize; | 
|  | secaddr += name_offset; | 
|  | offset += name_offset; | 
|  |  | 
|  | /* Now do the index, we'll align this to 4 bytes although the read code | 
|  | will handle unaligned.  */ | 
|  |  | 
|  | offset += 3; | 
|  | offset &= ~0x03; | 
|  | if (!simple_object_mach_o_write_section_header (sobj, descriptor, | 
|  | sechdr_offset, | 
|  | GNU_WRAPPER_INDEX, | 
|  | sobj->segment_name, | 
|  | 0 /*secaddr*/, | 
|  | nsects_in * 16, | 
|  | offset, | 
|  | 2, errmsg, err)) | 
|  | return 0; | 
|  |  | 
|  | /* ... and the content.. */ | 
|  | if (!simple_object_internal_write (descriptor, offset, | 
|  | (const unsigned char *) index, | 
|  | nsects_in*16, errmsg, err)) | 
|  | return 0; | 
|  |  | 
|  | XDELETEVEC (index); | 
|  | XDELETEVEC (snames); | 
|  | } | 
|  |  | 
|  | /* Write out the segment header.  */ | 
|  |  | 
|  | memset (hdrbuf, 0, sizeof hdrbuf); | 
|  |  | 
|  | hdr = &hdrbuf[0]; | 
|  | if (attrs->magic == MACH_O_MH_MAGIC) | 
|  | { | 
|  | set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmd), | 
|  | MACH_O_LC_SEGMENT); | 
|  | set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmdsize), | 
|  | cmdsize); | 
|  | /* MH_OBJECTS have a single, anonymous, segment - so the segment name | 
|  | is left empty.  */ | 
|  | /* vmaddr left as zero.  */ | 
|  | /* vmsize left as zero.  */ | 
|  | set_32 (hdr + offsetof (struct mach_o_segment_command_32, fileoff), | 
|  | hdrsize + cmdsize); | 
|  | set_32 (hdr + offsetof (struct mach_o_segment_command_32, filesize), | 
|  | offset - (hdrsize + cmdsize)); | 
|  | /* maxprot left as zero.  */ | 
|  | /* initprot left as zero.  */ | 
|  | set_32 (hdr + offsetof (struct mach_o_segment_command_32, nsects), | 
|  | *nsects); | 
|  | /* flags left as zero.  */ | 
|  | } | 
|  | else | 
|  | { | 
|  | #ifdef UNSIGNED_64BIT_TYPE | 
|  | void (*set_64) (unsigned char *, ulong_type); | 
|  |  | 
|  | set_64 = (attrs->is_big_endian | 
|  | ? simple_object_set_big_64 | 
|  | : simple_object_set_little_64); | 
|  |  | 
|  | set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmd), | 
|  | MACH_O_LC_SEGMENT); | 
|  | set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmdsize), | 
|  | cmdsize); | 
|  | /* MH_OBJECTS have a single, anonymous, segment - so the segment name | 
|  | is left empty.  */ | 
|  | /* vmaddr left as zero.  */ | 
|  | /* vmsize left as zero.  */ | 
|  | set_64 (hdr + offsetof (struct mach_o_segment_command_64, fileoff), | 
|  | hdrsize + cmdsize); | 
|  | set_64 (hdr + offsetof (struct mach_o_segment_command_64, filesize), | 
|  | offset - (hdrsize + cmdsize)); | 
|  | /* maxprot left as zero.  */ | 
|  | /* initprot left as zero.  */ | 
|  | set_32 (hdr + offsetof (struct mach_o_segment_command_64, nsects), | 
|  | *nsects); | 
|  | /* flags left as zero.  */ | 
|  | #endif | 
|  | } | 
|  |  | 
|  | return simple_object_internal_write (descriptor, hdrsize, hdr, seghdrsize, | 
|  | errmsg, err); | 
|  | } | 
|  |  | 
|  | /* Write out a complete Mach-O file.  */ | 
|  |  | 
|  | static const char * | 
|  | simple_object_mach_o_write_to_file (simple_object_write *sobj, int descriptor, | 
|  | int *err) | 
|  | { | 
|  | size_t nsects = 0; | 
|  | const char *errmsg; | 
|  |  | 
|  | if (!simple_object_mach_o_write_segment (sobj, descriptor, &nsects, | 
|  | &errmsg, err)) | 
|  | return errmsg; | 
|  |  | 
|  | if (!simple_object_mach_o_write_header (sobj, descriptor, nsects, | 
|  | &errmsg, err)) | 
|  | return errmsg; | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Release the private data for an simple_object_write structure.  */ | 
|  |  | 
|  | static void | 
|  | simple_object_mach_o_release_write (void *data) | 
|  | { | 
|  | XDELETE (data); | 
|  | } | 
|  |  | 
|  | /* The Mach-O functions.  */ | 
|  |  | 
|  | const struct simple_object_functions simple_object_mach_o_functions = | 
|  | { | 
|  | simple_object_mach_o_match, | 
|  | simple_object_mach_o_find_sections, | 
|  | simple_object_mach_o_fetch_attributes, | 
|  | simple_object_mach_o_release_read, | 
|  | simple_object_mach_o_attributes_merge, | 
|  | simple_object_mach_o_release_attributes, | 
|  | simple_object_mach_o_start_write, | 
|  | simple_object_mach_o_write_to_file, | 
|  | simple_object_mach_o_release_write, | 
|  | NULL | 
|  | }; |