138889Sjdp/* rescoff.c -- read and write resources in Windows COFF files. 2218822Sdim Copyright 1997, 1998, 1999, 2000, 2003, 2007 3130561Sobrien Free Software Foundation, Inc. 438889Sjdp Written by Ian Lance Taylor, Cygnus Support. 5218822Sdim Rewritten by Kai Tietz, Onevision. 638889Sjdp 738889Sjdp This file is part of GNU Binutils. 838889Sjdp 938889Sjdp This program is free software; you can redistribute it and/or modify 1038889Sjdp it under the terms of the GNU General Public License as published by 1138889Sjdp the Free Software Foundation; either version 2 of the License, or 1238889Sjdp (at your option) any later version. 1338889Sjdp 1438889Sjdp This program is distributed in the hope that it will be useful, 1538889Sjdp but WITHOUT ANY WARRANTY; without even the implied warranty of 1638889Sjdp MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1738889Sjdp GNU General Public License for more details. 1838889Sjdp 1938889Sjdp You should have received a copy of the GNU General Public License 2038889Sjdp along with this program; if not, write to the Free Software 21218822Sdim Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 22218822Sdim 02110-1301, USA. */ 2338889Sjdp 2438889Sjdp/* This file contains function that read and write Windows resources 2538889Sjdp in COFF files. */ 2638889Sjdp 27218822Sdim#include "sysdep.h" 2838889Sjdp#include "bfd.h" 2938889Sjdp#include "bucomm.h" 3038889Sjdp#include "libiberty.h" 3138889Sjdp#include "windres.h" 3238889Sjdp 3338889Sjdp#include <assert.h> 3438889Sjdp 3538889Sjdp/* In order to use the address of a resource data entry, we need to 3638889Sjdp get the image base of the file. Right now we extract it from 3738889Sjdp internal BFD information. FIXME. */ 3838889Sjdp 3938889Sjdp#include "coff/internal.h" 4038889Sjdp#include "libcoff.h" 4138889Sjdp 4238889Sjdp/* Information we extract from the file. */ 4338889Sjdp 4438889Sjdpstruct coff_file_info 4538889Sjdp{ 4638889Sjdp /* File name. */ 4738889Sjdp const char *filename; 4838889Sjdp /* Data read from the file. */ 4938889Sjdp const bfd_byte *data; 5038889Sjdp /* End of data read from file. */ 5138889Sjdp const bfd_byte *data_end; 5238889Sjdp /* Address of the resource section minus the image base of the file. */ 53218822Sdim rc_uint_type secaddr; 5438889Sjdp}; 5538889Sjdp 5638889Sjdp/* A resource directory table in a COFF file. */ 5738889Sjdp 58218822Sdimstruct __attribute__ ((__packed__)) extern_res_directory 5938889Sjdp{ 6038889Sjdp /* Characteristics. */ 6138889Sjdp bfd_byte characteristics[4]; 6238889Sjdp /* Time stamp. */ 6338889Sjdp bfd_byte time[4]; 6438889Sjdp /* Major version number. */ 6538889Sjdp bfd_byte major[2]; 6638889Sjdp /* Minor version number. */ 6738889Sjdp bfd_byte minor[2]; 6838889Sjdp /* Number of named directory entries. */ 6938889Sjdp bfd_byte name_count[2]; 7038889Sjdp /* Number of directory entries with IDs. */ 7138889Sjdp bfd_byte id_count[2]; 7238889Sjdp}; 7338889Sjdp 7438889Sjdp/* A resource directory entry in a COFF file. */ 7538889Sjdp 7638889Sjdpstruct extern_res_entry 7738889Sjdp{ 7838889Sjdp /* Name or ID. */ 7938889Sjdp bfd_byte name[4]; 8038889Sjdp /* Address of resource entry or subdirectory. */ 8138889Sjdp bfd_byte rva[4]; 8238889Sjdp}; 8338889Sjdp 8438889Sjdp/* A resource data entry in a COFF file. */ 8538889Sjdp 8638889Sjdpstruct extern_res_data 8738889Sjdp{ 8838889Sjdp /* Address of resource data. This is apparently a file relative 8938889Sjdp address, rather than a section offset. */ 9038889Sjdp bfd_byte rva[4]; 9138889Sjdp /* Size of resource data. */ 9238889Sjdp bfd_byte size[4]; 9338889Sjdp /* Code page. */ 9438889Sjdp bfd_byte codepage[4]; 9538889Sjdp /* Reserved. */ 9638889Sjdp bfd_byte reserved[4]; 9738889Sjdp}; 9838889Sjdp 9938889Sjdp/* Local functions. */ 10038889Sjdp 101130561Sobrienstatic void overrun (const struct coff_file_info *, const char *); 102218822Sdimstatic rc_res_directory *read_coff_res_dir (windres_bfd *, const bfd_byte *, 103218822Sdim const struct coff_file_info *, 104218822Sdim const rc_res_id *, int); 105218822Sdimstatic rc_res_resource *read_coff_data_entry (windres_bfd *, const bfd_byte *, 106218822Sdim const struct coff_file_info *, 107218822Sdim const rc_res_id *); 10838889Sjdp 10938889Sjdp/* Read the resources in a COFF file. */ 11038889Sjdp 111218822Sdimrc_res_directory * 112130561Sobrienread_coff_rsrc (const char *filename, const char *target) 11338889Sjdp{ 114218822Sdim rc_res_directory *ret; 11538889Sjdp bfd *abfd; 116218822Sdim windres_bfd wrbfd; 11738889Sjdp char **matching; 11838889Sjdp asection *sec; 11938889Sjdp bfd_size_type size; 12038889Sjdp bfd_byte *data; 12138889Sjdp struct coff_file_info finfo; 12238889Sjdp 12338889Sjdp if (filename == NULL) 12460484Sobrien fatal (_("filename required for COFF input")); 12538889Sjdp 12638889Sjdp abfd = bfd_openr (filename, target); 12738889Sjdp if (abfd == NULL) 12838889Sjdp bfd_fatal (filename); 12938889Sjdp 13038889Sjdp if (! bfd_check_format_matches (abfd, bfd_object, &matching)) 13138889Sjdp { 13238889Sjdp bfd_nonfatal (bfd_get_filename (abfd)); 13338889Sjdp if (bfd_get_error () == bfd_error_file_ambiguously_recognized) 13438889Sjdp list_matching_formats (matching); 13538889Sjdp xexit (1); 13638889Sjdp } 13738889Sjdp 13838889Sjdp sec = bfd_get_section_by_name (abfd, ".rsrc"); 13938889Sjdp if (sec == NULL) 14038889Sjdp { 14160484Sobrien fatal (_("%s: no resource section"), filename); 14238889Sjdp } 14338889Sjdp 144218822Sdim set_windres_bfd (&wrbfd, abfd, sec, WR_KIND_BFD); 14538889Sjdp size = bfd_section_size (abfd, sec); 14638889Sjdp data = (bfd_byte *) res_alloc (size); 14738889Sjdp 148218822Sdim get_windres_bfd_content (&wrbfd, data, 0, size); 14938889Sjdp 15038889Sjdp finfo.filename = filename; 15138889Sjdp finfo.data = data; 15238889Sjdp finfo.data_end = data + size; 15338889Sjdp finfo.secaddr = (bfd_get_section_vma (abfd, sec) 15438889Sjdp - pe_data (abfd)->pe_opthdr.ImageBase); 15538889Sjdp 15638889Sjdp /* Now just read in the top level resource directory. Note that we 15738889Sjdp don't free data, since we create resource entries that point into 15838889Sjdp it. If we ever want to free up the resource information we read, 15938889Sjdp this will have to be cleaned up. */ 16038889Sjdp 161218822Sdim ret = read_coff_res_dir (&wrbfd, data, &finfo, (const rc_res_id *) NULL, 0); 162218822Sdim 163218822Sdim bfd_close (abfd); 164218822Sdim 165218822Sdim return ret; 16638889Sjdp} 16738889Sjdp 16838889Sjdp/* Give an error if we are out of bounds. */ 16938889Sjdp 17038889Sjdpstatic void 171130561Sobrienoverrun (const struct coff_file_info *finfo, const char *msg) 17238889Sjdp{ 17360484Sobrien fatal (_("%s: %s: address out of bounds"), finfo->filename, msg); 17438889Sjdp} 17538889Sjdp 17638889Sjdp/* Read a resource directory. */ 17738889Sjdp 178218822Sdimstatic rc_res_directory * 179218822Sdimread_coff_res_dir (windres_bfd *wrbfd, const bfd_byte *data, 180218822Sdim const struct coff_file_info *finfo, 181218822Sdim const rc_res_id *type, int level) 18238889Sjdp{ 18338889Sjdp const struct extern_res_directory *erd; 184218822Sdim rc_res_directory *rd; 18538889Sjdp int name_count, id_count, i; 186218822Sdim rc_res_entry **pp; 18738889Sjdp const struct extern_res_entry *ere; 18838889Sjdp 18960484Sobrien if ((size_t) (finfo->data_end - data) < sizeof (struct extern_res_directory)) 19060484Sobrien overrun (finfo, _("directory")); 19138889Sjdp 19238889Sjdp erd = (const struct extern_res_directory *) data; 19338889Sjdp 194218822Sdim rd = (rc_res_directory *) res_alloc (sizeof (rc_res_directory)); 195218822Sdim rd->characteristics = windres_get_32 (wrbfd, erd->characteristics, 4); 196218822Sdim rd->time = windres_get_32 (wrbfd, erd->time, 4); 197218822Sdim rd->major = windres_get_16 (wrbfd, erd->major, 2); 198218822Sdim rd->minor = windres_get_16 (wrbfd, erd->minor, 2); 19938889Sjdp rd->entries = NULL; 20038889Sjdp 201218822Sdim name_count = windres_get_16 (wrbfd, erd->name_count, 2); 202218822Sdim id_count = windres_get_16 (wrbfd, erd->id_count, 2); 20338889Sjdp 20438889Sjdp pp = &rd->entries; 20538889Sjdp 20638889Sjdp /* The resource directory entries immediately follow the directory 20738889Sjdp table. */ 20838889Sjdp ere = (const struct extern_res_entry *) (erd + 1); 20938889Sjdp 21038889Sjdp for (i = 0; i < name_count; i++, ere++) 21138889Sjdp { 212218822Sdim rc_uint_type name, rva; 213218822Sdim rc_res_entry *re; 21438889Sjdp const bfd_byte *ers; 21538889Sjdp int length, j; 21638889Sjdp 21738889Sjdp if ((const bfd_byte *) ere >= finfo->data_end) 21860484Sobrien overrun (finfo, _("named directory entry")); 21938889Sjdp 220218822Sdim name = windres_get_32 (wrbfd, ere->name, 4); 221218822Sdim rva = windres_get_32 (wrbfd, ere->rva, 4); 22238889Sjdp 22338889Sjdp /* For some reason the high bit in NAME is set. */ 22438889Sjdp name &=~ 0x80000000; 22538889Sjdp 226218822Sdim if (name > (rc_uint_type) (finfo->data_end - finfo->data)) 22760484Sobrien overrun (finfo, _("directory entry name")); 22838889Sjdp 22938889Sjdp ers = finfo->data + name; 23038889Sjdp 231218822Sdim re = (rc_res_entry *) res_alloc (sizeof *re); 23238889Sjdp re->next = NULL; 23338889Sjdp re->id.named = 1; 234218822Sdim length = windres_get_16 (wrbfd, ers, 2); 23538889Sjdp re->id.u.n.length = length; 23638889Sjdp re->id.u.n.name = (unichar *) res_alloc (length * sizeof (unichar)); 23738889Sjdp for (j = 0; j < length; j++) 238218822Sdim re->id.u.n.name[j] = windres_get_16 (wrbfd, ers + j * 2 + 2, 2); 23938889Sjdp 24038889Sjdp if (level == 0) 24138889Sjdp type = &re->id; 24238889Sjdp 24338889Sjdp if ((rva & 0x80000000) != 0) 24438889Sjdp { 24538889Sjdp rva &=~ 0x80000000; 246218822Sdim if (rva >= (rc_uint_type) (finfo->data_end - finfo->data)) 24760484Sobrien overrun (finfo, _("named subdirectory")); 24838889Sjdp re->subdir = 1; 249218822Sdim re->u.dir = read_coff_res_dir (wrbfd, finfo->data + rva, finfo, type, 25038889Sjdp level + 1); 25138889Sjdp } 25238889Sjdp else 25338889Sjdp { 254218822Sdim if (rva >= (rc_uint_type) (finfo->data_end - finfo->data)) 25560484Sobrien overrun (finfo, _("named resource")); 25638889Sjdp re->subdir = 0; 257218822Sdim re->u.res = read_coff_data_entry (wrbfd, finfo->data + rva, finfo, type); 25838889Sjdp } 25938889Sjdp 26038889Sjdp *pp = re; 26138889Sjdp pp = &re->next; 26238889Sjdp } 26338889Sjdp 26438889Sjdp for (i = 0; i < id_count; i++, ere++) 26538889Sjdp { 26638889Sjdp unsigned long name, rva; 267218822Sdim rc_res_entry *re; 26838889Sjdp 26938889Sjdp if ((const bfd_byte *) ere >= finfo->data_end) 27060484Sobrien overrun (finfo, _("ID directory entry")); 27138889Sjdp 272218822Sdim name = windres_get_32 (wrbfd, ere->name, 4); 273218822Sdim rva = windres_get_32 (wrbfd, ere->rva, 4); 27438889Sjdp 275218822Sdim re = (rc_res_entry *) res_alloc (sizeof *re); 27638889Sjdp re->next = NULL; 27738889Sjdp re->id.named = 0; 27838889Sjdp re->id.u.id = name; 27938889Sjdp 28038889Sjdp if (level == 0) 28138889Sjdp type = &re->id; 28238889Sjdp 28338889Sjdp if ((rva & 0x80000000) != 0) 28438889Sjdp { 28538889Sjdp rva &=~ 0x80000000; 286218822Sdim if (rva >= (rc_uint_type) (finfo->data_end - finfo->data)) 28760484Sobrien overrun (finfo, _("ID subdirectory")); 28838889Sjdp re->subdir = 1; 289218822Sdim re->u.dir = read_coff_res_dir (wrbfd, finfo->data + rva, finfo, type, 29038889Sjdp level + 1); 29138889Sjdp } 29238889Sjdp else 29338889Sjdp { 294218822Sdim if (rva >= (rc_uint_type) (finfo->data_end - finfo->data)) 29560484Sobrien overrun (finfo, _("ID resource")); 29638889Sjdp re->subdir = 0; 297218822Sdim re->u.res = read_coff_data_entry (wrbfd, finfo->data + rva, finfo, type); 29838889Sjdp } 29938889Sjdp 30038889Sjdp *pp = re; 30138889Sjdp pp = &re->next; 30238889Sjdp } 30338889Sjdp 30438889Sjdp return rd; 30538889Sjdp} 30638889Sjdp 30738889Sjdp/* Read a resource data entry. */ 30838889Sjdp 309218822Sdimstatic rc_res_resource * 310218822Sdimread_coff_data_entry (windres_bfd *wrbfd, const bfd_byte *data, 311218822Sdim const struct coff_file_info *finfo, 312218822Sdim const rc_res_id *type) 31338889Sjdp{ 31438889Sjdp const struct extern_res_data *erd; 315218822Sdim rc_res_resource *r; 316218822Sdim rc_uint_type size, rva; 31738889Sjdp const bfd_byte *resdata; 31838889Sjdp 31938889Sjdp if (type == NULL) 32060484Sobrien fatal (_("resource type unknown")); 32138889Sjdp 32260484Sobrien if ((size_t) (finfo->data_end - data) < sizeof (struct extern_res_data)) 32360484Sobrien overrun (finfo, _("data entry")); 32438889Sjdp 32538889Sjdp erd = (const struct extern_res_data *) data; 32638889Sjdp 327218822Sdim size = windres_get_32 (wrbfd, erd->size, 4); 328218822Sdim rva = windres_get_32 (wrbfd, erd->rva, 4); 32938889Sjdp if (rva < finfo->secaddr 330218822Sdim || rva - finfo->secaddr >= (rc_uint_type) (finfo->data_end - finfo->data)) 33160484Sobrien overrun (finfo, _("resource data")); 33238889Sjdp 33338889Sjdp resdata = finfo->data + (rva - finfo->secaddr); 33438889Sjdp 335218822Sdim if (size > (rc_uint_type) (finfo->data_end - resdata)) 33660484Sobrien overrun (finfo, _("resource data size")); 33738889Sjdp 338218822Sdim r = bin_to_res (wrbfd, *type, resdata, size); 33938889Sjdp 340218822Sdim memset (&r->res_info, 0, sizeof (rc_res_res_info)); 341218822Sdim r->coff_info.codepage = windres_get_32 (wrbfd, erd->codepage, 4); 342218822Sdim r->coff_info.reserved = windres_get_32 (wrbfd, erd->reserved, 4); 34338889Sjdp 34438889Sjdp return r; 34538889Sjdp} 34638889Sjdp 34738889Sjdp/* This structure is used to build a list of bindata structures. */ 34838889Sjdp 34938889Sjdpstruct bindata_build 35038889Sjdp{ 35138889Sjdp /* The data. */ 352218822Sdim bindata *d; 35338889Sjdp /* The last structure we have added to the list. */ 354218822Sdim bindata *last; 35538889Sjdp /* The size of the list as a whole. */ 35638889Sjdp unsigned long length; 35738889Sjdp}; 35838889Sjdp 359218822Sdimstruct coff_res_data_build 360218822Sdim{ 361218822Sdim /* The data. */ 362218822Sdim coff_res_data *d; 363218822Sdim /* The last structure we have added to the list. */ 364218822Sdim coff_res_data *last; 365218822Sdim /* The size of the list as a whole. */ 366218822Sdim unsigned long length; 367218822Sdim}; 368218822Sdim 36938889Sjdp/* This structure keeps track of information as we build the directory 37038889Sjdp tree. */ 37138889Sjdp 37238889Sjdpstruct coff_write_info 37338889Sjdp{ 37438889Sjdp /* These fields are based on the BFD. */ 37538889Sjdp /* The BFD itself. */ 376218822Sdim windres_bfd *wrbfd; 37738889Sjdp /* Pointer to section symbol used to build RVA relocs. */ 37838889Sjdp asymbol **sympp; 37938889Sjdp 38038889Sjdp /* These fields are computed initially, and then not changed. */ 38138889Sjdp /* Length of directory tables and entries. */ 38238889Sjdp unsigned long dirsize; 38338889Sjdp /* Length of directory entry strings. */ 38438889Sjdp unsigned long dirstrsize; 38538889Sjdp /* Length of resource data entries. */ 38638889Sjdp unsigned long dataentsize; 38738889Sjdp 38838889Sjdp /* These fields are updated as we add data. */ 38938889Sjdp /* Directory tables and entries. */ 39038889Sjdp struct bindata_build dirs; 39138889Sjdp /* Directory entry strings. */ 39238889Sjdp struct bindata_build dirstrs; 39338889Sjdp /* Resource data entries. */ 39438889Sjdp struct bindata_build dataents; 39538889Sjdp /* Actual resource data. */ 396218822Sdim struct coff_res_data_build resources; 39738889Sjdp /* Relocations. */ 39838889Sjdp arelent **relocs; 39938889Sjdp /* Number of relocations. */ 40038889Sjdp unsigned int reloc_count; 40138889Sjdp}; 40238889Sjdp 403218822Sdimstatic void coff_bin_sizes (const rc_res_directory *, struct coff_write_info *); 404218822Sdimstatic bfd_byte *coff_alloc (struct bindata_build *, rc_uint_type); 40538889Sjdpstatic void coff_to_bin 406218822Sdim (const rc_res_directory *, struct coff_write_info *); 40738889Sjdpstatic void coff_res_to_bin 408218822Sdim (const rc_res_resource *, struct coff_write_info *); 40938889Sjdp 41038889Sjdp/* Write resources to a COFF file. RESOURCES should already be 41138889Sjdp sorted. 41238889Sjdp 41338889Sjdp Right now we always create a new file. Someday we should also 41438889Sjdp offer the ability to merge resources into an existing file. This 41538889Sjdp would require doing the basic work of objcopy, just modifying or 41638889Sjdp adding the .rsrc section. */ 41738889Sjdp 41838889Sjdpvoid 419130561Sobrienwrite_coff_file (const char *filename, const char *target, 420218822Sdim const rc_res_directory *resources) 42138889Sjdp{ 42238889Sjdp bfd *abfd; 42338889Sjdp asection *sec; 42438889Sjdp struct coff_write_info cwi; 425218822Sdim windres_bfd wrbfd; 426218822Sdim bindata *d; 427218822Sdim coff_res_data *rd; 42838889Sjdp unsigned long length, offset; 42938889Sjdp 43038889Sjdp if (filename == NULL) 43160484Sobrien fatal (_("filename required for COFF output")); 43238889Sjdp 43338889Sjdp abfd = bfd_openw (filename, target); 43438889Sjdp if (abfd == NULL) 43538889Sjdp bfd_fatal (filename); 43638889Sjdp 43738889Sjdp if (! bfd_set_format (abfd, bfd_object)) 43838889Sjdp bfd_fatal ("bfd_set_format"); 43938889Sjdp 44060484Sobrien#if defined DLLTOOL_SH 44160484Sobrien if (! bfd_set_arch_mach (abfd, bfd_arch_sh, 0)) 44260484Sobrien bfd_fatal ("bfd_set_arch_mach(sh)"); 44360484Sobrien#elif defined DLLTOOL_MIPS 44460484Sobrien if (! bfd_set_arch_mach (abfd, bfd_arch_mips, 0)) 44560484Sobrien bfd_fatal ("bfd_set_arch_mach(mips)"); 44660484Sobrien#elif defined DLLTOOL_ARM 44760484Sobrien if (! bfd_set_arch_mach (abfd, bfd_arch_arm, 0)) 44860484Sobrien bfd_fatal ("bfd_set_arch_mach(arm)"); 44960484Sobrien#else 45038889Sjdp /* FIXME: This is obviously i386 specific. */ 45138889Sjdp if (! bfd_set_arch_mach (abfd, bfd_arch_i386, 0)) 45260484Sobrien bfd_fatal ("bfd_set_arch_mach(i386)"); 45360484Sobrien#endif 45438889Sjdp 45538889Sjdp if (! bfd_set_file_flags (abfd, HAS_SYMS | HAS_RELOC)) 45638889Sjdp bfd_fatal ("bfd_set_file_flags"); 45738889Sjdp 45838889Sjdp sec = bfd_make_section (abfd, ".rsrc"); 45938889Sjdp if (sec == NULL) 46038889Sjdp bfd_fatal ("bfd_make_section"); 46138889Sjdp 46238889Sjdp if (! bfd_set_section_flags (abfd, sec, 46338889Sjdp (SEC_HAS_CONTENTS | SEC_ALLOC 46438889Sjdp | SEC_LOAD | SEC_DATA))) 46538889Sjdp bfd_fatal ("bfd_set_section_flags"); 46638889Sjdp 46738889Sjdp if (! bfd_set_symtab (abfd, sec->symbol_ptr_ptr, 1)) 46838889Sjdp bfd_fatal ("bfd_set_symtab"); 46938889Sjdp 47038889Sjdp /* Requiring this is probably a bug in BFD. */ 47138889Sjdp sec->output_section = sec; 47238889Sjdp 47338889Sjdp /* The order of data in the .rsrc section is 47438889Sjdp resource directory tables and entries 47538889Sjdp resource directory strings 47638889Sjdp resource data entries 47738889Sjdp actual resource data 47838889Sjdp 47938889Sjdp We build these different types of data in different lists. */ 48038889Sjdp 481218822Sdim set_windres_bfd (&wrbfd, abfd, sec, WR_KIND_BFD); 482218822Sdim 483218822Sdim cwi.wrbfd = &wrbfd; 48438889Sjdp cwi.sympp = sec->symbol_ptr_ptr; 48538889Sjdp cwi.dirsize = 0; 48638889Sjdp cwi.dirstrsize = 0; 48738889Sjdp cwi.dataentsize = 0; 48838889Sjdp cwi.dirs.d = NULL; 48938889Sjdp cwi.dirs.last = NULL; 49038889Sjdp cwi.dirs.length = 0; 49138889Sjdp cwi.dirstrs.d = NULL; 49238889Sjdp cwi.dirstrs.last = NULL; 49338889Sjdp cwi.dirstrs.length = 0; 49438889Sjdp cwi.dataents.d = NULL; 49538889Sjdp cwi.dataents.last = NULL; 49638889Sjdp cwi.dataents.length = 0; 49738889Sjdp cwi.resources.d = NULL; 49838889Sjdp cwi.resources.last = NULL; 49938889Sjdp cwi.resources.length = 0; 50038889Sjdp cwi.relocs = NULL; 50138889Sjdp cwi.reloc_count = 0; 50238889Sjdp 50338889Sjdp /* Work out the sizes of the resource directory entries, so that we 50438889Sjdp know the various offsets we will need. */ 50538889Sjdp coff_bin_sizes (resources, &cwi); 50638889Sjdp 50738889Sjdp /* Force the directory strings to be 32 bit aligned. Every other 50838889Sjdp structure is 32 bit aligned anyhow. */ 50938889Sjdp cwi.dirstrsize = (cwi.dirstrsize + 3) &~ 3; 51038889Sjdp 51138889Sjdp /* Actually convert the resources to binary. */ 51238889Sjdp coff_to_bin (resources, &cwi); 51338889Sjdp 51438889Sjdp /* Add another 2 bytes to the directory strings if needed for 51538889Sjdp alignment. */ 51638889Sjdp if ((cwi.dirstrs.length & 3) != 0) 51738889Sjdp { 518218822Sdim bfd_byte *ex; 51938889Sjdp 52038889Sjdp ex = coff_alloc (&cwi.dirstrs, 2); 52138889Sjdp ex[0] = 0; 52238889Sjdp ex[1] = 0; 52338889Sjdp } 52438889Sjdp 52538889Sjdp /* Make sure that the data we built came out to the same size as we 52638889Sjdp calculated initially. */ 52738889Sjdp assert (cwi.dirs.length == cwi.dirsize); 52838889Sjdp assert (cwi.dirstrs.length == cwi.dirstrsize); 52938889Sjdp assert (cwi.dataents.length == cwi.dataentsize); 53038889Sjdp 53138889Sjdp length = (cwi.dirsize 53238889Sjdp + cwi.dirstrsize 53338889Sjdp + cwi.dataentsize 53438889Sjdp + cwi.resources.length); 53538889Sjdp 53638889Sjdp if (! bfd_set_section_size (abfd, sec, length)) 53738889Sjdp bfd_fatal ("bfd_set_section_size"); 53838889Sjdp 53938889Sjdp bfd_set_reloc (abfd, sec, cwi.relocs, cwi.reloc_count); 54038889Sjdp 54138889Sjdp offset = 0; 54238889Sjdp for (d = cwi.dirs.d; d != NULL; d = d->next) 54338889Sjdp { 54438889Sjdp if (! bfd_set_section_contents (abfd, sec, d->data, offset, d->length)) 54538889Sjdp bfd_fatal ("bfd_set_section_contents"); 54638889Sjdp offset += d->length; 54738889Sjdp } 54838889Sjdp for (d = cwi.dirstrs.d; d != NULL; d = d->next) 54938889Sjdp { 550218822Sdim set_windres_bfd_content (&wrbfd, d->data, offset, d->length); 55138889Sjdp offset += d->length; 55238889Sjdp } 55338889Sjdp for (d = cwi.dataents.d; d != NULL; d = d->next) 55438889Sjdp { 555218822Sdim set_windres_bfd_content (&wrbfd, d->data, offset, d->length); 55638889Sjdp offset += d->length; 55738889Sjdp } 558218822Sdim for (rd = cwi.resources.d; rd != NULL; rd = rd->next) 55938889Sjdp { 560218822Sdim res_to_bin (cwi.wrbfd, (rc_uint_type) offset, rd->res); 561218822Sdim offset += rd->length; 56238889Sjdp } 56338889Sjdp 56438889Sjdp assert (offset == length); 56538889Sjdp 56638889Sjdp if (! bfd_close (abfd)) 56738889Sjdp bfd_fatal ("bfd_close"); 56838889Sjdp 56938889Sjdp /* We allocated the relocs array using malloc. */ 57038889Sjdp free (cwi.relocs); 57138889Sjdp} 57238889Sjdp 57338889Sjdp/* Work out the sizes of the various fixed size resource directory 57438889Sjdp entries. This updates fields in CWI. */ 57538889Sjdp 57638889Sjdpstatic void 577218822Sdimcoff_bin_sizes (const rc_res_directory *resdir, 578130561Sobrien struct coff_write_info *cwi) 57938889Sjdp{ 580218822Sdim const rc_res_entry *re; 58138889Sjdp 58238889Sjdp cwi->dirsize += sizeof (struct extern_res_directory); 58338889Sjdp 58438889Sjdp for (re = resdir->entries; re != NULL; re = re->next) 58538889Sjdp { 58638889Sjdp cwi->dirsize += sizeof (struct extern_res_entry); 58738889Sjdp 58838889Sjdp if (re->id.named) 58938889Sjdp cwi->dirstrsize += re->id.u.n.length * 2 + 2; 59038889Sjdp 59138889Sjdp if (re->subdir) 59238889Sjdp coff_bin_sizes (re->u.dir, cwi); 59338889Sjdp else 59438889Sjdp cwi->dataentsize += sizeof (struct extern_res_data); 59538889Sjdp } 59638889Sjdp} 59738889Sjdp 59838889Sjdp/* Allocate data for a particular list. */ 59938889Sjdp 600218822Sdimstatic bfd_byte * 601218822Sdimcoff_alloc (struct bindata_build *bb, rc_uint_type size) 60238889Sjdp{ 603218822Sdim bindata *d; 60438889Sjdp 605218822Sdim d = (bindata *) reswr_alloc (sizeof (bindata)); 60638889Sjdp 60738889Sjdp d->next = NULL; 608218822Sdim d->data = (bfd_byte *) reswr_alloc (size); 60938889Sjdp d->length = size; 61038889Sjdp 61138889Sjdp if (bb->d == NULL) 61238889Sjdp bb->d = d; 61338889Sjdp else 61438889Sjdp bb->last->next = d; 61538889Sjdp bb->last = d; 61638889Sjdp bb->length += size; 61738889Sjdp 61838889Sjdp return d->data; 61938889Sjdp} 62038889Sjdp 62138889Sjdp/* Convert the resource directory RESDIR to binary. */ 62238889Sjdp 62338889Sjdpstatic void 624218822Sdimcoff_to_bin (const rc_res_directory *resdir, struct coff_write_info *cwi) 62538889Sjdp{ 62638889Sjdp struct extern_res_directory *erd; 62738889Sjdp int ci, cn; 628218822Sdim const rc_res_entry *e; 62938889Sjdp struct extern_res_entry *ere; 63038889Sjdp 63138889Sjdp /* Write out the directory table. */ 63238889Sjdp 63338889Sjdp erd = ((struct extern_res_directory *) 63438889Sjdp coff_alloc (&cwi->dirs, sizeof (*erd))); 63538889Sjdp 636218822Sdim windres_put_32 (cwi->wrbfd, erd->characteristics, resdir->characteristics); 637218822Sdim windres_put_32 (cwi->wrbfd, erd->time, resdir->time); 638218822Sdim windres_put_16 (cwi->wrbfd, erd->major, resdir->major); 639218822Sdim windres_put_16 (cwi->wrbfd, erd->minor, resdir->minor); 64038889Sjdp 64138889Sjdp ci = 0; 64238889Sjdp cn = 0; 64338889Sjdp for (e = resdir->entries; e != NULL; e = e->next) 64438889Sjdp { 64538889Sjdp if (e->id.named) 64638889Sjdp ++cn; 64738889Sjdp else 64838889Sjdp ++ci; 64938889Sjdp } 65038889Sjdp 651218822Sdim windres_put_16 (cwi->wrbfd, erd->name_count, cn); 652218822Sdim windres_put_16 (cwi->wrbfd, erd->id_count, ci); 65338889Sjdp 65438889Sjdp /* Write out the data entries. Note that we allocate space for all 65538889Sjdp the entries before writing them out. That permits a recursive 65638889Sjdp call to work correctly when writing out subdirectories. */ 65738889Sjdp 65838889Sjdp ere = ((struct extern_res_entry *) 65938889Sjdp coff_alloc (&cwi->dirs, (ci + cn) * sizeof (*ere))); 66038889Sjdp for (e = resdir->entries; e != NULL; e = e->next, ere++) 66138889Sjdp { 66238889Sjdp if (! e->id.named) 663218822Sdim windres_put_32 (cwi->wrbfd, ere->name, e->id.u.id); 66438889Sjdp else 66538889Sjdp { 666218822Sdim bfd_byte *str; 667218822Sdim rc_uint_type i; 66838889Sjdp 66938889Sjdp /* For some reason existing files seem to have the high bit 67038889Sjdp set on the address of the name, although that is not 67138889Sjdp documented. */ 672218822Sdim windres_put_32 (cwi->wrbfd, ere->name, 673218822Sdim 0x80000000 | (cwi->dirsize + cwi->dirstrs.length)); 67438889Sjdp 67538889Sjdp str = coff_alloc (&cwi->dirstrs, e->id.u.n.length * 2 + 2); 676218822Sdim windres_put_16 (cwi->wrbfd, str, e->id.u.n.length); 67738889Sjdp for (i = 0; i < e->id.u.n.length; i++) 678218822Sdim windres_put_16 (cwi->wrbfd, str + (i + 1) * sizeof (unichar), e->id.u.n.name[i]); 67938889Sjdp } 68038889Sjdp 68138889Sjdp if (e->subdir) 68238889Sjdp { 683218822Sdim windres_put_32 (cwi->wrbfd, ere->rva, 0x80000000 | cwi->dirs.length); 68438889Sjdp coff_to_bin (e->u.dir, cwi); 68538889Sjdp } 68638889Sjdp else 68738889Sjdp { 688218822Sdim windres_put_32 (cwi->wrbfd, ere->rva, 689218822Sdim cwi->dirsize + cwi->dirstrsize + cwi->dataents.length); 69038889Sjdp 69138889Sjdp coff_res_to_bin (e->u.res, cwi); 69238889Sjdp } 69338889Sjdp } 69438889Sjdp} 69538889Sjdp 69638889Sjdp/* Convert the resource RES to binary. */ 69738889Sjdp 69838889Sjdpstatic void 699218822Sdimcoff_res_to_bin (const rc_res_resource *res, struct coff_write_info *cwi) 70038889Sjdp{ 70138889Sjdp arelent *r; 70238889Sjdp struct extern_res_data *erd; 703218822Sdim coff_res_data *d; 70438889Sjdp 70538889Sjdp /* For some reason, although every other address is a section 70638889Sjdp offset, the address of the resource data itself is an RVA. That 70738889Sjdp means that we need to generate a relocation for it. We allocate 70838889Sjdp the relocs array using malloc so that we can use realloc. FIXME: 70938889Sjdp This relocation handling is correct for the i386, but probably 71038889Sjdp not for any other target. */ 71138889Sjdp 71238889Sjdp r = (arelent *) reswr_alloc (sizeof (arelent)); 71338889Sjdp r->sym_ptr_ptr = cwi->sympp; 71438889Sjdp r->address = cwi->dirsize + cwi->dirstrsize + cwi->dataents.length; 71538889Sjdp r->addend = 0; 716218822Sdim r->howto = bfd_reloc_type_lookup (WR_BFD (cwi->wrbfd), BFD_RELOC_RVA); 71738889Sjdp if (r->howto == NULL) 71860484Sobrien bfd_fatal (_("can't get BFD_RELOC_RVA relocation type")); 71938889Sjdp 72038889Sjdp cwi->relocs = xrealloc (cwi->relocs, 72138889Sjdp (cwi->reloc_count + 2) * sizeof (arelent *)); 72238889Sjdp cwi->relocs[cwi->reloc_count] = r; 72338889Sjdp cwi->relocs[cwi->reloc_count + 1] = NULL; 72438889Sjdp ++cwi->reloc_count; 72538889Sjdp 72638889Sjdp erd = (struct extern_res_data *) coff_alloc (&cwi->dataents, sizeof (*erd)); 72738889Sjdp 728218822Sdim windres_put_32 (cwi->wrbfd, erd->rva, 72938889Sjdp (cwi->dirsize 73038889Sjdp + cwi->dirstrsize 73138889Sjdp + cwi->dataentsize 732218822Sdim + cwi->resources.length)); 733218822Sdim windres_put_32 (cwi->wrbfd, erd->codepage, res->coff_info.codepage); 734218822Sdim windres_put_32 (cwi->wrbfd, erd->reserved, res->coff_info.reserved); 73538889Sjdp 736218822Sdim d = (coff_res_data *) reswr_alloc (sizeof (coff_res_data)); 737218822Sdim d->length = res_to_bin (NULL, (rc_uint_type) 0, res); 738218822Sdim d->res = res; 739218822Sdim d->next = NULL; 74038889Sjdp 74138889Sjdp if (cwi->resources.d == NULL) 74238889Sjdp cwi->resources.d = d; 74338889Sjdp else 74438889Sjdp cwi->resources.last->next = d; 74538889Sjdp 74638889Sjdp cwi->resources.last = d; 747218822Sdim cwi->resources.length += (d->length + 3) & ~3; 74838889Sjdp 749218822Sdim windres_put_32 (cwi->wrbfd, erd->size, d->length); 75038889Sjdp 75138889Sjdp /* Force the next resource to have 32 bit alignment. */ 752218822Sdim d->length = (d->length + 3) & ~3; 75338889Sjdp} 754