(root)/
gcc-13.2.0/
libiberty/
simple-object-coff.c
       1  /* simple-object-coff.c -- routines to manipulate COFF object files.
       2     Copyright (C) 2010-2023 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    NULL
     805  };