This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH] Fix DW_CFA_set_loc handling
- From: Jakub Jelinek <jakub at redhat dot com>
- To: binutils at sources dot redhat dot com
- Date: Tue, 26 Sep 2006 14:38:40 +0200
- Subject: [PATCH] Fix DW_CFA_set_loc handling
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
Hi!
Recent http://gcc.gnu.org/PR22313 change (including gcc 4.1 branch) started
(unnecessarily) using DW_CFA_set_loc at the start of all FDEs, but
unfortunately linker .eh_frame optimizations didn't handle that well.
If FDE encoding is pc relative or linker is changing it from absptr
to pc relative, we need to adjust the DW_CFA_set_loc operand accordingly.
Tested with make check on x86_64-linux, visual inspection of x86_64
readelf -wf libc.so.6 (this was seriously broken before) and visual
inspection of a hacked up test for absptr -> pcrel conversion.
Can anyone test this on e.g. mips (which I believe heavily converts from
absolute to relative)?
2006-09-26 Jakub Jelinek <jakub@redhat.com>
* elf-bfd.h (struct eh_cie_fde): Add set_loc pointer.
* elf-eh-frame.c (skip_cfa_op): Fix handling of DW_CFA_advance_loc.
Handle DW_CFA_{remember,restore}_state, DW_CFA_GNU_window_save,
DW_CFA_val_{offset{,_sf},expression}.
(skip_non_nops): Record number of DW_CFA_set_loc ops.
(_bfd_elf_discard_section_eh_frame): Require skip_non_nops recognizes
all ops. If there are any DW_CFA_set_loc ops and they are pcrel
or going to be pcrel, compute set_loc array.
(_bfd_elf_eh_frame_section_offset): If make_relative, kill relocations
against DW_CFA_set_loc operands.
(_bfd_elf_write_section_eh_frame): Handle DW_CFA_set_loc adjusting.
--- bfd/elf-eh-frame.c.jj 2006-06-29 14:25:14.000000000 +0200
+++ bfd/elf-eh-frame.c 2006-09-26 14:12:57.000000000 +0200
@@ -273,11 +273,14 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *
if (!read_byte (iter, end, &op))
return FALSE;
- switch (op & 0x80 ? op & 0xc0 : op)
+ switch (op & 0xc0 ? op & 0xc0 : op)
{
case DW_CFA_nop:
case DW_CFA_advance_loc:
case DW_CFA_restore:
+ case DW_CFA_remember_state:
+ case DW_CFA_restore_state:
+ case DW_CFA_GNU_window_save:
/* No arguments. */
return TRUE;
@@ -292,6 +295,8 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *
/* One leb128 argument. */
return skip_leb128 (iter, end);
+ case DW_CFA_val_offset:
+ case DW_CFA_val_offset_sf:
case DW_CFA_offset_extended:
case DW_CFA_register:
case DW_CFA_def_cfa:
@@ -308,6 +313,7 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *
&& skip_bytes (iter, end, length));
case DW_CFA_expression:
+ case DW_CFA_val_expression:
/* A leb128 followed by a variable-length argument. */
return (skip_leb128 (iter, end)
&& read_uleb128 (iter, end, &length)
@@ -339,7 +345,8 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *
ENCODED_PTR_WIDTH is as for skip_cfa_op. */
static bfd_byte *
-skip_non_nops (bfd_byte *buf, bfd_byte *end, unsigned int encoded_ptr_width)
+skip_non_nops (bfd_byte *buf, bfd_byte *end, unsigned int encoded_ptr_width,
+ unsigned int *set_loc_count)
{
bfd_byte *last;
@@ -349,6 +356,8 @@ skip_non_nops (bfd_byte *buf, bfd_byte *
buf++;
else
{
+ if (*buf == DW_CFA_set_loc)
+ ++*set_loc_count;
if (!skip_cfa_op (&buf, end, encoded_ptr_width))
return 0;
last = buf;
@@ -453,8 +462,9 @@ _bfd_elf_discard_section_eh_frame
for (;;)
{
char *aug;
- bfd_byte *start, *end, *insns;
+ bfd_byte *start, *end, *insns, *insns_end;
bfd_size_type length;
+ unsigned int set_loc_count;
if (sec_info->count == sec_info->alloced)
{
@@ -558,6 +568,7 @@ _bfd_elf_discard_section_eh_frame
cie_usage_count = 0;
memset (&cie, 0, sizeof (cie));
cie.hdr = hdr;
+ start = buf;
REQUIRE (read_byte (&buf, end, &cie.version));
/* Cannot handle unknown versions. */
@@ -775,11 +786,38 @@ _bfd_elf_discard_section_eh_frame
/* Try to interpret the CFA instructions and find the first
padding nop. Shrink this_inf's size so that it doesn't
- including the padding. */
+ include the padding. */
length = get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
- insns = skip_non_nops (insns, end, length);
- if (insns != 0)
- this_inf->size -= end - insns;
+ 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
+ what needs to be adjusted there. */
+ if (insns_end == NULL
+ /* For the time being we don't support DW_CFA_set_loc in
+ CIE instructions. */
+ || (set_loc_count && this_inf->cie))
+ goto free_no_table;
+ this_inf->size -= end - insns_end;
+ if (set_loc_count
+ && ((cie.fde_encoding & 0xf0) == DW_EH_PE_pcrel
+ || cie.make_relative))
+ {
+ unsigned int cnt;
+ bfd_byte *p;
+
+ this_inf->set_loc = bfd_malloc ((set_loc_count + 1)
+ * sizeof (unsigned int));
+ REQUIRE (this_inf->set_loc);
+ this_inf->set_loc[0] = set_loc_count;
+ p = insns;
+ cnt = 0;
+ while (p < end)
+ {
+ if (*p == DW_CFA_set_loc)
+ this_inf->set_loc[++cnt] = p + 1 - start;
+ REQUIRE (skip_cfa_op (&p, end, length));
+ }
+ }
this_inf->fde_encoding = cie.fde_encoding;
this_inf->lsda_encoding = cie.lsda_encoding;
@@ -965,6 +1003,23 @@ _bfd_elf_eh_frame_section_offset (bfd *o
return (bfd_vma) -2;
}
+ /* If converting to DW_EH_PE_pcrel, there will be no need for run-time
+ relocation against DW_CFA_set_loc's arguments. */
+ if (sec_info->entry[mid].set_loc
+ && (sec_info->entry[mid].cie
+ ? sec_info->entry[mid].make_relative
+ : sec_info->entry[mid].cie_inf->make_relative)
+ && (offset >= sec_info->entry[mid].offset + 8
+ + sec_info->entry[mid].set_loc[1]))
+ {
+ unsigned int cnt;
+
+ for (cnt = 1; cnt <= sec_info->entry[mid].set_loc[0]; cnt++)
+ if (offset == sec_info->entry[mid].offset + 8
+ + sec_info->entry[mid].set_loc[cnt])
+ return (bfd_vma) -2;
+ }
+
if (hdr_info->offsets_adjusted)
offset -= sec->output_offset;
/* Any new augmentation bytes go before the first relocation. */
@@ -1189,6 +1244,7 @@ _bfd_elf_write_section_eh_frame (bfd *ab
/* FDE */
bfd_vma value, address;
unsigned int width;
+ bfd_byte *start;
/* Skip length. */
buf += 4;
@@ -1225,6 +1281,8 @@ _bfd_elf_write_section_eh_frame (bfd *ab
write_value (abfd, buf, value, width);
}
+ start = buf;
+
if (hdr_info)
{
hdr_info->array[hdr_info->array_count].initial_loc = address;
@@ -1257,6 +1315,36 @@ _bfd_elf_write_section_eh_frame (bfd *ab
memmove (buf + 1, buf, end - buf);
*buf = 0;
}
+
+ if (ent->set_loc)
+ {
+ /* Adjust DW_CFA_set_loc. */
+ unsigned int cnt, width;
+ bfd_vma new_offset;
+
+ width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
+ new_offset = ent->new_offset + 8
+ + extra_augmentation_string_bytes (ent)
+ + extra_augmentation_data_bytes (ent);
+
+ for (cnt = 1; cnt <= ent->set_loc[0]; cnt++)
+ {
+ bfd_vma value;
+ buf = start + ent->set_loc[cnt];
+
+ value = read_value (abfd, buf, width,
+ get_DW_EH_PE_signed (ent->fde_encoding));
+ if (!value)
+ continue;
+
+ if ((ent->fde_encoding & 0xf0) == DW_EH_PE_pcrel)
+ value += ent->offset + 8 - new_offset;
+ if (ent->cie_inf->make_relative)
+ value -= sec->output_section->vma + new_offset
+ + ent->set_loc[cnt];
+ write_value (abfd, buf, value, width);
+ }
+ }
}
}
--- bfd/elf-bfd.h.jj 2006-09-26 07:42:56.000000000 +0200
+++ bfd/elf-bfd.h 2006-09-26 13:38:45.000000000 +0200
@@ -304,6 +304,7 @@ struct eh_cie_fde
unsigned int make_lsda_relative : 1;
unsigned int need_lsda_relative : 1;
unsigned int per_encoding_relative : 1;
+ unsigned int *set_loc;
};
struct eh_frame_sec_info
Jakub