Commit | Line | Data |
---|---|---|
07a8e9f0 | 1 | /* simple-object-coff.c -- routines to manipulate XCOFF object files. |
82704155 | 2 | Copyright (C) 2013-2019 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 | { | |
de54ee81 EZ |
599 | /* Use an intermediate 64-bit type to avoid |
600 | compilation warning about 32-bit shift below on | |
601 | hosts with 32-bit off_t which aren't supported by | |
602 | AC_SYS_LARGEFILE. */ | |
603 | ulong_type x_scnlen64; | |
604 | ||
f8ad2513 NC |
605 | if ((auxent->u.xcoff64.x_csect.x_smtyp & 0x7) != XTY_SD |
606 | || auxent->u.xcoff64.x_csect.x_smclas != XMC_XO) | |
607 | continue; | |
608 | ||
e9301e76 NC |
609 | x_scnlen64 = |
610 | fetch_32 (aux + offsetof (union external_auxent, | |
611 | u.xcoff64.x_csect.x_scnlen_hi)); | |
612 | x_scnlen = | |
613 | ((x_scnlen64 << 32) | |
614 | | fetch_32 (aux | |
615 | + offsetof (union external_auxent, | |
616 | u.xcoff64.x_csect.x_scnlen_lo))); | |
f8ad2513 NC |
617 | } |
618 | else | |
619 | { | |
620 | if ((auxent->u.xcoff32.x_csect.x_smtyp & 0x7) != XTY_SD | |
621 | || auxent->u.xcoff32.x_csect.x_smclas != XMC_XO) | |
622 | continue; | |
623 | ||
624 | x_scnlen = fetch_32 (aux + offsetof (union external_auxent, | |
625 | u.xcoff32.x_csect.x_scnlen)); | |
626 | } | |
627 | ||
628 | /* Get header of containing section. */ | |
629 | scnhdr = scnbuf + (n_scnum - 1) * scnhdr_size; | |
630 | if (u64) | |
631 | { | |
632 | scnptr = fetch_64 (scnhdr + offsetof (struct external_scnhdr, | |
633 | u.xcoff64.s_scnptr)); | |
634 | size = fetch_64 (scnhdr + offsetof (struct external_scnhdr, | |
635 | u.xcoff64.s_size)); | |
636 | } | |
637 | else | |
638 | { | |
639 | scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, | |
640 | u.xcoff32.s_scnptr)); | |
641 | size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, | |
642 | u.xcoff32.s_size)); | |
643 | } | |
644 | if (n_value + x_scnlen > size) | |
645 | break; | |
646 | ||
647 | (*pfn) (data, ".go_export", scnptr + n_value, x_scnlen); | |
648 | break; | |
649 | } | |
650 | } | |
651 | } | |
652 | ||
653 | if (symtab != NULL) | |
654 | XDELETEVEC (symtab); | |
07a8e9f0 DD |
655 | if (strtab != NULL) |
656 | XDELETEVEC (strtab); | |
657 | XDELETEVEC (scnbuf); | |
658 | ||
659 | return NULL; | |
660 | } | |
661 | ||
662 | /* Fetch the attributes for an simple_object_read. */ | |
663 | ||
664 | static void * | |
665 | simple_object_xcoff_fetch_attributes (simple_object_read *sobj, | |
666 | const char **errmsg ATTRIBUTE_UNUSED, | |
667 | int *err ATTRIBUTE_UNUSED) | |
668 | { | |
669 | struct simple_object_xcoff_read *ocr = | |
670 | (struct simple_object_xcoff_read *) sobj->data; | |
671 | struct simple_object_xcoff_attributes *ret; | |
672 | ||
673 | ret = XNEW (struct simple_object_xcoff_attributes); | |
674 | ret->magic = ocr->magic; | |
675 | ret->flags = ocr->flags; | |
676 | return ret; | |
677 | } | |
678 | ||
679 | /* Release the private data for an simple_object_read. */ | |
680 | ||
681 | static void | |
682 | simple_object_xcoff_release_read (void *data) | |
683 | { | |
684 | XDELETE (data); | |
685 | } | |
686 | ||
687 | /* Compare two attributes structures. */ | |
688 | ||
689 | static const char * | |
690 | simple_object_xcoff_attributes_merge (void *todata, void *fromdata, int *err) | |
691 | { | |
692 | struct simple_object_xcoff_attributes *to = | |
693 | (struct simple_object_xcoff_attributes *) todata; | |
694 | struct simple_object_xcoff_attributes *from = | |
695 | (struct simple_object_xcoff_attributes *) fromdata; | |
696 | ||
697 | if (to->magic != from->magic) | |
698 | { | |
699 | *err = 0; | |
700 | return "XCOFF object format mismatch"; | |
701 | } | |
702 | return NULL; | |
703 | } | |
704 | ||
705 | /* Release the private data for an attributes structure. */ | |
706 | ||
707 | static void | |
708 | simple_object_xcoff_release_attributes (void *data) | |
709 | { | |
710 | XDELETE (data); | |
711 | } | |
712 | ||
713 | /* Prepare to write out a file. */ | |
714 | ||
715 | static void * | |
716 | simple_object_xcoff_start_write (void *attributes_data, | |
717 | const char **errmsg ATTRIBUTE_UNUSED, | |
718 | int *err ATTRIBUTE_UNUSED) | |
719 | { | |
720 | struct simple_object_xcoff_attributes *attrs = | |
721 | (struct simple_object_xcoff_attributes *) attributes_data; | |
722 | struct simple_object_xcoff_attributes *ret; | |
723 | ||
724 | /* We're just going to record the attributes, but we need to make a | |
725 | copy because the user may delete them. */ | |
726 | ret = XNEW (struct simple_object_xcoff_attributes); | |
727 | *ret = *attrs; | |
728 | return ret; | |
729 | } | |
730 | ||
731 | /* Write out a XCOFF filehdr. */ | |
732 | ||
733 | static int | |
734 | simple_object_xcoff_write_filehdr (simple_object_write *sobj, int descriptor, | |
735 | unsigned int nscns, size_t symtab_offset, | |
736 | unsigned int nsyms, const char **errmsg, | |
737 | int *err) | |
738 | { | |
739 | struct simple_object_xcoff_attributes *attrs = | |
740 | (struct simple_object_xcoff_attributes *) sobj->data; | |
741 | int u64 = attrs->magic == U64_TOCMAGIC; | |
742 | unsigned char hdrbuf[sizeof (struct external_filehdr)]; | |
743 | unsigned char *hdr; | |
744 | void (*set_16) (unsigned char *, unsigned short); | |
745 | void (*set_32) (unsigned char *, unsigned int); | |
746 | void (*set_64) (unsigned char *, ulong_type); | |
747 | ||
748 | hdr = &hdrbuf[0]; | |
749 | ||
750 | set_16 = simple_object_set_big_16; | |
751 | set_32 = simple_object_set_big_32; | |
752 | set_64 = simple_object_set_big_64; | |
753 | ||
754 | memset (hdr, 0, sizeof (struct external_filehdr)); | |
755 | ||
756 | set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic); | |
757 | set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns); | |
758 | /* f_timdat left as zero. */ | |
759 | if (u64) | |
760 | { | |
761 | set_64 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr), | |
762 | symtab_offset); | |
763 | set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms), | |
764 | nsyms); | |
765 | /* f_opthdr left as zero. */ | |
766 | set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags), | |
767 | attrs->flags); | |
768 | } | |
769 | else | |
770 | { | |
771 | set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr), | |
772 | symtab_offset); | |
773 | set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms), | |
774 | nsyms); | |
775 | /* f_opthdr left as zero. */ | |
776 | set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags), | |
777 | attrs->flags); | |
778 | } | |
779 | ||
780 | return simple_object_internal_write (descriptor, 0, hdrbuf, | |
781 | sizeof (struct external_filehdr), | |
782 | errmsg, err); | |
783 | } | |
784 | ||
785 | /* Write out a XCOFF section header. */ | |
786 | ||
787 | static int | |
788 | simple_object_xcoff_write_scnhdr (simple_object_write *sobj, | |
789 | int descriptor, | |
790 | const char *name, size_t *name_offset, | |
791 | off_t scnhdr_offset, size_t scnsize, | |
792 | off_t offset, unsigned int align, | |
793 | const char **errmsg, int *err) | |
794 | { | |
795 | struct simple_object_xcoff_read *ocr = | |
796 | (struct simple_object_xcoff_read *) sobj->data; | |
797 | int u64 = ocr->magic == U64_TOCMAGIC; | |
798 | void (*set_32) (unsigned char *, unsigned int); | |
799 | void (*set_64) (unsigned char *, unsigned int); | |
800 | unsigned char hdrbuf[sizeof (struct external_scnhdr)]; | |
801 | unsigned char *hdr; | |
802 | size_t namelen; | |
803 | unsigned int flags; | |
804 | ||
805 | set_32 = simple_object_set_big_32; | |
806 | set_64 = simple_object_set_big_32; | |
807 | ||
808 | memset (hdrbuf, 0, sizeof hdrbuf); | |
809 | hdr = &hdrbuf[0]; | |
810 | ||
811 | namelen = strlen (name); | |
812 | if (namelen <= SCNNMLEN) | |
813 | strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name), | |
814 | name, SCNNMLEN); | |
815 | else | |
816 | { | |
817 | snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name), | |
818 | SCNNMLEN, "/%lu", (unsigned long) *name_offset); | |
819 | *name_offset += namelen + 1; | |
820 | } | |
821 | ||
822 | /* s_paddr left as zero. */ | |
823 | /* s_vaddr left as zero. */ | |
824 | if (u64) | |
825 | { | |
826 | set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_size), | |
827 | scnsize); | |
828 | set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_scnptr), | |
829 | offset); | |
830 | } | |
831 | else | |
832 | { | |
833 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_size), | |
834 | scnsize); | |
835 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_scnptr), | |
836 | offset); | |
837 | } | |
838 | /* s_relptr left as zero. */ | |
839 | /* s_lnnoptr left as zero. */ | |
840 | /* s_nreloc left as zero. */ | |
841 | /* s_nlnno left as zero. */ | |
842 | flags = STYP_DATA; | |
843 | if (align > 13) | |
844 | align = 13; | |
845 | if (u64) | |
846 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_flags), flags); | |
847 | else | |
848 | set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_flags), flags); | |
849 | ||
850 | return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf, | |
851 | u64 ? SCNHSZ64 : SCNHSZ32, | |
852 | errmsg, err); | |
853 | } | |
854 | ||
855 | /* Write out a complete XCOFF file. */ | |
856 | ||
857 | static const char * | |
858 | simple_object_xcoff_write_to_file (simple_object_write *sobj, int descriptor, | |
859 | int *err) | |
860 | { | |
861 | struct simple_object_xcoff_read *ocr = | |
862 | (struct simple_object_xcoff_read *) sobj->data; | |
863 | int u64 = ocr->magic == U64_TOCMAGIC; | |
864 | unsigned int nscns, secnum; | |
865 | simple_object_write_section *section; | |
866 | off_t scnhdr_offset; | |
867 | size_t symtab_offset; | |
868 | off_t secsym_offset; | |
869 | unsigned int nsyms; | |
870 | size_t offset; | |
871 | size_t name_offset; | |
872 | const char *errmsg; | |
873 | unsigned char strsizebuf[4]; | |
874 | /* The interface doesn't give us access to the name of the input file | |
875 | yet. We want to use its basename for the FILE symbol. This is | |
876 | what 'gas' uses when told to assemble from stdin. */ | |
877 | const char *source_filename = "fake"; | |
878 | size_t sflen; | |
879 | union | |
880 | { | |
881 | struct external_syment sym; | |
882 | union external_auxent aux; | |
883 | } syms[2]; | |
884 | void (*set_16) (unsigned char *, unsigned short); | |
885 | void (*set_32) (unsigned char *, unsigned int); | |
886 | ||
887 | set_16 = simple_object_set_big_16; | |
888 | set_32 = simple_object_set_big_32; | |
889 | ||
890 | nscns = 0; | |
891 | for (section = sobj->sections; section != NULL; section = section->next) | |
892 | ++nscns; | |
893 | ||
894 | scnhdr_offset = sizeof (struct external_filehdr) - (u64 ? 4 : 0); | |
895 | offset = scnhdr_offset + nscns * (u64 ? SCNHSZ64 : SCNHSZ32); | |
896 | name_offset = 4; | |
897 | for (section = sobj->sections; section != NULL; section = section->next) | |
898 | { | |
899 | size_t mask; | |
900 | size_t new_offset; | |
901 | size_t scnsize; | |
902 | struct simple_object_write_section_buffer *buffer; | |
903 | ||
904 | mask = (1U << section->align) - 1; | |
905 | new_offset = offset & mask; | |
906 | new_offset &= ~ mask; | |
907 | while (new_offset > offset) | |
908 | { | |
909 | unsigned char zeroes[16]; | |
910 | size_t write; | |
911 | ||
912 | memset (zeroes, 0, sizeof zeroes); | |
913 | write = new_offset - offset; | |
914 | if (write > sizeof zeroes) | |
915 | write = sizeof zeroes; | |
916 | if (!simple_object_internal_write (descriptor, offset, zeroes, write, | |
917 | &errmsg, err)) | |
918 | return errmsg; | |
919 | } | |
920 | ||
921 | scnsize = 0; | |
922 | for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) | |
923 | { | |
924 | if (!simple_object_internal_write (descriptor, offset + scnsize, | |
925 | ((const unsigned char *) | |
926 | buffer->buffer), | |
927 | buffer->size, &errmsg, err)) | |
928 | return errmsg; | |
929 | scnsize += buffer->size; | |
930 | } | |
931 | ||
932 | if (!simple_object_xcoff_write_scnhdr (sobj, descriptor, section->name, | |
933 | &name_offset, scnhdr_offset, | |
934 | scnsize, offset, section->align, | |
935 | &errmsg, err)) | |
936 | return errmsg; | |
937 | ||
938 | scnhdr_offset += u64 ? SCNHSZ64 : SCNHSZ32; | |
939 | offset += scnsize; | |
940 | } | |
941 | ||
942 | /* Symbol table is always half-word aligned. */ | |
943 | offset += (offset & 1); | |
944 | /* There is a file symbol and a section symbol per section, | |
945 | and each of these has a single auxiliary symbol following. */ | |
946 | nsyms = 2 * (nscns + 1); | |
947 | symtab_offset = offset; | |
948 | /* Advance across space reserved for symbol table to locate | |
949 | start of string table. */ | |
950 | offset += nsyms * SYMESZ; | |
951 | ||
952 | /* Write out file symbol. */ | |
953 | memset (&syms[0], 0, sizeof (syms)); | |
954 | if (!u64) | |
955 | strcpy ((char *)&syms[0].sym.u.xcoff32.n.n_name[0], ".file"); | |
956 | set_16 (&syms[0].sym.n_scnum[0], N_DEBUG); | |
957 | set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE); | |
958 | syms[0].sym.n_sclass[0] = C_FILE; | |
959 | syms[0].sym.n_numaux[0] = 1; | |
960 | /* The name need not be nul-terminated if it fits into the x_fname field | |
961 | directly, but must be if it has to be placed into the string table. */ | |
962 | sflen = strlen (source_filename); | |
963 | if (sflen <= FILNMLEN) | |
964 | memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen); | |
965 | else | |
966 | { | |
967 | set_32 (&syms[1].aux.x_file._x.x_offset[0], name_offset); | |
968 | if (!simple_object_internal_write (descriptor, offset + name_offset, | |
969 | ((const unsigned char *) | |
970 | source_filename), | |
971 | sflen + 1, &errmsg, err)) | |
972 | return errmsg; | |
973 | name_offset += strlen (source_filename) + 1; | |
974 | } | |
975 | if (!simple_object_internal_write (descriptor, symtab_offset, | |
976 | (const unsigned char *) &syms[0], | |
977 | sizeof (syms), &errmsg, err)) | |
978 | return errmsg; | |
979 | ||
980 | /* Write the string table length, followed by the strings and section | |
981 | symbols in step with each other. */ | |
982 | set_32 (strsizebuf, name_offset); | |
983 | if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4, | |
984 | &errmsg, err)) | |
985 | return errmsg; | |
986 | ||
987 | name_offset = 4; | |
988 | secsym_offset = symtab_offset + sizeof (syms); | |
989 | memset (&syms[0], 0, sizeof (syms)); | |
990 | set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE); | |
991 | syms[0].sym.n_sclass[0] = C_STAT; | |
992 | syms[0].sym.n_numaux[0] = 1; | |
993 | secnum = 1; | |
994 | ||
995 | for (section = sobj->sections; section != NULL; section = section->next) | |
996 | { | |
997 | size_t namelen; | |
998 | size_t scnsize; | |
999 | struct simple_object_write_section_buffer *buffer; | |
1000 | ||
1001 | namelen = strlen (section->name); | |
1002 | set_16 (&syms[0].sym.n_scnum[0], secnum++); | |
1003 | scnsize = 0; | |
1004 | for (buffer = section->buffers; buffer != NULL; buffer = buffer->next) | |
1005 | scnsize += buffer->size; | |
1006 | set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize); | |
1007 | if (namelen > SCNNMLEN) | |
1008 | { | |
1009 | set_32 (&syms[0].sym.u.xcoff32.n.n.n_zeroes[0], 0); | |
1010 | set_32 (&syms[0].sym.u.xcoff32.n.n.n_offset[0], name_offset); | |
1011 | if (!simple_object_internal_write (descriptor, offset + name_offset, | |
1012 | ((const unsigned char *) | |
1013 | section->name), | |
1014 | namelen + 1, &errmsg, err)) | |
1015 | return errmsg; | |
1016 | name_offset += namelen + 1; | |
1017 | } | |
1018 | else | |
1019 | { | |
1020 | memcpy (&syms[0].sym.u.xcoff32.n.n_name[0], section->name, | |
1021 | strlen (section->name)); | |
1022 | memset (&syms[0].sym.u.xcoff32.n.n_name[strlen (section->name)], 0, | |
1023 | N_SYMNMLEN - strlen (section->name)); | |
1024 | } | |
1025 | ||
1026 | if (!simple_object_internal_write (descriptor, secsym_offset, | |
1027 | (const unsigned char *) &syms[0], | |
1028 | sizeof (syms), &errmsg, err)) | |
1029 | return errmsg; | |
1030 | secsym_offset += sizeof (syms); | |
1031 | } | |
1032 | ||
1033 | if (!simple_object_xcoff_write_filehdr (sobj, descriptor, nscns, | |
1034 | symtab_offset, nsyms, &errmsg, err)) | |
1035 | return errmsg; | |
1036 | ||
1037 | return NULL; | |
1038 | } | |
1039 | ||
1040 | /* Release the private data for an simple_object_write structure. */ | |
1041 | ||
1042 | static void | |
1043 | simple_object_xcoff_release_write (void *data) | |
1044 | { | |
1045 | XDELETE (data); | |
1046 | } | |
1047 | ||
1048 | /* The XCOFF functions. */ | |
1049 | ||
1050 | const struct simple_object_functions simple_object_xcoff_functions = | |
1051 | { | |
1052 | simple_object_xcoff_match, | |
1053 | simple_object_xcoff_find_sections, | |
1054 | simple_object_xcoff_fetch_attributes, | |
1055 | simple_object_xcoff_release_read, | |
1056 | simple_object_xcoff_attributes_merge, | |
1057 | simple_object_xcoff_release_attributes, | |
1058 | simple_object_xcoff_start_write, | |
1059 | simple_object_xcoff_write_to_file, | |
f8ad2513 NC |
1060 | simple_object_xcoff_release_write, |
1061 | NULL | |
07a8e9f0 | 1062 | }; |