/* .eh_frame section optimization.
- Copyright 2001 Free Software Foundation, Inc.
+ Copyright 2001, 2002 Free Software Foundation, Inc.
Written by Jakub Jelinek <jakub@redhat.com>.
This file is part of BFD, the Binary File Descriptor library.
unsigned char fde_encoding;
unsigned char initial_insn_length;
unsigned char make_relative;
+ unsigned char make_lsda_relative;
unsigned char initial_instructions[50];
};
asection *sec;
unsigned int new_offset;
unsigned char fde_encoding;
+ unsigned char lsda_encoding;
+ unsigned char lsda_offset;
unsigned char cie : 1;
unsigned char removed : 1;
unsigned char make_relative : 1;
+ unsigned char make_lsda_relative : 1;
+ unsigned char per_encoding_relative : 1;
};
struct eh_frame_sec_info
PARAMS ((bfd *, char *, unsigned int *));
static int get_DW_EH_PE_width
PARAMS ((int, int));
+static bfd_vma read_value
+ PARAMS ((bfd *, bfd_byte *, int));
+static void write_value
+ PARAMS ((bfd *, bfd_byte *, bfd_vma, int));
static int cie_compare
PARAMS ((struct cie *, struct cie *));
static int vma_compare
return 0;
}
+/* Read a width sized value from memory. */
+
+static bfd_vma
+read_value (abfd, buf, width)
+ bfd *abfd;
+ bfd_byte *buf;
+ int width;
+{
+ bfd_vma value;
+
+ switch (width)
+ {
+ case 2: value = bfd_get_16 (abfd, buf); break;
+ case 4: value = bfd_get_32 (abfd, buf); break;
+ case 8: value = bfd_get_64 (abfd, buf); break;
+ default: BFD_FAIL (); return 0;
+ }
+
+ return value;
+}
+
+/* Store a width sized value to memory. */
+
+static void
+write_value (abfd, buf, value, width)
+ bfd *abfd;
+ bfd_byte *buf;
+ bfd_vma value;
+ int width;
+{
+ switch (width)
+ {
+ case 2: bfd_put_16 (abfd, value, buf); break;
+ case 4: bfd_put_32 (abfd, value, buf); break;
+ case 8: bfd_put_64 (abfd, value, buf); break;
+ default: BFD_FAIL ();
+ }
+}
+
/* Return zero if C1 and C2 CIEs can be merged. */
static
bfd *abfd;
struct bfd_link_info *info;
asection *sec, *ehdrsec;
- boolean (*reloc_symbol_deleted_p) (bfd_vma, PTR);
+ boolean (*reloc_symbol_deleted_p) PARAMS ((bfd_vma, PTR));
struct elf_reloc_cookie *cookie;
{
bfd_byte *ehbuf = NULL, *buf;
struct eh_frame_hdr_info *hdr_info;
struct eh_frame_sec_info *sec_info = NULL;
unsigned int leb128_tmp;
- unsigned int cie_usage_count, last_cie_ndx, i, offset, make_relative;
+ unsigned int cie_usage_count, last_cie_ndx, i, offset;
+ unsigned int make_relative, make_lsda_relative;
Elf_Internal_Rela *rel;
bfd_size_type new_size;
unsigned int ptr_size;
cie_usage_count = 0;
new_size = sec->_raw_size;
make_relative = hdr_info->last_cie.make_relative;
+ make_lsda_relative = hdr_info->last_cie.make_lsda_relative;
sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
+ 99 * sizeof (struct eh_cie_fde));
if (sec_info == NULL)
hdr_info->last_cie_offset = last_cie - ehbuf;
sec_info->entry[last_cie_ndx].make_relative
= cie.make_relative;
+ sec_info->entry[last_cie_ndx].make_lsda_relative
+ = cie.make_lsda_relative;
+ sec_info->entry[last_cie_ndx].per_encoding_relative
+ = (cie.per_encoding & 0x70) == DW_EH_PE_pcrel;
}
}
}
read_uleb128 (cie.code_align, buf);
read_sleb128 (cie.data_align, buf);
+ /* Note - in DWARF2 the return address column is an unsigned byte.
+ In DWARF3 it is a ULEB128. We are following DWARF3. For most
+ ports this will not matter as the value will be less than 128.
+ For the others (eg FRV, SH, MMIX, IA64) they need a fixed GCC
+ which conforms to the DWARF3 standard. */
read_uleb128 (cie.ra_column, buf);
ENSURE_NO_RELOCS (buf);
cie.lsda_encoding = DW_EH_PE_omit;
}
/* For shared libraries, try to get rid of as many RELATIVE relocs
- as possible.
- FIXME: For this to work, ELF backends need to perform the
- relocation if omitting dynamic relocs, not skip it. */
- if (0
- && info->shared
+ as possible. */
+ if (info->shared
&& (cie.fde_encoding & 0xf0) == DW_EH_PE_absptr)
cie.make_relative = 1;
+ if (info->shared
+ && (cie.lsda_encoding & 0xf0) == DW_EH_PE_absptr)
+ cie.make_lsda_relative = 1;
+
/* If FDE encoding was not specified, it defaults to
DW_EH_absptr. */
if (cie.fde_encoding == DW_EH_PE_omit)
goto free_no_table;
if ((*reloc_symbol_deleted_p) (buf - ehbuf, cookie))
{
- cookie->rel = rel;
/* This is a FDE against discarded section, it should
be deleted. */
new_size -= hdr.length + 4;
sec_info->entry[sec_info->count].removed = 1;
+ memset (rel, 0, sizeof (*rel));
}
else
{
+ if (info->shared
+ && (((cie.fde_encoding & 0xf0) == DW_EH_PE_absptr
+ && cie.make_relative == 0)
+ || (cie.fde_encoding & 0xf0) == DW_EH_PE_aligned))
+ {
+ /* If shared library uses absolute pointers
+ which we cannot turn into PC relative,
+ don't create the binary search table,
+ since it is affected by runtime relocations. */
+ hdr_info->table = false;
+ }
cie_usage_count++;
hdr_info->fde_count++;
}
cookie->rel = rel;
+ if (cie.lsda_encoding != DW_EH_PE_omit)
+ {
+ unsigned int dummy;
+
+ aug = buf;
+ buf += 2 * get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
+ if (cie.augmentation[0] == 'z')
+ read_uleb128 (dummy, buf);
+ /* If some new augmentation data is added before LSDA
+ in FDE augmentation area, this need to be adjusted. */
+ sec_info->entry[sec_info->count].lsda_offset = (buf - aug);
+ }
buf = last_fde + 4 + hdr.length;
SKIP_RELOCS (buf);
}
sec_info->entry[sec_info->count].fde_encoding = cie.fde_encoding;
+ sec_info->entry[sec_info->count].lsda_encoding = cie.lsda_encoding;
sec_info->count++;
}
{
last_cie_ndx = i;
make_relative = sec_info->entry[i].make_relative;
+ make_lsda_relative = sec_info->entry[i].make_lsda_relative;
}
else
- sec_info->entry[i].make_relative = make_relative;
+ {
+ sec_info->entry[i].make_relative = make_relative;
+ sec_info->entry[i].make_lsda_relative = make_lsda_relative;
+ sec_info->entry[i].per_encoding_relative = 0;
+ }
}
else if (sec_info->entry[i].cie && sec_info->entry[i].sec == sec)
{
hdr_info->last_cie_offset = sec_info->entry[last_cie_ndx].new_offset;
}
+ /* FIXME: Currently it is not possible to shrink sections to zero size at
+ this point, so build a fake minimal CIE. */
+ if (new_size == 0)
+ new_size = 16;
+
/* Shrink the sec as needed. */
-
sec->_cooked_size = new_size;
if (sec->_cooked_size == 0)
sec->flags |= SEC_EXCLUDE;
struct eh_frame_hdr_info *hdr_info;
sec = bfd_get_section_by_name (elf_hash_table (info)->dynobj, ".eh_frame_hdr");
- if (sec == NULL)
+ if (sec == NULL || bfd_is_abs_section (sec->output_section))
return true;
hdr_info
/* Count only sections which have at least a single CIE or FDE.
There cannot be any CIE or FDE <= 8 bytes. */
o = bfd_get_section_by_name (abfd, ".eh_frame");
- if (o && o->_raw_size > 8)
+ if (o && o->_raw_size > 8 && !bfd_is_abs_section (o->output_section))
break;
}
if (sec_info->entry[mid].make_relative
&& ! sec_info->entry[mid].cie
&& offset == sec_info->entry[mid].offset + 8)
- return (bfd_vma) -1;
+ return (bfd_vma) -2;
- return (offset
- + (sec_info->entry[mid].new_offset - sec_info->entry[mid].offset));
+ /* If converting LSDA pointers to DW_EH_PE_pcrel, there will be no need
+ for run-time relocation against LSDA field. */
+ if (sec_info->entry[mid].make_lsda_relative
+ && ! sec_info->entry[mid].cie
+ && (offset
+ == (sec_info->entry[mid].offset + 8
+ + sec_info->entry[mid].lsda_offset)))
+ return (bfd_vma) -2;
+
+ return (offset + sec_info->entry[mid].new_offset
+ - sec_info->entry[mid].offset);
}
/* Write out .eh_frame section. This is called with the relocated
{
if (sec_info->entry[i].cie)
{
- cie_offset = sec_info->entry[i].new_offset;
- cie_offset += (sec_info->entry[i].sec->output_section->vma
- + sec_info->entry[i].sec->output_offset
- - sec->output_section->vma
- - sec->output_offset);
+ /* If CIE is removed due to no remaining FDEs referencing it
+ and there were no CIEs kept before it, sec_info->entry[i].sec
+ will be zero. */
+ if (sec_info->entry[i].sec == NULL)
+ cie_offset = 0;
+ else
+ {
+ cie_offset = sec_info->entry[i].new_offset;
+ cie_offset += (sec_info->entry[i].sec->output_section->vma
+ + sec_info->entry[i].sec->output_offset
+ - sec->output_section->vma
+ - sec->output_offset);
+ }
}
continue;
}
+
if (sec_info->entry[i].cie)
{
/* CIE */
cie_offset = sec_info->entry[i].new_offset;
- if (sec_info->entry[i].make_relative)
+ if (sec_info->entry[i].make_relative
+ || sec_info->entry[i].make_lsda_relative
+ || sec_info->entry[i].per_encoding_relative)
{
unsigned char *aug;
+ unsigned int action;
unsigned int dummy, per_width, per_encoding;
- /* Need to find 'R' augmentation's argument and modify
+ /* Need to find 'R' or 'L' augmentation's argument and modify
DW_EH_PE_* value. */
+ action = (sec_info->entry[i].make_relative ? 1 : 0)
+ | (sec_info->entry[i].make_lsda_relative ? 2 : 0)
+ | (sec_info->entry[i].per_encoding_relative ? 4 : 0);
buf = contents + sec_info->entry[i].offset;
/* Skip length, id and version. */
buf += 9;
aug++;
}
- while (*aug != 'R')
+ while (action)
switch (*aug++)
{
case 'L':
+ if (action & 2)
+ {
+ BFD_ASSERT (*buf == sec_info->entry[i].lsda_encoding);
+ *buf |= DW_EH_PE_pcrel;
+ action &= ~2;
+ }
buf++;
break;
case 'P':
per_width = get_DW_EH_PE_width (per_encoding,
ptr_size);
BFD_ASSERT (per_width != 0);
+ BFD_ASSERT (((per_encoding & 0x70) == DW_EH_PE_pcrel)
+ == sec_info->entry[i].per_encoding_relative);
if ((per_encoding & 0xf0) == DW_EH_PE_aligned)
buf = (contents
+ ((buf - contents + per_width - 1)
& ~((bfd_size_type) per_width - 1)));
+ if (action & 4)
+ {
+ bfd_vma value;
+
+ value = read_value (abfd, buf, per_width);
+ value += (sec_info->entry[i].offset
+ - sec_info->entry[i].new_offset);
+ write_value (abfd, buf, value, per_width);
+ action &= ~4;
+ }
buf += per_width;
break;
+ case 'R':
+ if (action & 1)
+ {
+ BFD_ASSERT (*buf == sec_info->entry[i].fde_encoding);
+ *buf |= DW_EH_PE_pcrel;
+ action &= ~1;
+ }
+ buf++;
+ break;
default:
BFD_FAIL ();
}
-
- BFD_ASSERT (*buf == sec_info->entry[i].fde_encoding);
- *buf |= DW_EH_PE_pcrel;
}
}
- else
+ else if (sec_info->entry[i].size > 4)
{
/* FDE */
bfd_vma value = 0, address;
- unsigned int fde_width;
+ unsigned int width;
buf = contents + sec_info->entry[i].offset;
/* Skip length. */
bfd_put_32 (abfd,
sec_info->entry[i].new_offset + 4 - cie_offset, buf);
buf += 4;
- fde_width = get_DW_EH_PE_width (sec_info->entry[i].fde_encoding,
- ptr_size);
- switch (fde_width)
- {
- case 2: value = bfd_get_16 (abfd, buf); break;
- case 4: value = bfd_get_32 (abfd, buf); break;
- case 8: value = bfd_get_64 (abfd, buf); break;
- default: BFD_FAIL ();
- }
- address = value;
- switch (sec_info->entry[i].fde_encoding & 0xf0)
+ width = get_DW_EH_PE_width (sec_info->entry[i].fde_encoding,
+ ptr_size);
+ address = value = read_value (abfd, buf, width);
+ if (value)
{
- case DW_EH_PE_indirect:
- case DW_EH_PE_textrel:
- BFD_ASSERT (hdr_info == NULL);
- break;
- case DW_EH_PE_datarel:
- {
- asection *got = bfd_get_section_by_name (abfd, ".got");
-
- BFD_ASSERT (got != NULL);
- address += got->vma;
- }
- break;
- case DW_EH_PE_pcrel:
- value += (sec_info->entry[i].offset
- - sec_info->entry[i].new_offset);
- address += (sec->output_section->vma + sec->output_offset
+ switch (sec_info->entry[i].fde_encoding & 0xf0)
+ {
+ case DW_EH_PE_indirect:
+ case DW_EH_PE_textrel:
+ BFD_ASSERT (hdr_info == NULL);
+ break;
+ case DW_EH_PE_datarel:
+ {
+ asection *got = bfd_get_section_by_name (abfd, ".got");
+
+ BFD_ASSERT (got != NULL);
+ address += got->vma;
+ }
+ break;
+ case DW_EH_PE_pcrel:
+ value += (sec_info->entry[i].offset
+ - sec_info->entry[i].new_offset);
+ address += (sec->output_section->vma + sec->output_offset
+ + sec_info->entry[i].offset + 8);
+ break;
+ }
+ if (sec_info->entry[i].make_relative)
+ value -= (sec->output_section->vma + sec->output_offset
+ sec_info->entry[i].new_offset + 8);
- break;
- }
- if (sec_info->entry[i].make_relative)
- value -= (sec->output_section->vma + sec->output_offset
- + sec_info->entry[i].new_offset + 8);
- switch (fde_width)
- {
- case 2: bfd_put_16 (abfd, value, buf); break;
- case 4: bfd_put_32 (abfd, value, buf); break;
- case 8: bfd_put_64 (abfd, value, buf); break;
+ write_value (abfd, buf, value, width);
}
if (hdr_info)
= (sec->output_section->vma + sec->output_offset
+ sec_info->entry[i].new_offset);
}
+
+ if ((sec_info->entry[i].lsda_encoding & 0xf0) == DW_EH_PE_pcrel
+ || sec_info->entry[i].make_lsda_relative)
+ {
+ buf += sec_info->entry[i].lsda_offset;
+ width = get_DW_EH_PE_width (sec_info->entry[i].lsda_encoding,
+ ptr_size);
+ value = read_value (abfd, buf, width);
+ if (value)
+ {
+ if ((sec_info->entry[i].lsda_encoding & 0xf0)
+ == DW_EH_PE_pcrel)
+ value += (sec_info->entry[i].offset
+ - sec_info->entry[i].new_offset);
+ else if (sec_info->entry[i].make_lsda_relative)
+ value -= (sec->output_section->vma + sec->output_offset
+ + sec_info->entry[i].new_offset + 8
+ + sec_info->entry[i].lsda_offset);
+ write_value (abfd, buf, value, width);
+ }
+ }
}
+ else
+ /* Terminating FDE must be at the end of .eh_frame section only. */
+ BFD_ASSERT (i == sec_info->count - 1);
BFD_ASSERT (p == contents + sec_info->entry[i].new_offset);
memmove (p, contents + sec_info->entry[i].offset,
p += sec_info->entry[i].size;
}
+ /* FIXME: Once _bfd_elf_discard_section_eh_frame will be able to
+ shrink sections to zero size, this won't be needed any more. */
+ if (p == contents && sec->_cooked_size == 16)
+ {
+ bfd_put_32 (abfd, 12, p); /* Fake CIE length */
+ bfd_put_32 (abfd, 0, p + 4); /* Fake CIE id */
+ p[8] = 1; /* Fake CIE version */
+ memset (p + 9, 0, 7); /* Fake CIE augmentation, 3xleb128
+ and 3xDW_CFA_nop as pad */
+ p += 16;
+ }
+
BFD_ASSERT ((bfd_size_type) (p - contents) == sec->_cooked_size);
return bfd_set_section_contents (abfd, sec->output_section,
== ELF_INFO_TYPE_EH_FRAME_HDR);
hdr_info = (struct eh_frame_hdr_info *)
elf_section_data (sec)->sec_info;
+ if (hdr_info->strip)
+ return true;
+
size = EH_FRAME_HDR_SIZE;
if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
size += 4 + hdr_info->fde_count * 8;