diff options
author | Jakub Jelinek <jakub@redhat.com> | 2006-10-03 13:15:39 +0000 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2006-10-03 13:15:39 +0000 |
commit | ac685e6adfb9aa42b56c6348050561feed9be5fb (patch) | |
tree | 97731c5949a17e7d40fd0db02d76f9011ce2a9d5 /bfd | |
parent | 00479ba8f9cd7f2ba84c6c3304987afcb4f109b8 (diff) | |
download | gdb-ac685e6adfb9aa42b56c6348050561feed9be5fb.zip gdb-ac685e6adfb9aa42b56c6348050561feed9be5fb.tar.gz gdb-ac685e6adfb9aa42b56c6348050561feed9be5fb.tar.bz2 |
* 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.
* ld-elf/eh4.d: New test.
* ld-elf/eh4.s: New file.
* ld-elf/eh4a.s: New file.
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 14 | ||||
-rw-r--r-- | bfd/elf-bfd.h | 1 | ||||
-rw-r--r-- | bfd/elf-eh-frame.c | 102 |
3 files changed, 110 insertions, 7 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 9acdf86..dfd47f6 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,17 @@ +2006-10-03 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. + 2006-10-02 Daniel Jacobowitz <dan@codesourcery.com> * elflink.c (_bfd_elf_merge_symbol): Discard references to TLS diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index d2c6b8e..1e541da 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -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 diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c index 91596df..859f534 100644 --- a/bfd/elf-eh-frame.c +++ b/bfd/elf-eh-frame.c @@ -273,11 +273,14 @@ skip_cfa_op (bfd_byte **iter, bfd_byte *end, unsigned int encoded_ptr_width) 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 *end, unsigned int encoded_ptr_width) /* 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 *end, unsigned int encoded_ptr_width) && 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 *end, unsigned int encoded_ptr_width) 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 *end, unsigned int encoded_ptr_width) 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 *output_bfd ATTRIBUTE_UNUSED, 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 *abfd, /* 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 *abfd, 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 *abfd, 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); + } + } } } |