(root)/
binutils-2.41/
binutils/
rescoff.c
       1  /* rescoff.c -- read and write resources in Windows COFF files.
       2     Copyright (C) 1997-2023 Free Software Foundation, Inc.
       3     Written by Ian Lance Taylor, Cygnus Support.
       4     Rewritten by Kai Tietz, Onevision.
       5  
       6     This file is part of GNU Binutils.
       7  
       8     This program is free software; you can redistribute it and/or modify
       9     it under the terms of the GNU General Public License as published by
      10     the Free Software Foundation; either version 3 of the License, or
      11     (at your option) any later version.
      12  
      13     This program is distributed in the hope that it will be useful,
      14     but WITHOUT ANY WARRANTY; without even the implied warranty of
      15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16     GNU General Public License for more details.
      17  
      18     You should have received a copy of the GNU General Public License
      19     along with this program; if not, write to the Free Software
      20     Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
      21     02110-1301, USA.  */
      22  
      23  /* This file contains function that read and write Windows resources
      24     in COFF files.  */
      25  
      26  #include "sysdep.h"
      27  #include "bfd.h"
      28  #include "bucomm.h"
      29  #include "libiberty.h"
      30  #include "windres.h"
      31  
      32  #include <assert.h>
      33  
      34  /* In order to use the address of a resource data entry, we need to
      35     get the image base of the file.  Right now we extract it from
      36     internal BFD information.  FIXME.  */
      37  
      38  #include "coff/internal.h"
      39  #include "libcoff.h"
      40  
      41  /* Information we extract from the file.  */
      42  
      43  struct coff_file_info
      44  {
      45    /* File name.  */
      46    const char *filename;
      47    /* Data read from the file.  */
      48    const bfd_byte *data;
      49    /* End of data read from file.  */
      50    const bfd_byte *data_end;
      51    /* Address of the resource section minus the image base of the file.  */
      52    rc_uint_type secaddr;
      53  };
      54  
      55  /* A resource directory table in a COFF file.  */
      56  
      57  struct __attribute__ ((__packed__)) extern_res_directory
      58  {
      59    /* Characteristics.  */
      60    bfd_byte characteristics[4];
      61    /* Time stamp.  */
      62    bfd_byte time[4];
      63    /* Major version number.  */
      64    bfd_byte major[2];
      65    /* Minor version number.  */
      66    bfd_byte minor[2];
      67    /* Number of named directory entries.  */
      68    bfd_byte name_count[2];
      69    /* Number of directory entries with IDs.  */
      70    bfd_byte id_count[2];
      71  };
      72  
      73  /* A resource directory entry in a COFF file.  */
      74  
      75  struct extern_res_entry
      76  {
      77    /* Name or ID.  */
      78    bfd_byte name[4];
      79    /* Address of resource entry or subdirectory.  */
      80    bfd_byte rva[4];
      81  };
      82  
      83  /* A resource data entry in a COFF file.  */
      84  
      85  struct extern_res_data
      86  {
      87    /* Address of resource data.  This is apparently a file relative
      88       address, rather than a section offset.  */
      89    bfd_byte rva[4];
      90    /* Size of resource data.  */
      91    bfd_byte size[4];
      92    /* Code page.  */
      93    bfd_byte codepage[4];
      94    /* Reserved.  */
      95    bfd_byte reserved[4];
      96  };
      97  
      98  /* Local functions.  */
      99  
     100  static void overrun (const struct coff_file_info *, const char *);
     101  static rc_res_directory *read_coff_res_dir (windres_bfd *, const bfd_byte *,
     102  					    const struct coff_file_info *,
     103  					    const rc_res_id *, int);
     104  static rc_res_resource *read_coff_data_entry (windres_bfd *, const bfd_byte *,
     105  					      const struct coff_file_info *,
     106  					      const rc_res_id *);
     107  
     108  /* Read the resources in a COFF file.  */
     109  
     110  rc_res_directory *
     111  read_coff_rsrc (const char *filename, const char *target)
     112  {
     113    rc_res_directory *ret;
     114    bfd *abfd;
     115    windres_bfd wrbfd;
     116    char **matching;
     117    asection *sec;
     118    bfd_size_type size;
     119    bfd_byte *data;
     120    struct coff_file_info flaginfo;
     121  
     122    if (filename == NULL)
     123      fatal (_("filename required for COFF input"));
     124  
     125    abfd = bfd_openr (filename, target);
     126    if (abfd == NULL)
     127      bfd_fatal (filename);
     128  
     129    if (! bfd_check_format_matches (abfd, bfd_object, &matching))
     130      {
     131        bfd_nonfatal (bfd_get_filename (abfd));
     132        if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
     133  	list_matching_formats (matching);
     134        xexit (1);
     135      }
     136  
     137    sec = bfd_get_section_by_name (abfd, ".rsrc");
     138    if (sec == NULL)
     139      {
     140        fatal (_("%s: no resource section"), filename);
     141      }
     142  
     143    set_windres_bfd (&wrbfd, abfd, sec, WR_KIND_BFD);
     144    size = bfd_section_size (sec);
     145    /* PR 17512: file: 1b25ba5d
     146       The call to get_file_size here may be expensive
     147       but there is no other way to determine if the section size
     148       is reasonable.  */
     149    if (size > (bfd_size_type) get_file_size (filename))
     150      fatal (_("%s: .rsrc section is bigger than the file!"), filename);
     151  
     152    data = (bfd_byte *) res_alloc (size);
     153    get_windres_bfd_content (&wrbfd, data, 0, size);
     154  
     155    flaginfo.filename = filename;
     156    flaginfo.data = data;
     157    flaginfo.data_end = data + size;
     158    flaginfo.secaddr = (bfd_section_vma (sec)
     159  		      - pe_data (abfd)->pe_opthdr.ImageBase);
     160  
     161    /* Now just read in the top level resource directory.  Note that we
     162       don't free data, since we create resource entries that point into
     163       it.  If we ever want to free up the resource information we read,
     164       this will have to be cleaned up.  */
     165  
     166    ret = read_coff_res_dir (&wrbfd, data, &flaginfo, (const rc_res_id *) NULL, 0);
     167  
     168    bfd_close (abfd);
     169  
     170    return ret;
     171  }
     172  
     173  /* Give an error if we are out of bounds.  */
     174  
     175  static void
     176  overrun (const struct coff_file_info *flaginfo, const char *msg)
     177  {
     178    fatal (_("%s: %s: address out of bounds"), flaginfo->filename, msg);
     179  }
     180  
     181  /* Read a resource directory.  */
     182  
     183  static rc_res_directory *
     184  read_coff_res_dir (windres_bfd *wrbfd, const bfd_byte *data,
     185  		   const struct coff_file_info *flaginfo,
     186  		   const rc_res_id *type, int level)
     187  {
     188    const struct extern_res_directory *erd;
     189    rc_res_directory *rd;
     190    int name_count, id_count, i;
     191    rc_res_entry **pp;
     192    const struct extern_res_entry *ere;
     193  
     194    /* PR 17512: file: 09d80f53.
     195       Whilst in theory resources can nest to any level, in practice
     196       Microsoft only defines 3 levels.  Corrupt files however might
     197       claim to use more.  */
     198    if (level > 4)
     199      overrun (flaginfo, _("Resources nest too deep"));
     200  
     201    if ((size_t) (flaginfo->data_end - data) < sizeof (struct extern_res_directory))
     202      overrun (flaginfo, _("directory"));
     203  
     204    erd = (const struct extern_res_directory *) data;
     205  
     206    rd = (rc_res_directory *) res_alloc (sizeof (rc_res_directory));
     207    rd->characteristics = windres_get_32 (wrbfd, erd->characteristics, 4);
     208    rd->time = windres_get_32 (wrbfd, erd->time, 4);
     209    rd->major = windres_get_16 (wrbfd, erd->major, 2);
     210    rd->minor = windres_get_16 (wrbfd, erd->minor, 2);
     211    rd->entries = NULL;
     212  
     213    name_count = windres_get_16 (wrbfd, erd->name_count, 2);
     214    id_count = windres_get_16 (wrbfd, erd->id_count, 2);
     215  
     216    pp = &rd->entries;
     217  
     218    /* The resource directory entries immediately follow the directory
     219       table.  */
     220    ere = (const struct extern_res_entry *) (erd + 1);
     221  
     222    for (i = 0; i < name_count; i++, ere++)
     223      {
     224        rc_uint_type name, rva;
     225        rc_res_entry *re;
     226        const bfd_byte *ers;
     227        int length, j;
     228  
     229        if ((const bfd_byte *) ere >= flaginfo->data_end)
     230  	overrun (flaginfo, _("named directory entry"));
     231  
     232        name = windres_get_32 (wrbfd, ere->name, 4);
     233        rva = windres_get_32 (wrbfd, ere->rva, 4);
     234  
     235        /* For some reason the high bit in NAME is set.  */
     236        name &=~ 0x80000000;
     237  
     238        if (name > (rc_uint_type) (flaginfo->data_end - flaginfo->data))
     239  	overrun (flaginfo, _("directory entry name"));
     240  
     241        ers = flaginfo->data + name;
     242  
     243        re = (rc_res_entry *) res_alloc (sizeof *re);
     244        re->next = NULL;
     245        re->id.named = 1;
     246        length = windres_get_16 (wrbfd, ers, 2);
     247        re->id.u.n.length = length;
     248        re->id.u.n.name = (unichar *) res_alloc (length * sizeof (unichar));
     249        for (j = 0; j < length; j++)
     250  	{
     251  	  /* PR 17512: file: 05dc4a16.  */
     252  	  if (length < 0 || ers >= flaginfo->data_end || ers + j * 2 + 4 >= flaginfo->data_end)
     253  	    overrun (flaginfo, _("resource name"));
     254  	  re->id.u.n.name[j] = windres_get_16 (wrbfd, ers + j * 2 + 2, 2);
     255  	}
     256  
     257        if (level == 0)
     258  	type = &re->id;
     259  
     260        if ((rva & 0x80000000) != 0)
     261  	{
     262  	  rva &=~ 0x80000000;
     263  	  if (rva >= (rc_uint_type) (flaginfo->data_end - flaginfo->data))
     264  	    overrun (flaginfo, _("named subdirectory"));
     265  	  re->subdir = 1;
     266  	  re->u.dir = read_coff_res_dir (wrbfd, flaginfo->data + rva, flaginfo, type,
     267  					 level + 1);
     268  	}
     269        else
     270  	{
     271  	  if (rva >= (rc_uint_type) (flaginfo->data_end - flaginfo->data))
     272  	    overrun (flaginfo, _("named resource"));
     273  	  re->subdir = 0;
     274  	  re->u.res = read_coff_data_entry (wrbfd, flaginfo->data + rva, flaginfo, type);
     275  	}
     276  
     277        *pp = re;
     278        pp = &re->next;
     279      }
     280  
     281    for (i = 0; i < id_count; i++, ere++)
     282      {
     283        unsigned long name, rva;
     284        rc_res_entry *re;
     285  
     286        if ((const bfd_byte *) ere >= flaginfo->data_end)
     287  	overrun (flaginfo, _("ID directory entry"));
     288  
     289        name = windres_get_32 (wrbfd, ere->name, 4);
     290        rva = windres_get_32 (wrbfd, ere->rva, 4);
     291  
     292        re = (rc_res_entry *) res_alloc (sizeof *re);
     293        re->next = NULL;
     294        re->id.named = 0;
     295        re->id.u.id = name;
     296  
     297        if (level == 0)
     298  	type = &re->id;
     299  
     300        if ((rva & 0x80000000) != 0)
     301  	{
     302  	  rva &=~ 0x80000000;
     303  	  if (rva >= (rc_uint_type) (flaginfo->data_end - flaginfo->data))
     304  	    overrun (flaginfo, _("ID subdirectory"));
     305  	  re->subdir = 1;
     306  	  re->u.dir = read_coff_res_dir (wrbfd, flaginfo->data + rva, flaginfo, type,
     307  					 level + 1);
     308  	}
     309        else
     310  	{
     311  	  if (rva >= (rc_uint_type) (flaginfo->data_end - flaginfo->data))
     312  	    overrun (flaginfo, _("ID resource"));
     313  	  re->subdir = 0;
     314  	  re->u.res = read_coff_data_entry (wrbfd, flaginfo->data + rva, flaginfo, type);
     315  	}
     316  
     317        *pp = re;
     318        pp = &re->next;
     319      }
     320  
     321    return rd;
     322  }
     323  
     324  /* Read a resource data entry.  */
     325  
     326  static rc_res_resource *
     327  read_coff_data_entry (windres_bfd *wrbfd, const bfd_byte *data,
     328  		      const struct coff_file_info *flaginfo,
     329  		      const rc_res_id *type)
     330  {
     331    const struct extern_res_data *erd;
     332    rc_res_resource *r;
     333    rc_uint_type size, rva;
     334    const bfd_byte *resdata;
     335  
     336    if (type == NULL)
     337      fatal (_("resource type unknown"));
     338  
     339    if ((size_t) (flaginfo->data_end - data) < sizeof (struct extern_res_data))
     340      overrun (flaginfo, _("data entry"));
     341  
     342    erd = (const struct extern_res_data *) data;
     343  
     344    size = windres_get_32 (wrbfd, erd->size, 4);
     345    rva = windres_get_32 (wrbfd, erd->rva, 4);
     346    if (rva < flaginfo->secaddr
     347        || rva - flaginfo->secaddr >= (rc_uint_type) (flaginfo->data_end - flaginfo->data))
     348      overrun (flaginfo, _("resource data"));
     349  
     350    resdata = flaginfo->data + (rva - flaginfo->secaddr);
     351  
     352    if (size > (rc_uint_type) (flaginfo->data_end - resdata))
     353      overrun (flaginfo, _("resource data size"));
     354  
     355    r = bin_to_res (wrbfd, *type, resdata, size);
     356  
     357    memset (&r->res_info, 0, sizeof (rc_res_res_info));
     358    r->coff_info.codepage = windres_get_32 (wrbfd, erd->codepage, 4);
     359    r->coff_info.reserved = windres_get_32 (wrbfd, erd->reserved, 4);
     360  
     361    return r;
     362  }
     363  
     364  /* This structure is used to build a list of bindata structures.  */
     365  
     366  struct bindata_build
     367  {
     368    /* The data.  */
     369    bindata *d;
     370    /* The last structure we have added to the list.  */
     371    bindata *last;
     372    /* The size of the list as a whole.  */
     373    unsigned long length;
     374  };
     375  
     376  struct coff_res_data_build
     377  {
     378    /* The data.  */
     379    coff_res_data *d;
     380    /* The last structure we have added to the list.  */
     381    coff_res_data *last;
     382    /* The size of the list as a whole.  */
     383    unsigned long length;
     384  };
     385  
     386  /* This structure keeps track of information as we build the directory
     387     tree.  */
     388  
     389  struct coff_write_info
     390  {
     391    /* These fields are based on the BFD.  */
     392    /* The BFD itself.  */
     393    windres_bfd *wrbfd;
     394    /* Pointer to section symbol used to build RVA relocs.  */
     395    asymbol **sympp;
     396  
     397    /* These fields are computed initially, and then not changed.  */
     398    /* Length of directory tables and entries.  */
     399    unsigned long dirsize;
     400    /* Length of directory entry strings.  */
     401    unsigned long dirstrsize;
     402    /* Length of resource data entries.  */
     403    unsigned long dataentsize;
     404  
     405    /* These fields are updated as we add data.  */
     406    /* Directory tables and entries.  */
     407    struct bindata_build dirs;
     408    /* Directory entry strings.  */
     409    struct bindata_build dirstrs;
     410    /* Resource data entries.  */
     411    struct bindata_build dataents;
     412    /* Actual resource data.  */
     413    struct coff_res_data_build resources;
     414    /* Relocations.  */
     415    arelent **relocs;
     416    /* Number of relocations.  */
     417    unsigned int reloc_count;
     418  };
     419  
     420  static void coff_bin_sizes (const rc_res_directory *, struct coff_write_info *);
     421  static bfd_byte *coff_alloc (struct bindata_build *, rc_uint_type);
     422  static void coff_to_bin
     423    (const rc_res_directory *, struct coff_write_info *);
     424  static void coff_res_to_bin
     425    (const rc_res_resource *, struct coff_write_info *);
     426  
     427  /* Write resources to a COFF file.  RESOURCES should already be
     428     sorted.
     429  
     430     Right now we always create a new file.  Someday we should also
     431     offer the ability to merge resources into an existing file.  This
     432     would require doing the basic work of objcopy, just modifying or
     433     adding the .rsrc section.  */
     434  
     435  void
     436  write_coff_file (const char *filename, const char *target,
     437  		 const rc_res_directory *resources)
     438  {
     439    bfd *abfd;
     440    asection *sec;
     441    struct coff_write_info cwi;
     442    windres_bfd wrbfd;
     443    bindata *d;
     444    coff_res_data *rd;
     445    unsigned long length, offset;
     446  
     447    if (filename == NULL)
     448      fatal (_("filename required for COFF output"));
     449  
     450    abfd = bfd_openw (filename, target);
     451    if (abfd == NULL)
     452      bfd_fatal (filename);
     453  
     454    if (! bfd_set_format (abfd, bfd_object))
     455      bfd_fatal ("bfd_set_format");
     456  
     457  #if defined DLLTOOL_SH
     458    if (! bfd_set_arch_mach (abfd, bfd_arch_sh, 0))
     459      bfd_fatal ("bfd_set_arch_mach(sh)");
     460  #elif defined DLLTOOL_MIPS
     461    if (! bfd_set_arch_mach (abfd, bfd_arch_mips, 0))
     462      bfd_fatal ("bfd_set_arch_mach(mips)");
     463  #elif defined DLLTOOL_ARM
     464    if (! bfd_set_arch_mach (abfd, bfd_arch_arm, 0))
     465      bfd_fatal ("bfd_set_arch_mach(arm)");
     466  #elif defined DLLTOOL_AARCH64
     467    if (! bfd_set_arch_mach (abfd, bfd_arch_aarch64, 0))
     468      bfd_fatal ("bfd_set_arch_mach(aarch64)");
     469  #else
     470    /* FIXME: This is obviously i386 specific.  */
     471    if (! bfd_set_arch_mach (abfd, bfd_arch_i386, 0))
     472      bfd_fatal ("bfd_set_arch_mach(i386)");
     473  #endif
     474  
     475    if (! bfd_set_file_flags (abfd, HAS_SYMS | HAS_RELOC))
     476      bfd_fatal ("bfd_set_file_flags");
     477  
     478    sec = bfd_make_section_with_flags (abfd, ".rsrc",
     479  				     (SEC_HAS_CONTENTS | SEC_ALLOC
     480  				      | SEC_LOAD | SEC_DATA | SEC_READONLY));
     481    if (sec == NULL)
     482      bfd_fatal ("bfd_make_section");
     483  
     484    if (! bfd_set_symtab (abfd, sec->symbol_ptr_ptr, 1))
     485      bfd_fatal ("bfd_set_symtab");
     486  
     487    /* Requiring this is probably a bug in BFD.  */
     488    sec->output_section = sec;
     489  
     490    /* The order of data in the .rsrc section is
     491         resource directory tables and entries
     492         resource directory strings
     493         resource data entries
     494         actual resource data
     495  
     496       We build these different types of data in different lists.  */
     497  
     498    set_windres_bfd (&wrbfd, abfd, sec, WR_KIND_BFD);
     499  
     500    cwi.wrbfd = &wrbfd;
     501    cwi.sympp = sec->symbol_ptr_ptr;
     502    cwi.dirsize = 0;
     503    cwi.dirstrsize = 0;
     504    cwi.dataentsize = 0;
     505    cwi.dirs.d = NULL;
     506    cwi.dirs.last = NULL;
     507    cwi.dirs.length = 0;
     508    cwi.dirstrs.d = NULL;
     509    cwi.dirstrs.last = NULL;
     510    cwi.dirstrs.length = 0;
     511    cwi.dataents.d = NULL;
     512    cwi.dataents.last = NULL;
     513    cwi.dataents.length = 0;
     514    cwi.resources.d = NULL;
     515    cwi.resources.last = NULL;
     516    cwi.resources.length = 0;
     517    cwi.relocs = NULL;
     518    cwi.reloc_count = 0;
     519  
     520    /* Work out the sizes of the resource directory entries, so that we
     521       know the various offsets we will need.  */
     522    coff_bin_sizes (resources, &cwi);
     523  
     524    /* Force the directory strings to be 64 bit aligned.  Every other
     525       structure is 64 bit aligned anyhow.  */
     526    cwi.dirstrsize = (cwi.dirstrsize + 7) & ~7;
     527  
     528    /* Actually convert the resources to binary.  */
     529    coff_to_bin (resources, &cwi);
     530  
     531    /* Add another few bytes to the directory strings if needed for
     532       alignment.  */
     533    if ((cwi.dirstrs.length & 7) != 0)
     534      {
     535        rc_uint_type pad = 8 - (cwi.dirstrs.length & 7);
     536        bfd_byte *ex;
     537  
     538        ex = coff_alloc (& cwi.dirstrs, pad);
     539        memset (ex, 0, pad);
     540      }
     541  
     542    /* Make sure that the data we built came out to the same size as we
     543       calculated initially.  */
     544    assert (cwi.dirs.length == cwi.dirsize);
     545    assert (cwi.dirstrs.length == cwi.dirstrsize);
     546    assert (cwi.dataents.length == cwi.dataentsize);
     547  
     548    length = (cwi.dirsize
     549  	    + cwi.dirstrsize
     550  	    + cwi.dataentsize
     551  	    + cwi.resources.length);
     552  
     553    if (!bfd_set_section_size (sec, length))
     554      bfd_fatal ("bfd_set_section_size");
     555  
     556    bfd_set_reloc (abfd, sec, cwi.relocs, cwi.reloc_count);
     557  
     558    offset = 0;
     559    for (d = cwi.dirs.d; d != NULL; d = d->next)
     560      {
     561        if (! bfd_set_section_contents (abfd, sec, d->data, offset, d->length))
     562  	bfd_fatal ("bfd_set_section_contents");
     563        offset += d->length;
     564      }
     565    for (d = cwi.dirstrs.d; d != NULL; d = d->next)
     566      {
     567        set_windres_bfd_content (&wrbfd, d->data, offset, d->length);
     568        offset += d->length;
     569      }
     570    for (d = cwi.dataents.d; d != NULL; d = d->next)
     571      {
     572        set_windres_bfd_content (&wrbfd, d->data, offset, d->length);
     573        offset += d->length;
     574      }
     575    for (rd = cwi.resources.d; rd != NULL; rd = rd->next)
     576      {
     577        res_to_bin (cwi.wrbfd, (rc_uint_type) offset, rd->res);
     578        offset += rd->length;
     579      }
     580  
     581    assert (offset == length);
     582  
     583    if (! bfd_close (abfd))
     584      bfd_fatal ("bfd_close");
     585  
     586    /* We allocated the relocs array using malloc.  */
     587    free (cwi.relocs);
     588  }
     589  
     590  /* Work out the sizes of the various fixed size resource directory
     591     entries.  This updates fields in CWI.  */
     592  
     593  static void
     594  coff_bin_sizes (const rc_res_directory *resdir,
     595  		struct coff_write_info *cwi)
     596  {
     597    const rc_res_entry *re;
     598  
     599    cwi->dirsize += sizeof (struct extern_res_directory);
     600  
     601    for (re = resdir->entries; re != NULL; re = re->next)
     602      {
     603        cwi->dirsize += sizeof (struct extern_res_entry);
     604  
     605        if (re->id.named)
     606  	cwi->dirstrsize += re->id.u.n.length * 2 + 2;
     607  
     608        if (re->subdir)
     609  	coff_bin_sizes (re->u.dir, cwi);
     610        else
     611  	cwi->dataentsize += sizeof (struct extern_res_data);
     612      }
     613  }
     614  
     615  /* Allocate data for a particular list.  */
     616  
     617  static bfd_byte *
     618  coff_alloc (struct bindata_build *bb, rc_uint_type size)
     619  {
     620    bindata *d;
     621  
     622    d = (bindata *) reswr_alloc (sizeof (bindata));
     623  
     624    d->next = NULL;
     625    d->data = (bfd_byte *) reswr_alloc (size);
     626    d->length = size;
     627  
     628    if (bb->d == NULL)
     629      bb->d = d;
     630    else
     631      bb->last->next = d;
     632    bb->last = d;
     633    bb->length += size;
     634  
     635    return d->data;
     636  }
     637  
     638  /* Convert the resource directory RESDIR to binary.  */
     639  
     640  static void
     641  coff_to_bin (const rc_res_directory *resdir, struct coff_write_info *cwi)
     642  {
     643    struct extern_res_directory *erd;
     644    int ci, cn;
     645    const rc_res_entry *e;
     646    struct extern_res_entry *ere;
     647  
     648    /* Write out the directory table.  */
     649  
     650    erd = ((struct extern_res_directory *)
     651  	 coff_alloc (&cwi->dirs, sizeof (*erd)));
     652  
     653    windres_put_32 (cwi->wrbfd, erd->characteristics, resdir->characteristics);
     654    windres_put_32 (cwi->wrbfd, erd->time, resdir->time);
     655    windres_put_16 (cwi->wrbfd, erd->major, resdir->major);
     656    windres_put_16 (cwi->wrbfd, erd->minor, resdir->minor);
     657  
     658    ci = 0;
     659    cn = 0;
     660    for (e = resdir->entries; e != NULL; e = e->next)
     661      {
     662        if (e->id.named)
     663  	++cn;
     664        else
     665  	++ci;
     666      }
     667  
     668    windres_put_16 (cwi->wrbfd, erd->name_count, cn);
     669    windres_put_16 (cwi->wrbfd, erd->id_count, ci);
     670  
     671    /* Write out the data entries.  Note that we allocate space for all
     672       the entries before writing them out.  That permits a recursive
     673       call to work correctly when writing out subdirectories.  */
     674  
     675    ere = ((struct extern_res_entry *)
     676  	 coff_alloc (&cwi->dirs, (ci + cn) * sizeof (*ere)));
     677    for (e = resdir->entries; e != NULL; e = e->next, ere++)
     678      {
     679        if (! e->id.named)
     680  	windres_put_32 (cwi->wrbfd, ere->name, e->id.u.id);
     681        else
     682  	{
     683  	  bfd_byte *str;
     684  	  rc_uint_type i;
     685  
     686  	  /* For some reason existing files seem to have the high bit
     687               set on the address of the name, although that is not
     688               documented.  */
     689  	  windres_put_32 (cwi->wrbfd, ere->name,
     690  		     0x80000000 | (cwi->dirsize + cwi->dirstrs.length));
     691  
     692  	  str = coff_alloc (&cwi->dirstrs, e->id.u.n.length * 2 + 2);
     693  	  windres_put_16 (cwi->wrbfd, str, e->id.u.n.length);
     694  	  for (i = 0; i < e->id.u.n.length; i++)
     695  	    windres_put_16 (cwi->wrbfd, str + (i + 1) * sizeof (unichar), e->id.u.n.name[i]);
     696  	}
     697  
     698        if (e->subdir)
     699  	{
     700  	  windres_put_32 (cwi->wrbfd, ere->rva, 0x80000000 | cwi->dirs.length);
     701  	  coff_to_bin (e->u.dir, cwi);
     702  	}
     703        else
     704  	{
     705  	  windres_put_32 (cwi->wrbfd, ere->rva,
     706  		     cwi->dirsize + cwi->dirstrsize + cwi->dataents.length);
     707  
     708  	  coff_res_to_bin (e->u.res, cwi);
     709  	}
     710      }
     711  }
     712  
     713  /* Convert the resource RES to binary.  */
     714  
     715  static void
     716  coff_res_to_bin (const rc_res_resource *res, struct coff_write_info *cwi)
     717  {
     718    arelent *r;
     719    struct extern_res_data *erd;
     720    coff_res_data *d;
     721  
     722    /* For some reason, although every other address is a section
     723       offset, the address of the resource data itself is an RVA.  That
     724       means that we need to generate a relocation for it.  We allocate
     725       the relocs array using malloc so that we can use realloc.  FIXME:
     726       This relocation handling is correct for the i386, but probably
     727       not for any other target.  */
     728  
     729    r = (arelent *) reswr_alloc (sizeof (arelent));
     730    r->sym_ptr_ptr = cwi->sympp;
     731    r->address = cwi->dirsize + cwi->dirstrsize + cwi->dataents.length;
     732    r->addend = 0;
     733    r->howto = bfd_reloc_type_lookup (WR_BFD (cwi->wrbfd), BFD_RELOC_RVA);
     734    if (r->howto == NULL)
     735      bfd_fatal (_("can't get BFD_RELOC_RVA relocation type"));
     736  
     737    cwi->relocs = xrealloc (cwi->relocs,
     738  			  (cwi->reloc_count + 2) * sizeof (arelent *));
     739    cwi->relocs[cwi->reloc_count] = r;
     740    cwi->relocs[cwi->reloc_count + 1] = NULL;
     741    ++cwi->reloc_count;
     742  
     743    erd = (struct extern_res_data *) coff_alloc (&cwi->dataents, sizeof (*erd));
     744  
     745    windres_put_32 (cwi->wrbfd, erd->rva,
     746  	     (cwi->dirsize
     747  	      + cwi->dirstrsize
     748  	      + cwi->dataentsize
     749  	      + cwi->resources.length));
     750    windres_put_32 (cwi->wrbfd, erd->codepage, res->coff_info.codepage);
     751    windres_put_32 (cwi->wrbfd, erd->reserved, res->coff_info.reserved);
     752  
     753    d = (coff_res_data *) reswr_alloc (sizeof (coff_res_data));
     754    d->length = res_to_bin (NULL, (rc_uint_type) 0, res);
     755    d->res = res;
     756    d->next = NULL;
     757  
     758    if (cwi->resources.d == NULL)
     759      cwi->resources.d = d;
     760    else
     761      cwi->resources.last->next = d;
     762  
     763    cwi->resources.last = d;
     764    cwi->resources.length += (d->length + 7) & ~7;
     765  
     766    windres_put_32 (cwi->wrbfd, erd->size, d->length);
     767  
     768    /* Force the next resource to have 64 bit alignment.  */
     769    d->length = (d->length + 7) & ~7;
     770  }