1/* simple-object-mach-o.c -- routines to manipulate Mach-O object files.
2   Copyright 2010 Free Software Foundation, Inc.
3   Written by Ian Lance Taylor, Google.
4
5This program is free software; you can redistribute it and/or modify it
6under the terms of the GNU General Public License as published by the
7Free Software Foundation; either version 2, or (at your option) any
8later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 51 Franklin Street - Fifth Floor,
18Boston, MA 02110-1301, USA.  */
19
20#include "config.h"
21#include "libiberty.h"
22#include "simple-object.h"
23
24#include <stddef.h>
25
26#ifdef HAVE_STDLIB_H
27#include <stdlib.h>
28#endif
29
30#ifdef HAVE_STDINT_H
31#include <stdint.h>
32#endif
33
34#ifdef HAVE_STRING_H
35#include <string.h>
36#endif
37
38#ifdef HAVE_INTTYPES_H
39#include <inttypes.h>
40#endif
41
42#include "simple-object-common.h"
43
44/* Mach-O structures and constants.  */
45
46/* Mach-O header (32-bit version).  */
47
48struct mach_o_header_32
49{
50  unsigned char magic[4];	/* Magic number.  */
51  unsigned char cputype[4];	/* CPU that this object is for.  */
52  unsigned char cpusubtype[4];	/* CPU subtype.  */
53  unsigned char filetype[4];	/* Type of file.  */
54  unsigned char ncmds[4];	/* Number of load commands.  */
55  unsigned char sizeofcmds[4];	/* Total size of load commands.  */
56  unsigned char flags[4];	/* Flags for special featues.  */
57};
58
59/* Mach-O header (64-bit version).  */
60
61struct mach_o_header_64
62{
63  unsigned char magic[4];	/* Magic number.  */
64  unsigned char cputype[4];	/* CPU that this object is for.  */
65  unsigned char cpusubtype[4];	/* CPU subtype.  */
66  unsigned char filetype[4];	/* Type of file.  */
67  unsigned char ncmds[4];	/* Number of load commands.  */
68  unsigned char sizeofcmds[4];	/* Total size of load commands.  */
69  unsigned char flags[4];	/* Flags for special featues.  */
70  unsigned char reserved[4];	/* Reserved.  Duh.  */
71};
72
73/* For magic field in header.  */
74
75#define MACH_O_MH_MAGIC			0xfeedface
76#define MACH_O_MH_MAGIC_64		0xfeedfacf
77
78/* For filetype field in header.  */
79
80#define MACH_O_MH_OBJECT		0x01
81
82/* A Mach-O file is a list of load commands.  This is the header of a
83   load command.  */
84
85struct mach_o_load_command
86{
87  unsigned char cmd[4];		/* The type of load command.  */
88  unsigned char cmdsize[4];	/* Size in bytes of entire command.  */
89};
90
91/* For cmd field in load command.   */
92
93#define MACH_O_LC_SEGMENT		0x01
94#define MACH_O_LC_SEGMENT_64		0x19
95
96/* LC_SEGMENT load command.  */
97
98struct mach_o_segment_command_32
99{
100  unsigned char cmd[4];		/* The type of load command (LC_SEGMENT).  */
101  unsigned char cmdsize[4];	/* Size in bytes of entire command.  */
102  unsigned char segname[16];	/* Name of this segment.  */
103  unsigned char vmaddr[4];	/* Virtual memory address of this segment.  */
104  unsigned char vmsize[4];	/* Size there, in bytes.  */
105  unsigned char fileoff[4];	/* Offset in bytes of the data to be mapped.  */
106  unsigned char filesize[4];	/* Size in bytes on disk.  */
107  unsigned char maxprot[4];	/* Maximum permitted vmem protection.  */
108  unsigned char initprot[4];	/* Initial vmem protection.  */
109  unsigned char nsects[4];	/* Number of sections in this segment.  */
110  unsigned char flags[4];	/* Flags that affect the loading.  */
111};
112
113/* LC_SEGMENT_64 load command.  */
114
115struct mach_o_segment_command_64
116{
117  unsigned char cmd[4];		/* The type of load command (LC_SEGMENT_64).  */
118  unsigned char cmdsize[4];	/* Size in bytes of entire command.  */
119  unsigned char segname[16];	/* Name of this segment.  */
120  unsigned char vmaddr[8];	/* Virtual memory address of this segment.  */
121  unsigned char vmsize[8];	/* Size there, in bytes.  */
122  unsigned char fileoff[8];	/* Offset in bytes of the data to be mapped.  */
123  unsigned char filesize[8];	/* Size in bytes on disk.  */
124  unsigned char maxprot[4];	/* Maximum permitted vmem protection.  */
125  unsigned char initprot[4];	/* Initial vmem protection.  */
126  unsigned char nsects[4];	/* Number of sections in this segment.  */
127  unsigned char flags[4];	/* Flags that affect the loading.  */
128};
129
130/* 32-bit section header.  */
131
132struct mach_o_section_32
133{
134  unsigned char sectname[16];	/* Section name.  */
135  unsigned char segname[16];	/* Segment that the section belongs to.  */
136  unsigned char addr[4];	/* Address of this section in memory.  */
137  unsigned char size[4];	/* Size in bytes of this section.  */
138  unsigned char offset[4];	/* File offset of this section.  */
139  unsigned char align[4];	/* log2 of this section's alignment.  */
140  unsigned char reloff[4];	/* File offset of this section's relocs.  */
141  unsigned char nreloc[4];	/* Number of relocs for this section.  */
142  unsigned char flags[4];	/* Section flags/attributes.  */
143  unsigned char reserved1[4];
144  unsigned char reserved2[4];
145};
146
147/* 64-bit section header.  */
148
149struct mach_o_section_64
150{
151  unsigned char sectname[16];	/* Section name.  */
152  unsigned char segname[16];	/* Segment that the section belongs to.  */
153  unsigned char addr[8];	/* Address of this section in memory.  */
154  unsigned char size[8];	/* Size in bytes of this section.  */
155  unsigned char offset[4];	/* File offset of this section.  */
156  unsigned char align[4];	/* log2 of this section's alignment.  */
157  unsigned char reloff[4];	/* File offset of this section's relocs.  */
158  unsigned char nreloc[4];	/* Number of relocs for this section.  */
159  unsigned char flags[4];	/* Section flags/attributes.  */
160  unsigned char reserved1[4];
161  unsigned char reserved2[4];
162  unsigned char reserved3[4];
163};
164
165/* Flags for Mach-O sections.  */
166
167#define MACH_O_S_ATTR_DEBUG			0x02000000
168
169/* The length of a segment or section name.  */
170
171#define MACH_O_NAME_LEN (16)
172
173/* A GNU specific extension for long section names.  */
174
175#define GNU_SECTION_NAMES "__section_names"
176
177/* Private data for an simple_object_read.  */
178
179struct simple_object_mach_o_read
180{
181  /* User specified segment name.  */
182  char *segment_name;
183  /* Magic number.  */
184  unsigned int magic;
185  /* Whether this file is big-endian.  */
186  int is_big_endian;
187  /* CPU type from header.  */
188  unsigned int cputype;
189  /* CPU subtype from header.  */
190  unsigned int cpusubtype;
191  /* Number of commands, from header.  */
192  unsigned int ncmds;
193  /* Flags from header.  */
194  unsigned int flags;
195  /* Reserved field from header, only used on 64-bit.  */
196  unsigned int reserved;
197};
198
199/* Private data for an simple_object_attributes.  */
200
201struct simple_object_mach_o_attributes
202{
203  /* Magic number.  */
204  unsigned int magic;
205  /* Whether this file is big-endian.  */
206  int is_big_endian;
207  /* CPU type from header.  */
208  unsigned int cputype;
209  /* CPU subtype from header.  */
210  unsigned int cpusubtype;
211  /* Flags from header.  */
212  unsigned int flags;
213  /* Reserved field from header, only used on 64-bit.  */
214  unsigned int reserved;
215};
216
217/* See if we have a Mach-O file.  */
218
219static void *
220simple_object_mach_o_match (
221    unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
222    int descriptor,
223    off_t offset,
224    const char *segment_name,
225    const char **errmsg,
226    int *err)
227{
228  unsigned int magic;
229  int is_big_endian;
230  unsigned int (*fetch_32) (const unsigned char *);
231  unsigned int filetype;
232  struct simple_object_mach_o_read *omr;
233  unsigned char buf[sizeof (struct mach_o_header_64)];
234  unsigned char *b;
235
236  magic = simple_object_fetch_big_32 (header);
237  if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64)
238    is_big_endian = 1;
239  else
240    {
241      magic = simple_object_fetch_little_32 (header);
242      if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64)
243	is_big_endian = 0;
244      else
245	{
246	  *errmsg = NULL;
247	  *err = 0;
248	  return NULL;
249	}
250    }
251
252#ifndef UNSIGNED_64BIT_TYPE
253  if (magic == MACH_O_MH_MAGIC_64)
254    {
255      *errmsg = "64-bit Mach-O objects not supported";
256      *err = 0;
257      return NULL;
258    }
259#endif
260
261  /* We require the user to provide a segment name.  This is
262     unfortunate but I don't see any good choices here.  */
263
264  if (segment_name == NULL)
265    {
266      *errmsg = "Mach-O file found but no segment name specified";
267      *err = 0;
268      return NULL;
269    }
270
271  if (strlen (segment_name) > MACH_O_NAME_LEN)
272    {
273      *errmsg = "Mach-O segment name too long";
274      *err = 0;
275      return NULL;
276    }
277
278  /* The 32-bit and 64-bit headers are similar enough that we can use
279     the same code.  */
280
281  fetch_32 = (is_big_endian
282	      ? simple_object_fetch_big_32
283	      : simple_object_fetch_little_32);
284
285  if (!simple_object_internal_read (descriptor, offset, buf,
286				    (magic == MACH_O_MH_MAGIC
287				     ? sizeof (struct mach_o_header_32)
288				     : sizeof (struct mach_o_header_64)),
289				    errmsg, err))
290    return NULL;
291
292  b = &buf[0];
293
294  filetype = (*fetch_32) (b + offsetof (struct mach_o_header_32, filetype));
295  if (filetype != MACH_O_MH_OBJECT)
296    {
297      *errmsg = "Mach-O file is not object file";
298      *err = 0;
299      return NULL;
300    }
301
302  omr = XNEW (struct simple_object_mach_o_read);
303  omr->segment_name = xstrdup (segment_name);
304  omr->magic = magic;
305  omr->is_big_endian = is_big_endian;
306  omr->cputype = (*fetch_32) (b + offsetof (struct mach_o_header_32, cputype));
307  omr->cpusubtype = (*fetch_32) (b
308				 + offsetof (struct mach_o_header_32,
309					     cpusubtype));
310  omr->ncmds = (*fetch_32) (b + offsetof (struct mach_o_header_32, ncmds));
311  omr->flags = (*fetch_32) (b + offsetof (struct mach_o_header_32, flags));
312  if (magic == MACH_O_MH_MAGIC)
313    omr->reserved = 0;
314  else
315    omr->reserved = (*fetch_32) (b
316				 + offsetof (struct mach_o_header_64,
317					     reserved));
318
319  return (void *) omr;
320}
321
322/* Get the file offset and size from a section header.  */
323
324static void
325simple_object_mach_o_section_info (int is_big_endian, int is_32,
326				   const unsigned char *sechdr, off_t *offset,
327				   size_t *size)
328{
329  unsigned int (*fetch_32) (const unsigned char *);
330  ulong_type (*fetch_64) (const unsigned char *);
331
332  fetch_32 = (is_big_endian
333	      ? simple_object_fetch_big_32
334	      : simple_object_fetch_little_32);
335
336  fetch_64 = NULL;
337#ifdef UNSIGNED_64BIT_TYPE
338  fetch_64 = (is_big_endian
339	      ? simple_object_fetch_big_64
340	      : simple_object_fetch_little_64);
341#endif
342
343  if (is_32)
344    {
345      *offset = fetch_32 (sechdr
346			  + offsetof (struct mach_o_section_32, offset));
347      *size = fetch_32 (sechdr
348			+ offsetof (struct mach_o_section_32, size));
349    }
350  else
351    {
352      *offset = fetch_32 (sechdr
353			  + offsetof (struct mach_o_section_64, offset));
354      *size = fetch_64 (sechdr
355			+ offsetof (struct mach_o_section_64, size));
356    }
357}
358
359/* Handle a segment in a Mach-O file.  Return 1 if we should continue,
360   0 if the caller should return.  */
361
362static int
363simple_object_mach_o_segment (simple_object_read *sobj, off_t offset,
364			      const unsigned char *segbuf,
365			      int (*pfn) (void *, const char *, off_t offset,
366					  off_t length),
367			      void *data,
368			      const char **errmsg, int *err)
369{
370  struct simple_object_mach_o_read *omr =
371    (struct simple_object_mach_o_read *) sobj->data;
372  unsigned int (*fetch_32) (const unsigned char *);
373  int is_32;
374  size_t seghdrsize;
375  size_t sechdrsize;
376  size_t segname_offset;
377  size_t sectname_offset;
378  unsigned int nsects;
379  unsigned char *secdata;
380  unsigned int i;
381  unsigned int strtab_index;
382  char *strtab;
383  size_t strtab_size;
384
385  fetch_32 = (omr->is_big_endian
386	      ? simple_object_fetch_big_32
387	      : simple_object_fetch_little_32);
388
389  is_32 = omr->magic == MACH_O_MH_MAGIC;
390
391  if (is_32)
392    {
393      seghdrsize = sizeof (struct mach_o_segment_command_32);
394      sechdrsize = sizeof (struct mach_o_section_32);
395      segname_offset = offsetof (struct mach_o_section_32, segname);
396      sectname_offset = offsetof (struct mach_o_section_32, sectname);
397      nsects = (*fetch_32) (segbuf
398			    + offsetof (struct mach_o_segment_command_32,
399					nsects));
400    }
401  else
402    {
403      seghdrsize = sizeof (struct mach_o_segment_command_64);
404      sechdrsize = sizeof (struct mach_o_section_64);
405      segname_offset = offsetof (struct mach_o_section_64, segname);
406      sectname_offset = offsetof (struct mach_o_section_64, sectname);
407      nsects = (*fetch_32) (segbuf
408			    + offsetof (struct mach_o_segment_command_64,
409					nsects));
410    }
411
412  secdata = XNEWVEC (unsigned char, nsects * sechdrsize);
413  if (!simple_object_internal_read (sobj->descriptor, offset + seghdrsize,
414				    secdata, nsects * sechdrsize, errmsg, err))
415    {
416      XDELETEVEC (secdata);
417      return 0;
418    }
419
420  /* Scan for a __section_names section.  This is in effect a GNU
421     extension that permits section names longer than 16 chars.  */
422
423  for (i = 0; i < nsects; ++i)
424    {
425      size_t nameoff;
426
427      nameoff = i * sechdrsize + segname_offset;
428      if (strcmp ((char *) secdata + nameoff, omr->segment_name) != 0)
429	continue;
430      nameoff = i * sechdrsize + sectname_offset;
431      if (strcmp ((char *) secdata + nameoff, GNU_SECTION_NAMES) == 0)
432	break;
433    }
434
435  strtab_index = i;
436  if (strtab_index >= nsects)
437    {
438      strtab = NULL;
439      strtab_size = 0;
440    }
441  else
442    {
443      off_t strtab_offset;
444
445      simple_object_mach_o_section_info (omr->is_big_endian, is_32,
446					 secdata + strtab_index * sechdrsize,
447					 &strtab_offset, &strtab_size);
448      strtab = XNEWVEC (char, strtab_size);
449      if (!simple_object_internal_read (sobj->descriptor,
450					sobj->offset + strtab_offset,
451					(unsigned char *) strtab, strtab_size,
452					errmsg, err))
453	{
454	  XDELETEVEC (strtab);
455	  XDELETEVEC (secdata);
456	  return 0;
457	}
458    }
459
460  /* Process the sections.  */
461
462  for (i = 0; i < nsects; ++i)
463    {
464      const unsigned char *sechdr;
465      char namebuf[MACH_O_NAME_LEN + 1];
466      char *name;
467      off_t secoffset;
468      size_t secsize;
469
470      if (i == strtab_index)
471	continue;
472
473      sechdr = secdata + i * sechdrsize;
474
475      if (strcmp ((char *) sechdr + segname_offset, omr->segment_name) != 0)
476	continue;
477
478      memcpy (namebuf, sechdr + sectname_offset, MACH_O_NAME_LEN);
479      namebuf[MACH_O_NAME_LEN] = '\0';
480
481      name = &namebuf[0];
482      if (strtab != NULL && name[0] == '_' && name[1] == '_')
483	{
484	  unsigned long stringoffset;
485
486	  if (sscanf (name + 2, "%08lX", &stringoffset) == 1)
487	    {
488	      if (stringoffset >= strtab_size)
489		{
490		  *errmsg = "section name offset out of range";
491		  *err = 0;
492		  XDELETEVEC (strtab);
493		  XDELETEVEC (secdata);
494		  return 0;
495		}
496
497	      name = strtab + stringoffset;
498	    }
499	}
500
501      simple_object_mach_o_section_info (omr->is_big_endian, is_32, sechdr,
502					 &secoffset, &secsize);
503
504      if (!(*pfn) (data, name, secoffset, secsize))
505	{
506	  *errmsg = NULL;
507	  *err = 0;
508	  XDELETEVEC (strtab);
509	  XDELETEVEC (secdata);
510	  return 0;
511	}
512    }
513
514  XDELETEVEC (strtab);
515  XDELETEVEC (secdata);
516
517  return 1;
518}
519
520/* Find all sections in a Mach-O file.  */
521
522static const char *
523simple_object_mach_o_find_sections (simple_object_read *sobj,
524				    int (*pfn) (void *, const char *,
525						off_t offset, off_t length),
526				    void *data,
527				    int *err)
528{
529  struct simple_object_mach_o_read *omr =
530    (struct simple_object_mach_o_read *) sobj->data;
531  off_t offset;
532  size_t seghdrsize;
533  unsigned int (*fetch_32) (const unsigned char *);
534  const char *errmsg;
535  unsigned int i;
536
537  if (omr->magic == MACH_O_MH_MAGIC)
538    {
539      offset = sizeof (struct mach_o_header_32);
540      seghdrsize = sizeof (struct mach_o_segment_command_32);
541    }
542  else
543    {
544      offset = sizeof (struct mach_o_header_64);
545      seghdrsize = sizeof (struct mach_o_segment_command_64);
546    }
547
548  fetch_32 = (omr->is_big_endian
549	      ? simple_object_fetch_big_32
550	      : simple_object_fetch_little_32);
551
552  for (i = 0; i < omr->ncmds; ++i)
553    {
554      unsigned char loadbuf[sizeof (struct mach_o_load_command)];
555      unsigned int cmd;
556      unsigned int cmdsize;
557
558      if (!simple_object_internal_read (sobj->descriptor,
559					sobj->offset + offset,
560					loadbuf,
561					sizeof (struct mach_o_load_command),
562					&errmsg, err))
563	return errmsg;
564
565      cmd = (*fetch_32) (loadbuf + offsetof (struct mach_o_load_command, cmd));
566      cmdsize = (*fetch_32) (loadbuf
567			     + offsetof (struct mach_o_load_command, cmdsize));
568
569      if (cmd == MACH_O_LC_SEGMENT || cmd == MACH_O_LC_SEGMENT_64)
570	{
571	  unsigned char segbuf[sizeof (struct mach_o_segment_command_64)];
572	  int r;
573
574	  if (!simple_object_internal_read (sobj->descriptor,
575					    sobj->offset + offset,
576					    segbuf, seghdrsize, &errmsg, err))
577	    return errmsg;
578
579	  r = simple_object_mach_o_segment (sobj, offset, segbuf, pfn,
580					    data, &errmsg, err);
581	  if (!r)
582	    return errmsg;
583	}
584
585      offset += cmdsize;
586    }
587
588  return NULL;
589}
590
591/* Fetch the attributes for an simple_object_read.  */
592
593static void *
594simple_object_mach_o_fetch_attributes (simple_object_read *sobj,
595				       const char **errmsg ATTRIBUTE_UNUSED,
596				       int *err ATTRIBUTE_UNUSED)
597{
598  struct simple_object_mach_o_read *omr =
599    (struct simple_object_mach_o_read *) sobj->data;
600  struct simple_object_mach_o_attributes *ret;
601
602  ret = XNEW (struct simple_object_mach_o_attributes);
603  ret->magic = omr->magic;
604  ret->is_big_endian = omr->is_big_endian;
605  ret->cputype = omr->cputype;
606  ret->cpusubtype = omr->cpusubtype;
607  ret->flags = omr->flags;
608  ret->reserved = omr->reserved;
609  return ret;
610}
611
612/* Release the private data for an simple_object_read.  */
613
614static void
615simple_object_mach_o_release_read (void *data)
616{
617  struct simple_object_mach_o_read *omr =
618    (struct simple_object_mach_o_read *) data;
619
620  free (omr->segment_name);
621  XDELETE (omr);
622}
623
624/* Compare two attributes structures.  */
625
626static const char *
627simple_object_mach_o_attributes_merge (void *todata, void *fromdata, int *err)
628{
629  struct simple_object_mach_o_attributes *to =
630    (struct simple_object_mach_o_attributes *) todata;
631  struct simple_object_mach_o_attributes *from =
632    (struct simple_object_mach_o_attributes *) fromdata;
633
634  if (to->magic != from->magic
635      || to->is_big_endian != from->is_big_endian
636      || to->cputype != from->cputype)
637    {
638      *err = 0;
639      return "Mach-O object format mismatch";
640    }
641  return NULL;
642}
643
644/* Release the private data for an attributes structure.  */
645
646static void
647simple_object_mach_o_release_attributes (void *data)
648{
649  XDELETE (data);
650}
651
652/* Prepare to write out a file.  */
653
654static void *
655simple_object_mach_o_start_write (void *attributes_data,
656				  const char **errmsg ATTRIBUTE_UNUSED,
657				  int *err ATTRIBUTE_UNUSED)
658{
659  struct simple_object_mach_o_attributes *attrs =
660    (struct simple_object_mach_o_attributes *) attributes_data;
661  struct simple_object_mach_o_attributes *ret;
662
663  /* We're just going to record the attributes, but we need to make a
664     copy because the user may delete them.  */
665  ret = XNEW (struct simple_object_mach_o_attributes);
666  *ret = *attrs;
667  return ret;
668}
669
670/* Write out the header of a Mach-O file.  */
671
672static int
673simple_object_mach_o_write_header (simple_object_write *sobj, int descriptor,
674				   size_t nsects, const char **errmsg,
675				   int *err)
676{
677  struct simple_object_mach_o_attributes *attrs =
678    (struct simple_object_mach_o_attributes *) sobj->data;
679  void (*set_32) (unsigned char *, unsigned int);
680  unsigned char hdrbuf[sizeof (struct mach_o_header_64)];
681  unsigned char *hdr;
682  size_t wrsize;
683
684  set_32 = (attrs->is_big_endian
685	    ? simple_object_set_big_32
686	    : simple_object_set_little_32);
687
688  memset (hdrbuf, 0, sizeof hdrbuf);
689
690  /* The 32-bit and 64-bit headers start out the same.  */
691
692  hdr = &hdrbuf[0];
693  set_32 (hdr + offsetof (struct mach_o_header_32, magic), attrs->magic);
694  set_32 (hdr + offsetof (struct mach_o_header_32, cputype), attrs->cputype);
695  set_32 (hdr + offsetof (struct mach_o_header_32, cpusubtype),
696	  attrs->cpusubtype);
697  set_32 (hdr + offsetof (struct mach_o_header_32, filetype), MACH_O_MH_OBJECT);
698  set_32 (hdr + offsetof (struct mach_o_header_32, ncmds), 1);
699  set_32 (hdr + offsetof (struct mach_o_header_32, flags), attrs->flags);
700  if (attrs->magic == MACH_O_MH_MAGIC)
701    {
702      wrsize = sizeof (struct mach_o_header_32);
703      set_32 (hdr + offsetof (struct mach_o_header_32, sizeofcmds),
704	      (sizeof (struct mach_o_segment_command_32)
705	       + nsects * sizeof (struct mach_o_section_32)));
706    }
707  else
708    {
709      set_32 (hdr + offsetof (struct mach_o_header_64, sizeofcmds),
710	      (sizeof (struct mach_o_segment_command_64)
711	       + nsects * sizeof (struct mach_o_section_64)));
712      set_32 (hdr + offsetof (struct mach_o_header_64, reserved),
713	      attrs->reserved);
714      wrsize = sizeof (struct mach_o_header_64);
715    }
716
717  return simple_object_internal_write (descriptor, 0, hdrbuf, wrsize,
718				       errmsg, err);
719}
720
721/* Write a Mach-O section header.  */
722
723static int
724simple_object_mach_o_write_section_header (simple_object_write *sobj,
725					   int descriptor,
726					   size_t sechdr_offset,
727					   const char *name, size_t secaddr,
728					   size_t secsize, size_t offset,
729					   unsigned int align,
730					   const char **errmsg, int *err)
731{
732  struct simple_object_mach_o_attributes *attrs =
733    (struct simple_object_mach_o_attributes *) sobj->data;
734  void (*set_32) (unsigned char *, unsigned int);
735  unsigned char hdrbuf[sizeof (struct mach_o_section_64)];
736  unsigned char *hdr;
737  size_t sechdrsize;
738
739  set_32 = (attrs->is_big_endian
740	    ? simple_object_set_big_32
741	    : simple_object_set_little_32);
742
743  memset (hdrbuf, 0, sizeof hdrbuf);
744
745  hdr = &hdrbuf[0];
746  if (attrs->magic == MACH_O_MH_MAGIC)
747    {
748      strncpy ((char *) hdr + offsetof (struct mach_o_section_32, sectname),
749	       name, MACH_O_NAME_LEN);
750      strncpy ((char *) hdr + offsetof (struct mach_o_section_32, segname),
751	       sobj->segment_name, MACH_O_NAME_LEN);
752      set_32 (hdr + offsetof (struct mach_o_section_32, addr), secaddr);
753      set_32 (hdr + offsetof (struct mach_o_section_32, size), secsize);
754      set_32 (hdr + offsetof (struct mach_o_section_32, offset), offset);
755      set_32 (hdr + offsetof (struct mach_o_section_32, align), align);
756      /* reloff left as zero.  */
757      /* nreloc left as zero.  */
758      set_32 (hdr + offsetof (struct mach_o_section_32, flags),
759	      MACH_O_S_ATTR_DEBUG);
760      /* reserved1 left as zero.  */
761      /* reserved2 left as zero.  */
762      sechdrsize = sizeof (struct mach_o_section_32);
763    }
764  else
765    {
766#ifdef UNSIGNED_64BIT_TYPE
767      void (*set_64) (unsigned char *, ulong_type);
768
769      set_64 = (attrs->is_big_endian
770		? simple_object_set_big_64
771		: simple_object_set_little_64);
772
773      strncpy ((char *) hdr + offsetof (struct mach_o_section_64, sectname),
774	       name, MACH_O_NAME_LEN);
775      strncpy ((char *) hdr + offsetof (struct mach_o_section_64, segname),
776	       sobj->segment_name, MACH_O_NAME_LEN);
777      set_64 (hdr + offsetof (struct mach_o_section_64, addr), secaddr);
778      set_64 (hdr + offsetof (struct mach_o_section_64, size), secsize);
779      set_32 (hdr + offsetof (struct mach_o_section_64, offset), offset);
780      set_32 (hdr + offsetof (struct mach_o_section_64, align), align);
781      /* reloff left as zero.  */
782      /* nreloc left as zero.  */
783      set_32 (hdr + offsetof (struct mach_o_section_64, flags),
784	      MACH_O_S_ATTR_DEBUG);
785      /* reserved1 left as zero.  */
786      /* reserved2 left as zero.  */
787      /* reserved3 left as zero.  */
788#endif
789      sechdrsize = sizeof (struct mach_o_section_64);
790    }
791
792  return simple_object_internal_write (descriptor, sechdr_offset, hdr,
793				       sechdrsize, errmsg, err);
794}
795
796/* Write out the single segment and the sections of a Mach-O file.  */
797
798static int
799simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor,
800				    size_t nsects, const char **errmsg,
801				    int *err)
802{
803  struct simple_object_mach_o_attributes *attrs =
804    (struct simple_object_mach_o_attributes *) sobj->data;
805  void (*set_32) (unsigned char *, unsigned int);
806  size_t hdrsize;
807  size_t seghdrsize;
808  size_t sechdrsize;
809  size_t cmdsize;
810  size_t offset;
811  size_t sechdr_offset;
812  size_t secaddr;
813  unsigned int name_offset;
814  simple_object_write_section *section;
815  unsigned char hdrbuf[sizeof (struct mach_o_segment_command_64)];
816  unsigned char *hdr;
817
818  set_32 = (attrs->is_big_endian
819	    ? simple_object_set_big_32
820	    : simple_object_set_little_32);
821
822  /* Write out the sections first.  */
823
824  if (attrs->magic == MACH_O_MH_MAGIC)
825    {
826      hdrsize = sizeof (struct mach_o_header_32);
827      seghdrsize = sizeof (struct mach_o_segment_command_32);
828      sechdrsize = sizeof (struct mach_o_section_32);
829    }
830  else
831    {
832      hdrsize = sizeof (struct mach_o_header_64);
833      seghdrsize = sizeof (struct mach_o_segment_command_64);
834      sechdrsize = sizeof (struct mach_o_section_64);
835    }
836
837  sechdr_offset = hdrsize + seghdrsize;
838  cmdsize = seghdrsize + nsects * sechdrsize;
839  offset = hdrsize + cmdsize;
840  name_offset = 0;
841  secaddr = 0;
842
843  for (section = sobj->sections; section != NULL; section = section->next)
844    {
845      size_t mask;
846      size_t new_offset;
847      size_t secsize;
848      struct simple_object_write_section_buffer *buffer;
849      char namebuf[MACH_O_NAME_LEN + 1];
850
851      mask = (1U << section->align) - 1;
852      new_offset = offset + mask;
853      new_offset &= ~ mask;
854      while (new_offset > offset)
855	{
856	  unsigned char zeroes[16];
857	  size_t write;
858
859	  memset (zeroes, 0, sizeof zeroes);
860	  write = new_offset - offset;
861	  if (write > sizeof zeroes)
862	    write = sizeof zeroes;
863	  if (!simple_object_internal_write (descriptor, offset, zeroes, write,
864					     errmsg, err))
865	    return 0;
866	  offset += write;
867	}
868
869      secsize = 0;
870      for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
871	{
872	  if (!simple_object_internal_write (descriptor, offset + secsize,
873					     ((const unsigned char *)
874					      buffer->buffer),
875					     buffer->size, errmsg, err))
876	    return 0;
877	  secsize += buffer->size;
878	}
879
880      snprintf (namebuf, sizeof namebuf, "__%08X", name_offset);
881      if (!simple_object_mach_o_write_section_header (sobj, descriptor,
882						      sechdr_offset, namebuf,
883						      secaddr, secsize, offset,
884						      section->align,
885						      errmsg, err))
886	return 0;
887
888      sechdr_offset += sechdrsize;
889      offset += secsize;
890      name_offset += strlen (section->name) + 1;
891      secaddr += secsize;
892    }
893
894  /* Write out the section names.  */
895
896  if (!simple_object_mach_o_write_section_header (sobj, descriptor,
897						  sechdr_offset,
898						  GNU_SECTION_NAMES, secaddr,
899						  name_offset, offset, 0,
900						  errmsg, err))
901    return 0;
902
903  for (section = sobj->sections; section != NULL; section = section->next)
904    {
905      size_t namelen;
906
907      namelen = strlen (section->name) + 1;
908      if (!simple_object_internal_write (descriptor, offset,
909					 (const unsigned char *) section->name,
910					 namelen, errmsg, err))
911	return 0;
912      offset += namelen;
913    }
914
915  /* Write out the segment header.  */
916
917  memset (hdrbuf, 0, sizeof hdrbuf);
918
919  hdr = &hdrbuf[0];
920  if (attrs->magic == MACH_O_MH_MAGIC)
921    {
922      set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmd),
923	      MACH_O_LC_SEGMENT);
924      set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmdsize),
925	      cmdsize);
926      strncpy (((char *) hdr
927		+ offsetof (struct mach_o_segment_command_32, segname)),
928	       sobj->segment_name, MACH_O_NAME_LEN);
929      /* vmaddr left as zero.  */
930      /* vmsize left as zero.  */
931      set_32 (hdr + offsetof (struct mach_o_segment_command_32, fileoff),
932	      hdrsize + cmdsize);
933      set_32 (hdr + offsetof (struct mach_o_segment_command_32, filesize),
934	      offset - (hdrsize + cmdsize));
935      /* maxprot left as zero.  */
936      /* initprot left as zero.  */
937      set_32 (hdr + offsetof (struct mach_o_segment_command_32, nsects),
938	      nsects);
939      /* flags left as zero.  */
940    }
941  else
942    {
943#ifdef UNSIGNED_64BIT_TYPE
944      void (*set_64) (unsigned char *, ulong_type);
945
946      set_64 = (attrs->is_big_endian
947		? simple_object_set_big_64
948		: simple_object_set_little_64);
949
950      set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmd),
951	      MACH_O_LC_SEGMENT);
952      set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmdsize),
953	      cmdsize);
954      strncpy (((char *) hdr
955		+ offsetof (struct mach_o_segment_command_64, segname)),
956	       sobj->segment_name, MACH_O_NAME_LEN);
957      /* vmaddr left as zero.  */
958      /* vmsize left as zero.  */
959      set_64 (hdr + offsetof (struct mach_o_segment_command_64, fileoff),
960	      hdrsize + cmdsize);
961      set_64 (hdr + offsetof (struct mach_o_segment_command_64, filesize),
962	      offset - (hdrsize + cmdsize));
963      /* maxprot left as zero.  */
964      /* initprot left as zero.  */
965      set_32 (hdr + offsetof (struct mach_o_segment_command_64, nsects),
966	      nsects);
967      /* flags left as zero.  */
968#endif
969    }
970
971  return simple_object_internal_write (descriptor, hdrsize, hdr, seghdrsize,
972				       errmsg, err);
973}
974
975/* Write out a complete Mach-O file.  */
976
977static const char *
978simple_object_mach_o_write_to_file (simple_object_write *sobj, int descriptor,
979				    int *err)
980{
981  size_t nsects;
982  simple_object_write_section *section;
983  const char *errmsg;
984
985  /* Start at 1 for symbol_names section.  */
986  nsects = 1;
987  for (section = sobj->sections; section != NULL; section = section->next)
988    ++nsects;
989
990  if (!simple_object_mach_o_write_header (sobj, descriptor, nsects,
991					  &errmsg, err))
992    return errmsg;
993
994  if (!simple_object_mach_o_write_segment (sobj, descriptor, nsects,
995					   &errmsg, err))
996    return errmsg;
997
998  return NULL;
999}
1000
1001/* Release the private data for an simple_object_write structure.  */
1002
1003static void
1004simple_object_mach_o_release_write (void *data)
1005{
1006  XDELETE (data);
1007}
1008
1009/* The Mach-O functions.  */
1010
1011const struct simple_object_functions simple_object_mach_o_functions =
1012{
1013  simple_object_mach_o_match,
1014  simple_object_mach_o_find_sections,
1015  simple_object_mach_o_fetch_attributes,
1016  simple_object_mach_o_release_read,
1017  simple_object_mach_o_attributes_merge,
1018  simple_object_mach_o_release_attributes,
1019  simple_object_mach_o_start_write,
1020  simple_object_mach_o_write_to_file,
1021  simple_object_mach_o_release_write
1022};
1023