Mercurial > hg > CbC > CbC_gcc
comparison libiberty/simple-object-mach-o.c @ 68:561a7518be6b
update gcc-4.6
author | Nobuyasu Oshiro <dimolto@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Sun, 21 Aug 2011 07:07:55 +0900 |
parents | |
children | 04ced10e8804 |
comparison
equal
deleted
inserted
replaced
67:f6334be47118 | 68:561a7518be6b |
---|---|
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 | |
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 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 | |
61 struct 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 | |
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 /* Private data for an simple_object_read. */ | |
178 | |
179 struct 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 | |
201 struct 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 | |
219 static void * | |
220 simple_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 | |
324 static void | |
325 simple_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 | |
362 static int | |
363 simple_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 | |
522 static const char * | |
523 simple_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 | |
593 static void * | |
594 simple_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 | |
614 static void | |
615 simple_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 | |
626 static const char * | |
627 simple_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 | |
646 static void | |
647 simple_object_mach_o_release_attributes (void *data) | |
648 { | |
649 XDELETE (data); | |
650 } | |
651 | |
652 /* Prepare to write out a file. */ | |
653 | |
654 static void * | |
655 simple_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 | |
672 static int | |
673 simple_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 | |
723 static int | |
724 simple_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 | |
798 static int | |
799 simple_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 | |
977 static const char * | |
978 simple_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 | |
1003 static void | |
1004 simple_object_mach_o_release_write (void *data) | |
1005 { | |
1006 XDELETE (data); | |
1007 } | |
1008 | |
1009 /* The Mach-O functions. */ | |
1010 | |
1011 const 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 }; |