xref: /netbsd-src/external/gpl3/gdb/dist/libiberty/simple-object-mach-o.c (revision 5173eb0a33e5d83890ba976253e703be4c92557c)
198b9484cSchristos /* simple-object-mach-o.c -- routines to manipulate Mach-O object files.
2*5173eb0aSchristos    Copyright (C) 2010-2024 Free Software Foundation, Inc.
398b9484cSchristos    Written by Ian Lance Taylor, Google.
498b9484cSchristos 
598b9484cSchristos This program is free software; you can redistribute it and/or modify it
698b9484cSchristos under the terms of the GNU General Public License as published by the
798b9484cSchristos Free Software Foundation; either version 2, or (at your option) any
898b9484cSchristos later version.
998b9484cSchristos 
1098b9484cSchristos This program is distributed in the hope that it will be useful,
1198b9484cSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of
1298b9484cSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1398b9484cSchristos GNU General Public License for more details.
1498b9484cSchristos 
1598b9484cSchristos You should have received a copy of the GNU General Public License
1698b9484cSchristos along with this program; if not, write to the Free Software
1798b9484cSchristos Foundation, 51 Franklin Street - Fifth Floor,
1898b9484cSchristos Boston, MA 02110-1301, USA.  */
1998b9484cSchristos 
2098b9484cSchristos #include "config.h"
2198b9484cSchristos #include "libiberty.h"
2298b9484cSchristos #include "simple-object.h"
2398b9484cSchristos 
2498b9484cSchristos #include <stddef.h>
2598b9484cSchristos 
2698b9484cSchristos #ifdef HAVE_STDLIB_H
2798b9484cSchristos #include <stdlib.h>
2898b9484cSchristos #endif
2998b9484cSchristos 
3098b9484cSchristos #ifdef HAVE_STDINT_H
3198b9484cSchristos #include <stdint.h>
3298b9484cSchristos #endif
3398b9484cSchristos 
3498b9484cSchristos #ifdef HAVE_STRING_H
3598b9484cSchristos #include <string.h>
3698b9484cSchristos #endif
3798b9484cSchristos 
3898b9484cSchristos #ifdef HAVE_INTTYPES_H
3998b9484cSchristos #include <inttypes.h>
4098b9484cSchristos #endif
4198b9484cSchristos 
4298b9484cSchristos #include "simple-object-common.h"
4398b9484cSchristos 
4498b9484cSchristos /* Mach-O structures and constants.  */
4598b9484cSchristos 
4698b9484cSchristos /* Mach-O header (32-bit version).  */
4798b9484cSchristos 
4898b9484cSchristos struct mach_o_header_32
4998b9484cSchristos {
5098b9484cSchristos   unsigned char magic[4];	/* Magic number.  */
5198b9484cSchristos   unsigned char cputype[4];	/* CPU that this object is for.  */
5298b9484cSchristos   unsigned char cpusubtype[4];	/* CPU subtype.  */
5398b9484cSchristos   unsigned char filetype[4];	/* Type of file.  */
5498b9484cSchristos   unsigned char ncmds[4];	/* Number of load commands.  */
5598b9484cSchristos   unsigned char sizeofcmds[4];	/* Total size of load commands.  */
5698b9484cSchristos   unsigned char flags[4];	/* Flags for special featues.  */
5798b9484cSchristos };
5898b9484cSchristos 
5998b9484cSchristos /* Mach-O header (64-bit version).  */
6098b9484cSchristos 
6198b9484cSchristos struct mach_o_header_64
6298b9484cSchristos {
6398b9484cSchristos   unsigned char magic[4];	/* Magic number.  */
6498b9484cSchristos   unsigned char cputype[4];	/* CPU that this object is for.  */
6598b9484cSchristos   unsigned char cpusubtype[4];	/* CPU subtype.  */
6698b9484cSchristos   unsigned char filetype[4];	/* Type of file.  */
6798b9484cSchristos   unsigned char ncmds[4];	/* Number of load commands.  */
6898b9484cSchristos   unsigned char sizeofcmds[4];	/* Total size of load commands.  */
6998b9484cSchristos   unsigned char flags[4];	/* Flags for special featues.  */
7098b9484cSchristos   unsigned char reserved[4];	/* Reserved.  Duh.  */
7198b9484cSchristos };
7298b9484cSchristos 
7398b9484cSchristos /* For magic field in header.  */
7498b9484cSchristos 
7598b9484cSchristos #define MACH_O_MH_MAGIC			0xfeedface
7698b9484cSchristos #define MACH_O_MH_MAGIC_64		0xfeedfacf
7798b9484cSchristos 
7898b9484cSchristos /* For filetype field in header.  */
7998b9484cSchristos 
8098b9484cSchristos #define MACH_O_MH_OBJECT		0x01
8198b9484cSchristos 
8298b9484cSchristos /* A Mach-O file is a list of load commands.  This is the header of a
8398b9484cSchristos    load command.  */
8498b9484cSchristos 
8598b9484cSchristos struct mach_o_load_command
8698b9484cSchristos {
8798b9484cSchristos   unsigned char cmd[4];		/* The type of load command.  */
8898b9484cSchristos   unsigned char cmdsize[4];	/* Size in bytes of entire command.  */
8998b9484cSchristos };
9098b9484cSchristos 
9198b9484cSchristos /* For cmd field in load command.   */
9298b9484cSchristos 
9398b9484cSchristos #define MACH_O_LC_SEGMENT		0x01
9498b9484cSchristos #define MACH_O_LC_SEGMENT_64		0x19
9598b9484cSchristos 
9698b9484cSchristos /* LC_SEGMENT load command.  */
9798b9484cSchristos 
9898b9484cSchristos struct mach_o_segment_command_32
9998b9484cSchristos {
10098b9484cSchristos   unsigned char cmd[4];		/* The type of load command (LC_SEGMENT).  */
10198b9484cSchristos   unsigned char cmdsize[4];	/* Size in bytes of entire command.  */
10298b9484cSchristos   unsigned char segname[16];	/* Name of this segment.  */
10398b9484cSchristos   unsigned char vmaddr[4];	/* Virtual memory address of this segment.  */
10498b9484cSchristos   unsigned char vmsize[4];	/* Size there, in bytes.  */
10598b9484cSchristos   unsigned char fileoff[4];	/* Offset in bytes of the data to be mapped.  */
10698b9484cSchristos   unsigned char filesize[4];	/* Size in bytes on disk.  */
10798b9484cSchristos   unsigned char maxprot[4];	/* Maximum permitted vmem protection.  */
10898b9484cSchristos   unsigned char initprot[4];	/* Initial vmem protection.  */
10998b9484cSchristos   unsigned char nsects[4];	/* Number of sections in this segment.  */
11098b9484cSchristos   unsigned char flags[4];	/* Flags that affect the loading.  */
11198b9484cSchristos };
11298b9484cSchristos 
11398b9484cSchristos /* LC_SEGMENT_64 load command.  */
11498b9484cSchristos 
11598b9484cSchristos struct mach_o_segment_command_64
11698b9484cSchristos {
11798b9484cSchristos   unsigned char cmd[4];		/* The type of load command (LC_SEGMENT_64).  */
11898b9484cSchristos   unsigned char cmdsize[4];	/* Size in bytes of entire command.  */
11998b9484cSchristos   unsigned char segname[16];	/* Name of this segment.  */
12098b9484cSchristos   unsigned char vmaddr[8];	/* Virtual memory address of this segment.  */
12198b9484cSchristos   unsigned char vmsize[8];	/* Size there, in bytes.  */
12298b9484cSchristos   unsigned char fileoff[8];	/* Offset in bytes of the data to be mapped.  */
12398b9484cSchristos   unsigned char filesize[8];	/* Size in bytes on disk.  */
12498b9484cSchristos   unsigned char maxprot[4];	/* Maximum permitted vmem protection.  */
12598b9484cSchristos   unsigned char initprot[4];	/* Initial vmem protection.  */
12698b9484cSchristos   unsigned char nsects[4];	/* Number of sections in this segment.  */
12798b9484cSchristos   unsigned char flags[4];	/* Flags that affect the loading.  */
12898b9484cSchristos };
12998b9484cSchristos 
13098b9484cSchristos /* 32-bit section header.  */
13198b9484cSchristos 
13298b9484cSchristos struct mach_o_section_32
13398b9484cSchristos {
13498b9484cSchristos   unsigned char sectname[16];	/* Section name.  */
13598b9484cSchristos   unsigned char segname[16];	/* Segment that the section belongs to.  */
13698b9484cSchristos   unsigned char addr[4];	/* Address of this section in memory.  */
13798b9484cSchristos   unsigned char size[4];	/* Size in bytes of this section.  */
13898b9484cSchristos   unsigned char offset[4];	/* File offset of this section.  */
13998b9484cSchristos   unsigned char align[4];	/* log2 of this section's alignment.  */
14098b9484cSchristos   unsigned char reloff[4];	/* File offset of this section's relocs.  */
14198b9484cSchristos   unsigned char nreloc[4];	/* Number of relocs for this section.  */
14298b9484cSchristos   unsigned char flags[4];	/* Section flags/attributes.  */
14398b9484cSchristos   unsigned char reserved1[4];
14498b9484cSchristos   unsigned char reserved2[4];
14598b9484cSchristos };
14698b9484cSchristos 
14798b9484cSchristos /* 64-bit section header.  */
14898b9484cSchristos 
14998b9484cSchristos struct mach_o_section_64
15098b9484cSchristos {
15198b9484cSchristos   unsigned char sectname[16];	/* Section name.  */
15298b9484cSchristos   unsigned char segname[16];	/* Segment that the section belongs to.  */
15398b9484cSchristos   unsigned char addr[8];	/* Address of this section in memory.  */
15498b9484cSchristos   unsigned char size[8];	/* Size in bytes of this section.  */
15598b9484cSchristos   unsigned char offset[4];	/* File offset of this section.  */
15698b9484cSchristos   unsigned char align[4];	/* log2 of this section's alignment.  */
15798b9484cSchristos   unsigned char reloff[4];	/* File offset of this section's relocs.  */
15898b9484cSchristos   unsigned char nreloc[4];	/* Number of relocs for this section.  */
15998b9484cSchristos   unsigned char flags[4];	/* Section flags/attributes.  */
16098b9484cSchristos   unsigned char reserved1[4];
16198b9484cSchristos   unsigned char reserved2[4];
16298b9484cSchristos   unsigned char reserved3[4];
16398b9484cSchristos };
16498b9484cSchristos 
16598b9484cSchristos /* Flags for Mach-O sections.  */
16698b9484cSchristos 
16798b9484cSchristos #define MACH_O_S_ATTR_DEBUG			0x02000000
16898b9484cSchristos 
16998b9484cSchristos /* The length of a segment or section name.  */
17098b9484cSchristos 
17198b9484cSchristos #define MACH_O_NAME_LEN (16)
17298b9484cSchristos 
17398b9484cSchristos /* A GNU specific extension for long section names.  */
17498b9484cSchristos 
17598b9484cSchristos #define GNU_SECTION_NAMES "__section_names"
17698b9484cSchristos 
177a2e2270fSchristos /* A GNU-specific extension to wrap multiple sections using three
178a2e2270fSchristos    mach-o sections within a given segment.  The section '__wrapper_sects'
179a2e2270fSchristos    is subdivided according to the index '__wrapper_index' and each sub
180a2e2270fSchristos    sect is named according to the names supplied in '__wrapper_names'.  */
181a2e2270fSchristos 
182a2e2270fSchristos #define GNU_WRAPPER_SECTS "__wrapper_sects"
183a2e2270fSchristos #define GNU_WRAPPER_INDEX "__wrapper_index"
184a2e2270fSchristos #define GNU_WRAPPER_NAMES "__wrapper_names"
185a2e2270fSchristos 
18698b9484cSchristos /* Private data for an simple_object_read.  */
18798b9484cSchristos 
18898b9484cSchristos struct simple_object_mach_o_read
18998b9484cSchristos {
19098b9484cSchristos   /* User specified segment name.  */
19198b9484cSchristos   char *segment_name;
19298b9484cSchristos   /* Magic number.  */
19398b9484cSchristos   unsigned int magic;
19498b9484cSchristos   /* Whether this file is big-endian.  */
19598b9484cSchristos   int is_big_endian;
19698b9484cSchristos   /* CPU type from header.  */
19798b9484cSchristos   unsigned int cputype;
19898b9484cSchristos   /* CPU subtype from header.  */
19998b9484cSchristos   unsigned int cpusubtype;
20098b9484cSchristos   /* Number of commands, from header.  */
20198b9484cSchristos   unsigned int ncmds;
20298b9484cSchristos   /* Flags from header.  */
20398b9484cSchristos   unsigned int flags;
20498b9484cSchristos   /* Reserved field from header, only used on 64-bit.  */
20598b9484cSchristos   unsigned int reserved;
20698b9484cSchristos };
20798b9484cSchristos 
20898b9484cSchristos /* Private data for an simple_object_attributes.  */
20998b9484cSchristos 
21098b9484cSchristos struct simple_object_mach_o_attributes
21198b9484cSchristos {
21298b9484cSchristos   /* Magic number.  */
21398b9484cSchristos   unsigned int magic;
21498b9484cSchristos   /* Whether this file is big-endian.  */
21598b9484cSchristos   int is_big_endian;
21698b9484cSchristos   /* CPU type from header.  */
21798b9484cSchristos   unsigned int cputype;
21898b9484cSchristos   /* CPU subtype from header.  */
21998b9484cSchristos   unsigned int cpusubtype;
22098b9484cSchristos   /* Flags from header.  */
22198b9484cSchristos   unsigned int flags;
22298b9484cSchristos   /* Reserved field from header, only used on 64-bit.  */
22398b9484cSchristos   unsigned int reserved;
22498b9484cSchristos };
22598b9484cSchristos 
226a2e2270fSchristos /* See if we have a Mach-O MH_OBJECT file:
227a2e2270fSchristos 
228a2e2270fSchristos    A standard MH_OBJECT (from as) will have three load commands:
229a2e2270fSchristos    0 - LC_SEGMENT/LC_SEGMENT64
230a2e2270fSchristos    1 - LC_SYMTAB
231a2e2270fSchristos    2 - LC_DYSYMTAB
232a2e2270fSchristos 
233a2e2270fSchristos    The LC_SEGMENT/LC_SEGMENT64 will introduce a single anonymous segment
234a2e2270fSchristos    containing all the sections.
235a2e2270fSchristos 
236a2e2270fSchristos    Files written by simple-object will have only the segment command
237a2e2270fSchristos    (no symbol tables).  */
23898b9484cSchristos 
23998b9484cSchristos static void *
24098b9484cSchristos simple_object_mach_o_match (
24198b9484cSchristos     unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
24298b9484cSchristos     int descriptor,
24398b9484cSchristos     off_t offset,
24498b9484cSchristos     const char *segment_name,
24598b9484cSchristos     const char **errmsg,
24698b9484cSchristos     int *err)
24798b9484cSchristos {
24898b9484cSchristos   unsigned int magic;
24998b9484cSchristos   int is_big_endian;
25098b9484cSchristos   unsigned int (*fetch_32) (const unsigned char *);
25198b9484cSchristos   unsigned int filetype;
25298b9484cSchristos   struct simple_object_mach_o_read *omr;
25398b9484cSchristos   unsigned char buf[sizeof (struct mach_o_header_64)];
25498b9484cSchristos   unsigned char *b;
25598b9484cSchristos 
25698b9484cSchristos   magic = simple_object_fetch_big_32 (header);
25798b9484cSchristos   if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64)
25898b9484cSchristos     is_big_endian = 1;
25998b9484cSchristos   else
26098b9484cSchristos     {
26198b9484cSchristos       magic = simple_object_fetch_little_32 (header);
26298b9484cSchristos       if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64)
26398b9484cSchristos 	is_big_endian = 0;
26498b9484cSchristos       else
26598b9484cSchristos 	{
26698b9484cSchristos 	  *errmsg = NULL;
26798b9484cSchristos 	  *err = 0;
26898b9484cSchristos 	  return NULL;
26998b9484cSchristos 	}
27098b9484cSchristos     }
27198b9484cSchristos 
27298b9484cSchristos #ifndef UNSIGNED_64BIT_TYPE
27398b9484cSchristos   if (magic == MACH_O_MH_MAGIC_64)
27498b9484cSchristos     {
27598b9484cSchristos       *errmsg = "64-bit Mach-O objects not supported";
27698b9484cSchristos       *err = 0;
27798b9484cSchristos       return NULL;
27898b9484cSchristos     }
27998b9484cSchristos #endif
28098b9484cSchristos 
28198b9484cSchristos   /* We require the user to provide a segment name.  This is
28298b9484cSchristos      unfortunate but I don't see any good choices here.  */
28398b9484cSchristos 
28498b9484cSchristos   if (segment_name == NULL)
28598b9484cSchristos     {
28698b9484cSchristos       *errmsg = "Mach-O file found but no segment name specified";
28798b9484cSchristos       *err = 0;
28898b9484cSchristos       return NULL;
28998b9484cSchristos     }
29098b9484cSchristos 
29198b9484cSchristos   if (strlen (segment_name) > MACH_O_NAME_LEN)
29298b9484cSchristos     {
29398b9484cSchristos       *errmsg = "Mach-O segment name too long";
29498b9484cSchristos       *err = 0;
29598b9484cSchristos       return NULL;
29698b9484cSchristos     }
29798b9484cSchristos 
29898b9484cSchristos   /* The 32-bit and 64-bit headers are similar enough that we can use
29998b9484cSchristos      the same code.  */
30098b9484cSchristos 
30198b9484cSchristos   fetch_32 = (is_big_endian
30298b9484cSchristos 	      ? simple_object_fetch_big_32
30398b9484cSchristos 	      : simple_object_fetch_little_32);
30498b9484cSchristos 
30598b9484cSchristos   if (!simple_object_internal_read (descriptor, offset, buf,
30698b9484cSchristos 				    (magic == MACH_O_MH_MAGIC
30798b9484cSchristos 				     ? sizeof (struct mach_o_header_32)
30898b9484cSchristos 				     : sizeof (struct mach_o_header_64)),
30998b9484cSchristos 				    errmsg, err))
31098b9484cSchristos     return NULL;
31198b9484cSchristos 
31298b9484cSchristos   b = &buf[0];
31398b9484cSchristos 
31498b9484cSchristos   filetype = (*fetch_32) (b + offsetof (struct mach_o_header_32, filetype));
31598b9484cSchristos   if (filetype != MACH_O_MH_OBJECT)
31698b9484cSchristos     {
31798b9484cSchristos       *errmsg = "Mach-O file is not object file";
31898b9484cSchristos       *err = 0;
31998b9484cSchristos       return NULL;
32098b9484cSchristos     }
32198b9484cSchristos 
32298b9484cSchristos   omr = XNEW (struct simple_object_mach_o_read);
32398b9484cSchristos   omr->segment_name = xstrdup (segment_name);
32498b9484cSchristos   omr->magic = magic;
32598b9484cSchristos   omr->is_big_endian = is_big_endian;
32698b9484cSchristos   omr->cputype = (*fetch_32) (b + offsetof (struct mach_o_header_32, cputype));
32798b9484cSchristos   omr->cpusubtype = (*fetch_32) (b
32898b9484cSchristos 				 + offsetof (struct mach_o_header_32,
32998b9484cSchristos 					     cpusubtype));
33098b9484cSchristos   omr->ncmds = (*fetch_32) (b + offsetof (struct mach_o_header_32, ncmds));
33198b9484cSchristos   omr->flags = (*fetch_32) (b + offsetof (struct mach_o_header_32, flags));
33298b9484cSchristos   if (magic == MACH_O_MH_MAGIC)
33398b9484cSchristos     omr->reserved = 0;
33498b9484cSchristos   else
33598b9484cSchristos     omr->reserved = (*fetch_32) (b
33698b9484cSchristos 				 + offsetof (struct mach_o_header_64,
33798b9484cSchristos 					     reserved));
33898b9484cSchristos 
33998b9484cSchristos   return (void *) omr;
34098b9484cSchristos }
34198b9484cSchristos 
34298b9484cSchristos /* Get the file offset and size from a section header.  */
34398b9484cSchristos 
34498b9484cSchristos static void
34598b9484cSchristos simple_object_mach_o_section_info (int is_big_endian, int is_32,
34698b9484cSchristos 				   const unsigned char *sechdr, off_t *offset,
34798b9484cSchristos 				   size_t *size)
34898b9484cSchristos {
34998b9484cSchristos   unsigned int (*fetch_32) (const unsigned char *);
35098b9484cSchristos   ulong_type (*fetch_64) (const unsigned char *);
35198b9484cSchristos 
35298b9484cSchristos   fetch_32 = (is_big_endian
35398b9484cSchristos 	      ? simple_object_fetch_big_32
35498b9484cSchristos 	      : simple_object_fetch_little_32);
35598b9484cSchristos 
35698b9484cSchristos   fetch_64 = NULL;
35798b9484cSchristos #ifdef UNSIGNED_64BIT_TYPE
35898b9484cSchristos   fetch_64 = (is_big_endian
35998b9484cSchristos 	      ? simple_object_fetch_big_64
36098b9484cSchristos 	      : simple_object_fetch_little_64);
36198b9484cSchristos #endif
36298b9484cSchristos 
36398b9484cSchristos   if (is_32)
36498b9484cSchristos     {
36598b9484cSchristos       *offset = fetch_32 (sechdr
36698b9484cSchristos 			  + offsetof (struct mach_o_section_32, offset));
36798b9484cSchristos       *size = fetch_32 (sechdr
36898b9484cSchristos 			+ offsetof (struct mach_o_section_32, size));
36998b9484cSchristos     }
37098b9484cSchristos   else
37198b9484cSchristos     {
37298b9484cSchristos       *offset = fetch_32 (sechdr
37398b9484cSchristos 			  + offsetof (struct mach_o_section_64, offset));
37498b9484cSchristos       *size = fetch_64 (sechdr
37598b9484cSchristos 			+ offsetof (struct mach_o_section_64, size));
37698b9484cSchristos     }
37798b9484cSchristos }
37898b9484cSchristos 
379a2e2270fSchristos /* Handle a segment in a Mach-O Object file.
380a2e2270fSchristos 
381a2e2270fSchristos    This will callback to the function pfn for each "section found" the meaning
382a2e2270fSchristos    of which depends on gnu extensions to mach-o:
383a2e2270fSchristos 
384a2e2270fSchristos    If we find mach-o sections (with the segment name as specified) which also
385a2e2270fSchristos    contain: a 'sects' wrapper, an index, and a  name table, we expand this into
386a2e2270fSchristos    as many sections as are specified in the index.  In this case, there will
387a2e2270fSchristos    be a callback for each of these.
388a2e2270fSchristos 
389a2e2270fSchristos    We will also allow an extension that permits long names (more than 16
390a2e2270fSchristos    characters) to be used with mach-o.  In this case, the section name has
391a2e2270fSchristos    a specific format embedding an index into a name table, and the file must
392a2e2270fSchristos    contain such name table.
393a2e2270fSchristos 
394a2e2270fSchristos    Return 1 if we should continue, 0 if the caller should return.  */
395a2e2270fSchristos 
396a2e2270fSchristos #define SOMO_SECTS_PRESENT 0x01
397a2e2270fSchristos #define SOMO_INDEX_PRESENT 0x02
398a2e2270fSchristos #define SOMO_NAMES_PRESENT 0x04
399a2e2270fSchristos #define SOMO_LONGN_PRESENT 0x08
400a2e2270fSchristos #define SOMO_WRAPPING (SOMO_SECTS_PRESENT | SOMO_INDEX_PRESENT \
401a2e2270fSchristos 		       | SOMO_NAMES_PRESENT)
40298b9484cSchristos 
40398b9484cSchristos static int
40498b9484cSchristos simple_object_mach_o_segment (simple_object_read *sobj, off_t offset,
40598b9484cSchristos 			      const unsigned char *segbuf,
40698b9484cSchristos 			      int (*pfn) (void *, const char *, off_t offset,
40798b9484cSchristos 					  off_t length),
40898b9484cSchristos 			      void *data,
40998b9484cSchristos 			      const char **errmsg, int *err)
41098b9484cSchristos {
41198b9484cSchristos   struct simple_object_mach_o_read *omr =
41298b9484cSchristos     (struct simple_object_mach_o_read *) sobj->data;
41398b9484cSchristos   unsigned int (*fetch_32) (const unsigned char *);
41498b9484cSchristos   int is_32;
41598b9484cSchristos   size_t seghdrsize;
41698b9484cSchristos   size_t sechdrsize;
41798b9484cSchristos   size_t segname_offset;
41898b9484cSchristos   size_t sectname_offset;
41998b9484cSchristos   unsigned int nsects;
42098b9484cSchristos   unsigned char *secdata;
42198b9484cSchristos   unsigned int i;
422a2e2270fSchristos   unsigned int gnu_sections_found;
42398b9484cSchristos   unsigned int strtab_index;
424a2e2270fSchristos   unsigned int index_index;
425a2e2270fSchristos   unsigned int nametab_index;
426a2e2270fSchristos   unsigned int sections_index;
42798b9484cSchristos   char *strtab;
428a2e2270fSchristos   char *nametab;
429a2e2270fSchristos   unsigned char *index;
43098b9484cSchristos   size_t strtab_size;
431a2e2270fSchristos   size_t nametab_size;
432a2e2270fSchristos   size_t index_size;
433a2e2270fSchristos   unsigned int n_wrapped_sects;
434a2e2270fSchristos   size_t wrapper_sect_size;
43503467a24Schristos   off_t wrapper_sect_offset = 0;
43698b9484cSchristos 
43798b9484cSchristos   fetch_32 = (omr->is_big_endian
43898b9484cSchristos 	      ? simple_object_fetch_big_32
43998b9484cSchristos 	      : simple_object_fetch_little_32);
44098b9484cSchristos 
44198b9484cSchristos   is_32 = omr->magic == MACH_O_MH_MAGIC;
44298b9484cSchristos 
44398b9484cSchristos   if (is_32)
44498b9484cSchristos     {
44598b9484cSchristos       seghdrsize = sizeof (struct mach_o_segment_command_32);
44698b9484cSchristos       sechdrsize = sizeof (struct mach_o_section_32);
44798b9484cSchristos       segname_offset = offsetof (struct mach_o_section_32, segname);
44898b9484cSchristos       sectname_offset = offsetof (struct mach_o_section_32, sectname);
44998b9484cSchristos       nsects = (*fetch_32) (segbuf
45098b9484cSchristos 			    + offsetof (struct mach_o_segment_command_32,
45198b9484cSchristos 					nsects));
45298b9484cSchristos     }
45398b9484cSchristos   else
45498b9484cSchristos     {
45598b9484cSchristos       seghdrsize = sizeof (struct mach_o_segment_command_64);
45698b9484cSchristos       sechdrsize = sizeof (struct mach_o_section_64);
45798b9484cSchristos       segname_offset = offsetof (struct mach_o_section_64, segname);
45898b9484cSchristos       sectname_offset = offsetof (struct mach_o_section_64, sectname);
45998b9484cSchristos       nsects = (*fetch_32) (segbuf
46098b9484cSchristos 			    + offsetof (struct mach_o_segment_command_64,
46198b9484cSchristos 					nsects));
46298b9484cSchristos     }
46398b9484cSchristos 
464a2e2270fSchristos   /* Fetch the section headers from the segment command.  */
465a2e2270fSchristos 
46698b9484cSchristos   secdata = XNEWVEC (unsigned char, nsects * sechdrsize);
46798b9484cSchristos   if (!simple_object_internal_read (sobj->descriptor, offset + seghdrsize,
46898b9484cSchristos 				    secdata, nsects * sechdrsize, errmsg, err))
46998b9484cSchristos     {
47098b9484cSchristos       XDELETEVEC (secdata);
47198b9484cSchristos       return 0;
47298b9484cSchristos     }
47398b9484cSchristos 
474a2e2270fSchristos   /* Scan for special sections that signal GNU extensions to the format.  */
47598b9484cSchristos 
476a2e2270fSchristos   gnu_sections_found = 0;
477a2e2270fSchristos   index_index = nsects;
478a2e2270fSchristos   sections_index = nsects;
479a2e2270fSchristos   strtab_index = nsects;
480a2e2270fSchristos   nametab_index = nsects;
48198b9484cSchristos   for (i = 0; i < nsects; ++i)
48298b9484cSchristos     {
48398b9484cSchristos       size_t nameoff;
48498b9484cSchristos 
48598b9484cSchristos       nameoff = i * sechdrsize + segname_offset;
48698b9484cSchristos       if (strcmp ((char *) secdata + nameoff, omr->segment_name) != 0)
48798b9484cSchristos 	continue;
488a2e2270fSchristos 
48998b9484cSchristos       nameoff = i * sechdrsize + sectname_offset;
490a2e2270fSchristos       if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_NAMES) == 0)
491a2e2270fSchristos 	{
492a2e2270fSchristos 	  nametab_index = i;
493a2e2270fSchristos 	  gnu_sections_found |= SOMO_NAMES_PRESENT;
494a2e2270fSchristos 	}
495a2e2270fSchristos       else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_INDEX) == 0)
496a2e2270fSchristos 	{
497a2e2270fSchristos 	  index_index = i;
498a2e2270fSchristos 	  gnu_sections_found |= SOMO_INDEX_PRESENT;
499a2e2270fSchristos 	}
500a2e2270fSchristos       else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_SECTS) == 0)
501a2e2270fSchristos 	{
502a2e2270fSchristos 	  sections_index = i;
503a2e2270fSchristos 	  gnu_sections_found |= SOMO_SECTS_PRESENT;
504a2e2270fSchristos 	}
505a2e2270fSchristos       else if (strcmp ((char *) secdata + nameoff, GNU_SECTION_NAMES) == 0)
506a2e2270fSchristos 	{
507a2e2270fSchristos 	  strtab_index = i;
508a2e2270fSchristos 	  gnu_sections_found |= SOMO_LONGN_PRESENT;
509a2e2270fSchristos 	}
51098b9484cSchristos     }
51198b9484cSchristos 
512a2e2270fSchristos   /* If any of the special wrapper section components is present, then
513a2e2270fSchristos      they all should be.  */
514a2e2270fSchristos 
515a2e2270fSchristos   if ((gnu_sections_found & SOMO_WRAPPING) != 0)
51698b9484cSchristos     {
517a2e2270fSchristos       off_t nametab_offset;
518a2e2270fSchristos       off_t index_offset;
519a2e2270fSchristos 
520a2e2270fSchristos       if ((gnu_sections_found & SOMO_WRAPPING) != SOMO_WRAPPING)
521a2e2270fSchristos 	{
522a2e2270fSchristos 	  *errmsg = "GNU Mach-o section wrapper: required section missing";
523a2e2270fSchristos 	  *err = 0; /* No useful errno.  */
524a2e2270fSchristos 	  XDELETEVEC (secdata);
525a2e2270fSchristos 	  return 0;
526a2e2270fSchristos 	}
527a2e2270fSchristos 
528a2e2270fSchristos       /* Fetch the name table.  */
529a2e2270fSchristos 
530a2e2270fSchristos       simple_object_mach_o_section_info (omr->is_big_endian, is_32,
531a2e2270fSchristos 					 secdata + nametab_index * sechdrsize,
532a2e2270fSchristos 					 &nametab_offset, &nametab_size);
533a2e2270fSchristos       nametab = XNEWVEC (char, nametab_size);
534a2e2270fSchristos       if (!simple_object_internal_read (sobj->descriptor,
535a2e2270fSchristos 					sobj->offset + nametab_offset,
536a2e2270fSchristos 					(unsigned char *) nametab, nametab_size,
537a2e2270fSchristos 					errmsg, err))
538a2e2270fSchristos 	{
539a2e2270fSchristos 	  XDELETEVEC (nametab);
540a2e2270fSchristos 	  XDELETEVEC (secdata);
541a2e2270fSchristos 	  return 0;
542a2e2270fSchristos 	}
543a2e2270fSchristos 
544a2e2270fSchristos       /* Fetch the index.  */
545a2e2270fSchristos 
546a2e2270fSchristos       simple_object_mach_o_section_info (omr->is_big_endian, is_32,
547a2e2270fSchristos 					 secdata + index_index * sechdrsize,
548a2e2270fSchristos 					 &index_offset, &index_size);
549a2e2270fSchristos       index = XNEWVEC (unsigned char, index_size);
550a2e2270fSchristos       if (!simple_object_internal_read (sobj->descriptor,
551a2e2270fSchristos 					sobj->offset + index_offset,
552a2e2270fSchristos 					index, index_size,
553a2e2270fSchristos 					errmsg, err))
554a2e2270fSchristos 	{
555a2e2270fSchristos 	  XDELETEVEC (index);
556a2e2270fSchristos 	  XDELETEVEC (nametab);
557a2e2270fSchristos 	  XDELETEVEC (secdata);
558a2e2270fSchristos 	  return 0;
559a2e2270fSchristos 	}
560a2e2270fSchristos 
561a2e2270fSchristos       /* The index contains 4 unsigned ints per sub-section:
562a2e2270fSchristos 	 sub-section offset/length, sub-section name/length.
563a2e2270fSchristos 	 We fix this for both 32 and 64 bit mach-o for now, since
564a2e2270fSchristos 	 other fields limit the maximum size of an object to 4G.  */
565a2e2270fSchristos       n_wrapped_sects = index_size / 16;
566a2e2270fSchristos 
567a2e2270fSchristos       /* Get the parameters for the wrapper too.  */
568a2e2270fSchristos       simple_object_mach_o_section_info (omr->is_big_endian, is_32,
569a2e2270fSchristos 					 secdata + sections_index * sechdrsize,
570a2e2270fSchristos 					 &wrapper_sect_offset,
571a2e2270fSchristos 					 &wrapper_sect_size);
57298b9484cSchristos     }
57398b9484cSchristos   else
57498b9484cSchristos     {
575a2e2270fSchristos       index = NULL;
576a2e2270fSchristos       index_size = 0;
577a2e2270fSchristos       nametab = NULL;
578a2e2270fSchristos       nametab_size = 0;
579a2e2270fSchristos       n_wrapped_sects = 0;
580a2e2270fSchristos     }
581a2e2270fSchristos 
582a2e2270fSchristos   /* If we have a long names section, fetch it.  */
583a2e2270fSchristos 
584a2e2270fSchristos   if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0)
585a2e2270fSchristos     {
58698b9484cSchristos       off_t strtab_offset;
58798b9484cSchristos 
58898b9484cSchristos       simple_object_mach_o_section_info (omr->is_big_endian, is_32,
58998b9484cSchristos 					 secdata + strtab_index * sechdrsize,
59098b9484cSchristos 					 &strtab_offset, &strtab_size);
59198b9484cSchristos       strtab = XNEWVEC (char, strtab_size);
59298b9484cSchristos       if (!simple_object_internal_read (sobj->descriptor,
59398b9484cSchristos 					sobj->offset + strtab_offset,
59498b9484cSchristos 					(unsigned char *) strtab, strtab_size,
59598b9484cSchristos 					errmsg, err))
59698b9484cSchristos 	{
59798b9484cSchristos 	  XDELETEVEC (strtab);
598a2e2270fSchristos 	  XDELETEVEC (index);
599a2e2270fSchristos 	  XDELETEVEC (nametab);
60098b9484cSchristos 	  XDELETEVEC (secdata);
60198b9484cSchristos 	  return 0;
60298b9484cSchristos 	}
60398b9484cSchristos     }
604a2e2270fSchristos   else
605a2e2270fSchristos     {
606a2e2270fSchristos       strtab = NULL;
607a2e2270fSchristos       strtab_size = 0;
608a2e2270fSchristos       strtab_index = nsects;
609a2e2270fSchristos     }
61098b9484cSchristos 
61198b9484cSchristos   /* Process the sections.  */
61298b9484cSchristos 
61398b9484cSchristos   for (i = 0; i < nsects; ++i)
61498b9484cSchristos     {
61598b9484cSchristos       const unsigned char *sechdr;
616a2e2270fSchristos       char namebuf[MACH_O_NAME_LEN * 2 + 2];
61798b9484cSchristos       char *name;
61898b9484cSchristos       off_t secoffset;
61998b9484cSchristos       size_t secsize;
620a2e2270fSchristos       int l;
62198b9484cSchristos 
62298b9484cSchristos       sechdr = secdata + i * sechdrsize;
62398b9484cSchristos 
624a2e2270fSchristos       /* We've already processed the long section names.  */
625a2e2270fSchristos 
626a2e2270fSchristos       if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0
627a2e2270fSchristos 	  && i == strtab_index)
628a2e2270fSchristos 	continue;
629a2e2270fSchristos 
630a2e2270fSchristos       /* We only act on the segment named.  */
631a2e2270fSchristos 
63298b9484cSchristos       if (strcmp ((char *) sechdr + segname_offset, omr->segment_name) != 0)
63398b9484cSchristos 	continue;
63498b9484cSchristos 
635a2e2270fSchristos       /* Process sections associated with the wrapper.  */
636a2e2270fSchristos 
637a2e2270fSchristos       if ((gnu_sections_found & SOMO_WRAPPING) != 0)
638a2e2270fSchristos 	{
639a2e2270fSchristos 	  if (i == nametab_index || i == index_index)
640a2e2270fSchristos 	    continue;
641a2e2270fSchristos 
642a2e2270fSchristos 	  if (i == sections_index)
643a2e2270fSchristos 	    {
644a2e2270fSchristos 	      unsigned int j;
645a2e2270fSchristos 	      for (j = 0; j < n_wrapped_sects; ++j)
646a2e2270fSchristos 		{
647a2e2270fSchristos 		  unsigned int subsect_offset, subsect_length, name_offset;
648a2e2270fSchristos 		  subsect_offset = (*fetch_32) (index + 16 * j);
649a2e2270fSchristos 		  subsect_length = (*fetch_32) (index + 16 * j + 4);
650a2e2270fSchristos 		  name_offset = (*fetch_32) (index + 16 * j + 8);
651a2e2270fSchristos 		  /* We don't need the name_length yet.  */
652a2e2270fSchristos 
653a2e2270fSchristos 		  secoffset = wrapper_sect_offset + subsect_offset;
654a2e2270fSchristos 		  secsize = subsect_length;
655a2e2270fSchristos 		  name = nametab + name_offset;
656a2e2270fSchristos 
657a2e2270fSchristos 		  if (!(*pfn) (data, name, secoffset, secsize))
658a2e2270fSchristos 		    {
659a2e2270fSchristos 		      *errmsg = NULL;
660a2e2270fSchristos 		      *err = 0;
661a2e2270fSchristos 		      XDELETEVEC (index);
662a2e2270fSchristos 		      XDELETEVEC (nametab);
663a2e2270fSchristos 		      XDELETEVEC (strtab);
664a2e2270fSchristos 		      XDELETEVEC (secdata);
665a2e2270fSchristos 		      return 0;
666a2e2270fSchristos 		    }
667a2e2270fSchristos 		}
668a2e2270fSchristos 	      continue;
669a2e2270fSchristos 	    }
670a2e2270fSchristos 	}
671a2e2270fSchristos 
672a2e2270fSchristos       if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0)
673a2e2270fSchristos 	{
67498b9484cSchristos 	  memcpy (namebuf, sechdr + sectname_offset, MACH_O_NAME_LEN);
67598b9484cSchristos 	  namebuf[MACH_O_NAME_LEN] = '\0';
67698b9484cSchristos 
67798b9484cSchristos 	  name = &namebuf[0];
67898b9484cSchristos 	  if (strtab != NULL && name[0] == '_' && name[1] == '_')
67998b9484cSchristos 	    {
68098b9484cSchristos 	      unsigned long stringoffset;
68198b9484cSchristos 
68298b9484cSchristos 	      if (sscanf (name + 2, "%08lX", &stringoffset) == 1)
68398b9484cSchristos 		{
68498b9484cSchristos 		  if (stringoffset >= strtab_size)
68598b9484cSchristos 		    {
68698b9484cSchristos 		      *errmsg = "section name offset out of range";
68798b9484cSchristos 		      *err = 0;
688a2e2270fSchristos 		      XDELETEVEC (index);
689a2e2270fSchristos 		      XDELETEVEC (nametab);
69098b9484cSchristos 		      XDELETEVEC (strtab);
69198b9484cSchristos 		      XDELETEVEC (secdata);
69298b9484cSchristos 		      return 0;
69398b9484cSchristos 		    }
69498b9484cSchristos 
69598b9484cSchristos 		  name = strtab + stringoffset;
69698b9484cSchristos 		}
69798b9484cSchristos 	  }
698a2e2270fSchristos 	}
699a2e2270fSchristos       else
700a2e2270fSchristos 	{
701a2e2270fSchristos 	   /* Otherwise, make a name like __segment,__section as per the
702a2e2270fSchristos 	      convention in mach-o asm.  */
703a2e2270fSchristos 	  name = &namebuf[0];
704a2e2270fSchristos 	  memcpy (namebuf, (char *) sechdr + segname_offset, MACH_O_NAME_LEN);
705a2e2270fSchristos 	  namebuf[MACH_O_NAME_LEN] = '\0';
706a2e2270fSchristos 	  l = strlen (namebuf);
707a2e2270fSchristos 	  namebuf[l] = ',';
708a2e2270fSchristos 	  memcpy (namebuf + l + 1, (char *) sechdr + sectname_offset,
709a2e2270fSchristos 		  MACH_O_NAME_LEN);
710a2e2270fSchristos 	  namebuf[l + 1 + MACH_O_NAME_LEN] = '\0';
711a2e2270fSchristos 	}
71298b9484cSchristos 
71398b9484cSchristos       simple_object_mach_o_section_info (omr->is_big_endian, is_32, sechdr,
71498b9484cSchristos 					 &secoffset, &secsize);
71598b9484cSchristos 
71698b9484cSchristos       if (!(*pfn) (data, name, secoffset, secsize))
71798b9484cSchristos 	{
71898b9484cSchristos 	  *errmsg = NULL;
71998b9484cSchristos 	  *err = 0;
720a2e2270fSchristos 	  XDELETEVEC (index);
721a2e2270fSchristos 	  XDELETEVEC (nametab);
72298b9484cSchristos 	  XDELETEVEC (strtab);
72398b9484cSchristos 	  XDELETEVEC (secdata);
72498b9484cSchristos 	  return 0;
72598b9484cSchristos 	}
72698b9484cSchristos     }
72798b9484cSchristos 
728a2e2270fSchristos   XDELETEVEC (index);
729a2e2270fSchristos   XDELETEVEC (nametab);
73098b9484cSchristos   XDELETEVEC (strtab);
73198b9484cSchristos   XDELETEVEC (secdata);
73298b9484cSchristos 
73398b9484cSchristos   return 1;
73498b9484cSchristos }
73598b9484cSchristos 
73698b9484cSchristos /* Find all sections in a Mach-O file.  */
73798b9484cSchristos 
73898b9484cSchristos static const char *
73998b9484cSchristos simple_object_mach_o_find_sections (simple_object_read *sobj,
74098b9484cSchristos 				    int (*pfn) (void *, const char *,
74198b9484cSchristos 						off_t offset, off_t length),
74298b9484cSchristos 				    void *data,
74398b9484cSchristos 				    int *err)
74498b9484cSchristos {
74598b9484cSchristos   struct simple_object_mach_o_read *omr =
74698b9484cSchristos     (struct simple_object_mach_o_read *) sobj->data;
74798b9484cSchristos   off_t offset;
74898b9484cSchristos   size_t seghdrsize;
74998b9484cSchristos   unsigned int (*fetch_32) (const unsigned char *);
75098b9484cSchristos   const char *errmsg;
75198b9484cSchristos   unsigned int i;
75298b9484cSchristos 
75398b9484cSchristos   if (omr->magic == MACH_O_MH_MAGIC)
75498b9484cSchristos     {
75598b9484cSchristos       offset = sizeof (struct mach_o_header_32);
75698b9484cSchristos       seghdrsize = sizeof (struct mach_o_segment_command_32);
75798b9484cSchristos     }
75898b9484cSchristos   else
75998b9484cSchristos     {
76098b9484cSchristos       offset = sizeof (struct mach_o_header_64);
76198b9484cSchristos       seghdrsize = sizeof (struct mach_o_segment_command_64);
76298b9484cSchristos     }
76398b9484cSchristos 
76498b9484cSchristos   fetch_32 = (omr->is_big_endian
76598b9484cSchristos 	      ? simple_object_fetch_big_32
76698b9484cSchristos 	      : simple_object_fetch_little_32);
76798b9484cSchristos 
76898b9484cSchristos   for (i = 0; i < omr->ncmds; ++i)
76998b9484cSchristos     {
77098b9484cSchristos       unsigned char loadbuf[sizeof (struct mach_o_load_command)];
77198b9484cSchristos       unsigned int cmd;
77298b9484cSchristos       unsigned int cmdsize;
77398b9484cSchristos 
77498b9484cSchristos       if (!simple_object_internal_read (sobj->descriptor,
77598b9484cSchristos 					sobj->offset + offset,
77698b9484cSchristos 					loadbuf,
77798b9484cSchristos 					sizeof (struct mach_o_load_command),
77898b9484cSchristos 					&errmsg, err))
77998b9484cSchristos 	return errmsg;
78098b9484cSchristos 
78198b9484cSchristos       cmd = (*fetch_32) (loadbuf + offsetof (struct mach_o_load_command, cmd));
78298b9484cSchristos       cmdsize = (*fetch_32) (loadbuf
78398b9484cSchristos 			     + offsetof (struct mach_o_load_command, cmdsize));
78498b9484cSchristos 
78598b9484cSchristos       if (cmd == MACH_O_LC_SEGMENT || cmd == MACH_O_LC_SEGMENT_64)
78698b9484cSchristos 	{
78798b9484cSchristos 	  unsigned char segbuf[sizeof (struct mach_o_segment_command_64)];
78898b9484cSchristos 	  int r;
78998b9484cSchristos 
79098b9484cSchristos 	  if (!simple_object_internal_read (sobj->descriptor,
79198b9484cSchristos 					    sobj->offset + offset,
79298b9484cSchristos 					    segbuf, seghdrsize, &errmsg, err))
79398b9484cSchristos 	    return errmsg;
79498b9484cSchristos 
79598b9484cSchristos 	  r = simple_object_mach_o_segment (sobj, offset, segbuf, pfn,
79698b9484cSchristos 					    data, &errmsg, err);
79798b9484cSchristos 	  if (!r)
79898b9484cSchristos 	    return errmsg;
79998b9484cSchristos 	}
80098b9484cSchristos 
80198b9484cSchristos       offset += cmdsize;
80298b9484cSchristos     }
80398b9484cSchristos 
80498b9484cSchristos   return NULL;
80598b9484cSchristos }
80698b9484cSchristos 
80798b9484cSchristos /* Fetch the attributes for an simple_object_read.  */
80898b9484cSchristos 
80998b9484cSchristos static void *
81098b9484cSchristos simple_object_mach_o_fetch_attributes (simple_object_read *sobj,
81198b9484cSchristos 				       const char **errmsg ATTRIBUTE_UNUSED,
81298b9484cSchristos 				       int *err ATTRIBUTE_UNUSED)
81398b9484cSchristos {
81498b9484cSchristos   struct simple_object_mach_o_read *omr =
81598b9484cSchristos     (struct simple_object_mach_o_read *) sobj->data;
81698b9484cSchristos   struct simple_object_mach_o_attributes *ret;
81798b9484cSchristos 
81898b9484cSchristos   ret = XNEW (struct simple_object_mach_o_attributes);
81998b9484cSchristos   ret->magic = omr->magic;
82098b9484cSchristos   ret->is_big_endian = omr->is_big_endian;
82198b9484cSchristos   ret->cputype = omr->cputype;
82298b9484cSchristos   ret->cpusubtype = omr->cpusubtype;
82398b9484cSchristos   ret->flags = omr->flags;
82498b9484cSchristos   ret->reserved = omr->reserved;
82598b9484cSchristos   return ret;
82698b9484cSchristos }
82798b9484cSchristos 
82898b9484cSchristos /* Release the private data for an simple_object_read.  */
82998b9484cSchristos 
83098b9484cSchristos static void
83198b9484cSchristos simple_object_mach_o_release_read (void *data)
83298b9484cSchristos {
83398b9484cSchristos   struct simple_object_mach_o_read *omr =
83498b9484cSchristos     (struct simple_object_mach_o_read *) data;
83598b9484cSchristos 
83698b9484cSchristos   free (omr->segment_name);
83798b9484cSchristos   XDELETE (omr);
83898b9484cSchristos }
83998b9484cSchristos 
84098b9484cSchristos /* Compare two attributes structures.  */
84198b9484cSchristos 
84298b9484cSchristos static const char *
84398b9484cSchristos simple_object_mach_o_attributes_merge (void *todata, void *fromdata, int *err)
84498b9484cSchristos {
84598b9484cSchristos   struct simple_object_mach_o_attributes *to =
84698b9484cSchristos     (struct simple_object_mach_o_attributes *) todata;
84798b9484cSchristos   struct simple_object_mach_o_attributes *from =
84898b9484cSchristos     (struct simple_object_mach_o_attributes *) fromdata;
84998b9484cSchristos 
85098b9484cSchristos   if (to->magic != from->magic
85198b9484cSchristos       || to->is_big_endian != from->is_big_endian
85298b9484cSchristos       || to->cputype != from->cputype)
85398b9484cSchristos     {
85498b9484cSchristos       *err = 0;
85598b9484cSchristos       return "Mach-O object format mismatch";
85698b9484cSchristos     }
85798b9484cSchristos   return NULL;
85898b9484cSchristos }
85998b9484cSchristos 
86098b9484cSchristos /* Release the private data for an attributes structure.  */
86198b9484cSchristos 
86298b9484cSchristos static void
86398b9484cSchristos simple_object_mach_o_release_attributes (void *data)
86498b9484cSchristos {
86598b9484cSchristos   XDELETE (data);
86698b9484cSchristos }
86798b9484cSchristos 
86898b9484cSchristos /* Prepare to write out a file.  */
86998b9484cSchristos 
87098b9484cSchristos static void *
87198b9484cSchristos simple_object_mach_o_start_write (void *attributes_data,
87298b9484cSchristos 				  const char **errmsg ATTRIBUTE_UNUSED,
87398b9484cSchristos 				  int *err ATTRIBUTE_UNUSED)
87498b9484cSchristos {
87598b9484cSchristos   struct simple_object_mach_o_attributes *attrs =
87698b9484cSchristos     (struct simple_object_mach_o_attributes *) attributes_data;
87798b9484cSchristos   struct simple_object_mach_o_attributes *ret;
87898b9484cSchristos 
87998b9484cSchristos   /* We're just going to record the attributes, but we need to make a
88098b9484cSchristos      copy because the user may delete them.  */
88198b9484cSchristos   ret = XNEW (struct simple_object_mach_o_attributes);
88298b9484cSchristos   *ret = *attrs;
88398b9484cSchristos   return ret;
88498b9484cSchristos }
88598b9484cSchristos 
88698b9484cSchristos /* Write out the header of a Mach-O file.  */
88798b9484cSchristos 
88898b9484cSchristos static int
88998b9484cSchristos simple_object_mach_o_write_header (simple_object_write *sobj, int descriptor,
89098b9484cSchristos 				   size_t nsects, const char **errmsg,
89198b9484cSchristos 				   int *err)
89298b9484cSchristos {
89398b9484cSchristos   struct simple_object_mach_o_attributes *attrs =
89498b9484cSchristos     (struct simple_object_mach_o_attributes *) sobj->data;
89598b9484cSchristos   void (*set_32) (unsigned char *, unsigned int);
89698b9484cSchristos   unsigned char hdrbuf[sizeof (struct mach_o_header_64)];
89798b9484cSchristos   unsigned char *hdr;
89898b9484cSchristos   size_t wrsize;
89998b9484cSchristos 
90098b9484cSchristos   set_32 = (attrs->is_big_endian
90198b9484cSchristos 	    ? simple_object_set_big_32
90298b9484cSchristos 	    : simple_object_set_little_32);
90398b9484cSchristos 
90498b9484cSchristos   memset (hdrbuf, 0, sizeof hdrbuf);
90598b9484cSchristos 
90698b9484cSchristos   /* The 32-bit and 64-bit headers start out the same.  */
90798b9484cSchristos 
90898b9484cSchristos   hdr = &hdrbuf[0];
90998b9484cSchristos   set_32 (hdr + offsetof (struct mach_o_header_32, magic), attrs->magic);
91098b9484cSchristos   set_32 (hdr + offsetof (struct mach_o_header_32, cputype), attrs->cputype);
91198b9484cSchristos   set_32 (hdr + offsetof (struct mach_o_header_32, cpusubtype),
91298b9484cSchristos 	  attrs->cpusubtype);
91398b9484cSchristos   set_32 (hdr + offsetof (struct mach_o_header_32, filetype), MACH_O_MH_OBJECT);
91498b9484cSchristos   set_32 (hdr + offsetof (struct mach_o_header_32, ncmds), 1);
91598b9484cSchristos   set_32 (hdr + offsetof (struct mach_o_header_32, flags), attrs->flags);
91698b9484cSchristos   if (attrs->magic == MACH_O_MH_MAGIC)
91798b9484cSchristos     {
91898b9484cSchristos       wrsize = sizeof (struct mach_o_header_32);
91998b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_header_32, sizeofcmds),
92098b9484cSchristos 	      (sizeof (struct mach_o_segment_command_32)
92198b9484cSchristos 	       + nsects * sizeof (struct mach_o_section_32)));
92298b9484cSchristos     }
92398b9484cSchristos   else
92498b9484cSchristos     {
92598b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_header_64, sizeofcmds),
92698b9484cSchristos 	      (sizeof (struct mach_o_segment_command_64)
92798b9484cSchristos 	       + nsects * sizeof (struct mach_o_section_64)));
92898b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_header_64, reserved),
92998b9484cSchristos 	      attrs->reserved);
93098b9484cSchristos       wrsize = sizeof (struct mach_o_header_64);
93198b9484cSchristos     }
93298b9484cSchristos 
93398b9484cSchristos   return simple_object_internal_write (descriptor, 0, hdrbuf, wrsize,
93498b9484cSchristos 				       errmsg, err);
93598b9484cSchristos }
93698b9484cSchristos 
93798b9484cSchristos /* Write a Mach-O section header.  */
93898b9484cSchristos 
93998b9484cSchristos static int
94098b9484cSchristos simple_object_mach_o_write_section_header (simple_object_write *sobj,
94198b9484cSchristos 					   int descriptor,
94298b9484cSchristos 					   size_t sechdr_offset,
943a2e2270fSchristos 					   const char *name, const char *segn,
944a2e2270fSchristos 					   size_t secaddr, size_t secsize,
945a2e2270fSchristos 					   size_t offset, unsigned int align,
94698b9484cSchristos 					   const char **errmsg, int *err)
94798b9484cSchristos {
94898b9484cSchristos   struct simple_object_mach_o_attributes *attrs =
94998b9484cSchristos     (struct simple_object_mach_o_attributes *) sobj->data;
95098b9484cSchristos   void (*set_32) (unsigned char *, unsigned int);
95198b9484cSchristos   unsigned char hdrbuf[sizeof (struct mach_o_section_64)];
95298b9484cSchristos   unsigned char *hdr;
95398b9484cSchristos   size_t sechdrsize;
95498b9484cSchristos 
95598b9484cSchristos   set_32 = (attrs->is_big_endian
95698b9484cSchristos 	    ? simple_object_set_big_32
95798b9484cSchristos 	    : simple_object_set_little_32);
95898b9484cSchristos 
95998b9484cSchristos   memset (hdrbuf, 0, sizeof hdrbuf);
96098b9484cSchristos 
96198b9484cSchristos   hdr = &hdrbuf[0];
96298b9484cSchristos   if (attrs->magic == MACH_O_MH_MAGIC)
96398b9484cSchristos     {
96498b9484cSchristos       strncpy ((char *) hdr + offsetof (struct mach_o_section_32, sectname),
96598b9484cSchristos 	       name, MACH_O_NAME_LEN);
96698b9484cSchristos       strncpy ((char *) hdr + offsetof (struct mach_o_section_32, segname),
967a2e2270fSchristos 	       segn, MACH_O_NAME_LEN);
96898b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_section_32, addr), secaddr);
96998b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_section_32, size), secsize);
97098b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_section_32, offset), offset);
97198b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_section_32, align), align);
97298b9484cSchristos       /* reloff left as zero.  */
97398b9484cSchristos       /* nreloc left as zero.  */
97498b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_section_32, flags),
97598b9484cSchristos 	      MACH_O_S_ATTR_DEBUG);
97698b9484cSchristos       /* reserved1 left as zero.  */
97798b9484cSchristos       /* reserved2 left as zero.  */
97898b9484cSchristos       sechdrsize = sizeof (struct mach_o_section_32);
97998b9484cSchristos     }
98098b9484cSchristos   else
98198b9484cSchristos     {
98298b9484cSchristos #ifdef UNSIGNED_64BIT_TYPE
98398b9484cSchristos       void (*set_64) (unsigned char *, ulong_type);
98498b9484cSchristos 
98598b9484cSchristos       set_64 = (attrs->is_big_endian
98698b9484cSchristos 		? simple_object_set_big_64
98798b9484cSchristos 		: simple_object_set_little_64);
98898b9484cSchristos 
98998b9484cSchristos       strncpy ((char *) hdr + offsetof (struct mach_o_section_64, sectname),
99098b9484cSchristos 	       name, MACH_O_NAME_LEN);
99198b9484cSchristos       strncpy ((char *) hdr + offsetof (struct mach_o_section_64, segname),
992a2e2270fSchristos 	       segn, MACH_O_NAME_LEN);
99398b9484cSchristos       set_64 (hdr + offsetof (struct mach_o_section_64, addr), secaddr);
99498b9484cSchristos       set_64 (hdr + offsetof (struct mach_o_section_64, size), secsize);
99598b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_section_64, offset), offset);
99698b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_section_64, align), align);
99798b9484cSchristos       /* reloff left as zero.  */
99898b9484cSchristos       /* nreloc left as zero.  */
99998b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_section_64, flags),
100098b9484cSchristos 	      MACH_O_S_ATTR_DEBUG);
100198b9484cSchristos       /* reserved1 left as zero.  */
100298b9484cSchristos       /* reserved2 left as zero.  */
100398b9484cSchristos       /* reserved3 left as zero.  */
100498b9484cSchristos #endif
100598b9484cSchristos       sechdrsize = sizeof (struct mach_o_section_64);
100698b9484cSchristos     }
100798b9484cSchristos 
100898b9484cSchristos   return simple_object_internal_write (descriptor, sechdr_offset, hdr,
100998b9484cSchristos 				       sechdrsize, errmsg, err);
101098b9484cSchristos }
101198b9484cSchristos 
1012a2e2270fSchristos /* Write out the single (anonymous) segment containing the sections of a Mach-O
1013a2e2270fSchristos    Object file.
1014a2e2270fSchristos 
1015a2e2270fSchristos    As a GNU extension to mach-o, when the caller specifies a segment name in
1016a2e2270fSchristos    sobj->segment_name, all the sections passed will be output under a single
1017a2e2270fSchristos    mach-o section header.  The caller's sections are indexed within this
1018a2e2270fSchristos    'wrapper' section by a table stored in a second mach-o section.  Finally,
1019a2e2270fSchristos    arbitrary length section names are permitted by the extension and these are
1020a2e2270fSchristos    stored in a table in a third mach-o section.
1021a2e2270fSchristos 
1022a2e2270fSchristos    Note that this is only likely to make any sense for the __GNU_LTO segment
1023a2e2270fSchristos    at present.
1024a2e2270fSchristos 
1025a2e2270fSchristos    If the wrapper extension is not in force, we assume that the section name
1026a2e2270fSchristos    is in the form __SEGMENT_NAME,__section_name as per Mach-O asm.  */
102798b9484cSchristos 
102898b9484cSchristos static int
102998b9484cSchristos simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor,
1030a2e2270fSchristos 				    size_t *nsects, const char **errmsg,
103198b9484cSchristos 				    int *err)
103298b9484cSchristos {
103398b9484cSchristos   struct simple_object_mach_o_attributes *attrs =
103498b9484cSchristos     (struct simple_object_mach_o_attributes *) sobj->data;
103598b9484cSchristos   void (*set_32) (unsigned char *, unsigned int);
103698b9484cSchristos   size_t hdrsize;
103798b9484cSchristos   size_t seghdrsize;
103898b9484cSchristos   size_t sechdrsize;
103998b9484cSchristos   size_t cmdsize;
104098b9484cSchristos   size_t offset;
104198b9484cSchristos   size_t sechdr_offset;
104298b9484cSchristos   size_t secaddr;
104398b9484cSchristos   unsigned int name_offset;
104498b9484cSchristos   simple_object_write_section *section;
104598b9484cSchristos   unsigned char hdrbuf[sizeof (struct mach_o_segment_command_64)];
104698b9484cSchristos   unsigned char *hdr;
1047a2e2270fSchristos   size_t nsects_in;
1048a2e2270fSchristos   unsigned int *index;
1049a2e2270fSchristos   char *snames;
1050a2e2270fSchristos   unsigned int sect;
105198b9484cSchristos 
105298b9484cSchristos   set_32 = (attrs->is_big_endian
105398b9484cSchristos 	    ? simple_object_set_big_32
105498b9484cSchristos 	    : simple_object_set_little_32);
105598b9484cSchristos 
105698b9484cSchristos   /* Write out the sections first.  */
105798b9484cSchristos 
105898b9484cSchristos   if (attrs->magic == MACH_O_MH_MAGIC)
105998b9484cSchristos     {
106098b9484cSchristos       hdrsize = sizeof (struct mach_o_header_32);
106198b9484cSchristos       seghdrsize = sizeof (struct mach_o_segment_command_32);
106298b9484cSchristos       sechdrsize = sizeof (struct mach_o_section_32);
106398b9484cSchristos     }
106498b9484cSchristos   else
106598b9484cSchristos     {
106698b9484cSchristos       hdrsize = sizeof (struct mach_o_header_64);
106798b9484cSchristos       seghdrsize = sizeof (struct mach_o_segment_command_64);
106898b9484cSchristos       sechdrsize = sizeof (struct mach_o_section_64);
106998b9484cSchristos     }
107098b9484cSchristos 
107198b9484cSchristos   name_offset = 0;
1072a2e2270fSchristos   *nsects = nsects_in = 0;
1073a2e2270fSchristos 
1074a2e2270fSchristos   /* Count the number of sections we start with.  */
107598b9484cSchristos 
107698b9484cSchristos   for (section = sobj->sections; section != NULL; section = section->next)
1077a2e2270fSchristos     nsects_in++;
1078a2e2270fSchristos 
1079a2e2270fSchristos   if (sobj->segment_name != NULL)
1080a2e2270fSchristos     {
1081a2e2270fSchristos       /* We will only write 3 sections: wrapped data, index and names.  */
1082a2e2270fSchristos 
1083a2e2270fSchristos       *nsects = 3;
1084a2e2270fSchristos 
1085a2e2270fSchristos       /* The index has four entries per wrapped section:
1086a2e2270fSchristos 	   Section Offset, length,  Name offset, length.
1087a2e2270fSchristos 	 Where the offsets are based at the start of the wrapper and name
1088a2e2270fSchristos 	 sections respectively.
1089a2e2270fSchristos 	 The values are stored as 32 bit int for both 32 and 64 bit mach-o
1090a2e2270fSchristos 	 since the size of a mach-o MH_OBJECT cannot exceed 4G owing to
1091a2e2270fSchristos 	 other constraints.  */
1092a2e2270fSchristos 
1093a2e2270fSchristos       index = XNEWVEC (unsigned int, nsects_in * 4);
1094a2e2270fSchristos 
1095a2e2270fSchristos       /* We now need to figure out the size of the names section.  This just
1096a2e2270fSchristos 	 stores the names as null-terminated c strings, packed without any
1097a2e2270fSchristos 	 alignment padding.  */
1098a2e2270fSchristos 
1099a2e2270fSchristos       for (section = sobj->sections, sect = 0; section != NULL;
1100a2e2270fSchristos 	   section = section->next, sect++)
1101a2e2270fSchristos 	{
1102a2e2270fSchristos 	  index[sect*4+2] = name_offset;
1103a2e2270fSchristos 	  index[sect*4+3] = strlen (section->name) + 1;
1104a2e2270fSchristos 	  name_offset += strlen (section->name) + 1;
1105a2e2270fSchristos 	}
1106a2e2270fSchristos       snames = XNEWVEC (char, name_offset);
1107a2e2270fSchristos     }
1108a2e2270fSchristos   else
1109a2e2270fSchristos     {
1110a2e2270fSchristos       *nsects = nsects_in;
1111a2e2270fSchristos       index = NULL;
1112a2e2270fSchristos       snames = NULL;
1113a2e2270fSchristos     }
1114a2e2270fSchristos 
1115a2e2270fSchristos   sechdr_offset = hdrsize + seghdrsize;
1116a2e2270fSchristos   cmdsize = seghdrsize + *nsects * sechdrsize;
1117a2e2270fSchristos   offset = hdrsize + cmdsize;
1118a2e2270fSchristos   secaddr = 0;
1119a2e2270fSchristos 
1120a2e2270fSchristos   for (section = sobj->sections, sect = 0;
1121a2e2270fSchristos        section != NULL; section = section->next, sect++)
112298b9484cSchristos     {
112398b9484cSchristos       size_t mask;
112498b9484cSchristos       size_t new_offset;
112598b9484cSchristos       size_t secsize;
112698b9484cSchristos       struct simple_object_write_section_buffer *buffer;
112798b9484cSchristos 
112898b9484cSchristos       mask = (1U << section->align) - 1;
112998b9484cSchristos       new_offset = offset + mask;
113098b9484cSchristos       new_offset &= ~ mask;
113198b9484cSchristos       while (new_offset > offset)
113298b9484cSchristos 	{
113398b9484cSchristos 	  unsigned char zeroes[16];
113498b9484cSchristos 	  size_t write;
113598b9484cSchristos 
113698b9484cSchristos 	  memset (zeroes, 0, sizeof zeroes);
113798b9484cSchristos 	  write = new_offset - offset;
113898b9484cSchristos 	  if (write > sizeof zeroes)
113998b9484cSchristos 	    write = sizeof zeroes;
114098b9484cSchristos 	  if (!simple_object_internal_write (descriptor, offset, zeroes, write,
114198b9484cSchristos 					     errmsg, err))
114298b9484cSchristos 	    return 0;
114398b9484cSchristos 	  offset += write;
114498b9484cSchristos 	}
114598b9484cSchristos 
114698b9484cSchristos       secsize = 0;
114798b9484cSchristos       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
114898b9484cSchristos 	{
114998b9484cSchristos 	  if (!simple_object_internal_write (descriptor, offset + secsize,
115098b9484cSchristos 					     ((const unsigned char *)
115198b9484cSchristos 					      buffer->buffer),
115298b9484cSchristos 					     buffer->size, errmsg, err))
115398b9484cSchristos 	    return 0;
115498b9484cSchristos 	  secsize += buffer->size;
115598b9484cSchristos 	}
115698b9484cSchristos 
1157a2e2270fSchristos       if (sobj->segment_name != NULL)
1158a2e2270fSchristos 	{
1159a2e2270fSchristos 	  index[sect*4+0] = (unsigned int) offset;
1160a2e2270fSchristos 	  index[sect*4+1] = secsize;
1161a2e2270fSchristos 	  /* Stash the section name in our table.  */
1162a2e2270fSchristos 	  memcpy (snames + index[sect * 4 + 2], section->name,
1163a2e2270fSchristos 		  index[sect * 4 + 3]);
116498b9484cSchristos 	}
1165a2e2270fSchristos       else
1166a2e2270fSchristos 	{
1167a2e2270fSchristos 	  char namebuf[MACH_O_NAME_LEN + 1];
1168a2e2270fSchristos 	  char segnbuf[MACH_O_NAME_LEN + 1];
1169a2e2270fSchristos 	  char *comma;
117098b9484cSchristos 
1171a2e2270fSchristos 	  /* Try to extract segment,section from the input name.  */
1172a2e2270fSchristos 
1173a2e2270fSchristos 	  memset (namebuf, 0, sizeof namebuf);
1174a2e2270fSchristos 	  memset (segnbuf, 0, sizeof segnbuf);
1175a2e2270fSchristos 	  comma = strchr (section->name, ',');
1176a2e2270fSchristos 	  if (comma != NULL)
1177a2e2270fSchristos 	    {
1178a2e2270fSchristos 	      int len = comma - section->name;
1179a2e2270fSchristos 	      len = len > MACH_O_NAME_LEN ? MACH_O_NAME_LEN : len;
1180a2e2270fSchristos 	      strncpy (namebuf, section->name, len);
1181a2e2270fSchristos 	      strncpy (segnbuf, comma + 1, MACH_O_NAME_LEN);
1182a2e2270fSchristos 	    }
1183a2e2270fSchristos 	  else /* just try to copy the name, leave segment blank.  */
1184a2e2270fSchristos 	    strncpy (namebuf, section->name, MACH_O_NAME_LEN);
118598b9484cSchristos 
118698b9484cSchristos 	  if (!simple_object_mach_o_write_section_header (sobj, descriptor,
118798b9484cSchristos 							  sechdr_offset,
1188a2e2270fSchristos 							  namebuf, segnbuf,
1189a2e2270fSchristos 							  secaddr, secsize,
1190a2e2270fSchristos 							  offset,
1191a2e2270fSchristos 							  section->align,
1192a2e2270fSchristos 							  errmsg, err))
1193a2e2270fSchristos 	    return 0;
1194a2e2270fSchristos 	  sechdr_offset += sechdrsize;
1195a2e2270fSchristos 	}
1196a2e2270fSchristos 
1197a2e2270fSchristos       offset += secsize;
1198a2e2270fSchristos       secaddr += secsize;
1199a2e2270fSchristos     }
1200a2e2270fSchristos 
1201a2e2270fSchristos   if (sobj->segment_name != NULL)
1202a2e2270fSchristos     {
1203a2e2270fSchristos       size_t secsize;
1204a2e2270fSchristos       unsigned int i;
1205a2e2270fSchristos 
1206a2e2270fSchristos       /* Write the section header for the wrapper.  */
1207a2e2270fSchristos       /* Account for any initial aligment - which becomes the alignment for this
1208a2e2270fSchristos 	 created section.  */
1209a2e2270fSchristos 
1210a2e2270fSchristos       secsize = (offset - index[0]);
1211a2e2270fSchristos       if (!simple_object_mach_o_write_section_header (sobj, descriptor,
1212a2e2270fSchristos 						      sechdr_offset,
1213a2e2270fSchristos 						      GNU_WRAPPER_SECTS,
1214a2e2270fSchristos 						      sobj->segment_name,
1215a2e2270fSchristos 						      0 /*secaddr*/,
1216a2e2270fSchristos 						      secsize, index[0],
1217a2e2270fSchristos 						      sobj->sections->align,
121898b9484cSchristos 						      errmsg, err))
121998b9484cSchristos 	return 0;
122098b9484cSchristos 
1221a2e2270fSchristos       /* Subtract the wrapper section start from the begining of each sub
1222a2e2270fSchristos 	 section.  */
122398b9484cSchristos 
1224a2e2270fSchristos       for (i = 1; i < nsects_in; ++i)
1225a2e2270fSchristos 	index[4 * i] -= index[0];
1226a2e2270fSchristos       index[0] = 0;
1227a2e2270fSchristos 
12284b169a6bSchristos       /* Swap the indices, if required.  */
12294b169a6bSchristos 
12304b169a6bSchristos       for (i = 0; i < (nsects_in * 4); ++i)
12314b169a6bSchristos 	set_32 ((unsigned char *) &index[i], index[i]);
12324b169a6bSchristos 
1233a2e2270fSchristos       sechdr_offset += sechdrsize;
1234a2e2270fSchristos 
1235a2e2270fSchristos       /* Write out the section names.
1236a2e2270fSchristos 	 ... the header ...
1237a2e2270fSchristos 	 name_offset contains the length of the section.  It is not aligned.  */
1238a2e2270fSchristos 
1239a2e2270fSchristos       if (!simple_object_mach_o_write_section_header (sobj, descriptor,
1240a2e2270fSchristos 						      sechdr_offset,
1241a2e2270fSchristos 						      GNU_WRAPPER_NAMES,
1242a2e2270fSchristos 						      sobj->segment_name,
1243a2e2270fSchristos 						      0 /*secaddr*/,
1244a2e2270fSchristos 						      name_offset,
1245a2e2270fSchristos 						      offset,
1246a2e2270fSchristos 						      0, errmsg, err))
124798b9484cSchristos 	return 0;
1248a2e2270fSchristos 
1249a2e2270fSchristos       /* ... and the content.. */
1250a2e2270fSchristos       if (!simple_object_internal_write (descriptor, offset,
1251a2e2270fSchristos 					 (const unsigned char *) snames,
1252a2e2270fSchristos 					 name_offset, errmsg, err))
1253a2e2270fSchristos 	return 0;
1254a2e2270fSchristos 
1255a2e2270fSchristos       sechdr_offset += sechdrsize;
1256a2e2270fSchristos       secaddr += name_offset;
1257a2e2270fSchristos       offset += name_offset;
1258a2e2270fSchristos 
1259a2e2270fSchristos       /* Now do the index, we'll align this to 4 bytes although the read code
1260a2e2270fSchristos 	 will handle unaligned.  */
1261a2e2270fSchristos 
1262a2e2270fSchristos       offset += 3;
1263a2e2270fSchristos       offset &= ~0x03;
1264a2e2270fSchristos       if (!simple_object_mach_o_write_section_header (sobj, descriptor,
1265a2e2270fSchristos 						      sechdr_offset,
1266a2e2270fSchristos 						      GNU_WRAPPER_INDEX,
1267a2e2270fSchristos 						      sobj->segment_name,
1268a2e2270fSchristos 						      0 /*secaddr*/,
1269a2e2270fSchristos 						      nsects_in * 16,
1270a2e2270fSchristos 						      offset,
1271a2e2270fSchristos 						      2, errmsg, err))
1272a2e2270fSchristos 	return 0;
1273a2e2270fSchristos 
1274a2e2270fSchristos       /* ... and the content.. */
1275a2e2270fSchristos       if (!simple_object_internal_write (descriptor, offset,
1276a2e2270fSchristos 					 (const unsigned char *) index,
1277a2e2270fSchristos 					 nsects_in*16, errmsg, err))
1278a2e2270fSchristos 	return 0;
1279a2e2270fSchristos 
1280a2e2270fSchristos       XDELETEVEC (index);
1281a2e2270fSchristos       XDELETEVEC (snames);
128298b9484cSchristos     }
128398b9484cSchristos 
128498b9484cSchristos   /* Write out the segment header.  */
128598b9484cSchristos 
128698b9484cSchristos   memset (hdrbuf, 0, sizeof hdrbuf);
128798b9484cSchristos 
128898b9484cSchristos   hdr = &hdrbuf[0];
128998b9484cSchristos   if (attrs->magic == MACH_O_MH_MAGIC)
129098b9484cSchristos     {
129198b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmd),
129298b9484cSchristos 	      MACH_O_LC_SEGMENT);
129398b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmdsize),
129498b9484cSchristos 	      cmdsize);
1295a2e2270fSchristos      /* MH_OBJECTS have a single, anonymous, segment - so the segment name
1296a2e2270fSchristos 	 is left empty.  */
129798b9484cSchristos       /* vmaddr left as zero.  */
129898b9484cSchristos       /* vmsize left as zero.  */
129998b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_segment_command_32, fileoff),
130098b9484cSchristos 	      hdrsize + cmdsize);
130198b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_segment_command_32, filesize),
130298b9484cSchristos 	      offset - (hdrsize + cmdsize));
130398b9484cSchristos       /* maxprot left as zero.  */
130498b9484cSchristos       /* initprot left as zero.  */
130598b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_segment_command_32, nsects),
1306a2e2270fSchristos 	      *nsects);
130798b9484cSchristos       /* flags left as zero.  */
130898b9484cSchristos     }
130998b9484cSchristos   else
131098b9484cSchristos     {
131198b9484cSchristos #ifdef UNSIGNED_64BIT_TYPE
131298b9484cSchristos       void (*set_64) (unsigned char *, ulong_type);
131398b9484cSchristos 
131498b9484cSchristos       set_64 = (attrs->is_big_endian
131598b9484cSchristos 		? simple_object_set_big_64
131698b9484cSchristos 		: simple_object_set_little_64);
131798b9484cSchristos 
131898b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmd),
131998b9484cSchristos 	      MACH_O_LC_SEGMENT);
132098b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmdsize),
132198b9484cSchristos 	      cmdsize);
1322a2e2270fSchristos       /* MH_OBJECTS have a single, anonymous, segment - so the segment name
1323a2e2270fSchristos 	 is left empty.  */
132498b9484cSchristos       /* vmaddr left as zero.  */
132598b9484cSchristos       /* vmsize left as zero.  */
132698b9484cSchristos       set_64 (hdr + offsetof (struct mach_o_segment_command_64, fileoff),
132798b9484cSchristos 	      hdrsize + cmdsize);
132898b9484cSchristos       set_64 (hdr + offsetof (struct mach_o_segment_command_64, filesize),
132998b9484cSchristos 	      offset - (hdrsize + cmdsize));
133098b9484cSchristos       /* maxprot left as zero.  */
133198b9484cSchristos       /* initprot left as zero.  */
133298b9484cSchristos       set_32 (hdr + offsetof (struct mach_o_segment_command_64, nsects),
1333a2e2270fSchristos 	      *nsects);
133498b9484cSchristos       /* flags left as zero.  */
133598b9484cSchristos #endif
133698b9484cSchristos     }
133798b9484cSchristos 
133898b9484cSchristos   return simple_object_internal_write (descriptor, hdrsize, hdr, seghdrsize,
133998b9484cSchristos 				       errmsg, err);
134098b9484cSchristos }
134198b9484cSchristos 
134298b9484cSchristos /* Write out a complete Mach-O file.  */
134398b9484cSchristos 
134498b9484cSchristos static const char *
134598b9484cSchristos simple_object_mach_o_write_to_file (simple_object_write *sobj, int descriptor,
134698b9484cSchristos 				    int *err)
134798b9484cSchristos {
1348a2e2270fSchristos   size_t nsects = 0;
134998b9484cSchristos   const char *errmsg;
135098b9484cSchristos 
1351a2e2270fSchristos   if (!simple_object_mach_o_write_segment (sobj, descriptor, &nsects,
135298b9484cSchristos 					   &errmsg, err))
135398b9484cSchristos     return errmsg;
135498b9484cSchristos 
1355a2e2270fSchristos   if (!simple_object_mach_o_write_header (sobj, descriptor, nsects,
135698b9484cSchristos 					  &errmsg, err))
135798b9484cSchristos     return errmsg;
135898b9484cSchristos 
135998b9484cSchristos   return NULL;
136098b9484cSchristos }
136198b9484cSchristos 
136298b9484cSchristos /* Release the private data for an simple_object_write structure.  */
136398b9484cSchristos 
136498b9484cSchristos static void
136598b9484cSchristos simple_object_mach_o_release_write (void *data)
136698b9484cSchristos {
136798b9484cSchristos   XDELETE (data);
136898b9484cSchristos }
136998b9484cSchristos 
137098b9484cSchristos /* The Mach-O functions.  */
137198b9484cSchristos 
137298b9484cSchristos const struct simple_object_functions simple_object_mach_o_functions =
137398b9484cSchristos {
137498b9484cSchristos   simple_object_mach_o_match,
137598b9484cSchristos   simple_object_mach_o_find_sections,
137698b9484cSchristos   simple_object_mach_o_fetch_attributes,
137798b9484cSchristos   simple_object_mach_o_release_read,
137898b9484cSchristos   simple_object_mach_o_attributes_merge,
137998b9484cSchristos   simple_object_mach_o_release_attributes,
138098b9484cSchristos   simple_object_mach_o_start_write,
138198b9484cSchristos   simple_object_mach_o_write_to_file,
13824559860eSchristos   simple_object_mach_o_release_write,
13834559860eSchristos   NULL
138498b9484cSchristos };
1385