From ac685e6adfb9aa42b56c6348050561feed9be5fb Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 3 Oct 2006 13:15:39 +0000 Subject: [PATCH] * 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. --- bfd/ChangeLog | 14 +++++ bfd/elf-bfd.h | 1 + bfd/elf-eh-frame.c | 102 ++++++++++++++++++++++++++++++++++--- ld/testsuite/ChangeLog | 6 +++ ld/testsuite/ld-elf/eh4.d | 32 ++++++++++++ ld/testsuite/ld-elf/eh4.s | 92 +++++++++++++++++++++++++++++++++ ld/testsuite/ld-elf/eh4a.s | 3 ++ 7 files changed, 243 insertions(+), 7 deletions(-) create mode 100644 ld/testsuite/ld-elf/eh4.d create mode 100644 ld/testsuite/ld-elf/eh4.s create mode 100644 ld/testsuite/ld-elf/eh4a.s diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 9acdf86781a..dfd47f650ed 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,17 @@ +2006-10-03 Jakub Jelinek + + * 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 * elflink.c (_bfd_elf_merge_symbol): Discard references to TLS diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index d2c6b8e4a02..1e541daef12 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 91596dfab2b..859f534b158 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); + } + } } } diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index f77f4750b06..63770f06404 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2006-10-03 Jakub Jelinek + + * ld-elf/eh4.d: New test. + * ld-elf/eh4.s: New file. + * ld-elf/eh4a.s: New file. + 2006-10-02 Vladimir Prus * ld-arm/use-thumb-lib.sym: Robustify, by ignoring symbols we're diff --git a/ld/testsuite/ld-elf/eh4.d b/ld/testsuite/ld-elf/eh4.d new file mode 100644 index 00000000000..5fdd722bd38 --- /dev/null +++ b/ld/testsuite/ld-elf/eh4.d @@ -0,0 +1,32 @@ +#source: eh4.s +#source: eh4a.s +#ld: -shared +#readelf: -wf +#target: x86_64-*-* + +The section .eh_frame contains: + +00000000 00000014 00000000 CIE + Version: 1 + Augmentation: "zR" + Code alignment factor: 1 + Data alignment factor: -8 + Return address column: 16 + Augmentation data: 1b + + DW_CFA_def_cfa: r7 ofs 8 + DW_CFA_offset: r16 at cfa-8 + DW_CFA_nop + DW_CFA_nop + +00000018 00000014 0000001c FDE cie=00000000 pc=00000400..00000413 + DW_CFA_set_loc: 00000404 + DW_CFA_def_cfa_offset: 80 + +00000030 00000014 00000034 FDE cie=00000000 pc=00000413..00000426 + DW_CFA_set_loc: 00000417 + DW_CFA_def_cfa_offset: 80 + +00000048 ZERO terminator +#pass + diff --git a/ld/testsuite/ld-elf/eh4.s b/ld/testsuite/ld-elf/eh4.s new file mode 100644 index 00000000000..2714ad64dc8 --- /dev/null +++ b/ld/testsuite/ld-elf/eh4.s @@ -0,0 +1,92 @@ + .text + .align 512 + .globl foo + .type foo, @function +foo: +.LFB1: + subq $72, %rsp +.LCFI1: + xorl %eax, %eax + movq %rsp, %rdi + call bar@PLT + addq $72, %rsp + ret +.LFE1: + .size foo, .-foo + .globl bar + .type bar, @function +bar: +.LFB2: + subq $72, %rsp +.LCFI2: + xorl %eax, %eax + movq %rsp, %rdi + call bar@PLT + addq $72, %rsp + ret +.LFE2: + .size bar, .-bar + .section .eh_frame,"a",@progbits +.Lframe1: + .long .LECIE1-.LSCIE1 # Length of Common Information Entry +.LSCIE1: + .long 0x0 # CIE Identifier Tag + .byte 0x1 # CIE Version + .ascii "zR\0" # CIE Augmentation + .uleb128 0x1 # CIE Code Alignment Factor + .sleb128 -8 # CIE Data Alignment Factor + .byte 0x10 # CIE RA Column + .uleb128 0x1 # Augmentation size + .byte 0x1b # FDE Encoding (pcrel sdata4) + .byte 0xc # DW_CFA_def_cfa + .uleb128 0x7 + .uleb128 0x8 + .byte 0x90 # DW_CFA_offset, column 0x10 + .uleb128 0x1 + .align 8 +.LECIE1: +.LSFDE1: + .long .LEFDE1-.LASFDE1 # FDE Length +.LASFDE1: + .long .LASFDE1-.Lframe1 # FDE CIE offset + .long .LFB1-. # FDE initial location + .long .LFE1-.LFB1 # FDE address range + .uleb128 0x0 # Augmentation size + .byte 0x1 # DW_CFA_set_loc + .long .LCFI1-. + .byte 0xe # DW_CFA_def_cfa_offset + .uleb128 0x50 + .align 8 +.LEFDE1: +.Lframe2: + .long .LECIE2-.LSCIE2 # Length of Common Information Entry +.LSCIE2: + .long 0x0 # CIE Identifier Tag + .byte 0x1 # CIE Version + .ascii "zR\0" # CIE Augmentation + .uleb128 0x1 # CIE Code Alignment Factor + .sleb128 -8 # CIE Data Alignment Factor + .byte 0x10 # CIE RA Column + .uleb128 0x1 # Augmentation size + .byte 0x1b # FDE Encoding (pcrel sdata4) + .byte 0xc # DW_CFA_def_cfa + .uleb128 0x7 + .uleb128 0x8 + .byte 0x90 # DW_CFA_offset, column 0x10 + .uleb128 0x1 + .align 8 +.LECIE2: +.LSFDE2: + .long .LEFDE2-.LASFDE2 # FDE Length +.LASFDE2: + .long .LASFDE2-.Lframe2 # FDE CIE offset + .long .LFB2-. # FDE initial location + .long .LFE2-.LFB2 # FDE address range + .uleb128 0x0 # Augmentation size + .byte 0x1 # DW_CFA_set_loc + .long .LCFI2-. + .byte 0xe # DW_CFA_def_cfa_offset + .uleb128 0x50 + .align 8 +.LEFDE2: + .section .note.GNU-stack,"",@progbits diff --git a/ld/testsuite/ld-elf/eh4a.s b/ld/testsuite/ld-elf/eh4a.s new file mode 100644 index 00000000000..c245871ed10 --- /dev/null +++ b/ld/testsuite/ld-elf/eh4a.s @@ -0,0 +1,3 @@ + .section .eh_frame,"a",%progbits + .align 8 + .zero 8 -- 2.30.2