1/* simple-object-mach-o.c -- routines to manipulate Mach-O object files.
2 Copyright (C) 2010-2026 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/* A GNU-specific extension to wrap multiple sections using three
178 mach-o sections within a given segment. The section '__wrapper_sects'
179 is subdivided according to the index '__wrapper_index' and each sub
180 sect is named according to the names supplied in '__wrapper_names'. */
181
182#define GNU_WRAPPER_SECTS "__wrapper_sects"
183#define GNU_WRAPPER_INDEX "__wrapper_index"
184#define GNU_WRAPPER_NAMES "__wrapper_names"
185
186/* Private data for an simple_object_read. */
187
188struct simple_object_mach_o_read
189{
190 /* User specified segment name. */
191 char *segment_name;
192 /* Magic number. */
193 unsigned int magic;
194 /* Whether this file is big-endian. */
195 int is_big_endian;
196 /* CPU type from header. */
197 unsigned int cputype;
198 /* CPU subtype from header. */
199 unsigned int cpusubtype;
200 /* Number of commands, from header. */
201 unsigned int ncmds;
202 /* Flags from header. */
203 unsigned int flags;
204 /* Reserved field from header, only used on 64-bit. */
205 unsigned int reserved;
206};
207
208/* Private data for an simple_object_attributes. */
209
210struct simple_object_mach_o_attributes
211{
212 /* Magic number. */
213 unsigned int magic;
214 /* Whether this file is big-endian. */
215 int is_big_endian;
216 /* CPU type from header. */
217 unsigned int cputype;
218 /* CPU subtype from header. */
219 unsigned int cpusubtype;
220 /* Flags from header. */
221 unsigned int flags;
222 /* Reserved field from header, only used on 64-bit. */
223 unsigned int reserved;
224};
225
226/* See if we have a Mach-O MH_OBJECT file:
227
228 A standard MH_OBJECT (from as) will have three load commands:
229 0 - LC_SEGMENT/LC_SEGMENT64
230 1 - LC_SYMTAB
231 2 - LC_DYSYMTAB
232
233 The LC_SEGMENT/LC_SEGMENT64 will introduce a single anonymous segment
234 containing all the sections.
235
236 Files written by simple-object will have only the segment command
237 (no symbol tables). */
238
239static void *
240simple_object_mach_o_match (
241 unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
242 int descriptor,
243 off_t offset,
244 const char *segment_name,
245 const char **errmsg,
246 int *err)
247{
248 unsigned int magic;
249 int is_big_endian;
250 unsigned int (*fetch_32) (const unsigned char *);
251 unsigned int filetype;
252 struct simple_object_mach_o_read *omr;
253 unsigned char buf[sizeof (struct mach_o_header_64)];
254 unsigned char *b;
255
256 magic = simple_object_fetch_big_32 (buf: header);
257 if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64)
258 is_big_endian = 1;
259 else
260 {
261 magic = simple_object_fetch_little_32 (buf: header);
262 if (magic == MACH_O_MH_MAGIC || magic == MACH_O_MH_MAGIC_64)
263 is_big_endian = 0;
264 else
265 {
266 *errmsg = NULL;
267 *err = 0;
268 return NULL;
269 }
270 }
271
272#ifndef UNSIGNED_64BIT_TYPE
273 if (magic == MACH_O_MH_MAGIC_64)
274 {
275 *errmsg = "64-bit Mach-O objects not supported";
276 *err = 0;
277 return NULL;
278 }
279#endif
280
281 /* We require the user to provide a segment name. This is
282 unfortunate but I don't see any good choices here. */
283
284 if (segment_name == NULL)
285 {
286 *errmsg = "Mach-O file found but no segment name specified";
287 *err = 0;
288 return NULL;
289 }
290
291 if (strlen (s: segment_name) > MACH_O_NAME_LEN)
292 {
293 *errmsg = "Mach-O segment name too long";
294 *err = 0;
295 return NULL;
296 }
297
298 /* The 32-bit and 64-bit headers are similar enough that we can use
299 the same code. */
300
301 fetch_32 = (is_big_endian
302 ? simple_object_fetch_big_32
303 : simple_object_fetch_little_32);
304
305 if (!simple_object_internal_read (descriptor, offset, buffer: buf,
306 size: (magic == MACH_O_MH_MAGIC
307 ? sizeof (struct mach_o_header_32)
308 : sizeof (struct mach_o_header_64)),
309 errmsg, err))
310 return NULL;
311
312 b = &buf[0];
313
314 filetype = (*fetch_32) (b + offsetof (struct mach_o_header_32, filetype));
315 if (filetype != MACH_O_MH_OBJECT)
316 {
317 *errmsg = "Mach-O file is not object file";
318 *err = 0;
319 return NULL;
320 }
321
322 omr = XNEW (struct simple_object_mach_o_read);
323 omr->segment_name = xstrdup (segment_name);
324 omr->magic = magic;
325 omr->is_big_endian = is_big_endian;
326 omr->cputype = (*fetch_32) (b + offsetof (struct mach_o_header_32, cputype));
327 omr->cpusubtype = (*fetch_32) (b
328 + offsetof (struct mach_o_header_32,
329 cpusubtype));
330 omr->ncmds = (*fetch_32) (b + offsetof (struct mach_o_header_32, ncmds));
331 omr->flags = (*fetch_32) (b + offsetof (struct mach_o_header_32, flags));
332 if (magic == MACH_O_MH_MAGIC)
333 omr->reserved = 0;
334 else
335 omr->reserved = (*fetch_32) (b
336 + offsetof (struct mach_o_header_64,
337 reserved));
338
339 return (void *) omr;
340}
341
342/* Get the file offset and size from a section header. */
343
344static void
345simple_object_mach_o_section_info (int is_big_endian, int is_32,
346 const unsigned char *sechdr, off_t *offset,
347 size_t *size)
348{
349 unsigned int (*fetch_32) (const unsigned char *);
350 ulong_type (*fetch_64) (const unsigned char *);
351
352 fetch_32 = (is_big_endian
353 ? simple_object_fetch_big_32
354 : simple_object_fetch_little_32);
355
356 fetch_64 = NULL;
357#ifdef UNSIGNED_64BIT_TYPE
358 fetch_64 = (is_big_endian
359 ? simple_object_fetch_big_64
360 : simple_object_fetch_little_64);
361#endif
362
363 if (is_32)
364 {
365 *offset = fetch_32 (sechdr
366 + offsetof (struct mach_o_section_32, offset));
367 *size = fetch_32 (sechdr
368 + offsetof (struct mach_o_section_32, size));
369 }
370 else
371 {
372 *offset = fetch_32 (sechdr
373 + offsetof (struct mach_o_section_64, offset));
374 *size = fetch_64 (sechdr
375 + offsetof (struct mach_o_section_64, size));
376 }
377}
378
379/* Handle a segment in a Mach-O Object file.
380
381 This will callback to the function pfn for each "section found" the meaning
382 of which depends on gnu extensions to mach-o:
383
384 If we find mach-o sections (with the segment name as specified) which also
385 contain: a 'sects' wrapper, an index, and a name table, we expand this into
386 as many sections as are specified in the index. In this case, there will
387 be a callback for each of these.
388
389 We will also allow an extension that permits long names (more than 16
390 characters) to be used with mach-o. In this case, the section name has
391 a specific format embedding an index into a name table, and the file must
392 contain such name table.
393
394 Return 1 if we should continue, 0 if the caller should return. */
395
396#define SOMO_SECTS_PRESENT 0x01
397#define SOMO_INDEX_PRESENT 0x02
398#define SOMO_NAMES_PRESENT 0x04
399#define SOMO_LONGN_PRESENT 0x08
400#define SOMO_WRAPPING (SOMO_SECTS_PRESENT | SOMO_INDEX_PRESENT \
401 | SOMO_NAMES_PRESENT)
402
403static int
404simple_object_mach_o_segment (simple_object_read *sobj, off_t offset,
405 const unsigned char *segbuf,
406 int (*pfn) (void *, const char *, off_t offset,
407 off_t length),
408 void *data,
409 const char **errmsg, int *err)
410{
411 struct simple_object_mach_o_read *omr =
412 (struct simple_object_mach_o_read *) sobj->data;
413 unsigned int (*fetch_32) (const unsigned char *);
414 int is_32;
415 size_t seghdrsize;
416 size_t sechdrsize;
417 size_t segname_offset;
418 size_t sectname_offset;
419 unsigned int nsects;
420 unsigned char *secdata;
421 unsigned int i;
422 unsigned int gnu_sections_found;
423 unsigned int strtab_index;
424 unsigned int index_index;
425 unsigned int nametab_index;
426 unsigned int sections_index;
427 char *strtab;
428 char *nametab;
429 unsigned char *index;
430 size_t strtab_size;
431 size_t nametab_size;
432 size_t index_size;
433 unsigned int n_wrapped_sects;
434 size_t wrapper_sect_size;
435 off_t wrapper_sect_offset = 0;
436
437 fetch_32 = (omr->is_big_endian
438 ? simple_object_fetch_big_32
439 : simple_object_fetch_little_32);
440
441 is_32 = omr->magic == MACH_O_MH_MAGIC;
442
443 if (is_32)
444 {
445 seghdrsize = sizeof (struct mach_o_segment_command_32);
446 sechdrsize = sizeof (struct mach_o_section_32);
447 segname_offset = offsetof (struct mach_o_section_32, segname);
448 sectname_offset = offsetof (struct mach_o_section_32, sectname);
449 nsects = (*fetch_32) (segbuf
450 + offsetof (struct mach_o_segment_command_32,
451 nsects));
452 }
453 else
454 {
455 seghdrsize = sizeof (struct mach_o_segment_command_64);
456 sechdrsize = sizeof (struct mach_o_section_64);
457 segname_offset = offsetof (struct mach_o_section_64, segname);
458 sectname_offset = offsetof (struct mach_o_section_64, sectname);
459 nsects = (*fetch_32) (segbuf
460 + offsetof (struct mach_o_segment_command_64,
461 nsects));
462 }
463
464 /* Fetch the section headers from the segment command. */
465
466 secdata = XNEWVEC (unsigned char, nsects * sechdrsize);
467 if (!simple_object_internal_read (descriptor: sobj->descriptor, offset: offset + seghdrsize,
468 buffer: secdata, size: nsects * sechdrsize, errmsg, err))
469 {
470 XDELETEVEC (secdata);
471 return 0;
472 }
473
474 /* Scan for special sections that signal GNU extensions to the format. */
475
476 gnu_sections_found = 0;
477 index_index = nsects;
478 sections_index = nsects;
479 strtab_index = nsects;
480 nametab_index = nsects;
481 for (i = 0; i < nsects; ++i)
482 {
483 size_t nameoff;
484
485 nameoff = i * sechdrsize + segname_offset;
486 if (strcmp (s1: (char *) secdata + nameoff, s2: omr->segment_name) != 0)
487 continue;
488
489 nameoff = i * sechdrsize + sectname_offset;
490 if (strcmp (s1: (char *) secdata + nameoff, GNU_WRAPPER_NAMES) == 0)
491 {
492 nametab_index = i;
493 gnu_sections_found |= SOMO_NAMES_PRESENT;
494 }
495 else if (strcmp (s1: (char *) secdata + nameoff, GNU_WRAPPER_INDEX) == 0)
496 {
497 index_index = i;
498 gnu_sections_found |= SOMO_INDEX_PRESENT;
499 }
500 else if (strcmp (s1: (char *) secdata + nameoff, GNU_WRAPPER_SECTS) == 0)
501 {
502 sections_index = i;
503 gnu_sections_found |= SOMO_SECTS_PRESENT;
504 }
505 else if (strcmp (s1: (char *) secdata + nameoff, GNU_SECTION_NAMES) == 0)
506 {
507 strtab_index = i;
508 gnu_sections_found |= SOMO_LONGN_PRESENT;
509 }
510 }
511
512 /* If any of the special wrapper section components is present, then
513 they all should be. */
514
515 if ((gnu_sections_found & SOMO_WRAPPING) != 0)
516 {
517 off_t nametab_offset;
518 off_t index_offset;
519
520 if ((gnu_sections_found & SOMO_WRAPPING) != SOMO_WRAPPING)
521 {
522 *errmsg = "GNU Mach-o section wrapper: required section missing";
523 *err = 0; /* No useful errno. */
524 XDELETEVEC (secdata);
525 return 0;
526 }
527
528 /* Fetch the name table. */
529
530 simple_object_mach_o_section_info (is_big_endian: omr->is_big_endian, is_32,
531 sechdr: secdata + nametab_index * sechdrsize,
532 offset: &nametab_offset, size: &nametab_size);
533 nametab = XNEWVEC (char, nametab_size);
534 if (!simple_object_internal_read (descriptor: sobj->descriptor,
535 offset: sobj->offset + nametab_offset,
536 buffer: (unsigned char *) nametab, size: nametab_size,
537 errmsg, err))
538 {
539 XDELETEVEC (nametab);
540 XDELETEVEC (secdata);
541 return 0;
542 }
543
544 /* Fetch the index. */
545
546 simple_object_mach_o_section_info (is_big_endian: omr->is_big_endian, is_32,
547 sechdr: secdata + index_index * sechdrsize,
548 offset: &index_offset, size: &index_size);
549 index = XNEWVEC (unsigned char, index_size);
550 if (!simple_object_internal_read (descriptor: sobj->descriptor,
551 offset: sobj->offset + index_offset,
552 buffer: index, size: index_size,
553 errmsg, err))
554 {
555 XDELETEVEC (index);
556 XDELETEVEC (nametab);
557 XDELETEVEC (secdata);
558 return 0;
559 }
560
561 /* The index contains 4 unsigned ints per sub-section:
562 sub-section offset/length, sub-section name/length.
563 We fix this for both 32 and 64 bit mach-o for now, since
564 other fields limit the maximum size of an object to 4G. */
565 n_wrapped_sects = index_size / 16;
566
567 /* Get the parameters for the wrapper too. */
568 simple_object_mach_o_section_info (is_big_endian: omr->is_big_endian, is_32,
569 sechdr: secdata + sections_index * sechdrsize,
570 offset: &wrapper_sect_offset,
571 size: &wrapper_sect_size);
572 }
573 else
574 {
575 index = NULL;
576 index_size = 0;
577 nametab = NULL;
578 nametab_size = 0;
579 n_wrapped_sects = 0;
580 }
581
582 /* If we have a long names section, fetch it. */
583
584 if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0)
585 {
586 off_t strtab_offset;
587
588 simple_object_mach_o_section_info (is_big_endian: omr->is_big_endian, is_32,
589 sechdr: secdata + strtab_index * sechdrsize,
590 offset: &strtab_offset, size: &strtab_size);
591 strtab = XNEWVEC (char, strtab_size);
592 if (!simple_object_internal_read (descriptor: sobj->descriptor,
593 offset: sobj->offset + strtab_offset,
594 buffer: (unsigned char *) strtab, size: strtab_size,
595 errmsg, err))
596 {
597 XDELETEVEC (strtab);
598 XDELETEVEC (index);
599 XDELETEVEC (nametab);
600 XDELETEVEC (secdata);
601 return 0;
602 }
603 }
604 else
605 {
606 strtab = NULL;
607 strtab_size = 0;
608 strtab_index = nsects;
609 }
610
611 /* Process the sections. */
612
613 for (i = 0; i < nsects; ++i)
614 {
615 const unsigned char *sechdr;
616 char namebuf[MACH_O_NAME_LEN * 2 + 2];
617 char *name;
618 off_t secoffset;
619 size_t secsize;
620
621 sechdr = secdata + i * sechdrsize;
622
623 /* We've already processed the long section names. */
624
625 if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0
626 && i == strtab_index)
627 continue;
628
629 /* We only act on the segment named. */
630
631 if (strcmp (s1: (char *) sechdr + segname_offset, s2: omr->segment_name) != 0)
632 continue;
633
634 /* Process sections associated with the wrapper. */
635
636 if ((gnu_sections_found & SOMO_WRAPPING) != 0)
637 {
638 if (i == nametab_index || i == index_index)
639 continue;
640
641 if (i == sections_index)
642 {
643 unsigned int j;
644 for (j = 0; j < n_wrapped_sects; ++j)
645 {
646 unsigned int subsect_offset, subsect_length, name_offset;
647 subsect_offset = (*fetch_32) (index + 16 * j);
648 subsect_length = (*fetch_32) (index + 16 * j + 4);
649 name_offset = (*fetch_32) (index + 16 * j + 8);
650 /* We don't need the name_length yet. */
651
652 secoffset = wrapper_sect_offset + subsect_offset;
653 secsize = subsect_length;
654 name = nametab + name_offset;
655
656 if (!(*pfn) (data, name, secoffset, secsize))
657 {
658 *errmsg = NULL;
659 *err = 0;
660 XDELETEVEC (index);
661 XDELETEVEC (nametab);
662 XDELETEVEC (strtab);
663 XDELETEVEC (secdata);
664 return 0;
665 }
666 }
667 continue;
668 }
669 }
670
671 memset (s: namebuf, c: 0, n: sizeof (namebuf));
672 /* Copy the section name so we can append a null to make it into a
673 c-string (Mach-o section names are not terminated). */
674 memcpy (dest: namebuf, src: sechdr + sectname_offset, MACH_O_NAME_LEN);
675 namebuf[MACH_O_NAME_LEN] = '\0';
676 name = &namebuf[0];
677 /* Maybe override this if we have long section name extension. */
678 if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0)
679 {
680 if (strtab != NULL && name[0] == '_' && name[1] == '_')
681 {
682 unsigned long stringoffset;
683
684 if (sscanf (s: name + 2, format: "%08lX", &stringoffset) == 1)
685 {
686 if (stringoffset >= strtab_size)
687 {
688 *errmsg = "section name offset out of range";
689 *err = 0;
690 XDELETEVEC (index);
691 XDELETEVEC (nametab);
692 XDELETEVEC (strtab);
693 XDELETEVEC (secdata);
694 return 0;
695 }
696
697 name = strtab + stringoffset;
698 }
699 }
700 }
701
702 simple_object_mach_o_section_info (is_big_endian: omr->is_big_endian, is_32, sechdr,
703 offset: &secoffset, size: &secsize);
704
705 if (!(*pfn) (data, name, secoffset, secsize))
706 {
707 *errmsg = NULL;
708 *err = 0;
709 XDELETEVEC (index);
710 XDELETEVEC (nametab);
711 XDELETEVEC (strtab);
712 XDELETEVEC (secdata);
713 return 0;
714 }
715 }
716
717 XDELETEVEC (index);
718 XDELETEVEC (nametab);
719 XDELETEVEC (strtab);
720 XDELETEVEC (secdata);
721
722 return 1;
723}
724
725/* Find all sections in a Mach-O file. */
726
727static const char *
728simple_object_mach_o_find_sections (simple_object_read *sobj,
729 int (*pfn) (void *, const char *,
730 off_t offset, off_t length),
731 void *data,
732 int *err)
733{
734 struct simple_object_mach_o_read *omr =
735 (struct simple_object_mach_o_read *) sobj->data;
736 off_t offset;
737 size_t seghdrsize;
738 unsigned int (*fetch_32) (const unsigned char *);
739 const char *errmsg;
740 unsigned int i;
741
742 if (omr->magic == MACH_O_MH_MAGIC)
743 {
744 offset = sizeof (struct mach_o_header_32);
745 seghdrsize = sizeof (struct mach_o_segment_command_32);
746 }
747 else
748 {
749 offset = sizeof (struct mach_o_header_64);
750 seghdrsize = sizeof (struct mach_o_segment_command_64);
751 }
752
753 fetch_32 = (omr->is_big_endian
754 ? simple_object_fetch_big_32
755 : simple_object_fetch_little_32);
756
757 for (i = 0; i < omr->ncmds; ++i)
758 {
759 unsigned char loadbuf[sizeof (struct mach_o_load_command)];
760 unsigned int cmd;
761 unsigned int cmdsize;
762
763 if (!simple_object_internal_read (descriptor: sobj->descriptor,
764 offset: sobj->offset + offset,
765 buffer: loadbuf,
766 size: sizeof (struct mach_o_load_command),
767 errmsg: &errmsg, err))
768 return errmsg;
769
770 cmd = (*fetch_32) (loadbuf + offsetof (struct mach_o_load_command, cmd));
771 cmdsize = (*fetch_32) (loadbuf
772 + offsetof (struct mach_o_load_command, cmdsize));
773
774 if (cmd == MACH_O_LC_SEGMENT || cmd == MACH_O_LC_SEGMENT_64)
775 {
776 unsigned char segbuf[sizeof (struct mach_o_segment_command_64)];
777 int r;
778
779 if (!simple_object_internal_read (descriptor: sobj->descriptor,
780 offset: sobj->offset + offset,
781 buffer: segbuf, size: seghdrsize, errmsg: &errmsg, err))
782 return errmsg;
783
784 r = simple_object_mach_o_segment (sobj, offset, segbuf, pfn,
785 data, errmsg: &errmsg, err);
786 if (!r)
787 return errmsg;
788 }
789
790 offset += cmdsize;
791 }
792
793 return NULL;
794}
795
796/* Fetch the attributes for an simple_object_read. */
797
798static void *
799simple_object_mach_o_fetch_attributes (simple_object_read *sobj,
800 const char **errmsg ATTRIBUTE_UNUSED,
801 int *err ATTRIBUTE_UNUSED)
802{
803 struct simple_object_mach_o_read *omr =
804 (struct simple_object_mach_o_read *) sobj->data;
805 struct simple_object_mach_o_attributes *ret;
806
807 ret = XNEW (struct simple_object_mach_o_attributes);
808 ret->magic = omr->magic;
809 ret->is_big_endian = omr->is_big_endian;
810 ret->cputype = omr->cputype;
811 ret->cpusubtype = omr->cpusubtype;
812 ret->flags = omr->flags;
813 ret->reserved = omr->reserved;
814 return ret;
815}
816
817/* Release the private data for an simple_object_read. */
818
819static void
820simple_object_mach_o_release_read (void *data)
821{
822 struct simple_object_mach_o_read *omr =
823 (struct simple_object_mach_o_read *) data;
824
825 free (ptr: omr->segment_name);
826 XDELETE (omr);
827}
828
829/* Compare two attributes structures. */
830
831static const char *
832simple_object_mach_o_attributes_merge (void *todata, void *fromdata, int *err)
833{
834 struct simple_object_mach_o_attributes *to =
835 (struct simple_object_mach_o_attributes *) todata;
836 struct simple_object_mach_o_attributes *from =
837 (struct simple_object_mach_o_attributes *) fromdata;
838
839 if (to->magic != from->magic
840 || to->is_big_endian != from->is_big_endian
841 || to->cputype != from->cputype)
842 {
843 *err = 0;
844 return "Mach-O object format mismatch";
845 }
846 return NULL;
847}
848
849/* Release the private data for an attributes structure. */
850
851static void
852simple_object_mach_o_release_attributes (void *data)
853{
854 XDELETE (data);
855}
856
857/* Prepare to write out a file. */
858
859static void *
860simple_object_mach_o_start_write (void *attributes_data,
861 const char **errmsg ATTRIBUTE_UNUSED,
862 int *err ATTRIBUTE_UNUSED)
863{
864 struct simple_object_mach_o_attributes *attrs =
865 (struct simple_object_mach_o_attributes *) attributes_data;
866 struct simple_object_mach_o_attributes *ret;
867
868 /* We're just going to record the attributes, but we need to make a
869 copy because the user may delete them. */
870 ret = XNEW (struct simple_object_mach_o_attributes);
871 *ret = *attrs;
872 return ret;
873}
874
875/* Write out the header of a Mach-O file. */
876
877static int
878simple_object_mach_o_write_header (simple_object_write *sobj, int descriptor,
879 size_t nsects, const char **errmsg,
880 int *err)
881{
882 struct simple_object_mach_o_attributes *attrs =
883 (struct simple_object_mach_o_attributes *) sobj->data;
884 void (*set_32) (unsigned char *, unsigned int);
885 unsigned char hdrbuf[sizeof (struct mach_o_header_64)];
886 unsigned char *hdr;
887 size_t wrsize;
888
889 set_32 = (attrs->is_big_endian
890 ? simple_object_set_big_32
891 : simple_object_set_little_32);
892
893 memset (s: hdrbuf, c: 0, n: sizeof hdrbuf);
894
895 /* The 32-bit and 64-bit headers start out the same. */
896
897 hdr = &hdrbuf[0];
898 set_32 (hdr + offsetof (struct mach_o_header_32, magic), attrs->magic);
899 set_32 (hdr + offsetof (struct mach_o_header_32, cputype), attrs->cputype);
900 set_32 (hdr + offsetof (struct mach_o_header_32, cpusubtype),
901 attrs->cpusubtype);
902 set_32 (hdr + offsetof (struct mach_o_header_32, filetype), MACH_O_MH_OBJECT);
903 set_32 (hdr + offsetof (struct mach_o_header_32, ncmds), 1);
904 set_32 (hdr + offsetof (struct mach_o_header_32, flags), attrs->flags);
905 if (attrs->magic == MACH_O_MH_MAGIC)
906 {
907 wrsize = sizeof (struct mach_o_header_32);
908 set_32 (hdr + offsetof (struct mach_o_header_32, sizeofcmds),
909 (sizeof (struct mach_o_segment_command_32)
910 + nsects * sizeof (struct mach_o_section_32)));
911 }
912 else
913 {
914 set_32 (hdr + offsetof (struct mach_o_header_64, sizeofcmds),
915 (sizeof (struct mach_o_segment_command_64)
916 + nsects * sizeof (struct mach_o_section_64)));
917 set_32 (hdr + offsetof (struct mach_o_header_64, reserved),
918 attrs->reserved);
919 wrsize = sizeof (struct mach_o_header_64);
920 }
921
922 return simple_object_internal_write (descriptor, offset: 0, buffer: hdrbuf, size: wrsize,
923 errmsg, err);
924}
925
926/* Write a Mach-O section header. */
927
928static int
929simple_object_mach_o_write_section_header (simple_object_write *sobj,
930 int descriptor,
931 size_t sechdr_offset,
932 const char *name, const char *segn,
933 size_t secaddr, size_t secsize,
934 size_t offset, unsigned int align,
935 const char **errmsg, int *err)
936{
937 struct simple_object_mach_o_attributes *attrs =
938 (struct simple_object_mach_o_attributes *) sobj->data;
939 void (*set_32) (unsigned char *, unsigned int);
940 unsigned char hdrbuf[sizeof (struct mach_o_section_64)];
941 unsigned char *hdr;
942 size_t sechdrsize;
943
944 set_32 = (attrs->is_big_endian
945 ? simple_object_set_big_32
946 : simple_object_set_little_32);
947
948 memset (s: hdrbuf, c: 0, n: sizeof hdrbuf);
949
950 hdr = &hdrbuf[0];
951 if (attrs->magic == MACH_O_MH_MAGIC)
952 {
953 strncpy (dest: (char *) hdr + offsetof (struct mach_o_section_32, sectname),
954 src: name, MACH_O_NAME_LEN);
955 strncpy (dest: (char *) hdr + offsetof (struct mach_o_section_32, segname),
956 src: segn, MACH_O_NAME_LEN);
957 set_32 (hdr + offsetof (struct mach_o_section_32, addr), secaddr);
958 set_32 (hdr + offsetof (struct mach_o_section_32, size), secsize);
959 set_32 (hdr + offsetof (struct mach_o_section_32, offset), offset);
960 set_32 (hdr + offsetof (struct mach_o_section_32, align), align);
961 /* reloff left as zero. */
962 /* nreloc left as zero. */
963 set_32 (hdr + offsetof (struct mach_o_section_32, flags),
964 MACH_O_S_ATTR_DEBUG);
965 /* reserved1 left as zero. */
966 /* reserved2 left as zero. */
967 sechdrsize = sizeof (struct mach_o_section_32);
968 }
969 else
970 {
971#ifdef UNSIGNED_64BIT_TYPE
972 void (*set_64) (unsigned char *, ulong_type);
973
974 set_64 = (attrs->is_big_endian
975 ? simple_object_set_big_64
976 : simple_object_set_little_64);
977
978 strncpy (dest: (char *) hdr + offsetof (struct mach_o_section_64, sectname),
979 src: name, MACH_O_NAME_LEN);
980 strncpy (dest: (char *) hdr + offsetof (struct mach_o_section_64, segname),
981 src: segn, MACH_O_NAME_LEN);
982 set_64 (hdr + offsetof (struct mach_o_section_64, addr), secaddr);
983 set_64 (hdr + offsetof (struct mach_o_section_64, size), secsize);
984 set_32 (hdr + offsetof (struct mach_o_section_64, offset), offset);
985 set_32 (hdr + offsetof (struct mach_o_section_64, align), align);
986 /* reloff left as zero. */
987 /* nreloc left as zero. */
988 set_32 (hdr + offsetof (struct mach_o_section_64, flags),
989 MACH_O_S_ATTR_DEBUG);
990 /* reserved1 left as zero. */
991 /* reserved2 left as zero. */
992 /* reserved3 left as zero. */
993#endif
994 sechdrsize = sizeof (struct mach_o_section_64);
995 }
996
997 return simple_object_internal_write (descriptor, offset: sechdr_offset, buffer: hdr,
998 size: sechdrsize, errmsg, err);
999}
1000
1001/* Write out the single (anonymous) segment containing the sections of a Mach-O
1002 Object file.
1003
1004 As a GNU extension to mach-o, when the caller specifies a segment name in
1005 sobj->segment_name, all the sections passed will be output under a single
1006 mach-o section header. The caller's sections are indexed within this
1007 'wrapper' section by a table stored in a second mach-o section. Finally,
1008 arbitrary length section names are permitted by the extension and these are
1009 stored in a table in a third mach-o section.
1010
1011 Note that this is only likely to make any sense for the __GNU_LTO segment
1012 at present.
1013
1014 If the wrapper extension is not in force, we assume that the section name
1015 is in the form __SEGMENT_NAME,__section_name as per Mach-O asm. */
1016
1017static int
1018simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor,
1019 size_t *nsects, const char **errmsg,
1020 int *err)
1021{
1022 struct simple_object_mach_o_attributes *attrs =
1023 (struct simple_object_mach_o_attributes *) sobj->data;
1024 void (*set_32) (unsigned char *, unsigned int);
1025 size_t hdrsize;
1026 size_t seghdrsize;
1027 size_t sechdrsize;
1028 size_t cmdsize;
1029 size_t offset;
1030 size_t sechdr_offset;
1031 size_t secaddr;
1032 unsigned int name_offset;
1033 simple_object_write_section *section;
1034 unsigned char hdrbuf[sizeof (struct mach_o_segment_command_64)];
1035 unsigned char *hdr;
1036 size_t nsects_in;
1037 unsigned int *index;
1038 char *snames;
1039 unsigned int sect;
1040
1041 set_32 = (attrs->is_big_endian
1042 ? simple_object_set_big_32
1043 : simple_object_set_little_32);
1044
1045 /* Write out the sections first. */
1046
1047 if (attrs->magic == MACH_O_MH_MAGIC)
1048 {
1049 hdrsize = sizeof (struct mach_o_header_32);
1050 seghdrsize = sizeof (struct mach_o_segment_command_32);
1051 sechdrsize = sizeof (struct mach_o_section_32);
1052 }
1053 else
1054 {
1055 hdrsize = sizeof (struct mach_o_header_64);
1056 seghdrsize = sizeof (struct mach_o_segment_command_64);
1057 sechdrsize = sizeof (struct mach_o_section_64);
1058 }
1059
1060 name_offset = 0;
1061 *nsects = nsects_in = 0;
1062
1063 /* Count the number of sections we start with. */
1064
1065 for (section = sobj->sections; section != NULL; section = section->next)
1066 nsects_in++;
1067
1068 if (sobj->segment_name != NULL)
1069 {
1070 /* We will only write 3 sections: wrapped data, index and names. */
1071
1072 *nsects = 3;
1073
1074 /* The index has four entries per wrapped section:
1075 Section Offset, length, Name offset, length.
1076 Where the offsets are based at the start of the wrapper and name
1077 sections respectively.
1078 The values are stored as 32 bit int for both 32 and 64 bit mach-o
1079 since the size of a mach-o MH_OBJECT cannot exceed 4G owing to
1080 other constraints. */
1081
1082 index = XNEWVEC (unsigned int, nsects_in * 4);
1083
1084 /* We now need to figure out the size of the names section. This just
1085 stores the names as null-terminated c strings, packed without any
1086 alignment padding. */
1087
1088 for (section = sobj->sections, sect = 0; section != NULL;
1089 section = section->next, sect++)
1090 {
1091 index[sect*4+2] = name_offset;
1092 index[sect*4+3] = strlen (s: section->name) + 1;
1093 name_offset += strlen (s: section->name) + 1;
1094 }
1095 snames = XNEWVEC (char, name_offset);
1096 }
1097 else
1098 {
1099 *nsects = nsects_in;
1100 index = NULL;
1101 snames = NULL;
1102 }
1103
1104 sechdr_offset = hdrsize + seghdrsize;
1105 cmdsize = seghdrsize + *nsects * sechdrsize;
1106 offset = hdrsize + cmdsize;
1107 secaddr = 0;
1108
1109 for (section = sobj->sections, sect = 0;
1110 section != NULL; section = section->next, sect++)
1111 {
1112 size_t mask;
1113 size_t new_offset;
1114 size_t secsize;
1115 struct simple_object_write_section_buffer *buffer;
1116
1117 mask = (1U << section->align) - 1;
1118 new_offset = offset + mask;
1119 new_offset &= ~ mask;
1120 while (new_offset > offset)
1121 {
1122 unsigned char zeroes[16];
1123 size_t write;
1124
1125 memset (s: zeroes, c: 0, n: sizeof zeroes);
1126 write = new_offset - offset;
1127 if (write > sizeof zeroes)
1128 write = sizeof zeroes;
1129 if (!simple_object_internal_write (descriptor, offset, buffer: zeroes, size: write,
1130 errmsg, err))
1131 return 0;
1132 offset += write;
1133 }
1134
1135 secsize = 0;
1136 for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
1137 {
1138 if (!simple_object_internal_write (descriptor, offset: offset + secsize,
1139 buffer: ((const unsigned char *)
1140 buffer->buffer),
1141 size: buffer->size, errmsg, err))
1142 return 0;
1143 secsize += buffer->size;
1144 }
1145
1146 if (sobj->segment_name != NULL)
1147 {
1148 index[sect*4+0] = (unsigned int) offset;
1149 index[sect*4+1] = secsize;
1150 /* Stash the section name in our table. */
1151 memcpy (dest: snames + index[sect * 4 + 2], src: section->name,
1152 n: index[sect * 4 + 3]);
1153 }
1154 else
1155 {
1156 char namebuf[MACH_O_NAME_LEN + 1];
1157 char segnbuf[MACH_O_NAME_LEN + 1];
1158 char *comma;
1159
1160 /* Try to extract segment,section from the input name. */
1161
1162 memset (s: namebuf, c: 0, n: sizeof namebuf);
1163 memset (s: segnbuf, c: 0, n: sizeof segnbuf);
1164 comma = strchr (s: section->name, c: ',');
1165 if (comma != NULL)
1166 {
1167 int len = comma - section->name;
1168 len = len > MACH_O_NAME_LEN ? MACH_O_NAME_LEN : len;
1169 strncpy (dest: namebuf, src: section->name, n: len);
1170 strncpy (dest: segnbuf, src: comma + 1, MACH_O_NAME_LEN);
1171 }
1172 else /* just try to copy the name, leave segment blank. */
1173 strncpy (dest: namebuf, src: section->name, MACH_O_NAME_LEN);
1174
1175 if (!simple_object_mach_o_write_section_header (sobj, descriptor,
1176 sechdr_offset,
1177 name: namebuf, segn: segnbuf,
1178 secaddr, secsize,
1179 offset,
1180 align: section->align,
1181 errmsg, err))
1182 return 0;
1183 sechdr_offset += sechdrsize;
1184 }
1185
1186 offset += secsize;
1187 secaddr += secsize;
1188 }
1189
1190 if (sobj->segment_name != NULL)
1191 {
1192 size_t secsize;
1193 unsigned int i;
1194
1195 /* Write the section header for the wrapper. */
1196 /* Account for any initial aligment - which becomes the alignment for this
1197 created section. */
1198
1199 secsize = (offset - index[0]);
1200 if (!simple_object_mach_o_write_section_header (sobj, descriptor,
1201 sechdr_offset,
1202 GNU_WRAPPER_SECTS,
1203 segn: sobj->segment_name,
1204 secaddr: 0 /*secaddr*/,
1205 secsize, offset: index[0],
1206 align: sobj->sections->align,
1207 errmsg, err))
1208 return 0;
1209
1210 /* Subtract the wrapper section start from the begining of each sub
1211 section. */
1212
1213 for (i = 1; i < nsects_in; ++i)
1214 index[4 * i] -= index[0];
1215 index[0] = 0;
1216
1217 /* Swap the indices, if required. */
1218
1219 for (i = 0; i < (nsects_in * 4); ++i)
1220 set_32 ((unsigned char *) &index[i], index[i]);
1221
1222 sechdr_offset += sechdrsize;
1223
1224 /* Write out the section names.
1225 ... the header ...
1226 name_offset contains the length of the section. It is not aligned. */
1227
1228 if (!simple_object_mach_o_write_section_header (sobj, descriptor,
1229 sechdr_offset,
1230 GNU_WRAPPER_NAMES,
1231 segn: sobj->segment_name,
1232 secaddr: 0 /*secaddr*/,
1233 secsize: name_offset,
1234 offset,
1235 align: 0, errmsg, err))
1236 return 0;
1237
1238 /* ... and the content.. */
1239 if (!simple_object_internal_write (descriptor, offset,
1240 buffer: (const unsigned char *) snames,
1241 size: name_offset, errmsg, err))
1242 return 0;
1243
1244 sechdr_offset += sechdrsize;
1245 secaddr += name_offset;
1246 offset += name_offset;
1247
1248 /* Now do the index, we'll align this to 4 bytes although the read code
1249 will handle unaligned. */
1250
1251 offset += 3;
1252 offset &= ~0x03;
1253 if (!simple_object_mach_o_write_section_header (sobj, descriptor,
1254 sechdr_offset,
1255 GNU_WRAPPER_INDEX,
1256 segn: sobj->segment_name,
1257 secaddr: 0 /*secaddr*/,
1258 secsize: nsects_in * 16,
1259 offset,
1260 align: 2, errmsg, err))
1261 return 0;
1262
1263 /* ... and the content.. */
1264 if (!simple_object_internal_write (descriptor, offset,
1265 buffer: (const unsigned char *) index,
1266 size: nsects_in*16, errmsg, err))
1267 return 0;
1268
1269 XDELETEVEC (index);
1270 XDELETEVEC (snames);
1271 }
1272
1273 /* Write out the segment header. */
1274
1275 memset (s: hdrbuf, c: 0, n: sizeof hdrbuf);
1276
1277 hdr = &hdrbuf[0];
1278 if (attrs->magic == MACH_O_MH_MAGIC)
1279 {
1280 set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmd),
1281 MACH_O_LC_SEGMENT);
1282 set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmdsize),
1283 cmdsize);
1284 /* MH_OBJECTS have a single, anonymous, segment - so the segment name
1285 is left empty. */
1286 /* vmaddr left as zero. */
1287 /* vmsize left as zero. */
1288 set_32 (hdr + offsetof (struct mach_o_segment_command_32, fileoff),
1289 hdrsize + cmdsize);
1290 set_32 (hdr + offsetof (struct mach_o_segment_command_32, filesize),
1291 offset - (hdrsize + cmdsize));
1292 /* maxprot left as zero. */
1293 /* initprot left as zero. */
1294 set_32 (hdr + offsetof (struct mach_o_segment_command_32, nsects),
1295 *nsects);
1296 /* flags left as zero. */
1297 }
1298 else
1299 {
1300#ifdef UNSIGNED_64BIT_TYPE
1301 void (*set_64) (unsigned char *, ulong_type);
1302
1303 set_64 = (attrs->is_big_endian
1304 ? simple_object_set_big_64
1305 : simple_object_set_little_64);
1306
1307 set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmd),
1308 MACH_O_LC_SEGMENT);
1309 set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmdsize),
1310 cmdsize);
1311 /* MH_OBJECTS have a single, anonymous, segment - so the segment name
1312 is left empty. */
1313 /* vmaddr left as zero. */
1314 /* vmsize left as zero. */
1315 set_64 (hdr + offsetof (struct mach_o_segment_command_64, fileoff),
1316 hdrsize + cmdsize);
1317 set_64 (hdr + offsetof (struct mach_o_segment_command_64, filesize),
1318 offset - (hdrsize + cmdsize));
1319 /* maxprot left as zero. */
1320 /* initprot left as zero. */
1321 set_32 (hdr + offsetof (struct mach_o_segment_command_64, nsects),
1322 *nsects);
1323 /* flags left as zero. */
1324#endif
1325 }
1326
1327 return simple_object_internal_write (descriptor, offset: hdrsize, buffer: hdr, size: seghdrsize,
1328 errmsg, err);
1329}
1330
1331/* Write out a complete Mach-O file. */
1332
1333static const char *
1334simple_object_mach_o_write_to_file (simple_object_write *sobj, int descriptor,
1335 int *err)
1336{
1337 size_t nsects = 0;
1338 const char *errmsg;
1339
1340 if (!simple_object_mach_o_write_segment (sobj, descriptor, nsects: &nsects,
1341 errmsg: &errmsg, err))
1342 return errmsg;
1343
1344 if (!simple_object_mach_o_write_header (sobj, descriptor, nsects,
1345 errmsg: &errmsg, err))
1346 return errmsg;
1347
1348 return NULL;
1349}
1350
1351/* Release the private data for an simple_object_write structure. */
1352
1353static void
1354simple_object_mach_o_release_write (void *data)
1355{
1356 XDELETE (data);
1357}
1358
1359/* The Mach-O functions. */
1360
1361const struct simple_object_functions simple_object_mach_o_functions =
1362{
1363 .match: simple_object_mach_o_match,
1364 .find_sections: simple_object_mach_o_find_sections,
1365 .fetch_attributes: simple_object_mach_o_fetch_attributes,
1366 .release_read: simple_object_mach_o_release_read,
1367 .attributes_merge: simple_object_mach_o_attributes_merge,
1368 .release_attributes: simple_object_mach_o_release_attributes,
1369 .start_write: simple_object_mach_o_start_write,
1370 .write_to_file: simple_object_mach_o_write_to_file,
1371 .release_write: simple_object_mach_o_release_write,
1372 NULL
1373};
1374

source code of libiberty/simple-object-mach-o.c