Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5191 | serge | 1 | /* simple-object-mach-o.c -- routines to manipulate Mach-O object files. |
2 | Copyright 2010, 2011, 2013 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 |
||
25 | |||
26 | #ifdef HAVE_STDLIB_H |
||
27 | #include |
||
28 | #endif |
||
29 | |||
30 | #ifdef HAVE_STDINT_H |
||
31 | #include |
||
32 | #endif |
||
33 | |||
34 | #ifdef HAVE_STRING_H |
||
35 | #include |
||
36 | #endif |
||
37 | |||
38 | #ifdef HAVE_INTTYPES_H |
||
39 | #include |
||
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 | /* 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 | |||
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 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 (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 (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 (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, buf, |
||
306 | (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 (sobj->descriptor, offset + seghdrsize, |
||
468 | secdata, 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 ((char *) secdata + nameoff, omr->segment_name) != 0) |
||
487 | continue; |
||
488 | |||
489 | nameoff = i * sechdrsize + sectname_offset; |
||
490 | if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_NAMES) == 0) |
||
491 | { |
||
492 | nametab_index = i; |
||
493 | gnu_sections_found |= SOMO_NAMES_PRESENT; |
||
494 | } |
||
495 | else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_INDEX) == 0) |
||
496 | { |
||
497 | index_index = i; |
||
498 | gnu_sections_found |= SOMO_INDEX_PRESENT; |
||
499 | } |
||
500 | else if (strcmp ((char *) secdata + nameoff, GNU_WRAPPER_SECTS) == 0) |
||
501 | { |
||
502 | sections_index = i; |
||
503 | gnu_sections_found |= SOMO_SECTS_PRESENT; |
||
504 | } |
||
505 | else if (strcmp ((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 (omr->is_big_endian, is_32, |
||
531 | secdata + nametab_index * sechdrsize, |
||
532 | &nametab_offset, &nametab_size); |
||
533 | nametab = XNEWVEC (char, nametab_size); |
||
534 | if (!simple_object_internal_read (sobj->descriptor, |
||
535 | sobj->offset + nametab_offset, |
||
536 | (unsigned char *) nametab, 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 (omr->is_big_endian, is_32, |
||
547 | secdata + index_index * sechdrsize, |
||
548 | &index_offset, &index_size); |
||
549 | index = XNEWVEC (unsigned char, index_size); |
||
550 | if (!simple_object_internal_read (sobj->descriptor, |
||
551 | sobj->offset + index_offset, |
||
552 | index, 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 (omr->is_big_endian, is_32, |
||
569 | secdata + sections_index * sechdrsize, |
||
570 | &wrapper_sect_offset, |
||
571 | &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 (omr->is_big_endian, is_32, |
||
589 | secdata + strtab_index * sechdrsize, |
||
590 | &strtab_offset, &strtab_size); |
||
591 | strtab = XNEWVEC (char, strtab_size); |
||
592 | if (!simple_object_internal_read (sobj->descriptor, |
||
593 | sobj->offset + strtab_offset, |
||
594 | (unsigned char *) strtab, 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 ((char *) sechdr + segname_offset, 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 (namebuf, 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 (name + 2, "%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 (namebuf, (char *) sechdr + segname_offset, MACH_O_NAME_LEN); |
||
705 | namebuf[MACH_O_NAME_LEN] = '\0'; |
||
706 | l = strlen (namebuf); |
||
707 | namebuf[l] = ','; |
||
708 | memcpy (namebuf + l + 1, (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 (omr->is_big_endian, is_32, sechdr, |
||
714 | &secoffset, &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 (sobj->descriptor, |
||
775 | sobj->offset + offset, |
||
776 | loadbuf, |
||
777 | sizeof (struct mach_o_load_command), |
||
778 | &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 (sobj->descriptor, |
||
791 | sobj->offset + offset, |
||
792 | segbuf, seghdrsize, &errmsg, err)) |
||
793 | return errmsg; |
||
794 | |||
795 | r = simple_object_mach_o_segment (sobj, offset, segbuf, pfn, |
||
796 | data, &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 (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_mach_o_write_header (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 (hdrbuf, 0, 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, 0, hdrbuf, wrsize, |
||
934 | errmsg, err); |
||
935 | } |
||
936 | |||
937 | /* Write a Mach-O section header. */ |
||
938 | |||
939 | static int |
||
940 | simple_object_mach_o_write_section_header (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 (hdrbuf, 0, sizeof hdrbuf); |
||
960 | |||
961 | hdr = &hdrbuf[0]; |
||
962 | if (attrs->magic == MACH_O_MH_MAGIC) |
||
963 | { |
||
964 | strncpy ((char *) hdr + offsetof (struct mach_o_section_32, sectname), |
||
965 | name, MACH_O_NAME_LEN); |
||
966 | strncpy ((char *) hdr + offsetof (struct mach_o_section_32, segname), |
||
967 | 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 ((char *) hdr + offsetof (struct mach_o_section_64, sectname), |
||
990 | name, MACH_O_NAME_LEN); |
||
991 | strncpy ((char *) hdr + offsetof (struct mach_o_section_64, segname), |
||
992 | 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, sechdr_offset, hdr, |
||
1009 | 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 (section->name) + 1; |
||
1104 | name_offset += strlen (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 (zeroes, 0, sizeof zeroes); |
||
1137 | write = new_offset - offset; |
||
1138 | if (write > sizeof zeroes) |
||
1139 | write = sizeof zeroes; |
||
1140 | if (!simple_object_internal_write (descriptor, offset, zeroes, 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 + secsize, |
||
1150 | ((const unsigned char *) |
||
1151 | buffer->buffer), |
||
1152 | 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 (snames + index[sect * 4 + 2], section->name, |
||
1163 | 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 (namebuf, 0, sizeof namebuf); |
||
1174 | memset (segnbuf, 0, sizeof segnbuf); |
||
1175 | comma = strchr (section->name, ','); |
||
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 (namebuf, section->name, len); |
||
1181 | strncpy (segnbuf, comma + 1, MACH_O_NAME_LEN); |
||
1182 | } |
||
1183 | else /* just try to copy the name, leave segment blank. */ |
||
1184 | strncpy (namebuf, section->name, MACH_O_NAME_LEN); |
||
1185 | |||
1186 | if (!simple_object_mach_o_write_section_header (sobj, descriptor, |
||
1187 | sechdr_offset, |
||
1188 | namebuf, segnbuf, |
||
1189 | secaddr, secsize, |
||
1190 | offset, |
||
1191 | 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 | sobj->segment_name, |
||
1215 | |||
1216 | secsize, index[0], |
||
1217 | 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 | sechdr_offset += sechdrsize; |
||
1229 | |||
1230 | /* Write out the section names. |
||
1231 | ... the header ... |
||
1232 | name_offset contains the length of the section. It is not aligned. */ |
||
1233 | |||
1234 | if (!simple_object_mach_o_write_section_header (sobj, descriptor, |
||
1235 | sechdr_offset, |
||
1236 | GNU_WRAPPER_NAMES, |
||
1237 | sobj->segment_name, |
||
1238 | |||
1239 | name_offset, |
||
1240 | offset, |
||
1241 | 0, errmsg, err)) |
||
1242 | return 0; |
||
1243 | |||
1244 | /* ... and the content.. */ |
||
1245 | if (!simple_object_internal_write (descriptor, offset, |
||
1246 | (const unsigned char *) snames, |
||
1247 | name_offset, errmsg, err)) |
||
1248 | return 0; |
||
1249 | |||
1250 | sechdr_offset += sechdrsize; |
||
1251 | secaddr += name_offset; |
||
1252 | offset += name_offset; |
||
1253 | |||
1254 | /* Now do the index, we'll align this to 4 bytes although the read code |
||
1255 | will handle unaligned. */ |
||
1256 | |||
1257 | offset += 3; |
||
1258 | offset &= ~0x03; |
||
1259 | if (!simple_object_mach_o_write_section_header (sobj, descriptor, |
||
1260 | sechdr_offset, |
||
1261 | GNU_WRAPPER_INDEX, |
||
1262 | sobj->segment_name, |
||
1263 | |||
1264 | nsects_in * 16, |
||
1265 | offset, |
||
1266 | 2, errmsg, err)) |
||
1267 | return 0; |
||
1268 | |||
1269 | /* ... and the content.. */ |
||
1270 | if (!simple_object_internal_write (descriptor, offset, |
||
1271 | (const unsigned char *) index, |
||
1272 | nsects_in*16, errmsg, err)) |
||
1273 | return 0; |
||
1274 | |||
1275 | XDELETEVEC (index); |
||
1276 | XDELETEVEC (snames); |
||
1277 | } |
||
1278 | |||
1279 | /* Write out the segment header. */ |
||
1280 | |||
1281 | memset (hdrbuf, 0, sizeof hdrbuf); |
||
1282 | |||
1283 | hdr = &hdrbuf[0]; |
||
1284 | if (attrs->magic == MACH_O_MH_MAGIC) |
||
1285 | { |
||
1286 | set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmd), |
||
1287 | MACH_O_LC_SEGMENT); |
||
1288 | set_32 (hdr + offsetof (struct mach_o_segment_command_32, cmdsize), |
||
1289 | cmdsize); |
||
1290 | /* MH_OBJECTS have a single, anonymous, segment - so the segment name |
||
1291 | is left empty. */ |
||
1292 | /* vmaddr left as zero. */ |
||
1293 | /* vmsize left as zero. */ |
||
1294 | set_32 (hdr + offsetof (struct mach_o_segment_command_32, fileoff), |
||
1295 | hdrsize + cmdsize); |
||
1296 | set_32 (hdr + offsetof (struct mach_o_segment_command_32, filesize), |
||
1297 | offset - (hdrsize + cmdsize)); |
||
1298 | /* maxprot left as zero. */ |
||
1299 | /* initprot left as zero. */ |
||
1300 | set_32 (hdr + offsetof (struct mach_o_segment_command_32, nsects), |
||
1301 | *nsects); |
||
1302 | /* flags left as zero. */ |
||
1303 | } |
||
1304 | else |
||
1305 | { |
||
1306 | #ifdef UNSIGNED_64BIT_TYPE |
||
1307 | void (*set_64) (unsigned char *, ulong_type); |
||
1308 | |||
1309 | set_64 = (attrs->is_big_endian |
||
1310 | ? simple_object_set_big_64 |
||
1311 | : simple_object_set_little_64); |
||
1312 | |||
1313 | set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmd), |
||
1314 | MACH_O_LC_SEGMENT); |
||
1315 | set_32 (hdr + offsetof (struct mach_o_segment_command_64, cmdsize), |
||
1316 | cmdsize); |
||
1317 | /* MH_OBJECTS have a single, anonymous, segment - so the segment name |
||
1318 | is left empty. */ |
||
1319 | /* vmaddr left as zero. */ |
||
1320 | /* vmsize left as zero. */ |
||
1321 | set_64 (hdr + offsetof (struct mach_o_segment_command_64, fileoff), |
||
1322 | hdrsize + cmdsize); |
||
1323 | set_64 (hdr + offsetof (struct mach_o_segment_command_64, filesize), |
||
1324 | offset - (hdrsize + cmdsize)); |
||
1325 | /* maxprot left as zero. */ |
||
1326 | /* initprot left as zero. */ |
||
1327 | set_32 (hdr + offsetof (struct mach_o_segment_command_64, nsects), |
||
1328 | *nsects); |
||
1329 | /* flags left as zero. */ |
||
1330 | #endif |
||
1331 | } |
||
1332 | |||
1333 | return simple_object_internal_write (descriptor, hdrsize, hdr, seghdrsize, |
||
1334 | errmsg, err); |
||
1335 | } |
||
1336 | |||
1337 | /* Write out a complete Mach-O file. */ |
||
1338 | |||
1339 | static const char * |
||
1340 | simple_object_mach_o_write_to_file (simple_object_write *sobj, int descriptor, |
||
1341 | int *err) |
||
1342 | { |
||
1343 | size_t nsects = 0; |
||
1344 | const char *errmsg; |
||
1345 | |||
1346 | if (!simple_object_mach_o_write_segment (sobj, descriptor, &nsects, |
||
1347 | &errmsg, err)) |
||
1348 | return errmsg; |
||
1349 | |||
1350 | if (!simple_object_mach_o_write_header (sobj, descriptor, nsects, |
||
1351 | &errmsg, err)) |
||
1352 | return errmsg; |
||
1353 | |||
1354 | return NULL; |
||
1355 | } |
||
1356 | |||
1357 | /* Release the private data for an simple_object_write structure. */ |
||
1358 | |||
1359 | static void |
||
1360 | simple_object_mach_o_release_write (void *data) |
||
1361 | { |
||
1362 | XDELETE (data); |
||
1363 | } |
||
1364 | |||
1365 | /* The Mach-O functions. */ |
||
1366 | |||
1367 | const struct simple_object_functions simple_object_mach_o_functions = |
||
1368 | { |
||
1369 | simple_object_mach_o_match, |
||
1370 | simple_object_mach_o_find_sections, |
||
1371 | simple_object_mach_o_fetch_attributes, |
||
1372 | simple_object_mach_o_release_read, |
||
1373 | simple_object_mach_o_attributes_merge, |
||
1374 | simple_object_mach_o_release_attributes, |
||
1375 | simple_object_mach_o_start_write, |
||
1376 | simple_object_mach_o_write_to_file, |
||
1377 | simple_object_mach_o_release_write |
||
1378 | };>><>>>>> |