Commit | Line | Data |
---|---|---|
07a8e9f0 | 1 | /* simple-object-coff.c -- routines to manipulate XCOFF object files. |
e495212d | 2 | Copyright (C) 2013-2017 Free Software Foundation, Inc. |
07a8e9f0 DD |
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 { | |
f8ad2513 NC |
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; | |
07a8e9f0 DD |
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 | ||
f8ad2513 | 258 | #define C_EXT (2) |
07a8e9f0 DD |
259 | #define C_STAT (3) |
260 | #define C_FILE (103) | |
f8ad2513 NC |
261 | #define C_HIDEXT (107) |
262 | ||
263 | #define XTY_SD (1) /* section definition */ | |
264 | ||
265 | #define XMC_XO (7) /* extended operation */ | |
07a8e9f0 DD |
266 | |
267 | /* Private data for an simple_object_read. */ | |
268 | ||
269 | struct simple_object_xcoff_read | |
270 | { | |
271 | /* Magic number. */ | |
272 | unsigned short magic; | |
273 | /* Number of sections. */ | |
274 | unsigned short nscns; | |
275 | /* File offset of symbol table. */ | |
276 | off_t symptr; | |
277 | /* Number of symbol table entries. */ | |
278 | unsigned int nsyms; | |
279 | /* Flags. */ | |
280 | unsigned short flags; | |
281 | /* Offset of section headers in file. */ | |
282 | off_t scnhdr_offset; | |
283 | }; | |
284 | ||
285 | /* Private data for an simple_object_attributes. */ | |
286 | ||
287 | struct simple_object_xcoff_attributes | |
288 | { | |
289 | /* Magic number. */ | |
290 | unsigned short magic; | |
291 | /* Flags. */ | |
292 | unsigned short flags; | |
293 | }; | |
294 | ||
295 | /* See if we have a XCOFF file. */ | |
296 | ||
297 | static void * | |
298 | simple_object_xcoff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN], | |
299 | int descriptor, off_t offset, | |
300 | const char *segment_name ATTRIBUTE_UNUSED, | |
301 | const char **errmsg, int *err) | |
302 | { | |
303 | unsigned short magic; | |
304 | unsigned short (*fetch_16) (const unsigned char *); | |
305 | unsigned int (*fetch_32) (const unsigned char *); | |
306 | ulong_type (*fetch_64) (const unsigned char *); | |
307 | unsigned char hdrbuf[sizeof (struct external_filehdr)]; | |
308 | struct simple_object_xcoff_read *ocr; | |
309 | int u64; | |
310 | ||
311 | magic = simple_object_fetch_big_16 (header); | |
312 | ||
313 | if (magic != U802TOCMAGIC && magic != U64_TOCMAGIC) | |
314 | { | |
315 | *errmsg = NULL; | |
316 | *err = 0; | |
317 | return NULL; | |
318 | } | |
319 | ||
320 | fetch_16 = simple_object_fetch_big_16; | |
321 | fetch_32 = simple_object_fetch_big_32; | |
322 | fetch_64 = simple_object_fetch_big_64; | |
323 | ||
324 | if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof hdrbuf, | |
325 | errmsg, err)) | |
326 | return NULL; | |
327 | ||
328 | u64 = magic == U64_TOCMAGIC; | |
329 | ||
330 | ocr = XNEW (struct simple_object_xcoff_read); | |
331 | ocr->magic = magic; | |
332 | ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns)); | |
333 | if (u64) | |
334 | { | |
335 | ocr->symptr = fetch_64 (hdrbuf | |
336 | + offsetof (struct external_filehdr, | |
337 | u.xcoff64.f_symptr)); | |
338 | ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, | |
339 | u.xcoff64.f_nsyms)); | |
340 | ocr->scnhdr_offset = (sizeof (struct external_filehdr) | |
341 | + fetch_16 (hdrbuf + offsetof (struct external_filehdr, | |
342 | u.xcoff64.f_opthdr))); | |
343 | ||
344 | } | |
345 | else | |
346 | { | |
347 | ocr->symptr = fetch_32 (hdrbuf | |
348 | + offsetof (struct external_filehdr, | |
349 | u.xcoff32.f_symptr)); | |
350 | ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, | |
351 | u.xcoff32.f_nsyms)); | |
352 | ocr->scnhdr_offset = (sizeof (struct external_filehdr) - 4 | |
353 | + fetch_16 (hdrbuf + offsetof (struct external_filehdr, | |
354 | u.xcoff32.f_opthdr))); | |
355 | ||
356 | } | |
357 | ||
358 | return (void *) ocr; | |
359 | } | |
360 | ||
361 | /* Read the string table in a XCOFF file. */ | |
362 | ||
363 | static char * | |
364 | simple_object_xcoff_read_strtab (simple_object_read *sobj, size_t *strtab_size, | |
365 | const char **errmsg, int *err) | |
366 | { | |
367 | struct simple_object_xcoff_read *ocr = | |
368 | (struct simple_object_xcoff_read *) sobj->data; | |
369 | off_t strtab_offset; | |
370 | unsigned char strsizebuf[4]; | |
371 | size_t strsize; | |
372 | char *strtab; | |
373 | ||
374 | strtab_offset = sobj->offset + ocr->symptr | |
375 | + ocr->nsyms * SYMESZ; | |
376 | if (!simple_object_internal_read (sobj->descriptor, strtab_offset, | |
377 | strsizebuf, 4, errmsg, err)) | |
378 | return NULL; | |
379 | strsize = simple_object_fetch_big_32 (strsizebuf); | |
380 | strtab = XNEWVEC (char, strsize); | |
381 | if (!simple_object_internal_read (sobj->descriptor, strtab_offset, | |
382 | (unsigned char *) strtab, strsize, errmsg, | |
383 | err)) | |
384 | { | |
385 | XDELETEVEC (strtab); | |
386 | return NULL; | |
387 | } | |
388 | *strtab_size = strsize; | |
389 | return strtab; | |
390 | } | |
391 | ||
392 | /* Find all sections in a XCOFF file. */ | |
393 | ||
394 | static const char * | |
395 | simple_object_xcoff_find_sections (simple_object_read *sobj, | |
396 | int (*pfn) (void *, const char *, | |
397 | off_t offset, off_t length), | |
398 | void *data, | |
399 | int *err) | |
400 | { | |
401 | struct simple_object_xcoff_read *ocr = | |
402 | (struct simple_object_xcoff_read *) sobj->data; | |
403 | int u64 = ocr->magic == U64_TOCMAGIC; | |
404 | size_t scnhdr_size; | |
405 | unsigned char *scnbuf; | |
406 | const char *errmsg; | |
f8ad2513 | 407 | unsigned short (*fetch_16) (const unsigned char *); |
07a8e9f0 DD |
408 | unsigned int (*fetch_32) (const unsigned char *); |
409 | ulong_type (*fetch_64) (const unsigned char *); | |
410 | unsigned int nscns; | |
411 | char *strtab; | |
412 | size_t strtab_size; | |
f8ad2513 | 413 | struct external_syment *symtab = NULL; |
07a8e9f0 DD |
414 | unsigned int i; |
415 | ||
416 | scnhdr_size = u64 ? SCNHSZ64 : SCNHSZ32; | |
417 | scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns); | |
418 | if (!simple_object_internal_read (sobj->descriptor, | |
419 | sobj->offset + ocr->scnhdr_offset, | |
420 | scnbuf, scnhdr_size * ocr->nscns, &errmsg, | |
421 | err)) | |
422 | { | |
423 | XDELETEVEC (scnbuf); | |
424 | return errmsg; | |
425 | } | |
426 | ||
f8ad2513 | 427 | fetch_16 = simple_object_fetch_big_16; |
07a8e9f0 DD |
428 | fetch_32 = simple_object_fetch_big_32; |
429 | fetch_64 = simple_object_fetch_big_64; | |
430 | ||
431 | nscns = ocr->nscns; | |
432 | strtab = NULL; | |
433 | strtab_size = 0; | |
434 | for (i = 0; i < nscns; ++i) | |
435 | { | |
436 | unsigned char *scnhdr; | |
437 | unsigned char *scnname; | |
438 | char namebuf[SCNNMLEN + 1]; | |
439 | char *name; | |
440 | off_t scnptr; | |
f8ad2513 | 441 | off_t size; |
07a8e9f0 DD |
442 | |
443 | scnhdr = scnbuf + i * scnhdr_size; | |
444 | scnname = scnhdr + offsetof (struct external_scnhdr, s_name); | |
445 | memcpy (namebuf, scnname, SCNNMLEN); | |
446 | namebuf[SCNNMLEN] = '\0'; | |
447 | name = &namebuf[0]; | |
448 | if (namebuf[0] == '/') | |
449 | { | |
450 | size_t strindex; | |
451 | char *end; | |
452 | ||
453 | strindex = strtol (namebuf + 1, &end, 10); | |
454 | if (*end == '\0') | |
455 | { | |
456 | /* The real section name is found in the string | |
457 | table. */ | |
458 | if (strtab == NULL) | |
459 | { | |
460 | strtab = simple_object_xcoff_read_strtab (sobj, | |
461 | &strtab_size, | |
462 | &errmsg, err); | |
463 | if (strtab == NULL) | |
464 | { | |
465 | XDELETEVEC (scnbuf); | |
466 | return errmsg; | |
467 | } | |
468 | } | |
469 | ||
470 | if (strindex < 4 || strindex >= strtab_size) | |
471 | { | |
472 | XDELETEVEC (strtab); | |
473 | XDELETEVEC (scnbuf); | |
474 | *err = 0; | |
475 | return "section string index out of range"; | |
476 | } | |
477 | ||
478 | name = strtab + strindex; | |
479 | } | |
480 | } | |
481 | ||
482 | if (u64) | |
483 | { | |
484 | scnptr = fetch_64 (scnhdr + offsetof (struct external_scnhdr, | |
485 | u.xcoff64.s_scnptr)); | |
486 | size = fetch_64 (scnhdr + offsetof (struct external_scnhdr, | |
487 | u.xcoff64.s_size)); | |
488 | } | |
489 | else | |
490 | { | |
491 | scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, | |
492 | u.xcoff32.s_scnptr)); | |
493 | size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, | |
494 | u.xcoff32.s_size)); | |
495 | } | |
496 | ||
497 | if (!(*pfn) (data, name, scnptr, size)) | |
498 | break; | |
499 | } | |
500 | ||
f8ad2513 NC |
501 | /* Special handling for .go_export csect. */ |
502 | if (ocr->nsyms > 0) | |
503 | { | |
504 | unsigned char *sym; | |
505 | const char *n_name; | |
506 | off_t size, n_value; | |
507 | unsigned int n_numaux, n_offset, n_zeroes; | |
508 | short n_scnum; | |
509 | ||
510 | /* Read symbol table. */ | |
511 | symtab = XNEWVEC (struct external_syment, ocr->nsyms * SYMESZ); | |
512 | if (!simple_object_internal_read (sobj->descriptor, | |
513 | sobj->offset + ocr->symptr, | |
514 | (unsigned char *) symtab, | |
515 | ocr->nsyms * SYMESZ, | |
516 | &errmsg, err)) | |
517 | { | |
518 | XDELETEVEC (symtab); | |
519 | XDELETEVEC (scnbuf); | |
520 | return NULL; | |
521 | } | |
522 | ||
523 | /* Search in symbol table if we have a ".go_export" symbol. */ | |
524 | for (i = 0; i < ocr->nsyms; i += n_numaux + 1) | |
525 | { | |
526 | sym = (unsigned char *) &symtab[i]; | |
527 | n_numaux = symtab[i].n_numaux[0]; | |
528 | ||
529 | if (symtab[i].n_sclass[0] != C_EXT | |
530 | && symtab[i].n_sclass[0] != C_HIDEXT) | |
531 | continue; | |
532 | ||
533 | /* Must have at least one csect auxiliary entry. */ | |
534 | if (n_numaux < 1 || i + n_numaux >= ocr->nsyms) | |
535 | continue; | |
536 | ||
537 | n_scnum = fetch_16 (sym + offsetof (struct external_syment, | |
538 | n_scnum)); | |
539 | if (n_scnum < 1 || (unsigned int) n_scnum > nscns) | |
540 | continue; | |
541 | ||
542 | if (u64) | |
543 | { | |
544 | n_value = fetch_64 (sym + offsetof (struct external_syment, | |
545 | u.xcoff64.n_value)); | |
546 | n_offset = fetch_32 (sym + offsetof (struct external_syment, | |
547 | u.xcoff64.n_offset)); | |
548 | } | |
549 | else | |
550 | { | |
551 | /* ".go_export" is longer than N_SYMNMLEN. */ | |
552 | n_zeroes = fetch_32 (sym + offsetof (struct external_syment, | |
553 | u.xcoff32.n.n.n_zeroes)); | |
554 | if (n_zeroes != 0) | |
555 | continue; | |
556 | ||
557 | n_value = fetch_32 (sym + offsetof (struct external_syment, | |
558 | u.xcoff32.n_value)); | |
559 | n_offset = fetch_32 (sym + offsetof (struct external_syment, | |
560 | u.xcoff32.n.n.n_offset)); | |
561 | } | |
562 | ||
563 | /* The real symbol name is found in the string table. */ | |
564 | if (strtab == NULL) | |
565 | { | |
566 | strtab = simple_object_xcoff_read_strtab (sobj, | |
567 | &strtab_size, | |
568 | &errmsg, err); | |
569 | if (strtab == NULL) | |
570 | { | |
571 | XDELETEVEC (symtab); | |
572 | XDELETEVEC (scnbuf); | |
573 | return errmsg; | |
574 | } | |
575 | } | |
576 | ||
577 | if (n_offset >= strtab_size) | |
578 | { | |
579 | XDELETEVEC (strtab); | |
580 | XDELETEVEC (symtab); | |
581 | XDELETEVEC (scnbuf); | |
582 | *err = 0; | |
583 | return "symbol string index out of range"; | |
584 | } | |
585 | n_name = strtab + n_offset; | |
586 | ||
587 | if (!strcmp (n_name, ".go_export")) | |
588 | { | |
589 | union external_auxent *auxent; | |
590 | unsigned char *aux, *scnhdr; | |
591 | off_t scnptr, x_scnlen; | |
592 | ||
593 | /* Found .go_export symbol, read its csect auxiliary entry. | |
594 | By convention, it is the last auxiliary entry. */ | |
595 | auxent = (union external_auxent *) &symtab[i + n_numaux]; | |
596 | aux = (unsigned char *) auxent; | |
597 | if (u64) | |
598 | { | |
599 | if ((auxent->u.xcoff64.x_csect.x_smtyp & 0x7) != XTY_SD | |
600 | || auxent->u.xcoff64.x_csect.x_smclas != XMC_XO) | |
601 | continue; | |
602 | ||
603 | x_scnlen = fetch_32 (aux + offsetof (union external_auxent, | |
604 | u.xcoff64.x_csect.x_scnlen_hi)); | |
605 | x_scnlen = x_scnlen << 32 | |
606 | | fetch_32 (aux + offsetof (union external_auxent, | |
607 | u.xcoff64.x_csect.x_scnlen_lo)); | |
608 | } | |
609 | else | |
610 | { | |
611 | if ((auxent->u.xcoff32.x_csect.x_smtyp & 0x7) != XTY_SD | |
612 | || auxent->u.xcoff32.x_csect.x_smclas != XMC_XO) | |
613 | continue; | |
614 | ||
615 | x_scnlen = fetch_32 (aux + offsetof (union external_auxent, | |
616 | u.xcoff32.x_csect.x_scnlen)); | |
617 | } | |
618 | ||
619 | /* Get header of containing section. */ | |
620 | scnhdr = scnbuf + (n_scnum - 1) * scnhdr_size; | |
621 | if (u64) | |
622 | { | |
623 | scnptr = fetch_64 (scnhdr + offsetof (struct external_scnhdr, | |
624 | u.xcoff64.s_scnptr)); | |
625 | size = fetch_64 (scnhdr + offsetof (struct external_scnhdr, | |
626 | u.xcoff64.s_size)); | |
627 | } | |
628 | else | |
629 | { | |
630 | scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, | |
631 | u.xcoff32.s_scnptr)); | |
632 | size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, | |
633 | u.xcoff32.s_size)); | |
634 | } | |
635 | if (n_value + x_scnlen > size) | |
636 | break; | |
637 | ||
638 | (*pfn) (data, ".go_export", scnptr + n_value, x_scnlen); | |
639 | break; | |
640 | } | |
641 | } | |
642 | } | |
643 | ||
644 | if (symtab != NULL) | |
645 | XDELETEVEC (symtab); | |
07a8e9f0 DD |
646 | if (strtab != NULL) |
647 | XDELETEVEC (strtab); | |
648 | XDELETEVEC (scnbuf); | |
649 | ||
650 | return NULL; | |
651 | } | |
652 | ||
653 | /* Fetch the attributes for an simple_object_read. */ | |
654 | ||
655 | static void * | |
656 | simple_object_xcoff_fetch_attributes (simple_object_read *sobj, | |
657 | const char **errmsg ATTRIBUTE_UNUSED, | |
658 | int *err ATTRIBUTE_UNUSED) | |
659 | { | |
660 | struct simple_object_xcoff_read *ocr = | |
661 | (struct simple_object_xcoff_read *) sobj->data; | |
662 | struct simple_object_xcoff_attributes *ret; | |
663 | ||
664 | ret = XNEW (struct simple_object_xcoff_attributes); | |
665 | ret->magic = ocr->magic; | |
666 | ret->flags = ocr->flags; | |
667 | return ret; | |
668 | } | |
669 | ||
670 | /* Release the private data for an simple_object_read. */ | |
671 | ||
672 | static void | |
673 | simple_object_xcoff_release_read (void *data) | |
674 | { | |
675 | XDELETE (data); | |
676 | } | |
677 | ||
678 | /* Compare two attributes structures. */ | |
679 | ||
680 | static const char * | |
681 | simple_object_xcoff_attributes_merge (void *todata, void *fromdata, int *err) | |
682 | { | |
683 | struct simple_object_xcoff_attributes *to = | |
684 | (struct simple_object_xcoff_attributes *) todata; | |
685 | struct simple_object_xcoff_attributes *from = | |
686 | (struct simple_object_xcoff_attributes *) fromdata; | |
687 | ||
688 | if (to->magic != from->magic) | |
689 | { | |
690 | *err = 0; | |
691 | return "XCOFF object format mismatch"; | |
692 | } | |
693 | return NULL; | |
694 | } | |
695 | ||
696 | /* Release the private data for an attributes structure. */ | |
697 | ||
698 | static void | |
699 | simple_object_xcoff_release_attributes (void *data) | |
700 | { | |
701 | XDELETE (data); | |
702 | } | |
703 | ||
704 | /* Prepare to write out a file. */ | |
705 | ||
706 | static void * | |
707 | simple_object_xcoff_start_write (void *attributes_data, | |
708 | const char **errmsg ATTRIBUTE_UNUSED, | |
709 | int *err ATTRIBUTE_UNUSED) | |
710 | { | |
711 | struct simple_object_xcoff_attributes *attrs = | |
712 | (struct simple_object_xcoff_attributes *) attributes_data; | |
713 | struct simple_object_xcoff_attributes *ret; | |
714 | ||
715 | /* We're just going to record the attributes, but we need to make a | |
716 | copy because the user may delete them. */ | |
717 | ret = XNEW (struct simple_object_xcoff_attributes); | |
718 | *ret = *attrs; | |
719 | return ret; | |
720 | } | |
721 | ||
722 | /* Write out a XCOFF filehdr. */ | |
723 | ||
724 | static int | |
725 | simple_object_xcoff_write_filehdr (simple_object_write *sobj, int descriptor, | |
726 | unsigned int nscns, size_t symtab_offset, | |
727 | unsigned int nsyms, const char **errmsg, | |
728 | int *err) | |
729 | { | |
730 | struct simple_object_xcoff_attributes *attrs = | |
731 | (struct simple_object_xcoff_attributes *) sobj->data; | |
732 | int u64 = attrs->magic == U64_TOCMAGIC; | |
733 | unsigned char hdrbuf[sizeof (struct external_filehdr)]; | |
734 | unsigned char *hdr; | |
735 | void (*set_16) (unsigned char *, unsigned short); | |
736 | void (*set_32) (unsigned char *, unsigned int); | |
737 | void (*set_64) (unsigned char *, ulong_type); | |
738 | ||
739 | hdr = &hdrbuf[0]; | |
740 | ||
741 | set_16 = simple_object_set_big_16; | |
742 | set_32 = simple_object_set_big_32; | |
743 | set_64 = simple_object_set_big_64; | |
744 | ||
745 | memset (hdr, 0, sizeof (struct external_filehdr)); | |
746 | ||
747 | set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic); | |
748 | set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns); | |
749 | /* f_timdat left as zero. */ | |
750 | if (u64) | |
751 | { | |
752 | set_64 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr), | |
753 | symtab_offset); | |
754 | set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms), | |
755 | nsyms); | |
756 | /* f_opthdr left as zero. */ | |
757 | set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags), | |
758 | attrs->flags); | |
759 | } | |
760 | else | |
761 | { | |
762 | set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr), | |
763 | symtab_offset); | |
764 | set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms), | |
765 | nsyms); | |
766 | /* f_opthdr left as zero. */ | |
767 | set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags), | |
768 | attrs->flags); | |
769 | } | |
770 | ||
771 | return simple_object_internal_write (descriptor, 0, hdrbuf, | |
772 | sizeof (struct external_filehdr), | |
773 | errmsg, err); | |
774 | } | |
775 | ||
776 | /* Write out a XCOFF section header. */ | |
777 | ||
778 | static int | |
779 | simple_object_xcoff_write_scnhdr (simple_object_write *sobj, | |
780 | int descriptor, | |
781 | const char *name, size_t *name_offset, | |
782 | off_t scnhdr_offset, size_t scnsize, | |
783 | off_t offset, unsigned int align, | |
784 | const char **errmsg, int *err) | |
785 | { | |
786 | struct simple_object_xcoff_read *ocr = | |
787 | (struct simple_object_xcoff_read *) sobj->data; | |
788 | int u64 = ocr->magic == U64_TOCMAGIC; | |
789 | void (*set_32) (unsigned char *, unsigned int); | |
790 | void (*set_64) (unsigned char *, unsigned int); | |
791 | unsigned char hdrbuf[sizeof (struct external_scnhdr)]; | |
792 | unsigned char *hdr; | |
793 | size_t namelen; | |
794 | unsigned int flags; | |
795 | ||
796 | set_32 = simple_object_set_big_32; | |
797 | set_64 = simple_object_set_big_32; | |
798 | ||
799 | memset (hdrbuf, 0, sizeof hdrbuf); | |
800 | hdr = &hdrbuf[0]; | |
801 | ||
802 | namelen = strlen (name); | |
803 | if (namelen <= SCNNMLEN) | |
804 | strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name), | |
805 | name, SCNNMLEN); | |
806 | else | |
807 | { | |
808 | snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name), | |
809 | SCNNMLEN, "/%lu", (unsigned long) *name_offset); | |
810 | *name_offset += namelen + 1; | |
811 | } | |
812 | ||
813 | /* s_paddr left as zero. */ | |
814 | /* s_vaddr left as zero. */ | |
815 | if (u64) | |
816 | { | |
817 | set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_size), | |
818 | scnsize); | |
819 | set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_scnptr), | |
820 | offset); | |
821 | } | |
822 | else | |
823 | { | |
824 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_size), | |
825 | scnsize); | |
826 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_scnptr), | |
827 | offset); | |
828 | } | |
829 | /* s_relptr left as zero. */ | |
830 | /* s_lnnoptr left as zero. */ | |
831 | /* s_nreloc left as zero. */ | |
832 | /* s_nlnno left as zero. */ | |
833 | flags = STYP_DATA; | |
834 | if (align > 13) | |
835 | align = 13; | |
836 | if (u64) | |
837 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_flags), flags); | |
838 | else | |
839 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_flags), flags); | |
840 | ||
841 | return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf, | |
842 | u64 ? SCNHSZ64 : SCNHSZ32, | |
843 | errmsg, err); | |
844 | } | |
845 | ||
846 | /* Write out a complete XCOFF file. */ | |
847 | ||
848 | static const char * | |
849 | simple_object_xcoff_write_to_file (simple_object_write *sobj, int descriptor, | |
850 | int *err) | |
851 | { | |
852 | struct simple_object_xcoff_read *ocr = | |
853 | (struct simple_object_xcoff_read *) sobj->data; | |
854 | int u64 = ocr->magic == U64_TOCMAGIC; | |
855 | unsigned int nscns, secnum; | |
856 | simple_object_write_section *section; | |
857 | off_t scnhdr_offset; | |
858 | size_t symtab_offset; | |
859 | off_t secsym_offset; | |
860 | unsigned int nsyms; | |
861 | size_t offset; | |
862 | size_t name_offset; | |
863 | const char *errmsg; | |
864 | unsigned char strsizebuf[4]; | |
865 | /* The interface doesn't give us access to the name of the input file | |
866 | yet. We want to use its basename for the FILE symbol. This is | |
867 | what 'gas' uses when told to assemble from stdin. */ | |
868 | const char *source_filename = "fake"; | |
869 | size_t sflen; | |
870 | union | |
871 | { | |
872 | struct external_syment sym; | |
873 | union external_auxent aux; | |
874 | } syms[2]; | |
875 | void (*set_16) (unsigned char *, unsigned short); | |
876 | void (*set_32) (unsigned char *, unsigned int); | |
877 | ||
878 | set_16 = simple_object_set_big_16; | |
879 | set_32 = simple_object_set_big_32; | |
880 | ||
881 | nscns = 0; | |
882 | for (section = sobj->sections; section != NULL; section = section->next) | |
883 | ++nscns; | |
884 | ||
885 | scnhdr_offset = sizeof (struct external_filehdr) - (u64 ? 4 : 0); | |
886 | offset = scnhdr_offset + nscns * (u64 ? SCNHSZ64 : SCNHSZ32); | |
887 | name_offset = 4; | |
888 | for (section = sobj->sections; section != NULL; section = section->next) | |
889 | { | |
890 | size_t mask; | |
891 | size_t new_offset; | |
892 | size_t scnsize; | |
893 | struct simple_object_write_section_buffer *buffer; | |
894 | ||
895 | mask = (1U << section->align) - 1; | |
896 | new_offset = offset & mask; | |
897 | new_offset &= ~ mask; | |
898 | while (new_offset > offset) | |
899 | { | |
900 | unsigned char zeroes[16]; | |
901 | size_t write; | |
902 | ||
903 | memset (zeroes, 0, sizeof zeroes); | |
904 | write = new_offset - offset; | |
905 | if (write > sizeof zeroes) | |
906 | write = sizeof zeroes; | |
907 | if (!simple_object_internal_write (descriptor, offset, zeroes, write, | |
908 | &errmsg, err)) | |
909 | return errmsg; | |
910 | } | |
911 | ||
912 | scnsize = 0; | |
913 | for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) | |
914 | { | |
915 | if (!simple_object_internal_write (descriptor, offset + scnsize, | |
916 | ((const unsigned char *) | |
917 | buffer->buffer), | |
918 | buffer->size, &errmsg, err)) | |
919 | return errmsg; | |
920 | scnsize += buffer->size; | |
921 | } | |
922 | ||
923 | if (!simple_object_xcoff_write_scnhdr (sobj, descriptor, section->name, | |
924 | &name_offset, scnhdr_offset, | |
925 | scnsize, offset, section->align, | |
926 | &errmsg, err)) | |
927 | return errmsg; | |
928 | ||
929 | scnhdr_offset += u64 ? SCNHSZ64 : SCNHSZ32; | |
930 | offset += scnsize; | |
931 | } | |
932 | ||
933 | /* Symbol table is always half-word aligned. */ | |
934 | offset += (offset & 1); | |
935 | /* There is a file symbol and a section symbol per section, | |
936 | and each of these has a single auxiliary symbol following. */ | |
937 | nsyms = 2 * (nscns + 1); | |
938 | symtab_offset = offset; | |
939 | /* Advance across space reserved for symbol table to locate | |
940 | start of string table. */ | |
941 | offset += nsyms * SYMESZ; | |
942 | ||
943 | /* Write out file symbol. */ | |
944 | memset (&syms[0], 0, sizeof (syms)); | |
945 | if (!u64) | |
946 | strcpy ((char *)&syms[0].sym.u.xcoff32.n.n_name[0], ".file"); | |
947 | set_16 (&syms[0].sym.n_scnum[0], N_DEBUG); | |
948 | set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE); | |
949 | syms[0].sym.n_sclass[0] = C_FILE; | |
950 | syms[0].sym.n_numaux[0] = 1; | |
951 | /* The name need not be nul-terminated if it fits into the x_fname field | |
952 | directly, but must be if it has to be placed into the string table. */ | |
953 | sflen = strlen (source_filename); | |
954 | if (sflen <= FILNMLEN) | |
955 | memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen); | |
956 | else | |
957 | { | |
958 | set_32 (&syms[1].aux.x_file._x.x_offset[0], name_offset); | |
959 | if (!simple_object_internal_write (descriptor, offset + name_offset, | |
960 | ((const unsigned char *) | |
961 | source_filename), | |
962 | sflen + 1, &errmsg, err)) | |
963 | return errmsg; | |
964 | name_offset += strlen (source_filename) + 1; | |
965 | } | |
966 | if (!simple_object_internal_write (descriptor, symtab_offset, | |
967 | (const unsigned char *) &syms[0], | |
968 | sizeof (syms), &errmsg, err)) | |
969 | return errmsg; | |
970 | ||
971 | /* Write the string table length, followed by the strings and section | |
972 | symbols in step with each other. */ | |
973 | set_32 (strsizebuf, name_offset); | |
974 | if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4, | |
975 | &errmsg, err)) | |
976 | return errmsg; | |
977 | ||
978 | name_offset = 4; | |
979 | secsym_offset = symtab_offset + sizeof (syms); | |
980 | memset (&syms[0], 0, sizeof (syms)); | |
981 | set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE); | |
982 | syms[0].sym.n_sclass[0] = C_STAT; | |
983 | syms[0].sym.n_numaux[0] = 1; | |
984 | secnum = 1; | |
985 | ||
986 | for (section = sobj->sections; section != NULL; section = section->next) | |
987 | { | |
988 | size_t namelen; | |
989 | size_t scnsize; | |
990 | struct simple_object_write_section_buffer *buffer; | |
991 | ||
992 | namelen = strlen (section->name); | |
993 | set_16 (&syms[0].sym.n_scnum[0], secnum++); | |
994 | scnsize = 0; | |
995 | for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) | |
996 | scnsize += buffer->size; | |
997 | set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize); | |
998 | if (namelen > SCNNMLEN) | |
999 | { | |
1000 | set_32 (&syms[0].sym.u.xcoff32.n.n.n_zeroes[0], 0); | |
1001 | set_32 (&syms[0].sym.u.xcoff32.n.n.n_offset[0], name_offset); | |
1002 | if (!simple_object_internal_write (descriptor, offset + name_offset, | |
1003 | ((const unsigned char *) | |
1004 | section->name), | |
1005 | namelen + 1, &errmsg, err)) | |
1006 | return errmsg; | |
1007 | name_offset += namelen + 1; | |
1008 | } | |
1009 | else | |
1010 | { | |
1011 | memcpy (&syms[0].sym.u.xcoff32.n.n_name[0], section->name, | |
1012 | strlen (section->name)); | |
1013 | memset (&syms[0].sym.u.xcoff32.n.n_name[strlen (section->name)], 0, | |
1014 | N_SYMNMLEN - strlen (section->name)); | |
1015 | } | |
1016 | ||
1017 | if (!simple_object_internal_write (descriptor, secsym_offset, | |
1018 | (const unsigned char *) &syms[0], | |
1019 | sizeof (syms), &errmsg, err)) | |
1020 | return errmsg; | |
1021 | secsym_offset += sizeof (syms); | |
1022 | } | |
1023 | ||
1024 | if (!simple_object_xcoff_write_filehdr (sobj, descriptor, nscns, | |
1025 | symtab_offset, nsyms, &errmsg, err)) | |
1026 | return errmsg; | |
1027 | ||
1028 | return NULL; | |
1029 | } | |
1030 | ||
1031 | /* Release the private data for an simple_object_write structure. */ | |
1032 | ||
1033 | static void | |
1034 | simple_object_xcoff_release_write (void *data) | |
1035 | { | |
1036 | XDELETE (data); | |
1037 | } | |
1038 | ||
1039 | /* The XCOFF functions. */ | |
1040 | ||
1041 | const struct simple_object_functions simple_object_xcoff_functions = | |
1042 | { | |
1043 | simple_object_xcoff_match, | |
1044 | simple_object_xcoff_find_sections, | |
1045 | simple_object_xcoff_fetch_attributes, | |
1046 | simple_object_xcoff_release_read, | |
1047 | simple_object_xcoff_attributes_merge, | |
1048 | simple_object_xcoff_release_attributes, | |
1049 | simple_object_xcoff_start_write, | |
1050 | simple_object_xcoff_write_to_file, | |
f8ad2513 NC |
1051 | simple_object_xcoff_release_write, |
1052 | NULL | |
07a8e9f0 | 1053 | }; |