From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libiberty/simple-object-mach-o.c | 1377 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1377 insertions(+) create mode 100644 libiberty/simple-object-mach-o.c (limited to 'libiberty/simple-object-mach-o.c') diff --git a/libiberty/simple-object-mach-o.c b/libiberty/simple-object-mach-o.c new file mode 100644 index 000000000..af5e4f9ca --- /dev/null +++ b/libiberty/simple-object-mach-o.c @@ -0,0 +1,1377 @@ +/* simple-object-mach-o.c -- routines to manipulate Mach-O object files. + Copyright 2010, 2011 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" + +/* 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; + + 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]; + memset (namebuf, 0, MACH_O_NAME_LEN * 2 + 2); + memcpy (namebuf, (char *) sechdr + segname_offset, MACH_O_NAME_LEN); + l = strlen (namebuf); + namebuf[l] = ','; + memcpy (namebuf + l + 1, (char *) sechdr + sectname_offset, + MACH_O_NAME_LEN); + } + + 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 +}; -- cgit v1.2.3