Home | History | Annotate | Line # | Download | only in libiberty
      1 /* simple-object-coff.c -- routines to manipulate COFF object files.
      2    Copyright (C) 2010-2025 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 /* Bits for filehdr f_flags field.  */
     61 
     62 #define F_EXEC			(0x0002)
     63 #define IMAGE_FILE_SYSTEM	(0x1000)
     64 #define IMAGE_FILE_DLL		(0x2000)
     65 
     66 /* COFF section header.  */
     67 
     68 struct external_scnhdr
     69 {
     70   unsigned char s_name[8];	/* section name				*/
     71   unsigned char s_paddr[4];	/* physical address, aliased s_nlib 	*/
     72   unsigned char s_vaddr[4];	/* virtual address			*/
     73   unsigned char s_size[4];	/* section size				*/
     74   unsigned char s_scnptr[4];	/* file ptr to raw data for section 	*/
     75   unsigned char s_relptr[4];	/* file ptr to relocation		*/
     76   unsigned char s_lnnoptr[4];	/* file ptr to line numbers		*/
     77   unsigned char s_nreloc[2];	/* number of relocation entries		*/
     78   unsigned char s_nlnno[2];	/* number of line number entries	*/
     79   unsigned char s_flags[4];	/* flags				*/
     80 };
     81 
     82 /* The length of the s_name field in struct external_scnhdr.  */
     83 
     84 #define SCNNMLEN (8)
     85 
     86 /* Bits for scnhdr s_flags field.  This includes some bits defined
     87    only for PE.  This may need to be moved into coff_magic.  */
     88 
     89 #define STYP_DATA			(1 << 6)
     90 #define IMAGE_SCN_MEM_DISCARDABLE	(1 << 25)
     91 #define IMAGE_SCN_MEM_SHARED		(1 << 28)
     92 #define IMAGE_SCN_MEM_READ		(1 << 30)
     93 
     94 #define IMAGE_SCN_ALIGN_POWER_BIT_POS	     20
     95 #define IMAGE_SCN_ALIGN_POWER_CONST(val)     \
     96   (((val) + 1) << IMAGE_SCN_ALIGN_POWER_BIT_POS)
     97 
     98 /* COFF symbol table entry.  */
     99 
    100 #define E_SYMNMLEN	8	/* # characters in a symbol name	*/
    101 
    102 struct external_syment
    103 {
    104   union
    105   {
    106     unsigned char e_name[E_SYMNMLEN];
    107 
    108     struct
    109     {
    110       unsigned char e_zeroes[4];
    111       unsigned char e_offset[4];
    112     } e;
    113   } e;
    114 
    115   unsigned char e_value[4];
    116   unsigned char e_scnum[2];
    117   unsigned char e_type[2];
    118   unsigned char e_sclass[1];
    119   unsigned char e_numaux[1];
    120 };
    121 
    122 /* Length allowed for filename in aux sym format 4.  */
    123 
    124 #define E_FILNMLEN	18
    125 
    126 /* Omits x_sym and other unused variants.  */
    127 
    128 union external_auxent
    129 {
    130   /* Aux sym format 4: file.  */
    131   union
    132   {
    133     char x_fname[E_FILNMLEN];
    134     struct
    135     {
    136       unsigned char x_zeroes[4];
    137       unsigned char x_offset[4];
    138     } x_n;
    139   } x_file;
    140   /* Aux sym format 5: section.  */
    141   struct
    142   {
    143     unsigned char x_scnlen[4];		/* section length		*/
    144     unsigned char x_nreloc[2];		/* # relocation entries		*/
    145     unsigned char x_nlinno[2];		/* # line numbers		*/
    146     unsigned char x_checksum[4];	/* section COMDAT checksum	*/
    147     unsigned char x_associated[2];	/* COMDAT assoc section index	*/
    148     unsigned char x_comdat[1];		/* COMDAT selection number	*/
    149   } x_scn;
    150 };
    151 
    152 /* Symbol-related constants.  */
    153 
    154 #define IMAGE_SYM_DEBUG		(-2)
    155 #define IMAGE_SYM_TYPE_NULL	(0)
    156 #define IMAGE_SYM_DTYPE_NULL	(0)
    157 #define IMAGE_SYM_CLASS_STATIC	(3)
    158 #define IMAGE_SYM_CLASS_FILE	(103)
    159 
    160 #define IMAGE_SYM_TYPE \
    161   ((IMAGE_SYM_DTYPE_NULL << 4) | IMAGE_SYM_TYPE_NULL)
    162 
    163 /* Private data for an simple_object_read.  */
    164 
    165 struct simple_object_coff_read
    166 {
    167   /* Magic number.  */
    168   unsigned short magic;
    169   /* Whether the file is big-endian.  */
    170   unsigned char is_big_endian;
    171   /* Number of sections.  */
    172   unsigned short nscns;
    173   /* File offset of symbol table.  */
    174   off_t symptr;
    175   /* Number of symbol table entries.  */
    176   unsigned int nsyms;
    177   /* Flags.  */
    178   unsigned short flags;
    179   /* Offset of section headers in file.  */
    180   off_t scnhdr_offset;
    181 };
    182 
    183 /* Private data for an simple_object_attributes.  */
    184 
    185 struct simple_object_coff_attributes
    186 {
    187   /* Magic number.  */
    188   unsigned short magic;
    189   /* Whether the file is big-endian.  */
    190   unsigned char is_big_endian;
    191   /* Flags.  */
    192   unsigned short flags;
    193 };
    194 
    195 /* There is no magic number which indicates a COFF file as opposed to
    196    any other sort of file.  Instead, each COFF file starts with a
    197    two-byte magic number which also indicates the type of the target.
    198    This struct holds a magic number as well as characteristics of that
    199    COFF format.  */
    200 
    201 struct coff_magic_struct
    202 {
    203   /* Magic number.  */
    204   unsigned short magic;
    205   /* Whether this magic number is for a big-endian file.  */
    206   unsigned char is_big_endian;
    207   /* Flag bits, in the f_flags fields, which indicates that this file
    208      is not a relocatable object file.  There is no flag which
    209      specifically indicates a relocatable object file, it is only
    210      implied by the absence of these flags.  */
    211   unsigned short non_object_flags;
    212 };
    213 
    214 /* This is a list of the COFF magic numbers which we recognize, namely
    215    the ones used on Windows.  More can be added as needed.  */
    216 
    217 static const struct coff_magic_struct coff_magic[] =
    218 {
    219   /* i386.  */
    220   { 0x14c, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL },
    221   /* x86_64.  */
    222   { 0x8664, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL },
    223   /* AArch64.  */
    224   { 0xaa64, 0, F_EXEC | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL }
    225 };
    226 
    227 /* See if we have a COFF file.  */
    228 
    229 static void *
    230 simple_object_coff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
    231 			  int descriptor, off_t offset,
    232 			  const char *segment_name ATTRIBUTE_UNUSED,
    233 			  const char **errmsg, int *err)
    234 {
    235   size_t c;
    236   unsigned short magic_big;
    237   unsigned short magic_little;
    238   unsigned short magic;
    239   size_t i;
    240   int is_big_endian;
    241   unsigned short (*fetch_16) (const unsigned char *);
    242   unsigned int (*fetch_32) (const unsigned char *);
    243   unsigned char hdrbuf[sizeof (struct external_filehdr)];
    244   unsigned short flags;
    245   struct simple_object_coff_read *ocr;
    246 
    247   c = sizeof (coff_magic) / sizeof (coff_magic[0]);
    248   magic_big = simple_object_fetch_big_16 (header);
    249   magic_little = simple_object_fetch_little_16 (header);
    250   for (i = 0; i < c; ++i)
    251     {
    252       if (coff_magic[i].is_big_endian
    253 	  ? coff_magic[i].magic == magic_big
    254 	  : coff_magic[i].magic == magic_little)
    255 	break;
    256     }
    257   if (i >= c)
    258     {
    259       *errmsg = NULL;
    260       *err = 0;
    261       return NULL;
    262     }
    263   is_big_endian = coff_magic[i].is_big_endian;
    264 
    265   magic = is_big_endian ? magic_big : magic_little;
    266   fetch_16 = (is_big_endian
    267 	      ? simple_object_fetch_big_16
    268 	      : simple_object_fetch_little_16);
    269   fetch_32 = (is_big_endian
    270 	      ? simple_object_fetch_big_32
    271 	      : simple_object_fetch_little_32);
    272 
    273   if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof hdrbuf,
    274 				    errmsg, err))
    275     return NULL;
    276 
    277   flags = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_flags));
    278   if ((flags & coff_magic[i].non_object_flags) != 0)
    279     {
    280       *errmsg = "not relocatable object file";
    281       *err = 0;
    282       return NULL;
    283     }
    284 
    285   ocr = XNEW (struct simple_object_coff_read);
    286   ocr->magic = magic;
    287   ocr->is_big_endian = is_big_endian;
    288   ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns));
    289   ocr->symptr = fetch_32 (hdrbuf
    290 			  + offsetof (struct external_filehdr, f_symptr));
    291   ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, f_nsyms));
    292   ocr->flags = flags;
    293   ocr->scnhdr_offset = (sizeof (struct external_filehdr)
    294 			+ fetch_16 (hdrbuf + offsetof (struct external_filehdr,
    295 						       f_opthdr)));
    296 
    297   return (void *) ocr;
    298 }
    299 
    300 /* Read the string table in a COFF file.  */
    301 
    302 static char *
    303 simple_object_coff_read_strtab (simple_object_read *sobj, size_t *strtab_size,
    304 				const char **errmsg, int *err)
    305 {
    306   struct simple_object_coff_read *ocr =
    307     (struct simple_object_coff_read *) sobj->data;
    308   off_t strtab_offset;
    309   unsigned char strsizebuf[4];
    310   size_t strsize;
    311   char *strtab;
    312 
    313   strtab_offset = sobj->offset + ocr->symptr
    314 		  + ocr->nsyms * sizeof (struct external_syment);
    315   if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
    316 				    strsizebuf, 4, errmsg, err))
    317     return NULL;
    318   strsize = (ocr->is_big_endian
    319 	     ? simple_object_fetch_big_32 (strsizebuf)
    320 	     : simple_object_fetch_little_32 (strsizebuf));
    321   strtab = XNEWVEC (char, strsize);
    322   if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
    323 				    (unsigned char *) strtab, strsize, errmsg,
    324 				    err))
    325     {
    326       XDELETEVEC (strtab);
    327       return NULL;
    328     }
    329   *strtab_size = strsize;
    330   return strtab;
    331 }
    332 
    333 /* Find all sections in a COFF file.  */
    334 
    335 static const char *
    336 simple_object_coff_find_sections (simple_object_read *sobj,
    337 				  int (*pfn) (void *, const char *,
    338 					      off_t offset, off_t length),
    339 				  void *data,
    340 				  int *err)
    341 {
    342   struct simple_object_coff_read *ocr =
    343     (struct simple_object_coff_read *) sobj->data;
    344   size_t scnhdr_size;
    345   unsigned char *scnbuf;
    346   const char *errmsg;
    347   unsigned int (*fetch_32) (const unsigned char *);
    348   unsigned int nscns;
    349   char *strtab;
    350   size_t strtab_size;
    351   unsigned int i;
    352 
    353   scnhdr_size = sizeof (struct external_scnhdr);
    354   scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns);
    355   if (!simple_object_internal_read (sobj->descriptor,
    356 				    sobj->offset + ocr->scnhdr_offset,
    357 				    scnbuf, scnhdr_size * ocr->nscns, &errmsg,
    358 				    err))
    359     {
    360       XDELETEVEC (scnbuf);
    361       return errmsg;
    362     }
    363 
    364   fetch_32 = (ocr->is_big_endian
    365 	      ? simple_object_fetch_big_32
    366 	      : simple_object_fetch_little_32);
    367 
    368   nscns = ocr->nscns;
    369   strtab = NULL;
    370   strtab_size = 0;
    371   for (i = 0; i < nscns; ++i)
    372     {
    373       unsigned char *scnhdr;
    374       unsigned char *scnname;
    375       char namebuf[SCNNMLEN + 1];
    376       char *name;
    377       off_t scnptr;
    378       unsigned int size;
    379 
    380       scnhdr = scnbuf + i * scnhdr_size;
    381       scnname = scnhdr + offsetof (struct external_scnhdr, s_name);
    382       memcpy (namebuf, scnname, SCNNMLEN);
    383       namebuf[SCNNMLEN] = '\0';
    384       name = &namebuf[0];
    385       if (namebuf[0] == '/')
    386 	{
    387 	  size_t strindex;
    388 	  char *end;
    389 
    390 	  strindex = strtol (namebuf + 1, &end, 10);
    391 	  if (*end == '\0')
    392 	    {
    393 	      /* The real section name is found in the string
    394 		 table.  */
    395 	      if (strtab == NULL)
    396 		{
    397 		  strtab = simple_object_coff_read_strtab (sobj,
    398 							   &strtab_size,
    399 							   &errmsg, err);
    400 		  if (strtab == NULL)
    401 		    {
    402 		      XDELETEVEC (scnbuf);
    403 		      return errmsg;
    404 		    }
    405 		}
    406 
    407 	      if (strindex < 4 || strindex >= strtab_size)
    408 		{
    409 		  XDELETEVEC (strtab);
    410 		  XDELETEVEC (scnbuf);
    411 		  *err = 0;
    412 		  return "section string index out of range";
    413 		}
    414 
    415 	      name = strtab + strindex;
    416 	    }
    417 	}
    418 
    419       scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_scnptr));
    420       size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_size));
    421 
    422       if (!(*pfn) (data, name, scnptr, size))
    423 	break;
    424     }
    425 
    426   if (strtab != NULL)
    427     XDELETEVEC (strtab);
    428   XDELETEVEC (scnbuf);
    429 
    430   return NULL;
    431 }
    432 
    433 /* Fetch the attributes for an simple_object_read.  */
    434 
    435 static void *
    436 simple_object_coff_fetch_attributes (simple_object_read *sobj,
    437 				     const char **errmsg ATTRIBUTE_UNUSED,
    438 				     int *err ATTRIBUTE_UNUSED)
    439 {
    440   struct simple_object_coff_read *ocr =
    441     (struct simple_object_coff_read *) sobj->data;
    442   struct simple_object_coff_attributes *ret;
    443 
    444   ret = XNEW (struct simple_object_coff_attributes);
    445   ret->magic = ocr->magic;
    446   ret->is_big_endian = ocr->is_big_endian;
    447   ret->flags = ocr->flags;
    448   return ret;
    449 }
    450 
    451 /* Release the private data for an simple_object_read.  */
    452 
    453 static void
    454 simple_object_coff_release_read (void *data)
    455 {
    456   XDELETE (data);
    457 }
    458 
    459 /* Compare two attributes structures.  */
    460 
    461 static const char *
    462 simple_object_coff_attributes_merge (void *todata, void *fromdata, int *err)
    463 {
    464   struct simple_object_coff_attributes *to =
    465     (struct simple_object_coff_attributes *) todata;
    466   struct simple_object_coff_attributes *from =
    467     (struct simple_object_coff_attributes *) fromdata;
    468 
    469   if (to->magic != from->magic || to->is_big_endian != from->is_big_endian)
    470     {
    471       *err = 0;
    472       return "COFF object format mismatch";
    473     }
    474   return NULL;
    475 }
    476 
    477 /* Release the private data for an attributes structure.  */
    478 
    479 static void
    480 simple_object_coff_release_attributes (void *data)
    481 {
    482   XDELETE (data);
    483 }
    484 
    485 /* Prepare to write out a file.  */
    486 
    487 static void *
    488 simple_object_coff_start_write (void *attributes_data,
    489 				const char **errmsg ATTRIBUTE_UNUSED,
    490 				int *err ATTRIBUTE_UNUSED)
    491 {
    492   struct simple_object_coff_attributes *attrs =
    493     (struct simple_object_coff_attributes *) attributes_data;
    494   struct simple_object_coff_attributes *ret;
    495 
    496   /* We're just going to record the attributes, but we need to make a
    497      copy because the user may delete them.  */
    498   ret = XNEW (struct simple_object_coff_attributes);
    499   *ret = *attrs;
    500   return ret;
    501 }
    502 
    503 /* Write out a COFF filehdr.  */
    504 
    505 static int
    506 simple_object_coff_write_filehdr (simple_object_write *sobj, int descriptor,
    507 				  unsigned int nscns, size_t symtab_offset,
    508 				  unsigned int nsyms, const char **errmsg,
    509 				  int *err)
    510 {
    511   struct simple_object_coff_attributes *attrs =
    512     (struct simple_object_coff_attributes *) sobj->data;
    513   unsigned char hdrbuf[sizeof (struct external_filehdr)];
    514   unsigned char *hdr;
    515   void (*set_16) (unsigned char *, unsigned short);
    516   void (*set_32) (unsigned char *, unsigned int);
    517 
    518   hdr = &hdrbuf[0];
    519 
    520   set_16 = (attrs->is_big_endian
    521 	    ? simple_object_set_big_16
    522 	    : simple_object_set_little_16);
    523   set_32 = (attrs->is_big_endian
    524 	    ? simple_object_set_big_32
    525 	    : simple_object_set_little_32);
    526 
    527   memset (hdr, 0, sizeof (struct external_filehdr));
    528 
    529   set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic);
    530   set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns);
    531   /* f_timdat left as zero.  */
    532   set_32 (hdr + offsetof (struct external_filehdr, f_symptr), symtab_offset);
    533   set_32 (hdr + offsetof (struct external_filehdr, f_nsyms), nsyms);
    534   /* f_opthdr left as zero.  */
    535   set_16 (hdr + offsetof (struct external_filehdr, f_flags), attrs->flags);
    536 
    537   return simple_object_internal_write (descriptor, 0, hdrbuf,
    538 				       sizeof (struct external_filehdr),
    539 				       errmsg, err);
    540 }
    541 
    542 /* Write out a COFF section header.  */
    543 
    544 static int
    545 simple_object_coff_write_scnhdr (simple_object_write *sobj, int descriptor,
    546 				 const char *name, size_t *name_offset,
    547 				 off_t scnhdr_offset, size_t scnsize,
    548 				 off_t offset, unsigned int align,
    549 				 const char **errmsg, int *err)
    550 {
    551   struct simple_object_coff_attributes *attrs =
    552     (struct simple_object_coff_attributes *) sobj->data;
    553   void (*set_32) (unsigned char *, unsigned int);
    554   unsigned char hdrbuf[sizeof (struct external_scnhdr)];
    555   unsigned char *hdr;
    556   size_t namelen;
    557   unsigned int flags;
    558 
    559   set_32 = (attrs->is_big_endian
    560 	    ? simple_object_set_big_32
    561 	    : simple_object_set_little_32);
    562 
    563   memset (hdrbuf, 0, sizeof hdrbuf);
    564   hdr = &hdrbuf[0];
    565 
    566   namelen = strlen (name);
    567   if (namelen <= SCNNMLEN)
    568     strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name), name,
    569 	     SCNNMLEN);
    570   else
    571     {
    572       snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name),
    573 		SCNNMLEN, "/%lu", (unsigned long) *name_offset);
    574       *name_offset += namelen + 1;
    575     }
    576 
    577   /* s_paddr left as zero.  */
    578   /* s_vaddr left as zero.  */
    579   set_32 (hdr + offsetof (struct external_scnhdr, s_size), scnsize);
    580   set_32 (hdr + offsetof (struct external_scnhdr, s_scnptr), offset);
    581   /* s_relptr left as zero.  */
    582   /* s_lnnoptr left as zero.  */
    583   /* s_nreloc left as zero.  */
    584   /* s_nlnno left as zero.  */
    585   flags = (STYP_DATA | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_SHARED
    586 	   | IMAGE_SCN_MEM_READ);
    587   /* PE can represent alignment up to 13.  */
    588   if (align > 13)
    589     align = 13;
    590   flags |= IMAGE_SCN_ALIGN_POWER_CONST(align);
    591   set_32 (hdr + offsetof (struct external_scnhdr, s_flags), flags);
    592 
    593   return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf,
    594 				       sizeof (struct external_scnhdr),
    595 				       errmsg, err);
    596 }
    597 
    598 /* Write out a complete COFF file.  */
    599 
    600 static const char *
    601 simple_object_coff_write_to_file (simple_object_write *sobj, int descriptor,
    602 				  int *err)
    603 {
    604   struct simple_object_coff_attributes *attrs =
    605     (struct simple_object_coff_attributes *) sobj->data;
    606   unsigned int nscns, secnum;
    607   simple_object_write_section *section;
    608   off_t scnhdr_offset;
    609   size_t symtab_offset;
    610   off_t secsym_offset;
    611   unsigned int nsyms;
    612   size_t offset;
    613   size_t name_offset;
    614   const char *errmsg;
    615   unsigned char strsizebuf[4];
    616   /* The interface doesn't give us access to the name of the input file
    617      yet.  We want to use its basename for the FILE symbol.  This is
    618      what 'gas' uses when told to assemble from stdin.  */
    619   const char *source_filename = "fake";
    620   size_t sflen;
    621   union
    622   {
    623     struct external_syment sym;
    624     union external_auxent aux;
    625   } syms[2];
    626   void (*set_16) (unsigned char *, unsigned short);
    627   void (*set_32) (unsigned char *, unsigned int);
    628 
    629   set_16 = (attrs->is_big_endian
    630 	    ? simple_object_set_big_16
    631 	    : simple_object_set_little_16);
    632   set_32 = (attrs->is_big_endian
    633 	    ? simple_object_set_big_32
    634 	    : simple_object_set_little_32);
    635 
    636   nscns = 0;
    637   for (section = sobj->sections; section != NULL; section = section->next)
    638     ++nscns;
    639 
    640   scnhdr_offset = sizeof (struct external_filehdr);
    641   offset = scnhdr_offset + nscns * sizeof (struct external_scnhdr);
    642   name_offset = 4;
    643   for (section = sobj->sections; section != NULL; section = section->next)
    644     {
    645       size_t mask;
    646       size_t new_offset;
    647       size_t scnsize;
    648       struct simple_object_write_section_buffer *buffer;
    649 
    650       mask = (1U << section->align) - 1;
    651       new_offset = offset & mask;
    652       new_offset &= ~ mask;
    653       while (new_offset > offset)
    654 	{
    655 	  unsigned char zeroes[16];
    656 	  size_t write;
    657 
    658 	  memset (zeroes, 0, sizeof zeroes);
    659 	  write = new_offset - offset;
    660 	  if (write > sizeof zeroes)
    661 	    write = sizeof zeroes;
    662 	  if (!simple_object_internal_write (descriptor, offset, zeroes, write,
    663 					     &errmsg, err))
    664 	    return errmsg;
    665 	}
    666 
    667       scnsize = 0;
    668       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
    669 	{
    670 	  if (!simple_object_internal_write (descriptor, offset + scnsize,
    671 					     ((const unsigned char *)
    672 					      buffer->buffer),
    673 					     buffer->size, &errmsg, err))
    674 	    return errmsg;
    675 	  scnsize += buffer->size;
    676 	}
    677 
    678       if (!simple_object_coff_write_scnhdr (sobj, descriptor, section->name,
    679 					    &name_offset, scnhdr_offset,
    680 					    scnsize, offset, section->align,
    681 					    &errmsg, err))
    682 	return errmsg;
    683 
    684       scnhdr_offset += sizeof (struct external_scnhdr);
    685       offset += scnsize;
    686     }
    687 
    688   /* Symbol table is always half-word aligned.  */
    689   offset += (offset & 1);
    690   /* There is a file symbol and a section symbol per section,
    691      and each of these has a single auxiliary symbol following.  */
    692   nsyms = 2 * (nscns + 1);
    693   symtab_offset = offset;
    694   /* Advance across space reserved for symbol table to locate
    695      start of string table.  */
    696   offset += nsyms * sizeof (struct external_syment);
    697 
    698   /* Write out file symbol.  */
    699   memset (&syms[0], 0, sizeof (syms));
    700   strcpy ((char *)&syms[0].sym.e.e_name[0], ".file");
    701   set_16 (&syms[0].sym.e_scnum[0], IMAGE_SYM_DEBUG);
    702   set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
    703   syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_FILE;
    704   syms[0].sym.e_numaux[0] = 1;
    705   /* The name need not be nul-terminated if it fits into the x_fname field
    706      directly, but must be if it has to be placed into the string table.  */
    707   sflen = strlen (source_filename);
    708   if (sflen <= E_FILNMLEN)
    709     memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen);
    710   else
    711     {
    712       set_32 (&syms[1].aux.x_file.x_n.x_offset[0], name_offset);
    713       if (!simple_object_internal_write (descriptor, offset + name_offset,
    714 					 ((const unsigned char *)
    715 					  source_filename),
    716 					 sflen + 1, &errmsg, err))
    717 	return errmsg;
    718       name_offset += strlen (source_filename) + 1;
    719     }
    720   if (!simple_object_internal_write (descriptor, symtab_offset,
    721 				     (const unsigned char *) &syms[0],
    722 				     sizeof (syms), &errmsg, err))
    723     return errmsg;
    724 
    725   /* Write the string table length, followed by the strings and section
    726      symbols in step with each other.  */
    727   set_32 (strsizebuf, name_offset);
    728   if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4,
    729 				     &errmsg, err))
    730     return errmsg;
    731 
    732   name_offset = 4;
    733   secsym_offset = symtab_offset + sizeof (syms);
    734   memset (&syms[0], 0, sizeof (syms));
    735   set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
    736   syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_STATIC;
    737   syms[0].sym.e_numaux[0] = 1;
    738   secnum = 1;
    739 
    740   for (section = sobj->sections; section != NULL; section = section->next)
    741     {
    742       size_t namelen;
    743       size_t scnsize;
    744       struct simple_object_write_section_buffer *buffer;
    745 
    746       namelen = strlen (section->name);
    747       set_16 (&syms[0].sym.e_scnum[0], secnum++);
    748       scnsize = 0;
    749       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
    750 	scnsize += buffer->size;
    751       set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
    752       if (namelen > SCNNMLEN)
    753 	{
    754 	  set_32 (&syms[0].sym.e.e.e_zeroes[0], 0);
    755 	  set_32 (&syms[0].sym.e.e.e_offset[0], name_offset);
    756 	  if (!simple_object_internal_write (descriptor, offset + name_offset,
    757 					     ((const unsigned char *)
    758 					      section->name),
    759 					     namelen + 1, &errmsg, err))
    760 	    return errmsg;
    761 	  name_offset += namelen + 1;
    762 	}
    763       else
    764 	{
    765 	  memcpy (&syms[0].sym.e.e_name[0], section->name,
    766 		  strlen (section->name));
    767 	  memset (&syms[0].sym.e.e_name[strlen (section->name)], 0,
    768 		  E_SYMNMLEN - strlen (section->name));
    769 	}
    770 
    771       if (!simple_object_internal_write (descriptor, secsym_offset,
    772 					 (const unsigned char *) &syms[0],
    773 					 sizeof (syms), &errmsg, err))
    774 	return errmsg;
    775       secsym_offset += sizeof (syms);
    776     }
    777 
    778   if (!simple_object_coff_write_filehdr (sobj, descriptor, nscns,
    779 					 symtab_offset, nsyms, &errmsg, err))
    780     return errmsg;
    781 
    782   return NULL;
    783 }
    784 
    785 /* Release the private data for an simple_object_write structure.  */
    786 
    787 static void
    788 simple_object_coff_release_write (void *data)
    789 {
    790   XDELETE (data);
    791 }
    792 
    793 /* The COFF functions.  */
    794 
    795 const struct simple_object_functions simple_object_coff_functions =
    796 {
    797   simple_object_coff_match,
    798   simple_object_coff_find_sections,
    799   simple_object_coff_fetch_attributes,
    800   simple_object_coff_release_read,
    801   simple_object_coff_attributes_merge,
    802   simple_object_coff_release_attributes,
    803   simple_object_coff_start_write,
    804   simple_object_coff_write_to_file,
    805   simple_object_coff_release_write,
    806   NULL
    807 };
    808