Home | History | Annotate | Line # | Download | only in libiberty
      1 /* simple-object-coff.c -- routines to manipulate COFF object files.
      2    Copyright (C) 2010-2026 Free Software Foundation, Inc.
      3    Written by Ian Lance Taylor, Google.
      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 /* COFF structures and constants.  */
     46 
     47 /* COFF 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   unsigned char f_symptr[4];	/* file pointer to symtab	*/
     55   unsigned char f_nsyms[4];	/* number of symtab entries	*/
     56   unsigned char f_opthdr[2];	/* sizeof(optional hdr)		*/
     57   unsigned char f_flags[2];	/* flags			*/
     58 };
     59 
     60 /* BigObj COFF file header.  */
     61 
     62 struct external_filehdr_bigobj
     63 {
     64   unsigned char sig1[2];	/* Must be 0x0000 */
     65   unsigned char sig2[2];	/* Must be 0xFFFF */
     66   unsigned char version[2];	/* Version, currently 2 */
     67   unsigned char machine[2];	/* Machine type */
     68   unsigned char timdat[4];	/* time & date stamp */
     69   unsigned char classid[16];	/* Magic GUID that identifies BigObj format */
     70   unsigned char sizeofdata[4];	/* Size of data (unused, set to 0) */
     71   unsigned char flags[4];	/* Flags (unused, set to 0) */
     72   unsigned char metadatasize[4];	/* Metadata size (unused, set to 0) */
     73   unsigned char metadataoffset[4];	/* Metadata offset (unused, set to 0) */
     74   unsigned char nscns[4];	/* number of sections (32-bit!) */
     75   unsigned char symptr[4];	/* file pointer to symtab */
     76   unsigned char nsyms[4];	/* number of symtab entries */
     77 };
     78 
     79 /* The BigObj magic GUID (ClassID).  */
     80 static const unsigned char bigobj_magic[16] =
     81 {
     82   0xC7, 0xA1, 0xBA, 0xD1, 0xEE, 0xBA, 0xA9, 0x4B,
     83   0xAF, 0x20, 0xFA, 0xF6, 0x6A, 0xA4, 0xDC, 0xB8
     84 };
     85 
     86 /* Bits for filehdr f_flags field.  */
     87 
     88 #define F_EXEC			(0x0002)
     89 #define IMAGE_FILE_SYSTEM	(0x1000)
     90 #define IMAGE_FILE_DLL		(0x2000)
     91 
     92 /* COFF section header.  */
     93 
     94 struct external_scnhdr
     95 {
     96   unsigned char s_name[8];	/* section name				*/
     97   unsigned char s_paddr[4];	/* physical address, aliased s_nlib 	*/
     98   unsigned char s_vaddr[4];	/* virtual address			*/
     99   unsigned char s_size[4];	/* section size				*/
    100   unsigned char s_scnptr[4];	/* file ptr to raw data for section 	*/
    101   unsigned char s_relptr[4];	/* file ptr to relocation		*/
    102   unsigned char s_lnnoptr[4];	/* file ptr to line numbers		*/
    103   unsigned char s_nreloc[2];	/* number of relocation entries		*/
    104   unsigned char s_nlnno[2];	/* number of line number entries	*/
    105   unsigned char s_flags[4];	/* flags				*/
    106 };
    107 
    108 /* The length of the s_name field in struct external_scnhdr.  */
    109 
    110 #define SCNNMLEN (8)
    111 
    112 /* Bits for scnhdr s_flags field.  This includes some bits defined
    113    only for PE.  This may need to be moved into coff_magic.  */
    114 
    115 #define STYP_DATA			(1 << 6)
    116 #define IMAGE_SCN_MEM_DISCARDABLE	(1 << 25)
    117 #define IMAGE_SCN_MEM_SHARED		(1 << 28)
    118 #define IMAGE_SCN_MEM_READ		(1 << 30)
    119 
    120 #define IMAGE_SCN_ALIGN_POWER_BIT_POS	     20
    121 #define IMAGE_SCN_ALIGN_POWER_CONST(val)     \
    122   (((val) + 1) << IMAGE_SCN_ALIGN_POWER_BIT_POS)
    123 
    124 /* COFF symbol table entry.  */
    125 
    126 #define E_SYMNMLEN	8	/* # characters in a symbol name	*/
    127 
    128 struct external_syment
    129 {
    130   union
    131   {
    132     unsigned char e_name[E_SYMNMLEN];
    133 
    134     struct
    135     {
    136       unsigned char e_zeroes[4];
    137       unsigned char e_offset[4];
    138     } e;
    139   } e;
    140 
    141   unsigned char e_value[4];
    142   unsigned char e_scnum[2];
    143   unsigned char e_type[2];
    144   unsigned char e_sclass[1];
    145   unsigned char e_numaux[1];
    146 };
    147 
    148 /* BigObj COFF symbol table entry (20 bytes instead of 18).  */
    149 
    150 struct external_syment_bigobj
    151 {
    152   union
    153   {
    154     unsigned char e_name[E_SYMNMLEN];
    155 
    156     struct
    157     {
    158       unsigned char e_zeroes[4];
    159       unsigned char e_offset[4];
    160     } e;
    161   } e;
    162 
    163   unsigned char e_value[4];
    164   unsigned char e_scnum[4];	/* 32-bit section number! */
    165   unsigned char e_type[2];
    166   unsigned char e_sclass[1];
    167   unsigned char e_numaux[1];
    168 };
    169 
    170 /* Length allowed for filename in aux sym format 4.  */
    171 
    172 #define E_FILNMLEN	18
    173 
    174 /* Omits x_sym and other unused variants.  */
    175 
    176 union external_auxent
    177 {
    178   /* Aux sym format 4: file.  */
    179   union
    180   {
    181     char x_fname[E_FILNMLEN];
    182     struct
    183     {
    184       unsigned char x_zeroes[4];
    185       unsigned char x_offset[4];
    186     } x_n;
    187   } x_file;
    188   /* Aux sym format 5: section.  */
    189   struct
    190   {
    191     unsigned char x_scnlen[4];		/* section length		*/
    192     unsigned char x_nreloc[2];		/* # relocation entries		*/
    193     unsigned char x_nlinno[2];		/* # line numbers		*/
    194     unsigned char x_checksum[4];	/* section COMDAT checksum	*/
    195     unsigned char x_associated[2];	/* COMDAT assoc section index	*/
    196     unsigned char x_comdat[1];		/* COMDAT selection number	*/
    197   } x_scn;
    198 };
    199 
    200 /* BigObj auxiliary symbol (20 bytes to match symbol size).  */
    201 
    202 union external_auxent_bigobj
    203 {
    204   /* Aux sym format 4: file.  */
    205   union
    206   {
    207     char x_fname[E_FILNMLEN];
    208     struct
    209     {
    210       unsigned char x_zeroes[4];
    211       unsigned char x_offset[4];
    212     } x_n;
    213   } x_file;
    214   /* Aux sym format 5: section.  */
    215   struct
    216   {
    217     unsigned char x_scnlen[4];		/* section length		*/
    218     unsigned char x_nreloc[2];		/* # relocation entries		*/
    219     unsigned char x_nlinno[2];		/* # line numbers		*/
    220     unsigned char x_checksum[4];	/* section COMDAT checksum	*/
    221     unsigned char x_associated[2];	/* COMDAT assoc section index	*/
    222     unsigned char x_comdat[1];		/* COMDAT selection number	*/
    223     unsigned char x_pad[3];		/* Padding to 20 bytes		*/
    224   } x_scn;
    225 };
    226 
    227 /* Symbol-related constants.  */
    228 
    229 #define IMAGE_SYM_DEBUG		(-2)
    230 #define IMAGE_SYM_TYPE_NULL	(0)
    231 #define IMAGE_SYM_DTYPE_NULL	(0)
    232 #define IMAGE_SYM_CLASS_STATIC	(3)
    233 #define IMAGE_SYM_CLASS_FILE	(103)
    234 
    235 #define IMAGE_SYM_TYPE \
    236   ((IMAGE_SYM_DTYPE_NULL << 4) | IMAGE_SYM_TYPE_NULL)
    237 
    238 /* Private data for an simple_object_read.  */
    239 
    240 struct simple_object_coff_read
    241 {
    242   /* Magic number.  */
    243   unsigned short magic;
    244   /* Whether the file is big-endian.  */
    245   unsigned char is_big_endian;
    246   /* Whether this is BigObj format.  */
    247   unsigned char is_bigobj;
    248   /* Number of sections.  */
    249   unsigned int nscns;
    250   /* File offset of symbol table.  */
    251   off_t symptr;
    252   /* Number of symbol table entries.  */
    253   unsigned int nsyms;
    254   /* Flags.  */
    255   unsigned short flags;
    256   /* Offset of section headers in file.  */
    257   off_t scnhdr_offset;
    258 };
    259 
    260 /* Private data for an simple_object_attributes.  */
    261 
    262 struct simple_object_coff_attributes
    263 {
    264   /* Magic number.  */
    265   unsigned short magic;
    266   /* Whether the file is big-endian.  */
    267   unsigned char is_big_endian;
    268   /* Whether this is BigObj format.  */
    269   unsigned char is_bigobj;
    270   /* Flags.  */
    271   unsigned short flags;
    272 };
    273 
    274 /* There is no magic number which indicates a COFF file as opposed to
    275    any other sort of file.  Instead, each COFF file starts with a
    276    two-byte magic number which also indicates the type of the target.
    277    This struct holds a magic number as well as characteristics of that
    278    COFF format.  */
    279 
    280 struct coff_magic_struct
    281 {
    282   /* Magic number.  */
    283   unsigned short magic;
    284   /* Whether this magic number is for a big-endian file.  */
    285   unsigned char is_big_endian;
    286   /* Flag bits, in the f_flags fields, which indicates that this file
    287      is not a relocatable object file.  There is no flag which
    288      specifically indicates a relocatable object file, it is only
    289      implied by the absence of these flags.  */
    290   unsigned short non_object_flags;
    291 };
    292 
    293 /* This is a list of the COFF magic numbers which we recognize, namely
    294    the ones used on Windows.  More can be added as needed.  */
    295 
    296 static const struct coff_magic_struct coff_magic[] =
    297 {
    298   /* i386.  */
    299   { 0x14c, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL },
    300   /* x86_64.  */
    301   { 0x8664, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL },
    302   /* AArch64.  */
    303   { 0xaa64, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL }
    304 };
    305 
    306 /* See if we have a COFF file.  */
    307 
    308 static void *
    309 simple_object_coff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
    310 			  int descriptor, off_t offset,
    311 			  const char *segment_name ATTRIBUTE_UNUSED,
    312 			  const char **errmsg, int *err)
    313 {
    314   size_t c;
    315   unsigned short magic_big;
    316   unsigned short magic_little;
    317   unsigned short magic;
    318   size_t i;
    319   int is_big_endian;
    320   unsigned short (*fetch_16) (const unsigned char *);
    321   unsigned int (*fetch_32) (const unsigned char *);
    322   unsigned char hdrbuf[sizeof (struct external_filehdr_bigobj)];
    323   unsigned short flags;
    324   struct simple_object_coff_read *ocr;
    325   unsigned short sig1, sig2;
    326 
    327   /* Try regular COFF first.  */
    328   c = sizeof (coff_magic) / sizeof (coff_magic[0]);
    329   magic_big = simple_object_fetch_big_16 (header);
    330   magic_little = simple_object_fetch_little_16 (header);
    331   for (i = 0; i < c; ++i)
    332     {
    333       if (coff_magic[i].is_big_endian
    334 	  ? coff_magic[i].magic == magic_big
    335 	  : coff_magic[i].magic == magic_little)
    336 	break;
    337     }
    338 
    339   /* Check for BigObj if regular COFF didn't match.  */
    340   sig1 = simple_object_fetch_little_16 (header);
    341   sig2 = simple_object_fetch_little_16 (header + 2);
    342 
    343   if (i >= c && (sig1 != 0 || sig2 != 0xFFFF))
    344     {
    345       /* Not regular COFF and not BigObj.  */
    346       *errmsg = NULL;
    347       *err = 0;
    348       return NULL;
    349     }
    350 
    351   if (sig1 == 0 && sig2 == 0xFFFF)
    352     {
    353       /* This looks like BigObj.  Verify the ClassID.  */
    354       unsigned char bigobj_hdrbuf[sizeof (struct external_filehdr_bigobj)];
    355 
    356       if (!simple_object_internal_read (descriptor, offset, bigobj_hdrbuf,
    357 					sizeof bigobj_hdrbuf, errmsg, err))
    358 	return NULL;
    359 
    360       if (memcmp (bigobj_hdrbuf + offsetof (struct external_filehdr_bigobj,
    361 					    classid),
    362 		  bigobj_magic, 16) != 0)
    363 	{
    364 	  *errmsg = NULL;
    365 	  *err = 0;
    366 	  return NULL;
    367 	}
    368 
    369       /* BigObj is always little-endian.  */
    370       is_big_endian = 0;
    371 
    372       ocr = XNEW (struct simple_object_coff_read);
    373       ocr->magic = simple_object_fetch_little_16
    374 		     (bigobj_hdrbuf
    375 		      + offsetof (struct external_filehdr_bigobj, machine));
    376       ocr->is_big_endian = 0;
    377       ocr->is_bigobj = 1;
    378       ocr->nscns = simple_object_fetch_little_32
    379 		     (bigobj_hdrbuf
    380 		      + offsetof (struct external_filehdr_bigobj, nscns));
    381       ocr->symptr = simple_object_fetch_little_32
    382 		      (bigobj_hdrbuf
    383 		       + offsetof (struct external_filehdr_bigobj, symptr));
    384       ocr->nsyms = simple_object_fetch_little_32
    385 		     (bigobj_hdrbuf
    386 		      + offsetof (struct external_filehdr_bigobj, nsyms));
    387       ocr->flags = simple_object_fetch_little_32
    388 		     (bigobj_hdrbuf
    389 		      + offsetof (struct external_filehdr_bigobj, flags));
    390       ocr->scnhdr_offset = sizeof (struct external_filehdr_bigobj);
    391 
    392       return (void *) ocr;
    393     }
    394 
    395   /* Regular COFF.  */
    396   is_big_endian = coff_magic[i].is_big_endian;
    397 
    398   magic = is_big_endian ? magic_big : magic_little;
    399   fetch_16 = (is_big_endian
    400 	      ? simple_object_fetch_big_16
    401 	      : simple_object_fetch_little_16);
    402   fetch_32 = (is_big_endian
    403 	      ? simple_object_fetch_big_32
    404 	      : simple_object_fetch_little_32);
    405 
    406   if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof (struct external_filehdr),
    407 				    errmsg, err))
    408     return NULL;
    409 
    410   flags = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_flags));
    411   if ((flags & coff_magic[i].non_object_flags) != 0)
    412     {
    413       *errmsg = "not relocatable object file";
    414       *err = 0;
    415       return NULL;
    416     }
    417 
    418   ocr = XNEW (struct simple_object_coff_read);
    419   ocr->magic = magic;
    420   ocr->is_big_endian = is_big_endian;
    421   ocr->is_bigobj = 0;
    422   ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns));
    423   ocr->symptr = fetch_32 (hdrbuf
    424 			  + offsetof (struct external_filehdr, f_symptr));
    425   ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, f_nsyms));
    426   ocr->flags = flags;
    427   ocr->scnhdr_offset = (sizeof (struct external_filehdr)
    428 			+ fetch_16 (hdrbuf + offsetof (struct external_filehdr,
    429 						       f_opthdr)));
    430 
    431   return (void *) ocr;
    432 }
    433 
    434 /* Read the string table in a COFF file.  */
    435 
    436 static char *
    437 simple_object_coff_read_strtab (simple_object_read *sobj, size_t *strtab_size,
    438 				const char **errmsg, int *err)
    439 {
    440   struct simple_object_coff_read *ocr =
    441     (struct simple_object_coff_read *) sobj->data;
    442   off_t strtab_offset;
    443   unsigned char strsizebuf[4];
    444   size_t strsize;
    445   char *strtab;
    446   size_t sym_size;
    447 
    448   /* Symbol size depends on format.  */
    449   sym_size = ocr->is_bigobj ? sizeof (struct external_syment_bigobj)
    450 			    : sizeof (struct external_syment);
    451 
    452   strtab_offset = sobj->offset + ocr->symptr + ocr->nsyms * sym_size;
    453   if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
    454 				    strsizebuf, 4, errmsg, err))
    455     return NULL;
    456   strsize = (ocr->is_big_endian
    457 	     ? simple_object_fetch_big_32 (strsizebuf)
    458 	     : simple_object_fetch_little_32 (strsizebuf));
    459   strtab = XNEWVEC (char, strsize);
    460   if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
    461 				    (unsigned char *) strtab, strsize, errmsg,
    462 				    err))
    463     {
    464       XDELETEVEC (strtab);
    465       return NULL;
    466     }
    467   *strtab_size = strsize;
    468   return strtab;
    469 }
    470 
    471 /* Find all sections in a COFF file.  */
    472 
    473 static const char *
    474 simple_object_coff_find_sections (simple_object_read *sobj,
    475 				  int (*pfn) (void *, const char *,
    476 					      off_t offset, off_t length),
    477 				  void *data,
    478 				  int *err)
    479 {
    480   struct simple_object_coff_read *ocr =
    481     (struct simple_object_coff_read *) sobj->data;
    482   size_t scnhdr_size;
    483   unsigned char *scnbuf;
    484   const char *errmsg;
    485   unsigned int (*fetch_32) (const unsigned char *);
    486   unsigned int nscns;
    487   char *strtab;
    488   size_t strtab_size;
    489   unsigned int i;
    490 
    491   scnhdr_size = sizeof (struct external_scnhdr);
    492   scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns);
    493   if (!simple_object_internal_read (sobj->descriptor,
    494 				    sobj->offset + ocr->scnhdr_offset,
    495 				    scnbuf, scnhdr_size * ocr->nscns, &errmsg,
    496 				    err))
    497     {
    498       XDELETEVEC (scnbuf);
    499       return errmsg;
    500     }
    501 
    502   fetch_32 = (ocr->is_big_endian
    503 	      ? simple_object_fetch_big_32
    504 	      : simple_object_fetch_little_32);
    505 
    506   nscns = ocr->nscns;
    507   strtab = NULL;
    508   strtab_size = 0;
    509   for (i = 0; i < nscns; ++i)
    510     {
    511       unsigned char *scnhdr;
    512       unsigned char *scnname;
    513       char namebuf[SCNNMLEN + 1];
    514       char *name;
    515       off_t scnptr;
    516       unsigned int size;
    517 
    518       scnhdr = scnbuf + i * scnhdr_size;
    519       scnname = scnhdr + offsetof (struct external_scnhdr, s_name);
    520       memcpy (namebuf, scnname, SCNNMLEN);
    521       namebuf[SCNNMLEN] = '\0';
    522       name = &namebuf[0];
    523       if (namebuf[0] == '/')
    524 	{
    525 	  size_t strindex;
    526 	  char *end;
    527 
    528 	  strindex = strtol (namebuf + 1, &end, 10);
    529 	  if (*end == '\0')
    530 	    {
    531 	      /* The real section name is found in the string
    532 		 table.  */
    533 	      if (strtab == NULL)
    534 		{
    535 		  strtab = simple_object_coff_read_strtab (sobj,
    536 							   &strtab_size,
    537 							   &errmsg, err);
    538 		  if (strtab == NULL)
    539 		    {
    540 		      XDELETEVEC (scnbuf);
    541 		      return errmsg;
    542 		    }
    543 		}
    544 
    545 	      if (strindex < 4 || strindex >= strtab_size)
    546 		{
    547 		  XDELETEVEC (strtab);
    548 		  XDELETEVEC (scnbuf);
    549 		  *err = 0;
    550 		  return "section string index out of range";
    551 		}
    552 
    553 	      name = strtab + strindex;
    554 	    }
    555 	}
    556 
    557       scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_scnptr));
    558       size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_size));
    559 
    560       if (!(*pfn) (data, name, scnptr, size))
    561 	break;
    562     }
    563 
    564   if (strtab != NULL)
    565     XDELETEVEC (strtab);
    566   XDELETEVEC (scnbuf);
    567 
    568   return NULL;
    569 }
    570 
    571 /* Fetch the attributes for an simple_object_read.  */
    572 
    573 static void *
    574 simple_object_coff_fetch_attributes (simple_object_read *sobj,
    575 				     const char **errmsg ATTRIBUTE_UNUSED,
    576 				     int *err ATTRIBUTE_UNUSED)
    577 {
    578   struct simple_object_coff_read *ocr =
    579     (struct simple_object_coff_read *) sobj->data;
    580   struct simple_object_coff_attributes *ret;
    581 
    582   ret = XNEW (struct simple_object_coff_attributes);
    583   ret->magic = ocr->magic;
    584   ret->is_big_endian = ocr->is_big_endian;
    585   ret->is_bigobj = ocr->is_bigobj;
    586   ret->flags = ocr->flags;
    587   return ret;
    588 }
    589 
    590 /* Release the private data for an simple_object_read.  */
    591 
    592 static void
    593 simple_object_coff_release_read (void *data)
    594 {
    595   XDELETE (data);
    596 }
    597 
    598 /* Compare two attributes structures.  */
    599 
    600 static const char *
    601 simple_object_coff_attributes_merge (void *todata, void *fromdata, int *err)
    602 {
    603   struct simple_object_coff_attributes *to =
    604     (struct simple_object_coff_attributes *) todata;
    605   struct simple_object_coff_attributes *from =
    606     (struct simple_object_coff_attributes *) fromdata;
    607 
    608   if (to->magic != from->magic
    609       || to->is_big_endian != from->is_big_endian
    610       || to->is_bigobj != from->is_bigobj)
    611     {
    612       *err = 0;
    613       return "COFF object format mismatch";
    614     }
    615   return NULL;
    616 }
    617 
    618 /* Release the private data for an attributes structure.  */
    619 
    620 static void
    621 simple_object_coff_release_attributes (void *data)
    622 {
    623   XDELETE (data);
    624 }
    625 
    626 /* Prepare to write out a file.  */
    627 
    628 static void *
    629 simple_object_coff_start_write (void *attributes_data,
    630 				const char **errmsg ATTRIBUTE_UNUSED,
    631 				int *err ATTRIBUTE_UNUSED)
    632 {
    633   struct simple_object_coff_attributes *attrs =
    634     (struct simple_object_coff_attributes *) attributes_data;
    635   struct simple_object_coff_attributes *ret;
    636 
    637   /* We're just going to record the attributes, but we need to make a
    638      copy because the user may delete them.  */
    639   ret = XNEW (struct simple_object_coff_attributes);
    640   *ret = *attrs;
    641   return ret;
    642 }
    643 
    644 /* Write out a BigObj COFF filehdr.  */
    645 
    646 static int
    647 simple_object_coff_write_filehdr_bigobj (simple_object_write *sobj,
    648 					 int descriptor,
    649 					 unsigned int nscns,
    650 					 size_t symtab_offset,
    651 					 unsigned int nsyms,
    652 					 const char **errmsg, int *err)
    653 {
    654   struct simple_object_coff_attributes *attrs =
    655     (struct simple_object_coff_attributes *) sobj->data;
    656   unsigned char hdrbuf[sizeof (struct external_filehdr_bigobj)];
    657   unsigned char *hdr;
    658   void (*set_16) (unsigned char *, unsigned short);
    659   void (*set_32) (unsigned char *, unsigned int);
    660 
    661   hdr = &hdrbuf[0];
    662 
    663   /* BigObj is always little-endian.  */
    664   set_16 = simple_object_set_little_16;
    665   set_32 = simple_object_set_little_32;
    666 
    667   memset (hdr, 0, sizeof (struct external_filehdr_bigobj));
    668 
    669   /* Set BigObj signatures.  */
    670   set_16 (hdr + offsetof (struct external_filehdr_bigobj, sig1), 0);
    671   set_16 (hdr + offsetof (struct external_filehdr_bigobj, sig2), 0xFFFF);
    672   set_16 (hdr + offsetof (struct external_filehdr_bigobj, version), 2);
    673   set_16 (hdr + offsetof (struct external_filehdr_bigobj, machine),
    674 	  attrs->magic);
    675   /* timdat left as zero.  */
    676   /* Copy ClassID.  */
    677   memcpy (hdr + offsetof (struct external_filehdr_bigobj, classid),
    678 	  bigobj_magic, 16);
    679   /* sizeofdata, flags, metadatasize, metadataoffset left as zero.  */
    680   set_32 (hdr + offsetof (struct external_filehdr_bigobj, nscns), nscns);
    681   set_32 (hdr + offsetof (struct external_filehdr_bigobj, symptr),
    682 	  symtab_offset);
    683   set_32 (hdr + offsetof (struct external_filehdr_bigobj, nsyms), nsyms);
    684 
    685   return simple_object_internal_write (descriptor, 0, hdrbuf,
    686 				       sizeof (struct external_filehdr_bigobj),
    687 				       errmsg, err);
    688 }
    689 
    690 /* Write out a COFF filehdr.  */
    691 
    692 static int
    693 simple_object_coff_write_filehdr (simple_object_write *sobj, int descriptor,
    694 				  unsigned int nscns, size_t symtab_offset,
    695 				  unsigned int nsyms, const char **errmsg,
    696 				  int *err)
    697 {
    698   struct simple_object_coff_attributes *attrs =
    699     (struct simple_object_coff_attributes *) sobj->data;
    700   unsigned char hdrbuf[sizeof (struct external_filehdr)];
    701   unsigned char *hdr;
    702   void (*set_16) (unsigned char *, unsigned short);
    703   void (*set_32) (unsigned char *, unsigned int);
    704 
    705   hdr = &hdrbuf[0];
    706 
    707   set_16 = (attrs->is_big_endian
    708 	    ? simple_object_set_big_16
    709 	    : simple_object_set_little_16);
    710   set_32 = (attrs->is_big_endian
    711 	    ? simple_object_set_big_32
    712 	    : simple_object_set_little_32);
    713 
    714   memset (hdr, 0, sizeof (struct external_filehdr));
    715 
    716   set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic);
    717   set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns);
    718   /* f_timdat left as zero.  */
    719   set_32 (hdr + offsetof (struct external_filehdr, f_symptr), symtab_offset);
    720   set_32 (hdr + offsetof (struct external_filehdr, f_nsyms), nsyms);
    721   /* f_opthdr left as zero.  */
    722   set_16 (hdr + offsetof (struct external_filehdr, f_flags), attrs->flags);
    723 
    724   return simple_object_internal_write (descriptor, 0, hdrbuf,
    725 				       sizeof (struct external_filehdr),
    726 				       errmsg, err);
    727 }
    728 
    729 /* Write out a COFF section header.  */
    730 
    731 static int
    732 simple_object_coff_write_scnhdr (simple_object_write *sobj, int descriptor,
    733 				 const char *name, size_t *name_offset,
    734 				 off_t scnhdr_offset, size_t scnsize,
    735 				 off_t offset, unsigned int align,
    736 				 const char **errmsg, int *err)
    737 {
    738   struct simple_object_coff_attributes *attrs =
    739     (struct simple_object_coff_attributes *) sobj->data;
    740   void (*set_32) (unsigned char *, unsigned int);
    741   unsigned char hdrbuf[sizeof (struct external_scnhdr)];
    742   unsigned char *hdr;
    743   size_t namelen;
    744   unsigned int flags;
    745 
    746   set_32 = (attrs->is_big_endian
    747 	    ? simple_object_set_big_32
    748 	    : simple_object_set_little_32);
    749 
    750   memset (hdrbuf, 0, sizeof hdrbuf);
    751   hdr = &hdrbuf[0];
    752 
    753   namelen = strlen (name);
    754   if (namelen <= SCNNMLEN)
    755     strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name), name,
    756 	     SCNNMLEN);
    757   else
    758     {
    759       snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name),
    760 		SCNNMLEN, "/%lu", (unsigned long) *name_offset);
    761       *name_offset += namelen + 1;
    762     }
    763 
    764   /* s_paddr left as zero.  */
    765   /* s_vaddr left as zero.  */
    766   set_32 (hdr + offsetof (struct external_scnhdr, s_size), scnsize);
    767   set_32 (hdr + offsetof (struct external_scnhdr, s_scnptr), offset);
    768   /* s_relptr left as zero.  */
    769   /* s_lnnoptr left as zero.  */
    770   /* s_nreloc left as zero.  */
    771   /* s_nlnno left as zero.  */
    772   flags = (STYP_DATA | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_SHARED
    773 	   | IMAGE_SCN_MEM_READ);
    774   /* PE can represent alignment up to 13.  */
    775   if (align > 13)
    776     align = 13;
    777   flags |= IMAGE_SCN_ALIGN_POWER_CONST(align);
    778   set_32 (hdr + offsetof (struct external_scnhdr, s_flags), flags);
    779 
    780   return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf,
    781 				       sizeof (struct external_scnhdr),
    782 				       errmsg, err);
    783 }
    784 
    785 /* Write out a complete COFF file.  */
    786 
    787 static const char *
    788 simple_object_coff_write_to_file (simple_object_write *sobj, int descriptor,
    789 				  int *err)
    790 {
    791   struct simple_object_coff_attributes *attrs =
    792     (struct simple_object_coff_attributes *) sobj->data;
    793   unsigned int nscns, secnum;
    794   simple_object_write_section *section;
    795   off_t scnhdr_offset;
    796   size_t symtab_offset;
    797   off_t secsym_offset;
    798   unsigned int nsyms;
    799   size_t offset;
    800   size_t name_offset;
    801   const char *errmsg;
    802   unsigned char strsizebuf[4];
    803   /* The interface doesn't give us access to the name of the input file
    804      yet.  We want to use its basename for the FILE symbol.  This is
    805      what 'gas' uses when told to assemble from stdin.  */
    806   const char *source_filename = "fake";
    807   size_t sflen;
    808   size_t symsize;
    809   void (*set_16) (unsigned char *, unsigned short);
    810   void (*set_32) (unsigned char *, unsigned int);
    811 
    812   /* Determine symbol size based on format.  */
    813   if (attrs->is_bigobj)
    814     symsize = sizeof (struct external_syment_bigobj);
    815   else
    816     symsize = sizeof (struct external_syment);
    817 
    818   set_16 = (attrs->is_big_endian
    819 	    ? simple_object_set_big_16
    820 	    : simple_object_set_little_16);
    821   set_32 = (attrs->is_big_endian
    822 	    ? simple_object_set_big_32
    823 	    : simple_object_set_little_32);
    824 
    825   nscns = 0;
    826   for (section = sobj->sections; section != NULL; section = section->next)
    827     ++nscns;
    828 
    829   if (attrs->is_bigobj)
    830     scnhdr_offset = sizeof (struct external_filehdr_bigobj);
    831   else
    832     scnhdr_offset = sizeof (struct external_filehdr);
    833   offset = scnhdr_offset + nscns * sizeof (struct external_scnhdr);
    834   name_offset = 4;
    835   for (section = sobj->sections; section != NULL; section = section->next)
    836     {
    837       size_t mask;
    838       size_t new_offset;
    839       size_t scnsize;
    840       struct simple_object_write_section_buffer *buffer;
    841 
    842       mask = (1U << section->align) - 1;
    843       new_offset = offset & mask;
    844       new_offset &= ~ mask;
    845       while (new_offset > offset)
    846 	{
    847 	  unsigned char zeroes[16];
    848 	  size_t write;
    849 
    850 	  memset (zeroes, 0, sizeof zeroes);
    851 	  write = new_offset - offset;
    852 	  if (write > sizeof zeroes)
    853 	    write = sizeof zeroes;
    854 	  if (!simple_object_internal_write (descriptor, offset, zeroes, write,
    855 					     &errmsg, err))
    856 	    return errmsg;
    857 	}
    858 
    859       scnsize = 0;
    860       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
    861 	{
    862 	  if (!simple_object_internal_write (descriptor, offset + scnsize,
    863 					     ((const unsigned char *)
    864 					      buffer->buffer),
    865 					     buffer->size, &errmsg, err))
    866 	    return errmsg;
    867 	  scnsize += buffer->size;
    868 	}
    869 
    870       if (!simple_object_coff_write_scnhdr (sobj, descriptor, section->name,
    871 					    &name_offset, scnhdr_offset,
    872 					    scnsize, offset, section->align,
    873 					    &errmsg, err))
    874 	return errmsg;
    875 
    876       scnhdr_offset += sizeof (struct external_scnhdr);
    877       offset += scnsize;
    878     }
    879 
    880   /* Symbol table is always half-word aligned.  */
    881   offset += (offset & 1);
    882   /* There is a file symbol and a section symbol per section,
    883      and each of these has a single auxiliary symbol following.  */
    884   nsyms = 2 * (nscns + 1);
    885   symtab_offset = offset;
    886   /* Advance across space reserved for symbol table to locate
    887      start of string table.  */
    888   offset += nsyms * symsize;
    889 
    890   /* Write out file symbol.  */
    891   if (attrs->is_bigobj)
    892     {
    893       union
    894       {
    895 	struct external_syment_bigobj sym;
    896 	union external_auxent_bigobj aux;
    897       } syms[2];
    898 
    899       memset (&syms[0], 0, sizeof (syms));
    900       strcpy ((char *)&syms[0].sym.e.e_name[0], ".file");
    901       set_32 (&syms[0].sym.e_scnum[0], IMAGE_SYM_DEBUG);
    902       set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
    903       syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_FILE;
    904       syms[0].sym.e_numaux[0] = 1;
    905       /* The name need not be nul-terminated if it fits into the x_fname field
    906 	 directly, but must be if it has to be placed into the string table.  */
    907       sflen = strlen (source_filename);
    908       if (sflen <= E_FILNMLEN)
    909 	memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen);
    910       else
    911 	{
    912 	  set_32 (&syms[1].aux.x_file.x_n.x_offset[0], name_offset);
    913 	  if (!simple_object_internal_write (descriptor, offset + name_offset,
    914 					     ((const unsigned char *)
    915 					      source_filename),
    916 					     sflen + 1, &errmsg, err))
    917 	    return errmsg;
    918 	  name_offset += strlen (source_filename) + 1;
    919 	}
    920       if (!simple_object_internal_write (descriptor, symtab_offset,
    921 					 (const unsigned char *) &syms[0],
    922 					 sizeof (syms), &errmsg, err))
    923 	return errmsg;
    924 
    925       /* Write the string table length, followed by the strings and section
    926 	 symbols in step with each other.  */
    927       set_32 (strsizebuf, name_offset);
    928       if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4,
    929 					 &errmsg, err))
    930 	return errmsg;
    931 
    932       name_offset = 4;
    933       secsym_offset = symtab_offset + sizeof (syms);
    934       memset (&syms[0], 0, sizeof (syms));
    935       set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
    936       syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_STATIC;
    937       syms[0].sym.e_numaux[0] = 1;
    938       secnum = 1;
    939 
    940       for (section = sobj->sections; section != NULL; section = section->next)
    941 	{
    942 	  size_t namelen;
    943 	  size_t scnsize;
    944 	  struct simple_object_write_section_buffer *buffer;
    945 
    946 	  namelen = strlen (section->name);
    947 	  set_32 (&syms[0].sym.e_scnum[0], secnum++);
    948 	  scnsize = 0;
    949 	  for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
    950 	    scnsize += buffer->size;
    951 	  set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
    952 	  if (namelen > SCNNMLEN)
    953 	    {
    954 	      set_32 (&syms[0].sym.e.e.e_zeroes[0], 0);
    955 	      set_32 (&syms[0].sym.e.e.e_offset[0], name_offset);
    956 	      if (!simple_object_internal_write (descriptor, offset + name_offset,
    957 						 ((const unsigned char *)
    958 						  section->name),
    959 						 namelen + 1, &errmsg, err))
    960 		return errmsg;
    961 	      name_offset += namelen + 1;
    962 	    }
    963 	  else
    964 	    {
    965 	      memcpy (&syms[0].sym.e.e_name[0], section->name,
    966 		      strlen (section->name));
    967 	      memset (&syms[0].sym.e.e_name[strlen (section->name)], 0,
    968 		      E_SYMNMLEN - strlen (section->name));
    969 	    }
    970 
    971 	  if (!simple_object_internal_write (descriptor, secsym_offset,
    972 					     (const unsigned char *) &syms[0],
    973 					     sizeof (syms), &errmsg, err))
    974 	    return errmsg;
    975 	  secsym_offset += sizeof (syms);
    976 	}
    977     }
    978   else
    979     {
    980       /* Regular COFF.  */
    981       union
    982       {
    983 	struct external_syment sym;
    984 	union external_auxent aux;
    985       } syms[2];
    986 
    987       memset (&syms[0], 0, sizeof (syms));
    988       strcpy ((char *)&syms[0].sym.e.e_name[0], ".file");
    989       set_16 (&syms[0].sym.e_scnum[0], IMAGE_SYM_DEBUG);
    990       set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
    991       syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_FILE;
    992       syms[0].sym.e_numaux[0] = 1;
    993       /* The name need not be nul-terminated if it fits into the x_fname field
    994 	 directly, but must be if it has to be placed into the string table.  */
    995       sflen = strlen (source_filename);
    996       if (sflen <= E_FILNMLEN)
    997 	memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen);
    998       else
    999 	{
   1000 	  set_32 (&syms[1].aux.x_file.x_n.x_offset[0], name_offset);
   1001 	  if (!simple_object_internal_write (descriptor, offset + name_offset,
   1002 					     ((const unsigned char *)
   1003 					      source_filename),
   1004 					     sflen + 1, &errmsg, err))
   1005 	    return errmsg;
   1006 	  name_offset += strlen (source_filename) + 1;
   1007 	}
   1008       if (!simple_object_internal_write (descriptor, symtab_offset,
   1009 					 (const unsigned char *) &syms[0],
   1010 					 sizeof (syms), &errmsg, err))
   1011 	return errmsg;
   1012 
   1013       /* Write the string table length, followed by the strings and section
   1014 	 symbols in step with each other.  */
   1015       set_32 (strsizebuf, name_offset);
   1016       if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4,
   1017 					 &errmsg, err))
   1018 	return errmsg;
   1019 
   1020       name_offset = 4;
   1021       secsym_offset = symtab_offset + sizeof (syms);
   1022       memset (&syms[0], 0, sizeof (syms));
   1023       set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
   1024       syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_STATIC;
   1025       syms[0].sym.e_numaux[0] = 1;
   1026       secnum = 1;
   1027 
   1028       for (section = sobj->sections; section != NULL; section = section->next)
   1029 	{
   1030 	  size_t namelen;
   1031 	  size_t scnsize;
   1032 	  struct simple_object_write_section_buffer *buffer;
   1033 
   1034 	  namelen = strlen (section->name);
   1035 	  set_16 (&syms[0].sym.e_scnum[0], secnum++);
   1036 	  scnsize = 0;
   1037 	  for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
   1038 	    scnsize += buffer->size;
   1039 	  set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
   1040 	  if (namelen > SCNNMLEN)
   1041 	    {
   1042 	      set_32 (&syms[0].sym.e.e.e_zeroes[0], 0);
   1043 	      set_32 (&syms[0].sym.e.e.e_offset[0], name_offset);
   1044 	      if (!simple_object_internal_write (descriptor, offset + name_offset,
   1045 						 ((const unsigned char *)
   1046 						  section->name),
   1047 						 namelen + 1, &errmsg, err))
   1048 		return errmsg;
   1049 	      name_offset += namelen + 1;
   1050 	    }
   1051 	  else
   1052 	    {
   1053 	      memcpy (&syms[0].sym.e.e_name[0], section->name,
   1054 		      strlen (section->name));
   1055 	      memset (&syms[0].sym.e.e_name[strlen (section->name)], 0,
   1056 		      E_SYMNMLEN - strlen (section->name));
   1057 	    }
   1058 
   1059 	  if (!simple_object_internal_write (descriptor, secsym_offset,
   1060 					     (const unsigned char *) &syms[0],
   1061 					     sizeof (syms), &errmsg, err))
   1062 	    return errmsg;
   1063 	  secsym_offset += sizeof (syms);
   1064 	}
   1065     }
   1066 
   1067   if (attrs->is_bigobj)
   1068     {
   1069       if (!simple_object_coff_write_filehdr_bigobj (sobj, descriptor, nscns,
   1070 						     symtab_offset, nsyms,
   1071 						     &errmsg, err))
   1072 	return errmsg;
   1073     }
   1074   else
   1075     {
   1076       if (!simple_object_coff_write_filehdr (sobj, descriptor, nscns,
   1077 					     symtab_offset, nsyms, &errmsg, err))
   1078 	return errmsg;
   1079     }
   1080 
   1081   return NULL;
   1082 }
   1083 
   1084 /* Release the private data for an simple_object_write structure.  */
   1085 
   1086 static void
   1087 simple_object_coff_release_write (void *data)
   1088 {
   1089   XDELETE (data);
   1090 }
   1091 
   1092 /* The COFF functions.  */
   1093 
   1094 const struct simple_object_functions simple_object_coff_functions =
   1095 {
   1096   simple_object_coff_match,
   1097   simple_object_coff_find_sections,
   1098   simple_object_coff_fetch_attributes,
   1099   simple_object_coff_release_read,
   1100   simple_object_coff_attributes_merge,
   1101   simple_object_coff_release_attributes,
   1102   simple_object_coff_start_write,
   1103   simple_object_coff_write_to_file,
   1104   simple_object_coff_release_write,
   1105   NULL
   1106 };
   1107