| /* 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 <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" |
| |
| /* 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_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, 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_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 |
| }; |