(root)/
gcc-13.2.0/
libiberty/
simple-object-xcoff.c
       1  /* simple-object-coff.c -- routines to manipulate XCOFF object files.
       2     Copyright (C) 2013-2023 Free Software Foundation, Inc.
       3     Written by Ian Lance Taylor, Google and David Edelsohn, IBM.
       4  
       5  This program is free software; you can redistribute it and/or modify it
       6  under the terms of the GNU General Public License as published by the
       7  Free Software Foundation; either version 2, or (at your option) any
       8  later version.
       9  
      10  This program is distributed in the hope that it will be useful,
      11  but WITHOUT ANY WARRANTY; without even the implied warranty of
      12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13  GNU General Public License for more details.
      14  
      15  You should have received a copy of the GNU General Public License
      16  along with this program; if not, write to the Free Software
      17  Foundation, 51 Franklin Street - Fifth Floor,
      18  Boston, MA 02110-1301, USA.  */
      19  
      20  #include "config.h"
      21  #include "libiberty.h"
      22  #include "simple-object.h"
      23  
      24  #include <errno.h>
      25  #include <stddef.h>
      26  
      27  #ifdef HAVE_STDLIB_H
      28  #include <stdlib.h>
      29  #endif
      30  
      31  #ifdef HAVE_STDINT_H
      32  #include <stdint.h>
      33  #endif
      34  
      35  #ifdef HAVE_STRING_H
      36  #include <string.h>
      37  #endif
      38  
      39  #ifdef HAVE_INTTYPES_H
      40  #include <inttypes.h>
      41  #endif
      42  
      43  #include "simple-object-common.h"
      44  
      45  /* XCOFF structures and constants.  */
      46  
      47  /* XCOFF file header.  */
      48  
      49  struct external_filehdr
      50  {
      51    unsigned char f_magic[2];	/* magic number			*/
      52    unsigned char f_nscns[2];	/* number of sections		*/
      53    unsigned char f_timdat[4];	/* time & date stamp		*/
      54    union
      55    {
      56      struct
      57      {
      58        unsigned char f_symptr[4];	/* file pointer to symtab	*/
      59        unsigned char f_nsyms[4];	/* number of symtab entries	*/
      60        unsigned char f_opthdr[2];	/* sizeof(optional hdr)		*/
      61        unsigned char f_flags[2];	/* flags			*/
      62      } xcoff32;
      63      struct
      64      {
      65        unsigned char f_symptr[8];	/* file pointer to symtab	*/
      66        unsigned char f_opthdr[2];	/* sizeof(optional hdr)		*/
      67        unsigned char f_flags[2];	/* flags			*/
      68        unsigned char f_nsyms[4];	/* number of symtab entries	*/
      69      } xcoff64;
      70    } u;
      71  };
      72  
      73  /* Bits for filehdr f_flags field.  */
      74  
      75  #define F_EXEC			(0x0002)
      76  
      77  /* The known values of f_magic in an XCOFF file header.  */
      78  
      79  #define U802WRMAGIC 0730        /* Writeable text segments.  */
      80  #define U802ROMAGIC 0735        /* Readonly sharable text segments.  */
      81  #define U802TOCMAGIC 0737       /* Readonly text segments and TOC.  */
      82  #define U803XTOCMAGIC 0757      /* Aix 4.3 64-bit XCOFF.  */
      83  #define U64_TOCMAGIC 0767       /* AIX 5+ 64-bit XCOFF.  */
      84  
      85  /* XCOFF section header.  */
      86  
      87  struct external_scnhdr
      88  {
      89    unsigned char s_name[8];	/* section name				*/
      90    union
      91    {
      92      struct
      93      {
      94        unsigned char s_paddr[4];	/* physical address, aliased s_nlib 	*/
      95        unsigned char s_vaddr[4];	/* virtual address			*/
      96        unsigned char s_size[4];	/* section size				*/
      97        unsigned char s_scnptr[4];	/* file ptr to raw data for section */
      98        unsigned char s_relptr[4];	/* file ptr to relocation	*/
      99        unsigned char s_lnnoptr[4];	/* file ptr to line numbers	*/
     100        unsigned char s_nreloc[2];	/* number of relocation entries	*/
     101        unsigned char s_nlnno[2];	/* number of line number entries	*/
     102        unsigned char s_flags[4];	/* flags				*/
     103      } xcoff32;
     104      struct
     105      {
     106        unsigned char s_paddr[8];	/* physical address, aliased s_nlib 	*/
     107        unsigned char s_vaddr[8];	/* virtual address			*/
     108        unsigned char s_size[8];	/* section size				*/
     109        unsigned char s_scnptr[8];	/* file ptr to raw data for section */
     110        unsigned char s_relptr[8];	/* file ptr to relocation	*/
     111        unsigned char s_lnnoptr[8];	/* file ptr to line numbers	*/
     112        unsigned char s_nreloc[4];	/* number of relocation entries	*/
     113        unsigned char s_nlnno[4];	/* number of line number entries	*/
     114        unsigned char s_flags[4];	/* flags				*/
     115      } xcoff64;
     116    } u;
     117  };
     118  
     119  #define SCNHSZ32	(40)
     120  #define SCNHSZ64	(68)
     121  
     122  /* The length of the s_name field in struct external_scnhdr.  */
     123  
     124  #define SCNNMLEN	(8)
     125  
     126  /* Bits for scnhdr s_flags field.  */
     127  
     128  #define STYP_DATA			0x40
     129  
     130  /* XCOFF symbol table entry.  */
     131  
     132  
     133  #define N_SYMNMLEN	(8)	/* # characters in a symbol name	*/
     134  
     135  /* The format of an XCOFF symbol-table entry.  */
     136  struct external_syment
     137  {
     138    union {
     139      struct {
     140        union {
     141  /* The name of the symbol.  There is an implicit null character
     142     after the end of the array.  */
     143  	char n_name[N_SYMNMLEN];
     144  	struct {
     145  	  /* If n_zeroes is zero, n_offset is the offset the name from
     146  	     the start of the string table.  */
     147  	  unsigned char n_zeroes[4];
     148  	  unsigned char n_offset[4];
     149  	} n;
     150        } n;
     151  
     152        /* The symbol's value.  */
     153        unsigned char n_value[4];
     154      } xcoff32;
     155      struct {
     156        /* The symbol's value.  */
     157        unsigned char n_value[8];
     158  
     159        /* The offset of the symbol from the start of the string table.  */
     160        unsigned char n_offset[4];
     161      } xcoff64;
     162    } u;
     163  
     164    /* The number of the section to which this symbol belongs.  */
     165    unsigned char n_scnum[2];
     166  
     167    /* The type of symbol.  (It can be interpreted as an n_lang
     168       and an n_cpu byte, but we don't care about that here.)  */
     169    unsigned char n_type[2];
     170  
     171    /* The class of symbol (a C_* value).  */
     172    unsigned char n_sclass[1];
     173  
     174    /* The number of auxiliary symbols attached to this entry.  */
     175    unsigned char n_numaux[1];
     176  };
     177  
     178  #define SYMESZ		(18)
     179  
     180  /* Length allowed for filename in aux sym format 4.  */
     181  
     182  #define FILNMLEN	(14)
     183  
     184  /* Omits x_sym and other unused variants.  */
     185  
     186  union external_auxent
     187  {
     188    /* Aux sym format 4: file.  */
     189    union
     190    {
     191      char x_fname[FILNMLEN];
     192      struct
     193      {
     194        unsigned char x_zeroes[4];
     195        unsigned char x_offset[4];
     196        unsigned char x_pad[FILNMLEN-8];
     197        unsigned char x_ftype;
     198      } _x;
     199    } x_file;
     200    /* Aux sym format 5: section.  */
     201    struct
     202    {
     203      unsigned char x_scnlen[4];		/* section length		*/
     204      unsigned char x_nreloc[2];		/* # relocation entries		*/
     205      unsigned char x_nlinno[2];		/* # line numbers		*/
     206    } x_scn;
     207    /* CSECT auxiliary entry.  */
     208    union
     209    {
     210      struct
     211      {
     212        struct
     213        {
     214  	unsigned char x_scnlen[4];	/* csect length */
     215  	unsigned char x_parmhash[4];	/* parm type hash index */
     216  	unsigned char x_snhash[2];	/* sect num with parm hash */
     217  	unsigned char x_smtyp;		/* symbol align and type */
     218  	unsigned char x_smclas;		/* storage mapping class */
     219  	unsigned char x_stab;		/* dbx stab info index */
     220  	unsigned char x_snstab[2];	/* sect num with dbx stab */
     221        } x_csect;
     222      } xcoff32;
     223      struct
     224      {
     225        struct
     226        {
     227  	unsigned char x_scnlen_lo[4];	/* csect length */
     228  	unsigned char x_parmhash[4];	/* parm type hash index */
     229  	unsigned char x_snhash[2];	/* sect num with parm hash */
     230  	unsigned char x_smtyp;		/* symbol align and type */
     231  	unsigned char x_smclas;		/* storage mapping class */
     232  	unsigned char x_scnlen_hi[4];
     233  	unsigned char pad;
     234  	unsigned char x_auxtype;
     235        } x_csect;
     236      } xcoff64;
     237    } u;
     238    /* SECTION/DWARF auxiliary entry.  */
     239    struct
     240    {
     241      unsigned char x_scnlen[4];		/* section length */
     242      unsigned char pad1[4];
     243      unsigned char x_nreloc[4];		/* number RLDs */
     244    } x_sect;
     245  };
     246  
     247  /* Symbol-related constants.  */
     248  
     249  #define N_DEBUG		(-2)
     250  #define IMAGE_SYM_TYPE_NULL	(0)
     251  #define IMAGE_SYM_DTYPE_NULL	(0)
     252  #define IMAGE_SYM_CLASS_STATIC	(3)
     253  #define IMAGE_SYM_CLASS_FILE	(103)
     254  
     255  #define IMAGE_SYM_TYPE \
     256    ((IMAGE_SYM_DTYPE_NULL << 4) | IMAGE_SYM_TYPE_NULL)
     257  
     258  #define C_EXT		(2)
     259  #define C_STAT		(3)
     260  #define C_FILE		(103)
     261  #define C_HIDEXT	(107)
     262  
     263  #define XTY_SD		(1)	/* section definition */
     264  
     265  #define XMC_XO		(7)	/* extended operation */
     266  
     267  /* Private data for an simple_object_read.  */
     268  
     269  struct simple_object_xcoff_read
     270  {
     271    /* Magic number.  */
     272    unsigned short magic;
     273    /* Number of sections.  */
     274    unsigned short nscns;
     275    /* File offset of symbol table.  */
     276    off_t symptr;
     277    /* Number of symbol table entries.  */
     278    unsigned int nsyms;
     279    /* Flags.  */
     280    unsigned short flags;
     281    /* Offset of section headers in file.  */
     282    off_t scnhdr_offset;
     283  };
     284  
     285  /* Private data for an simple_object_attributes.  */
     286  
     287  struct simple_object_xcoff_attributes
     288  {
     289    /* Magic number.  */
     290    unsigned short magic;
     291    /* Flags.  */
     292    unsigned short flags;
     293  };
     294  
     295  /* See if we have a XCOFF file.  */
     296  
     297  static void *
     298  simple_object_xcoff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
     299  			   int descriptor, off_t offset,
     300  			   const char *segment_name ATTRIBUTE_UNUSED,
     301  			   const char **errmsg, int *err)
     302  {
     303    unsigned short magic;
     304    unsigned short (*fetch_16) (const unsigned char *);
     305    unsigned int (*fetch_32) (const unsigned char *);
     306    ulong_type (*fetch_64) (const unsigned char *);
     307    unsigned char hdrbuf[sizeof (struct external_filehdr)];
     308    struct simple_object_xcoff_read *ocr;
     309    int u64;
     310  
     311    magic = simple_object_fetch_big_16 (header);
     312  
     313    if (magic != U802TOCMAGIC && magic != U64_TOCMAGIC)
     314      {
     315        *errmsg = NULL;
     316        *err = 0;
     317        return NULL;
     318      }
     319  
     320    fetch_16 = simple_object_fetch_big_16;
     321    fetch_32 = simple_object_fetch_big_32;
     322    fetch_64 = simple_object_fetch_big_64;
     323  
     324    if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof hdrbuf,
     325  				    errmsg, err))
     326      return NULL;
     327  
     328    u64 = magic == U64_TOCMAGIC;
     329  
     330    ocr = XNEW (struct simple_object_xcoff_read);
     331    ocr->magic = magic;
     332    ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns));
     333    if (u64)
     334      {
     335        ocr->symptr = fetch_64 (hdrbuf
     336  			      + offsetof (struct external_filehdr,
     337  					  u.xcoff64.f_symptr));
     338        ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr,
     339  						u.xcoff64.f_nsyms));
     340        ocr->scnhdr_offset = (sizeof (struct external_filehdr)
     341  			    + fetch_16 (hdrbuf + offsetof (struct external_filehdr,
     342  							   u.xcoff64.f_opthdr)));
     343  
     344      }
     345    else
     346      {
     347        ocr->symptr = fetch_32 (hdrbuf
     348  			      + offsetof (struct external_filehdr,
     349  					  u.xcoff32.f_symptr));
     350        ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr,
     351  						u.xcoff32.f_nsyms));
     352        ocr->scnhdr_offset = (sizeof (struct external_filehdr) - 4
     353  			    + fetch_16 (hdrbuf + offsetof (struct external_filehdr,
     354  							   u.xcoff32.f_opthdr)));
     355  
     356      }
     357  
     358    return (void *) ocr;
     359  }
     360  
     361  /* Read the string table in a XCOFF file.  */
     362  
     363  static char *
     364  simple_object_xcoff_read_strtab (simple_object_read *sobj, size_t *strtab_size,
     365  				 const char **errmsg, int *err)
     366  {
     367    struct simple_object_xcoff_read *ocr =
     368      (struct simple_object_xcoff_read *) sobj->data;
     369    off_t strtab_offset;
     370    unsigned char strsizebuf[4];
     371    size_t strsize;
     372    char *strtab;
     373  
     374    strtab_offset = sobj->offset + ocr->symptr
     375      + ocr->nsyms * SYMESZ;
     376    if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
     377  				    strsizebuf, 4, errmsg, err))
     378      return NULL;
     379    strsize = simple_object_fetch_big_32 (strsizebuf);
     380    strtab = XNEWVEC (char, strsize);
     381    if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
     382  				    (unsigned char *) strtab, strsize, errmsg,
     383  				    err))
     384      {
     385        XDELETEVEC (strtab);
     386        return NULL;
     387      }
     388    *strtab_size = strsize;
     389    return strtab;
     390  }
     391  
     392  /* Find all sections in a XCOFF file.  */
     393  
     394  static const char *
     395  simple_object_xcoff_find_sections (simple_object_read *sobj,
     396  				  int (*pfn) (void *, const char *,
     397  					      off_t offset, off_t length),
     398  				  void *data,
     399  				  int *err)
     400  {
     401    struct simple_object_xcoff_read *ocr =
     402      (struct simple_object_xcoff_read *) sobj->data;
     403    int u64 = ocr->magic == U64_TOCMAGIC;
     404    size_t scnhdr_size;
     405    unsigned char *scnbuf;
     406    const char *errmsg;
     407    unsigned short (*fetch_16) (const unsigned char *);
     408    unsigned int (*fetch_32) (const unsigned char *);
     409    ulong_type (*fetch_64) (const unsigned char *);
     410    unsigned int nscns;
     411    char *strtab;
     412    size_t strtab_size;
     413    struct external_syment *symtab = NULL;
     414    unsigned int i;
     415  
     416    scnhdr_size = u64 ? SCNHSZ64 : SCNHSZ32;
     417    scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns);
     418    if (!simple_object_internal_read (sobj->descriptor,
     419  				    sobj->offset + ocr->scnhdr_offset,
     420  				    scnbuf, scnhdr_size * ocr->nscns, &errmsg,
     421  				    err))
     422      {
     423        XDELETEVEC (scnbuf);
     424        return errmsg;
     425      }
     426  
     427    fetch_16 = simple_object_fetch_big_16;
     428    fetch_32 = simple_object_fetch_big_32;
     429    fetch_64 = simple_object_fetch_big_64;
     430  
     431    nscns = ocr->nscns;
     432    strtab = NULL;
     433    strtab_size = 0;
     434    for (i = 0; i < nscns; ++i)
     435      {
     436        unsigned char *scnhdr;
     437        unsigned char *scnname;
     438        char namebuf[SCNNMLEN + 1];
     439        char *name;
     440        off_t scnptr;
     441        off_t size;
     442  
     443        scnhdr = scnbuf + i * scnhdr_size;
     444        scnname = scnhdr + offsetof (struct external_scnhdr, s_name);
     445        memcpy (namebuf, scnname, SCNNMLEN);
     446        namebuf[SCNNMLEN] = '\0';
     447        name = &namebuf[0];
     448        if (namebuf[0] == '/')
     449  	{
     450  	  size_t strindex;
     451  	  char *end;
     452  
     453  	  strindex = strtol (namebuf + 1, &end, 10);
     454  	  if (*end == '\0')
     455  	    {
     456  	      /* The real section name is found in the string
     457  		 table.  */
     458  	      if (strtab == NULL)
     459  		{
     460  		  strtab = simple_object_xcoff_read_strtab (sobj,
     461  							   &strtab_size,
     462  							   &errmsg, err);
     463  		  if (strtab == NULL)
     464  		    {
     465  		      XDELETEVEC (scnbuf);
     466  		      return errmsg;
     467  		    }
     468  		}
     469  
     470  	      if (strindex < 4 || strindex >= strtab_size)
     471  		{
     472  		  XDELETEVEC (strtab);
     473  		  XDELETEVEC (scnbuf);
     474  		  *err = 0;
     475  		  return "section string index out of range";
     476  		}
     477  
     478  	      name = strtab + strindex;
     479  	    }
     480  	}
     481  
     482        if (u64)
     483  	{
     484  	  scnptr = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
     485  						u.xcoff64.s_scnptr));
     486  	  size = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
     487  					      u.xcoff64.s_size));
     488  	}
     489        else
     490  	{
     491  	  scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
     492  						u.xcoff32.s_scnptr));
     493  	  size = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
     494  					      u.xcoff32.s_size));
     495  	}
     496  
     497        if (!(*pfn) (data, name, scnptr, size))
     498  	break;
     499      }
     500  
     501    /* Special handling for .go_export csect.  */
     502    if (ocr->nsyms > 0)
     503      {
     504        unsigned char *sym;
     505        const char *n_name;
     506        off_t size, n_value;
     507        unsigned int n_numaux, n_offset, n_zeroes;
     508        short n_scnum;
     509  
     510        /* Read symbol table.  */
     511        symtab = XNEWVEC (struct external_syment, ocr->nsyms * SYMESZ);
     512        if (!simple_object_internal_read (sobj->descriptor,
     513  					sobj->offset + ocr->symptr,
     514  					(unsigned char *) symtab,
     515  					ocr->nsyms * SYMESZ,
     516  					&errmsg, err))
     517  	{
     518  	  XDELETEVEC (symtab);
     519  	  XDELETEVEC (scnbuf);
     520  	  return NULL;
     521  	}
     522  
     523        /* Search in symbol table if we have a ".go_export" symbol.  */
     524        for (i = 0; i < ocr->nsyms; i += n_numaux + 1)
     525  	{
     526  	  sym = (unsigned char *) &symtab[i];
     527  	  n_numaux = symtab[i].n_numaux[0];
     528  
     529  	  if (symtab[i].n_sclass[0] != C_EXT
     530  	      && symtab[i].n_sclass[0] != C_HIDEXT)
     531  	    continue;
     532  
     533  	  /* Must have at least one csect auxiliary entry.  */
     534  	  if (n_numaux < 1 || i + n_numaux >= ocr->nsyms)
     535  	    continue;
     536  
     537  	  n_scnum = fetch_16 (sym + offsetof (struct external_syment,
     538  					      n_scnum));
     539  	  if (n_scnum < 1 || (unsigned int) n_scnum > nscns)
     540  	    continue;
     541  
     542  	  if (u64)
     543  	    {
     544  	      n_value = fetch_64 (sym + offsetof (struct external_syment,
     545  						  u.xcoff64.n_value));
     546  	      n_offset = fetch_32 (sym + offsetof (struct external_syment,
     547  						   u.xcoff64.n_offset));
     548  	    }
     549  	  else
     550  	    {
     551  	      /* ".go_export" is longer than N_SYMNMLEN.  */
     552  	      n_zeroes = fetch_32 (sym + offsetof (struct external_syment,
     553  						   u.xcoff32.n.n.n_zeroes));
     554  	      if (n_zeroes != 0)
     555  		continue;
     556  
     557  	      n_value = fetch_32 (sym + offsetof (struct external_syment,
     558  						  u.xcoff32.n_value));
     559  	      n_offset = fetch_32 (sym + offsetof (struct external_syment,
     560  						   u.xcoff32.n.n.n_offset));
     561  	    }
     562  
     563  	  /* The real symbol name is found in the string table.  */
     564  	  if (strtab == NULL)
     565  	    {
     566  	      strtab = simple_object_xcoff_read_strtab (sobj,
     567  	  						&strtab_size,
     568  							&errmsg, err);
     569  	      if (strtab == NULL)
     570  		{
     571  		  XDELETEVEC (symtab);
     572  		  XDELETEVEC (scnbuf);
     573  		  return errmsg;
     574  		}
     575  	    }
     576  
     577  	  if (n_offset >= strtab_size)
     578  	    {
     579  	      XDELETEVEC (strtab);
     580  	      XDELETEVEC (symtab);
     581  	      XDELETEVEC (scnbuf);
     582  	      *err = 0;
     583  	      return "symbol string index out of range";
     584  	    }
     585  	  n_name = strtab + n_offset;
     586  
     587  	  if (!strcmp (n_name, ".go_export"))
     588  	    {
     589  	      union external_auxent *auxent;
     590  	      unsigned char *aux, *scnhdr;
     591  	      off_t scnptr, x_scnlen;
     592  
     593  	      /* Found .go_export symbol, read its csect auxiliary entry.
     594  		 By convention, it is the last auxiliary entry.  */
     595  	      auxent = (union external_auxent *) &symtab[i + n_numaux];
     596  	      aux = (unsigned char *) auxent;
     597  	      if (u64)
     598  		{
     599  		  /* Use an intermediate 64-bit type to avoid
     600  		     compilation warning about 32-bit shift below on
     601  		     hosts with 32-bit off_t which aren't supported by
     602  		     AC_SYS_LARGEFILE.  */
     603  		  ulong_type x_scnlen64;
     604  
     605  		  if ((auxent->u.xcoff64.x_csect.x_smtyp & 0x7) != XTY_SD
     606  		      || auxent->u.xcoff64.x_csect.x_smclas != XMC_XO)
     607  		    continue;
     608  
     609  		  x_scnlen64 = 
     610  		    fetch_32 (aux + offsetof (union external_auxent,
     611  					      u.xcoff64.x_csect.x_scnlen_hi));
     612  		  x_scnlen = 
     613  		    ((x_scnlen64 << 32)
     614  		     | fetch_32 (aux
     615  				 + offsetof (union external_auxent,
     616  					     u.xcoff64.x_csect.x_scnlen_lo)));
     617  		}
     618  	      else
     619  		{
     620  		  if ((auxent->u.xcoff32.x_csect.x_smtyp & 0x7) != XTY_SD
     621  		      || auxent->u.xcoff32.x_csect.x_smclas != XMC_XO)
     622  		    continue;
     623  
     624  		  x_scnlen = fetch_32 (aux + offsetof (union external_auxent,
     625  						       u.xcoff32.x_csect.x_scnlen));
     626  		}
     627  
     628  	      /* Get header of containing section.  */
     629  	      scnhdr = scnbuf + (n_scnum - 1) * scnhdr_size;
     630  	      if (u64)
     631  		{
     632  		  scnptr = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
     633  							u.xcoff64.s_scnptr));
     634  		  size = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
     635  						      u.xcoff64.s_size));
     636  		}
     637  	      else
     638  		{
     639  		  scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
     640  							u.xcoff32.s_scnptr));
     641  		  size = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
     642  						      u.xcoff32.s_size));
     643  		}
     644  	      if (n_value + x_scnlen > size)
     645  		break;
     646  
     647  	      (*pfn) (data, ".go_export", scnptr + n_value, x_scnlen);
     648  	      break;
     649  	    }
     650  	}
     651      }
     652  
     653    if (symtab != NULL)
     654      XDELETEVEC (symtab);
     655    if (strtab != NULL)
     656      XDELETEVEC (strtab);
     657    XDELETEVEC (scnbuf);
     658  
     659    return NULL;
     660  }
     661  
     662  /* Fetch the attributes for an simple_object_read.  */
     663  
     664  static void *
     665  simple_object_xcoff_fetch_attributes (simple_object_read *sobj,
     666  				     const char **errmsg ATTRIBUTE_UNUSED,
     667  				     int *err ATTRIBUTE_UNUSED)
     668  {
     669    struct simple_object_xcoff_read *ocr =
     670      (struct simple_object_xcoff_read *) sobj->data;
     671    struct simple_object_xcoff_attributes *ret;
     672  
     673    ret = XNEW (struct simple_object_xcoff_attributes);
     674    ret->magic = ocr->magic;
     675    ret->flags = ocr->flags;
     676    return ret;
     677  }
     678  
     679  /* Release the private data for an simple_object_read.  */
     680  
     681  static void
     682  simple_object_xcoff_release_read (void *data)
     683  {
     684    XDELETE (data);
     685  }
     686  
     687  /* Compare two attributes structures.  */
     688  
     689  static const char *
     690  simple_object_xcoff_attributes_merge (void *todata, void *fromdata, int *err)
     691  {
     692    struct simple_object_xcoff_attributes *to =
     693      (struct simple_object_xcoff_attributes *) todata;
     694    struct simple_object_xcoff_attributes *from =
     695      (struct simple_object_xcoff_attributes *) fromdata;
     696  
     697    if (to->magic != from->magic)
     698      {
     699        *err = 0;
     700        return "XCOFF object format mismatch";
     701      }
     702    return NULL;
     703  }
     704  
     705  /* Release the private data for an attributes structure.  */
     706  
     707  static void
     708  simple_object_xcoff_release_attributes (void *data)
     709  {
     710    XDELETE (data);
     711  }
     712  
     713  /* Prepare to write out a file.  */
     714  
     715  static void *
     716  simple_object_xcoff_start_write (void *attributes_data,
     717  				const char **errmsg ATTRIBUTE_UNUSED,
     718  				int *err ATTRIBUTE_UNUSED)
     719  {
     720    struct simple_object_xcoff_attributes *attrs =
     721      (struct simple_object_xcoff_attributes *) attributes_data;
     722    struct simple_object_xcoff_attributes *ret;
     723  
     724    /* We're just going to record the attributes, but we need to make a
     725       copy because the user may delete them.  */
     726    ret = XNEW (struct simple_object_xcoff_attributes);
     727    *ret = *attrs;
     728    return ret;
     729  }
     730  
     731  /* Write out a XCOFF filehdr.  */
     732  
     733  static int
     734  simple_object_xcoff_write_filehdr (simple_object_write *sobj, int descriptor,
     735  				  unsigned int nscns, size_t symtab_offset,
     736  				  unsigned int nsyms, const char **errmsg,
     737  				  int *err)
     738  {
     739    struct simple_object_xcoff_attributes *attrs =
     740      (struct simple_object_xcoff_attributes *) sobj->data;
     741    int u64 = attrs->magic == U64_TOCMAGIC;
     742    unsigned char hdrbuf[sizeof (struct external_filehdr)];
     743    unsigned char *hdr;
     744    void (*set_16) (unsigned char *, unsigned short);
     745    void (*set_32) (unsigned char *, unsigned int);
     746    void (*set_64) (unsigned char *, ulong_type);
     747  
     748    hdr = &hdrbuf[0];
     749  
     750    set_16 = simple_object_set_big_16;
     751    set_32 = simple_object_set_big_32;
     752    set_64 = simple_object_set_big_64;
     753  
     754    memset (hdr, 0, sizeof (struct external_filehdr));
     755  
     756    set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic);
     757    set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns);
     758    /* f_timdat left as zero.  */
     759    if (u64)
     760      {
     761        set_64 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr),
     762  	      symtab_offset);
     763        set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms),
     764  	      nsyms);
     765        /* f_opthdr left as zero.  */
     766        set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags),
     767  	      attrs->flags);
     768      }
     769    else
     770      {
     771        set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr),
     772  	      symtab_offset);
     773        set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms),
     774  	      nsyms);
     775        /* f_opthdr left as zero.  */
     776        set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags),
     777  	      attrs->flags);
     778      }
     779  
     780    return simple_object_internal_write (descriptor, 0, hdrbuf,
     781  				       sizeof (struct external_filehdr),
     782  				       errmsg, err);
     783  }
     784  
     785  /* Write out a XCOFF section header.  */
     786  
     787  static int
     788  simple_object_xcoff_write_scnhdr (simple_object_write *sobj,
     789  				  int descriptor,
     790  				  const char *name, size_t *name_offset,
     791  				  off_t scnhdr_offset, size_t scnsize,
     792  				  off_t offset, unsigned int align,
     793  				  const char **errmsg, int *err)
     794  {
     795    struct simple_object_xcoff_read *ocr =
     796      (struct simple_object_xcoff_read *) sobj->data;
     797    int u64 = ocr->magic == U64_TOCMAGIC;
     798    void (*set_32) (unsigned char *, unsigned int);
     799    void (*set_64) (unsigned char *, unsigned int);
     800    unsigned char hdrbuf[sizeof (struct external_scnhdr)];
     801    unsigned char *hdr;
     802    size_t namelen;
     803    unsigned int flags;
     804  
     805    set_32 = simple_object_set_big_32;
     806    set_64 = simple_object_set_big_32;
     807  
     808    memset (hdrbuf, 0, sizeof hdrbuf);
     809    hdr = &hdrbuf[0];
     810  
     811    namelen = strlen (name);
     812    if (namelen <= SCNNMLEN)
     813      strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name),
     814  	     name, SCNNMLEN);
     815    else
     816      {
     817        snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name),
     818  		SCNNMLEN, "/%lu", (unsigned long) *name_offset);
     819        *name_offset += namelen + 1;
     820      }
     821  
     822    /* s_paddr left as zero.  */
     823    /* s_vaddr left as zero.  */
     824    if (u64)
     825      {
     826        set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_size),
     827  	      scnsize);
     828        set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_scnptr),
     829  	      offset);
     830      }
     831    else
     832      {
     833        set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_size),
     834  	      scnsize);
     835        set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_scnptr),
     836  	      offset);
     837      }
     838    /* s_relptr left as zero.  */
     839    /* s_lnnoptr left as zero.  */
     840    /* s_nreloc left as zero.  */
     841    /* s_nlnno left as zero.  */
     842    flags = STYP_DATA;
     843    if (align > 13)
     844      align = 13;
     845    if (u64)
     846      set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_flags), flags);
     847    else
     848      set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_flags), flags);
     849  
     850    return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf,
     851  				       u64 ? SCNHSZ64 : SCNHSZ32,
     852  				       errmsg, err);
     853  }
     854  
     855  /* Write out a complete XCOFF file.  */
     856  
     857  static const char *
     858  simple_object_xcoff_write_to_file (simple_object_write *sobj, int descriptor,
     859  				  int *err)
     860  {
     861    struct simple_object_xcoff_read *ocr =
     862      (struct simple_object_xcoff_read *) sobj->data;
     863    int u64 = ocr->magic == U64_TOCMAGIC;
     864    unsigned int nscns, secnum;
     865    simple_object_write_section *section;
     866    off_t scnhdr_offset;
     867    size_t symtab_offset;
     868    off_t secsym_offset;
     869    unsigned int nsyms;
     870    size_t offset;
     871    size_t name_offset;
     872    const char *errmsg;
     873    unsigned char strsizebuf[4];
     874    /* The interface doesn't give us access to the name of the input file
     875       yet.  We want to use its basename for the FILE symbol.  This is
     876       what 'gas' uses when told to assemble from stdin.  */
     877    const char *source_filename = "fake";
     878    size_t sflen;
     879    union
     880    {
     881      struct external_syment sym;
     882      union external_auxent aux;
     883    } syms[2];
     884    void (*set_16) (unsigned char *, unsigned short);
     885    void (*set_32) (unsigned char *, unsigned int);
     886  
     887    set_16 = simple_object_set_big_16;
     888    set_32 = simple_object_set_big_32;
     889  
     890    nscns = 0;
     891    for (section = sobj->sections; section != NULL; section = section->next)
     892      ++nscns;
     893  
     894    scnhdr_offset = sizeof (struct external_filehdr) - (u64 ? 4 : 0);
     895    offset = scnhdr_offset + nscns * (u64 ? SCNHSZ64 : SCNHSZ32);
     896    name_offset = 4;
     897    for (section = sobj->sections; section != NULL; section = section->next)
     898      {
     899        size_t mask;
     900        size_t new_offset;
     901        size_t scnsize;
     902        struct simple_object_write_section_buffer *buffer;
     903  
     904        mask = (1U << section->align) - 1;
     905        new_offset = offset & mask;
     906        new_offset &= ~ mask;
     907        while (new_offset > offset)
     908  	{
     909  	  unsigned char zeroes[16];
     910  	  size_t write;
     911  
     912  	  memset (zeroes, 0, sizeof zeroes);
     913  	  write = new_offset - offset;
     914  	  if (write > sizeof zeroes)
     915  	    write = sizeof zeroes;
     916  	  if (!simple_object_internal_write (descriptor, offset, zeroes, write,
     917  					     &errmsg, err))
     918  	    return errmsg;
     919  	}
     920  
     921        scnsize = 0;
     922        for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
     923  	{
     924  	  if (!simple_object_internal_write (descriptor, offset + scnsize,
     925  					     ((const unsigned char *)
     926  					      buffer->buffer),
     927  					     buffer->size, &errmsg, err))
     928  	    return errmsg;
     929  	  scnsize += buffer->size;
     930  	}
     931  
     932        if (!simple_object_xcoff_write_scnhdr (sobj, descriptor, section->name,
     933  					    &name_offset, scnhdr_offset,
     934  					    scnsize, offset, section->align,
     935  					    &errmsg, err))
     936  	return errmsg;
     937  
     938        scnhdr_offset += u64 ? SCNHSZ64 : SCNHSZ32;
     939        offset += scnsize;
     940      }
     941  
     942    /* Symbol table is always half-word aligned.  */
     943    offset += (offset & 1);
     944    /* There is a file symbol and a section symbol per section,
     945       and each of these has a single auxiliary symbol following.  */
     946    nsyms = 2 * (nscns + 1);
     947    symtab_offset = offset;
     948    /* Advance across space reserved for symbol table to locate
     949       start of string table.  */
     950    offset += nsyms * SYMESZ;
     951  
     952    /* Write out file symbol.  */
     953    memset (&syms[0], 0, sizeof (syms));
     954    if (!u64)
     955      strcpy ((char *)&syms[0].sym.u.xcoff32.n.n_name[0], ".file");
     956    set_16 (&syms[0].sym.n_scnum[0], N_DEBUG);
     957    set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE);
     958    syms[0].sym.n_sclass[0] = C_FILE;
     959    syms[0].sym.n_numaux[0] = 1;
     960    /* The name need not be nul-terminated if it fits into the x_fname field
     961       directly, but must be if it has to be placed into the string table.  */
     962    sflen = strlen (source_filename);
     963    if (sflen <= FILNMLEN)
     964      memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen);
     965    else
     966      {
     967        set_32 (&syms[1].aux.x_file._x.x_offset[0], name_offset);
     968        if (!simple_object_internal_write (descriptor, offset + name_offset,
     969  					 ((const unsigned char *)
     970  					  source_filename),
     971  					 sflen + 1, &errmsg, err))
     972  	return errmsg;
     973        name_offset += strlen (source_filename) + 1;
     974      }
     975    if (!simple_object_internal_write (descriptor, symtab_offset,
     976  				     (const unsigned char *) &syms[0],
     977  				     sizeof (syms), &errmsg, err))
     978      return errmsg;
     979  
     980    /* Write the string table length, followed by the strings and section
     981       symbols in step with each other.  */
     982    set_32 (strsizebuf, name_offset);
     983    if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4,
     984  				     &errmsg, err))
     985      return errmsg;
     986  
     987    name_offset = 4;
     988    secsym_offset = symtab_offset + sizeof (syms);
     989    memset (&syms[0], 0, sizeof (syms));
     990    set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE);
     991    syms[0].sym.n_sclass[0] = C_STAT;
     992    syms[0].sym.n_numaux[0] = 1;
     993    secnum = 1;
     994  
     995    for (section = sobj->sections; section != NULL; section = section->next)
     996      {
     997        size_t namelen;
     998        size_t scnsize;
     999        struct simple_object_write_section_buffer *buffer;
    1000  
    1001        namelen = strlen (section->name);
    1002        set_16 (&syms[0].sym.n_scnum[0], secnum++);
    1003        scnsize = 0;
    1004        for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
    1005  	scnsize += buffer->size;
    1006        set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
    1007        if (namelen > SCNNMLEN)
    1008  	{
    1009  	  set_32 (&syms[0].sym.u.xcoff32.n.n.n_zeroes[0], 0);
    1010  	  set_32 (&syms[0].sym.u.xcoff32.n.n.n_offset[0], name_offset);
    1011  	  if (!simple_object_internal_write (descriptor, offset + name_offset,
    1012  					     ((const unsigned char *)
    1013  					      section->name),
    1014  					     namelen + 1, &errmsg, err))
    1015  	    return errmsg;
    1016  	  name_offset += namelen + 1;
    1017  	}
    1018        else
    1019  	{
    1020  	  memcpy (&syms[0].sym.u.xcoff32.n.n_name[0], section->name,
    1021  		  strlen (section->name));
    1022  	  memset (&syms[0].sym.u.xcoff32.n.n_name[strlen (section->name)], 0,
    1023  		  N_SYMNMLEN - strlen (section->name));
    1024  	}
    1025  
    1026        if (!simple_object_internal_write (descriptor, secsym_offset,
    1027  					 (const unsigned char *) &syms[0],
    1028  					 sizeof (syms), &errmsg, err))
    1029  	return errmsg;
    1030        secsym_offset += sizeof (syms);
    1031      }
    1032  
    1033    if (!simple_object_xcoff_write_filehdr (sobj, descriptor, nscns,
    1034  					 symtab_offset, nsyms, &errmsg, err))
    1035      return errmsg;
    1036  
    1037    return NULL;
    1038  }
    1039  
    1040  /* Release the private data for an simple_object_write structure.  */
    1041  
    1042  static void
    1043  simple_object_xcoff_release_write (void *data)
    1044  {
    1045    XDELETE (data);
    1046  }
    1047  
    1048  /* The XCOFF functions.  */
    1049  
    1050  const struct simple_object_functions simple_object_xcoff_functions =
    1051  {
    1052    simple_object_xcoff_match,
    1053    simple_object_xcoff_find_sections,
    1054    simple_object_xcoff_fetch_attributes,
    1055    simple_object_xcoff_release_read,
    1056    simple_object_xcoff_attributes_merge,
    1057    simple_object_xcoff_release_attributes,
    1058    simple_object_xcoff_start_write,
    1059    simple_object_xcoff_write_to_file,
    1060    simple_object_xcoff_release_write,
    1061    NULL
    1062  };