Back to index

cell-binutils  2.17cvs20070401
rescoff.c
Go to the documentation of this file.
00001 /* rescoff.c -- read and write resources in Windows COFF files.
00002    Copyright 1997, 1998, 1999, 2000, 2003
00003    Free Software Foundation, Inc.
00004    Written by Ian Lance Taylor, Cygnus Support.
00005 
00006    This file is part of GNU Binutils.
00007 
00008    This program is free software; you can redistribute it and/or modify
00009    it under the terms of the GNU General Public License as published by
00010    the Free Software Foundation; either version 2 of the License, or
00011    (at your option) any later version.
00012 
00013    This program is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016    GNU General Public License for more details.
00017 
00018    You should have received a copy of the GNU General Public License
00019    along with this program; if not, write to the Free Software
00020    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
00021    02110-1301, USA.  */
00022 
00023 /* This file contains function that read and write Windows resources
00024    in COFF files.  */
00025 
00026 #include "bfd.h"
00027 #include "bucomm.h"
00028 #include "libiberty.h"
00029 #include "windres.h"
00030 
00031 #include <assert.h>
00032 
00033 /* In order to use the address of a resource data entry, we need to
00034    get the image base of the file.  Right now we extract it from
00035    internal BFD information.  FIXME.  */
00036 
00037 #include "coff/internal.h"
00038 #include "libcoff.h"
00039 
00040 /* Information we extract from the file.  */
00041 
00042 struct coff_file_info
00043 {
00044   /* File name.  */
00045   const char *filename;
00046   /* Data read from the file.  */
00047   const bfd_byte *data;
00048   /* End of data read from file.  */
00049   const bfd_byte *data_end;
00050   /* Address of the resource section minus the image base of the file.  */
00051   bfd_vma secaddr;
00052   /* Non-zero if the file is big endian.  */
00053   int big_endian;
00054 };
00055 
00056 /* A resource directory table in a COFF file.  */
00057 
00058 struct extern_res_directory
00059 {
00060   /* Characteristics.  */
00061   bfd_byte characteristics[4];
00062   /* Time stamp.  */
00063   bfd_byte time[4];
00064   /* Major version number.  */
00065   bfd_byte major[2];
00066   /* Minor version number.  */
00067   bfd_byte minor[2];
00068   /* Number of named directory entries.  */
00069   bfd_byte name_count[2];
00070   /* Number of directory entries with IDs.  */
00071   bfd_byte id_count[2];
00072 };
00073 
00074 /* A resource directory entry in a COFF file.  */
00075 
00076 struct extern_res_entry
00077 {
00078   /* Name or ID.  */
00079   bfd_byte name[4];
00080   /* Address of resource entry or subdirectory.  */
00081   bfd_byte rva[4];
00082 };
00083 
00084 /* A resource data entry in a COFF file.  */
00085 
00086 struct extern_res_data
00087 {
00088   /* Address of resource data.  This is apparently a file relative
00089      address, rather than a section offset.  */
00090   bfd_byte rva[4];
00091   /* Size of resource data.  */
00092   bfd_byte size[4];
00093   /* Code page.  */
00094   bfd_byte codepage[4];
00095   /* Reserved.  */
00096   bfd_byte reserved[4];
00097 };
00098 
00099 /* Macros to swap in values.  */
00100 
00101 #define getfi_16(fi, s) ((fi)->big_endian ? bfd_getb16 (s) : bfd_getl16 (s))
00102 #define getfi_32(fi, s) ((fi)->big_endian ? bfd_getb32 (s) : bfd_getl32 (s))
00103 
00104 /* Local functions.  */
00105 
00106 static void overrun (const struct coff_file_info *, const char *);
00107 static struct res_directory *read_coff_res_dir
00108   (const bfd_byte *, const struct coff_file_info *,
00109    const struct res_id *, int);
00110 static struct res_resource *read_coff_data_entry
00111   (const bfd_byte *, const struct coff_file_info *, const struct res_id *);
00112 
00113 /* Read the resources in a COFF file.  */
00114 
00115 struct res_directory *
00116 read_coff_rsrc (const char *filename, const char *target)
00117 {
00118   bfd *abfd;
00119   char **matching;
00120   asection *sec;
00121   bfd_size_type size;
00122   bfd_byte *data;
00123   struct coff_file_info finfo;
00124 
00125   if (filename == NULL)
00126     fatal (_("filename required for COFF input"));
00127 
00128   abfd = bfd_openr (filename, target);
00129   if (abfd == NULL)
00130     bfd_fatal (filename);
00131 
00132   if (! bfd_check_format_matches (abfd, bfd_object, &matching))
00133     {
00134       bfd_nonfatal (bfd_get_filename (abfd));
00135       if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
00136        list_matching_formats (matching);
00137       xexit (1);
00138     }
00139 
00140   sec = bfd_get_section_by_name (abfd, ".rsrc");
00141   if (sec == NULL)
00142     {
00143       fatal (_("%s: no resource section"), filename);
00144     }
00145 
00146   size = bfd_section_size (abfd, sec);
00147   data = (bfd_byte *) res_alloc (size);
00148 
00149   if (! bfd_get_section_contents (abfd, sec, data, 0, size))
00150     bfd_fatal (_("can't read resource section"));
00151 
00152   finfo.filename = filename;
00153   finfo.data = data;
00154   finfo.data_end = data + size;
00155   finfo.secaddr = (bfd_get_section_vma (abfd, sec)
00156                  - pe_data (abfd)->pe_opthdr.ImageBase);
00157   finfo.big_endian = bfd_big_endian (abfd);
00158 
00159   bfd_close (abfd);
00160 
00161   /* Now just read in the top level resource directory.  Note that we
00162      don't free data, since we create resource entries that point into
00163      it.  If we ever want to free up the resource information we read,
00164      this will have to be cleaned up.  */
00165 
00166   return read_coff_res_dir (data, &finfo, (const struct res_id *) NULL, 0);
00167 }
00168 
00169 /* Give an error if we are out of bounds.  */
00170 
00171 static void
00172 overrun (const struct coff_file_info *finfo, const char *msg)
00173 {
00174   fatal (_("%s: %s: address out of bounds"), finfo->filename, msg);
00175 }
00176 
00177 /* Read a resource directory.  */
00178 
00179 static struct res_directory *
00180 read_coff_res_dir (const bfd_byte *data, const struct coff_file_info *finfo,
00181                  const struct res_id *type, int level)
00182 {
00183   const struct extern_res_directory *erd;
00184   struct res_directory *rd;
00185   int name_count, id_count, i;
00186   struct res_entry **pp;
00187   const struct extern_res_entry *ere;
00188 
00189   if ((size_t) (finfo->data_end - data) < sizeof (struct extern_res_directory))
00190     overrun (finfo, _("directory"));
00191 
00192   erd = (const struct extern_res_directory *) data;
00193 
00194   rd = (struct res_directory *) res_alloc (sizeof *rd);
00195   rd->characteristics = getfi_32 (finfo, erd->characteristics);
00196   rd->time = getfi_32 (finfo, erd->time);
00197   rd->major = getfi_16 (finfo, erd->major);
00198   rd->minor = getfi_16 (finfo, erd->minor);
00199   rd->entries = NULL;
00200 
00201   name_count = getfi_16 (finfo, erd->name_count);
00202   id_count = getfi_16 (finfo, erd->id_count);
00203 
00204   pp = &rd->entries;
00205 
00206   /* The resource directory entries immediately follow the directory
00207      table.  */
00208   ere = (const struct extern_res_entry *) (erd + 1);
00209 
00210   for (i = 0; i < name_count; i++, ere++)
00211     {
00212       unsigned long name, rva;
00213       struct res_entry *re;
00214       const bfd_byte *ers;
00215       int length, j;
00216 
00217       if ((const bfd_byte *) ere >= finfo->data_end)
00218        overrun (finfo, _("named directory entry"));
00219 
00220       name = getfi_32 (finfo, ere->name);
00221       rva = getfi_32 (finfo, ere->rva);
00222 
00223       /* For some reason the high bit in NAME is set.  */
00224       name &=~ 0x80000000;
00225 
00226       if (name > (size_t) (finfo->data_end - finfo->data))
00227        overrun (finfo, _("directory entry name"));
00228 
00229       ers = finfo->data + name;
00230 
00231       re = (struct res_entry *) res_alloc (sizeof *re);
00232       re->next = NULL;
00233       re->id.named = 1;
00234       length = getfi_16 (finfo, ers);
00235       re->id.u.n.length = length;
00236       re->id.u.n.name = (unichar *) res_alloc (length * sizeof (unichar));
00237       for (j = 0; j < length; j++)
00238        re->id.u.n.name[j] = getfi_16 (finfo, ers + j * 2 + 2);
00239 
00240       if (level == 0)
00241        type = &re->id;
00242 
00243       if ((rva & 0x80000000) != 0)
00244        {
00245          rva &=~ 0x80000000;
00246          if (rva >= (size_t) (finfo->data_end - finfo->data))
00247            overrun (finfo, _("named subdirectory"));
00248          re->subdir = 1;
00249          re->u.dir = read_coff_res_dir (finfo->data + rva, finfo, type,
00250                                     level + 1);
00251        }
00252       else
00253        {
00254          if (rva >= (size_t) (finfo->data_end - finfo->data))
00255            overrun (finfo, _("named resource"));
00256          re->subdir = 0;
00257          re->u.res = read_coff_data_entry (finfo->data + rva, finfo, type);
00258        }
00259 
00260       *pp = re;
00261       pp = &re->next;
00262     }
00263 
00264   for (i = 0; i < id_count; i++, ere++)
00265     {
00266       unsigned long name, rva;
00267       struct res_entry *re;
00268 
00269       if ((const bfd_byte *) ere >= finfo->data_end)
00270        overrun (finfo, _("ID directory entry"));
00271 
00272       name = getfi_32 (finfo, ere->name);
00273       rva = getfi_32 (finfo, ere->rva);
00274 
00275       re = (struct res_entry *) res_alloc (sizeof *re);
00276       re->next = NULL;
00277       re->id.named = 0;
00278       re->id.u.id = name;
00279 
00280       if (level == 0)
00281        type = &re->id;
00282 
00283       if ((rva & 0x80000000) != 0)
00284        {
00285          rva &=~ 0x80000000;
00286          if (rva >= (size_t) (finfo->data_end - finfo->data))
00287            overrun (finfo, _("ID subdirectory"));
00288          re->subdir = 1;
00289          re->u.dir = read_coff_res_dir (finfo->data + rva, finfo, type,
00290                                     level + 1);
00291        }
00292       else
00293        {
00294          if (rva >= (size_t) (finfo->data_end - finfo->data))
00295            overrun (finfo, _("ID resource"));
00296          re->subdir = 0;
00297          re->u.res = read_coff_data_entry (finfo->data + rva, finfo, type);
00298        }
00299 
00300       *pp = re;
00301       pp = &re->next;
00302     }
00303 
00304   return rd;
00305 }
00306 
00307 /* Read a resource data entry.  */
00308 
00309 static struct res_resource *
00310 read_coff_data_entry (const bfd_byte *data, const struct coff_file_info *finfo, const struct res_id *type)
00311 {
00312   const struct extern_res_data *erd;
00313   struct res_resource *r;
00314   unsigned long size, rva;
00315   const bfd_byte *resdata;
00316 
00317   if (type == NULL)
00318     fatal (_("resource type unknown"));
00319 
00320   if ((size_t) (finfo->data_end - data) < sizeof (struct extern_res_data))
00321     overrun (finfo, _("data entry"));
00322 
00323   erd = (const struct extern_res_data *) data;
00324 
00325   size = getfi_32 (finfo, erd->size);
00326   rva = getfi_32 (finfo, erd->rva);
00327   if (rva < finfo->secaddr
00328       || rva - finfo->secaddr >= (size_t) (finfo->data_end - finfo->data))
00329     overrun (finfo, _("resource data"));
00330 
00331   resdata = finfo->data + (rva - finfo->secaddr);
00332 
00333   if (size > (size_t) (finfo->data_end - resdata))
00334     overrun (finfo, _("resource data size"));
00335 
00336   r = bin_to_res (*type, resdata, size, finfo->big_endian);
00337 
00338   memset (&r->res_info, 0, sizeof (struct res_res_info));
00339   r->coff_info.codepage = getfi_32 (finfo, erd->codepage);
00340   r->coff_info.reserved = getfi_32 (finfo, erd->reserved);
00341 
00342   return r;
00343 }
00344 
00345 /* This structure is used to build a list of bindata structures.  */
00346 
00347 struct bindata_build
00348 {
00349   /* The data.  */
00350   struct bindata *d;
00351   /* The last structure we have added to the list.  */
00352   struct bindata *last;
00353   /* The size of the list as a whole.  */
00354   unsigned long length;
00355 };
00356 
00357 /* This structure keeps track of information as we build the directory
00358    tree.  */
00359 
00360 struct coff_write_info
00361 {
00362   /* These fields are based on the BFD.  */
00363   /* The BFD itself.  */
00364   bfd *abfd;
00365   /* Non-zero if the file is big endian.  */
00366   int big_endian;
00367   /* Pointer to section symbol used to build RVA relocs.  */
00368   asymbol **sympp;
00369 
00370   /* These fields are computed initially, and then not changed.  */
00371   /* Length of directory tables and entries.  */
00372   unsigned long dirsize;
00373   /* Length of directory entry strings.  */
00374   unsigned long dirstrsize;
00375   /* Length of resource data entries.  */
00376   unsigned long dataentsize;
00377 
00378   /* These fields are updated as we add data.  */
00379   /* Directory tables and entries.  */
00380   struct bindata_build dirs;
00381   /* Directory entry strings.  */
00382   struct bindata_build dirstrs;
00383   /* Resource data entries.  */
00384   struct bindata_build dataents;
00385   /* Actual resource data.  */
00386   struct bindata_build resources;
00387   /* Relocations.  */
00388   arelent **relocs;
00389   /* Number of relocations.  */
00390   unsigned int reloc_count;
00391 };
00392 
00393 /* Macros to swap out values.  */
00394 
00395 #define putcwi_16(cwi, v, s) \
00396   ((cwi->big_endian) ? bfd_putb16 ((v), (s)) : bfd_putl16 ((v), (s)))
00397 #define putcwi_32(cwi, v, s) \
00398   ((cwi->big_endian) ? bfd_putb32 ((v), (s)) : bfd_putl32 ((v), (s)))
00399 
00400 static void coff_bin_sizes
00401   (const struct res_directory *, struct coff_write_info *);
00402 static unsigned char *coff_alloc (struct bindata_build *, size_t);
00403 static void coff_to_bin
00404   (const struct res_directory *, struct coff_write_info *);
00405 static void coff_res_to_bin
00406   (const struct res_resource *, struct coff_write_info *);
00407 
00408 /* Write resources to a COFF file.  RESOURCES should already be
00409    sorted.
00410 
00411    Right now we always create a new file.  Someday we should also
00412    offer the ability to merge resources into an existing file.  This
00413    would require doing the basic work of objcopy, just modifying or
00414    adding the .rsrc section.  */
00415 
00416 void
00417 write_coff_file (const char *filename, const char *target,
00418                const struct res_directory *resources)
00419 {
00420   bfd *abfd;
00421   asection *sec;
00422   struct coff_write_info cwi;
00423   struct bindata *d;
00424   unsigned long length, offset;
00425 
00426   if (filename == NULL)
00427     fatal (_("filename required for COFF output"));
00428 
00429   abfd = bfd_openw (filename, target);
00430   if (abfd == NULL)
00431     bfd_fatal (filename);
00432 
00433   if (! bfd_set_format (abfd, bfd_object))
00434     bfd_fatal ("bfd_set_format");
00435 
00436 #if defined DLLTOOL_SH
00437   if (! bfd_set_arch_mach (abfd, bfd_arch_sh, 0))
00438     bfd_fatal ("bfd_set_arch_mach(sh)");
00439 #elif defined DLLTOOL_MIPS
00440   if (! bfd_set_arch_mach (abfd, bfd_arch_mips, 0))
00441     bfd_fatal ("bfd_set_arch_mach(mips)");
00442 #elif defined DLLTOOL_ARM
00443   if (! bfd_set_arch_mach (abfd, bfd_arch_arm, 0))
00444     bfd_fatal ("bfd_set_arch_mach(arm)");
00445 #else
00446   /* FIXME: This is obviously i386 specific.  */
00447   if (! bfd_set_arch_mach (abfd, bfd_arch_i386, 0))
00448     bfd_fatal ("bfd_set_arch_mach(i386)");
00449 #endif
00450 
00451   if (! bfd_set_file_flags (abfd, HAS_SYMS | HAS_RELOC))
00452     bfd_fatal ("bfd_set_file_flags");
00453 
00454   sec = bfd_make_section (abfd, ".rsrc");
00455   if (sec == NULL)
00456     bfd_fatal ("bfd_make_section");
00457 
00458   if (! bfd_set_section_flags (abfd, sec,
00459                             (SEC_HAS_CONTENTS | SEC_ALLOC
00460                             | SEC_LOAD | SEC_DATA)))
00461     bfd_fatal ("bfd_set_section_flags");
00462 
00463   if (! bfd_set_symtab (abfd, sec->symbol_ptr_ptr, 1))
00464     bfd_fatal ("bfd_set_symtab");
00465 
00466   /* Requiring this is probably a bug in BFD.  */
00467   sec->output_section = sec;
00468 
00469   /* The order of data in the .rsrc section is
00470        resource directory tables and entries
00471        resource directory strings
00472        resource data entries
00473        actual resource data
00474 
00475      We build these different types of data in different lists.  */
00476 
00477   cwi.abfd = abfd;
00478   cwi.big_endian = bfd_big_endian (abfd);
00479   cwi.sympp = sec->symbol_ptr_ptr;
00480   cwi.dirsize = 0;
00481   cwi.dirstrsize = 0;
00482   cwi.dataentsize = 0;
00483   cwi.dirs.d = NULL;
00484   cwi.dirs.last = NULL;
00485   cwi.dirs.length = 0;
00486   cwi.dirstrs.d = NULL;
00487   cwi.dirstrs.last = NULL;
00488   cwi.dirstrs.length = 0;
00489   cwi.dataents.d = NULL;
00490   cwi.dataents.last = NULL;
00491   cwi.dataents.length = 0;
00492   cwi.resources.d = NULL;
00493   cwi.resources.last = NULL;
00494   cwi.resources.length = 0;
00495   cwi.relocs = NULL;
00496   cwi.reloc_count = 0;
00497 
00498   /* Work out the sizes of the resource directory entries, so that we
00499      know the various offsets we will need.  */
00500   coff_bin_sizes (resources, &cwi);
00501 
00502   /* Force the directory strings to be 32 bit aligned.  Every other
00503      structure is 32 bit aligned anyhow.  */
00504   cwi.dirstrsize = (cwi.dirstrsize + 3) &~ 3;
00505 
00506   /* Actually convert the resources to binary.  */
00507   coff_to_bin (resources, &cwi);
00508 
00509   /* Add another 2 bytes to the directory strings if needed for
00510      alignment.  */
00511   if ((cwi.dirstrs.length & 3) != 0)
00512     {
00513       unsigned char *ex;
00514 
00515       ex = coff_alloc (&cwi.dirstrs, 2);
00516       ex[0] = 0;
00517       ex[1] = 0;
00518     }
00519 
00520   /* Make sure that the data we built came out to the same size as we
00521      calculated initially.  */
00522   assert (cwi.dirs.length == cwi.dirsize);
00523   assert (cwi.dirstrs.length == cwi.dirstrsize);
00524   assert (cwi.dataents.length == cwi.dataentsize);
00525 
00526   length = (cwi.dirsize
00527            + cwi.dirstrsize
00528            + cwi.dataentsize
00529            + cwi.resources.length);
00530 
00531   if (! bfd_set_section_size (abfd, sec, length))
00532     bfd_fatal ("bfd_set_section_size");
00533 
00534   bfd_set_reloc (abfd, sec, cwi.relocs, cwi.reloc_count);
00535 
00536   offset = 0;
00537   for (d = cwi.dirs.d; d != NULL; d = d->next)
00538     {
00539       if (! bfd_set_section_contents (abfd, sec, d->data, offset, d->length))
00540        bfd_fatal ("bfd_set_section_contents");
00541       offset += d->length;
00542     }
00543   for (d = cwi.dirstrs.d; d != NULL; d = d->next)
00544     {
00545       if (! bfd_set_section_contents (abfd, sec, d->data, offset, d->length))
00546        bfd_fatal ("bfd_set_section_contents");
00547       offset += d->length;
00548     }
00549   for (d = cwi.dataents.d; d != NULL; d = d->next)
00550     {
00551       if (! bfd_set_section_contents (abfd, sec, d->data, offset, d->length))
00552        bfd_fatal ("bfd_set_section_contents");
00553       offset += d->length;
00554     }
00555   for (d = cwi.resources.d; d != NULL; d = d->next)
00556     {
00557       if (! bfd_set_section_contents (abfd, sec, d->data, offset, d->length))
00558        bfd_fatal ("bfd_set_section_contents");
00559       offset += d->length;
00560     }
00561 
00562   assert (offset == length);
00563 
00564   if (! bfd_close (abfd))
00565     bfd_fatal ("bfd_close");
00566 
00567   /* We allocated the relocs array using malloc.  */
00568   free (cwi.relocs);
00569 }
00570 
00571 /* Work out the sizes of the various fixed size resource directory
00572    entries.  This updates fields in CWI.  */
00573 
00574 static void
00575 coff_bin_sizes (const struct res_directory *resdir,
00576               struct coff_write_info *cwi)
00577 {
00578   const struct res_entry *re;
00579 
00580   cwi->dirsize += sizeof (struct extern_res_directory);
00581 
00582   for (re = resdir->entries; re != NULL; re = re->next)
00583     {
00584       cwi->dirsize += sizeof (struct extern_res_entry);
00585 
00586       if (re->id.named)
00587        cwi->dirstrsize += re->id.u.n.length * 2 + 2;
00588 
00589       if (re->subdir)
00590        coff_bin_sizes (re->u.dir, cwi);
00591       else
00592        cwi->dataentsize += sizeof (struct extern_res_data);
00593     }
00594 }
00595 
00596 /* Allocate data for a particular list.  */
00597 
00598 static unsigned char *
00599 coff_alloc (struct bindata_build *bb, size_t size)
00600 {
00601   struct bindata *d;
00602 
00603   d = (struct bindata *) reswr_alloc (sizeof *d);
00604 
00605   d->next = NULL;
00606   d->data = (unsigned char *) reswr_alloc (size);
00607   d->length = size;
00608 
00609   if (bb->d == NULL)
00610     bb->d = d;
00611   else
00612     bb->last->next = d;
00613   bb->last = d;
00614   bb->length += size;
00615 
00616   return d->data;
00617 }
00618 
00619 /* Convert the resource directory RESDIR to binary.  */
00620 
00621 static void
00622 coff_to_bin (const struct res_directory *resdir, struct coff_write_info *cwi)
00623 {
00624   struct extern_res_directory *erd;
00625   int ci, cn;
00626   const struct res_entry *e;
00627   struct extern_res_entry *ere;
00628 
00629   /* Write out the directory table.  */
00630 
00631   erd = ((struct extern_res_directory *)
00632         coff_alloc (&cwi->dirs, sizeof (*erd)));
00633 
00634   putcwi_32 (cwi, resdir->characteristics, erd->characteristics);
00635   putcwi_32 (cwi, resdir->time, erd->time);
00636   putcwi_16 (cwi, resdir->major, erd->major);
00637   putcwi_16 (cwi, resdir->minor, erd->minor);
00638 
00639   ci = 0;
00640   cn = 0;
00641   for (e = resdir->entries; e != NULL; e = e->next)
00642     {
00643       if (e->id.named)
00644        ++cn;
00645       else
00646        ++ci;
00647     }
00648 
00649   putcwi_16 (cwi, cn, erd->name_count);
00650   putcwi_16 (cwi, ci, erd->id_count);
00651 
00652   /* Write out the data entries.  Note that we allocate space for all
00653      the entries before writing them out.  That permits a recursive
00654      call to work correctly when writing out subdirectories.  */
00655 
00656   ere = ((struct extern_res_entry *)
00657         coff_alloc (&cwi->dirs, (ci + cn) * sizeof (*ere)));
00658   for (e = resdir->entries; e != NULL; e = e->next, ere++)
00659     {
00660       if (! e->id.named)
00661        putcwi_32 (cwi, e->id.u.id, ere->name);
00662       else
00663        {
00664          unsigned char *str;
00665          int i;
00666 
00667          /* For some reason existing files seem to have the high bit
00668              set on the address of the name, although that is not
00669              documented.  */
00670          putcwi_32 (cwi,
00671                    0x80000000 | (cwi->dirsize + cwi->dirstrs.length),
00672                    ere->name);
00673 
00674          str = coff_alloc (&cwi->dirstrs, e->id.u.n.length * 2 + 2);
00675          putcwi_16 (cwi, e->id.u.n.length, str);
00676          for (i = 0; i < e->id.u.n.length; i++)
00677            putcwi_16 (cwi, e->id.u.n.name[i], str + i * 2 + 2);
00678        }
00679 
00680       if (e->subdir)
00681        {
00682          putcwi_32 (cwi, 0x80000000 | cwi->dirs.length, ere->rva);
00683          coff_to_bin (e->u.dir, cwi);
00684        }
00685       else
00686        {
00687          putcwi_32 (cwi,
00688                    cwi->dirsize + cwi->dirstrsize + cwi->dataents.length,
00689                    ere->rva);
00690 
00691          coff_res_to_bin (e->u.res, cwi);
00692        }
00693     }
00694 }
00695 
00696 /* Convert the resource RES to binary.  */
00697 
00698 static void
00699 coff_res_to_bin (const struct res_resource *res, struct coff_write_info *cwi)
00700 {
00701   arelent *r;
00702   struct extern_res_data *erd;
00703   struct bindata *d;
00704   unsigned long length;
00705 
00706   /* For some reason, although every other address is a section
00707      offset, the address of the resource data itself is an RVA.  That
00708      means that we need to generate a relocation for it.  We allocate
00709      the relocs array using malloc so that we can use realloc.  FIXME:
00710      This relocation handling is correct for the i386, but probably
00711      not for any other target.  */
00712 
00713   r = (arelent *) reswr_alloc (sizeof (arelent));
00714   r->sym_ptr_ptr = cwi->sympp;
00715   r->address = cwi->dirsize + cwi->dirstrsize + cwi->dataents.length;
00716   r->addend = 0;
00717   r->howto = bfd_reloc_type_lookup (cwi->abfd, BFD_RELOC_RVA);
00718   if (r->howto == NULL)
00719     bfd_fatal (_("can't get BFD_RELOC_RVA relocation type"));
00720 
00721   cwi->relocs = xrealloc (cwi->relocs,
00722                        (cwi->reloc_count + 2) * sizeof (arelent *));
00723   cwi->relocs[cwi->reloc_count] = r;
00724   cwi->relocs[cwi->reloc_count + 1] = NULL;
00725   ++cwi->reloc_count;
00726 
00727   erd = (struct extern_res_data *) coff_alloc (&cwi->dataents, sizeof (*erd));
00728 
00729   putcwi_32 (cwi,
00730             (cwi->dirsize
00731              + cwi->dirstrsize
00732              + cwi->dataentsize
00733              + cwi->resources.length),
00734             erd->rva);
00735   putcwi_32 (cwi, res->coff_info.codepage, erd->codepage);
00736   putcwi_32 (cwi, res->coff_info.reserved, erd->reserved);
00737 
00738   d = res_to_bin (res, cwi->big_endian);
00739 
00740   if (cwi->resources.d == NULL)
00741     cwi->resources.d = d;
00742   else
00743     cwi->resources.last->next = d;
00744 
00745   length = 0;
00746   for (; d->next != NULL; d = d->next)
00747     length += d->length;
00748   length += d->length;
00749   cwi->resources.last = d;
00750   cwi->resources.length += length;
00751 
00752   putcwi_32 (cwi, length, erd->size);
00753 
00754   /* Force the next resource to have 32 bit alignment.  */
00755 
00756   if ((length & 3) != 0)
00757     {
00758       int add;
00759       unsigned char *ex;
00760 
00761       add = 4 - (length & 3);
00762 
00763       ex = coff_alloc (&cwi->resources, add);
00764       memset (ex, 0, add);
00765     }
00766 }