1/* simple-object-coff.c -- routines to manipulate XCOFF object files.
2   Copyright 2013 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_STAT		(3)
259#define C_FILE		(103)
260
261/* Private data for an simple_object_read.  */
262
263struct simple_object_xcoff_read
264{
265  /* Magic number.  */
266  unsigned short magic;
267  /* Number of sections.  */
268  unsigned short nscns;
269  /* File offset of symbol table.  */
270  off_t symptr;
271  /* Number of symbol table entries.  */
272  unsigned int nsyms;
273  /* Flags.  */
274  unsigned short flags;
275  /* Offset of section headers in file.  */
276  off_t scnhdr_offset;
277};
278
279/* Private data for an simple_object_attributes.  */
280
281struct simple_object_xcoff_attributes
282{
283  /* Magic number.  */
284  unsigned short magic;
285  /* Flags.  */
286  unsigned short flags;
287};
288
289/* See if we have a XCOFF file.  */
290
291static void *
292simple_object_xcoff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
293			   int descriptor, off_t offset,
294			   const char *segment_name ATTRIBUTE_UNUSED,
295			   const char **errmsg, int *err)
296{
297  unsigned short magic;
298  unsigned short (*fetch_16) (const unsigned char *);
299  unsigned int (*fetch_32) (const unsigned char *);
300  ulong_type (*fetch_64) (const unsigned char *);
301  unsigned char hdrbuf[sizeof (struct external_filehdr)];
302  struct simple_object_xcoff_read *ocr;
303  int u64;
304
305  magic = simple_object_fetch_big_16 (header);
306
307  if (magic != U802TOCMAGIC && magic != U64_TOCMAGIC)
308    {
309      *errmsg = NULL;
310      *err = 0;
311      return NULL;
312    }
313
314  fetch_16 = simple_object_fetch_big_16;
315  fetch_32 = simple_object_fetch_big_32;
316  fetch_64 = simple_object_fetch_big_64;
317
318  if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof hdrbuf,
319				    errmsg, err))
320    return NULL;
321
322  u64 = magic == U64_TOCMAGIC;
323
324  ocr = XNEW (struct simple_object_xcoff_read);
325  ocr->magic = magic;
326  ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns));
327  if (u64)
328    {
329      ocr->symptr = fetch_64 (hdrbuf
330			      + offsetof (struct external_filehdr,
331					  u.xcoff64.f_symptr));
332      ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr,
333						u.xcoff64.f_nsyms));
334      ocr->scnhdr_offset = (sizeof (struct external_filehdr)
335			    + fetch_16 (hdrbuf + offsetof (struct external_filehdr,
336							   u.xcoff64.f_opthdr)));
337
338    }
339  else
340    {
341      ocr->symptr = fetch_32 (hdrbuf
342			      + offsetof (struct external_filehdr,
343					  u.xcoff32.f_symptr));
344      ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr,
345						u.xcoff32.f_nsyms));
346      ocr->scnhdr_offset = (sizeof (struct external_filehdr) - 4
347			    + fetch_16 (hdrbuf + offsetof (struct external_filehdr,
348							   u.xcoff32.f_opthdr)));
349
350    }
351
352  return (void *) ocr;
353}
354
355/* Read the string table in a XCOFF file.  */
356
357static char *
358simple_object_xcoff_read_strtab (simple_object_read *sobj, size_t *strtab_size,
359				 const char **errmsg, int *err)
360{
361  struct simple_object_xcoff_read *ocr =
362    (struct simple_object_xcoff_read *) sobj->data;
363  off_t strtab_offset;
364  unsigned char strsizebuf[4];
365  size_t strsize;
366  char *strtab;
367
368  strtab_offset = sobj->offset + ocr->symptr
369    + ocr->nsyms * SYMESZ;
370  if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
371				    strsizebuf, 4, errmsg, err))
372    return NULL;
373  strsize = simple_object_fetch_big_32 (strsizebuf);
374  strtab = XNEWVEC (char, strsize);
375  if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
376				    (unsigned char *) strtab, strsize, errmsg,
377				    err))
378    {
379      XDELETEVEC (strtab);
380      return NULL;
381    }
382  *strtab_size = strsize;
383  return strtab;
384}
385
386/* Find all sections in a XCOFF file.  */
387
388static const char *
389simple_object_xcoff_find_sections (simple_object_read *sobj,
390				  int (*pfn) (void *, const char *,
391					      off_t offset, off_t length),
392				  void *data,
393				  int *err)
394{
395  struct simple_object_xcoff_read *ocr =
396    (struct simple_object_xcoff_read *) sobj->data;
397  int u64 = ocr->magic == U64_TOCMAGIC;
398  size_t scnhdr_size;
399  unsigned char *scnbuf;
400  const char *errmsg;
401  unsigned int (*fetch_32) (const unsigned char *);
402  ulong_type (*fetch_64) (const unsigned char *);
403  unsigned int nscns;
404  char *strtab;
405  size_t strtab_size;
406  unsigned int i;
407
408  scnhdr_size = u64 ? SCNHSZ64 : SCNHSZ32;
409  scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns);
410  if (!simple_object_internal_read (sobj->descriptor,
411				    sobj->offset + ocr->scnhdr_offset,
412				    scnbuf, scnhdr_size * ocr->nscns, &errmsg,
413				    err))
414    {
415      XDELETEVEC (scnbuf);
416      return errmsg;
417    }
418
419  fetch_32 = simple_object_fetch_big_32;
420  fetch_64 = simple_object_fetch_big_64;
421
422  nscns = ocr->nscns;
423  strtab = NULL;
424  strtab_size = 0;
425  for (i = 0; i < nscns; ++i)
426    {
427      unsigned char *scnhdr;
428      unsigned char *scnname;
429      char namebuf[SCNNMLEN + 1];
430      char *name;
431      off_t scnptr;
432      unsigned int size;
433
434      scnhdr = scnbuf + i * scnhdr_size;
435      scnname = scnhdr + offsetof (struct external_scnhdr, s_name);
436      memcpy (namebuf, scnname, SCNNMLEN);
437      namebuf[SCNNMLEN] = '\0';
438      name = &namebuf[0];
439      if (namebuf[0] == '/')
440	{
441	  size_t strindex;
442	  char *end;
443
444	  strindex = strtol (namebuf + 1, &end, 10);
445	  if (*end == '\0')
446	    {
447	      /* The real section name is found in the string
448		 table.  */
449	      if (strtab == NULL)
450		{
451		  strtab = simple_object_xcoff_read_strtab (sobj,
452							   &strtab_size,
453							   &errmsg, err);
454		  if (strtab == NULL)
455		    {
456		      XDELETEVEC (scnbuf);
457		      return errmsg;
458		    }
459		}
460
461	      if (strindex < 4 || strindex >= strtab_size)
462		{
463		  XDELETEVEC (strtab);
464		  XDELETEVEC (scnbuf);
465		  *err = 0;
466		  return "section string index out of range";
467		}
468
469	      name = strtab + strindex;
470	    }
471	}
472
473      if (u64)
474	{
475	  scnptr = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
476						u.xcoff64.s_scnptr));
477	  size = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
478					      u.xcoff64.s_size));
479	}
480      else
481	{
482	  scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
483						u.xcoff32.s_scnptr));
484	  size = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
485					      u.xcoff32.s_size));
486	}
487
488      if (!(*pfn) (data, name, scnptr, size))
489	break;
490    }
491
492  if (strtab != NULL)
493    XDELETEVEC (strtab);
494  XDELETEVEC (scnbuf);
495
496  return NULL;
497}
498
499/* Fetch the attributes for an simple_object_read.  */
500
501static void *
502simple_object_xcoff_fetch_attributes (simple_object_read *sobj,
503				     const char **errmsg ATTRIBUTE_UNUSED,
504				     int *err ATTRIBUTE_UNUSED)
505{
506  struct simple_object_xcoff_read *ocr =
507    (struct simple_object_xcoff_read *) sobj->data;
508  struct simple_object_xcoff_attributes *ret;
509
510  ret = XNEW (struct simple_object_xcoff_attributes);
511  ret->magic = ocr->magic;
512  ret->flags = ocr->flags;
513  return ret;
514}
515
516/* Release the private data for an simple_object_read.  */
517
518static void
519simple_object_xcoff_release_read (void *data)
520{
521  XDELETE (data);
522}
523
524/* Compare two attributes structures.  */
525
526static const char *
527simple_object_xcoff_attributes_merge (void *todata, void *fromdata, int *err)
528{
529  struct simple_object_xcoff_attributes *to =
530    (struct simple_object_xcoff_attributes *) todata;
531  struct simple_object_xcoff_attributes *from =
532    (struct simple_object_xcoff_attributes *) fromdata;
533
534  if (to->magic != from->magic)
535    {
536      *err = 0;
537      return "XCOFF object format mismatch";
538    }
539  return NULL;
540}
541
542/* Release the private data for an attributes structure.  */
543
544static void
545simple_object_xcoff_release_attributes (void *data)
546{
547  XDELETE (data);
548}
549
550/* Prepare to write out a file.  */
551
552static void *
553simple_object_xcoff_start_write (void *attributes_data,
554				const char **errmsg ATTRIBUTE_UNUSED,
555				int *err ATTRIBUTE_UNUSED)
556{
557  struct simple_object_xcoff_attributes *attrs =
558    (struct simple_object_xcoff_attributes *) attributes_data;
559  struct simple_object_xcoff_attributes *ret;
560
561  /* We're just going to record the attributes, but we need to make a
562     copy because the user may delete them.  */
563  ret = XNEW (struct simple_object_xcoff_attributes);
564  *ret = *attrs;
565  return ret;
566}
567
568/* Write out a XCOFF filehdr.  */
569
570static int
571simple_object_xcoff_write_filehdr (simple_object_write *sobj, int descriptor,
572				  unsigned int nscns, size_t symtab_offset,
573				  unsigned int nsyms, const char **errmsg,
574				  int *err)
575{
576  struct simple_object_xcoff_attributes *attrs =
577    (struct simple_object_xcoff_attributes *) sobj->data;
578  int u64 = attrs->magic == U64_TOCMAGIC;
579  unsigned char hdrbuf[sizeof (struct external_filehdr)];
580  unsigned char *hdr;
581  void (*set_16) (unsigned char *, unsigned short);
582  void (*set_32) (unsigned char *, unsigned int);
583  void (*set_64) (unsigned char *, ulong_type);
584
585  hdr = &hdrbuf[0];
586
587  set_16 = simple_object_set_big_16;
588  set_32 = simple_object_set_big_32;
589  set_64 = simple_object_set_big_64;
590
591  memset (hdr, 0, sizeof (struct external_filehdr));
592
593  set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic);
594  set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns);
595  /* f_timdat left as zero.  */
596  if (u64)
597    {
598      set_64 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr),
599	      symtab_offset);
600      set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms),
601	      nsyms);
602      /* f_opthdr left as zero.  */
603      set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags),
604	      attrs->flags);
605    }
606  else
607    {
608      set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr),
609	      symtab_offset);
610      set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms),
611	      nsyms);
612      /* f_opthdr left as zero.  */
613      set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags),
614	      attrs->flags);
615    }
616
617  return simple_object_internal_write (descriptor, 0, hdrbuf,
618				       sizeof (struct external_filehdr),
619				       errmsg, err);
620}
621
622/* Write out a XCOFF section header.  */
623
624static int
625simple_object_xcoff_write_scnhdr (simple_object_write *sobj,
626				  int descriptor,
627				  const char *name, size_t *name_offset,
628				  off_t scnhdr_offset, size_t scnsize,
629				  off_t offset, unsigned int align,
630				  const char **errmsg, int *err)
631{
632  struct simple_object_xcoff_read *ocr =
633    (struct simple_object_xcoff_read *) sobj->data;
634  int u64 = ocr->magic == U64_TOCMAGIC;
635  void (*set_32) (unsigned char *, unsigned int);
636  void (*set_64) (unsigned char *, unsigned int);
637  unsigned char hdrbuf[sizeof (struct external_scnhdr)];
638  unsigned char *hdr;
639  size_t namelen;
640  unsigned int flags;
641
642  set_32 = simple_object_set_big_32;
643  set_64 = simple_object_set_big_32;
644
645  memset (hdrbuf, 0, sizeof hdrbuf);
646  hdr = &hdrbuf[0];
647
648  namelen = strlen (name);
649  if (namelen <= SCNNMLEN)
650    strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name),
651	     name, SCNNMLEN);
652  else
653    {
654      snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name),
655		SCNNMLEN, "/%lu", (unsigned long) *name_offset);
656      *name_offset += namelen + 1;
657    }
658
659  /* s_paddr left as zero.  */
660  /* s_vaddr left as zero.  */
661  if (u64)
662    {
663      set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_size),
664	      scnsize);
665      set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_scnptr),
666	      offset);
667    }
668  else
669    {
670      set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_size),
671	      scnsize);
672      set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_scnptr),
673	      offset);
674    }
675  /* s_relptr left as zero.  */
676  /* s_lnnoptr left as zero.  */
677  /* s_nreloc left as zero.  */
678  /* s_nlnno left as zero.  */
679  flags = STYP_DATA;
680  if (align > 13)
681    align = 13;
682  if (u64)
683    set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_flags), flags);
684  else
685    set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_flags), flags);
686
687  return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf,
688				       u64 ? SCNHSZ64 : SCNHSZ32,
689				       errmsg, err);
690}
691
692/* Write out a complete XCOFF file.  */
693
694static const char *
695simple_object_xcoff_write_to_file (simple_object_write *sobj, int descriptor,
696				  int *err)
697{
698  struct simple_object_xcoff_read *ocr =
699    (struct simple_object_xcoff_read *) sobj->data;
700  int u64 = ocr->magic == U64_TOCMAGIC;
701  unsigned int nscns, secnum;
702  simple_object_write_section *section;
703  off_t scnhdr_offset;
704  size_t symtab_offset;
705  off_t secsym_offset;
706  unsigned int nsyms;
707  size_t offset;
708  size_t name_offset;
709  const char *errmsg;
710  unsigned char strsizebuf[4];
711  /* The interface doesn't give us access to the name of the input file
712     yet.  We want to use its basename for the FILE symbol.  This is
713     what 'gas' uses when told to assemble from stdin.  */
714  const char *source_filename = "fake";
715  size_t sflen;
716  union
717  {
718    struct external_syment sym;
719    union external_auxent aux;
720  } syms[2];
721  void (*set_16) (unsigned char *, unsigned short);
722  void (*set_32) (unsigned char *, unsigned int);
723
724  set_16 = simple_object_set_big_16;
725  set_32 = simple_object_set_big_32;
726
727  nscns = 0;
728  for (section = sobj->sections; section != NULL; section = section->next)
729    ++nscns;
730
731  scnhdr_offset = sizeof (struct external_filehdr) - (u64 ? 4 : 0);
732  offset = scnhdr_offset + nscns * (u64 ? SCNHSZ64 : SCNHSZ32);
733  name_offset = 4;
734  for (section = sobj->sections; section != NULL; section = section->next)
735    {
736      size_t mask;
737      size_t new_offset;
738      size_t scnsize;
739      struct simple_object_write_section_buffer *buffer;
740
741      mask = (1U << section->align) - 1;
742      new_offset = offset & mask;
743      new_offset &= ~ mask;
744      while (new_offset > offset)
745	{
746	  unsigned char zeroes[16];
747	  size_t write;
748
749	  memset (zeroes, 0, sizeof zeroes);
750	  write = new_offset - offset;
751	  if (write > sizeof zeroes)
752	    write = sizeof zeroes;
753	  if (!simple_object_internal_write (descriptor, offset, zeroes, write,
754					     &errmsg, err))
755	    return errmsg;
756	}
757
758      scnsize = 0;
759      for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
760	{
761	  if (!simple_object_internal_write (descriptor, offset + scnsize,
762					     ((const unsigned char *)
763					      buffer->buffer),
764					     buffer->size, &errmsg, err))
765	    return errmsg;
766	  scnsize += buffer->size;
767	}
768
769      if (!simple_object_xcoff_write_scnhdr (sobj, descriptor, section->name,
770					    &name_offset, scnhdr_offset,
771					    scnsize, offset, section->align,
772					    &errmsg, err))
773	return errmsg;
774
775      scnhdr_offset += u64 ? SCNHSZ64 : SCNHSZ32;
776      offset += scnsize;
777    }
778
779  /* Symbol table is always half-word aligned.  */
780  offset += (offset & 1);
781  /* There is a file symbol and a section symbol per section,
782     and each of these has a single auxiliary symbol following.  */
783  nsyms = 2 * (nscns + 1);
784  symtab_offset = offset;
785  /* Advance across space reserved for symbol table to locate
786     start of string table.  */
787  offset += nsyms * SYMESZ;
788
789  /* Write out file symbol.  */
790  memset (&syms[0], 0, sizeof (syms));
791  if (!u64)
792    strcpy ((char *)&syms[0].sym.u.xcoff32.n.n_name[0], ".file");
793  set_16 (&syms[0].sym.n_scnum[0], N_DEBUG);
794  set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE);
795  syms[0].sym.n_sclass[0] = C_FILE;
796  syms[0].sym.n_numaux[0] = 1;
797  /* The name need not be nul-terminated if it fits into the x_fname field
798     directly, but must be if it has to be placed into the string table.  */
799  sflen = strlen (source_filename);
800  if (sflen <= FILNMLEN)
801    memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen);
802  else
803    {
804      set_32 (&syms[1].aux.x_file._x.x_offset[0], name_offset);
805      if (!simple_object_internal_write (descriptor, offset + name_offset,
806					 ((const unsigned char *)
807					  source_filename),
808					 sflen + 1, &errmsg, err))
809	return errmsg;
810      name_offset += strlen (source_filename) + 1;
811    }
812  if (!simple_object_internal_write (descriptor, symtab_offset,
813				     (const unsigned char *) &syms[0],
814				     sizeof (syms), &errmsg, err))
815    return errmsg;
816
817  /* Write the string table length, followed by the strings and section
818     symbols in step with each other.  */
819  set_32 (strsizebuf, name_offset);
820  if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4,
821				     &errmsg, err))
822    return errmsg;
823
824  name_offset = 4;
825  secsym_offset = symtab_offset + sizeof (syms);
826  memset (&syms[0], 0, sizeof (syms));
827  set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE);
828  syms[0].sym.n_sclass[0] = C_STAT;
829  syms[0].sym.n_numaux[0] = 1;
830  secnum = 1;
831
832  for (section = sobj->sections; section != NULL; section = section->next)
833    {
834      size_t namelen;
835      size_t scnsize;
836      struct simple_object_write_section_buffer *buffer;
837
838      namelen = strlen (section->name);
839      set_16 (&syms[0].sym.n_scnum[0], secnum++);
840      scnsize = 0;
841      for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
842	scnsize += buffer->size;
843      set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
844      if (namelen > SCNNMLEN)
845	{
846	  set_32 (&syms[0].sym.u.xcoff32.n.n.n_zeroes[0], 0);
847	  set_32 (&syms[0].sym.u.xcoff32.n.n.n_offset[0], name_offset);
848	  if (!simple_object_internal_write (descriptor, offset + name_offset,
849					     ((const unsigned char *)
850					      section->name),
851					     namelen + 1, &errmsg, err))
852	    return errmsg;
853	  name_offset += namelen + 1;
854	}
855      else
856	{
857	  memcpy (&syms[0].sym.u.xcoff32.n.n_name[0], section->name,
858		  strlen (section->name));
859	  memset (&syms[0].sym.u.xcoff32.n.n_name[strlen (section->name)], 0,
860		  N_SYMNMLEN - strlen (section->name));
861	}
862
863      if (!simple_object_internal_write (descriptor, secsym_offset,
864					 (const unsigned char *) &syms[0],
865					 sizeof (syms), &errmsg, err))
866	return errmsg;
867      secsym_offset += sizeof (syms);
868    }
869
870  if (!simple_object_xcoff_write_filehdr (sobj, descriptor, nscns,
871					 symtab_offset, nsyms, &errmsg, err))
872    return errmsg;
873
874  return NULL;
875}
876
877/* Release the private data for an simple_object_write structure.  */
878
879static void
880simple_object_xcoff_release_write (void *data)
881{
882  XDELETE (data);
883}
884
885/* The XCOFF functions.  */
886
887const struct simple_object_functions simple_object_xcoff_functions =
888{
889  simple_object_xcoff_match,
890  simple_object_xcoff_find_sections,
891  simple_object_xcoff_fetch_attributes,
892  simple_object_xcoff_release_read,
893  simple_object_xcoff_attributes_merge,
894  simple_object_xcoff_release_attributes,
895  simple_object_xcoff_start_write,
896  simple_object_xcoff_write_to_file,
897  simple_object_xcoff_release_write
898};
899