1/* simple-object-coff.c -- routines to manipulate XCOFF object files.
2   Copyright (C) 2013-2022 Free Software Foundation, Inc.
3   Written by Ian Lance Taylor, Google and David Edelsohn, IBM.
4
5This program is free software; you can redistribute it and/or modify it
6under the terms of the GNU General Public License as published by the
7Free Software Foundation; either version 2, or (at your option) any
8later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 51 Franklin Street - Fifth Floor,
18Boston, 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
49struct 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
87struct 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.  */
136struct 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
186union 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
269struct 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
287struct 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
297static void *
298simple_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
363static char *
364simple_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
394static const char *
395simple_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
664static void *
665simple_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
681static void
682simple_object_xcoff_release_read (void *data)
683{
684  XDELETE (data);
685}
686
687/* Compare two attributes structures.  */
688
689static const char *
690simple_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
707static void
708simple_object_xcoff_release_attributes (void *data)
709{
710  XDELETE (data);
711}
712
713/* Prepare to write out a file.  */
714
715static void *
716simple_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
733static int
734simple_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
787static int
788simple_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
857static const char *
858simple_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
1042static void
1043simple_object_xcoff_release_write (void *data)
1044{
1045  XDELETE (data);
1046}
1047
1048/* The XCOFF functions.  */
1049
1050const 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};
1063