/* simple-object-mach-o.c -- routines to manipulate Mach-O object files. Copyright 2010 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 #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STDINT_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_INTTYPES_H #include #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" /* 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 file. */ 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 file. Return 1 if we should continue, 0 if the caller should return. */ 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 strtab_index; char *strtab; size_t strtab_size; 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)); } 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 a __section_names section. This is in effect a GNU extension that permits section names longer than 16 chars. */ 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_SECTION_NAMES) == 0) break; } strtab_index = i; if (strtab_index >= nsects) { strtab = NULL; strtab_size = 0; } else { 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 (secdata); return 0; } } /* Process the sections. */ for (i = 0; i < nsects; ++i) { const unsigned char *sechdr; char namebuf[MACH_O_NAME_LEN + 1]; char *name; off_t secoffset; size_t secsize; if (i == strtab_index) continue; sechdr = secdata + i * sechdrsize; if (strcmp ((char *) sechdr + segname_offset, omr->segment_name) != 0) continue; 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 (strtab); XDELETEVEC (secdata); return 0; } name = strtab + stringoffset; } } 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 (strtab); XDELETEVEC (secdata); return 0; } } 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_compare (void *data1, void *data2, int *err) { struct simple_object_mach_o_attributes *attrs1 = (struct simple_object_mach_o_attributes *) data1; struct simple_object_mach_o_attributes *attrs2 = (struct simple_object_mach_o_attributes *) data2; if (attrs1->magic != attrs2->magic || attrs1->is_big_endian != attrs2->is_big_endian || attrs1->cputype != attrs2->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, 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), sobj->segment_name, 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), sobj->segment_name, 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 segment and the sections of a Mach-O file. */ 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; 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); } sechdr_offset = hdrsize + seghdrsize; cmdsize = seghdrsize + nsects * sechdrsize; offset = hdrsize + cmdsize; name_offset = 0; secaddr = 0; for (section = sobj->sections; section != NULL; section = section->next) { size_t mask; size_t new_offset; size_t secsize; struct simple_object_write_section_buffer *buffer; char namebuf[MACH_O_NAME_LEN + 1]; 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; } snprintf (namebuf, sizeof namebuf, "__%08X", name_offset); if (!simple_object_mach_o_write_section_header (sobj, descriptor, sechdr_offset, namebuf, secaddr, secsize, offset, section->align, errmsg, err)) return 0; sechdr_offset += sechdrsize; offset += secsize; name_offset += strlen (section->name) + 1; secaddr += secsize; } /* Write out the section names. */ if (!simple_object_mach_o_write_section_header (sobj, descriptor, sechdr_offset, GNU_SECTION_NAMES, secaddr, name_offset, offset, 0, errmsg, err)) return 0; for (section = sobj->sections; section != NULL; section = section->next) { size_t namelen; namelen = strlen (section->name) + 1; if (!simple_object_internal_write (descriptor, offset, (const unsigned char *) section->name, namelen, errmsg, err)) return 0; offset += namelen; } /* 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); strncpy (((char *) hdr + offsetof (struct mach_o_segment_command_32, segname)), sobj->segment_name, MACH_O_NAME_LEN); /* 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); strncpy (((char *) hdr + offsetof (struct mach_o_segment_command_64, segname)), sobj->segment_name, MACH_O_NAME_LEN); /* 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; simple_object_write_section *section; const char *errmsg; /* Start at 1 for symbol_names section. */ nsects = 1; for (section = sobj->sections; section != NULL; section = section->next) ++nsects; if (!simple_object_mach_o_write_header (sobj, descriptor, nsects, &errmsg, err)) return errmsg; if (!simple_object_mach_o_write_segment (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_compare, 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 };