This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH] Better merging of CIEs in .eh_frame optimizations
- From: Jakub Jelinek <jakub at redhat dot com>
- To: binutils at sources dot redhat dot com
- Date: Thu, 16 Nov 2006 14:44:04 +0100
- Subject: [PATCH] Better merging of CIEs in .eh_frame optimizations
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
Hi!
As has been mentioned in the
http://sources.redhat.com/ml/binutils/2006-10/msg00044.html
thread, elf-eh-frame.c required FDEs referencing last CIE, not arbitrary
CIE within the same input section, which is easily broken with
.cfi_* directive generated .eh_frame.
The following patch fixes that and in addition to that puts kept CIEs
into a hash table, so that all identical CIEs can be merged, not just
all consecutive identical ones. With this patch i686 glibc's .eh_frame
shrunk by 7.5%, without this patch it contained 184 CIEs, with this
patch only 4 (FYI .debug_frame has 1093 CIEs, so some merging was successful
even before this patch). Tested with make check on i686 glibc and
binutils i386-linux and x86_64-linux make check.
Ok to commit?
2006-11-15 Jakub Jelinek <jakub@redhat.com>
* elf-eh-frame.c (struct cie): New type.
(cie_compare): Removed.
(cie_eq, cie_hash, cie_compute_hash): New functions.
(_bfd_elf_discard_section_eh_frame): Rewrite not to rely on FDEs
pointing only to last CIE and allow merging of any duplicate CIEs,
not just duplicate consecutive CIEs.
(_bfd_elf_discard_section_eh_frame_hdr): Delete cies hash table.
* elf-bfd.h (struct cie_header, struct cie): Removed.
(struct eh_frame_sec_info): Remove alloced field.
(struct eh_frame_hdr_info): Remove last_cie, last_cie_sec
and last_cie_inf fields. Add cies field.
* ld-elf/eh5.d: New test.
* ld-elf/eh5.s: New file.
* ld-elf/eh5a.s: New file.
* ld-elf/eh5b.s: New file.
--- bfd/elf-eh-frame.c.jj 2006-10-03 15:15:39.000000000 +0200
+++ bfd/elf-eh-frame.c 2006-11-15 20:16:10.000000000 +0100
@@ -26,6 +26,30 @@
#define EH_FRAME_HDR_SIZE 8
+struct cie
+{
+ unsigned int length;
+ unsigned int hash;
+ unsigned char version;
+ char augmentation[20];
+ bfd_vma code_align;
+ bfd_signed_vma data_align;
+ bfd_vma ra_column;
+ bfd_vma augmentation_size;
+ struct elf_link_hash_entry *personality;
+ asection *output_sec;
+ struct eh_cie_fde *cie_inf;
+ unsigned char per_encoding;
+ unsigned char lsda_encoding;
+ unsigned char fde_encoding;
+ unsigned char initial_insn_length;
+ unsigned char make_relative;
+ unsigned char make_lsda_relative;
+ unsigned char initial_instructions[50];
+};
+
+
+
/* If *ITER hasn't reached END yet, read the next byte into *RESULT and
move onto the next byte. Return true on success. */
@@ -182,10 +206,14 @@ write_value (bfd *abfd, bfd_byte *buf, b
/* Return zero if C1 and C2 CIEs can be merged. */
-static
-int cie_compare (struct cie *c1, struct cie *c2)
+static int
+cie_eq (const void *e1, const void *e2)
{
- if (c1->hdr.length == c2->hdr.length
+ const struct cie *c1 = e1;
+ const struct cie *c2 = e2;
+
+ if (c1->hash == c2->hash
+ && c1->length == c2->length
&& c1->version == c2->version
&& strcmp (c1->augmentation, c2->augmentation) == 0
&& strcmp (c1->augmentation, "eh") != 0
@@ -194,6 +222,7 @@ int cie_compare (struct cie *c1, struct
&& c1->ra_column == c2->ra_column
&& c1->augmentation_size == c2->augmentation_size
&& c1->personality == c2->personality
+ && c1->output_sec == c2->output_sec
&& c1->per_encoding == c2->per_encoding
&& c1->lsda_encoding == c2->lsda_encoding
&& c1->fde_encoding == c2->fde_encoding
@@ -201,9 +230,38 @@ int cie_compare (struct cie *c1, struct
&& memcmp (c1->initial_instructions,
c2->initial_instructions,
c1->initial_insn_length) == 0)
- return 0;
+ return 1;
+
+ return 0;
+}
- return 1;
+static hashval_t
+cie_hash (const void *e)
+{
+ const struct cie *c = e;
+ return c->hash;
+}
+
+static hashval_t
+cie_compute_hash (struct cie *c)
+{
+ hashval_t h = 0;
+ h = iterative_hash_object (c->length, h);
+ h = iterative_hash_object (c->version, h);
+ h = iterative_hash (c->augmentation, strlen (c->augmentation) + 1, h);
+ h = iterative_hash_object (c->code_align, h);
+ h = iterative_hash_object (c->data_align, h);
+ h = iterative_hash_object (c->ra_column, h);
+ h = iterative_hash_object (c->augmentation_size, h);
+ h = iterative_hash_object (c->personality, h);
+ h = iterative_hash_object (c->output_sec, h);
+ h = iterative_hash_object (c->per_encoding, h);
+ h = iterative_hash_object (c->lsda_encoding, h);
+ h = iterative_hash_object (c->fde_encoding, h);
+ h = iterative_hash_object (c->initial_insn_length, h);
+ h = iterative_hash (c->initial_instructions, c->initial_insn_length, h);
+ c->hash = h;
+ return h;
}
/* Return the number of extra bytes that we'll be inserting into
@@ -383,15 +441,24 @@ _bfd_elf_discard_section_eh_frame
while (0)
bfd_byte *ehbuf = NULL, *buf;
- bfd_byte *last_cie, *last_fde;
- struct eh_cie_fde *ent, *last_cie_inf, *this_inf;
- struct cie_header hdr;
- struct cie cie;
+ bfd_byte *last_fde;
+ struct eh_cie_fde *ent, *this_inf;
+ unsigned int hdr_length, hdr_id;
+ struct extended_cie
+ {
+ struct cie cie;
+ unsigned int offset;
+ unsigned int usage_count;
+ unsigned int entry;
+ } *ecies = NULL, *ecie;
+ unsigned int ecie_count = 0, ecie_alloced = 0;
+ struct cie *cie;
struct elf_link_hash_table *htab;
struct eh_frame_hdr_info *hdr_info;
struct eh_frame_sec_info *sec_info = NULL;
- unsigned int cie_usage_count, offset;
+ unsigned int offset;
unsigned int ptr_size;
+ unsigned int entry_alloced;
if (sec->size == 0)
{
@@ -409,6 +476,9 @@ _bfd_elf_discard_section_eh_frame
htab = elf_hash_table (info);
hdr_info = &htab->eh_info;
+ if (hdr_info->cies == NULL && !info->relocatable)
+ hdr_info->cies = htab_try_create (1, cie_hash, cie_eq, free);
+
/* Read the frame unwind information from abfd. */
REQUIRE (bfd_malloc_and_get_section (abfd, sec, &ehbuf));
@@ -431,15 +501,11 @@ _bfd_elf_discard_section_eh_frame
REQUIRE (ptr_size != 0);
buf = ehbuf;
- last_cie = NULL;
- last_cie_inf = NULL;
- memset (&cie, 0, sizeof (cie));
- cie_usage_count = 0;
sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
+ 99 * sizeof (struct eh_cie_fde));
REQUIRE (sec_info);
- sec_info->alloced = 100;
+ entry_alloced = 100;
#define ENSURE_NO_RELOCS(buf) \
REQUIRE (!(cookie->rel < cookie->relend \
@@ -466,116 +532,80 @@ _bfd_elf_discard_section_eh_frame
bfd_size_type length;
unsigned int set_loc_count;
- if (sec_info->count == sec_info->alloced)
+ if (sec_info->count == entry_alloced)
{
- struct eh_cie_fde *old_entry = sec_info->entry;
sec_info = bfd_realloc (sec_info,
sizeof (struct eh_frame_sec_info)
- + ((sec_info->alloced + 99)
+ + ((entry_alloced + 99)
* sizeof (struct eh_cie_fde)));
REQUIRE (sec_info);
- memset (&sec_info->entry[sec_info->alloced], 0,
+ memset (&sec_info->entry[entry_alloced], 0,
100 * sizeof (struct eh_cie_fde));
- sec_info->alloced += 100;
-
- /* Now fix any pointers into the array. */
- if (last_cie_inf >= old_entry
- && last_cie_inf < old_entry + sec_info->count)
- last_cie_inf = sec_info->entry + (last_cie_inf - old_entry);
+ entry_alloced += 100;
}
this_inf = sec_info->entry + sec_info->count;
last_fde = buf;
- /* If we are at the end of the section, we still need to decide
- on whether to output or discard last encountered CIE (if any). */
+
if ((bfd_size_type) (buf - ehbuf) == sec->size)
- {
- hdr.length = 0;
- hdr.id = (unsigned int) -1;
- end = buf;
- }
- else
- {
- /* Read the length of the entry. */
- REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4));
- hdr.length = bfd_get_32 (abfd, buf - 4);
+ break;
- /* 64-bit .eh_frame is not supported. */
- REQUIRE (hdr.length != 0xffffffff);
+ /* Read the length of the entry. */
+ REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4));
+ hdr_length = bfd_get_32 (abfd, buf - 4);
- /* The CIE/FDE must be fully contained in this input section. */
- REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr.length <= sec->size);
- end = buf + hdr.length;
+ /* 64-bit .eh_frame is not supported. */
+ REQUIRE (hdr_length != 0xffffffff);
- this_inf->offset = last_fde - ehbuf;
- this_inf->size = 4 + hdr.length;
+ /* The CIE/FDE must be fully contained in this input section. */
+ REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr_length <= sec->size);
+ end = buf + hdr_length;
- if (hdr.length == 0)
- {
- /* A zero-length CIE should only be found at the end of
- the section. */
- REQUIRE ((bfd_size_type) (buf - ehbuf) == sec->size);
- ENSURE_NO_RELOCS (buf);
- sec_info->count++;
- /* Now just finish last encountered CIE processing and break
- the loop. */
- hdr.id = (unsigned int) -1;
- }
- else
- {
- REQUIRE (skip_bytes (&buf, end, 4));
- hdr.id = bfd_get_32 (abfd, buf - 4);
- REQUIRE (hdr.id != (unsigned int) -1);
- }
+ this_inf->offset = last_fde - ehbuf;
+ this_inf->size = 4 + hdr_length;
+
+ if (hdr_length == 0)
+ {
+ /* A zero-length CIE should only be found at the end of
+ the section. */
+ REQUIRE ((bfd_size_type) (buf - ehbuf) == sec->size);
+ ENSURE_NO_RELOCS (buf);
+ sec_info->count++;
+ break;
}
- if (hdr.id == 0 || hdr.id == (unsigned int) -1)
+ REQUIRE (skip_bytes (&buf, end, 4));
+ hdr_id = bfd_get_32 (abfd, buf - 4);
+
+ if (hdr_id == 0)
{
unsigned int initial_insn_length;
/* CIE */
- if (last_cie != NULL)
+ this_inf->cie = 1;
+
+ if (ecie_count == ecie_alloced)
{
- /* Now check if this CIE is identical to the last CIE,
- in which case we can remove it provided we adjust
- all FDEs. Also, it can be removed if we have removed
- all FDEs using it. */
- if ((!info->relocatable
- && hdr_info->last_cie_sec
- && (sec->output_section
- == hdr_info->last_cie_sec->output_section)
- && cie_compare (&cie, &hdr_info->last_cie) == 0)
- || cie_usage_count == 0)
- last_cie_inf->removed = 1;
- else
- {
- hdr_info->last_cie = cie;
- hdr_info->last_cie_sec = sec;
- last_cie_inf->make_relative = cie.make_relative;
- last_cie_inf->make_lsda_relative = cie.make_lsda_relative;
- last_cie_inf->per_encoding_relative
- = (cie.per_encoding & 0x70) == DW_EH_PE_pcrel;
- }
+ ecies = bfd_realloc (ecies,
+ (ecie_alloced + 20) * sizeof (*ecies));
+ REQUIRE (ecies);
+ memset (&ecies[ecie_alloced], 0, 20 * sizeof (*ecies));
+ ecie_alloced += 20;
}
- if (hdr.id == (unsigned int) -1)
- break;
-
- last_cie_inf = this_inf;
- this_inf->cie = 1;
-
- cie_usage_count = 0;
- memset (&cie, 0, sizeof (cie));
- cie.hdr = hdr;
+ cie = &ecies[ecie_count].cie;
+ ecies[ecie_count].offset = this_inf->offset;
+ ecies[ecie_count++].entry = sec_info->count;
+ cie->length = hdr_length;
start = buf;
- REQUIRE (read_byte (&buf, end, &cie.version));
+ REQUIRE (read_byte (&buf, end, &cie->version));
/* Cannot handle unknown versions. */
- REQUIRE (cie.version == 1 || cie.version == 3);
- REQUIRE (strlen ((char *) buf) < sizeof (cie.augmentation));
+ REQUIRE (cie->version == 1 || cie->version == 3);
+ REQUIRE (strlen ((char *) buf) < sizeof (cie->augmentation));
- strcpy (cie.augmentation, (char *) buf);
+ strcpy (cie->augmentation, (char *) buf);
buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
ENSURE_NO_RELOCS (buf);
if (buf[0] == 'e' && buf[1] == 'h')
@@ -587,26 +617,26 @@ _bfd_elf_discard_section_eh_frame
REQUIRE (skip_bytes (&buf, end, ptr_size));
SKIP_RELOCS (buf);
}
- REQUIRE (read_uleb128 (&buf, end, &cie.code_align));
- REQUIRE (read_sleb128 (&buf, end, &cie.data_align));
- if (cie.version == 1)
+ REQUIRE (read_uleb128 (&buf, end, &cie->code_align));
+ REQUIRE (read_sleb128 (&buf, end, &cie->data_align));
+ if (cie->version == 1)
{
REQUIRE (buf < end);
- cie.ra_column = *buf++;
+ cie->ra_column = *buf++;
}
else
- REQUIRE (read_uleb128 (&buf, end, &cie.ra_column));
+ REQUIRE (read_uleb128 (&buf, end, &cie->ra_column));
ENSURE_NO_RELOCS (buf);
- cie.lsda_encoding = DW_EH_PE_omit;
- cie.fde_encoding = DW_EH_PE_omit;
- cie.per_encoding = DW_EH_PE_omit;
- aug = cie.augmentation;
+ cie->lsda_encoding = DW_EH_PE_omit;
+ cie->fde_encoding = DW_EH_PE_omit;
+ cie->per_encoding = DW_EH_PE_omit;
+ aug = cie->augmentation;
if (aug[0] != 'e' || aug[1] != 'h')
{
if (*aug == 'z')
{
aug++;
- REQUIRE (read_uleb128 (&buf, end, &cie.augmentation_size));
+ REQUIRE (read_uleb128 (&buf, end, &cie->augmentation_size));
ENSURE_NO_RELOCS (buf);
}
@@ -614,14 +644,14 @@ _bfd_elf_discard_section_eh_frame
switch (*aug++)
{
case 'L':
- REQUIRE (read_byte (&buf, end, &cie.lsda_encoding));
+ REQUIRE (read_byte (&buf, end, &cie->lsda_encoding));
ENSURE_NO_RELOCS (buf);
- REQUIRE (get_DW_EH_PE_width (cie.lsda_encoding, ptr_size));
+ REQUIRE (get_DW_EH_PE_width (cie->lsda_encoding, ptr_size));
break;
case 'R':
- REQUIRE (read_byte (&buf, end, &cie.fde_encoding));
+ REQUIRE (read_byte (&buf, end, &cie->fde_encoding));
ENSURE_NO_RELOCS (buf);
- REQUIRE (get_DW_EH_PE_width (cie.fde_encoding, ptr_size));
+ REQUIRE (get_DW_EH_PE_width (cie->fde_encoding, ptr_size));
break;
case 'S':
break;
@@ -629,11 +659,11 @@ _bfd_elf_discard_section_eh_frame
{
int per_width;
- REQUIRE (read_byte (&buf, end, &cie.per_encoding));
- per_width = get_DW_EH_PE_width (cie.per_encoding,
+ REQUIRE (read_byte (&buf, end, &cie->per_encoding));
+ per_width = get_DW_EH_PE_width (cie->per_encoding,
ptr_size);
REQUIRE (per_width);
- if ((cie.per_encoding & 0xf0) == DW_EH_PE_aligned)
+ if ((cie->per_encoding & 0xf0) == DW_EH_PE_aligned)
{
length = -(buf - ehbuf) & (per_width - 1);
REQUIRE (skip_bytes (&buf, end, length));
@@ -663,7 +693,7 @@ _bfd_elf_discard_section_eh_frame
h = (struct elf_link_hash_entry *)
h->root.u.i.link;
- cie.personality = h;
+ cie->personality = h;
}
/* Cope with MIPS-style composite relocations. */
do
@@ -671,6 +701,7 @@ _bfd_elf_discard_section_eh_frame
while (GET_RELOC (buf) != NULL);
}
REQUIRE (skip_bytes (&buf, end, per_width));
+ REQUIRE (cie->personality);
}
break;
default:
@@ -686,18 +717,18 @@ _bfd_elf_discard_section_eh_frame
->elf_backend_can_make_relative_eh_frame
(abfd, info, sec)))
{
- if ((cie.fde_encoding & 0xf0) == DW_EH_PE_absptr)
- cie.make_relative = 1;
+ if ((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr)
+ cie->make_relative = 1;
/* If the CIE doesn't already have an 'R' entry, it's fairly
easy to add one, provided that there's no aligned data
after the augmentation string. */
- else if (cie.fde_encoding == DW_EH_PE_omit
- && (cie.per_encoding & 0xf0) != DW_EH_PE_aligned)
+ else if (cie->fde_encoding == DW_EH_PE_omit
+ && (cie->per_encoding & 0xf0) != DW_EH_PE_aligned)
{
- if (*cie.augmentation == 0)
+ if (*cie->augmentation == 0)
this_inf->add_augmentation_size = 1;
this_inf->add_fde_encoding = 1;
- cie.make_relative = 1;
+ cie->make_relative = 1;
}
}
@@ -705,30 +736,36 @@ _bfd_elf_discard_section_eh_frame
&& (get_elf_backend_data (abfd)
->elf_backend_can_make_lsda_relative_eh_frame
(abfd, info, sec))
- && (cie.lsda_encoding & 0xf0) == DW_EH_PE_absptr)
- cie.make_lsda_relative = 1;
+ && (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)
- cie.fde_encoding = DW_EH_PE_absptr;
+ if (cie->fde_encoding == DW_EH_PE_omit)
+ cie->fde_encoding = DW_EH_PE_absptr;
initial_insn_length = end - buf;
- if (initial_insn_length <= 50)
+ if (initial_insn_length <= sizeof (cie->initial_instructions))
{
- cie.initial_insn_length = initial_insn_length;
- memcpy (cie.initial_instructions, buf, initial_insn_length);
+ cie->initial_insn_length = initial_insn_length;
+ memcpy (cie->initial_instructions, buf, initial_insn_length);
}
insns = buf;
buf += initial_insn_length;
ENSURE_NO_RELOCS (buf);
- last_cie = last_fde;
}
else
{
- /* Ensure this FDE uses the last CIE encountered. */
- REQUIRE (last_cie);
- REQUIRE (hdr.id == (unsigned int) (buf - 4 - last_cie));
+ /* Find the corresponding CIE. */
+ unsigned int cie_offset = this_inf->offset + 4 - hdr_id;
+ for (ecie = ecies; ecie < ecies + ecie_count; ++ecie)
+ if (cie_offset == ecie->offset)
+ break;
+
+ /* Ensure this FDE references one of the CIEs in this input
+ section. */
+ REQUIRE (ecie != ecies + ecie_count);
+ cie = &ecie->cie;
ENSURE_NO_RELOCS (buf);
REQUIRE (GET_RELOC (buf));
@@ -740,9 +777,9 @@ _bfd_elf_discard_section_eh_frame
else
{
if (info->shared
- && (((cie.fde_encoding & 0xf0) == DW_EH_PE_absptr
- && cie.make_relative == 0)
- || (cie.fde_encoding & 0xf0) == DW_EH_PE_aligned))
+ && (((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr
+ && cie->make_relative == 0)
+ || (cie->fde_encoding & 0xf0) == DW_EH_PE_aligned))
{
/* If a shared library uses absolute pointers
which we cannot turn into PC relative,
@@ -750,16 +787,18 @@ _bfd_elf_discard_section_eh_frame
since it is affected by runtime relocations. */
hdr_info->table = FALSE;
}
- cie_usage_count++;
+ ecie->usage_count++;
hdr_info->fde_count++;
+ this_inf->cie_inf = (void *) (ecie - ecies);
}
+
/* Skip the initial location and address range. */
start = buf;
- length = get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
+ length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
REQUIRE (skip_bytes (&buf, end, 2 * length));
/* Skip the augmentation size, if present. */
- if (cie.augmentation[0] == 'z')
+ if (cie->augmentation[0] == 'z')
REQUIRE (read_uleb128 (&buf, end, &length));
else
length = 0;
@@ -767,12 +806,12 @@ _bfd_elf_discard_section_eh_frame
/* Of the supported augmentation characters above, only 'L'
adds augmentation data to the FDE. This code would need to
be adjusted if any future augmentations do the same thing. */
- if (cie.lsda_encoding != DW_EH_PE_omit)
+ if (cie->lsda_encoding != DW_EH_PE_omit)
{
this_inf->lsda_offset = buf - start;
/* If there's no 'z' augmentation, we don't know where the
CFA insns begin. Assume no padding. */
- if (cie.augmentation[0] != 'z')
+ if (cie->augmentation[0] != 'z')
length = end - buf;
}
@@ -780,14 +819,14 @@ _bfd_elf_discard_section_eh_frame
REQUIRE (skip_bytes (&buf, end, length));
insns = buf;
- buf = last_fde + 4 + hdr.length;
+ buf = last_fde + 4 + hdr_length;
SKIP_RELOCS (buf);
}
/* Try to interpret the CFA instructions and find the first
padding nop. Shrink this_inf's size so that it doesn't
include the padding. */
- length = get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
+ length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
set_loc_count = 0;
insns_end = skip_non_nops (insns, end, length, &set_loc_count);
/* If we don't understand the CFA instructions, we can't know
@@ -798,9 +837,14 @@ _bfd_elf_discard_section_eh_frame
|| (set_loc_count && this_inf->cie))
goto free_no_table;
this_inf->size -= end - insns_end;
+ if (insns_end != end && this_inf->cie)
+ {
+ cie->initial_insn_length -= end - insns_end;
+ cie->length -= end - insns_end;
+ }
if (set_loc_count
- && ((cie.fde_encoding & 0xf0) == DW_EH_PE_pcrel
- || cie.make_relative))
+ && ((cie->fde_encoding & 0xf0) == DW_EH_PE_pcrel
+ || cie->make_relative))
{
unsigned int cnt;
bfd_byte *p;
@@ -819,28 +863,66 @@ _bfd_elf_discard_section_eh_frame
}
}
- this_inf->fde_encoding = cie.fde_encoding;
- this_inf->lsda_encoding = cie.lsda_encoding;
+ this_inf->fde_encoding = cie->fde_encoding;
+ this_inf->lsda_encoding = cie->lsda_encoding;
sec_info->count++;
}
elf_section_data (sec)->sec_info = sec_info;
sec->sec_info_type = ELF_INFO_TYPE_EH_FRAME;
+ /* Look at all CIEs in this section and determine which can be
+ removed as unused, which can be merged with previous duplicate
+ CIEs and which need to be kept. */
+ for (ecie = ecies; ecie < ecies + ecie_count; ++ecie)
+ {
+ if (ecie->usage_count == 0)
+ {
+ sec_info->entry[ecie->entry].removed = 1;
+ continue;
+ }
+ ecie->cie.output_sec = sec->output_section;
+ ecie->cie.cie_inf = sec_info->entry + ecie->entry;
+ cie_compute_hash (&ecie->cie);
+ if (hdr_info->cies != NULL)
+ {
+ void **loc = htab_find_slot_with_hash (hdr_info->cies, &ecie->cie,
+ ecie->cie.hash, INSERT);
+ if (loc != NULL)
+ {
+ if (*loc != HTAB_EMPTY_ENTRY)
+ {
+ sec_info->entry[ecie->entry].removed = 1;
+ ecie->cie.cie_inf = ((struct cie *) *loc)->cie_inf;
+ continue;
+ }
+
+ *loc = malloc (sizeof (struct cie));
+ if (*loc == NULL)
+ *loc = HTAB_DELETED_ENTRY;
+ else
+ memcpy (*loc, &ecie->cie, sizeof (struct cie));
+ }
+ }
+ ecie->cie.cie_inf->make_relative = ecie->cie.make_relative;
+ ecie->cie.cie_inf->make_lsda_relative = ecie->cie.make_lsda_relative;
+ ecie->cie.cie_inf->per_encoding_relative
+ = (ecie->cie.per_encoding & 0x70) == DW_EH_PE_pcrel;
+ }
+
/* Ok, now we can assign new offsets. */
offset = 0;
- last_cie_inf = hdr_info->last_cie_inf;
for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
if (!ent->removed)
{
- if (ent->cie)
- last_cie_inf = ent;
- else
- ent->cie_inf = last_cie_inf;
+ if (!ent->cie)
+ {
+ ecie = ecies + (unsigned long) ent->cie_inf;
+ ent->cie_inf = ecie->cie.cie_inf;
+ }
ent->new_offset = offset;
offset += size_of_output_cie_fde (ent, ptr_size);
}
- hdr_info->last_cie_inf = last_cie_inf;
/* Resize the sec as needed. */
sec->rawsize = sec->size;
@@ -849,6 +931,8 @@ _bfd_elf_discard_section_eh_frame
sec->flags |= SEC_EXCLUDE;
free (ehbuf);
+ if (ecies)
+ free (ecies);
return offset != sec->rawsize;
free_no_table:
@@ -856,8 +940,9 @@ free_no_table:
free (ehbuf);
if (sec_info)
free (sec_info);
+ if (ecies)
+ free (ecies);
hdr_info->table = FALSE;
- hdr_info->last_cie.hdr.length = 0;
return FALSE;
#undef REQUIRE
@@ -876,6 +961,13 @@ _bfd_elf_discard_section_eh_frame_hdr (b
htab = elf_hash_table (info);
hdr_info = &htab->eh_info;
+
+ if (hdr_info->cies != NULL)
+ {
+ htab_delete (hdr_info->cies);
+ hdr_info->cies = NULL;
+ }
+
sec = hdr_info->hdr_sec;
if (sec == NULL)
return FALSE;
--- bfd/elf-bfd.h.jj 2006-11-03 08:18:12.000000000 +0100
+++ bfd/elf-bfd.h 2006-11-15 16:51:56.000000000 +0100
@@ -261,31 +261,6 @@ struct elf_link_loaded_list
};
/* Structures used by the eh_frame optimization code. */
-struct cie_header
-{
- unsigned int length;
- unsigned int id;
-};
-
-struct cie
-{
- struct cie_header hdr;
- unsigned char version;
- char augmentation[20];
- bfd_vma code_align;
- bfd_signed_vma data_align;
- bfd_vma ra_column;
- bfd_vma augmentation_size;
- struct elf_link_hash_entry *personality;
- unsigned char per_encoding;
- unsigned char lsda_encoding;
- unsigned char fde_encoding;
- unsigned char initial_insn_length;
- unsigned char make_relative;
- unsigned char make_lsda_relative;
- unsigned char initial_instructions[50];
-};
-
struct eh_cie_fde
{
/* For FDEs, this points to the CIE used. */
@@ -310,7 +285,6 @@ struct eh_cie_fde
struct eh_frame_sec_info
{
unsigned int count;
- unsigned int alloced;
struct eh_cie_fde entry[1];
};
@@ -320,11 +294,11 @@ struct eh_frame_array_ent
bfd_vma fde;
};
+struct htab;
+
struct eh_frame_hdr_info
{
- struct cie last_cie;
- asection *last_cie_sec;
- struct eh_cie_fde *last_cie_inf;
+ struct htab *cies;
asection *hdr_sec;
unsigned int fde_count, array_count;
struct eh_frame_array_ent *array;
--- ld/testsuite/ld-elf/eh5.d.jj 2006-11-16 10:56:35.000000000 +0100
+++ ld/testsuite/ld-elf/eh5.d 2006-11-16 12:09:11.000000000 +0100
@@ -0,0 +1,161 @@
+#source: eh5.s
+#source: eh5a.s
+#source: eh5b.s
+#ld:
+#readelf: -wf
+#target: x86_64-*-* i?86-*-*
+
+The section .eh_frame contains:
+
+00000000 0000001[04] 00000000 CIE
+ Version: 1
+ Augmentation: "zR"
+ Code alignment factor: 1
+ Data alignment factor: .*
+ Return address column: .*
+ Augmentation data: 1b
+
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+#...
+0000001[48] 00000014 0000001[8c] FDE cie=00000000 pc=.*
+ DW_CFA_advance_loc: 4 to .*
+ DW_CFA_def_cfa: r0 ofs 16
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+
+000000(2c|30) 00000014 00000000 CIE
+ Version: 1
+ Augmentation: "zPR"
+ Code alignment factor: 1
+ Data alignment factor: .*
+ Return address column: .*
+ Augmentation data: 03 .. .. .. .. 1b
+
+ DW_CFA_nop
+
+0000004[48] 00000014 0000001c FDE cie=000000(2c|30) pc=.*
+ DW_CFA_advance_loc: 4 to .*
+ DW_CFA_def_cfa: r0 ofs 16
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+
+000000(5c|60) 00000014 0000006[04] FDE cie=00000000 pc=.*
+ DW_CFA_advance_loc: 4 to .*
+ DW_CFA_def_cfa: r0 ofs 16
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+
+0000007[48] 0000001[8c] 00000000 CIE
+ Version: 1
+ Augmentation: "zPLR"
+ Code alignment factor: 1
+ Data alignment factor: .*
+ Return address column: .*
+ Augmentation data: 03 .. .. .. .. 0c 1b
+
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+#...
+0000009[08] 0000001c 0000002[04] FDE cie=0000007[48] pc=.*
+ Augmentation data: (ef be ad de 00 00 00 00|00 00 00 00 de ad be ef)
+
+ DW_CFA_advance_loc: 4 to .*
+ DW_CFA_def_cfa: r0 ofs 16
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+
+000000b[08] 0000001[04] 00000000 CIE
+ Version: 1
+ Augmentation: "zR"
+ Code alignment factor: 1
+ Data alignment factor: .*
+ Return address column: .*
+ Augmentation data: 1b
+
+ DW_CFA_def_cfa: r0 ofs 16
+#...
+000000(c4|d0) 0000001[04] 0000001[8c] FDE cie=000000b[08] pc=.*
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+#...
+000000[de]8 00000014 00000000 CIE
+ Version: 1
+ Augmentation: "zPR"
+ Code alignment factor: 1
+ Data alignment factor: .*
+ Return address column: .*
+ Augmentation data: 03 .. .. .. .. 1b
+
+ DW_CFA_nop
+
+00000(0f|10)0 00000014 0000001c FDE cie=000000[de]8 pc=.*
+ DW_CFA_advance_loc: 4 to .*
+ DW_CFA_def_cfa: r0 ofs 16
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+
+000001[01]8 0000001[04] 000000(5c|64) FDE cie=000000b[08] pc=.*
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+#...
+000001(1c|30) 0000001[8c] 00000000 CIE
+ Version: 1
+ Augmentation: "zPLR"
+ Code alignment factor: 1
+ Data alignment factor: .*
+ Return address column: .*
+ Augmentation data: 03 .. .. .. .. 0c 1b
+
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+#...
+000001(38|50) 0000001c 0000002[04] FDE cie=000001(1c|30) pc=.*
+ Augmentation data: (ef be ad de 00 00 00 00|00 00 00 00 de ad be ef)
+
+ DW_CFA_advance_loc: 4 to .*
+ DW_CFA_def_cfa: r0 ofs 16
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+
+000001(58|70) 00000014 000001(5c|74) FDE cie=00000000 pc=.*
+ DW_CFA_advance_loc: 4 to .*
+ DW_CFA_def_cfa: r0 ofs 16
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+
+000001(70|88) 00000014 000001(48|5c) FDE cie=000000(2c|30) pc=.*
+ DW_CFA_advance_loc: 4 to .*
+ DW_CFA_def_cfa: r0 ofs 16
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+
+000001(88|a0) 00000014 000001(8c|a4) FDE cie=00000000 pc=.*
+ DW_CFA_advance_loc: 4 to .*
+ DW_CFA_def_cfa: r0 ofs 16
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+
+000001(a0|b8) 0000001c 000001(30|44) FDE cie=0000007[48] pc=.*
+ Augmentation data: (ef be ad de 00 00 00 00|00 00 00 00 de ad be ef)
+
+ DW_CFA_advance_loc: 4 to .*
+ DW_CFA_def_cfa: r0 ofs 16
+ DW_CFA_nop
+ DW_CFA_nop
+ DW_CFA_nop
+
--- ld/testsuite/ld-elf/eh5.s.jj 2006-11-16 10:45:45.000000000 +0100
+++ ld/testsuite/ld-elf/eh5.s 2006-11-16 10:45:36.000000000 +0100
@@ -0,0 +1,29 @@
+ .text
+ .cfi_startproc simple
+ .long 0
+ .cfi_def_cfa 0, 16
+ .long 0
+ .cfi_endproc
+
+ .cfi_startproc simple
+ .cfi_personality 3, my_personality_v0
+ .long 0
+ .cfi_def_cfa 0, 16
+ .cfi_endproc
+
+ .cfi_startproc simple
+ .long 0
+ .cfi_def_cfa 0, 16
+ .long 0
+ .cfi_endproc
+
+ .cfi_startproc simple
+ .cfi_personality 3, my_personality_v0
+ .cfi_lsda 12, 0xdeadbeef
+ .long 0
+ .cfi_def_cfa 0, 16
+ .cfi_endproc
+
+ .globl my_personality_v0
+my_personality_v0:
+ .long 0
--- ld/testsuite/ld-elf/eh5a.s.jj 2006-11-16 10:45:48.000000000 +0100
+++ ld/testsuite/ld-elf/eh5a.s 2006-11-16 10:55:06.000000000 +0100
@@ -0,0 +1,27 @@
+ .text
+ .cfi_startproc simple
+ .cfi_def_cfa 0, 16
+ .long 0
+ .cfi_endproc
+
+ .cfi_startproc simple
+ .cfi_personality 3, my_personality_v1
+ .long 0
+ .cfi_def_cfa 0, 16
+ .cfi_endproc
+
+ .cfi_startproc simple
+ .cfi_def_cfa 0, 16
+ .long 0
+ .cfi_endproc
+
+ .cfi_startproc simple
+ .cfi_personality 3, my_personality_v1
+ .cfi_lsda 12, 0xdeadbeef
+ .long 0
+ .cfi_def_cfa 0, 16
+ .cfi_endproc
+
+ .globl my_personality_v1
+my_personality_v1:
+ .long 0
--- ld/testsuite/ld-elf/eh5b.s.jj 2006-11-16 10:55:15.000000000 +0100
+++ ld/testsuite/ld-elf/eh5b.s 2006-11-16 11:09:52.000000000 +0100
@@ -0,0 +1,29 @@
+ .text
+ .cfi_startproc simple
+ .long 0
+ .cfi_def_cfa 0, 16
+ .long 0
+ .cfi_endproc
+
+ .cfi_startproc simple
+ .cfi_personality 3, my_personality_v0
+ .long 0
+ .cfi_def_cfa 0, 16
+ .cfi_endproc
+
+ .cfi_startproc simple
+ .long 0
+ .cfi_def_cfa 0, 16
+ .long 0
+ .cfi_endproc
+
+ .cfi_startproc simple
+ .cfi_personality 3, my_personality_v0
+ .cfi_lsda 12, 0xdeadbeef
+ .long 0
+ .cfi_def_cfa 0, 16
+ .cfi_endproc
+
+ .globl _start
+_start:
+ .long 0
Jakub