Home | History | Annotate | Download | only in libiberty
      1 /* simple-object-coff.c -- routines to manipulate COFF object files.
      2    Copyright 2010 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 };
    224 
    225 /* See if we have a COFF file.  */
    226 
    227 static void *
    228 simple_object_coff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
    229 			  int descriptor, off_t offset,
    230 			  const char *segment_name ATTRIBUTE_UNUSED,
    231 			  const char **errmsg, int *err)
    232 {
    233   size_t c;
    234   unsigned short magic_big;
    235   unsigned short magic_little;
    236   unsigned short magic;
    237   size_t i;
    238   int is_big_endian;
    239   unsigned short (*fetch_16) (const unsigned char *);
    240   unsigned int (*fetch_32) (const unsigned char *);
    241   unsigned char hdrbuf[sizeof (struct external_filehdr)];
    242   unsigned short flags;
    243   struct simple_object_coff_read *ocr;
    244 
    245   c = sizeof (coff_magic) / sizeof (coff_magic[0]);
    246   magic_big = simple_object_fetch_big_16 (header);
    247   magic_little = simple_object_fetch_little_16 (header);
    248   for (i = 0; i < c; ++i)
    249     {
    250       if (coff_magic[i].is_big_endian
    251 	  ? coff_magic[i].magic == magic_big
    252 	  : coff_magic[i].magic == magic_little)
    253 	break;
    254     }
    255   if (i >= c)
    256     {
    257       *errmsg = NULL;
    258       *err = 0;
    259       return NULL;
    260     }
    261   is_big_endian = coff_magic[i].is_big_endian;
    262 
    263   magic = is_big_endian ? magic_big : magic_little;
    264   fetch_16 = (is_big_endian
    265 	      ? simple_object_fetch_big_16
    266 	      : simple_object_fetch_little_16);
    267   fetch_32 = (is_big_endian
    268 	      ? simple_object_fetch_big_32
    269 	      : simple_object_fetch_little_32);
    270 
    271   if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof hdrbuf,
    272 				    errmsg, err))
    273     return NULL;
    274 
    275   flags = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_flags));
    276   if ((flags & coff_magic[i].non_object_flags) != 0)
    277     {
    278       *errmsg = "not relocatable object file";
    279       *err = 0;
    280       return NULL;
    281     }
    282 
    283   ocr = XNEW (struct simple_object_coff_read);
    284   ocr->magic = magic;
    285   ocr->is_big_endian = is_big_endian;
    286   ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns));
    287   ocr->symptr = fetch_32 (hdrbuf
    288 			  + offsetof (struct external_filehdr, f_symptr));
    289   ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr, f_nsyms));
    290   ocr->flags = flags;
    291   ocr->scnhdr_offset = (sizeof (struct external_filehdr)
    292 			+ fetch_16 (hdrbuf + offsetof (struct external_filehdr,
    293 						       f_opthdr)));
    294 
    295   return (void *) ocr;
    296 }
    297 
    298 /* Read the string table in a COFF file.  */
    299 
    300 static char *
    301 simple_object_coff_read_strtab (simple_object_read *sobj, size_t *strtab_size,
    302 				const char **errmsg, int *err)
    303 {
    304   struct simple_object_coff_read *ocr =
    305     (struct simple_object_coff_read *) sobj->data;
    306   off_t strtab_offset;
    307   unsigned char strsizebuf[4];
    308   size_t strsize;
    309   char *strtab;
    310 
    311   strtab_offset = sobj->offset + ocr->symptr
    312 		  + ocr->nsyms * sizeof (struct external_syment);
    313   if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
    314 				    strsizebuf, 4, errmsg, err))
    315     return NULL;
    316   strsize = (ocr->is_big_endian
    317 	     ? simple_object_fetch_big_32 (strsizebuf)
    318 	     : simple_object_fetch_little_32 (strsizebuf));
    319   strtab = XNEWVEC (char, strsize);
    320   if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
    321 				    (unsigned char *) strtab, strsize, errmsg,
    322 				    err))
    323     {
    324       XDELETEVEC (strtab);
    325       return NULL;
    326     }
    327   *strtab_size = strsize;
    328   return strtab;
    329 }
    330 
    331 /* Find all sections in a COFF file.  */
    332 
    333 static const char *
    334 simple_object_coff_find_sections (simple_object_read *sobj,
    335 				  int (*pfn) (void *, const char *,
    336 					      off_t offset, off_t length),
    337 				  void *data,
    338 				  int *err)
    339 {
    340   struct simple_object_coff_read *ocr =
    341     (struct simple_object_coff_read *) sobj->data;
    342   size_t scnhdr_size;
    343   unsigned char *scnbuf;
    344   const char *errmsg;
    345   unsigned int (*fetch_32) (const unsigned char *);
    346   unsigned int nscns;
    347   char *strtab;
    348   size_t strtab_size;
    349   unsigned int i;
    350 
    351   scnhdr_size = sizeof (struct external_scnhdr);
    352   scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns);
    353   if (!simple_object_internal_read (sobj->descriptor,
    354 				    sobj->offset + ocr->scnhdr_offset,
    355 				    scnbuf, scnhdr_size * ocr->nscns, &errmsg,
    356 				    err))
    357     {
    358       XDELETEVEC (scnbuf);
    359       return errmsg;
    360     }
    361 
    362   fetch_32 = (ocr->is_big_endian
    363 	      ? simple_object_fetch_big_32
    364 	      : simple_object_fetch_little_32);
    365 
    366   nscns = ocr->nscns;
    367   strtab = NULL;
    368   strtab_size = 0;
    369   for (i = 0; i < nscns; ++i)
    370     {
    371       unsigned char *scnhdr;
    372       unsigned char *scnname;
    373       char namebuf[SCNNMLEN + 1];
    374       char *name;
    375       off_t scnptr;
    376       unsigned int size;
    377 
    378       scnhdr = scnbuf + i * scnhdr_size;
    379       scnname = scnhdr + offsetof (struct external_scnhdr, s_name);
    380       memcpy (namebuf, scnname, SCNNMLEN);
    381       namebuf[SCNNMLEN] = '\0';
    382       name = &namebuf[0];
    383       if (namebuf[0] == '/')
    384 	{
    385 	  size_t strindex;
    386 	  char *end;
    387 
    388 	  strindex = strtol (namebuf + 1, &end, 10);
    389 	  if (*end == '\0')
    390 	    {
    391 	      /* The real section name is found in the string
    392 		 table.  */
    393 	      if (strtab == NULL)
    394 		{
    395 		  strtab = simple_object_coff_read_strtab (sobj,
    396 							   &strtab_size,
    397 							   &errmsg, err);
    398 		  if (strtab == NULL)
    399 		    {
    400 		      XDELETEVEC (scnbuf);
    401 		      return errmsg;
    402 		    }
    403 		}
    404 
    405 	      if (strindex < 4 || strindex >= strtab_size)
    406 		{
    407 		  XDELETEVEC (strtab);
    408 		  XDELETEVEC (scnbuf);
    409 		  *err = 0;
    410 		  return "section string index out of range";
    411 		}
    412 
    413 	      name = strtab + strindex;
    414 	    }
    415 	}
    416 
    417       scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_scnptr));
    418       size = fetch_32 (scnhdr + offsetof (struct external_scnhdr, s_size));
    419 
    420       if (!(*pfn) (data, name, scnptr, size))
    421 	break;
    422     }
    423 
    424   if (strtab != NULL)
    425     XDELETEVEC (strtab);
    426   XDELETEVEC (scnbuf);
    427 
    428   return NULL;
    429 }
    430 
    431 /* Fetch the attributes for an simple_object_read.  */
    432 
    433 static void *
    434 simple_object_coff_fetch_attributes (simple_object_read *sobj,
    435 				     const char **errmsg ATTRIBUTE_UNUSED,
    436 				     int *err ATTRIBUTE_UNUSED)
    437 {
    438   struct simple_object_coff_read *ocr =
    439     (struct simple_object_coff_read *) sobj->data;
    440   struct simple_object_coff_attributes *ret;
    441 
    442   ret = XNEW (struct simple_object_coff_attributes);
    443   ret->magic = ocr->magic;
    444   ret->is_big_endian = ocr->is_big_endian;
    445   ret->flags = ocr->flags;
    446   return ret;
    447 }
    448 
    449 /* Release the private data for an simple_object_read.  */
    450 
    451 static void
    452 simple_object_coff_release_read (void *data)
    453 {
    454   XDELETE (data);
    455 }
    456 
    457 /* Compare two attributes structures.  */
    458 
    459 static const char *
    460 simple_object_coff_attributes_merge (void *todata, void *fromdata, int *err)
    461 {
    462   struct simple_object_coff_attributes *to =
    463     (struct simple_object_coff_attributes *) todata;
    464   struct simple_object_coff_attributes *from =
    465     (struct simple_object_coff_attributes *) fromdata;
    466 
    467   if (to->magic != from->magic || to->is_big_endian != from->is_big_endian)
    468     {
    469       *err = 0;
    470       return "COFF object format mismatch";
    471     }
    472   return NULL;
    473 }
    474 
    475 /* Release the private data for an attributes structure.  */
    476 
    477 static void
    478 simple_object_coff_release_attributes (void *data)
    479 {
    480   XDELETE (data);
    481 }
    482 
    483 /* Prepare to write out a file.  */
    484 
    485 static void *
    486 simple_object_coff_start_write (void *attributes_data,
    487 				const char **errmsg ATTRIBUTE_UNUSED,
    488 				int *err ATTRIBUTE_UNUSED)
    489 {
    490   struct simple_object_coff_attributes *attrs =
    491     (struct simple_object_coff_attributes *) attributes_data;
    492   struct simple_object_coff_attributes *ret;
    493 
    494   /* We're just going to record the attributes, but we need to make a
    495      copy because the user may delete them.  */
    496   ret = XNEW (struct simple_object_coff_attributes);
    497   *ret = *attrs;
    498   return ret;
    499 }
    500 
    501 /* Write out a COFF filehdr.  */
    502 
    503 static int
    504 simple_object_coff_write_filehdr (simple_object_write *sobj, int descriptor,
    505 				  unsigned int nscns, size_t symtab_offset,
    506 				  unsigned int nsyms, const char **errmsg,
    507 				  int *err)
    508 {
    509   struct simple_object_coff_attributes *attrs =
    510     (struct simple_object_coff_attributes *) sobj->data;
    511   unsigned char hdrbuf[sizeof (struct external_filehdr)];
    512   unsigned char *hdr;
    513   void (*set_16) (unsigned char *, unsigned short);
    514   void (*set_32) (unsigned char *, unsigned int);
    515 
    516   hdr = &hdrbuf[0];
    517 
    518   set_16 = (attrs->is_big_endian
    519 	    ? simple_object_set_big_16
    520 	    : simple_object_set_little_16);
    521   set_32 = (attrs->is_big_endian
    522 	    ? simple_object_set_big_32
    523 	    : simple_object_set_little_32);
    524 
    525   memset (hdr, 0, sizeof (struct external_filehdr));
    526 
    527   set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic);
    528   set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns);
    529   /* f_timdat left as zero.  */
    530   set_32 (hdr + offsetof (struct external_filehdr, f_symptr), symtab_offset);
    531   set_32 (hdr + offsetof (struct external_filehdr, f_nsyms), nsyms);
    532   /* f_opthdr left as zero.  */
    533   set_16 (hdr + offsetof (struct external_filehdr, f_flags), attrs->flags);
    534 
    535   return simple_object_internal_write (descriptor, 0, hdrbuf,
    536 				       sizeof (struct external_filehdr),
    537 				       errmsg, err);
    538 }
    539 
    540 /* Write out a COFF section header.  */
    541 
    542 static int
    543 simple_object_coff_write_scnhdr (simple_object_write *sobj, int descriptor,
    544 				 const char *name, size_t *name_offset,
    545 				 off_t scnhdr_offset, size_t scnsize,
    546 				 off_t offset, unsigned int align,
    547 				 const char **errmsg, int *err)
    548 {
    549   struct simple_object_coff_attributes *attrs =
    550     (struct simple_object_coff_attributes *) sobj->data;
    551   void (*set_32) (unsigned char *, unsigned int);
    552   unsigned char hdrbuf[sizeof (struct external_scnhdr)];
    553   unsigned char *hdr;
    554   size_t namelen;
    555   unsigned int flags;
    556 
    557   set_32 = (attrs->is_big_endian
    558 	    ? simple_object_set_big_32
    559 	    : simple_object_set_little_32);
    560 
    561   memset (hdrbuf, 0, sizeof hdrbuf);
    562   hdr = &hdrbuf[0];
    563 
    564   namelen = strlen (name);
    565   if (namelen <= SCNNMLEN)
    566     strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name), name,
    567 	     SCNNMLEN);
    568   else
    569     {
    570       snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name),
    571 		SCNNMLEN, "/%lu", (unsigned long) *name_offset);
    572       *name_offset += namelen + 1;
    573     }
    574 
    575   /* s_paddr left as zero.  */
    576   /* s_vaddr left as zero.  */
    577   set_32 (hdr + offsetof (struct external_scnhdr, s_size), scnsize);
    578   set_32 (hdr + offsetof (struct external_scnhdr, s_scnptr), offset);
    579   /* s_relptr left as zero.  */
    580   /* s_lnnoptr left as zero.  */
    581   /* s_nreloc left as zero.  */
    582   /* s_nlnno left as zero.  */
    583   flags = (STYP_DATA | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_SHARED
    584 	   | IMAGE_SCN_MEM_READ);
    585   /* PE can represent alignment up to 13.  */
    586   if (align > 13)
    587     align = 13;
    588   flags |= IMAGE_SCN_ALIGN_POWER_CONST(align);
    589   set_32 (hdr + offsetof (struct external_scnhdr, s_flags), flags);
    590 
    591   return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf,
    592 				       sizeof (struct external_scnhdr),
    593 				       errmsg, err);
    594 }
    595 
    596 /* Write out a complete COFF file.  */
    597 
    598 static const char *
    599 simple_object_coff_write_to_file (simple_object_write *sobj, int descriptor,
    600 				  int *err)
    601 {
    602   struct simple_object_coff_attributes *attrs =
    603     (struct simple_object_coff_attributes *) sobj->data;
    604   unsigned int nscns, secnum;
    605   simple_object_write_section *section;
    606   off_t scnhdr_offset;
    607   size_t symtab_offset;
    608   off_t secsym_offset;
    609   unsigned int nsyms;
    610   size_t offset;
    611   size_t name_offset;
    612   const char *errmsg;
    613   unsigned char strsizebuf[4];
    614   /* The interface doesn't give us access to the name of the input file
    615      yet.  We want to use its basename for the FILE symbol.  This is
    616      what 'gas' uses when told to assemble from stdin.  */
    617   const char *source_filename = "fake";
    618   size_t sflen;
    619   union
    620   {
    621     struct external_syment sym;
    622     union external_auxent aux;
    623   } syms[2];
    624   void (*set_16) (unsigned char *, unsigned short);
    625   void (*set_32) (unsigned char *, unsigned int);
    626 
    627   set_16 = (attrs->is_big_endian
    628 	    ? simple_object_set_big_16
    629 	    : simple_object_set_little_16);
    630   set_32 = (attrs->is_big_endian
    631 	    ? simple_object_set_big_32
    632 	    : simple_object_set_little_32);
    633 
    634   nscns = 0;
    635   for (section = sobj->sections; section != NULL; section = section->next)
    636     ++nscns;
    637 
    638   scnhdr_offset = sizeof (struct external_filehdr);
    639   offset = scnhdr_offset + nscns * sizeof (struct external_scnhdr);
    640   name_offset = 4;
    641   for (section = sobj->sections; section != NULL; section = section->next)
    642     {
    643       size_t mask;
    644       size_t new_offset;
    645       size_t scnsize;
    646       struct simple_object_write_section_buffer *buffer;
    647 
    648       mask = (1U << section->align) - 1;
    649       new_offset = offset & mask;
    650       new_offset &= ~ mask;
    651       while (new_offset > offset)
    652 	{
    653 	  unsigned char zeroes[16];
    654 	  size_t write;
    655 
    656 	  memset (zeroes, 0, sizeof zeroes);
    657 	  write = new_offset - offset;
    658 	  if (write > sizeof zeroes)
    659 	    write = sizeof zeroes;
    660 	  if (!simple_object_internal_write (descriptor, offset, zeroes, write,
    661 					     &errmsg, err))
    662 	    return errmsg;
    663 	}
    664 
    665       scnsize = 0;
    666       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
    667 	{
    668 	  if (!simple_object_internal_write (descriptor, offset + scnsize,
    669 					     ((const unsigned char *)
    670 					      buffer->buffer),
    671 					     buffer->size, &errmsg, err))
    672 	    return errmsg;
    673 	  scnsize += buffer->size;
    674 	}
    675 
    676       if (!simple_object_coff_write_scnhdr (sobj, descriptor, section->name,
    677 					    &name_offset, scnhdr_offset,
    678 					    scnsize, offset, section->align,
    679 					    &errmsg, err))
    680 	return errmsg;
    681 
    682       scnhdr_offset += sizeof (struct external_scnhdr);
    683       offset += scnsize;
    684     }
    685 
    686   /* Symbol table is always half-word aligned.  */
    687   offset += (offset & 1);
    688   /* There is a file symbol and a section symbol per section,
    689      and each of these has a single auxiliary symbol following.  */
    690   nsyms = 2 * (nscns + 1);
    691   symtab_offset = offset;
    692   /* Advance across space reserved for symbol table to locate
    693      start of string table.  */
    694   offset += nsyms * sizeof (struct external_syment);
    695 
    696   /* Write out file symbol.  */
    697   memset (&syms[0], 0, sizeof (syms));
    698   strcpy ((char *)&syms[0].sym.e.e_name[0], ".file");
    699   set_16 (&syms[0].sym.e_scnum[0], IMAGE_SYM_DEBUG);
    700   set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
    701   syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_FILE;
    702   syms[0].sym.e_numaux[0] = 1;
    703   /* The name need not be nul-terminated if it fits into the x_fname field
    704      directly, but must be if it has to be placed into the string table.  */
    705   sflen = strlen (source_filename);
    706   if (sflen <= E_FILNMLEN)
    707     memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen);
    708   else
    709     {
    710       set_32 (&syms[1].aux.x_file.x_n.x_offset[0], name_offset);
    711       if (!simple_object_internal_write (descriptor, offset + name_offset,
    712 					 ((const unsigned char *)
    713 					  source_filename),
    714 					 sflen + 1, &errmsg, err))
    715 	return errmsg;
    716       name_offset += strlen (source_filename) + 1;
    717     }
    718   if (!simple_object_internal_write (descriptor, symtab_offset,
    719 				     (const unsigned char *) &syms[0],
    720 				     sizeof (syms), &errmsg, err))
    721     return errmsg;
    722 
    723   /* Write the string table length, followed by the strings and section
    724      symbols in step with each other.  */
    725   set_32 (strsizebuf, name_offset);
    726   if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4,
    727 				     &errmsg, err))
    728     return errmsg;
    729 
    730   name_offset = 4;
    731   secsym_offset = symtab_offset + sizeof (syms);
    732   memset (&syms[0], 0, sizeof (syms));
    733   set_16 (&syms[0].sym.e_type[0], IMAGE_SYM_TYPE);
    734   syms[0].sym.e_sclass[0] = IMAGE_SYM_CLASS_STATIC;
    735   syms[0].sym.e_numaux[0] = 1;
    736   secnum = 1;
    737 
    738   for (section = sobj->sections; section != NULL; section = section->next)
    739     {
    740       size_t namelen;
    741       size_t scnsize;
    742       struct simple_object_write_section_buffer *buffer;
    743 
    744       namelen = strlen (section->name);
    745       set_16 (&syms[0].sym.e_scnum[0], secnum++);
    746       scnsize = 0;
    747       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
    748 	scnsize += buffer->size;
    749       set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
    750       if (namelen > SCNNMLEN)
    751 	{
    752 	  set_32 (&syms[0].sym.e.e.e_zeroes[0], 0);
    753 	  set_32 (&syms[0].sym.e.e.e_offset[0], name_offset);
    754 	  if (!simple_object_internal_write (descriptor, offset + name_offset,
    755 					     ((const unsigned char *)
    756 					      section->name),
    757 					     namelen + 1, &errmsg, err))
    758 	    return errmsg;
    759 	  name_offset += namelen + 1;
    760 	}
    761       else
    762 	{
    763 	  memcpy (&syms[0].sym.e.e_name[0], section->name,
    764 		  strlen (section->name));
    765 	  memset (&syms[0].sym.e.e_name[strlen (section->name)], 0,
    766 		  E_SYMNMLEN - strlen (section->name));
    767 	}
    768 
    769       if (!simple_object_internal_write (descriptor, secsym_offset,
    770 					 (const unsigned char *) &syms[0],
    771 					 sizeof (syms), &errmsg, err))
    772 	return errmsg;
    773       secsym_offset += sizeof (syms);
    774     }
    775 
    776   if (!simple_object_coff_write_filehdr (sobj, descriptor, nscns,
    777 					 symtab_offset, nsyms, &errmsg, err))
    778     return errmsg;
    779 
    780   return NULL;
    781 }
    782 
    783 /* Release the private data for an simple_object_write structure.  */
    784 
    785 static void
    786 simple_object_coff_release_write (void *data)
    787 {
    788   XDELETE (data);
    789 }
    790 
    791 /* The COFF functions.  */
    792 
    793 const struct simple_object_functions simple_object_coff_functions =
    794 {
    795   simple_object_coff_match,
    796   simple_object_coff_find_sections,
    797   simple_object_coff_fetch_attributes,
    798   simple_object_coff_release_read,
    799   simple_object_coff_attributes_merge,
    800   simple_object_coff_release_attributes,
    801   simple_object_coff_start_write,
    802   simple_object_coff_write_to_file,
    803   simple_object_coff_release_write
    804 };
    805