1 /* resrc.c -- read and write Windows rc files.
2 Copyright 1997 Free Software Foundation, Inc.
3 Written by Ian Lance Taylor, Cygnus Support.
5 This file is part of GNU Binutils.
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.
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.
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
22 /* This file contains function that read and write Windows resources
27 #include "libiberty.h"
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. */
34 #include "coff/internal.h"
37 /* Information we extract from the file. */
43 /* Data read from the file. */
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. */
49 /* Non-zero if the file is big endian. */
53 /* A resource directory table in a COFF file. */
55 struct extern_res_directory
57 /* Characteristics. */
58 bfd_byte characteristics[4];
61 /* Major version number. */
63 /* Minor version number. */
65 /* Number of named directory entries. */
66 bfd_byte name_count[2];
67 /* Number of directory entries with IDs. */
71 /* A resource directory entry in a COFF file. */
73 struct extern_res_entry
77 /* Address of resource entry or subdirectory. */
81 /* A resource data entry in a COFF file. */
83 struct extern_res_data
85 /* Address of resource data. This is apparently a file relative
86 address, rather than a section offset. */
88 /* Size of resource data. */
96 /* Macros to swap in values. */
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))
101 /* Local functions. */
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 *));
109 /* Read the resources in a COFF file. */
111 struct res_directory *
112 read_coff_rsrc (filename, target)
113 const char *filename;
121 struct coff_file_info finfo;
123 abfd = bfd_openr (filename, target);
125 bfd_fatal (filename);
127 if (! bfd_check_format_matches (abfd, bfd_object, &matching))
129 bfd_nonfatal (bfd_get_filename (abfd));
130 if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
131 list_matching_formats (matching);
135 sec = bfd_get_section_by_name (abfd, ".rsrc");
138 fprintf (stderr, "%s: %s: no resource section\n", program_name,
143 size = bfd_section_size (abfd, sec);
144 data = (bfd_byte *) xmalloc (size);
146 if (! bfd_get_section_contents (abfd, sec, data, 0, size))
147 bfd_fatal ("can't read resource section");
149 finfo.filename = filename;
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);
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. */
163 return read_coff_res_dir (data, &finfo);
166 /* Give an error if we are out of bounds. */
170 const struct coff_file_info *finfo;
173 fatal ("%s: %s: address out of bounds", finfo->filename, msg);
176 /* Read a resource directory. */
178 static struct res_directory *
179 read_coff_res_dir (data, finfo)
180 const bfd_byte *data;
181 const struct coff_file_info *finfo;
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;
189 if (finfo->data_end - data < sizeof (struct extern_res_directory))
190 overrun (finfo, "directory");
192 erd = (const struct extern_res_directory *) data;
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);
201 name_count = get_16 (finfo, erd->name_count);
202 id_count = get_16 (finfo, erd->id_count);
206 /* The resource directory entries immediately follow the directory
208 ere = (const struct extern_res_entry *) (erd + 1);
210 for (i = 0; i < name_count; i++, ere++)
212 unsigned long name, rva;
213 struct res_entry *re;
217 if ((const bfd_byte *) ere >= finfo->data_end)
218 overrun (finfo, "named directory entry");
220 name = get_32 (finfo, ere->name);
221 rva = get_32 (finfo, ere->rva);
223 /* For some reason the high bit in NAME is set. */
226 if (name > finfo->data_end - finfo->data)
227 overrun (finfo, "directory entry name");
229 ers = finfo->data + name;
231 re = (struct res_entry *) xmalloc (sizeof *re);
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);
241 if ((rva & 0x80000000) != 0)
244 if (rva >= finfo->data_end - finfo->data)
245 overrun (finfo, "named subdirectory");
247 re->u.dir = read_coff_res_dir (finfo->data + rva, finfo);
251 if (rva >= finfo->data_end - finfo->data)
252 overrun (finfo, "named resource");
254 re->u.res = read_coff_data_entry (finfo->data + rva, finfo);
261 for (i = 0; i < id_count; i++, ere++)
263 unsigned long name, rva;
264 struct res_entry *re;
266 if ((const bfd_byte *) ere >= finfo->data_end)
267 overrun (finfo, "ID directory entry");
269 name = get_32 (finfo, ere->name);
270 rva = get_32 (finfo, ere->rva);
272 re = (struct res_entry *) xmalloc (sizeof *re);
277 if ((rva & 0x80000000) != 0)
280 if (rva >= finfo->data_end - finfo->data)
281 overrun (finfo, "ID subdirectory");
283 re->u.dir = read_coff_res_dir (finfo->data + rva, finfo);
287 if (rva >= finfo->data_end - finfo->data)
288 overrun (finfo, "ID resource");
290 re->u.res = read_coff_data_entry (finfo->data + rva, finfo);
300 /* Read a resource data entry. */
302 static struct res_resource *
303 read_coff_data_entry (data, finfo)
304 const bfd_byte *data;
305 const struct coff_file_info *finfo;
307 const struct extern_res_data *erd;
308 struct res_resource *r;
309 unsigned long size, rva;
310 const bfd_byte *resdata;
312 if (finfo->data_end - data < sizeof (struct extern_res_data))
313 overrun (finfo, "data entry");
315 erd = (const struct extern_res_data *) data;
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);
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");
328 resdata = finfo->data + (rva - finfo->secaddr);
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;