diff options
author | Ian Lance Taylor <ian@airs.com> | 1997-06-26 00:59:44 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@airs.com> | 1997-06-26 00:59:44 +0000 |
commit | 662cc41eafa15f94499988b0e1c9a9ffc0afc39e (patch) | |
tree | 3a343a28f84626f02a9c9e9d4db58a522a8194f6 /binutils/rescoff.c | |
parent | 9fd0d551fc32d9ee5e96e1f5526fefaeb1ba9d3c (diff) | |
download | gdb-662cc41eafa15f94499988b0e1c9a9ffc0afc39e.zip gdb-662cc41eafa15f94499988b0e1c9a9ffc0afc39e.tar.gz gdb-662cc41eafa15f94499988b0e1c9a9ffc0afc39e.tar.bz2 |
* resbin.c: New file.
* rclex.l, rcparse.y, rescoff.c, resrc.c, windres.c, windres.h:
Numerous fixes and improvements.
* Makefile.in: Rebuild dependencies.
(CFILES): Add resbin.c.
(WINDRES_OBJS): Add resbin.o.
Diffstat (limited to 'binutils/rescoff.c')
-rw-r--r-- | binutils/rescoff.c | 523 |
1 files changed, 476 insertions, 47 deletions
diff --git a/binutils/rescoff.c b/binutils/rescoff.c index 65df9bf..0f83e54 100644 --- a/binutils/rescoff.c +++ b/binutils/rescoff.c @@ -1,4 +1,4 @@ -/* resrc.c -- read and write Windows rc files. +/* rescoff.c -- read and write resources in Windows COFF files. Copyright 1997 Free Software Foundation, Inc. Written by Ian Lance Taylor, Cygnus Support. @@ -27,6 +27,8 @@ #include "libiberty.h" #include "windres.h" +#include <assert.h> + /* In order to use the address of a resource data entry, we need to get the image base of the file. Right now we extract it from internal BFD information. FIXME. */ @@ -95,16 +97,18 @@ struct extern_res_data /* Macros to swap in values. */ -#define get_16(fi, s) ((fi)->big_endian ? bfd_getb16 (s) : bfd_getl16 (s)) -#define get_32(fi, s) ((fi)->big_endian ? bfd_getb32 (s) : bfd_getl32 (s)) +#define getfi_16(fi, s) ((fi)->big_endian ? bfd_getb16 (s) : bfd_getl16 (s)) +#define getfi_32(fi, s) ((fi)->big_endian ? bfd_getb32 (s) : bfd_getl32 (s)) /* Local functions. */ static void overrun PARAMS ((const struct coff_file_info *, const char *)); static struct res_directory *read_coff_res_dir - PARAMS ((const bfd_byte *, const struct coff_file_info *)); + PARAMS ((const bfd_byte *, const struct coff_file_info *, + const struct res_id *, int)); static struct res_resource *read_coff_data_entry - PARAMS ((const bfd_byte *, const struct coff_file_info *)); + PARAMS ((const bfd_byte *, const struct coff_file_info *, + const struct res_id *)); /* Read the resources in a COFF file. */ @@ -141,7 +145,7 @@ read_coff_rsrc (filename, target) } size = bfd_section_size (abfd, sec); - data = (bfd_byte *) xmalloc (size); + data = (bfd_byte *) res_alloc (size); if (! bfd_get_section_contents (abfd, sec, data, 0, size)) bfd_fatal ("can't read resource section"); @@ -160,7 +164,7 @@ read_coff_rsrc (filename, target) it. If we ever want to free up the resource information we read, this will have to be cleaned up. */ - return read_coff_res_dir (data, &finfo); + return read_coff_res_dir (data, &finfo, (const struct res_id *) NULL, 0); } /* Give an error if we are out of bounds. */ @@ -176,9 +180,11 @@ overrun (finfo, msg) /* Read a resource directory. */ static struct res_directory * -read_coff_res_dir (data, finfo) +read_coff_res_dir (data, finfo, type, level) const bfd_byte *data; const struct coff_file_info *finfo; + const struct res_id *type; + int level; { const struct extern_res_directory *erd; struct res_directory *rd; @@ -191,15 +197,15 @@ read_coff_res_dir (data, finfo) erd = (const struct extern_res_directory *) data; - rd = (struct res_directory *) xmalloc (sizeof *rd); - rd->characteristics = get_32 (finfo, erd->characteristics); - rd->time = get_32 (finfo, erd->time); - rd->major = get_16 (finfo, erd->major); - rd->minor = get_16 (finfo, erd->minor); + rd = (struct res_directory *) res_alloc (sizeof *rd); + rd->characteristics = getfi_32 (finfo, erd->characteristics); + rd->time = getfi_32 (finfo, erd->time); + rd->major = getfi_16 (finfo, erd->major); + rd->minor = getfi_16 (finfo, erd->minor); rd->entries = NULL; - name_count = get_16 (finfo, erd->name_count); - id_count = get_16 (finfo, erd->id_count); + name_count = getfi_16 (finfo, erd->name_count); + id_count = getfi_16 (finfo, erd->id_count); pp = &rd->entries; @@ -217,8 +223,8 @@ read_coff_res_dir (data, finfo) if ((const bfd_byte *) ere >= finfo->data_end) overrun (finfo, "named directory entry"); - name = get_32 (finfo, ere->name); - rva = get_32 (finfo, ere->rva); + name = getfi_32 (finfo, ere->name); + rva = getfi_32 (finfo, ere->rva); /* For some reason the high bit in NAME is set. */ name &=~ 0x80000000; @@ -228,15 +234,17 @@ read_coff_res_dir (data, finfo) ers = finfo->data + name; - re = (struct res_entry *) xmalloc (sizeof *re); + re = (struct res_entry *) res_alloc (sizeof *re); re->next = NULL; re->id.named = 1; - length = get_16 (finfo, ers); + length = getfi_16 (finfo, ers); re->id.u.n.length = length; - re->id.u.n.name = ((unsigned short *) - xmalloc (length * sizeof (unsigned short))); + re->id.u.n.name = (unichar *) res_alloc (length * sizeof (unichar)); for (j = 0; j < length; j++) - re->id.u.n.name[j] = get_16 (finfo, ers + j * 2 + 2); + re->id.u.n.name[j] = getfi_16 (finfo, ers + j * 2 + 2); + + if (level == 0) + type = &re->id; if ((rva & 0x80000000) != 0) { @@ -244,14 +252,15 @@ read_coff_res_dir (data, finfo) if (rva >= finfo->data_end - finfo->data) overrun (finfo, "named subdirectory"); re->subdir = 1; - re->u.dir = read_coff_res_dir (finfo->data + rva, finfo); + re->u.dir = read_coff_res_dir (finfo->data + rva, finfo, type, + level + 1); } else { if (rva >= finfo->data_end - finfo->data) overrun (finfo, "named resource"); re->subdir = 0; - re->u.res = read_coff_data_entry (finfo->data + rva, finfo); + re->u.res = read_coff_data_entry (finfo->data + rva, finfo, type); } *pp = re; @@ -266,28 +275,32 @@ read_coff_res_dir (data, finfo) if ((const bfd_byte *) ere >= finfo->data_end) overrun (finfo, "ID directory entry"); - name = get_32 (finfo, ere->name); - rva = get_32 (finfo, ere->rva); + name = getfi_32 (finfo, ere->name); + rva = getfi_32 (finfo, ere->rva); - re = (struct res_entry *) xmalloc (sizeof *re); + re = (struct res_entry *) res_alloc (sizeof *re); re->next = NULL; re->id.named = 0; re->id.u.id = name; + if (level == 0) + type = &re->id; + if ((rva & 0x80000000) != 0) { rva &=~ 0x80000000; if (rva >= finfo->data_end - finfo->data) overrun (finfo, "ID subdirectory"); re->subdir = 1; - re->u.dir = read_coff_res_dir (finfo->data + rva, finfo); + re->u.dir = read_coff_res_dir (finfo->data + rva, finfo, type, + level + 1); } else { if (rva >= finfo->data_end - finfo->data) overrun (finfo, "ID resource"); re->subdir = 0; - re->u.res = read_coff_data_entry (finfo->data + rva, finfo); + re->u.res = read_coff_data_entry (finfo->data + rva, finfo, type); } *pp = re; @@ -300,43 +313,459 @@ read_coff_res_dir (data, finfo) /* Read a resource data entry. */ static struct res_resource * -read_coff_data_entry (data, finfo) +read_coff_data_entry (data, finfo, type) const bfd_byte *data; const struct coff_file_info *finfo; + const struct res_id *type; { const struct extern_res_data *erd; struct res_resource *r; unsigned long size, rva; const bfd_byte *resdata; + if (type == NULL) + fatal ("resource type unknown"); + if (finfo->data_end - data < sizeof (struct extern_res_data)) overrun (finfo, "data entry"); erd = (const struct extern_res_data *) data; - r = (struct res_resource *) xmalloc (sizeof *r); - memset (&r->res_info, 0, sizeof (struct res_res_info)); - r->coff_info.codepage = get_32 (finfo, erd->codepage); - r->coff_info.reserved = get_32 (finfo, erd->reserved); - - size = get_32 (finfo, erd->size); - rva = get_32 (finfo, erd->rva); + size = getfi_32 (finfo, erd->size); + rva = getfi_32 (finfo, erd->rva); if (rva < finfo->secaddr || rva - finfo->secaddr >= finfo->data_end - finfo->data) overrun (finfo, "resource data"); resdata = finfo->data + (rva - finfo->secaddr); - r->type = RES_TYPE_USERDATA; - r->u.userdata = ((struct rcdata_data *) - xmalloc (sizeof (struct rcdata_data))); - r->u.userdata->first = ((struct rcdata_item *) - xmalloc (sizeof (struct rcdata_item))); - r->u.userdata->last = r->u.userdata->first; - r->u.userdata->first->next = NULL; - r->u.userdata->first->type = RCDATA_BUFFER; - r->u.userdata->first->u.buffer.length = size; - r->u.userdata->first->u.buffer.data = (unsigned char *) resdata; + if (size > finfo->data_end - resdata) + overrun (finfo, "resource data size"); + + r = bin_to_res (*type, resdata, size, finfo->big_endian); + + memset (&r->res_info, 0, sizeof (struct res_res_info)); + r->coff_info.codepage = getfi_32 (finfo, erd->codepage); + r->coff_info.reserved = getfi_32 (finfo, erd->reserved); return r; } + +/* This structure is used to build a list of bindata structures. */ + +struct bindata_build +{ + /* The data. */ + struct bindata *d; + /* The last structure we have added to the list. */ + struct bindata *last; + /* The size of the list as a whole. */ + unsigned long length; +}; + +/* This structure keeps track of information as we build the directory + tree. */ + +struct coff_write_info +{ + /* These fields are based on the BFD. */ + /* The BFD itself. */ + bfd *abfd; + /* Non-zero if the file is big endian. */ + int big_endian; + /* Pointer to section symbol used to build RVA relocs. */ + asymbol **sympp; + + /* These fields are computed initially, and then not changed. */ + /* Length of directory tables and entries. */ + unsigned long dirsize; + /* Length of directory entry strings. */ + unsigned long dirstrsize; + /* Length of resource data entries. */ + unsigned long dataentsize; + + /* These fields are updated as we add data. */ + /* Directory tables and entries. */ + struct bindata_build dirs; + /* Directory entry strings. */ + struct bindata_build dirstrs; + /* Resource data entries. */ + struct bindata_build dataents; + /* Actual resource data. */ + struct bindata_build resources; + /* Relocations. */ + arelent **relocs; + /* Number of relocations. */ + unsigned int reloc_count; +}; + +/* Macros to swap out values. */ + +#define putcwi_16(cwi, v, s) \ + ((cwi->big_endian) ? bfd_putb16 ((v), (s)) : bfd_putl16 ((v), (s))) +#define putcwi_32(cwi, v, s) \ + ((cwi->big_endian) ? bfd_putb32 ((v), (s)) : bfd_putl32 ((v), (s))) + +static void coff_bin_sizes + PARAMS ((const struct res_directory *, struct coff_write_info *)); +static unsigned char *coff_alloc PARAMS ((struct bindata_build *, size_t)); +static void coff_to_bin + PARAMS ((const struct res_directory *, struct coff_write_info *)); +static void coff_res_to_bin + PARAMS ((const struct res_resource *, struct coff_write_info *)); + +/* Write resources to a COFF file. RESOURCES should already be + sorted. + + Right now we always create a new file. Someday we should also + offer the ability to merge resources into an existing file. This + would require doing the basic work of objcopy, just modifying or + adding the .rsrc section. */ + + +void +write_coff_file (filename, target, resources) + const char *filename; + const char *target; + const struct res_directory *resources; +{ + bfd *abfd; + asection *sec; + struct coff_write_info cwi; + struct bindata *d; + unsigned long length, offset; + + abfd = bfd_openw (filename, target); + if (abfd == NULL) + bfd_fatal (filename); + + if (! bfd_set_format (abfd, bfd_object)) + bfd_fatal ("bfd_set_format"); + + /* FIXME: This is obviously i386 specific. */ + if (! bfd_set_arch_mach (abfd, bfd_arch_i386, 0)) + bfd_fatal ("bfd_set_arch_mach"); + + if (! bfd_set_file_flags (abfd, HAS_SYMS | HAS_RELOC)) + bfd_fatal ("bfd_set_file_flags"); + + sec = bfd_make_section (abfd, ".rsrc"); + if (sec == NULL) + bfd_fatal ("bfd_make_section"); + + if (! bfd_set_section_flags (abfd, sec, + (SEC_HAS_CONTENTS | SEC_ALLOC + | SEC_LOAD | SEC_DATA))) + bfd_fatal ("bfd_set_section_flags"); + + if (! bfd_set_symtab (abfd, sec->symbol_ptr_ptr, 1)) + bfd_fatal ("bfd_set_symtab"); + + /* Requiring this is probably a bug in BFD. */ + sec->output_section = sec; + + /* The order of data in the .rsrc section is + resource directory tables and entries + resource directory strings + resource data entries + actual resource data + + We build these different types of data in different lists. */ + + cwi.abfd = abfd; + cwi.big_endian = bfd_big_endian (abfd); + cwi.sympp = sec->symbol_ptr_ptr; + cwi.dirsize = 0; + cwi.dirstrsize = 0; + cwi.dataentsize = 0; + cwi.dirs.d = NULL; + cwi.dirs.last = NULL; + cwi.dirs.length = 0; + cwi.dirstrs.d = NULL; + cwi.dirstrs.last = NULL; + cwi.dirstrs.length = 0; + cwi.dataents.d = NULL; + cwi.dataents.last = NULL; + cwi.dataents.length = 0; + cwi.resources.d = NULL; + cwi.resources.last = NULL; + cwi.resources.length = 0; + cwi.relocs = NULL; + cwi.reloc_count = 0; + + /* Work out the sizes of the resource directory entries, so that we + know the various offsets we will need. */ + coff_bin_sizes (resources, &cwi); + + /* Force the directory strings to be 32 bit aligned. Every other + structure is 32 bit aligned anyhow. */ + cwi.dirstrsize = (cwi.dirstrsize + 3) &~ 3; + + /* Actually convert the resources to binary. */ + coff_to_bin (resources, &cwi); + + /* Add another 2 bytes to the directory strings if needed for + alignment. */ + if ((cwi.dirstrs.length & 3) != 0) + { + unsigned char *ex; + + ex = coff_alloc (&cwi.dirstrs, 2); + ex[0] = 0; + ex[1] = 0; + } + + /* Make sure that the data we built came out to the same size as we + calculated initially. */ + assert (cwi.dirs.length == cwi.dirsize); + assert (cwi.dirstrs.length == cwi.dirstrsize); + assert (cwi.dataents.length == cwi.dataentsize); + + length = (cwi.dirsize + + cwi.dirstrsize + + cwi.dataentsize + + cwi.resources.length); + + if (! bfd_set_section_size (abfd, sec, length)) + bfd_fatal ("bfd_set_section_size"); + + bfd_set_reloc (abfd, sec, cwi.relocs, cwi.reloc_count); + + /* We allocated the relocs array using malloc. */ + free (cwi.relocs); + + offset = 0; + for (d = cwi.dirs.d; d != NULL; d = d->next) + { + if (! bfd_set_section_contents (abfd, sec, d->data, offset, d->length)) + bfd_fatal ("bfd_set_section_contents"); + offset += d->length; + } + for (d = cwi.dirstrs.d; d != NULL; d = d->next) + { + if (! bfd_set_section_contents (abfd, sec, d->data, offset, d->length)) + bfd_fatal ("bfd_set_section_contents"); + offset += d->length; + } + for (d = cwi.dataents.d; d != NULL; d = d->next) + { + if (! bfd_set_section_contents (abfd, sec, d->data, offset, d->length)) + bfd_fatal ("bfd_set_section_contents"); + offset += d->length; + } + for (d = cwi.resources.d; d != NULL; d = d->next) + { + if (! bfd_set_section_contents (abfd, sec, d->data, offset, d->length)) + bfd_fatal ("bfd_set_section_contents"); + offset += d->length; + } + + assert (offset == length); + + if (! bfd_close (abfd)) + bfd_fatal ("bfd_close"); +} + +/* Work out the sizes of the various fixed size resource directory + entries. This updates fields in CWI. */ + +static void +coff_bin_sizes (resdir, cwi) + const struct res_directory *resdir; + struct coff_write_info *cwi; +{ + const struct res_entry *re; + + cwi->dirsize += sizeof (struct extern_res_directory); + + for (re = resdir->entries; re != NULL; re = re->next) + { + cwi->dirsize += sizeof (struct extern_res_entry); + + if (re->id.named) + cwi->dirstrsize += re->id.u.n.length * 2 + 2; + + if (re->subdir) + coff_bin_sizes (re->u.dir, cwi); + else + cwi->dataentsize += sizeof (struct extern_res_data); + } +} + +/* Allocate data for a particular list. */ + +static unsigned char * +coff_alloc (bb, size) + struct bindata_build *bb; + size_t size; +{ + struct bindata *d; + + d = (struct bindata *) reswr_alloc (sizeof *d); + + d->next = NULL; + d->data = (unsigned char *) reswr_alloc (size); + d->length = size; + + if (bb->d == NULL) + bb->d = d; + else + bb->last->next = d; + bb->last = d; + bb->length += size; + + return d->data; +} + +/* Convert the resource directory RESDIR to binary. */ + +static void +coff_to_bin (resdir, cwi) + const struct res_directory *resdir; + struct coff_write_info *cwi; +{ + struct extern_res_directory *erd; + int ci, cn; + const struct res_entry *e; + struct extern_res_entry *ere; + + /* Write out the directory table. */ + + erd = ((struct extern_res_directory *) + coff_alloc (&cwi->dirs, sizeof (*erd))); + + putcwi_32 (cwi, resdir->characteristics, erd->characteristics); + putcwi_32 (cwi, resdir->time, erd->time); + putcwi_16 (cwi, resdir->major, erd->major); + putcwi_16 (cwi, resdir->minor, erd->minor); + + ci = 0; + cn = 0; + for (e = resdir->entries; e != NULL; e = e->next) + { + if (e->id.named) + ++cn; + else + ++ci; + } + + putcwi_16 (cwi, cn, erd->name_count); + putcwi_16 (cwi, ci, erd->id_count); + + /* Write out the data entries. Note that we allocate space for all + the entries before writing them out. That permits a recursive + call to work correctly when writing out subdirectories. */ + + ere = ((struct extern_res_entry *) + coff_alloc (&cwi->dirs, (ci + cn) * sizeof (*ere))); + for (e = resdir->entries; e != NULL; e = e->next, ere++) + { + if (! e->id.named) + putcwi_32 (cwi, e->id.u.id, ere->name); + else + { + unsigned char *str; + int i; + + /* For some reason existing files seem to have the high bit + set on the address of the name, although that is not + documented. */ + putcwi_32 (cwi, + 0x80000000 | (cwi->dirsize + cwi->dirstrs.length), + ere->name); + + str = coff_alloc (&cwi->dirstrs, e->id.u.n.length * 2 + 2); + putcwi_16 (cwi, e->id.u.n.length, str); + for (i = 0; i < e->id.u.n.length; i++) + putcwi_16 (cwi, e->id.u.n.name[i], str + i * 2 + 2); + } + + if (e->subdir) + { + putcwi_32 (cwi, 0x80000000 | cwi->dirs.length, ere->rva); + coff_to_bin (e->u.dir, cwi); + } + else + { + putcwi_32 (cwi, + cwi->dirsize + cwi->dirstrsize + cwi->dataents.length, + ere->rva); + + coff_res_to_bin (e->u.res, cwi); + } + } +} + +/* Convert the resource RES to binary. */ + +static void +coff_res_to_bin (res, cwi) + const struct res_resource *res; + struct coff_write_info *cwi; +{ + arelent *r; + struct extern_res_data *erd; + struct bindata *d; + unsigned long length; + + /* For some reason, although every other address is a section + offset, the address of the resource data itself is an RVA. That + means that we need to generate a relocation for it. We allocate + the relocs array using malloc so that we can use realloc. FIXME: + This relocation handling is correct for the i386, but probably + not for any other target. */ + + r = (arelent *) reswr_alloc (sizeof (arelent)); + r->sym_ptr_ptr = cwi->sympp; + r->address = cwi->dirsize + cwi->dirstrsize + cwi->dataents.length; + r->addend = 0; + r->howto = bfd_reloc_type_lookup (cwi->abfd, BFD_RELOC_RVA); + if (r->howto == NULL) + bfd_fatal ("can't get BFD_RELOC_RVA relocation type"); + + cwi->relocs = xrealloc (cwi->relocs, + (cwi->reloc_count + 2) * sizeof (arelent *)); + cwi->relocs[cwi->reloc_count] = r; + cwi->relocs[cwi->reloc_count + 1] = NULL; + ++cwi->reloc_count; + + erd = (struct extern_res_data *) coff_alloc (&cwi->dataents, sizeof (*erd)); + + putcwi_32 (cwi, + (cwi->dirsize + + cwi->dirstrsize + + cwi->dataentsize + + cwi->resources.length), + erd->rva); + putcwi_32 (cwi, res->coff_info.codepage, erd->codepage); + putcwi_32 (cwi, res->coff_info.reserved, erd->reserved); + + d = res_to_bin (res, cwi->big_endian); + + if (cwi->resources.d == NULL) + cwi->resources.d = d; + else + cwi->resources.last->next = d; + + length = 0; + for (; d->next != NULL; d = d->next) + length += d->length; + length += d->length; + cwi->resources.last = d; + cwi->resources.length += length; + + putcwi_32 (cwi, length, erd->size); + + /* Force the next resource to have 32 bit alignment. */ + + if ((length & 3) != 0) + { + int add; + unsigned char *ex; + + add = 4 - (length & 3); + + ex = coff_alloc (&cwi->resources, add); + memset (ex, 0, add); + } +} |