1 | /* simple-object-mach-o.c -- routines to manipulate Mach-O object files. |
2 | Copyright (C) 2010-2024 Free Software Foundation, Inc. |
3 | Written by Ian Lance Taylor, Google. |
4 | |
5 | This program is free software; you can redistribute it and/or modify it |
6 | under the terms of the GNU General Public License as published by the |
7 | Free Software Foundation; either version 2, or (at your option) any |
8 | later version. |
9 | |
10 | This program is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | GNU General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU General Public License |
16 | along with this program; if not, write to the Free Software |
17 | Foundation, 51 Franklin Street - Fifth Floor, |
18 | Boston, 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 | |
48 | struct |
49 | { |
50 | unsigned char [4]; /* Magic number. */ |
51 | unsigned char [4]; /* CPU that this object is for. */ |
52 | unsigned char [4]; /* CPU subtype. */ |
53 | unsigned char [4]; /* Type of file. */ |
54 | unsigned char [4]; /* Number of load commands. */ |
55 | unsigned char [4]; /* Total size of load commands. */ |
56 | unsigned char [4]; /* Flags for special featues. */ |
57 | }; |
58 | |
59 | /* Mach-O header (64-bit version). */ |
60 | |
61 | struct |
62 | { |
63 | unsigned char [4]; /* Magic number. */ |
64 | unsigned char [4]; /* CPU that this object is for. */ |
65 | unsigned char [4]; /* CPU subtype. */ |
66 | unsigned char [4]; /* Type of file. */ |
67 | unsigned char [4]; /* Number of load commands. */ |
68 | unsigned char [4]; /* Total size of load commands. */ |
69 | unsigned char [4]; /* Flags for special featues. */ |
70 | unsigned char [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 | |
85 | struct 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 | |
98 | struct 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 | |
115 | struct 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 | |
132 | struct 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 | |
149 | struct 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 | |
188 | struct 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 | |
210 | struct 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 | |
239 | static void * |
240 | simple_object_mach_o_match ( |
241 | unsigned char [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 | |
344 | static void |
345 | simple_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 | |
403 | static int |
404 | simple_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 | int l; |
621 | |
622 | sechdr = secdata + i * sechdrsize; |
623 | |
624 | /* We've already processed the long section names. */ |
625 | |
626 | if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0 |
627 | && i == strtab_index) |
628 | continue; |
629 | |
630 | /* We only act on the segment named. */ |
631 | |
632 | if (strcmp (s1: (char *) sechdr + segname_offset, s2: omr->segment_name) != 0) |
633 | continue; |
634 | |
635 | /* Process sections associated with the wrapper. */ |
636 | |
637 | if ((gnu_sections_found & SOMO_WRAPPING) != 0) |
638 | { |
639 | if (i == nametab_index || i == index_index) |
640 | continue; |
641 | |
642 | if (i == sections_index) |
643 | { |
644 | unsigned int j; |
645 | for (j = 0; j < n_wrapped_sects; ++j) |
646 | { |
647 | unsigned int subsect_offset, subsect_length, name_offset; |
648 | subsect_offset = (*fetch_32) (index + 16 * j); |
649 | subsect_length = (*fetch_32) (index + 16 * j + 4); |
650 | name_offset = (*fetch_32) (index + 16 * j + 8); |
651 | /* We don't need the name_length yet. */ |
652 | |
653 | secoffset = wrapper_sect_offset + subsect_offset; |
654 | secsize = subsect_length; |
655 | name = nametab + name_offset; |
656 | |
657 | if (!(*pfn) (data, name, secoffset, secsize)) |
658 | { |
659 | *errmsg = NULL; |
660 | *err = 0; |
661 | XDELETEVEC (index); |
662 | XDELETEVEC (nametab); |
663 | XDELETEVEC (strtab); |
664 | XDELETEVEC (secdata); |
665 | return 0; |
666 | } |
667 | } |
668 | continue; |
669 | } |
670 | } |
671 | |
672 | if ((gnu_sections_found & SOMO_LONGN_PRESENT) != 0) |
673 | { |
674 | memcpy (dest: namebuf, src: sechdr + sectname_offset, MACH_O_NAME_LEN); |
675 | namebuf[MACH_O_NAME_LEN] = '\0'; |
676 | |
677 | name = &namebuf[0]; |
678 | if (strtab != NULL && name[0] == '_' && name[1] == '_') |
679 | { |
680 | unsigned long stringoffset; |
681 | |
682 | if (sscanf (s: name + 2, format: "%08lX" , &stringoffset) == 1) |
683 | { |
684 | if (stringoffset >= strtab_size) |
685 | { |
686 | *errmsg = "section name offset out of range" ; |
687 | *err = 0; |
688 | XDELETEVEC (index); |
689 | XDELETEVEC (nametab); |
690 | XDELETEVEC (strtab); |
691 | XDELETEVEC (secdata); |
692 | return 0; |
693 | } |
694 | |
695 | name = strtab + stringoffset; |
696 | } |
697 | } |
698 | } |
699 | else |
700 | { |
701 | /* Otherwise, make a name like __segment,__section as per the |
702 | convention in mach-o asm. */ |
703 | name = &namebuf[0]; |
704 | memcpy (dest: namebuf, src: (char *) sechdr + segname_offset, MACH_O_NAME_LEN); |
705 | namebuf[MACH_O_NAME_LEN] = '\0'; |
706 | l = strlen (s: namebuf); |
707 | namebuf[l] = ','; |
708 | memcpy (dest: namebuf + l + 1, src: (char *) sechdr + sectname_offset, |
709 | MACH_O_NAME_LEN); |
710 | namebuf[l + 1 + MACH_O_NAME_LEN] = '\0'; |
711 | } |
712 | |
713 | simple_object_mach_o_section_info (is_big_endian: omr->is_big_endian, is_32, sechdr, |
714 | offset: &secoffset, size: &secsize); |
715 | |
716 | if (!(*pfn) (data, name, secoffset, secsize)) |
717 | { |
718 | *errmsg = NULL; |
719 | *err = 0; |
720 | XDELETEVEC (index); |
721 | XDELETEVEC (nametab); |
722 | XDELETEVEC (strtab); |
723 | XDELETEVEC (secdata); |
724 | return 0; |
725 | } |
726 | } |
727 | |
728 | XDELETEVEC (index); |
729 | XDELETEVEC (nametab); |
730 | XDELETEVEC (strtab); |
731 | XDELETEVEC (secdata); |
732 | |
733 | return 1; |
734 | } |
735 | |
736 | /* Find all sections in a Mach-O file. */ |
737 | |
738 | static const char * |
739 | simple_object_mach_o_find_sections (simple_object_read *sobj, |
740 | int (*pfn) (void *, const char *, |
741 | off_t offset, off_t length), |
742 | void *data, |
743 | int *err) |
744 | { |
745 | struct simple_object_mach_o_read *omr = |
746 | (struct simple_object_mach_o_read *) sobj->data; |
747 | off_t offset; |
748 | size_t seghdrsize; |
749 | unsigned int (*fetch_32) (const unsigned char *); |
750 | const char *errmsg; |
751 | unsigned int i; |
752 | |
753 | if (omr->magic == MACH_O_MH_MAGIC) |
754 | { |
755 | offset = sizeof (struct mach_o_header_32); |
756 | seghdrsize = sizeof (struct mach_o_segment_command_32); |
757 | } |
758 | else |
759 | { |
760 | offset = sizeof (struct mach_o_header_64); |
761 | seghdrsize = sizeof (struct mach_o_segment_command_64); |
762 | } |
763 | |
764 | fetch_32 = (omr->is_big_endian |
765 | ? simple_object_fetch_big_32 |
766 | : simple_object_fetch_little_32); |
767 | |
768 | for (i = 0; i < omr->ncmds; ++i) |
769 | { |
770 | unsigned char loadbuf[sizeof (struct mach_o_load_command)]; |
771 | unsigned int cmd; |
772 | unsigned int cmdsize; |
773 | |
774 | if (!simple_object_internal_read (descriptor: sobj->descriptor, |
775 | offset: sobj->offset + offset, |
776 | buffer: loadbuf, |
777 | size: sizeof (struct mach_o_load_command), |
778 | errmsg: &errmsg, err)) |
779 | return errmsg; |
780 | |
781 | cmd = (*fetch_32) (loadbuf + offsetof (struct mach_o_load_command, cmd)); |
782 | cmdsize = (*fetch_32) (loadbuf |
783 | + offsetof (struct mach_o_load_command, cmdsize)); |
784 | |
785 | if (cmd == MACH_O_LC_SEGMENT || cmd == MACH_O_LC_SEGMENT_64) |
786 | { |
787 | unsigned char segbuf[sizeof (struct mach_o_segment_command_64)]; |
788 | int r; |
789 | |
790 | if (!simple_object_internal_read (descriptor: sobj->descriptor, |
791 | offset: sobj->offset + offset, |
792 | buffer: segbuf, size: seghdrsize, errmsg: &errmsg, err)) |
793 | return errmsg; |
794 | |
795 | r = simple_object_mach_o_segment (sobj, offset, segbuf, pfn, |
796 | data, errmsg: &errmsg, err); |
797 | if (!r) |
798 | return errmsg; |
799 | } |
800 | |
801 | offset += cmdsize; |
802 | } |
803 | |
804 | return NULL; |
805 | } |
806 | |
807 | /* Fetch the attributes for an simple_object_read. */ |
808 | |
809 | static void * |
810 | simple_object_mach_o_fetch_attributes (simple_object_read *sobj, |
811 | const char **errmsg ATTRIBUTE_UNUSED, |
812 | int *err ATTRIBUTE_UNUSED) |
813 | { |
814 | struct simple_object_mach_o_read *omr = |
815 | (struct simple_object_mach_o_read *) sobj->data; |
816 | struct simple_object_mach_o_attributes *ret; |
817 | |
818 | ret = XNEW (struct simple_object_mach_o_attributes); |
819 | ret->magic = omr->magic; |
820 | ret->is_big_endian = omr->is_big_endian; |
821 | ret->cputype = omr->cputype; |
822 | ret->cpusubtype = omr->cpusubtype; |
823 | ret->flags = omr->flags; |
824 | ret->reserved = omr->reserved; |
825 | return ret; |
826 | } |
827 | |
828 | /* Release the private data for an simple_object_read. */ |
829 | |
830 | static void |
831 | simple_object_mach_o_release_read (void *data) |
832 | { |
833 | struct simple_object_mach_o_read *omr = |
834 | (struct simple_object_mach_o_read *) data; |
835 | |
836 | free (ptr: omr->segment_name); |
837 | XDELETE (omr); |
838 | } |
839 | |
840 | /* Compare two attributes structures. */ |
841 | |
842 | static const char * |
843 | simple_object_mach_o_attributes_merge (void *todata, void *fromdata, int *err) |
844 | { |
845 | struct simple_object_mach_o_attributes *to = |
846 | (struct simple_object_mach_o_attributes *) todata; |
847 | struct simple_object_mach_o_attributes *from = |
848 | (struct simple_object_mach_o_attributes *) fromdata; |
849 | |
850 | if (to->magic != from->magic |
851 | || to->is_big_endian != from->is_big_endian |
852 | || to->cputype != from->cputype) |
853 | { |
854 | *err = 0; |
855 | return "Mach-O object format mismatch" ; |
856 | } |
857 | return NULL; |
858 | } |
859 | |
860 | /* Release the private data for an attributes structure. */ |
861 | |
862 | static void |
863 | simple_object_mach_o_release_attributes (void *data) |
864 | { |
865 | XDELETE (data); |
866 | } |
867 | |
868 | /* Prepare to write out a file. */ |
869 | |
870 | static void * |
871 | simple_object_mach_o_start_write (void *attributes_data, |
872 | const char **errmsg ATTRIBUTE_UNUSED, |
873 | int *err ATTRIBUTE_UNUSED) |
874 | { |
875 | struct simple_object_mach_o_attributes *attrs = |
876 | (struct simple_object_mach_o_attributes *) attributes_data; |
877 | struct simple_object_mach_o_attributes *ret; |
878 | |
879 | /* We're just going to record the attributes, but we need to make a |
880 | copy because the user may delete them. */ |
881 | ret = XNEW (struct simple_object_mach_o_attributes); |
882 | *ret = *attrs; |
883 | return ret; |
884 | } |
885 | |
886 | /* Write out the header of a Mach-O file. */ |
887 | |
888 | static int |
889 | (simple_object_write *sobj, int descriptor, |
890 | size_t nsects, const char **errmsg, |
891 | int *err) |
892 | { |
893 | struct simple_object_mach_o_attributes *attrs = |
894 | (struct simple_object_mach_o_attributes *) sobj->data; |
895 | void (*set_32) (unsigned char *, unsigned int); |
896 | unsigned char hdrbuf[sizeof (struct mach_o_header_64)]; |
897 | unsigned char *hdr; |
898 | size_t wrsize; |
899 | |
900 | set_32 = (attrs->is_big_endian |
901 | ? simple_object_set_big_32 |
902 | : simple_object_set_little_32); |
903 | |
904 | memset (s: hdrbuf, c: 0, n: sizeof hdrbuf); |
905 | |
906 | /* The 32-bit and 64-bit headers start out the same. */ |
907 | |
908 | hdr = &hdrbuf[0]; |
909 | set_32 (hdr + offsetof (struct mach_o_header_32, magic), attrs->magic); |
910 | set_32 (hdr + offsetof (struct mach_o_header_32, cputype), attrs->cputype); |
911 | set_32 (hdr + offsetof (struct mach_o_header_32, cpusubtype), |
912 | attrs->cpusubtype); |
913 | set_32 (hdr + offsetof (struct mach_o_header_32, filetype), MACH_O_MH_OBJECT); |
914 | set_32 (hdr + offsetof (struct mach_o_header_32, ncmds), 1); |
915 | set_32 (hdr + offsetof (struct mach_o_header_32, flags), attrs->flags); |
916 | if (attrs->magic == MACH_O_MH_MAGIC) |
917 | { |
918 | wrsize = sizeof (struct mach_o_header_32); |
919 | set_32 (hdr + offsetof (struct mach_o_header_32, sizeofcmds), |
920 | (sizeof (struct mach_o_segment_command_32) |
921 | + nsects * sizeof (struct mach_o_section_32))); |
922 | } |
923 | else |
924 | { |
925 | set_32 (hdr + offsetof (struct mach_o_header_64, sizeofcmds), |
926 | (sizeof (struct mach_o_segment_command_64) |
927 | + nsects * sizeof (struct mach_o_section_64))); |
928 | set_32 (hdr + offsetof (struct mach_o_header_64, reserved), |
929 | attrs->reserved); |
930 | wrsize = sizeof (struct mach_o_header_64); |
931 | } |
932 | |
933 | return simple_object_internal_write (descriptor, offset: 0, buffer: hdrbuf, size: wrsize, |
934 | errmsg, err); |
935 | } |
936 | |
937 | /* Write a Mach-O section header. */ |
938 | |
939 | static int |
940 | (simple_object_write *sobj, |
941 | int descriptor, |
942 | size_t sechdr_offset, |
943 | const char *name, const char *segn, |
944 | size_t secaddr, size_t secsize, |
945 | size_t offset, unsigned int align, |
946 | const char **errmsg, int *err) |
947 | { |
948 | struct simple_object_mach_o_attributes *attrs = |
949 | (struct simple_object_mach_o_attributes *) sobj->data; |
950 | void (*set_32) (unsigned char *, unsigned int); |
951 | unsigned char hdrbuf[sizeof (struct mach_o_section_64)]; |
952 | unsigned char *hdr; |
953 | size_t sechdrsize; |
954 | |
955 | set_32 = (attrs->is_big_endian |
956 | ? simple_object_set_big_32 |
957 | : simple_object_set_little_32); |
958 | |
959 | memset (s: hdrbuf, c: 0, n: sizeof hdrbuf); |
960 | |
961 | hdr = &hdrbuf[0]; |
962 | if (attrs->magic == MACH_O_MH_MAGIC) |
963 | { |
964 | strncpy (dest: (char *) hdr + offsetof (struct mach_o_section_32, sectname), |
965 | src: name, MACH_O_NAME_LEN); |
966 | strncpy (dest: (char *) hdr + offsetof (struct mach_o_section_32, segname), |
967 | src: segn, MACH_O_NAME_LEN); |
968 | set_32 (hdr + offsetof (struct mach_o_section_32, addr), secaddr); |
969 | set_32 (hdr + offsetof (struct mach_o_section_32, size), secsize); |
970 | set_32 (hdr + offsetof (struct mach_o_section_32, offset), offset); |
971 | set_32 (hdr + offsetof (struct mach_o_section_32, align), align); |
972 | /* reloff left as zero. */ |
973 | /* nreloc left as zero. */ |
974 | set_32 (hdr + offsetof (struct mach_o_section_32, flags), |
975 | MACH_O_S_ATTR_DEBUG); |
976 | /* reserved1 left as zero. */ |
977 | /* reserved2 left as zero. */ |
978 | sechdrsize = sizeof (struct mach_o_section_32); |
979 | } |
980 | else |
981 | { |
982 | #ifdef UNSIGNED_64BIT_TYPE |
983 | void (*set_64) (unsigned char *, ulong_type); |
984 | |
985 | set_64 = (attrs->is_big_endian |
986 | ? simple_object_set_big_64 |
987 | : simple_object_set_little_64); |
988 | |
989 | strncpy (dest: (char *) hdr + offsetof (struct mach_o_section_64, sectname), |
990 | src: name, MACH_O_NAME_LEN); |
991 | strncpy (dest: (char *) hdr + offsetof (struct mach_o_section_64, segname), |
992 | src: segn, MACH_O_NAME_LEN); |
993 | set_64 (hdr + offsetof (struct mach_o_section_64, addr), secaddr); |
994 | set_64 (hdr + offsetof (struct mach_o_section_64, size), secsize); |
995 | set_32 (hdr + offsetof (struct mach_o_section_64, offset), offset); |
996 | set_32 (hdr + offsetof (struct mach_o_section_64, align), align); |
997 | /* reloff left as zero. */ |
998 | /* nreloc left as zero. */ |
999 | set_32 (hdr + offsetof (struct mach_o_section_64, flags), |
1000 | MACH_O_S_ATTR_DEBUG); |
1001 | /* reserved1 left as zero. */ |
1002 | /* reserved2 left as zero. */ |
1003 | /* reserved3 left as zero. */ |
1004 | #endif |
1005 | sechdrsize = sizeof (struct mach_o_section_64); |
1006 | } |
1007 | |
1008 | return simple_object_internal_write (descriptor, offset: sechdr_offset, buffer: hdr, |
1009 | size: sechdrsize, errmsg, err); |
1010 | } |
1011 | |
1012 | /* Write out the single (anonymous) segment containing the sections of a Mach-O |
1013 | Object file. |
1014 | |
1015 | As a GNU extension to mach-o, when the caller specifies a segment name in |
1016 | sobj->segment_name, all the sections passed will be output under a single |
1017 | mach-o section header. The caller's sections are indexed within this |
1018 | 'wrapper' section by a table stored in a second mach-o section. Finally, |
1019 | arbitrary length section names are permitted by the extension and these are |
1020 | stored in a table in a third mach-o section. |
1021 | |
1022 | Note that this is only likely to make any sense for the __GNU_LTO segment |
1023 | at present. |
1024 | |
1025 | If the wrapper extension is not in force, we assume that the section name |
1026 | is in the form __SEGMENT_NAME,__section_name as per Mach-O asm. */ |
1027 | |
1028 | static int |
1029 | simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, |
1030 | size_t *nsects, const char **errmsg, |
1031 | int *err) |
1032 | { |
1033 | struct simple_object_mach_o_attributes *attrs = |
1034 | (struct simple_object_mach_o_attributes *) sobj->data; |
1035 | void (*set_32) (unsigned char *, unsigned int); |
1036 | size_t hdrsize; |
1037 | size_t seghdrsize; |
1038 | size_t sechdrsize; |
1039 | size_t cmdsize; |
1040 | size_t offset; |
1041 | size_t sechdr_offset; |
1042 | size_t secaddr; |
1043 | unsigned int name_offset; |
1044 | simple_object_write_section *section; |
1045 | unsigned char hdrbuf[sizeof (struct mach_o_segment_command_64)]; |
1046 | unsigned char *hdr; |
1047 | size_t nsects_in; |
1048 | unsigned int *index; |
1049 | char *snames; |
1050 | unsigned int sect; |
1051 | |
1052 | set_32 = (attrs->is_big_endian |
1053 | ? simple_object_set_big_32 |
1054 | : simple_object_set_little_32); |
1055 | |
1056 | /* Write out the sections first. */ |
1057 | |
1058 | if (attrs->magic == MACH_O_MH_MAGIC) |
1059 | { |
1060 | hdrsize = sizeof (struct mach_o_header_32); |
1061 | seghdrsize = sizeof (struct mach_o_segment_command_32); |
1062 | sechdrsize = sizeof (struct mach_o_section_32); |
1063 | } |
1064 | else |
1065 | { |
1066 | hdrsize = sizeof (struct mach_o_header_64); |
1067 | seghdrsize = sizeof (struct mach_o_segment_command_64); |
1068 | sechdrsize = sizeof (struct mach_o_section_64); |
1069 | } |
1070 | |
1071 | name_offset = 0; |
1072 | *nsects = nsects_in = 0; |
1073 | |
1074 | /* Count the number of sections we start with. */ |
1075 | |
1076 | for (section = sobj->sections; section != NULL; section = section->next) |
1077 | nsects_in++; |
1078 | |
1079 | if (sobj->segment_name != NULL) |
1080 | { |
1081 | /* We will only write 3 sections: wrapped data, index and names. */ |
1082 | |
1083 | *nsects = 3; |
1084 | |
1085 | /* The index has four entries per wrapped section: |
1086 | Section Offset, length, Name offset, length. |
1087 | Where the offsets are based at the start of the wrapper and name |
1088 | sections respectively. |
1089 | The values are stored as 32 bit int for both 32 and 64 bit mach-o |
1090 | since the size of a mach-o MH_OBJECT cannot exceed 4G owing to |
1091 | other constraints. */ |
1092 | |
1093 | index = XNEWVEC (unsigned int, nsects_in * 4); |
1094 | |
1095 | /* We now need to figure out the size of the names section. This just |
1096 | stores the names as null-terminated c strings, packed without any |
1097 | alignment padding. */ |
1098 | |
1099 | for (section = sobj->sections, sect = 0; section != NULL; |
1100 | section = section->next, sect++) |
1101 | { |
1102 | index[sect*4+2] = name_offset; |
1103 | index[sect*4+3] = strlen (s: section->name) + 1; |
1104 | name_offset += strlen (s: section->name) + 1; |
1105 | } |
1106 | snames = XNEWVEC (char, name_offset); |
1107 | } |
1108 | else |
1109 | { |
1110 | *nsects = nsects_in; |
1111 | index = NULL; |
1112 | snames = NULL; |
1113 | } |
1114 | |
1115 | sechdr_offset = hdrsize + seghdrsize; |
1116 | cmdsize = seghdrsize + *nsects * sechdrsize; |
1117 | offset = hdrsize + cmdsize; |
1118 | secaddr = 0; |
1119 | |
1120 | for (section = sobj->sections, sect = 0; |
1121 | section != NULL; section = section->next, sect++) |
1122 | { |
1123 | size_t mask; |
1124 | size_t new_offset; |
1125 | size_t secsize; |
1126 | struct simple_object_write_section_buffer *buffer; |
1127 | |
1128 | mask = (1U << section->align) - 1; |
1129 | new_offset = offset + mask; |
1130 | new_offset &= ~ mask; |
1131 | while (new_offset > offset) |
1132 | { |
1133 | unsigned char zeroes[16]; |
1134 | size_t write; |
1135 | |
1136 | memset (s: zeroes, c: 0, n: sizeof zeroes); |
1137 | write = new_offset - offset; |
1138 | if (write > sizeof zeroes) |
1139 | write = sizeof zeroes; |
1140 | if (!simple_object_internal_write (descriptor, offset, buffer: zeroes, size: write, |
1141 | errmsg, err)) |
1142 | return 0; |
1143 | offset += write; |
1144 | } |
1145 | |
1146 | secsize = 0; |
1147 | for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) |
1148 | { |
1149 | if (!simple_object_internal_write (descriptor, offset: offset + secsize, |
1150 | buffer: ((const unsigned char *) |
1151 | buffer->buffer), |
1152 | size: buffer->size, errmsg, err)) |
1153 | return 0; |
1154 | secsize += buffer->size; |
1155 | } |
1156 | |
1157 | if (sobj->segment_name != NULL) |
1158 | { |
1159 | index[sect*4+0] = (unsigned int) offset; |
1160 | index[sect*4+1] = secsize; |
1161 | /* Stash the section name in our table. */ |
1162 | memcpy (dest: snames + index[sect * 4 + 2], src: section->name, |
1163 | n: index[sect * 4 + 3]); |
1164 | } |
1165 | else |
1166 | { |
1167 | char namebuf[MACH_O_NAME_LEN + 1]; |
1168 | char segnbuf[MACH_O_NAME_LEN + 1]; |
1169 | char *comma; |
1170 | |
1171 | /* Try to extract segment,section from the input name. */ |
1172 | |
1173 | memset (s: namebuf, c: 0, n: sizeof namebuf); |
1174 | memset (s: segnbuf, c: 0, n: sizeof segnbuf); |
1175 | comma = strchr (s: section->name, c: ','); |
1176 | if (comma != NULL) |
1177 | { |
1178 | int len = comma - section->name; |
1179 | len = len > MACH_O_NAME_LEN ? MACH_O_NAME_LEN : len; |
1180 | strncpy (dest: namebuf, src: section->name, n: len); |
1181 | strncpy (dest: segnbuf, src: comma + 1, MACH_O_NAME_LEN); |
1182 | } |
1183 | else /* just try to copy the name, leave segment blank. */ |
1184 | strncpy (dest: namebuf, src: section->name, MACH_O_NAME_LEN); |
1185 | |
1186 | if (!simple_object_mach_o_write_section_header (sobj, descriptor, |
1187 | sechdr_offset, |
1188 | name: namebuf, segn: segnbuf, |
1189 | secaddr, secsize, |
1190 | offset, |
1191 | align: section->align, |
1192 | errmsg, err)) |
1193 | return 0; |
1194 | sechdr_offset += sechdrsize; |
1195 | } |
1196 | |
1197 | offset += secsize; |
1198 | secaddr += secsize; |
1199 | } |
1200 | |
1201 | if (sobj->segment_name != NULL) |
1202 | { |
1203 | size_t secsize; |
1204 | unsigned int i; |
1205 | |
1206 | /* Write the section header for the wrapper. */ |
1207 | /* Account for any initial aligment - which becomes the alignment for this |
1208 | created section. */ |
1209 | |
1210 | secsize = (offset - index[0]); |
1211 | if (!simple_object_mach_o_write_section_header (sobj, descriptor, |
1212 | sechdr_offset, |
1213 | GNU_WRAPPER_SECTS, |
1214 | segn: sobj->segment_name, |
1215 | secaddr: 0 /*secaddr*/, |
1216 | secsize, offset: index[0], |
1217 | align: sobj->sections->align, |
1218 | errmsg, err)) |
1219 | return 0; |
1220 | |
1221 | /* Subtract the wrapper section start from the begining of each sub |
1222 | section. */ |
1223 | |
1224 | for (i = 1; i < nsects_in; ++i) |
1225 | index[4 * i] -= index[0]; |
1226 | index[0] = 0; |
1227 | |
1228 | /* Swap the indices, if required. */ |
1229 | |
1230 | for (i = 0; i < (nsects_in * 4); ++i) |
1231 | set_32 ((unsigned char *) &index[i], index[i]); |
1232 | |
1233 | sechdr_offset += sechdrsize; |
1234 | |
1235 | /* Write out the section names. |
1236 | ... the header ... |
1237 | name_offset contains the length of the section. It is not aligned. */ |
1238 | |
1239 | if (!simple_object_mach_o_write_section_header (sobj, descriptor, |
1240 | sechdr_offset, |
1241 | GNU_WRAPPER_NAMES, |
1242 | segn: sobj->segment_name, |
1243 | secaddr: 0 /*secaddr*/, |
1244 | secsize: name_offset, |
1245 | offset, |
1246 | align: 0, errmsg, err)) |
1247 | return 0; |
1248 | |
1249 | /* ... and the content.. */ |
1250 | if (!simple_object_internal_write (descriptor, offset, |
1251 | buffer: (const unsigned char *) snames, |
1252 | size: name_offset, errmsg, err)) |
1253 | return 0; |
1254 | |
1255 | sechdr_offset += sechdrsize; |
1256 | secaddr += name_offset; |
1257 | offset += name_offset; |
1258 | |
1259 | /* Now do the index, we'll align this to 4 bytes although the read code |
1260 | will handle unaligned. */ |
1261 | |
1262 | offset += 3; |
1263 | offset &= ~0x03; |
1264 | if (!simple_object_mach_o_write_section_header (sobj, descriptor, |
1265 | sechdr_offset, |
1266 | GNU_WRAPPER_INDEX, |
1267 | segn: sobj->segment_name, |
1268 | secaddr: 0 /*secaddr*/, |
1269 | secsize: nsects_in * 16, |
1270 | offset, |
1271 | align: 2, errmsg, err)) |
1272 | return 0; |
1273 | |
1274 | /* ... and the content.. */ |
1275 | if (!simple_object_internal_write (descriptor, offset, |
1276 | buffer: (const unsigned char *) index, |
1277 | size: nsects_in*16, errmsg, err)) |
1278 | return 0; |
1279 | |
1280 | XDELETEVEC (index); |
1281 | XDELETEVEC (snames); |
1282 | } |
1283 | |
1284 | /* Write out the segment header. */ |
1285 | |
1286 | memset (s: hdrbuf, c: 0, n: sizeof hdrbuf); |
1287 | |
1288 | hdr = &hdrbuf[0]; |
1289 | if (attrs->magic == MACH_O_MH_MAGIC) |
1290 | { |
1291 | set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmd), |
1292 | MACH_O_LC_SEGMENT); |
1293 | set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmdsize), |
1294 | cmdsize); |
1295 | /* MH_OBJECTS have a single, anonymous, segment - so the segment name |
1296 | is left empty. */ |
1297 | /* vmaddr left as zero. */ |
1298 | /* vmsize left as zero. */ |
1299 | set_32 (hdr + offsetof (struct mach_o_segment_command_32, fileoff), |
1300 | hdrsize + cmdsize); |
1301 | set_32 (hdr + offsetof (struct mach_o_segment_command_32, filesize), |
1302 | offset - (hdrsize + cmdsize)); |
1303 | /* maxprot left as zero. */ |
1304 | /* initprot left as zero. */ |
1305 | set_32 (hdr + offsetof (struct mach_o_segment_command_32, nsects), |
1306 | *nsects); |
1307 | /* flags left as zero. */ |
1308 | } |
1309 | else |
1310 | { |
1311 | #ifdef UNSIGNED_64BIT_TYPE |
1312 | void (*set_64) (unsigned char *, ulong_type); |
1313 | |
1314 | set_64 = (attrs->is_big_endian |
1315 | ? simple_object_set_big_64 |
1316 | : simple_object_set_little_64); |
1317 | |
1318 | set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmd), |
1319 | MACH_O_LC_SEGMENT); |
1320 | set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmdsize), |
1321 | cmdsize); |
1322 | /* MH_OBJECTS have a single, anonymous, segment - so the segment name |
1323 | is left empty. */ |
1324 | /* vmaddr left as zero. */ |
1325 | /* vmsize left as zero. */ |
1326 | set_64 (hdr + offsetof (struct mach_o_segment_command_64, fileoff), |
1327 | hdrsize + cmdsize); |
1328 | set_64 (hdr + offsetof (struct mach_o_segment_command_64, filesize), |
1329 | offset - (hdrsize + cmdsize)); |
1330 | /* maxprot left as zero. */ |
1331 | /* initprot left as zero. */ |
1332 | set_32 (hdr + offsetof (struct mach_o_segment_command_64, nsects), |
1333 | *nsects); |
1334 | /* flags left as zero. */ |
1335 | #endif |
1336 | } |
1337 | |
1338 | return simple_object_internal_write (descriptor, offset: hdrsize, buffer: hdr, size: seghdrsize, |
1339 | errmsg, err); |
1340 | } |
1341 | |
1342 | /* Write out a complete Mach-O file. */ |
1343 | |
1344 | static const char * |
1345 | simple_object_mach_o_write_to_file (simple_object_write *sobj, int descriptor, |
1346 | int *err) |
1347 | { |
1348 | size_t nsects = 0; |
1349 | const char *errmsg; |
1350 | |
1351 | if (!simple_object_mach_o_write_segment (sobj, descriptor, nsects: &nsects, |
1352 | errmsg: &errmsg, err)) |
1353 | return errmsg; |
1354 | |
1355 | if (!simple_object_mach_o_write_header (sobj, descriptor, nsects, |
1356 | errmsg: &errmsg, err)) |
1357 | return errmsg; |
1358 | |
1359 | return NULL; |
1360 | } |
1361 | |
1362 | /* Release the private data for an simple_object_write structure. */ |
1363 | |
1364 | static void |
1365 | simple_object_mach_o_release_write (void *data) |
1366 | { |
1367 | XDELETE (data); |
1368 | } |
1369 | |
1370 | /* The Mach-O functions. */ |
1371 | |
1372 | const struct simple_object_functions simple_object_mach_o_functions = |
1373 | { |
1374 | .match: simple_object_mach_o_match, |
1375 | .find_sections: simple_object_mach_o_find_sections, |
1376 | .fetch_attributes: simple_object_mach_o_fetch_attributes, |
1377 | .release_read: simple_object_mach_o_release_read, |
1378 | .attributes_merge: simple_object_mach_o_attributes_merge, |
1379 | .release_attributes: simple_object_mach_o_release_attributes, |
1380 | .start_write: simple_object_mach_o_start_write, |
1381 | .write_to_file: simple_object_mach_o_write_to_file, |
1382 | .release_write: simple_object_mach_o_release_write, |
1383 | NULL |
1384 | }; |
1385 | |