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