Import GCC-8 to a new vendor branch
[dragonfly.git] / contrib / gcc-8.0 / libiberty / simple-object-xcoff.c
1 /* simple-object-coff.c -- routines to manipulate XCOFF object files.
2    Copyright (C) 2013-2018 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 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;
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 (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
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 (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
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                   /* 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 (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,
1060   simple_object_xcoff_release_write,
1061   NULL
1062 };