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