Commit | Line | Data |
---|---|---|
07a8e9f0 DD |
1 | /* simple-object-coff.c -- routines to manipulate XCOFF object files. |
2 | Copyright 2013 Free Software Foundation, Inc. | |
3 | Written by Ian Lance Taylor, Google and David Edelsohn, IBM. | |
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 <errno.h> | |
25 | #include <stddef.h> | |
26 | ||
27 | #ifdef HAVE_STDLIB_H | |
28 | #include <stdlib.h> | |
29 | #endif | |
30 | ||
31 | #ifdef HAVE_STDINT_H | |
32 | #include <stdint.h> | |
33 | #endif | |
34 | ||
35 | #ifdef HAVE_STRING_H | |
36 | #include <string.h> | |
37 | #endif | |
38 | ||
39 | #ifdef HAVE_INTTYPES_H | |
40 | #include <inttypes.h> | |
41 | #endif | |
42 | ||
43 | #include "simple-object-common.h" | |
44 | ||
45 | /* XCOFF structures and constants. */ | |
46 | ||
47 | /* XCOFF file header. */ | |
48 | ||
49 | struct external_filehdr | |
50 | { | |
51 | unsigned char f_magic[2]; /* magic number */ | |
52 | unsigned char f_nscns[2]; /* number of sections */ | |
53 | unsigned char f_timdat[4]; /* time & date stamp */ | |
54 | union | |
55 | { | |
56 | struct | |
57 | { | |
58 | unsigned char f_symptr[4]; /* file pointer to symtab */ | |
59 | unsigned char f_nsyms[4]; /* number of symtab entries */ | |
60 | unsigned char f_opthdr[2]; /* sizeof(optional hdr) */ | |
61 | unsigned char f_flags[2]; /* flags */ | |
62 | } xcoff32; | |
63 | struct | |
64 | { | |
65 | unsigned char f_symptr[8]; /* file pointer to symtab */ | |
66 | unsigned char f_opthdr[2]; /* sizeof(optional hdr) */ | |
67 | unsigned char f_flags[2]; /* flags */ | |
68 | unsigned char f_nsyms[4]; /* number of symtab entries */ | |
69 | } xcoff64; | |
70 | } u; | |
71 | }; | |
72 | ||
73 | /* Bits for filehdr f_flags field. */ | |
74 | ||
75 | #define F_EXEC (0x0002) | |
76 | ||
77 | /* The known values of f_magic in an XCOFF file header. */ | |
78 | ||
79 | #define U802WRMAGIC 0730 /* Writeable text segments. */ | |
80 | #define U802ROMAGIC 0735 /* Readonly sharable text segments. */ | |
81 | #define U802TOCMAGIC 0737 /* Readonly text segments and TOC. */ | |
82 | #define U803XTOCMAGIC 0757 /* Aix 4.3 64-bit XCOFF. */ | |
83 | #define U64_TOCMAGIC 0767 /* AIX 5+ 64-bit XCOFF. */ | |
84 | ||
85 | /* XCOFF section header. */ | |
86 | ||
87 | struct external_scnhdr | |
88 | { | |
89 | unsigned char s_name[8]; /* section name */ | |
90 | union | |
91 | { | |
92 | struct | |
93 | { | |
94 | unsigned char s_paddr[4]; /* physical address, aliased s_nlib */ | |
95 | unsigned char s_vaddr[4]; /* virtual address */ | |
96 | unsigned char s_size[4]; /* section size */ | |
97 | unsigned char s_scnptr[4]; /* file ptr to raw data for section */ | |
98 | unsigned char s_relptr[4]; /* file ptr to relocation */ | |
99 | unsigned char s_lnnoptr[4]; /* file ptr to line numbers */ | |
100 | unsigned char s_nreloc[2]; /* number of relocation entries */ | |
101 | unsigned char s_nlnno[2]; /* number of line number entries */ | |
102 | unsigned char s_flags[4]; /* flags */ | |
103 | } xcoff32; | |
104 | struct | |
105 | { | |
106 | unsigned char s_paddr[8]; /* physical address, aliased s_nlib */ | |
107 | unsigned char s_vaddr[8]; /* virtual address */ | |
108 | unsigned char s_size[8]; /* section size */ | |
109 | unsigned char s_scnptr[8]; /* file ptr to raw data for section */ | |
110 | unsigned char s_relptr[8]; /* file ptr to relocation */ | |
111 | unsigned char s_lnnoptr[8]; /* file ptr to line numbers */ | |
112 | unsigned char s_nreloc[4]; /* number of relocation entries */ | |
113 | unsigned char s_nlnno[4]; /* number of line number entries */ | |
114 | unsigned char s_flags[4]; /* flags */ | |
115 | } xcoff64; | |
116 | } u; | |
117 | }; | |
118 | ||
119 | #define SCNHSZ32 (40) | |
120 | #define SCNHSZ64 (68) | |
121 | ||
122 | /* The length of the s_name field in struct external_scnhdr. */ | |
123 | ||
124 | #define SCNNMLEN (8) | |
125 | ||
126 | /* Bits for scnhdr s_flags field. */ | |
127 | ||
128 | #define STYP_DATA 0x40 | |
129 | ||
130 | /* XCOFF symbol table entry. */ | |
131 | ||
132 | ||
133 | #define N_SYMNMLEN (8) /* # characters in a symbol name */ | |
134 | ||
135 | /* The format of an XCOFF symbol-table entry. */ | |
136 | struct external_syment | |
137 | { | |
138 | union { | |
139 | struct { | |
140 | union { | |
141 | /* The name of the symbol. There is an implicit null character | |
142 | after the end of the array. */ | |
143 | char n_name[N_SYMNMLEN]; | |
144 | struct { | |
145 | /* If n_zeroes is zero, n_offset is the offset the name from | |
146 | the start of the string table. */ | |
147 | unsigned char n_zeroes[4]; | |
148 | unsigned char n_offset[4]; | |
149 | } n; | |
150 | } n; | |
151 | ||
152 | /* The symbol's value. */ | |
153 | unsigned char n_value[4]; | |
154 | } xcoff32; | |
155 | struct { | |
156 | /* The symbol's value. */ | |
157 | unsigned char n_value[8]; | |
158 | ||
159 | /* The offset of the symbol from the start of the string table. */ | |
160 | unsigned char n_offset[4]; | |
161 | } xcoff64; | |
162 | } u; | |
163 | ||
164 | /* The number of the section to which this symbol belongs. */ | |
165 | unsigned char n_scnum[2]; | |
166 | ||
167 | /* The type of symbol. (It can be interpreted as an n_lang | |
168 | and an n_cpu byte, but we don't care about that here.) */ | |
169 | unsigned char n_type[2]; | |
170 | ||
171 | /* The class of symbol (a C_* value). */ | |
172 | unsigned char n_sclass[1]; | |
173 | ||
174 | /* The number of auxiliary symbols attached to this entry. */ | |
175 | unsigned char n_numaux[1]; | |
176 | }; | |
177 | ||
178 | #define SYMESZ (18) | |
179 | ||
180 | /* Length allowed for filename in aux sym format 4. */ | |
181 | ||
182 | #define FILNMLEN (14) | |
183 | ||
184 | /* Omits x_sym and other unused variants. */ | |
185 | ||
186 | union external_auxent | |
187 | { | |
188 | /* Aux sym format 4: file. */ | |
189 | union | |
190 | { | |
191 | char x_fname[FILNMLEN]; | |
192 | struct | |
193 | { | |
194 | unsigned char x_zeroes[4]; | |
195 | unsigned char x_offset[4]; | |
196 | unsigned char x_pad[FILNMLEN-8]; | |
197 | unsigned char x_ftype; | |
198 | } _x; | |
199 | } x_file; | |
200 | /* Aux sym format 5: section. */ | |
201 | struct | |
202 | { | |
203 | unsigned char x_scnlen[4]; /* section length */ | |
204 | unsigned char x_nreloc[2]; /* # relocation entries */ | |
205 | unsigned char x_nlinno[2]; /* # line numbers */ | |
206 | } x_scn; | |
207 | /* CSECT auxiliary entry. */ | |
208 | union | |
209 | { | |
210 | struct | |
211 | { | |
212 | struct | |
213 | { | |
214 | unsigned char x_scnlen[4]; /* csect length */ | |
215 | unsigned char x_parmhash[4]; /* parm type hash index */ | |
216 | unsigned char x_snhash[2]; /* sect num with parm hash */ | |
217 | unsigned char x_smtyp; /* symbol align and type */ | |
218 | unsigned char x_smclas; /* storage mapping class */ | |
219 | unsigned char x_stab; /* dbx stab info index */ | |
220 | unsigned char x_snstab[2]; /* sect num with dbx stab */ | |
221 | } x_csect; | |
222 | } xcoff32; | |
223 | struct | |
224 | { | |
225 | struct | |
226 | { | |
227 | unsigned char x_scnlen_lo[4]; /* csect length */ | |
228 | unsigned char x_parmhash[4]; /* parm type hash index */ | |
229 | unsigned char x_snhash[2]; /* sect num with parm hash */ | |
230 | unsigned char x_smtyp; /* symbol align and type */ | |
231 | unsigned char x_smclas; /* storage mapping class */ | |
232 | unsigned char x_scnlen_hi[4]; | |
233 | unsigned char pad; | |
234 | unsigned char x_auxtype; | |
235 | } x_csect; | |
236 | } xcoff64; | |
237 | } u; | |
238 | /* SECTION/DWARF auxiliary entry. */ | |
239 | struct | |
240 | { | |
241 | unsigned char x_scnlen[4]; /* section length */ | |
242 | unsigned char pad1[4]; | |
243 | unsigned char x_nreloc[4]; /* number RLDs */ | |
244 | } x_sect; | |
245 | }; | |
246 | ||
247 | /* Symbol-related constants. */ | |
248 | ||
249 | #define N_DEBUG (-2) | |
250 | #define IMAGE_SYM_TYPE_NULL (0) | |
251 | #define IMAGE_SYM_DTYPE_NULL (0) | |
252 | #define IMAGE_SYM_CLASS_STATIC (3) | |
253 | #define IMAGE_SYM_CLASS_FILE (103) | |
254 | ||
255 | #define IMAGE_SYM_TYPE \ | |
256 | ((IMAGE_SYM_DTYPE_NULL << 4) | IMAGE_SYM_TYPE_NULL) | |
257 | ||
258 | #define C_STAT (3) | |
259 | #define C_FILE (103) | |
260 | ||
261 | /* Private data for an simple_object_read. */ | |
262 | ||
263 | struct simple_object_xcoff_read | |
264 | { | |
265 | /* Magic number. */ | |
266 | unsigned short magic; | |
267 | /* Number of sections. */ | |
268 | unsigned short nscns; | |
269 | /* File offset of symbol table. */ | |
270 | off_t symptr; | |
271 | /* Number of symbol table entries. */ | |
272 | unsigned int nsyms; | |
273 | /* Flags. */ | |
274 | unsigned short flags; | |
275 | /* Offset of section headers in file. */ | |
276 | off_t scnhdr_offset; | |
277 | }; | |
278 | ||
279 | /* Private data for an simple_object_attributes. */ | |
280 | ||
281 | struct simple_object_xcoff_attributes | |
282 | { | |
283 | /* Magic number. */ | |
284 | unsigned short magic; | |
285 | /* Flags. */ | |
286 | unsigned short flags; | |
287 | }; | |
288 | ||
289 | /* See if we have a XCOFF file. */ | |
290 | ||
291 | static void * | |
292 | simple_object_xcoff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN], | |
293 | int descriptor, off_t offset, | |
294 | const char *segment_name ATTRIBUTE_UNUSED, | |
295 | const char **errmsg, int *err) | |
296 | { | |
297 | unsigned short magic; | |
298 | unsigned short (*fetch_16) (const unsigned char *); | |
299 | unsigned int (*fetch_32) (const unsigned char *); | |
300 | ulong_type (*fetch_64) (const unsigned char *); | |
301 | unsigned char hdrbuf[sizeof (struct external_filehdr)]; | |
302 | struct simple_object_xcoff_read *ocr; | |
303 | int u64; | |
304 | ||
305 | magic = simple_object_fetch_big_16 (header); | |
306 | ||
307 | if (magic != U802TOCMAGIC && magic != U64_TOCMAGIC) | |
308 | { | |
309 | *errmsg = NULL; | |
310 | *err = 0; | |
311 | return NULL; | |
312 | } | |
313 | ||
314 | fetch_16 = simple_object_fetch_big_16; | |
315 | fetch_32 = simple_object_fetch_big_32; | |
316 | fetch_64 = simple_object_fetch_big_64; | |
317 | ||
318 | if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof hdrbuf, | |
319 | errmsg, err)) | |
320 | return NULL; | |
321 | ||
322 | u64 = magic == U64_TOCMAGIC; | |
323 | ||
324 | ocr = XNEW (struct simple_object_xcoff_read); | |
325 | ocr->magic = magic; | |
326 | ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns)); | |
327 | if (u64) | |
328 | { | |
329 | ocr->symptr = fetch_64 (hdrbuf | |
330 | + offsetof (struct external_filehdr, | |
331 | u.xcoff64.f_symptr)); | |
332 | ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, | |
333 | u.xcoff64.f_nsyms)); | |
334 | ocr->scnhdr_offset = (sizeof (struct external_filehdr) | |
335 | + fetch_16 (hdrbuf + offsetof (struct external_filehdr, | |
336 | u.xcoff64.f_opthdr))); | |
337 | ||
338 | } | |
339 | else | |
340 | { | |
341 | ocr->symptr = fetch_32 (hdrbuf | |
342 | + offsetof (struct external_filehdr, | |
343 | u.xcoff32.f_symptr)); | |
344 | ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, | |
345 | u.xcoff32.f_nsyms)); | |
346 | ocr->scnhdr_offset = (sizeof (struct external_filehdr) - 4 | |
347 | + fetch_16 (hdrbuf + offsetof (struct external_filehdr, | |
348 | u.xcoff32.f_opthdr))); | |
349 | ||
350 | } | |
351 | ||
352 | return (void *) ocr; | |
353 | } | |
354 | ||
355 | /* Read the string table in a XCOFF file. */ | |
356 | ||
357 | static char * | |
358 | simple_object_xcoff_read_strtab (simple_object_read *sobj, size_t *strtab_size, | |
359 | const char **errmsg, int *err) | |
360 | { | |
361 | struct simple_object_xcoff_read *ocr = | |
362 | (struct simple_object_xcoff_read *) sobj->data; | |
363 | off_t strtab_offset; | |
364 | unsigned char strsizebuf[4]; | |
365 | size_t strsize; | |
366 | char *strtab; | |
367 | ||
368 | strtab_offset = sobj->offset + ocr->symptr | |
369 | + ocr->nsyms * SYMESZ; | |
370 | if (!simple_object_internal_read (sobj->descriptor, strtab_offset, | |
371 | strsizebuf, 4, errmsg, err)) | |
372 | return NULL; | |
373 | strsize = simple_object_fetch_big_32 (strsizebuf); | |
374 | strtab = XNEWVEC (char, strsize); | |
375 | if (!simple_object_internal_read (sobj->descriptor, strtab_offset, | |
376 | (unsigned char *) strtab, strsize, errmsg, | |
377 | err)) | |
378 | { | |
379 | XDELETEVEC (strtab); | |
380 | return NULL; | |
381 | } | |
382 | *strtab_size = strsize; | |
383 | return strtab; | |
384 | } | |
385 | ||
386 | /* Find all sections in a XCOFF file. */ | |
387 | ||
388 | static const char * | |
389 | simple_object_xcoff_find_sections (simple_object_read *sobj, | |
390 | int (*pfn) (void *, const char *, | |
391 | off_t offset, off_t length), | |
392 | void *data, | |
393 | int *err) | |
394 | { | |
395 | struct simple_object_xcoff_read *ocr = | |
396 | (struct simple_object_xcoff_read *) sobj->data; | |
397 | int u64 = ocr->magic == U64_TOCMAGIC; | |
398 | size_t scnhdr_size; | |
399 | unsigned char *scnbuf; | |
400 | const char *errmsg; | |
401 | unsigned int (*fetch_32) (const unsigned char *); | |
402 | ulong_type (*fetch_64) (const unsigned char *); | |
403 | unsigned int nscns; | |
404 | char *strtab; | |
405 | size_t strtab_size; | |
406 | unsigned int i; | |
407 | ||
408 | scnhdr_size = u64 ? SCNHSZ64 : SCNHSZ32; | |
409 | scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns); | |
410 | if (!simple_object_internal_read (sobj->descriptor, | |
411 | sobj->offset + ocr->scnhdr_offset, | |
412 | scnbuf, scnhdr_size * ocr->nscns, &errmsg, | |
413 | err)) | |
414 | { | |
415 | XDELETEVEC (scnbuf); | |
416 | return errmsg; | |
417 | } | |
418 | ||
419 | fetch_32 = simple_object_fetch_big_32; | |
420 | fetch_64 = simple_object_fetch_big_64; | |
421 | ||
422 | nscns = ocr->nscns; | |
423 | strtab = NULL; | |
424 | strtab_size = 0; | |
425 | for (i = 0; i < nscns; ++i) | |
426 | { | |
427 | unsigned char *scnhdr; | |
428 | unsigned char *scnname; | |
429 | char namebuf[SCNNMLEN + 1]; | |
430 | char *name; | |
431 | off_t scnptr; | |
432 | unsigned int size; | |
433 | ||
434 | scnhdr = scnbuf + i * scnhdr_size; | |
435 | scnname = scnhdr + offsetof (struct external_scnhdr, s_name); | |
436 | memcpy (namebuf, scnname, SCNNMLEN); | |
437 | namebuf[SCNNMLEN] = '\0'; | |
438 | name = &namebuf[0]; | |
439 | if (namebuf[0] == '/') | |
440 | { | |
441 | size_t strindex; | |
442 | char *end; | |
443 | ||
444 | strindex = strtol (namebuf + 1, &end, 10); | |
445 | if (*end == '\0') | |
446 | { | |
447 | /* The real section name is found in the string | |
448 | table. */ | |
449 | if (strtab == NULL) | |
450 | { | |
451 | strtab = simple_object_xcoff_read_strtab (sobj, | |
452 | &strtab_size, | |
453 | &errmsg, err); | |
454 | if (strtab == NULL) | |
455 | { | |
456 | XDELETEVEC (scnbuf); | |
457 | return errmsg; | |
458 | } | |
459 | } | |
460 | ||
461 | if (strindex < 4 || strindex >= strtab_size) | |
462 | { | |
463 | XDELETEVEC (strtab); | |
464 | XDELETEVEC (scnbuf); | |
465 | *err = 0; | |
466 | return "section string index out of range"; | |
467 | } | |
468 | ||
469 | name = strtab + strindex; | |
470 | } | |
471 | } | |
472 | ||
473 | if (u64) | |
474 | { | |
475 | scnptr = fetch_64 (scnhdr + offsetof (struct external_scnhdr, | |
476 | u.xcoff64.s_scnptr)); | |
477 | size = fetch_64 (scnhdr + offsetof (struct external_scnhdr, | |
478 | u.xcoff64.s_size)); | |
479 | } | |
480 | else | |
481 | { | |
482 | scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, | |
483 | u.xcoff32.s_scnptr)); | |
484 | size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, | |
485 | u.xcoff32.s_size)); | |
486 | } | |
487 | ||
488 | if (!(*pfn) (data, name, scnptr, size)) | |
489 | break; | |
490 | } | |
491 | ||
492 | if (strtab != NULL) | |
493 | XDELETEVEC (strtab); | |
494 | XDELETEVEC (scnbuf); | |
495 | ||
496 | return NULL; | |
497 | } | |
498 | ||
499 | /* Fetch the attributes for an simple_object_read. */ | |
500 | ||
501 | static void * | |
502 | simple_object_xcoff_fetch_attributes (simple_object_read *sobj, | |
503 | const char **errmsg ATTRIBUTE_UNUSED, | |
504 | int *err ATTRIBUTE_UNUSED) | |
505 | { | |
506 | struct simple_object_xcoff_read *ocr = | |
507 | (struct simple_object_xcoff_read *) sobj->data; | |
508 | struct simple_object_xcoff_attributes *ret; | |
509 | ||
510 | ret = XNEW (struct simple_object_xcoff_attributes); | |
511 | ret->magic = ocr->magic; | |
512 | ret->flags = ocr->flags; | |
513 | return ret; | |
514 | } | |
515 | ||
516 | /* Release the private data for an simple_object_read. */ | |
517 | ||
518 | static void | |
519 | simple_object_xcoff_release_read (void *data) | |
520 | { | |
521 | XDELETE (data); | |
522 | } | |
523 | ||
524 | /* Compare two attributes structures. */ | |
525 | ||
526 | static const char * | |
527 | simple_object_xcoff_attributes_merge (void *todata, void *fromdata, int *err) | |
528 | { | |
529 | struct simple_object_xcoff_attributes *to = | |
530 | (struct simple_object_xcoff_attributes *) todata; | |
531 | struct simple_object_xcoff_attributes *from = | |
532 | (struct simple_object_xcoff_attributes *) fromdata; | |
533 | ||
534 | if (to->magic != from->magic) | |
535 | { | |
536 | *err = 0; | |
537 | return "XCOFF object format mismatch"; | |
538 | } | |
539 | return NULL; | |
540 | } | |
541 | ||
542 | /* Release the private data for an attributes structure. */ | |
543 | ||
544 | static void | |
545 | simple_object_xcoff_release_attributes (void *data) | |
546 | { | |
547 | XDELETE (data); | |
548 | } | |
549 | ||
550 | /* Prepare to write out a file. */ | |
551 | ||
552 | static void * | |
553 | simple_object_xcoff_start_write (void *attributes_data, | |
554 | const char **errmsg ATTRIBUTE_UNUSED, | |
555 | int *err ATTRIBUTE_UNUSED) | |
556 | { | |
557 | struct simple_object_xcoff_attributes *attrs = | |
558 | (struct simple_object_xcoff_attributes *) attributes_data; | |
559 | struct simple_object_xcoff_attributes *ret; | |
560 | ||
561 | /* We're just going to record the attributes, but we need to make a | |
562 | copy because the user may delete them. */ | |
563 | ret = XNEW (struct simple_object_xcoff_attributes); | |
564 | *ret = *attrs; | |
565 | return ret; | |
566 | } | |
567 | ||
568 | /* Write out a XCOFF filehdr. */ | |
569 | ||
570 | static int | |
571 | simple_object_xcoff_write_filehdr (simple_object_write *sobj, int descriptor, | |
572 | unsigned int nscns, size_t symtab_offset, | |
573 | unsigned int nsyms, const char **errmsg, | |
574 | int *err) | |
575 | { | |
576 | struct simple_object_xcoff_attributes *attrs = | |
577 | (struct simple_object_xcoff_attributes *) sobj->data; | |
578 | int u64 = attrs->magic == U64_TOCMAGIC; | |
579 | unsigned char hdrbuf[sizeof (struct external_filehdr)]; | |
580 | unsigned char *hdr; | |
581 | void (*set_16) (unsigned char *, unsigned short); | |
582 | void (*set_32) (unsigned char *, unsigned int); | |
583 | void (*set_64) (unsigned char *, ulong_type); | |
584 | ||
585 | hdr = &hdrbuf[0]; | |
586 | ||
587 | set_16 = simple_object_set_big_16; | |
588 | set_32 = simple_object_set_big_32; | |
589 | set_64 = simple_object_set_big_64; | |
590 | ||
591 | memset (hdr, 0, sizeof (struct external_filehdr)); | |
592 | ||
593 | set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic); | |
594 | set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns); | |
595 | /* f_timdat left as zero. */ | |
596 | if (u64) | |
597 | { | |
598 | set_64 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr), | |
599 | symtab_offset); | |
600 | set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms), | |
601 | nsyms); | |
602 | /* f_opthdr left as zero. */ | |
603 | set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags), | |
604 | attrs->flags); | |
605 | } | |
606 | else | |
607 | { | |
608 | set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr), | |
609 | symtab_offset); | |
610 | set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms), | |
611 | nsyms); | |
612 | /* f_opthdr left as zero. */ | |
613 | set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags), | |
614 | attrs->flags); | |
615 | } | |
616 | ||
617 | return simple_object_internal_write (descriptor, 0, hdrbuf, | |
618 | sizeof (struct external_filehdr), | |
619 | errmsg, err); | |
620 | } | |
621 | ||
622 | /* Write out a XCOFF section header. */ | |
623 | ||
624 | static int | |
625 | simple_object_xcoff_write_scnhdr (simple_object_write *sobj, | |
626 | int descriptor, | |
627 | const char *name, size_t *name_offset, | |
628 | off_t scnhdr_offset, size_t scnsize, | |
629 | off_t offset, unsigned int align, | |
630 | const char **errmsg, int *err) | |
631 | { | |
632 | struct simple_object_xcoff_read *ocr = | |
633 | (struct simple_object_xcoff_read *) sobj->data; | |
634 | int u64 = ocr->magic == U64_TOCMAGIC; | |
635 | void (*set_32) (unsigned char *, unsigned int); | |
636 | void (*set_64) (unsigned char *, unsigned int); | |
637 | unsigned char hdrbuf[sizeof (struct external_scnhdr)]; | |
638 | unsigned char *hdr; | |
639 | size_t namelen; | |
640 | unsigned int flags; | |
641 | ||
642 | set_32 = simple_object_set_big_32; | |
643 | set_64 = simple_object_set_big_32; | |
644 | ||
645 | memset (hdrbuf, 0, sizeof hdrbuf); | |
646 | hdr = &hdrbuf[0]; | |
647 | ||
648 | namelen = strlen (name); | |
649 | if (namelen <= SCNNMLEN) | |
650 | strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name), | |
651 | name, SCNNMLEN); | |
652 | else | |
653 | { | |
654 | snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name), | |
655 | SCNNMLEN, "/%lu", (unsigned long) *name_offset); | |
656 | *name_offset += namelen + 1; | |
657 | } | |
658 | ||
659 | /* s_paddr left as zero. */ | |
660 | /* s_vaddr left as zero. */ | |
661 | if (u64) | |
662 | { | |
663 | set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_size), | |
664 | scnsize); | |
665 | set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_scnptr), | |
666 | offset); | |
667 | } | |
668 | else | |
669 | { | |
670 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_size), | |
671 | scnsize); | |
672 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_scnptr), | |
673 | offset); | |
674 | } | |
675 | /* s_relptr left as zero. */ | |
676 | /* s_lnnoptr left as zero. */ | |
677 | /* s_nreloc left as zero. */ | |
678 | /* s_nlnno left as zero. */ | |
679 | flags = STYP_DATA; | |
680 | if (align > 13) | |
681 | align = 13; | |
682 | if (u64) | |
683 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_flags), flags); | |
684 | else | |
685 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_flags), flags); | |
686 | ||
687 | return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf, | |
688 | u64 ? SCNHSZ64 : SCNHSZ32, | |
689 | errmsg, err); | |
690 | } | |
691 | ||
692 | /* Write out a complete XCOFF file. */ | |
693 | ||
694 | static const char * | |
695 | simple_object_xcoff_write_to_file (simple_object_write *sobj, int descriptor, | |
696 | int *err) | |
697 | { | |
698 | struct simple_object_xcoff_read *ocr = | |
699 | (struct simple_object_xcoff_read *) sobj->data; | |
700 | int u64 = ocr->magic == U64_TOCMAGIC; | |
701 | unsigned int nscns, secnum; | |
702 | simple_object_write_section *section; | |
703 | off_t scnhdr_offset; | |
704 | size_t symtab_offset; | |
705 | off_t secsym_offset; | |
706 | unsigned int nsyms; | |
707 | size_t offset; | |
708 | size_t name_offset; | |
709 | const char *errmsg; | |
710 | unsigned char strsizebuf[4]; | |
711 | /* The interface doesn't give us access to the name of the input file | |
712 | yet. We want to use its basename for the FILE symbol. This is | |
713 | what 'gas' uses when told to assemble from stdin. */ | |
714 | const char *source_filename = "fake"; | |
715 | size_t sflen; | |
716 | union | |
717 | { | |
718 | struct external_syment sym; | |
719 | union external_auxent aux; | |
720 | } syms[2]; | |
721 | void (*set_16) (unsigned char *, unsigned short); | |
722 | void (*set_32) (unsigned char *, unsigned int); | |
723 | ||
724 | set_16 = simple_object_set_big_16; | |
725 | set_32 = simple_object_set_big_32; | |
726 | ||
727 | nscns = 0; | |
728 | for (section = sobj->sections; section != NULL; section = section->next) | |
729 | ++nscns; | |
730 | ||
731 | scnhdr_offset = sizeof (struct external_filehdr) - (u64 ? 4 : 0); | |
732 | offset = scnhdr_offset + nscns * (u64 ? SCNHSZ64 : SCNHSZ32); | |
733 | name_offset = 4; | |
734 | for (section = sobj->sections; section != NULL; section = section->next) | |
735 | { | |
736 | size_t mask; | |
737 | size_t new_offset; | |
738 | size_t scnsize; | |
739 | struct simple_object_write_section_buffer *buffer; | |
740 | ||
741 | mask = (1U << section->align) - 1; | |
742 | new_offset = offset & mask; | |
743 | new_offset &= ~ mask; | |
744 | while (new_offset > offset) | |
745 | { | |
746 | unsigned char zeroes[16]; | |
747 | size_t write; | |
748 | ||
749 | memset (zeroes, 0, sizeof zeroes); | |
750 | write = new_offset - offset; | |
751 | if (write > sizeof zeroes) | |
752 | write = sizeof zeroes; | |
753 | if (!simple_object_internal_write (descriptor, offset, zeroes, write, | |
754 | &errmsg, err)) | |
755 | return errmsg; | |
756 | } | |
757 | ||
758 | scnsize = 0; | |
759 | for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) | |
760 | { | |
761 | if (!simple_object_internal_write (descriptor, offset + scnsize, | |
762 | ((const unsigned char *) | |
763 | buffer->buffer), | |
764 | buffer->size, &errmsg, err)) | |
765 | return errmsg; | |
766 | scnsize += buffer->size; | |
767 | } | |
768 | ||
769 | if (!simple_object_xcoff_write_scnhdr (sobj, descriptor, section->name, | |
770 | &name_offset, scnhdr_offset, | |
771 | scnsize, offset, section->align, | |
772 | &errmsg, err)) | |
773 | return errmsg; | |
774 | ||
775 | scnhdr_offset += u64 ? SCNHSZ64 : SCNHSZ32; | |
776 | offset += scnsize; | |
777 | } | |
778 | ||
779 | /* Symbol table is always half-word aligned. */ | |
780 | offset += (offset & 1); | |
781 | /* There is a file symbol and a section symbol per section, | |
782 | and each of these has a single auxiliary symbol following. */ | |
783 | nsyms = 2 * (nscns + 1); | |
784 | symtab_offset = offset; | |
785 | /* Advance across space reserved for symbol table to locate | |
786 | start of string table. */ | |
787 | offset += nsyms * SYMESZ; | |
788 | ||
789 | /* Write out file symbol. */ | |
790 | memset (&syms[0], 0, sizeof (syms)); | |
791 | if (!u64) | |
792 | strcpy ((char *)&syms[0].sym.u.xcoff32.n.n_name[0], ".file"); | |
793 | set_16 (&syms[0].sym.n_scnum[0], N_DEBUG); | |
794 | set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE); | |
795 | syms[0].sym.n_sclass[0] = C_FILE; | |
796 | syms[0].sym.n_numaux[0] = 1; | |
797 | /* The name need not be nul-terminated if it fits into the x_fname field | |
798 | directly, but must be if it has to be placed into the string table. */ | |
799 | sflen = strlen (source_filename); | |
800 | if (sflen <= FILNMLEN) | |
801 | memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen); | |
802 | else | |
803 | { | |
804 | set_32 (&syms[1].aux.x_file._x.x_offset[0], name_offset); | |
805 | if (!simple_object_internal_write (descriptor, offset + name_offset, | |
806 | ((const unsigned char *) | |
807 | source_filename), | |
808 | sflen + 1, &errmsg, err)) | |
809 | return errmsg; | |
810 | name_offset += strlen (source_filename) + 1; | |
811 | } | |
812 | if (!simple_object_internal_write (descriptor, symtab_offset, | |
813 | (const unsigned char *) &syms[0], | |
814 | sizeof (syms), &errmsg, err)) | |
815 | return errmsg; | |
816 | ||
817 | /* Write the string table length, followed by the strings and section | |
818 | symbols in step with each other. */ | |
819 | set_32 (strsizebuf, name_offset); | |
820 | if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4, | |
821 | &errmsg, err)) | |
822 | return errmsg; | |
823 | ||
824 | name_offset = 4; | |
825 | secsym_offset = symtab_offset + sizeof (syms); | |
826 | memset (&syms[0], 0, sizeof (syms)); | |
827 | set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE); | |
828 | syms[0].sym.n_sclass[0] = C_STAT; | |
829 | syms[0].sym.n_numaux[0] = 1; | |
830 | secnum = 1; | |
831 | ||
832 | for (section = sobj->sections; section != NULL; section = section->next) | |
833 | { | |
834 | size_t namelen; | |
835 | size_t scnsize; | |
836 | struct simple_object_write_section_buffer *buffer; | |
837 | ||
838 | namelen = strlen (section->name); | |
839 | set_16 (&syms[0].sym.n_scnum[0], secnum++); | |
840 | scnsize = 0; | |
841 | for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) | |
842 | scnsize += buffer->size; | |
843 | set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize); | |
844 | if (namelen > SCNNMLEN) | |
845 | { | |
846 | set_32 (&syms[0].sym.u.xcoff32.n.n.n_zeroes[0], 0); | |
847 | set_32 (&syms[0].sym.u.xcoff32.n.n.n_offset[0], name_offset); | |
848 | if (!simple_object_internal_write (descriptor, offset + name_offset, | |
849 | ((const unsigned char *) | |
850 | section->name), | |
851 | namelen + 1, &errmsg, err)) | |
852 | return errmsg; | |
853 | name_offset += namelen + 1; | |
854 | } | |
855 | else | |
856 | { | |
857 | memcpy (&syms[0].sym.u.xcoff32.n.n_name[0], section->name, | |
858 | strlen (section->name)); | |
859 | memset (&syms[0].sym.u.xcoff32.n.n_name[strlen (section->name)], 0, | |
860 | N_SYMNMLEN - strlen (section->name)); | |
861 | } | |
862 | ||
863 | if (!simple_object_internal_write (descriptor, secsym_offset, | |
864 | (const unsigned char *) &syms[0], | |
865 | sizeof (syms), &errmsg, err)) | |
866 | return errmsg; | |
867 | secsym_offset += sizeof (syms); | |
868 | } | |
869 | ||
870 | if (!simple_object_xcoff_write_filehdr (sobj, descriptor, nscns, | |
871 | symtab_offset, nsyms, &errmsg, err)) | |
872 | return errmsg; | |
873 | ||
874 | return NULL; | |
875 | } | |
876 | ||
877 | /* Release the private data for an simple_object_write structure. */ | |
878 | ||
879 | static void | |
880 | simple_object_xcoff_release_write (void *data) | |
881 | { | |
882 | XDELETE (data); | |
883 | } | |
884 | ||
885 | /* The XCOFF functions. */ | |
886 | ||
887 | const struct simple_object_functions simple_object_xcoff_functions = | |
888 | { | |
889 | simple_object_xcoff_match, | |
890 | simple_object_xcoff_find_sections, | |
891 | simple_object_xcoff_fetch_attributes, | |
892 | simple_object_xcoff_release_read, | |
893 | simple_object_xcoff_attributes_merge, | |
894 | simple_object_xcoff_release_attributes, | |
895 | simple_object_xcoff_start_write, | |
896 | simple_object_xcoff_write_to_file, | |
897 | simple_object_xcoff_release_write | |
898 | }; |