1 | /* simple-object-coff.c -- routines to manipulate XCOFF object files. |
2 | Copyright (C) 2013-2024 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_EXT (2) |
259 | #define C_STAT (3) |
260 | #define C_FILE (103) |
261 | #define C_HIDEXT (107) |
262 | |
263 | #define XTY_SD (1) /* section definition */ |
264 | |
265 | #define XMC_XO (7) /* extended operation */ |
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 [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 (buf: 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, buffer: hdrbuf, size: 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 (descriptor: sobj->descriptor, offset: strtab_offset, |
377 | buffer: strsizebuf, size: 4, errmsg, err)) |
378 | return NULL; |
379 | strsize = simple_object_fetch_big_32 (buf: strsizebuf); |
380 | strtab = XNEWVEC (char, strsize); |
381 | if (!simple_object_internal_read (descriptor: sobj->descriptor, offset: strtab_offset, |
382 | buffer: (unsigned char *) strtab, size: 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; |
407 | unsigned short (*fetch_16) (const unsigned char *); |
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; |
413 | struct external_syment *symtab = NULL; |
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 (descriptor: sobj->descriptor, |
419 | offset: sobj->offset + ocr->scnhdr_offset, |
420 | buffer: scnbuf, size: scnhdr_size * ocr->nscns, errmsg: &errmsg, |
421 | err)) |
422 | { |
423 | XDELETEVEC (scnbuf); |
424 | return errmsg; |
425 | } |
426 | |
427 | fetch_16 = simple_object_fetch_big_16; |
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; |
441 | off_t size; |
442 | |
443 | scnhdr = scnbuf + i * scnhdr_size; |
444 | scnname = scnhdr + offsetof (struct external_scnhdr, s_name); |
445 | memcpy (dest: namebuf, src: 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 (nptr: namebuf + 1, endptr: &end, base: 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: &strtab_size, |
462 | errmsg: &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 | |
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 (descriptor: sobj->descriptor, |
513 | offset: sobj->offset + ocr->symptr, |
514 | buffer: (unsigned char *) symtab, |
515 | size: ocr->nsyms * SYMESZ, |
516 | errmsg: &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: &strtab_size, |
568 | errmsg: &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 (s1: n_name, s2: ".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 | /* 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 | |
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 | |
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))); |
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); |
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 (s: hdr, c: 0, n: 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, offset: 0, buffer: hdrbuf, |
781 | size: 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 (s: hdrbuf, c: 0, n: sizeof hdrbuf); |
809 | hdr = &hdrbuf[0]; |
810 | |
811 | namelen = strlen (s: name); |
812 | if (namelen <= SCNNMLEN) |
813 | strncpy (dest: (char *) hdr + offsetof (struct external_scnhdr, s_name), |
814 | src: name, SCNNMLEN); |
815 | else |
816 | { |
817 | snprintf (s: (char *) hdr + offsetof (struct external_scnhdr, s_name), |
818 | SCNNMLEN, format: "/%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, offset: scnhdr_offset, buffer: hdrbuf, |
851 | size: 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 (s: zeroes, c: 0, n: sizeof zeroes); |
913 | write = new_offset - offset; |
914 | if (write > sizeof zeroes) |
915 | write = sizeof zeroes; |
916 | if (!simple_object_internal_write (descriptor, offset, buffer: zeroes, size: write, |
917 | errmsg: &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: offset + scnsize, |
925 | buffer: ((const unsigned char *) |
926 | buffer->buffer), |
927 | size: buffer->size, errmsg: &errmsg, err)) |
928 | return errmsg; |
929 | scnsize += buffer->size; |
930 | } |
931 | |
932 | if (!simple_object_xcoff_write_scnhdr (sobj, descriptor, name: section->name, |
933 | name_offset: &name_offset, scnhdr_offset, |
934 | scnsize, offset, align: section->align, |
935 | errmsg: &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 (s: &syms[0], c: 0, n: sizeof (syms)); |
954 | if (!u64) |
955 | strcpy (dest: (char *)&syms[0].sym.u.xcoff32.n.n_name[0], src: ".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 (s: source_filename); |
963 | if (sflen <= FILNMLEN) |
964 | memcpy (dest: &syms[1].aux.x_file.x_fname[0], src: source_filename, n: 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: offset + name_offset, |
969 | buffer: ((const unsigned char *) |
970 | source_filename), |
971 | size: sflen + 1, errmsg: &errmsg, err)) |
972 | return errmsg; |
973 | name_offset += strlen (s: source_filename) + 1; |
974 | } |
975 | if (!simple_object_internal_write (descriptor, offset: symtab_offset, |
976 | buffer: (const unsigned char *) &syms[0], |
977 | size: sizeof (syms), errmsg: &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, buffer: strsizebuf, size: 4, |
984 | errmsg: &errmsg, err)) |
985 | return errmsg; |
986 | |
987 | name_offset = 4; |
988 | secsym_offset = symtab_offset + sizeof (syms); |
989 | memset (s: &syms[0], c: 0, n: 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 (s: 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: offset + name_offset, |
1012 | buffer: ((const unsigned char *) |
1013 | section->name), |
1014 | size: namelen + 1, errmsg: &errmsg, err)) |
1015 | return errmsg; |
1016 | name_offset += namelen + 1; |
1017 | } |
1018 | else |
1019 | { |
1020 | memcpy (dest: &syms[0].sym.u.xcoff32.n.n_name[0], src: section->name, |
1021 | n: strlen (s: section->name)); |
1022 | memset (s: &syms[0].sym.u.xcoff32.n.n_name[strlen (s: section->name)], c: 0, |
1023 | N_SYMNMLEN - strlen (s: section->name)); |
1024 | } |
1025 | |
1026 | if (!simple_object_internal_write (descriptor, offset: secsym_offset, |
1027 | buffer: (const unsigned char *) &syms[0], |
1028 | size: sizeof (syms), errmsg: &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: &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 | .match: simple_object_xcoff_match, |
1053 | .find_sections: simple_object_xcoff_find_sections, |
1054 | .fetch_attributes: simple_object_xcoff_fetch_attributes, |
1055 | .release_read: simple_object_xcoff_release_read, |
1056 | .attributes_merge: simple_object_xcoff_attributes_merge, |
1057 | .release_attributes: simple_object_xcoff_release_attributes, |
1058 | .start_write: simple_object_xcoff_start_write, |
1059 | .write_to_file: simple_object_xcoff_write_to_file, |
1060 | .release_write: simple_object_xcoff_release_write, |
1061 | NULL |
1062 | }; |
1063 | |