Another windres snapshot. Can now read the COFF resources directory,
[external/binutils.git] / binutils / rescoff.c
1 /* resrc.c -- read and write Windows rc files.
2    Copyright 1997 Free Software Foundation, Inc.
3    Written by Ian Lance Taylor, Cygnus Support.
4
5    This file is part of GNU Binutils.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 /* This file contains function that read and write Windows resources
23    in COFF files.  */
24
25 #include "bfd.h"
26 #include "bucomm.h"
27 #include "libiberty.h"
28 #include "windres.h"
29
30 /* In order to use the address of a resource data entry, we need to
31    get the image base of the file.  Right now we extract it from
32    internal BFD information.  FIXME.  */
33
34 #include "coff/internal.h"
35 #include "libcoff.h"
36
37 /* Information we extract from the file.  */
38
39 struct coff_file_info
40 {
41   /* File name.  */
42   const char *filename;
43   /* Data read from the file.  */
44   const bfd_byte *data;
45   /* End of data read from file.  */
46   const bfd_byte *data_end;
47   /* Address of the resource section minus the image base of the file.  */
48   bfd_vma secaddr;
49   /* Non-zero if the file is big endian.  */
50   int big_endian;
51 };
52
53 /* A resource directory table in a COFF file.  */
54
55 struct extern_res_directory
56 {
57   /* Characteristics.  */
58   bfd_byte characteristics[4];
59   /* Time stamp.  */
60   bfd_byte time[4];
61   /* Major version number.  */
62   bfd_byte major[2];
63   /* Minor version number.  */
64   bfd_byte minor[2];
65   /* Number of named directory entries.  */
66   bfd_byte name_count[2];
67   /* Number of directory entries with IDs.  */
68   bfd_byte id_count[2];
69 };
70
71 /* A resource directory entry in a COFF file.  */
72
73 struct extern_res_entry
74 {
75   /* Name or ID.  */
76   bfd_byte name[4];
77   /* Address of resource entry or subdirectory.  */
78   bfd_byte rva[4];
79 };
80
81 /* A resource data entry in a COFF file.  */
82
83 struct extern_res_data
84 {
85   /* Address of resource data.  This is apparently a file relative
86      address, rather than a section offset.  */
87   bfd_byte rva[4];
88   /* Size of resource data.  */
89   bfd_byte size[4];
90   /* Code page.  */
91   bfd_byte codepage[4];
92   /* Reserved.  */
93   bfd_byte reserved[4];
94 };
95
96 /* Macros to swap in values.  */
97
98 #define get_16(fi, s) ((fi)->big_endian ? bfd_getb16 (s) : bfd_getl16 (s))
99 #define get_32(fi, s) ((fi)->big_endian ? bfd_getb32 (s) : bfd_getl32 (s))
100
101 /* Local functions.  */
102
103 static void overrun PARAMS ((const struct coff_file_info *, const char *));
104 static struct res_directory *read_coff_res_dir
105   PARAMS ((const bfd_byte *, const struct coff_file_info *));
106 static struct res_resource *read_coff_data_entry
107   PARAMS ((const bfd_byte *, const struct coff_file_info *));
108 \f
109 /* Read the resources in a COFF file.  */
110
111 struct res_directory *
112 read_coff_rsrc (filename, target)
113      const char *filename;
114      const char *target;
115 {
116   bfd *abfd;
117   char **matching;
118   asection *sec;
119   bfd_size_type size;
120   bfd_byte *data;
121   struct coff_file_info finfo;
122
123   abfd = bfd_openr (filename, target);
124   if (abfd == NULL)
125     bfd_fatal (filename);
126
127   if (! bfd_check_format_matches (abfd, bfd_object, &matching))
128     {
129       bfd_nonfatal (bfd_get_filename (abfd));
130       if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
131         list_matching_formats (matching);
132       xexit (1);
133     }
134
135   sec = bfd_get_section_by_name (abfd, ".rsrc");
136   if (sec == NULL)
137     {
138       fprintf (stderr, "%s: %s: no resource section\n", program_name,
139                filename);
140       xexit (1);
141     }
142
143   size = bfd_section_size (abfd, sec);
144   data = (bfd_byte *) xmalloc (size);
145
146   if (! bfd_get_section_contents (abfd, sec, data, 0, size))
147     bfd_fatal ("can't read resource section");
148
149   finfo.filename = filename;
150   finfo.data = data;
151   finfo.data_end = data + size;
152   finfo.secaddr = (bfd_get_section_vma (abfd, sec)
153                    - pe_data (abfd)->pe_opthdr.ImageBase);
154   finfo.big_endian = bfd_big_endian (abfd);
155
156   bfd_close (abfd);
157
158   /* Now just read in the top level resource directory.  Note that we
159      don't free data, since we create resource entries that point into
160      it.  If we ever want to free up the resource information we read,
161      this will have to be cleaned up.  */
162
163   return read_coff_res_dir (data, &finfo);
164 }
165
166 /* Give an error if we are out of bounds.  */
167
168 static void
169 overrun (finfo, msg)
170      const struct coff_file_info *finfo;
171      const char *msg;
172 {
173   fatal ("%s: %s: address out of bounds", finfo->filename, msg);
174 }
175
176 /* Read a resource directory.  */
177
178 static struct res_directory *
179 read_coff_res_dir (data, finfo)
180      const bfd_byte *data;
181      const struct coff_file_info *finfo;
182 {
183   const struct extern_res_directory *erd;
184   struct res_directory *rd;
185   int name_count, id_count, i;
186   struct res_entry **pp;
187   const struct extern_res_entry *ere;
188
189   if (finfo->data_end - data < sizeof (struct extern_res_directory))
190     overrun (finfo, "directory");
191
192   erd = (const struct extern_res_directory *) data;
193
194   rd = (struct res_directory *) xmalloc (sizeof *rd);
195   rd->characteristics = get_32 (finfo, erd->characteristics);
196   rd->time = get_32 (finfo, erd->time);
197   rd->major = get_16 (finfo, erd->major);
198   rd->minor = get_16 (finfo, erd->minor);
199   rd->entries = NULL;
200
201   name_count = get_16 (finfo, erd->name_count);
202   id_count = get_16 (finfo, erd->id_count);
203
204   pp = &rd->entries;
205
206   /* The resource directory entries immediately follow the directory
207      table.  */
208   ere = (const struct extern_res_entry *) (erd + 1);
209
210   for (i = 0; i < name_count; i++, ere++)
211     {
212       unsigned long name, rva;
213       struct res_entry *re;
214       const bfd_byte *ers;
215       int length, j;
216
217       if ((const bfd_byte *) ere >= finfo->data_end)
218         overrun (finfo, "named directory entry");
219
220       name = get_32 (finfo, ere->name);
221       rva = get_32 (finfo, ere->rva);
222
223       /* For some reason the high bit in NAME is set.  */
224       name &=~ 0x80000000;
225
226       if (name > finfo->data_end - finfo->data)
227         overrun (finfo, "directory entry name");
228
229       ers = finfo->data + name;
230
231       re = (struct res_entry *) xmalloc (sizeof *re);
232       re->next = NULL;
233       re->id.named = 1;
234       length = get_16 (finfo, ers);
235       re->id.u.n.length = length;
236       re->id.u.n.name = ((unsigned short *)
237                          xmalloc (length * sizeof (unsigned short)));
238       for (j = 0; j < length; j++)
239         re->id.u.n.name[j] = get_16 (finfo, ers + j * 2 + 2);
240
241       if ((rva & 0x80000000) != 0)
242         {
243           rva &=~ 0x80000000;
244           if (rva >= finfo->data_end - finfo->data)
245             overrun (finfo, "named subdirectory");
246           re->subdir = 1;
247           re->u.dir = read_coff_res_dir (finfo->data + rva, finfo);
248         }
249       else
250         {
251           if (rva >= finfo->data_end - finfo->data)
252             overrun (finfo, "named resource");
253           re->subdir = 0;
254           re->u.res = read_coff_data_entry (finfo->data + rva, finfo);
255         }
256
257       *pp = re;
258       pp = &re->next;
259     }
260
261   for (i = 0; i < id_count; i++, ere++)
262     {
263       unsigned long name, rva;
264       struct res_entry *re;
265
266       if ((const bfd_byte *) ere >= finfo->data_end)
267         overrun (finfo, "ID directory entry");
268
269       name = get_32 (finfo, ere->name);
270       rva = get_32 (finfo, ere->rva);
271
272       re = (struct res_entry *) xmalloc (sizeof *re);
273       re->next = NULL;
274       re->id.named = 0;
275       re->id.u.id = name;
276
277       if ((rva & 0x80000000) != 0)
278         {
279           rva &=~ 0x80000000;
280           if (rva >= finfo->data_end - finfo->data)
281             overrun (finfo, "ID subdirectory");
282           re->subdir = 1;
283           re->u.dir = read_coff_res_dir (finfo->data + rva, finfo);
284         }
285       else
286         {
287           if (rva >= finfo->data_end - finfo->data)
288             overrun (finfo, "ID resource");
289           re->subdir = 0;
290           re->u.res = read_coff_data_entry (finfo->data + rva, finfo);
291         }
292
293       *pp = re;
294       pp = &re->next;
295     }
296
297   return rd;
298 }
299
300 /* Read a resource data entry.  */
301
302 static struct res_resource *
303 read_coff_data_entry (data, finfo)
304      const bfd_byte *data;
305      const struct coff_file_info *finfo;
306 {
307   const struct extern_res_data *erd;
308   struct res_resource *r;
309   unsigned long size, rva;
310   const bfd_byte *resdata;
311
312   if (finfo->data_end - data < sizeof (struct extern_res_data))
313     overrun (finfo, "data entry");
314
315   erd = (const struct extern_res_data *) data;
316
317   r = (struct res_resource *) xmalloc (sizeof *r);
318   memset (&r->res_info, 0, sizeof (struct res_res_info));
319   r->coff_info.codepage = get_32 (finfo, erd->codepage);
320   r->coff_info.reserved = get_32 (finfo, erd->reserved);
321
322   size = get_32 (finfo, erd->size);
323   rva = get_32 (finfo, erd->rva);
324   if (rva < finfo->secaddr
325       || rva - finfo->secaddr >= finfo->data_end - finfo->data)
326     overrun (finfo, "resource data");
327
328   resdata = finfo->data + (rva - finfo->secaddr);
329
330   r->type = RES_TYPE_USERDATA;
331   r->u.userdata = ((struct rcdata_data *)
332                    xmalloc (sizeof (struct rcdata_data)));
333   r->u.userdata->first = ((struct rcdata_item *)
334                           xmalloc (sizeof (struct rcdata_item)));
335   r->u.userdata->last = r->u.userdata->first;
336   r->u.userdata->first->next = NULL;
337   r->u.userdata->first->type = RCDATA_BUFFER;
338   r->u.userdata->first->u.buffer.length = size;
339   r->u.userdata->first->u.buffer.data = (unsigned char *) resdata;
340
341   return r;
342 }