1,5 → 1,5 |
/* Support for the generic parts of PE/PEI; the common executable parts. |
Copyright 1995-2013 Free Software Foundation, Inc. |
Copyright (C) 1995-2015 Free Software Foundation, Inc. |
Written by Cygnus Solutions. |
|
This file is part of BFD, the Binary File Descriptor library. |
80,6 → 80,7 |
#include "coff/pe.h" |
#include "libcoff.h" |
#include "libpei.h" |
#include "safe-ctype.h" |
|
#if defined COFF_WITH_pep || defined COFF_WITH_pex64 |
# undef AOUTSZ |
87,6 → 88,10 |
# define PEAOUTHDR PEPAOUTHDR |
#endif |
|
#define HighBitSet(val) ((val) & 0x80000000) |
#define SetHighBit(val) ((val) | 0x80000000) |
#define WithoutHighBit(val) ((val) & 0x7fffffff) |
|
/* FIXME: This file has various tests of POWERPC_LE_PE. Those tests |
worked when the code was in peicode.h, but no longer work now that |
the code is in peigen.c. PowerPC NT is said to be dead. If |
141,8 → 146,13 |
|
name = _bfd_coff_internal_syment_name (abfd, in, namebuf); |
if (name == NULL) |
/* FIXME: Return error. */ |
abort (); |
{ |
_bfd_error_handler (_("%B: unable to find name for empty section"), |
abfd); |
bfd_set_error (bfd_error_invalid_target); |
return; |
} |
|
sec = bfd_get_section_by_name (abfd, name); |
if (sec != NULL) |
in->n_scnum = sec->target_index; |
162,15 → 172,22 |
{ |
name = (const char *) bfd_alloc (abfd, strlen (namebuf) + 1); |
if (name == NULL) |
/* FIXME: Return error. */ |
abort (); |
{ |
_bfd_error_handler (_("%B: out of memory creating name for empty section"), |
abfd); |
return; |
} |
strcpy ((char *) name, namebuf); |
} |
|
flags = SEC_HAS_CONTENTS | SEC_ALLOC | SEC_DATA | SEC_LOAD; |
sec = bfd_make_section_anyway_with_flags (abfd, name, flags); |
if (sec == NULL) |
/* FIXME: Return error. */ |
abort (); |
{ |
_bfd_error_handler (_("%B: unable to create fake empty section"), |
abfd); |
return; |
} |
|
sec->vma = 0; |
sec->lma = 0; |
199,6 → 216,14 |
#endif |
} |
|
static bfd_boolean |
abs_finder (bfd * abfd ATTRIBUTE_UNUSED, asection * sec, void * data) |
{ |
bfd_vma abs_val = * (bfd_vma *) data; |
|
return (sec->vma <= abs_val) && ((sec->vma + (1ULL << 32)) > abs_val); |
} |
|
unsigned int |
_bfd_pei_swap_sym_out (bfd * abfd, void * inp, void * extp) |
{ |
213,6 → 238,34 |
else |
memcpy (ext->e.e_name, in->_n._n_name, SYMNMLEN); |
|
/* The PE32 and PE32+ formats only use 4 bytes to hold the value of a |
symbol. This is a problem on 64-bit targets where we can generate |
absolute symbols with values >= 1^32. We try to work around this |
problem by finding a section whose base address is sufficient to |
reduce the absolute value to < 1^32, and then transforming the |
symbol into a section relative symbol. This of course is a hack. */ |
if (sizeof (in->n_value) > 4 |
/* The strange computation of the shift amount is here in order to |
avoid a compile time warning about the comparison always being |
false. It does not matter if this test fails to work as expected |
as the worst that can happen is that some absolute symbols are |
needlessly converted into section relative symbols. */ |
&& in->n_value > ((1ULL << (sizeof (in->n_value) > 4 ? 32 : 31)) - 1) |
&& in->n_scnum == -1) |
{ |
asection * sec; |
|
sec = bfd_sections_find_if (abfd, abs_finder, & in->n_value); |
if (sec) |
{ |
in->n_value -= sec->vma; |
in->n_scnum = sec->target_index; |
} |
/* else: FIXME: The value is outside the range of any section. This |
happens for __image_base__ and __ImageBase and maybe some other |
symbols as well. We should find a way to handle these values. */ |
} |
|
H_PUT_32 (abfd, in->n_value, ext->e_value); |
H_PUT_16 (abfd, in->n_scnum, ext->e_scnum); |
|
239,6 → 292,9 |
AUXENT *ext = (AUXENT *) ext1; |
union internal_auxent *in = (union internal_auxent *) in1; |
|
/* PR 17521: Make sure that all fields in the aux structure |
are initialised. */ |
memset (in, 0, sizeof * in); |
switch (in_class) |
{ |
case C_FILE: |
414,6 → 470,7 |
aouthdr_int->entry = GET_AOUTHDR_ENTRY (abfd, aouthdr_ext->entry); |
aouthdr_int->text_start = |
GET_AOUTHDR_TEXT_START (abfd, aouthdr_ext->text_start); |
|
#if !defined(COFF_WITH_pep) && !defined(COFF_WITH_pex64) |
/* PE32+ does not have data_start member! */ |
aouthdr_int->data_start = |
460,6 → 517,19 |
{ |
int idx; |
|
/* PR 17512: Corrupt PE binaries can cause seg-faults. */ |
if (a->NumberOfRvaAndSizes > IMAGE_NUMBEROF_DIRECTORY_ENTRIES) |
{ |
(*_bfd_error_handler) |
(_("%B: aout header specifies an invalid number of data-directory entries: %d"), |
abfd, a->NumberOfRvaAndSizes); |
bfd_set_error (bfd_error_bad_value); |
|
/* Paranoia: If the number is corrupt, then assume that the |
actual entries themselves might be corrupt as well. */ |
a->NumberOfRvaAndSizes = 0; |
} |
|
for (idx = 0; idx < a->NumberOfRvaAndSizes; idx++) |
{ |
/* If data directory is empty, rva also should be 0. */ |
474,7 → 544,14 |
else |
a->DataDirectory[idx].VirtualAddress = 0; |
} |
|
while (idx < IMAGE_NUMBEROF_DIRECTORY_ENTRIES) |
{ |
a->DataDirectory[idx].Size = 0; |
a->DataDirectory[idx].VirtualAddress = 0; |
idx ++; |
} |
} |
|
if (aouthdr_int->entry) |
{ |
717,7 → 794,7 |
{ |
int idx; |
|
for (idx = 0; idx < 16; idx++) |
for (idx = 0; idx < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; idx++) |
{ |
H_PUT_32 (abfd, extra->DataDirectory[idx].VirtualAddress, |
aouthdr_out->DataDirectory[idx][0]); |
977,8 → 1054,8 |
} |
|
if (coff_data (abfd)->link_info |
&& ! coff_data (abfd)->link_info->relocatable |
&& ! coff_data (abfd)->link_info->shared |
&& ! bfd_link_relocatable (coff_data (abfd)->link_info) |
&& ! bfd_link_pic (coff_data (abfd)->link_info) |
&& strcmp (scnhdr_int->s_name, ".text") == 0) |
{ |
/* By inference from looking at MS output, the 32 bit field |
1023,6 → 1100,121 |
return ret; |
} |
|
void |
_bfd_pei_swap_debugdir_in (bfd * abfd, void * ext1, void * in1) |
{ |
struct external_IMAGE_DEBUG_DIRECTORY *ext = (struct external_IMAGE_DEBUG_DIRECTORY *) ext1; |
struct internal_IMAGE_DEBUG_DIRECTORY *in = (struct internal_IMAGE_DEBUG_DIRECTORY *) in1; |
|
in->Characteristics = H_GET_32(abfd, ext->Characteristics); |
in->TimeDateStamp = H_GET_32(abfd, ext->TimeDateStamp); |
in->MajorVersion = H_GET_16(abfd, ext->MajorVersion); |
in->MinorVersion = H_GET_16(abfd, ext->MinorVersion); |
in->Type = H_GET_32(abfd, ext->Type); |
in->SizeOfData = H_GET_32(abfd, ext->SizeOfData); |
in->AddressOfRawData = H_GET_32(abfd, ext->AddressOfRawData); |
in->PointerToRawData = H_GET_32(abfd, ext->PointerToRawData); |
} |
|
unsigned int |
_bfd_pei_swap_debugdir_out (bfd * abfd, void * inp, void * extp) |
{ |
struct external_IMAGE_DEBUG_DIRECTORY *ext = (struct external_IMAGE_DEBUG_DIRECTORY *) extp; |
struct internal_IMAGE_DEBUG_DIRECTORY *in = (struct internal_IMAGE_DEBUG_DIRECTORY *) inp; |
|
H_PUT_32(abfd, in->Characteristics, ext->Characteristics); |
H_PUT_32(abfd, in->TimeDateStamp, ext->TimeDateStamp); |
H_PUT_16(abfd, in->MajorVersion, ext->MajorVersion); |
H_PUT_16(abfd, in->MinorVersion, ext->MinorVersion); |
H_PUT_32(abfd, in->Type, ext->Type); |
H_PUT_32(abfd, in->SizeOfData, ext->SizeOfData); |
H_PUT_32(abfd, in->AddressOfRawData, ext->AddressOfRawData); |
H_PUT_32(abfd, in->PointerToRawData, ext->PointerToRawData); |
|
return sizeof (struct external_IMAGE_DEBUG_DIRECTORY); |
} |
|
CODEVIEW_INFO * |
_bfd_pei_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length, CODEVIEW_INFO *cvinfo) |
{ |
char buffer[256+1]; |
|
if (bfd_seek (abfd, where, SEEK_SET) != 0) |
return NULL; |
|
if (bfd_bread (buffer, 256, abfd) < 4) |
return NULL; |
|
/* Ensure null termination of filename. */ |
buffer[256] = '\0'; |
|
cvinfo->CVSignature = H_GET_32 (abfd, buffer); |
cvinfo->Age = 0; |
|
if ((cvinfo->CVSignature == CVINFO_PDB70_CVSIGNATURE) |
&& (length > sizeof (CV_INFO_PDB70))) |
{ |
CV_INFO_PDB70 *cvinfo70 = (CV_INFO_PDB70 *)(buffer); |
|
cvinfo->Age = H_GET_32(abfd, cvinfo70->Age); |
|
/* A GUID consists of 4,2,2 byte values in little-endian order, followed |
by 8 single bytes. Byte swap them so we can conveniently treat the GUID |
as 16 bytes in big-endian order. */ |
bfd_putb32 (bfd_getl32 (cvinfo70->Signature), cvinfo->Signature); |
bfd_putb16 (bfd_getl16 (&(cvinfo70->Signature[4])), &(cvinfo->Signature[4])); |
bfd_putb16 (bfd_getl16 (&(cvinfo70->Signature[6])), &(cvinfo->Signature[6])); |
memcpy (&(cvinfo->Signature[8]), &(cvinfo70->Signature[8]), 8); |
|
cvinfo->SignatureLength = CV_INFO_SIGNATURE_LENGTH; |
// cvinfo->PdbFileName = cvinfo70->PdbFileName; |
|
return cvinfo; |
} |
else if ((cvinfo->CVSignature == CVINFO_PDB20_CVSIGNATURE) |
&& (length > sizeof (CV_INFO_PDB20))) |
{ |
CV_INFO_PDB20 *cvinfo20 = (CV_INFO_PDB20 *)(buffer); |
cvinfo->Age = H_GET_32(abfd, cvinfo20->Age); |
memcpy (cvinfo->Signature, cvinfo20->Signature, 4); |
cvinfo->SignatureLength = 4; |
// cvinfo->PdbFileName = cvinfo20->PdbFileName; |
|
return cvinfo; |
} |
|
return NULL; |
} |
|
unsigned int |
_bfd_pei_write_codeview_record (bfd * abfd, file_ptr where, CODEVIEW_INFO *cvinfo) |
{ |
unsigned int size = sizeof (CV_INFO_PDB70) + 1; |
CV_INFO_PDB70 *cvinfo70; |
char buffer[size]; |
|
if (bfd_seek (abfd, where, SEEK_SET) != 0) |
return 0; |
|
cvinfo70 = (CV_INFO_PDB70 *) buffer; |
H_PUT_32 (abfd, CVINFO_PDB70_CVSIGNATURE, cvinfo70->CvSignature); |
|
/* Byte swap the GUID from 16 bytes in big-endian order to 4,2,2 byte values |
in little-endian order, followed by 8 single bytes. */ |
bfd_putl32 (bfd_getb32 (cvinfo->Signature), cvinfo70->Signature); |
bfd_putl16 (bfd_getb16 (&(cvinfo->Signature[4])), &(cvinfo70->Signature[4])); |
bfd_putl16 (bfd_getb16 (&(cvinfo->Signature[6])), &(cvinfo70->Signature[6])); |
memcpy (&(cvinfo70->Signature[8]), &(cvinfo->Signature[8]), 8); |
|
H_PUT_32 (abfd, cvinfo->Age, cvinfo70->Age); |
cvinfo70->PdbFileName[0] = '\0'; |
|
if (bfd_bwrite (buffer, size, abfd) != size) |
return 0; |
|
return size; |
} |
|
static char * dir_names[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] = |
{ |
N_("Export Directory [.edata (or where ever we found it)]"), |
1103,7 → 1295,14 |
_("\nThere is an import table, but the section containing it could not be found\n")); |
return TRUE; |
} |
else if (!(section->flags & SEC_HAS_CONTENTS)) |
{ |
fprintf (file, |
_("\nThere is an import table in %s, but that section has no contents\n"), |
section->name); |
return TRUE; |
} |
} |
|
fprintf (file, _("\nThere is an import table in %s at 0x%lx\n"), |
section->name, (unsigned long) addr); |
1214,7 → 1413,9 |
break; |
|
dll = (char *) data + dll_name - adj; |
fprintf (file, _("\n\tDLL Name: %s\n"), dll); |
/* PR 17512 file: 078-12277-0.004. */ |
bfd_size_type maxlen = (char *)(data + datasize) - dll - 1; |
fprintf (file, _("\n\tDLL Name: %.*s\n"), (int) maxlen, dll); |
|
if (hint_addr != 0) |
{ |
1279,6 → 1480,7 |
#ifdef COFF_WITH_pex64 |
for (j = 0; idx + j + 8 <= datasize; j += 8) |
{ |
bfd_size_type amt; |
unsigned long member = bfd_get_32 (abfd, data + idx + j); |
unsigned long member_high = bfd_get_32 (abfd, data + idx + j + 4); |
|
1285,17 → 1487,24 |
if (!member && !member_high) |
break; |
|
if (member_high & 0x80000000) |
amt = member - adj; |
|
if (HighBitSet (member_high)) |
fprintf (file, "\t%lx%08lx\t %4lx%08lx <none>", |
member_high,member, member_high & 0x7fffffff, member); |
member_high, member, |
WithoutHighBit (member_high), member); |
/* PR binutils/17512: Handle corrupt PE data. */ |
else if (amt + 2 >= datasize) |
fprintf (file, _("\t<corrupt: 0x%04lx>"), member); |
else |
{ |
int ordinal; |
char *member_name; |
|
ordinal = bfd_get_16 (abfd, data + member - adj); |
member_name = (char *) data + member - adj + 2; |
fprintf (file, "\t%04lx\t %4d %s",member, ordinal, member_name); |
ordinal = bfd_get_16 (abfd, data + amt); |
member_name = (char *) data + amt + 2; |
fprintf (file, "\t%04lx\t %4d %.*s",member, ordinal, |
(int) (datasize - (amt + 2)), member_name); |
} |
|
/* If the time stamp is not zero, the import address |
1311,6 → 1520,7 |
#else |
for (j = 0; idx + j + 4 <= datasize; j += 4) |
{ |
bfd_size_type amt; |
unsigned long member = bfd_get_32 (abfd, data + idx + j); |
|
/* Print single IMAGE_IMPORT_BY_NAME vector. */ |
1317,18 → 1527,23 |
if (member == 0) |
break; |
|
if (member & 0x80000000) |
amt = member - adj; |
if (HighBitSet (member)) |
fprintf (file, "\t%04lx\t %4lu <none>", |
member, member & 0x7fffffff); |
member, WithoutHighBit (member)); |
/* PR binutils/17512: Handle corrupt PE data. */ |
else if (amt + 2 >= datasize) |
fprintf (file, _("\t<corrupt: 0x%04lx>"), member); |
else |
{ |
int ordinal; |
char *member_name; |
|
ordinal = bfd_get_16 (abfd, data + member - adj); |
member_name = (char *) data + member - adj + 2; |
fprintf (file, "\t%04lx\t %4d %s", |
member, ordinal, member_name); |
ordinal = bfd_get_16 (abfd, data + amt); |
member_name = (char *) data + amt + 2; |
fprintf (file, "\t%04lx\t %4d %.*s", |
member, ordinal, |
(int) (datasize - (amt + 2)), member_name); |
} |
|
/* If the time stamp is not zero, the import address |
1364,7 → 1579,7 |
bfd_size_type datasize = 0; |
bfd_size_type dataoff; |
bfd_size_type i; |
bfd_signed_vma adj; |
bfd_vma adj; |
struct EDT_type |
{ |
long export_flags; /* Reserved - should be zero. */ |
1414,6 → 1629,13 |
_("\nThere is an export table, but the section containing it could not be found\n")); |
return TRUE; |
} |
else if (!(section->flags & SEC_HAS_CONTENTS)) |
{ |
fprintf (file, |
_("\nThere is an export table in %s, but that section has no contents\n"), |
section->name); |
return TRUE; |
} |
|
dataoff = addr - section->vma; |
datasize = extra->DataDirectory[PE_EXPORT_TABLE].Size; |
1426,6 → 1648,15 |
} |
} |
|
/* PR 17512: Handle corrupt PE binaries. */ |
if (datasize < 36) |
{ |
fprintf (file, |
_("\nThere is an export table in %s, but it is too small (%d)\n"), |
section->name, (int) datasize); |
return TRUE; |
} |
|
fprintf (file, _("\nThere is an export table in %s at 0x%lx\n"), |
section->name, (unsigned long) addr); |
|
1469,9 → 1700,14 |
fprintf (file, |
_("Name \t\t\t\t")); |
bfd_fprintf_vma (abfd, file, edt.name); |
fprintf (file, |
" %s\n", data + edt.name - adj); |
|
if ((edt.name >= adj) && (edt.name < adj + datasize)) |
fprintf (file, " %.*s\n", |
(int) (datasize - (edt.name - adj)), |
data + edt.name - adj); |
else |
fprintf (file, "(outside .edata section)\n"); |
|
fprintf (file, |
_("Ordinal Base \t\t\t%ld\n"), edt.base); |
|
1516,7 → 1752,16 |
_("\nExport Address Table -- Ordinal Base %ld\n"), |
edt.base); |
|
for (i = 0; i < edt.num_functions; ++i) |
/* PR 17512: Handle corrupt PE binaries. */ |
if (edt.eat_addr + (edt.num_functions * 4) - adj >= datasize |
/* PR 17512: file: 092b1829 */ |
|| (edt.num_functions * 4) < edt.num_functions |
/* PR 17512 file: 140-165018-0.004. */ |
|| data + edt.eat_addr - adj < data) |
fprintf (file, _("\tInvalid Export Address Table rva (0x%lx) or entry count (0x%lx)\n"), |
(long) edt.eat_addr, |
(long) edt.num_functions); |
else for (i = 0; i < edt.num_functions; ++i) |
{ |
bfd_vma eat_member = bfd_get_32 (abfd, |
data + edt.eat_addr + (i * 4) - adj); |
1528,11 → 1773,12 |
/* This rva is to a name (forwarding function) in our section. */ |
/* Should locate a function descriptor. */ |
fprintf (file, |
"\t[%4ld] +base[%4ld] %04lx %s -- %s\n", |
"\t[%4ld] +base[%4ld] %04lx %s -- %.*s\n", |
(long) i, |
(long) (i + edt.base), |
(unsigned long) eat_member, |
_("Forwarder RVA"), |
(int)(datasize - (eat_member - adj)), |
data + eat_member - adj); |
} |
else |
1552,22 → 1798,41 |
fprintf (file, |
_("\n[Ordinal/Name Pointer] Table\n")); |
|
for (i = 0; i < edt.num_names; ++i) |
/* PR 17512: Handle corrupt PE binaries. */ |
if (edt.npt_addr + (edt.num_names * 4) - adj >= datasize |
/* PR 17512: file: bb68816e. */ |
|| edt.num_names * 4 < edt.num_names |
|| (data + edt.npt_addr - adj) < data) |
fprintf (file, _("\tInvalid Name Pointer Table rva (0x%lx) or entry count (0x%lx)\n"), |
(long) edt.npt_addr, |
(long) edt.num_names); |
/* PR 17512: file: 140-147171-0.004. */ |
else if (edt.ot_addr + (edt.num_names * 2) - adj >= datasize |
|| data + edt.ot_addr - adj < data) |
fprintf (file, _("\tInvalid Ordinal Table rva (0x%lx) or entry count (0x%lx)\n"), |
(long) edt.ot_addr, |
(long) edt.num_names); |
else for (i = 0; i < edt.num_names; ++i) |
{ |
bfd_vma name_ptr = bfd_get_32 (abfd, |
data + |
edt.npt_addr |
+ (i*4) - adj); |
bfd_vma name_ptr; |
bfd_vma ord; |
|
ord = bfd_get_16 (abfd, data + edt.ot_addr + (i * 2) - adj); |
name_ptr = bfd_get_32 (abfd, data + edt.npt_addr + (i * 4) - adj); |
|
if ((name_ptr - adj) >= datasize) |
{ |
fprintf (file, _("\t[%4ld] <corrupt offset: %lx>\n"), |
(long) ord, (long) name_ptr); |
} |
else |
{ |
char *name = (char *) data + name_ptr - adj; |
|
bfd_vma ord = bfd_get_16 (abfd, |
data + |
edt.ot_addr |
+ (i*2) - adj); |
fprintf (file, |
"\t[%4ld] %s\n", (long) ord, name); |
fprintf (file, "\t[%4ld] %.*s\n", (long) ord, |
(int)((char *)(data + datasize) - name), name); |
} |
} |
|
free (data); |
|
1628,6 → 1893,14 |
if (datasize == 0) |
return TRUE; |
|
/* PR 17512: file: 002-193900-0.004. */ |
if (datasize < stop) |
{ |
fprintf (file, _("Virtual size of .pdata section (%ld) larger than real size (%ld)\n"), |
(long) stop, (long) datasize); |
return FALSE; |
} |
|
if (! bfd_malloc_and_get_section (abfd, section, &data)) |
{ |
if (data != NULL) |
1737,7 → 2010,11 |
if (storage < 0) |
return NULL; |
if (storage) |
{ |
sy = (asymbol **) bfd_malloc (storage); |
if (sy == NULL) |
return NULL; |
} |
|
psc->symcount = bfd_canonicalize_symtab (abfd, sy); |
if (psc->symcount < 0) |
1915,15 → 2192,11 |
FILE *file = (FILE *) vfile; |
bfd_byte *data = 0; |
asection *section = bfd_get_section_by_name (abfd, ".reloc"); |
bfd_size_type i; |
bfd_size_type start, stop; |
bfd_byte *p, *end; |
|
if (section == NULL) |
if (section == NULL || section->size == 0 || !(section->flags & SEC_HAS_CONTENTS)) |
return TRUE; |
|
if (section->size == 0) |
return TRUE; |
|
fprintf (file, |
_("\n\nPE File Base Relocations (interpreted .reloc section contents)\n")); |
|
1934,20 → 2207,20 |
return FALSE; |
} |
|
start = 0; |
|
stop = section->size; |
|
for (i = start; i < stop;) |
p = data; |
end = data + section->size; |
while (p + 8 <= end) |
{ |
int j; |
bfd_vma virtual_address; |
long number, size; |
unsigned long number, size; |
bfd_byte *chunk_end; |
|
/* The .reloc section is a sequence of blocks, with a header consisting |
of two 32 bit quantities, followed by a number of 16 bit entries. */ |
virtual_address = bfd_get_32 (abfd, data+i); |
size = bfd_get_32 (abfd, data+i+4); |
virtual_address = bfd_get_32 (abfd, p); |
size = bfd_get_32 (abfd, p + 4); |
p += 8; |
number = (size - 8) / 2; |
|
if (size == 0) |
1955,11 → 2228,15 |
|
fprintf (file, |
_("\nVirtual Address: %08lx Chunk size %ld (0x%lx) Number of fixups %ld\n"), |
(unsigned long) virtual_address, size, (unsigned long) size, number); |
(unsigned long) virtual_address, size, size, number); |
|
for (j = 0; j < number; ++j) |
chunk_end = p + size; |
if (chunk_end > end) |
chunk_end = end; |
j = 0; |
while (p + 2 <= chunk_end) |
{ |
unsigned short e = bfd_get_16 (abfd, data + i + 8 + j * 2); |
unsigned short e = bfd_get_16 (abfd, p); |
unsigned int t = (e & 0xF000) >> 12; |
int off = e & 0x0FFF; |
|
1970,27 → 2247,459 |
_("\treloc %4d offset %4x [%4lx] %s"), |
j, off, (unsigned long) (off + virtual_address), tbl[t]); |
|
p += 2; |
j++; |
|
/* HIGHADJ takes an argument, - the next record *is* the |
low 16 bits of addend. */ |
if (t == IMAGE_REL_BASED_HIGHADJ) |
if (t == IMAGE_REL_BASED_HIGHADJ && p + 2 <= chunk_end) |
{ |
fprintf (file, " (%4x)", |
((unsigned int) |
bfd_get_16 (abfd, data + i + 8 + j * 2 + 2))); |
fprintf (file, " (%4x)", (unsigned int) bfd_get_16 (abfd, p)); |
p += 2; |
j++; |
} |
|
fprintf (file, "\n"); |
} |
} |
|
i += size; |
free (data); |
|
return TRUE; |
} |
|
/* A data structure describing the regions of a .rsrc section. |
Some fields are filled in as the section is parsed. */ |
|
typedef struct rsrc_regions |
{ |
bfd_byte * section_start; |
bfd_byte * section_end; |
bfd_byte * strings_start; |
bfd_byte * resource_start; |
} rsrc_regions; |
|
static bfd_byte * |
rsrc_print_resource_directory (FILE * , bfd *, unsigned int, bfd_byte *, |
rsrc_regions *, bfd_vma); |
|
/* Print the resource entry at DATA, with the text indented by INDENT. |
Recusively calls rsrc_print_resource_directory to print the contents |
of directory entries. |
Returns the address of the end of the data associated with the entry |
or section_end + 1 upon failure. */ |
|
static bfd_byte * |
rsrc_print_resource_entries (FILE * file, |
bfd * abfd, |
unsigned int indent, |
bfd_boolean is_name, |
bfd_byte * data, |
rsrc_regions * regions, |
bfd_vma rva_bias) |
{ |
unsigned long entry, addr, size; |
bfd_byte * leaf; |
|
if (data + 8 >= regions->section_end) |
return regions->section_end + 1; |
|
fprintf (file, _("%03x %*.s Entry: "), (int)(data - regions->section_start), indent, " "); |
|
entry = (unsigned long) bfd_get_32 (abfd, data); |
if (is_name) |
{ |
bfd_byte * name; |
|
/* Note - the documentation says that this field is an RVA value |
but windres appears to produce a section relative offset with |
the top bit set. Support both styles for now. */ |
if (HighBitSet (entry)) |
name = regions->section_start + WithoutHighBit (entry); |
else |
name = regions->section_start + entry - rva_bias; |
|
if (name + 2 < regions->section_end && name > regions->section_start) |
{ |
unsigned int len; |
|
if (regions->strings_start == NULL) |
regions->strings_start = name; |
|
len = bfd_get_16 (abfd, name); |
|
fprintf (file, _("name: [val: %08lx len %d]: "), entry, len); |
|
if (name + 2 + len * 2 < regions->section_end) |
{ |
/* This strange loop is to cope with multibyte characters. */ |
while (len --) |
{ |
char c; |
|
name += 2; |
c = * name; |
/* Avoid printing control characters. */ |
if (c > 0 && c < 32) |
fprintf (file, "^%c", c + 64); |
else |
fprintf (file, "%.1s", name); |
} |
} |
else |
{ |
fprintf (file, _("<corrupt string length: %#x>\n"), len); |
/* PR binutils/17512: Do not try to continue decoding a |
corrupted resource section. It is likely to end up with |
reams of extraneous output. FIXME: We could probably |
continue if we disable the printing of strings... */ |
return regions->section_end + 1; |
} |
} |
else |
{ |
fprintf (file, _("<corrupt string offset: %#lx>\n"), entry); |
return regions->section_end + 1; |
} |
} |
else |
fprintf (file, _("ID: %#08lx"), entry); |
|
entry = (long) bfd_get_32 (abfd, data + 4); |
fprintf (file, _(", Value: %#08lx\n"), entry); |
|
if (HighBitSet (entry)) |
{ |
data = regions->section_start + WithoutHighBit (entry); |
if (data <= regions->section_start || data > regions->section_end) |
return regions->section_end + 1; |
|
/* FIXME: PR binutils/17512: A corrupt file could contain a loop |
in the resource table. We need some way to detect this. */ |
return rsrc_print_resource_directory (file, abfd, indent + 1, data, |
regions, rva_bias); |
} |
|
leaf = regions->section_start + entry; |
|
if (leaf + 16 >= regions->section_end |
/* PR 17512: file: 055dff7e. */ |
|| leaf < regions->section_start) |
return regions->section_end + 1; |
|
fprintf (file, _("%03x %*.s Leaf: Addr: %#08lx, Size: %#08lx, Codepage: %d\n"), |
(int) (entry), indent, " ", |
addr = (long) bfd_get_32 (abfd, leaf), |
size = (long) bfd_get_32 (abfd, leaf + 4), |
(int) bfd_get_32 (abfd, leaf + 8)); |
|
/* Check that the reserved entry is 0. */ |
if (bfd_get_32 (abfd, leaf + 12) != 0 |
/* And that the data address/size is valid too. */ |
|| (regions->section_start + (addr - rva_bias) + size > regions->section_end)) |
return regions->section_end + 1; |
|
if (regions->resource_start == NULL) |
regions->resource_start = regions->section_start + (addr - rva_bias); |
|
return regions->section_start + (addr - rva_bias) + size; |
} |
|
#define max(a,b) ((a) > (b) ? (a) : (b)) |
#define min(a,b) ((a) < (b) ? (a) : (b)) |
|
static bfd_byte * |
rsrc_print_resource_directory (FILE * file, |
bfd * abfd, |
unsigned int indent, |
bfd_byte * data, |
rsrc_regions * regions, |
bfd_vma rva_bias) |
{ |
unsigned int num_names, num_ids; |
bfd_byte * highest_data = data; |
|
if (data + 16 >= regions->section_end) |
return regions->section_end + 1; |
|
fprintf (file, "%03x %*.s ", (int)(data - regions->section_start), indent, " "); |
switch (indent) |
{ |
case 0: fprintf (file, "Type"); break; |
case 2: fprintf (file, "Name"); break; |
case 4: fprintf (file, "Language"); break; |
default: |
fprintf (file, _("<unknown directory type: %d>\n"), indent); |
/* FIXME: For now we end the printing here. If in the |
future more directory types are added to the RSRC spec |
then we will need to change this. */ |
return regions->section_end + 1; |
} |
|
fprintf (file, _(" Table: Char: %d, Time: %08lx, Ver: %d/%d, Num Names: %d, IDs: %d\n"), |
(int) bfd_get_32 (abfd, data), |
(long) bfd_get_32 (abfd, data + 4), |
(int) bfd_get_16 (abfd, data + 8), |
(int) bfd_get_16 (abfd, data + 10), |
num_names = (int) bfd_get_16 (abfd, data + 12), |
num_ids = (int) bfd_get_16 (abfd, data + 14)); |
data += 16; |
|
while (num_names --) |
{ |
bfd_byte * entry_end; |
|
entry_end = rsrc_print_resource_entries (file, abfd, indent + 1, TRUE, |
data, regions, rva_bias); |
data += 8; |
highest_data = max (highest_data, entry_end); |
if (entry_end >= regions->section_end) |
return entry_end; |
} |
|
while (num_ids --) |
{ |
bfd_byte * entry_end; |
|
entry_end = rsrc_print_resource_entries (file, abfd, indent + 1, FALSE, |
data, regions, rva_bias); |
data += 8; |
highest_data = max (highest_data, entry_end); |
if (entry_end >= regions->section_end) |
return entry_end; |
} |
|
return max (highest_data, data); |
} |
|
/* Display the contents of a .rsrc section. We do not try to |
reproduce the resources, windres does that. Instead we dump |
the tables in a human readable format. */ |
|
static bfd_boolean |
rsrc_print_section (bfd * abfd, void * vfile) |
{ |
bfd_vma rva_bias; |
pe_data_type * pe; |
FILE * file = (FILE *) vfile; |
bfd_size_type datasize; |
asection * section; |
bfd_byte * data; |
rsrc_regions regions; |
|
pe = pe_data (abfd); |
if (pe == NULL) |
return TRUE; |
|
section = bfd_get_section_by_name (abfd, ".rsrc"); |
if (section == NULL) |
return TRUE; |
if (!(section->flags & SEC_HAS_CONTENTS)) |
return TRUE; |
|
datasize = section->size; |
if (datasize == 0) |
return TRUE; |
|
rva_bias = section->vma - pe->pe_opthdr.ImageBase; |
|
if (! bfd_malloc_and_get_section (abfd, section, & data)) |
{ |
if (data != NULL) |
free (data); |
return FALSE; |
} |
|
regions.section_start = data; |
regions.section_end = data + datasize; |
regions.strings_start = NULL; |
regions.resource_start = NULL; |
|
fflush (file); |
fprintf (file, "\nThe .rsrc Resource Directory section:\n"); |
|
while (data < regions.section_end) |
{ |
bfd_byte * p = data; |
|
data = rsrc_print_resource_directory (file, abfd, 0, data, & regions, rva_bias); |
|
if (data == regions.section_end + 1) |
fprintf (file, _("Corrupt .rsrc section detected!\n")); |
else |
{ |
/* Align data before continuing. */ |
int align = (1 << section->alignment_power) - 1; |
|
data = (bfd_byte *) (((ptrdiff_t) (data + align)) & ~ align); |
rva_bias += data - p; |
|
/* For reasons that are unclear .rsrc sections are sometimes created |
aligned to a 1^3 boundary even when their alignment is set at |
1^2. Catch that case here before we issue a spurious warning |
message. */ |
if (data == (regions.section_end - 4)) |
data = regions.section_end; |
else if (data < regions.section_end) |
{ |
/* If the extra data is all zeros then do not complain. |
This is just padding so that the section meets the |
page size requirements. */ |
while (++ data < regions.section_end) |
if (*data != 0) |
break; |
if (data < regions.section_end) |
fprintf (file, _("\nWARNING: Extra data in .rsrc section - it will be ignored by Windows:\n")); |
} |
} |
} |
|
if (regions.strings_start != NULL) |
fprintf (file, " String table starts at offset: %#03x\n", |
(int) (regions.strings_start - regions.section_start)); |
if (regions.resource_start != NULL) |
fprintf (file, " Resources start at offset: %#03x\n", |
(int) (regions.resource_start - regions.section_start)); |
|
free (regions.section_start); |
return TRUE; |
} |
|
#define IMAGE_NUMBEROF_DEBUG_TYPES 12 |
|
static char * debug_type_names[IMAGE_NUMBEROF_DEBUG_TYPES] = |
{ |
"Unknown", |
"COFF", |
"CodeView", |
"FPO", |
"Misc", |
"Exception", |
"Fixup", |
"OMAP-to-SRC", |
"OMAP-from-SRC", |
"Borland", |
"Reserved", |
"CLSID", |
}; |
|
static bfd_boolean |
pe_print_debugdata (bfd * abfd, void * vfile) |
{ |
FILE *file = (FILE *) vfile; |
pe_data_type *pe = pe_data (abfd); |
struct internal_extra_pe_aouthdr *extra = &pe->pe_opthdr; |
asection *section; |
bfd_byte *data = 0; |
bfd_size_type dataoff; |
unsigned int i; |
|
bfd_vma addr = extra->DataDirectory[PE_DEBUG_DATA].VirtualAddress; |
bfd_size_type size = extra->DataDirectory[PE_DEBUG_DATA].Size; |
|
if (size == 0) |
return TRUE; |
|
addr += extra->ImageBase; |
for (section = abfd->sections; section != NULL; section = section->next) |
{ |
if ((addr >= section->vma) && (addr < (section->vma + section->size))) |
break; |
} |
|
if (section == NULL) |
{ |
fprintf (file, |
_("\nThere is a debug directory, but the section containing it could not be found\n")); |
return TRUE; |
} |
else if (!(section->flags & SEC_HAS_CONTENTS)) |
{ |
fprintf (file, |
_("\nThere is a debug directory in %s, but that section has no contents\n"), |
section->name); |
return TRUE; |
} |
else if (section->size < size) |
{ |
fprintf (file, |
_("\nError: section %s contains the debug data starting address but it is too small\n"), |
section->name); |
return FALSE; |
} |
|
fprintf (file, _("\nThere is a debug directory in %s at 0x%lx\n\n"), |
section->name, (unsigned long) addr); |
|
dataoff = addr - section->vma; |
|
if (size > (section->size - dataoff)) |
{ |
fprintf (file, _("The debug data size field in the data directory is too big for the section")); |
return FALSE; |
} |
|
fprintf (file, |
_("Type Size Rva Offset\n")); |
|
/* Read the whole section. */ |
if (!bfd_malloc_and_get_section (abfd, section, &data)) |
{ |
if (data != NULL) |
free (data); |
return FALSE; |
} |
|
for (i = 0; i < size / sizeof (struct external_IMAGE_DEBUG_DIRECTORY); i++) |
{ |
const char *type_name; |
struct external_IMAGE_DEBUG_DIRECTORY *ext |
= &((struct external_IMAGE_DEBUG_DIRECTORY *)(data + dataoff))[i]; |
struct internal_IMAGE_DEBUG_DIRECTORY idd; |
|
_bfd_pei_swap_debugdir_in (abfd, ext, &idd); |
|
if ((idd.Type) >= IMAGE_NUMBEROF_DEBUG_TYPES) |
type_name = debug_type_names[0]; |
else |
type_name = debug_type_names[idd.Type]; |
|
fprintf (file, " %2ld %14s %08lx %08lx %08lx\n", |
idd.Type, type_name, idd.SizeOfData, |
idd.AddressOfRawData, idd.PointerToRawData); |
|
if (idd.Type == PE_IMAGE_DEBUG_TYPE_CODEVIEW) |
{ |
char signature[CV_INFO_SIGNATURE_LENGTH * 2 + 1]; |
/* PR 17512: file: 065-29434-0.001:0.1 |
We need to use a 32-bit aligned buffer |
to safely read in a codeview record. */ |
char buffer[256 + 1] ATTRIBUTE_ALIGNED_ALIGNOF (CODEVIEW_INFO); |
|
CODEVIEW_INFO *cvinfo = (CODEVIEW_INFO *) buffer; |
|
/* The debug entry doesn't have to have to be in a section, |
in which case AddressOfRawData is 0, so always use PointerToRawData. */ |
if (!_bfd_pei_slurp_codeview_record (abfd, (file_ptr) idd.PointerToRawData, |
idd.SizeOfData, cvinfo)) |
continue; |
|
for (i = 0; i < cvinfo->SignatureLength; i++) |
sprintf (&signature[i*2], "%02x", cvinfo->Signature[i] & 0xff); |
|
fprintf (file, "(format %c%c%c%c signature %s age %ld)\n", |
buffer[0], buffer[1], buffer[2], buffer[3], |
signature, cvinfo->Age); |
} |
} |
|
if (size % sizeof (struct external_IMAGE_DEBUG_DIRECTORY) != 0) |
fprintf (file, |
_("The debug directory size is not a multiple of the debug directory entry size\n")); |
|
return TRUE; |
} |
|
/* Print out the program headers. */ |
|
bfd_boolean |
2164,10 → 2873,26 |
else |
pe_print_pdata (abfd, vfile); |
pe_print_reloc (abfd, vfile); |
pe_print_debugdata (abfd, file); |
|
rsrc_print_section (abfd, vfile); |
|
return TRUE; |
} |
|
static bfd_boolean |
is_vma_in_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sect, void *obj) |
{ |
bfd_vma addr = * (bfd_vma *) obj; |
return (addr >= sect->vma) && (addr < (sect->vma + sect->size)); |
} |
|
static asection * |
find_section_by_vma (bfd *abfd, bfd_vma addr) |
{ |
return bfd_sections_find_if (abfd, is_vma_in_section, (void *) & addr); |
} |
|
/* Copy any private info we understand from the input bfd |
to the output bfd. */ |
|
2206,6 → 2931,65 |
&& ! (pe_data (ibfd)->real_flags & IMAGE_FILE_RELOCS_STRIPPED)) |
pe_data (obfd)->dont_strip_reloc = 1; |
|
/* The file offsets contained in the debug directory need rewriting. */ |
if (ope->pe_opthdr.DataDirectory[PE_DEBUG_DATA].Size != 0) |
{ |
bfd_vma addr = ope->pe_opthdr.DataDirectory[PE_DEBUG_DATA].VirtualAddress |
+ ope->pe_opthdr.ImageBase; |
asection *section = find_section_by_vma (obfd, addr); |
bfd_byte *data; |
|
if (section && bfd_malloc_and_get_section (obfd, section, &data)) |
{ |
unsigned int i; |
struct external_IMAGE_DEBUG_DIRECTORY *dd = |
(struct external_IMAGE_DEBUG_DIRECTORY *)(data + (addr - section->vma)); |
|
/* PR 17512: file: 0f15796a. */ |
if (ope->pe_opthdr.DataDirectory[PE_DEBUG_DATA].Size + (addr - section->vma) |
> bfd_get_section_size (section)) |
{ |
_bfd_error_handler (_("%B: Data Directory size (%lx) exceeds space left in section (%lx)"), |
obfd, ope->pe_opthdr.DataDirectory[PE_DEBUG_DATA].Size, |
bfd_get_section_size (section) - (addr - section->vma)); |
return FALSE; |
} |
|
for (i = 0; i < ope->pe_opthdr.DataDirectory[PE_DEBUG_DATA].Size |
/ sizeof (struct external_IMAGE_DEBUG_DIRECTORY); i++) |
{ |
asection *ddsection; |
struct external_IMAGE_DEBUG_DIRECTORY *edd = &(dd[i]); |
struct internal_IMAGE_DEBUG_DIRECTORY idd; |
|
_bfd_pei_swap_debugdir_in (obfd, edd, &idd); |
|
if (idd.AddressOfRawData == 0) |
continue; /* RVA 0 means only offset is valid, not handled yet. */ |
|
ddsection = find_section_by_vma (obfd, idd.AddressOfRawData + ope->pe_opthdr.ImageBase); |
if (!ddsection) |
continue; /* Not in a section! */ |
|
idd.PointerToRawData = ddsection->filepos + (idd.AddressOfRawData |
+ ope->pe_opthdr.ImageBase) - ddsection->vma; |
|
_bfd_pei_swap_debugdir_out (obfd, &idd, edd); |
} |
|
if (!bfd_set_section_contents (obfd, section, data, 0, section->size)) |
{ |
_bfd_error_handler (_("Failed to update file offsets in debug directory")); |
return FALSE; |
} |
} |
else if (section) |
{ |
_bfd_error_handler (_("%B: Failed to read debug data section"), obfd); |
return FALSE; |
} |
} |
|
return TRUE; |
} |
|
2270,6 → 3054,1269 |
} |
#endif |
|
/* Functions to process a .rsrc section. */ |
|
static unsigned int sizeof_leaves; |
static unsigned int sizeof_strings; |
static unsigned int sizeof_tables_and_entries; |
|
static bfd_byte * |
rsrc_count_directory (bfd *, bfd_byte *, bfd_byte *, bfd_byte *, bfd_vma); |
|
static bfd_byte * |
rsrc_count_entries (bfd * abfd, |
bfd_boolean is_name, |
bfd_byte * datastart, |
bfd_byte * data, |
bfd_byte * dataend, |
bfd_vma rva_bias) |
{ |
unsigned long entry, addr, size; |
|
if (data + 8 >= dataend) |
return dataend + 1; |
|
if (is_name) |
{ |
bfd_byte * name; |
|
entry = (long) bfd_get_32 (abfd, data); |
|
if (HighBitSet (entry)) |
name = datastart + WithoutHighBit (entry); |
else |
name = datastart + entry - rva_bias; |
|
if (name + 2 >= dataend || name < datastart) |
return dataend + 1; |
|
unsigned int len = bfd_get_16 (abfd, name); |
if (len == 0 || len > 256) |
return dataend + 1; |
} |
|
entry = (long) bfd_get_32 (abfd, data + 4); |
|
if (HighBitSet (entry)) |
{ |
data = datastart + WithoutHighBit (entry); |
|
if (data <= datastart || data >= dataend) |
return dataend + 1; |
|
return rsrc_count_directory (abfd, datastart, data, dataend, rva_bias); |
} |
|
if (datastart + entry + 16 >= dataend) |
return dataend + 1; |
|
addr = (long) bfd_get_32 (abfd, datastart + entry); |
size = (long) bfd_get_32 (abfd, datastart + entry + 4); |
|
return datastart + addr - rva_bias + size; |
} |
|
static bfd_byte * |
rsrc_count_directory (bfd * abfd, |
bfd_byte * datastart, |
bfd_byte * data, |
bfd_byte * dataend, |
bfd_vma rva_bias) |
{ |
unsigned int num_entries, num_ids; |
bfd_byte * highest_data = data; |
|
if (data + 16 >= dataend) |
return dataend + 1; |
|
num_entries = (int) bfd_get_16 (abfd, data + 12); |
num_ids = (int) bfd_get_16 (abfd, data + 14); |
|
num_entries += num_ids; |
|
data += 16; |
|
while (num_entries --) |
{ |
bfd_byte * entry_end; |
|
entry_end = rsrc_count_entries (abfd, num_entries >= num_ids, |
datastart, data, dataend, rva_bias); |
data += 8; |
highest_data = max (highest_data, entry_end); |
if (entry_end >= dataend) |
break; |
} |
|
return max (highest_data, data); |
} |
|
typedef struct rsrc_dir_chain |
{ |
unsigned int num_entries; |
struct rsrc_entry * first_entry; |
struct rsrc_entry * last_entry; |
} rsrc_dir_chain; |
|
typedef struct rsrc_directory |
{ |
unsigned int characteristics; |
unsigned int time; |
unsigned int major; |
unsigned int minor; |
|
rsrc_dir_chain names; |
rsrc_dir_chain ids; |
|
struct rsrc_entry * entry; |
} rsrc_directory; |
|
typedef struct rsrc_string |
{ |
unsigned int len; |
bfd_byte * string; |
} rsrc_string; |
|
typedef struct rsrc_leaf |
{ |
unsigned int size; |
unsigned int codepage; |
bfd_byte * data; |
} rsrc_leaf; |
|
typedef struct rsrc_entry |
{ |
bfd_boolean is_name; |
union |
{ |
unsigned int id; |
struct rsrc_string name; |
} name_id; |
|
bfd_boolean is_dir; |
union |
{ |
struct rsrc_directory * directory; |
struct rsrc_leaf * leaf; |
} value; |
|
struct rsrc_entry * next_entry; |
struct rsrc_directory * parent; |
} rsrc_entry; |
|
static bfd_byte * |
rsrc_parse_directory (bfd *, rsrc_directory *, bfd_byte *, |
bfd_byte *, bfd_byte *, bfd_vma, rsrc_entry *); |
|
static bfd_byte * |
rsrc_parse_entry (bfd * abfd, |
bfd_boolean is_name, |
rsrc_entry * entry, |
bfd_byte * datastart, |
bfd_byte * data, |
bfd_byte * dataend, |
bfd_vma rva_bias, |
rsrc_directory * parent) |
{ |
unsigned long val, addr, size; |
|
val = bfd_get_32 (abfd, data); |
|
entry->parent = parent; |
entry->is_name = is_name; |
|
if (is_name) |
{ |
bfd_byte * address; |
|
if (HighBitSet (val)) |
{ |
val = WithoutHighBit (val); |
|
address = datastart + val; |
} |
else |
{ |
address = datastart + val - rva_bias; |
} |
|
if (address + 3 > dataend) |
return dataend; |
|
entry->name_id.name.len = bfd_get_16 (abfd, address); |
entry->name_id.name.string = address + 2; |
} |
else |
entry->name_id.id = val; |
|
val = bfd_get_32 (abfd, data + 4); |
|
if (HighBitSet (val)) |
{ |
entry->is_dir = TRUE; |
entry->value.directory = bfd_malloc (sizeof * entry->value.directory); |
if (entry->value.directory == NULL) |
return dataend; |
|
return rsrc_parse_directory (abfd, entry->value.directory, |
datastart, |
datastart + WithoutHighBit (val), |
dataend, rva_bias, entry); |
} |
|
entry->is_dir = FALSE; |
entry->value.leaf = bfd_malloc (sizeof * entry->value.leaf); |
if (entry->value.leaf == NULL) |
return dataend; |
|
data = datastart + val; |
if (data < datastart || data >= dataend) |
return dataend; |
|
addr = bfd_get_32 (abfd, data); |
size = entry->value.leaf->size = bfd_get_32 (abfd, data + 4); |
entry->value.leaf->codepage = bfd_get_32 (abfd, data + 8); |
/* FIXME: We assume that the reserved field (data + 12) is OK. */ |
|
entry->value.leaf->data = bfd_malloc (size); |
if (entry->value.leaf->data == NULL) |
return dataend; |
|
memcpy (entry->value.leaf->data, datastart + addr - rva_bias, size); |
return datastart + (addr - rva_bias) + size; |
} |
|
static bfd_byte * |
rsrc_parse_entries (bfd * abfd, |
rsrc_dir_chain * chain, |
bfd_boolean is_name, |
bfd_byte * highest_data, |
bfd_byte * datastart, |
bfd_byte * data, |
bfd_byte * dataend, |
bfd_vma rva_bias, |
rsrc_directory * parent) |
{ |
unsigned int i; |
rsrc_entry * entry; |
|
if (chain->num_entries == 0) |
{ |
chain->first_entry = chain->last_entry = NULL; |
return highest_data; |
} |
|
entry = bfd_malloc (sizeof * entry); |
if (entry == NULL) |
return dataend; |
|
chain->first_entry = entry; |
|
for (i = chain->num_entries; i--;) |
{ |
bfd_byte * entry_end; |
|
entry_end = rsrc_parse_entry (abfd, is_name, entry, datastart, |
data, dataend, rva_bias, parent); |
data += 8; |
highest_data = max (entry_end, highest_data); |
if (entry_end > dataend) |
return dataend; |
|
if (i) |
{ |
entry->next_entry = bfd_malloc (sizeof * entry); |
entry = entry->next_entry; |
if (entry == NULL) |
return dataend; |
} |
else |
entry->next_entry = NULL; |
} |
|
chain->last_entry = entry; |
|
return highest_data; |
} |
|
static bfd_byte * |
rsrc_parse_directory (bfd * abfd, |
rsrc_directory * table, |
bfd_byte * datastart, |
bfd_byte * data, |
bfd_byte * dataend, |
bfd_vma rva_bias, |
rsrc_entry * entry) |
{ |
bfd_byte * highest_data = data; |
|
if (table == NULL) |
return dataend; |
|
table->characteristics = bfd_get_32 (abfd, data); |
table->time = bfd_get_32 (abfd, data + 4); |
table->major = bfd_get_16 (abfd, data + 8); |
table->minor = bfd_get_16 (abfd, data + 10); |
table->names.num_entries = bfd_get_16 (abfd, data + 12); |
table->ids.num_entries = bfd_get_16 (abfd, data + 14); |
table->entry = entry; |
|
data += 16; |
|
highest_data = rsrc_parse_entries (abfd, & table->names, TRUE, data, |
datastart, data, dataend, rva_bias, table); |
data += table->names.num_entries * 8; |
|
highest_data = rsrc_parse_entries (abfd, & table->ids, FALSE, highest_data, |
datastart, data, dataend, rva_bias, table); |
data += table->ids.num_entries * 8; |
|
return max (highest_data, data); |
} |
|
typedef struct rsrc_write_data |
{ |
bfd * abfd; |
bfd_byte * datastart; |
bfd_byte * next_table; |
bfd_byte * next_leaf; |
bfd_byte * next_string; |
bfd_byte * next_data; |
bfd_vma rva_bias; |
} rsrc_write_data; |
|
static void |
rsrc_write_string (rsrc_write_data * data, |
rsrc_string * string) |
{ |
bfd_put_16 (data->abfd, string->len, data->next_string); |
memcpy (data->next_string + 2, string->string, string->len * 2); |
data->next_string += (string->len + 1) * 2; |
} |
|
static inline unsigned int |
rsrc_compute_rva (rsrc_write_data * data, |
bfd_byte * addr) |
{ |
return (addr - data->datastart) + data->rva_bias; |
} |
|
static void |
rsrc_write_leaf (rsrc_write_data * data, |
rsrc_leaf * leaf) |
{ |
bfd_put_32 (data->abfd, rsrc_compute_rva (data, data->next_data), |
data->next_leaf); |
bfd_put_32 (data->abfd, leaf->size, data->next_leaf + 4); |
bfd_put_32 (data->abfd, leaf->codepage, data->next_leaf + 8); |
bfd_put_32 (data->abfd, 0 /*reserved*/, data->next_leaf + 12); |
data->next_leaf += 16; |
|
memcpy (data->next_data, leaf->data, leaf->size); |
/* An undocumented feature of Windows resources is that each unit |
of raw data is 8-byte aligned... */ |
data->next_data += ((leaf->size + 7) & ~7); |
} |
|
static void rsrc_write_directory (rsrc_write_data *, rsrc_directory *); |
|
static void |
rsrc_write_entry (rsrc_write_data * data, |
bfd_byte * where, |
rsrc_entry * entry) |
{ |
if (entry->is_name) |
{ |
bfd_put_32 (data->abfd, |
SetHighBit (data->next_string - data->datastart), |
where); |
rsrc_write_string (data, & entry->name_id.name); |
} |
else |
bfd_put_32 (data->abfd, entry->name_id.id, where); |
|
if (entry->is_dir) |
{ |
bfd_put_32 (data->abfd, |
SetHighBit (data->next_table - data->datastart), |
where + 4); |
rsrc_write_directory (data, entry->value.directory); |
} |
else |
{ |
bfd_put_32 (data->abfd, data->next_leaf - data->datastart, where + 4); |
rsrc_write_leaf (data, entry->value.leaf); |
} |
} |
|
static void |
rsrc_compute_region_sizes (rsrc_directory * dir) |
{ |
struct rsrc_entry * entry; |
|
if (dir == NULL) |
return; |
|
sizeof_tables_and_entries += 16; |
|
for (entry = dir->names.first_entry; entry != NULL; entry = entry->next_entry) |
{ |
sizeof_tables_and_entries += 8; |
|
sizeof_strings += (entry->name_id.name.len + 1) * 2; |
|
if (entry->is_dir) |
rsrc_compute_region_sizes (entry->value.directory); |
else |
sizeof_leaves += 16; |
} |
|
for (entry = dir->ids.first_entry; entry != NULL; entry = entry->next_entry) |
{ |
sizeof_tables_and_entries += 8; |
|
if (entry->is_dir) |
rsrc_compute_region_sizes (entry->value.directory); |
else |
sizeof_leaves += 16; |
} |
} |
|
static void |
rsrc_write_directory (rsrc_write_data * data, |
rsrc_directory * dir) |
{ |
rsrc_entry * entry; |
unsigned int i; |
bfd_byte * next_entry; |
bfd_byte * nt; |
|
bfd_put_32 (data->abfd, dir->characteristics, data->next_table); |
bfd_put_32 (data->abfd, 0 /*dir->time*/, data->next_table + 4); |
bfd_put_16 (data->abfd, dir->major, data->next_table + 8); |
bfd_put_16 (data->abfd, dir->minor, data->next_table + 10); |
bfd_put_16 (data->abfd, dir->names.num_entries, data->next_table + 12); |
bfd_put_16 (data->abfd, dir->ids.num_entries, data->next_table + 14); |
|
/* Compute where the entries and the next table will be placed. */ |
next_entry = data->next_table + 16; |
data->next_table = next_entry + (dir->names.num_entries * 8) |
+ (dir->ids.num_entries * 8); |
nt = data->next_table; |
|
/* Write the entries. */ |
for (i = dir->names.num_entries, entry = dir->names.first_entry; |
i > 0 && entry != NULL; |
i--, entry = entry->next_entry) |
{ |
BFD_ASSERT (entry->is_name); |
rsrc_write_entry (data, next_entry, entry); |
next_entry += 8; |
} |
BFD_ASSERT (i == 0); |
BFD_ASSERT (entry == NULL); |
|
for (i = dir->ids.num_entries, entry = dir->ids.first_entry; |
i > 0 && entry != NULL; |
i--, entry = entry->next_entry) |
{ |
BFD_ASSERT (! entry->is_name); |
rsrc_write_entry (data, next_entry, entry); |
next_entry += 8; |
} |
BFD_ASSERT (i == 0); |
BFD_ASSERT (entry == NULL); |
BFD_ASSERT (nt == next_entry); |
} |
|
#if defined HAVE_WCHAR_H && ! defined __CYGWIN__ && ! defined __MINGW32__ |
/* Return the length (number of units) of the first character in S, |
putting its 'ucs4_t' representation in *PUC. */ |
|
static unsigned int |
#if defined HAVE_WCTYPE_H |
u16_mbtouc (wint_t * puc, const unsigned short * s, unsigned int n) |
#else |
u16_mbtouc (wchar_t * puc, const unsigned short * s, unsigned int n) |
#endif |
{ |
unsigned short c = * s; |
|
if (c < 0xd800 || c >= 0xe000) |
{ |
*puc = c; |
return 1; |
} |
|
if (c < 0xdc00) |
{ |
if (n >= 2) |
{ |
if (s[1] >= 0xdc00 && s[1] < 0xe000) |
{ |
*puc = 0x10000 + ((c - 0xd800) << 10) + (s[1] - 0xdc00); |
return 2; |
} |
} |
else |
{ |
/* Incomplete multibyte character. */ |
*puc = 0xfffd; |
return n; |
} |
} |
|
/* Invalid multibyte character. */ |
*puc = 0xfffd; |
return 1; |
} |
#endif /* HAVE_WCHAR_H and not Cygwin/Mingw */ |
|
/* Perform a comparison of two entries. */ |
static signed int |
rsrc_cmp (bfd_boolean is_name, rsrc_entry * a, rsrc_entry * b) |
{ |
signed int res; |
bfd_byte * astring; |
unsigned int alen; |
bfd_byte * bstring; |
unsigned int blen; |
|
if (! is_name) |
return a->name_id.id - b->name_id.id; |
|
/* We have to perform a case insenstive, unicode string comparison... */ |
astring = a->name_id.name.string; |
alen = a->name_id.name.len; |
bstring = b->name_id.name.string; |
blen = b->name_id.name.len; |
|
#if defined __CYGWIN__ || defined __MINGW32__ |
/* Under Windows hosts (both Cygwin and Mingw types), |
unicode == UTF-16 == wchar_t. The case insensitive string comparison |
function however goes by different names in the two environments... */ |
|
#undef rscpcmp |
#ifdef __CYGWIN__ |
#define rscpcmp wcsncasecmp |
#endif |
#ifdef __MINGW32__ |
#define rscpcmp wcsnicmp |
#endif |
|
res = rscpcmp ((const wchar_t *) astring, (const wchar_t *) bstring, |
min (alen, blen)); |
|
#elif defined HAVE_WCHAR_H |
{ |
unsigned int i; |
|
res = 0; |
for (i = min (alen, blen); i--; astring += 2, bstring += 2) |
{ |
#if defined HAVE_WCTYPE_H |
wint_t awc; |
wint_t bwc; |
#else |
wchar_t awc; |
wchar_t bwc; |
#endif |
|
/* Convert UTF-16 unicode characters into wchar_t characters |
so that we can then perform a case insensitive comparison. */ |
unsigned int Alen = u16_mbtouc (& awc, (const unsigned short *) astring, 2); |
unsigned int Blen = u16_mbtouc (& bwc, (const unsigned short *) bstring, 2); |
|
if (Alen != Blen) |
return Alen - Blen; |
|
#ifdef HAVE_WCTYPE_H |
awc = towlower (awc); |
bwc = towlower (bwc); |
|
res = awc - bwc; |
#else |
res = wcsncasecmp (& awc, & bwc, 1); |
#endif |
if (res) |
break; |
} |
} |
#else |
/* Do the best we can - a case sensitive, untranslated comparison. */ |
res = memcmp (astring, bstring, min (alen, blen) * 2); |
#endif |
|
if (res == 0) |
res = alen - blen; |
|
return res; |
} |
|
static void |
rsrc_print_name (char * buffer, rsrc_string string) |
{ |
unsigned int i; |
bfd_byte * name = string.string; |
|
for (i = string.len; i--; name += 2) |
sprintf (buffer + strlen (buffer), "%.1s", name); |
} |
|
static const char * |
rsrc_resource_name (rsrc_entry * entry, rsrc_directory * dir) |
{ |
static char buffer [256]; |
bfd_boolean is_string = FALSE; |
|
buffer[0] = 0; |
|
if (dir != NULL && dir->entry != NULL && dir->entry->parent != NULL |
&& dir->entry->parent->entry != NULL) |
{ |
strcpy (buffer, "type: "); |
if (dir->entry->parent->entry->is_name) |
rsrc_print_name (buffer + strlen (buffer), |
dir->entry->parent->entry->name_id.name); |
else |
{ |
unsigned int id = dir->entry->parent->entry->name_id.id; |
|
sprintf (buffer + strlen (buffer), "%x", id); |
switch (id) |
{ |
case 1: strcat (buffer, " (CURSOR)"); break; |
case 2: strcat (buffer, " (BITMAP)"); break; |
case 3: strcat (buffer, " (ICON)"); break; |
case 4: strcat (buffer, " (MENU)"); break; |
case 5: strcat (buffer, " (DIALOG)"); break; |
case 6: strcat (buffer, " (STRING)"); is_string = TRUE; break; |
case 7: strcat (buffer, " (FONTDIR)"); break; |
case 8: strcat (buffer, " (FONT)"); break; |
case 9: strcat (buffer, " (ACCELERATOR)"); break; |
case 10: strcat (buffer, " (RCDATA)"); break; |
case 11: strcat (buffer, " (MESSAGETABLE)"); break; |
case 12: strcat (buffer, " (GROUP_CURSOR)"); break; |
case 14: strcat (buffer, " (GROUP_ICON)"); break; |
case 16: strcat (buffer, " (VERSION)"); break; |
case 17: strcat (buffer, " (DLGINCLUDE)"); break; |
case 19: strcat (buffer, " (PLUGPLAY)"); break; |
case 20: strcat (buffer, " (VXD)"); break; |
case 21: strcat (buffer, " (ANICURSOR)"); break; |
case 22: strcat (buffer, " (ANIICON)"); break; |
case 23: strcat (buffer, " (HTML)"); break; |
case 24: strcat (buffer, " (MANIFEST)"); break; |
case 240: strcat (buffer, " (DLGINIT)"); break; |
case 241: strcat (buffer, " (TOOLBAR)"); break; |
} |
} |
} |
|
if (dir != NULL && dir->entry != NULL) |
{ |
strcat (buffer, " name: "); |
if (dir->entry->is_name) |
rsrc_print_name (buffer + strlen (buffer), dir->entry->name_id.name); |
else |
{ |
unsigned int id = dir->entry->name_id.id; |
|
sprintf (buffer + strlen (buffer), "%x", id); |
|
if (is_string) |
sprintf (buffer + strlen (buffer), " (resource id range: %d - %d)", |
(id - 1) << 4, (id << 4) - 1); |
} |
} |
|
if (entry != NULL) |
{ |
strcat (buffer, " lang: "); |
|
if (entry->is_name) |
rsrc_print_name (buffer + strlen (buffer), entry->name_id.name); |
else |
sprintf (buffer + strlen (buffer), "%x", entry->name_id.id); |
} |
|
return buffer; |
} |
|
/* *sigh* Windows resource strings are special. Only the top 28-bits of |
their ID is stored in the NAME entry. The bottom four bits are used as |
an index into unicode string table that makes up the data of the leaf. |
So identical type-name-lang string resources may not actually be |
identical at all. |
|
This function is called when we have detected two string resources with |
match top-28-bit IDs. We have to scan the string tables inside the leaves |
and discover if there are any real collisions. If there are then we report |
them and return FALSE. Otherwise we copy any strings from B into A and |
then return TRUE. */ |
|
static bfd_boolean |
rsrc_merge_string_entries (rsrc_entry * a ATTRIBUTE_UNUSED, |
rsrc_entry * b ATTRIBUTE_UNUSED) |
{ |
unsigned int copy_needed = 0; |
unsigned int i; |
bfd_byte * astring; |
bfd_byte * bstring; |
bfd_byte * new_data; |
bfd_byte * nstring; |
|
/* Step one: Find out what we have to do. */ |
BFD_ASSERT (! a->is_dir); |
astring = a->value.leaf->data; |
|
BFD_ASSERT (! b->is_dir); |
bstring = b->value.leaf->data; |
|
for (i = 0; i < 16; i++) |
{ |
unsigned int alen = astring[0] + (astring[1] << 8); |
unsigned int blen = bstring[0] + (bstring[1] << 8); |
|
if (alen == 0) |
{ |
copy_needed += blen * 2; |
} |
else if (blen == 0) |
; |
else if (alen != blen) |
/* FIXME: Should we continue the loop in order to report other duplicates ? */ |
break; |
/* alen == blen != 0. We might have two identical strings. If so we |
can ignore the second one. There is no need for wchar_t vs UTF-16 |
theatrics here - we are only interested in (case sensitive) equality. */ |
else if (memcmp (astring + 2, bstring + 2, alen * 2) != 0) |
break; |
|
astring += (alen + 1) * 2; |
bstring += (blen + 1) * 2; |
} |
|
if (i != 16) |
{ |
if (a->parent != NULL |
&& a->parent->entry != NULL |
&& a->parent->entry->is_name == FALSE) |
_bfd_error_handler (_(".rsrc merge failure: duplicate string resource: %d"), |
((a->parent->entry->name_id.id - 1) << 4) + i); |
return FALSE; |
} |
|
if (copy_needed == 0) |
return TRUE; |
|
/* If we reach here then A and B must both have non-colliding strings. |
(We never get string resources with fully empty string tables). |
We need to allocate an extra COPY_NEEDED bytes in A and then bring |
in B's strings. */ |
new_data = bfd_malloc (a->value.leaf->size + copy_needed); |
if (new_data == NULL) |
return FALSE; |
|
nstring = new_data; |
astring = a->value.leaf->data; |
bstring = b->value.leaf->data; |
|
for (i = 0; i < 16; i++) |
{ |
unsigned int alen = astring[0] + (astring[1] << 8); |
unsigned int blen = bstring[0] + (bstring[1] << 8); |
|
if (alen != 0) |
{ |
memcpy (nstring, astring, (alen + 1) * 2); |
nstring += (alen + 1) * 2; |
} |
else if (blen != 0) |
{ |
memcpy (nstring, bstring, (blen + 1) * 2); |
nstring += (blen + 1) * 2; |
} |
else |
{ |
* nstring++ = 0; |
* nstring++ = 0; |
} |
|
astring += (alen + 1) * 2; |
bstring += (blen + 1) * 2; |
} |
|
BFD_ASSERT (nstring - new_data == (signed) (a->value.leaf->size + copy_needed)); |
|
free (a->value.leaf->data); |
a->value.leaf->data = new_data; |
a->value.leaf->size += copy_needed; |
|
return TRUE; |
} |
|
static void rsrc_merge (rsrc_entry *, rsrc_entry *); |
|
/* Sort the entries in given part of the directory. |
We use an old fashioned bubble sort because we are dealing |
with lists and we want to handle matches specially. */ |
|
static void |
rsrc_sort_entries (rsrc_dir_chain * chain, |
bfd_boolean is_name, |
rsrc_directory * dir) |
{ |
rsrc_entry * entry; |
rsrc_entry * next; |
rsrc_entry ** points_to_entry; |
bfd_boolean swapped; |
|
if (chain->num_entries < 2) |
return; |
|
do |
{ |
swapped = FALSE; |
points_to_entry = & chain->first_entry; |
entry = * points_to_entry; |
next = entry->next_entry; |
|
do |
{ |
signed int cmp = rsrc_cmp (is_name, entry, next); |
|
if (cmp > 0) |
{ |
entry->next_entry = next->next_entry; |
next->next_entry = entry; |
* points_to_entry = next; |
points_to_entry = & next->next_entry; |
next = entry->next_entry; |
swapped = TRUE; |
} |
else if (cmp == 0) |
{ |
if (entry->is_dir && next->is_dir) |
{ |
/* When we encounter identical directory entries we have to |
merge them together. The exception to this rule is for |
resource manifests - there can only be one of these, |
even if they differ in language. Zero-language manifests |
are assumed to be default manifests (provided by the |
Cygwin/MinGW build system) and these can be silently dropped, |
unless that would reduce the number of manifests to zero. |
There should only ever be one non-zero lang manifest - |
if there are more it is an error. A non-zero lang |
manifest takes precedence over a default manifest. */ |
if (entry->is_name == FALSE |
&& entry->name_id.id == 1 |
&& dir != NULL |
&& dir->entry != NULL |
&& dir->entry->is_name == FALSE |
&& dir->entry->name_id.id == 0x18) |
{ |
if (next->value.directory->names.num_entries == 0 |
&& next->value.directory->ids.num_entries == 1 |
&& next->value.directory->ids.first_entry->is_name == FALSE |
&& next->value.directory->ids.first_entry->name_id.id == 0) |
/* Fall through so that NEXT is dropped. */ |
; |
else if (entry->value.directory->names.num_entries == 0 |
&& entry->value.directory->ids.num_entries == 1 |
&& entry->value.directory->ids.first_entry->is_name == FALSE |
&& entry->value.directory->ids.first_entry->name_id.id == 0) |
{ |
/* Swap ENTRY and NEXT. Then fall through so that the old ENTRY is dropped. */ |
entry->next_entry = next->next_entry; |
next->next_entry = entry; |
* points_to_entry = next; |
points_to_entry = & next->next_entry; |
next = entry->next_entry; |
swapped = TRUE; |
} |
else |
{ |
_bfd_error_handler (_(".rsrc merge failure: multiple non-default manifests")); |
bfd_set_error (bfd_error_file_truncated); |
return; |
} |
|
/* Unhook NEXT from the chain. */ |
/* FIXME: memory loss here. */ |
entry->next_entry = next->next_entry; |
chain->num_entries --; |
if (chain->num_entries < 2) |
return; |
next = next->next_entry; |
} |
else |
rsrc_merge (entry, next); |
} |
else if (entry->is_dir != next->is_dir) |
{ |
_bfd_error_handler (_(".rsrc merge failure: a directory matches a leaf")); |
bfd_set_error (bfd_error_file_truncated); |
return; |
} |
else |
{ |
/* Otherwise with identical leaves we issue an error |
message - because there should never be duplicates. |
The exception is Type 18/Name 1/Lang 0 which is the |
defaul manifest - this can just be dropped. */ |
if (entry->is_name == FALSE |
&& entry->name_id.id == 0 |
&& dir != NULL |
&& dir->entry != NULL |
&& dir->entry->is_name == FALSE |
&& dir->entry->name_id.id == 1 |
&& dir->entry->parent != NULL |
&& dir->entry->parent->entry != NULL |
&& dir->entry->parent->entry->is_name == FALSE |
&& dir->entry->parent->entry->name_id.id == 0x18 /* RT_MANIFEST */) |
; |
else if (dir != NULL |
&& dir->entry != NULL |
&& dir->entry->parent != NULL |
&& dir->entry->parent->entry != NULL |
&& dir->entry->parent->entry->is_name == FALSE |
&& dir->entry->parent->entry->name_id.id == 0x6 /* RT_STRING */) |
{ |
/* Strings need special handling. */ |
if (! rsrc_merge_string_entries (entry, next)) |
{ |
/* _bfd_error_handler should have been called inside merge_strings. */ |
bfd_set_error (bfd_error_file_truncated); |
return; |
} |
} |
else |
{ |
if (dir == NULL |
|| dir->entry == NULL |
|| dir->entry->parent == NULL |
|| dir->entry->parent->entry == NULL) |
_bfd_error_handler (_(".rsrc merge failure: duplicate leaf")); |
else |
_bfd_error_handler (_(".rsrc merge failure: duplicate leaf: %s"), |
rsrc_resource_name (entry, dir)); |
bfd_set_error (bfd_error_file_truncated); |
return; |
} |
} |
|
/* Unhook NEXT from the chain. */ |
entry->next_entry = next->next_entry; |
chain->num_entries --; |
if (chain->num_entries < 2) |
return; |
next = next->next_entry; |
} |
else |
{ |
points_to_entry = & entry->next_entry; |
entry = next; |
next = next->next_entry; |
} |
} |
while (next); |
|
chain->last_entry = entry; |
} |
while (swapped); |
} |
|
/* Attach B's chain onto A. */ |
static void |
rsrc_attach_chain (rsrc_dir_chain * achain, rsrc_dir_chain * bchain) |
{ |
if (bchain->num_entries == 0) |
return; |
|
achain->num_entries += bchain->num_entries; |
|
if (achain->first_entry == NULL) |
{ |
achain->first_entry = bchain->first_entry; |
achain->last_entry = bchain->last_entry; |
} |
else |
{ |
achain->last_entry->next_entry = bchain->first_entry; |
achain->last_entry = bchain->last_entry; |
} |
|
bchain->num_entries = 0; |
bchain->first_entry = bchain->last_entry = NULL; |
} |
|
static void |
rsrc_merge (struct rsrc_entry * a, struct rsrc_entry * b) |
{ |
rsrc_directory * adir; |
rsrc_directory * bdir; |
|
BFD_ASSERT (a->is_dir); |
BFD_ASSERT (b->is_dir); |
|
adir = a->value.directory; |
bdir = b->value.directory; |
|
if (adir->characteristics != bdir->characteristics) |
{ |
_bfd_error_handler (_(".rsrc merge failure: dirs with differing characteristics\n")); |
bfd_set_error (bfd_error_file_truncated); |
return; |
} |
|
if (adir->major != bdir->major || adir->minor != bdir->minor) |
{ |
_bfd_error_handler (_(".rsrc merge failure: differing directory versions\n")); |
bfd_set_error (bfd_error_file_truncated); |
return; |
} |
|
/* Attach B's name chain to A. */ |
rsrc_attach_chain (& adir->names, & bdir->names); |
|
/* Attach B's ID chain to A. */ |
rsrc_attach_chain (& adir->ids, & bdir->ids); |
|
/* Now sort A's entries. */ |
rsrc_sort_entries (& adir->names, TRUE, adir); |
rsrc_sort_entries (& adir->ids, FALSE, adir); |
} |
|
/* Check the .rsrc section. If it contains multiple concatenated |
resources then we must merge them properly. Otherwise Windows |
will ignore all but the first set. */ |
|
static void |
rsrc_process_section (bfd * abfd, |
struct coff_final_link_info * pfinfo) |
{ |
rsrc_directory new_table; |
bfd_size_type size; |
asection * sec; |
pe_data_type * pe; |
bfd_vma rva_bias; |
bfd_byte * data; |
bfd_byte * datastart; |
bfd_byte * dataend; |
bfd_byte * new_data; |
unsigned int num_resource_sets; |
rsrc_directory * type_tables; |
rsrc_write_data write_data; |
unsigned int indx; |
bfd * input; |
unsigned int num_input_rsrc = 0; |
unsigned int max_num_input_rsrc = 4; |
ptrdiff_t * rsrc_sizes = NULL; |
|
new_table.names.num_entries = 0; |
new_table.ids.num_entries = 0; |
|
sec = bfd_get_section_by_name (abfd, ".rsrc"); |
if (sec == NULL || (size = sec->rawsize) == 0) |
return; |
|
pe = pe_data (abfd); |
if (pe == NULL) |
return; |
|
rva_bias = sec->vma - pe->pe_opthdr.ImageBase; |
|
data = bfd_malloc (size); |
if (data == NULL) |
return; |
|
datastart = data; |
|
if (! bfd_get_section_contents (abfd, sec, data, 0, size)) |
goto end; |
|
/* Step zero: Scan the input bfds looking for .rsrc sections and record |
their lengths. Note - we rely upon the fact that the linker script |
does *not* sort the input .rsrc sections, so that the order in the |
linkinfo list matches the order in the output .rsrc section. |
|
We need to know the lengths because each input .rsrc section has padding |
at the end of a variable amount. (It does not appear to be based upon |
the section alignment or the file alignment). We need to skip any |
padding bytes when parsing the input .rsrc sections. */ |
rsrc_sizes = bfd_malloc (max_num_input_rsrc * sizeof * rsrc_sizes); |
if (rsrc_sizes == NULL) |
goto end; |
|
for (input = pfinfo->info->input_bfds; |
input != NULL; |
input = input->link.next) |
{ |
asection * rsrc_sec = bfd_get_section_by_name (input, ".rsrc"); |
|
/* PR 18372 - skip discarded .rsrc sections. */ |
if (rsrc_sec != NULL && !discarded_section (rsrc_sec)) |
{ |
if (num_input_rsrc == max_num_input_rsrc) |
{ |
max_num_input_rsrc += 10; |
rsrc_sizes = bfd_realloc (rsrc_sizes, max_num_input_rsrc |
* sizeof * rsrc_sizes); |
if (rsrc_sizes == NULL) |
goto end; |
} |
|
BFD_ASSERT (rsrc_sec->size > 0); |
rsrc_sizes [num_input_rsrc ++] = rsrc_sec->size; |
} |
} |
|
if (num_input_rsrc < 2) |
goto end; |
|
/* Step one: Walk the section, computing the size of the tables, |
leaves and data and decide if we need to do anything. */ |
dataend = data + size; |
num_resource_sets = 0; |
|
while (data < dataend) |
{ |
bfd_byte * p = data; |
|
data = rsrc_count_directory (abfd, data, data, dataend, rva_bias); |
|
if (data > dataend) |
{ |
/* Corrupted .rsrc section - cannot merge. */ |
_bfd_error_handler (_("%s: .rsrc merge failure: corrupt .rsrc section"), |
bfd_get_filename (abfd)); |
bfd_set_error (bfd_error_file_truncated); |
goto end; |
} |
|
if ((data - p) > rsrc_sizes [num_resource_sets]) |
{ |
_bfd_error_handler (_("%s: .rsrc merge failure: unexpected .rsrc size"), |
bfd_get_filename (abfd)); |
bfd_set_error (bfd_error_file_truncated); |
goto end; |
} |
/* FIXME: Should we add a check for "data - p" being much smaller |
than rsrc_sizes[num_resource_sets] ? */ |
|
data = p + rsrc_sizes[num_resource_sets]; |
rva_bias += data - p; |
++ num_resource_sets; |
} |
BFD_ASSERT (num_resource_sets == num_input_rsrc); |
|
/* Step two: Walk the data again, building trees of the resources. */ |
data = datastart; |
rva_bias = sec->vma - pe->pe_opthdr.ImageBase; |
|
type_tables = bfd_malloc (num_resource_sets * sizeof * type_tables); |
if (type_tables == NULL) |
goto end; |
|
indx = 0; |
while (data < dataend) |
{ |
bfd_byte * p = data; |
|
(void) rsrc_parse_directory (abfd, type_tables + indx, data, data, |
dataend, rva_bias, NULL); |
data = p + rsrc_sizes[indx]; |
rva_bias += data - p; |
++ indx; |
} |
BFD_ASSERT (indx == num_resource_sets); |
|
/* Step three: Merge the top level tables (there can be only one). |
|
We must ensure that the merged entries are in ascending order. |
|
We also thread the top level table entries from the old tree onto |
the new table, so that they can be pulled off later. */ |
|
/* FIXME: Should we verify that all type tables are the same ? */ |
new_table.characteristics = type_tables[0].characteristics; |
new_table.time = type_tables[0].time; |
new_table.major = type_tables[0].major; |
new_table.minor = type_tables[0].minor; |
|
/* Chain the NAME entries onto the table. */ |
new_table.names.first_entry = NULL; |
new_table.names.last_entry = NULL; |
|
for (indx = 0; indx < num_resource_sets; indx++) |
rsrc_attach_chain (& new_table.names, & type_tables[indx].names); |
|
rsrc_sort_entries (& new_table.names, TRUE, & new_table); |
|
/* Chain the ID entries onto the table. */ |
new_table.ids.first_entry = NULL; |
new_table.ids.last_entry = NULL; |
|
for (indx = 0; indx < num_resource_sets; indx++) |
rsrc_attach_chain (& new_table.ids, & type_tables[indx].ids); |
|
rsrc_sort_entries (& new_table.ids, FALSE, & new_table); |
|
/* Step four: Create new contents for the .rsrc section. */ |
/* Step four point one: Compute the size of each region of the .rsrc section. |
We do this now, rather than earlier, as the merging above may have dropped |
some entries. */ |
sizeof_leaves = sizeof_strings = sizeof_tables_and_entries = 0; |
rsrc_compute_region_sizes (& new_table); |
/* We increment sizeof_strings to make sure that resource data |
starts on an 8-byte boundary. FIXME: Is this correct ? */ |
sizeof_strings = (sizeof_strings + 7) & ~ 7; |
|
new_data = bfd_zalloc (abfd, size); |
if (new_data == NULL) |
goto end; |
|
write_data.abfd = abfd; |
write_data.datastart = new_data; |
write_data.next_table = new_data; |
write_data.next_leaf = new_data + sizeof_tables_and_entries; |
write_data.next_string = write_data.next_leaf + sizeof_leaves; |
write_data.next_data = write_data.next_string + sizeof_strings; |
write_data.rva_bias = sec->vma - pe->pe_opthdr.ImageBase; |
|
rsrc_write_directory (& write_data, & new_table); |
|
/* Step five: Replace the old contents with the new. |
We recompute the size as we may have lost entries due to mergeing. */ |
size = ((write_data.next_data - new_data) + 3) & ~ 3; |
|
{ |
int page_size; |
|
if (coff_data (abfd)->link_info) |
{ |
page_size = pe_data (abfd)->pe_opthdr.FileAlignment; |
|
/* If no file alignment has been set, default to one. |
This repairs 'ld -r' for arm-wince-pe target. */ |
if (page_size == 0) |
page_size = 1; |
} |
else |
page_size = PE_DEF_FILE_ALIGNMENT; |
size = (size + page_size - 1) & - page_size; |
} |
|
bfd_set_section_contents (pfinfo->output_bfd, sec, new_data, 0, size); |
sec->size = sec->rawsize = size; |
|
end: |
/* Step six: Free all the memory that we have used. */ |
/* FIXME: Free the resource tree, if we have one. */ |
free (datastart); |
free (rsrc_sizes); |
} |
|
/* Handle the .idata section and other things that need symbol table |
access. */ |
|
2475,10 → 4522,14 |
} |
free (tmp_data); |
} |
else |
result = FALSE; |
} |
} |
#endif |
|
rsrc_process_section (abfd, pfinfo); |
|
/* If we couldn't find idata$2, we either have an excessively |
trivial program or are in DEEP trouble; we have to assume trivial |
program.... */ |