From 6576570080f306b7799bd1deb2abffdbcc6dc0a5 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Thu, 13 Dec 2001 11:09:34 +0000 Subject: [PATCH] * elf-bfd.h (enum elf_link_info_type): New. (struct bfd_elf_section_data): Remove stab_info and merge_info fields, add sec_info and sec_info_type. (struct elf_obj_tdata): Add eh_frame_hdr field. (_bfd_elf_discard_section_eh_frame): New prototype. (_bfd_elf_discard_section_eh_frame_hdr): Likewise. (_bfd_elf_eh_frame_section_offset): Likewise. (_bfd_elf_write_section_eh_frame): Likewise. (_bfd_elf_write_section_eh_frame_hdr): Likewise. * Makefile.am (BFD32_BACKENDS): Add elf-eh-frame.lo. (BFD32_BACKENDS_CFILES): Add elf-eh-frame.c. (elf-eh-frame.lo): New. * Makefile.in: Rebuilt. * configure.in (elf): Add elf-eh-frame.lo. * configure: Rebuilt. * elf.c (_bfd_elf_print_private_bfd_data): Support PT_GNU_EH_FRAME. (map_sections_to_segments): Create PT_GNU_EH_FRAME if requested. (get_program_header_size): Take into account PT_GNU_EH_FRAME segment. (_bfd_elf_rela_local_sym): Use sec_info_type and sec_info. (_bfd_elf_rel_local_sym): Likewise. (_bfd_elf_section_offset): Likewise. Call _bfd_elf_eh_frame_section_offset too. * elfxx-ia64.c (elfNN_ia64_relocate_section): Use sec_info_type and sec_info. * elf64-alpha.c (elf64_alpha_relocate_section): Likewise. * elf-eh-frame.c: New file. * elflink.h (elf_link_add_object_symbols): Don't optimize SHF_MERGE .stab sections. Set sec_info_type, use sec_info instead of merge_info and stab_info. (elf_link_create_dynamic_sections): Create .eh_frame_hdr section if --eh-frame-hdr. (elf_bfd_final_link): Write .eh_frame_hdr section. (elf_link_sec_merge_syms): Use sec_info_type and sec_info. (elf_link_input_bfd): Likewise. Call _bfd_elf_write_section_eh_frame to write .eh_frame sections. (elf_bfd_discard_info): Add output_bfd argument. Call _bfd_elf_discard_section_eh_frame and _bfd_elf_discard_section_eh_frame_hdr. (elf_section_ignore_discarded_relocs): Use sec_info_type, not section names. * bfd-in.h (bfd_elf32_discard_info, bfd_elf64_discard_info): Adjust prototypes. * bfd-in2.h (bfd_elf32_discard_info, bfd_elf64_discard_info): Likewise. * elf/common.h (PT_GNU_EH_FRAME): Define. * bfdlink.h (struct bfd_link_info): Add eh_frame_hdr field. * emultempl/elf32.em (finish): Supply output_bfd to bfd_elf*_discard_info. (OPTION_EH_FRAME_HDR): Define. (longopts): Add --eh-frame-hdr. (parse_args): Handle it. (list_options): Add --eh-frame-hdr to help. * emultempl/hppaelf.em (finish): Supply output_bfd to bfd_elf*_discard_info. * scripttempl/elf.sc (.eh_frame_hdr): Add. * readelf.c (get_segment_type): Support PT_GNU_EH_FRAME. --- bfd/ChangeLog | 47 ++ bfd/Makefile.am | 3 + bfd/Makefile.in | 3 + bfd/bfd-in.h | 4 +- bfd/bfd-in2.h | 4 +- bfd/configure | 2 +- bfd/configure.in | 2 +- bfd/elf-bfd.h | 34 +- bfd/elf-eh-frame.c | 1039 +++++++++++++++++++++++++++++++++++++++ bfd/elf.c | 52 +- bfd/elf64-alpha.c | 5 +- bfd/elflink.h | 203 ++++++-- bfd/elfxx-ia64.c | 5 +- binutils/ChangeLog | 4 + binutils/readelf.c | 3 + include/ChangeLog | 5 + include/bfdlink.h | 4 + include/elf/common.h | 2 + ld/ChangeLog | 12 + ld/emultempl/elf32.em | 9 +- ld/emultempl/hppaelf.em | 2 +- ld/scripttempl/elf.sc | 1 + 22 files changed, 1367 insertions(+), 78 deletions(-) create mode 100644 bfd/elf-eh-frame.c diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 46dd9b76cbb..36539921d44 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,50 @@ +2001-12-13 Jakub Jelinek + + * elf-bfd.h (enum elf_link_info_type): New. + (struct bfd_elf_section_data): Remove stab_info and merge_info + fields, add sec_info and sec_info_type. + (struct elf_obj_tdata): Add eh_frame_hdr field. + (_bfd_elf_discard_section_eh_frame): New prototype. + (_bfd_elf_discard_section_eh_frame_hdr): Likewise. + (_bfd_elf_eh_frame_section_offset): Likewise. + (_bfd_elf_write_section_eh_frame): Likewise. + (_bfd_elf_write_section_eh_frame_hdr): Likewise. + * Makefile.am (BFD32_BACKENDS): Add elf-eh-frame.lo. + (BFD32_BACKENDS_CFILES): Add elf-eh-frame.c. + (elf-eh-frame.lo): New. + * Makefile.in: Rebuilt. + * configure.in (elf): Add elf-eh-frame.lo. + * configure: Rebuilt. + * elf.c (_bfd_elf_print_private_bfd_data): Support PT_GNU_EH_FRAME. + (map_sections_to_segments): Create PT_GNU_EH_FRAME if requested. + (get_program_header_size): Take into account PT_GNU_EH_FRAME + segment. + (_bfd_elf_rela_local_sym): Use sec_info_type and sec_info. + (_bfd_elf_rel_local_sym): Likewise. + (_bfd_elf_section_offset): Likewise. Call + _bfd_elf_eh_frame_section_offset too. + * elfxx-ia64.c (elfNN_ia64_relocate_section): Use sec_info_type and + sec_info. + * elf64-alpha.c (elf64_alpha_relocate_section): Likewise. + * elf-eh-frame.c: New file. + * elflink.h (elf_link_add_object_symbols): Don't optimize SHF_MERGE + .stab sections. Set sec_info_type, use sec_info instead + of merge_info and stab_info. + (elf_link_create_dynamic_sections): Create .eh_frame_hdr section + if --eh-frame-hdr. + (elf_bfd_final_link): Write .eh_frame_hdr section. + (elf_link_sec_merge_syms): Use sec_info_type and sec_info. + (elf_link_input_bfd): Likewise. + Call _bfd_elf_write_section_eh_frame to write .eh_frame sections. + (elf_bfd_discard_info): Add output_bfd argument. + Call _bfd_elf_discard_section_eh_frame and + _bfd_elf_discard_section_eh_frame_hdr. + (elf_section_ignore_discarded_relocs): Use sec_info_type, not section + names. + * bfd-in.h (bfd_elf32_discard_info, bfd_elf64_discard_info): Adjust + prototypes. + * bfd-in2.h (bfd_elf32_discard_info, bfd_elf64_discard_info): Likewise. + 2001-12-12 Richard Henderson * syms.c (_bfd_generic_read_minisymbols): Early return for diff --git a/bfd/Makefile.am b/bfd/Makefile.am index ff58585ff56..842c68dcdb8 100644 --- a/bfd/Makefile.am +++ b/bfd/Makefile.am @@ -223,6 +223,7 @@ BFD32_BACKENDS = \ elf32.lo \ elflink.lo \ elf-strtab.lo \ + elf-eh-frame.lo \ epoc-pe-arm.lo \ epoc-pei-arm.lo \ hp300bsd.lo \ @@ -365,6 +366,7 @@ BFD32_BACKENDS_CFILES = \ elf32.c \ elflink.c \ elf-strtab.c \ + elf-eh-frame.c \ epoc-pe-arm.c \ epoc-pei-arm.c \ hp300bsd.c \ @@ -1149,6 +1151,7 @@ elflink.lo: elflink.c $(INCDIR)/filenames.h $(INCDIR)/bfdlink.h \ elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ $(INCDIR)/elf/external.h elf-strtab.lo: elf-strtab.c $(INCDIR)/filenames.h $(INCDIR)/hashtab.h +elf-eh-frame.lo: elf-eh-frame.c epoc-pe-arm.lo: epoc-pe-arm.c pe-arm.c $(INCDIR)/filenames.h \ coff-arm.c $(INCDIR)/coff/arm.h $(INCDIR)/coff/external.h \ $(INCDIR)/coff/internal.h $(INCDIR)/coff/pe.h libcoff.h \ diff --git a/bfd/Makefile.in b/bfd/Makefile.in index e0ff2be4295..4506902d765 100644 --- a/bfd/Makefile.in +++ b/bfd/Makefile.in @@ -351,6 +351,7 @@ BFD32_BACKENDS = \ elf32.lo \ elflink.lo \ elf-strtab.lo \ + elf-eh-frame.lo \ epoc-pe-arm.lo \ epoc-pei-arm.lo \ hp300bsd.lo \ @@ -494,6 +495,7 @@ BFD32_BACKENDS_CFILES = \ elf32.c \ elflink.c \ elf-strtab.c \ + elf-eh-frame.c \ epoc-pe-arm.c \ epoc-pei-arm.c \ hp300bsd.c \ @@ -1693,6 +1695,7 @@ elflink.lo: elflink.c $(INCDIR)/filenames.h $(INCDIR)/bfdlink.h \ elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \ $(INCDIR)/elf/external.h elf-strtab.lo: elf-strtab.c $(INCDIR)/filenames.h $(INCDIR)/hashtab.h +elf-eh-frame.lo: elf-eh-frame.c epoc-pe-arm.lo: epoc-pe-arm.c pe-arm.c $(INCDIR)/filenames.h \ coff-arm.c $(INCDIR)/coff/arm.h $(INCDIR)/coff/external.h \ $(INCDIR)/coff/internal.h $(INCDIR)/coff/pe.h libcoff.h \ diff --git a/bfd/bfd-in.h b/bfd/bfd-in.h index 9eadbce300f..c578614201b 100644 --- a/bfd/bfd-in.h +++ b/bfd/bfd-in.h @@ -636,9 +636,9 @@ extern const char *bfd_elf_get_dt_soname PARAMS ((bfd *)); extern struct bfd_link_needed_list *bfd_elf_get_runpath_list PARAMS ((bfd *, struct bfd_link_info *)); extern boolean bfd_elf32_discard_info - PARAMS ((struct bfd_link_info *)); + PARAMS ((bfd *, struct bfd_link_info *)); extern boolean bfd_elf64_discard_info - PARAMS ((struct bfd_link_info *)); + PARAMS ((bfd *, struct bfd_link_info *)); /* Return an upper bound on the number of bytes required to store a copy of ABFD's program header table entries. Return -1 if an error diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 091042f02c4..0a8c85da0c4 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -642,9 +642,9 @@ extern const char *bfd_elf_get_dt_soname PARAMS ((bfd *)); extern struct bfd_link_needed_list *bfd_elf_get_runpath_list PARAMS ((bfd *, struct bfd_link_info *)); extern boolean bfd_elf32_discard_info - PARAMS ((struct bfd_link_info *)); + PARAMS ((bfd *, struct bfd_link_info *)); extern boolean bfd_elf64_discard_info - PARAMS ((struct bfd_link_info *)); + PARAMS ((bfd *, struct bfd_link_info *)); /* Return an upper bound on the number of bytes required to store a copy of ABFD's program header table entries. Return -1 if an error diff --git a/bfd/configure b/bfd/configure index 0ee8912ecee..64b59639a11 100755 --- a/bfd/configure +++ b/bfd/configure @@ -5921,7 +5921,7 @@ selarchs="$f" # Target backend .o files. tb= -elf="elf.lo elflink.lo elf-strtab.lo dwarf1.lo" +elf="elf.lo elflink.lo elf-strtab.lo elf-eh-frame.lo dwarf1.lo" for vec in $selvecs do diff --git a/bfd/configure.in b/bfd/configure.in index 32740583b92..39be3799389 100644 --- a/bfd/configure.in +++ b/bfd/configure.in @@ -503,7 +503,7 @@ selarchs="$f" # Target backend .o files. tb= -elf="elf.lo elflink.lo elf-strtab.lo dwarf1.lo" +elf="elf.lo elflink.lo elf-strtab.lo elf-eh-frame.lo dwarf1.lo" for vec in $selvecs do diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 3323aaaa767..d1b088a59ba 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -223,6 +223,16 @@ struct elf_link_local_dynamic_entry Elf_Internal_Sym isym; }; +enum elf_link_info_type +{ + ELF_INFO_TYPE_NONE, + ELF_INFO_TYPE_STABS, + ELF_INFO_TYPE_MERGE, + ELF_INFO_TYPE_EH_FRAME, + ELF_INFO_TYPE_EH_FRAME_HDR, + ELF_INFO_TYPE_LAST +}; + /* ELF linker hash table. */ struct elf_link_hash_table @@ -848,11 +858,11 @@ struct bfd_elf_section_data no dynamic symbol for this section. */ long dynindx; - /* A pointer used for .stab linking optimizations. */ - PTR stab_info; + /* A pointer used for various section optimizations. */ + PTR sec_info; - /* A pointer used for SEC_MERGE optimizations. */ - PTR merge_info; + /* Type of that information. */ + enum elf_link_info_type sec_info_type; /* Group name, if this section is part of a group. */ const char *group_name; @@ -1043,6 +1053,10 @@ struct elf_obj_tdata /* Used to determine if the e_flags field has been initialized */ boolean flags_init; + /* Used to determine if PT_GNU_EH_FRAME segment header should be + created. */ + boolean eh_frame_hdr; + /* Number of symbol version definitions we are about to emit. */ unsigned int cverdefs; @@ -1271,6 +1285,18 @@ extern boolean _bfd_elf_strtab_emit extern void _bfd_elf_strtab_finalize PARAMS ((struct elf_strtab_hash *)); +extern boolean _bfd_elf_discard_section_eh_frame + PARAMS ((bfd *, struct bfd_link_info *, asection *, asection *, + boolean (*) (bfd_vma, PTR), struct elf_reloc_cookie *)); +extern boolean _bfd_elf_discard_section_eh_frame_hdr + PARAMS ((bfd *, struct bfd_link_info *, asection *)); +extern bfd_vma _bfd_elf_eh_frame_section_offset + PARAMS ((bfd *, asection *, bfd_vma)); +extern boolean _bfd_elf_write_section_eh_frame + PARAMS ((bfd *, asection *, asection *, bfd_byte *)); +extern boolean _bfd_elf_write_section_eh_frame_hdr + PARAMS ((bfd *, asection *)); + extern boolean _bfd_elf_link_record_dynamic_symbol PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); extern long _bfd_elf_link_lookup_local_dynindx diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c new file mode 100644 index 00000000000..f215fca036b --- /dev/null +++ b/bfd/elf-eh-frame.c @@ -0,0 +1,1039 @@ +/* .eh_frame section optimization. + Copyright 2001 Free Software Foundation, Inc. + Written by Jakub Jelinek . + +This file is part of BFD, the Binary File Descriptor library. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "bfd.h" +#include "sysdep.h" +#include "libbfd.h" +#include "elf-bfd.h" +#include "elf/dwarf2.h" + +#define EH_FRAME_HDR_SIZE 8 + +struct cie_header +{ + unsigned int length; + unsigned int id; +}; + +struct cie +{ + struct cie_header hdr; + unsigned char version; + unsigned char augmentation[20]; + unsigned int code_align; + int data_align; + unsigned int ra_column; + unsigned int 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 initial_instructions[50]; +}; + +struct eh_cie_fde +{ + unsigned int offset; + unsigned int size; + asection *sec; + unsigned int new_offset; + unsigned char fde_encoding; + unsigned char cie : 1; + unsigned char removed : 1; + unsigned char make_relative : 1; +}; + +struct eh_frame_sec_info +{ + unsigned int count; + unsigned int alloced; + struct eh_cie_fde entry[1]; +}; + +struct eh_frame_array_ent +{ + bfd_vma initial_loc; + bfd_vma fde; +}; + +struct eh_frame_hdr_info +{ + struct cie last_cie; + asection *last_cie_sec; + unsigned int last_cie_offset; + unsigned int fde_count, array_count; + struct eh_frame_array_ent *array; + /* TRUE if .eh_frame_hdr should contain the sorted search table. + We build it if we successfully read all .eh_frame input sections + and recognize them. */ + boolean table; +}; + +static bfd_vma read_unsigned_leb128 + PARAMS ((bfd *, char *, unsigned int *)); +static bfd_signed_vma read_signed_leb128 + PARAMS ((bfd *, char *, unsigned int *)); +static int get_DW_EH_PE_width + PARAMS ((int, int)); +static int cie_compare + PARAMS ((struct cie *, struct cie *)); +static int vma_compare + PARAMS ((const PTR a, const PTR b)); + +/* Helper function for reading uleb128 encoded data. */ + +static bfd_vma +read_unsigned_leb128 (abfd, buf, bytes_read_ptr) + bfd *abfd ATTRIBUTE_UNUSED; + char *buf; + unsigned int *bytes_read_ptr; +{ + bfd_vma result; + unsigned int num_read; + int shift; + unsigned char byte; + + result = 0; + shift = 0; + num_read = 0; + do + { + byte = bfd_get_8 (abfd, (bfd_byte *) buf); + buf ++; + num_read ++; + result |= (((bfd_vma) byte & 0x7f) << shift); + shift += 7; + } + while (byte & 0x80); + * bytes_read_ptr = num_read; + return result; +} + +/* Helper function for reading sleb128 encoded data. */ + +static bfd_signed_vma +read_signed_leb128 (abfd, buf, bytes_read_ptr) + bfd *abfd ATTRIBUTE_UNUSED; + char *buf; + unsigned int * bytes_read_ptr; +{ + bfd_vma result; + int shift; + int num_read; + unsigned char byte; + + result = 0; + shift = 0; + num_read = 0; + do + { + byte = bfd_get_8 (abfd, (bfd_byte *) buf); + buf ++; + num_read ++; + result |= (((bfd_vma) byte & 0x7f) << shift); + shift += 7; + } + while (byte & 0x80); + if (byte & 0x40) + result |= (((bfd_vma) -1) << (shift - 7)) << 7; + * bytes_read_ptr = num_read; + return result; +} + +#define read_uleb128(VAR, BUF) \ +do \ + { \ + (VAR) = read_unsigned_leb128 (abfd, buf, &leb128_tmp); \ + (BUF) += leb128_tmp; \ + } \ +while (0) + +#define read_sleb128(VAR, BUF) \ +do \ + { \ + (VAR) = read_signed_leb128 (abfd, buf, &leb128_tmp); \ + (BUF) += leb128_tmp; \ + } \ +while (0) + +/* Return 0 if either encoding is variable width, or not yet known to bfd. */ + +static +int get_DW_EH_PE_width (encoding, ptr_size) + int encoding, ptr_size; +{ + /* DW_EH_PE_ values of 0x60 and 0x70 weren't defined at the time .eh_frame + was added to bfd. */ + if ((encoding & 0x60) == 0x60) + return 0; + + switch (encoding & 7) + { + case DW_EH_PE_udata2: return 2; + case DW_EH_PE_udata4: return 4; + case DW_EH_PE_udata8: return 8; + case DW_EH_PE_absptr: return ptr_size; + default: + break; + } + + return 0; +} + +/* Return zero if C1 and C2 CIEs can be merged. */ + +static +int cie_compare (c1, c2) + struct cie *c1, *c2; +{ + if (c1->hdr.length == c2->hdr.length + && c1->version == c2->version + && strcmp (c1->augmentation, c2->augmentation) == 0 + && strcmp (c1->augmentation, "eh") != 0 + && c1->code_align == c2->code_align + && c1->data_align == c2->data_align + && c1->ra_column == c2->ra_column + && c1->augmentation_size == c2->augmentation_size + && c1->personality == c2->personality + && c1->per_encoding == c2->per_encoding + && c1->lsda_encoding == c2->lsda_encoding + && c1->fde_encoding == c2->fde_encoding + && (c1->initial_insn_length + == c2->initial_insn_length) + && memcmp (c1->initial_instructions, + c2->initial_instructions, + c1->initial_insn_length) == 0) + return 0; + + return 1; +} + +/* This function is called for each input file before the .eh_frame + section is relocated. It discards duplicate CIEs and FDEs for discarded + functions. The function returns true iff any entries have been + deleted. */ + +boolean +_bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec, + reloc_symbol_deleted_p, cookie) + bfd *abfd; + struct bfd_link_info *info; + asection *sec, *ehdrsec; + boolean (*reloc_symbol_deleted_p) (bfd_vma, PTR); + struct elf_reloc_cookie *cookie; +{ + bfd_byte *ehbuf = NULL, *buf; + bfd_byte *last_cie, *last_fde; + struct cie_header hdr; + struct cie cie; + struct eh_frame_hdr_info *hdr_info; + struct eh_frame_sec_info *sec_info; + unsigned int leb128_tmp; + unsigned int cie_usage_count, last_cie_ndx, i, offset, make_relative; + Elf_Internal_Rela *rel; + bfd_size_type new_size; + unsigned int ptr_size; + + if (sec->_raw_size == 0) + { + /* This file does not contain .eh_frame information. */ + return false; + } + + if ((sec->output_section != NULL + && bfd_is_abs_section (sec->output_section))) + { + /* At least one of the sections is being discarded from the + link, so we should just ignore them. */ + return false; + } + + /* Read the frame unwind information from abfd. */ + + ehbuf = (bfd_byte *) bfd_malloc (sec->_raw_size); + if (ehbuf == NULL + || ! bfd_get_section_contents (abfd, sec, ehbuf, (bfd_vma) 0, + sec->_raw_size)) + { + if (elf_section_data (ehdrsec)->sec_info_type + != ELF_INFO_TYPE_EH_FRAME_HDR) + { + elf_section_data (ehdrsec)->sec_info + = bfd_zmalloc (sizeof (struct eh_frame_hdr_info)); + elf_section_data (ehdrsec)->sec_info_type + = ELF_INFO_TYPE_EH_FRAME_HDR; + } + return false; + } + + if (sec->_raw_size >= 4 + && bfd_get_32 (abfd, ehbuf) == 0 + && cookie->rel == cookie->relend) + { + /* Empty .eh_frame section. */ + free (ehbuf); + return false; + } + + if (elf_section_data (ehdrsec)->sec_info_type + != ELF_INFO_TYPE_EH_FRAME_HDR) + { + hdr_info = (struct eh_frame_hdr_info *) + bfd_zmalloc (sizeof (struct eh_frame_hdr_info)); + hdr_info->table = true; + elf_section_data (ehdrsec)->sec_info = hdr_info; + elf_section_data (ehdrsec)->sec_info_type + = ELF_INFO_TYPE_EH_FRAME_HDR; + } + else + hdr_info = (struct eh_frame_hdr_info *) + elf_section_data (ehdrsec)->sec_info; + + /* If .eh_frame section size doesn't fit into int, we cannot handle + it (it would need to use 64-bit .eh_frame format anyway). */ + if (sec->_raw_size != (unsigned int) sec->_raw_size) + return false; + + ptr_size = (elf_elfheader (abfd)->e_ident[EI_CLASS] + == ELFCLASS64) ? 8 : 4; + buf = ehbuf; + last_cie = NULL; + last_cie_ndx = 0; + memset (&cie, 0, sizeof (cie)); + cie_usage_count = 0; + new_size = sec->_raw_size; + make_relative = hdr_info->last_cie.make_relative; + sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info) + + 99 * sizeof (struct eh_cie_fde)); + if (sec_info == NULL) + goto free_no_table; + sec_info->alloced = 100; + +#define ENSURE_NO_RELOCS(buf) \ + if (cookie->rel < cookie->relend \ + && (cookie->rel->r_offset \ + < (bfd_size_type) ((buf) - ehbuf))) \ + goto free_no_table + +#define SKIP_RELOCS(buf) \ + while (cookie->rel < cookie->relend \ + && (cookie->rel->r_offset \ + < (bfd_size_type) ((buf) - ehbuf))) \ + cookie->rel++ + +#define GET_RELOC(buf) \ + ((cookie->rel < cookie->relend \ + && (cookie->rel->r_offset \ + == (bfd_size_type) ((buf) - ehbuf))) \ + ? cookie->rel : NULL) + + for (;;) + { + unsigned char *aug; + + if (sec_info->count == sec_info->alloced) + { + sec_info = bfd_realloc (sec_info, + sizeof (struct eh_frame_sec_info) + + (sec_info->alloced + 99) + * sizeof (struct eh_cie_fde)); + if (sec_info == NULL) + goto free_no_table; + + memset (&sec_info->entry[sec_info->alloced], 0, + 100 * sizeof (struct eh_cie_fde)); + sec_info->alloced += 100; + } + + 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->_raw_size) + hdr.id = (unsigned int) -1; + else + { + if ((bfd_size_type) (buf + 4 - ehbuf) > sec->_raw_size) + /* No space for CIE/FDE header length. */ + goto free_no_table; + + hdr.length = bfd_get_32 (abfd, buf); + if (hdr.length == 0xffffffff) + /* 64-bit .eh_frame is not supported. */ + goto free_no_table; + buf += 4; + if ((buf - ehbuf) + hdr.length > sec->_raw_size) + /* CIE/FDE not contained fully in this .eh_frame input section. */ + goto free_no_table; + + sec_info->entry[sec_info->count].offset = last_fde - ehbuf; + sec_info->entry[sec_info->count].size = 4 + hdr.length; + + if (hdr.length == 0) + { + /* CIE with length 0 must be only the last in the section. */ + if ((bfd_size_type) (buf - ehbuf) < sec->_raw_size) + goto free_no_table; + ENSURE_NO_RELOCS (buf); + sec_info->count++; + /* Now just finish last encountered CIE processing and break + the loop. */ + hdr.id = (unsigned int) -1; + } + else + { + hdr.id = bfd_get_32 (abfd, buf); + buf += 4; + if (hdr.id == (unsigned int) -1) + goto free_no_table; + } + } + + if (hdr.id == 0 || hdr.id == (unsigned int) -1) + { + unsigned int initial_insn_length; + + /* CIE */ + if (last_cie != NULL) + { + /* Now check if this CIE is identical to 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 + that. */ + if (cie_compare (&cie, &hdr_info->last_cie) == 0 + || cie_usage_count == 0) + { + new_size -= cie.hdr.length + 4; + sec_info->entry[last_cie_ndx].removed = 1; + sec_info->entry[last_cie_ndx].sec = hdr_info->last_cie_sec; + sec_info->entry[last_cie_ndx].new_offset + = hdr_info->last_cie_offset; + } + else + { + hdr_info->last_cie = cie; + hdr_info->last_cie_sec = sec; + hdr_info->last_cie_offset = last_cie - ehbuf; + sec_info->entry[last_cie_ndx].make_relative + = cie.make_relative; + } + } + + if (hdr.id == (unsigned int) -1) + break; + + last_cie_ndx = sec_info->count; + sec_info->entry[sec_info->count].cie = 1; + + cie_usage_count = 0; + memset (&cie, 0, sizeof (cie)); + cie.hdr = hdr; + cie.version = *buf++; + + /* Cannot handle unknown versions. */ + if (cie.version != 1) + goto free_no_table; + if (strlen (buf) > sizeof (cie.augmentation) - 1) + goto free_no_table; + + strcpy (cie.augmentation, buf); + buf = strchr (buf, '\0') + 1; + ENSURE_NO_RELOCS (buf); + if (buf[0] == 'e' && buf[1] == 'h') + { + /* GCC < 3.0 .eh_frame CIE */ + /* We cannot merge "eh" CIEs because __EXCEPTION_TABLE__ + is private to each CIE, so we don't need it for anything. + Just skip it. */ + buf += ptr_size; + SKIP_RELOCS (buf); + } + read_uleb128 (cie.code_align, buf); + read_sleb128 (cie.data_align, buf); + read_uleb128 (cie.ra_column, buf); + 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; + if (aug[0] != 'e' || aug[1] != 'h') + { + if (*aug == 'z') + { + aug++; + read_uleb128 (cie.augmentation_size, buf); + ENSURE_NO_RELOCS (buf); + } + + while (*aug != '\0') + switch (*aug++) + { + case 'L': + cie.lsda_encoding = *buf++; + ENSURE_NO_RELOCS (buf); + if (get_DW_EH_PE_width (cie.lsda_encoding, ptr_size) == 0) + goto free_no_table; + break; + case 'R': + cie.fde_encoding = *buf++; + ENSURE_NO_RELOCS (buf); + if (get_DW_EH_PE_width (cie.fde_encoding, ptr_size) == 0) + goto free_no_table; + break; + case 'P': + { + int per_width; + + cie.per_encoding = *buf++; + per_width = get_DW_EH_PE_width (cie.per_encoding, + ptr_size); + if (per_width == 0) + goto free_no_table; + if ((cie.per_encoding & 0xf0) == DW_EH_PE_aligned) + buf = (ehbuf + + ((buf - ehbuf + per_width - 1) + & ~((bfd_size_type) per_width - 1))); + ENSURE_NO_RELOCS (buf); + rel = GET_RELOC (buf); + /* Ensure we have a reloc here, against + a global symbol. */ + if (rel != NULL) + { + unsigned long r_symndx; + +#ifdef BFD64 + if (ptr_size == 8) + r_symndx = ELF64_R_SYM (cookie->rel->r_info); + else +#endif + r_symndx = ELF32_R_SYM (cookie->rel->r_info); + if (r_symndx >= cookie->locsymcount) + { + struct elf_link_hash_entry *h; + + r_symndx -= cookie->extsymoff; + h = cookie->sym_hashes[r_symndx]; + + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) + h->root.u.i.link; + + cie.personality = h; + } + cookie->rel++; + } + buf += per_width; + } + break; + default: + /* Unrecognized augmentation. Better bail out. */ + goto free_no_table; + } + } + + /* 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 + && (cie.fde_encoding & 0xf0) == DW_EH_PE_absptr) + cie.make_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; + + initial_insn_length = cie.hdr.length - (buf - last_fde - 4); + if (initial_insn_length <= 50) + { + cie.initial_insn_length = initial_insn_length; + memcpy (cie.initial_instructions, buf, initial_insn_length); + } + buf += initial_insn_length; + ENSURE_NO_RELOCS (buf); + last_cie = last_fde; + } + else + { + /* Ensure this FDE uses the last CIE encountered. */ + if (last_cie == NULL + || hdr.id != (unsigned int) (buf - 4 - last_cie)) + goto free_no_table; + + ENSURE_NO_RELOCS (buf); + rel = GET_RELOC (buf); + if (rel == NULL) + /* This should not happen. */ + 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; + } + else + { + cie_usage_count++; + hdr_info->fde_count++; + } + cookie->rel = rel; + buf = last_fde + 4 + hdr.length; + SKIP_RELOCS (buf); + } + + sec_info->entry[sec_info->count].fde_encoding = cie.fde_encoding; + sec_info->count++; + } + + elf_section_data (sec)->sec_info = sec_info; + elf_section_data (sec)->sec_info_type = ELF_INFO_TYPE_EH_FRAME; + + /* Ok, now we can assign new offsets. */ + offset = 0; + last_cie_ndx = 0; + for (i = 0; i < sec_info->count; i++) + { + if (! sec_info->entry[i].removed) + { + sec_info->entry[i].new_offset = offset; + offset += sec_info->entry[i].size; + if (sec_info->entry[i].cie) + { + last_cie_ndx = i; + make_relative = sec_info->entry[i].make_relative; + } + else + sec_info->entry[i].make_relative = make_relative; + } + else if (sec_info->entry[i].cie && sec_info->entry[i].sec == sec) + { + /* Need to adjust new_offset too. */ + BFD_ASSERT (sec_info->entry[last_cie_ndx].offset + == sec_info->entry[i].new_offset); + sec_info->entry[i].new_offset + = sec_info->entry[last_cie_ndx].new_offset; + } + } + if (hdr_info->last_cie_sec == sec) + { + BFD_ASSERT (sec_info->entry[last_cie_ndx].offset + == hdr_info->last_cie_offset); + hdr_info->last_cie_offset = sec_info->entry[last_cie_ndx].new_offset; + } + + /* Shrink the sec as needed. */ + + sec->_cooked_size = new_size; + if (sec->_cooked_size == 0) + sec->flags |= SEC_EXCLUDE; + + return new_size != sec->_raw_size; + +free_no_table: + if (sec_info) + free (sec_info); + hdr_info->table = false; + hdr_info->last_cie.hdr.length = 0; + return false; +} + +/* This function is called for .eh_frame_hdr section after + _bfd_elf_discard_section_eh_frame has been called on all .eh_frame + input sections. It finalizes the size of .eh_frame_hdr section. */ + +boolean +_bfd_elf_discard_section_eh_frame_hdr (abfd, info, sec) + bfd *abfd; + struct bfd_link_info *info; + asection *sec; +{ + struct eh_frame_hdr_info *hdr_info; + unsigned int ptr_size; + + ptr_size = (elf_elfheader (abfd)->e_ident[EI_CLASS] + == ELFCLASS64) ? 8 : 4; + + if ((elf_section_data (sec)->sec_info_type + != ELF_INFO_TYPE_EH_FRAME_HDR) + || ! info->eh_frame_hdr) + { + _bfd_strip_section_from_output (info, sec); + return false; + } + + hdr_info = (struct eh_frame_hdr_info *) + elf_section_data (sec)->sec_info; + sec->_cooked_size = EH_FRAME_HDR_SIZE; + if (hdr_info->table) + sec->_cooked_size += 4 + hdr_info->fde_count * 8; + + /* Request program headers to be recalculated. */ + elf_tdata (abfd)->program_header_size = 0; + elf_tdata (abfd)->eh_frame_hdr = true; + return true; +} + +/* Adjust an address in the .eh_frame section. Given OFFSET within + SEC, this returns the new offset in the adjusted .eh_frame section, + or -1 if the address refers to a CIE/FDE which has been removed + or to offset with dynamic relocation which is no longer needed. */ + +bfd_vma +_bfd_elf_eh_frame_section_offset (output_bfd, sec, offset) + bfd *output_bfd ATTRIBUTE_UNUSED; + asection *sec; + bfd_vma offset; +{ + struct eh_frame_sec_info *sec_info; + unsigned int lo, hi, mid; + + if (elf_section_data (sec)->sec_info_type != ELF_INFO_TYPE_EH_FRAME) + return offset; + sec_info = (struct eh_frame_sec_info *) + elf_section_data (sec)->sec_info; + + if (offset >= sec->_raw_size) + return offset - (sec->_cooked_size - sec->_raw_size); + + lo = 0; + hi = sec_info->count; + mid = 0; + while (lo < hi) + { + mid = (lo + hi) / 2; + if (offset < sec_info->entry[mid].offset) + hi = mid; + else if (offset + >= sec_info->entry[mid].offset + sec_info->entry[mid].size) + lo = mid + 1; + else + break; + } + + BFD_ASSERT (lo < hi); + + /* FDE or CIE was removed. */ + if (sec_info->entry[mid].removed) + return (bfd_vma) -1; + + /* If converting to DW_EH_PE_pcrel, there will be no need for run-time + relocation against FDE's initial_location field. */ + if (sec_info->entry[mid].make_relative + && ! sec_info->entry[mid].cie + && offset == sec_info->entry[mid].offset + 8) + return (bfd_vma) -1; + + return (offset + + (sec_info->entry[mid].new_offset - sec_info->entry[mid].offset)); +} + +/* Write out .eh_frame section. This is called with the relocated + contents. */ + +boolean +_bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents) + bfd *abfd; + asection *sec, *ehdrsec; + bfd_byte *contents; +{ + struct eh_frame_sec_info *sec_info; + struct eh_frame_hdr_info *hdr_info; + unsigned int i; + bfd_byte *p, *buf; + unsigned int leb128_tmp; + unsigned int cie_offset = 0; + unsigned int ptr_size; + + ptr_size = (elf_elfheader (sec->owner)->e_ident[EI_CLASS] + == ELFCLASS64) ? 8 : 4; + + if (elf_section_data (sec)->sec_info_type != ELF_INFO_TYPE_EH_FRAME) + return bfd_set_section_contents (abfd, sec->output_section, + contents, + (file_ptr) sec->output_offset, + sec->_raw_size); + sec_info = (struct eh_frame_sec_info *) + elf_section_data (sec)->sec_info; + hdr_info = NULL; + if (ehdrsec + && (elf_section_data (ehdrsec)->sec_info_type + == ELF_INFO_TYPE_EH_FRAME_HDR)) + { + hdr_info = (struct eh_frame_hdr_info *) + elf_section_data (ehdrsec)->sec_info; + if (hdr_info->table && hdr_info->array == NULL) + hdr_info->array + = bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array)); + if (hdr_info->array == NULL) + hdr_info = NULL; + } + + p = contents; + for (i = 0; i < sec_info->count; ++i) + { + if (sec_info->entry[i].removed) + { + 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); + } + continue; + } + if (sec_info->entry[i].cie) + { + /* CIE */ + cie_offset = sec_info->entry[i].new_offset; + if (sec_info->entry[i].make_relative) + { + unsigned char *aug; + unsigned int dummy, per_width, per_encoding; + + /* Need to find 'R' augmentation's argument and modify + DW_EH_PE_* value. */ + buf = contents + sec_info->entry[i].offset; + /* Skip length, id and version. */ + buf += 9; + aug = buf; + buf = strchr (buf, '\0') + 1; + read_uleb128 (dummy, buf); + read_sleb128 (dummy, buf); + read_uleb128 (dummy, buf); + if (*aug == 'z') + { + read_uleb128 (dummy, buf); + aug++; + } + + while (*aug != 'R') + switch (*aug++) + { + case 'L': + buf++; + break; + case 'P': + per_encoding = *buf++; + per_width = get_DW_EH_PE_width (per_encoding, + ptr_size); + BFD_ASSERT (per_width != 0); + if ((per_encoding & 0xf0) == DW_EH_PE_aligned) + buf = (contents + + ((buf - contents + per_width - 1) + & ~((bfd_size_type) per_width - 1))); + buf += per_width; + break; + default: + BFD_FAIL (); + } + + BFD_ASSERT (*buf == sec_info->entry[i].fde_encoding); + *buf |= DW_EH_PE_pcrel; + } + } + else + { + /* FDE */ + bfd_vma value = 0, address; + unsigned int fde_width; + + buf = contents + sec_info->entry[i].offset; + /* Skip length. */ + buf += 4; + 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) + { + 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].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; + } + + if (hdr_info) + { + hdr_info->array[hdr_info->array_count].initial_loc = address; + hdr_info->array[hdr_info->array_count++].fde + = (sec->output_section->vma + sec->output_offset + + sec_info->entry[i].new_offset); + } + } + + BFD_ASSERT (p == contents + sec_info->entry[i].new_offset); + memmove (p, contents + sec_info->entry[i].offset, + sec_info->entry[i].size); + p += sec_info->entry[i].size; + } + + BFD_ASSERT ((bfd_size_type) (p - contents) == sec->_cooked_size); + + return bfd_set_section_contents (abfd, sec->output_section, + contents, (file_ptr) sec->output_offset, + sec->_cooked_size); +} + +/* Helper function used to sort .eh_frame_hdr search table by increasing + VMA of FDE initial location. */ + +static int +vma_compare (a, b) + const PTR a; + const PTR b; +{ + struct eh_frame_array_ent *p = (struct eh_frame_array_ent *) a; + struct eh_frame_array_ent *q = (struct eh_frame_array_ent *) b; + if (p->initial_loc > q->initial_loc) + return 1; + if (p->initial_loc < q->initial_loc) + return -1; + return 0; +} + +/* Write out .eh_frame_hdr section. This must be called after + _bfd_elf_write_section_eh_frame has been called on all input + .eh_frame sections. + .eh_frame_hdr format: + ubyte version (currently 1) + ubyte eh_frame_ptr_enc (DW_EH_PE_* encoding of pointer to start of + .eh_frame section) + ubyte fde_count_enc (DW_EH_PE_* encoding of total FDE count + number (or DW_EH_PE_omit if there is no + binary search table computed)) + ubyte table_enc (DW_EH_PE_* encoding of binary search table, + or DW_EH_PE_omit if not present. + DW_EH_PE_datarel is using address of + .eh_frame_hdr section start as base) + [encoded] eh_frame_ptr (pointer to start of .eh_frame section) + optionally followed by: + [encoded] fde_count (total number of FDEs in .eh_frame section) + fde_count x [encoded] initial_loc, fde + (array of encoded pairs containing + FDE initial_location field and FDE address, + sorted by increasing initial_loc) */ + +boolean +_bfd_elf_write_section_eh_frame_hdr (abfd, sec) + bfd *abfd; + asection *sec; +{ + struct eh_frame_hdr_info *hdr_info; + unsigned int ptr_size; + bfd_byte *contents; + asection *eh_frame_sec; + bfd_size_type size; + + ptr_size = (elf_elfheader (sec->owner)->e_ident[EI_CLASS] + == ELFCLASS64) ? 8 : 4; + + BFD_ASSERT (elf_section_data (sec)->sec_info_type + == ELF_INFO_TYPE_EH_FRAME_HDR); + hdr_info = (struct eh_frame_hdr_info *) + elf_section_data (sec)->sec_info; + size = EH_FRAME_HDR_SIZE; + if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count) + size += 4 + hdr_info->fde_count * 8; + contents = bfd_malloc (size); + if (contents == NULL) + return false; + + eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame"); + if (eh_frame_sec == NULL) + return false; + + memset (contents, 0, EH_FRAME_HDR_SIZE); + contents[0] = 1; /* Version */ + contents[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; /* .eh_frame offset */ + if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count) + { + contents[2] = DW_EH_PE_udata4; /* FDE count encoding */ + contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; /* search table enc */ + } + else + { + contents[2] = DW_EH_PE_omit; + contents[3] = DW_EH_PE_omit; + } + bfd_put_32 (abfd, eh_frame_sec->vma - sec->output_section->vma - 4, + contents + 4); + if (contents[2] != DW_EH_PE_omit) + { + unsigned int i; + + bfd_put_32 (abfd, hdr_info->fde_count, contents + EH_FRAME_HDR_SIZE); + qsort (hdr_info->array, hdr_info->fde_count, sizeof (*hdr_info->array), + vma_compare); + for (i = 0; i < hdr_info->fde_count; i++) + { + bfd_put_32 (abfd, + hdr_info->array[i].initial_loc + - sec->output_section->vma, + contents + EH_FRAME_HDR_SIZE + i * 8 + 4); + bfd_put_32 (abfd, + hdr_info->array[i].fde - sec->output_section->vma, + contents + EH_FRAME_HDR_SIZE + i * 8 + 8); + } + } + + return bfd_set_section_contents (abfd, sec->output_section, + contents, (file_ptr) sec->output_offset, + sec->_cooked_size); +} diff --git a/bfd/elf.c b/bfd/elf.c index a473366510e..98d3cd2d9b3 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -812,6 +812,7 @@ _bfd_elf_print_private_bfd_data (abfd, farg) case PT_NOTE: pt = "NOTE"; break; case PT_SHLIB: pt = "SHLIB"; break; case PT_PHDR: pt = "PHDR"; break; + case PT_GNU_EH_FRAME: pt = "EH_FRAME"; break; default: sprintf (buf, "0x%lx", p->p_type); pt = buf; break; } fprintf (f, "%8s off 0x", pt); @@ -2794,7 +2795,7 @@ map_sections_to_segments (abfd) asection **hdrpp; boolean phdr_in_segment = true; boolean writable; - asection *dynsec; + asection *dynsec, *eh_frame_hdr; bfd_size_type amt; if (elf_tdata (abfd)->segment_map != NULL) @@ -3034,6 +3035,24 @@ map_sections_to_segments (abfd) } } + /* If there is a .eh_frame_hdr section, throw in a PT_GNU_EH_FRAME + segment. */ + eh_frame_hdr = bfd_get_section_by_name (abfd, ".eh_frame_hdr"); + if (eh_frame_hdr != NULL && (eh_frame_hdr->flags & SEC_LOAD)) + { + amt = sizeof (struct elf_segment_map); + m = (struct elf_segment_map *) bfd_zalloc (abfd, amt); + if (m == NULL) + goto error_return; + m->next = NULL; + m->p_type = PT_GNU_EH_FRAME; + m->count = 1; + m->sections[0] = eh_frame_hdr; + + *pm = m; + pm = &m->next; + } + free (sections); sections = NULL; @@ -3548,6 +3567,13 @@ get_program_header_size (abfd) ++segs; } + if (elf_tdata (abfd)->eh_frame_hdr + && bfd_get_section_by_name (abfd, ".eh_frame_hdr") != NULL) + { + /* We need a PT_GNU_EH_FRAME segment. */ + ++segs; + } + for (s = abfd->sections; s != NULL; s = s->next) { if ((s->flags & SEC_LOAD) != 0 @@ -6392,14 +6418,14 @@ _bfd_elf_rela_local_sym (abfd, sym, sec, rel) + sym->st_value); if ((sec->flags & SEC_MERGE) && ELF_ST_TYPE (sym->st_info) == STT_SECTION - && elf_section_data (sec)->merge_info) + && elf_section_data (sec)->sec_info_type == ELF_INFO_TYPE_MERGE) { asection *msec; msec = sec; rel->r_addend = _bfd_merged_section_offset (abfd, &msec, - elf_section_data (sec)->merge_info, + elf_section_data (sec)->sec_info, sym->st_value + rel->r_addend, (bfd_vma) 0) - relocation; @@ -6417,11 +6443,11 @@ _bfd_elf_rel_local_sym (abfd, sym, psec, addend) { asection *sec = *psec; - if (elf_section_data (sec)->merge_info == NULL) + if (elf_section_data (sec)->sec_info_type != ELF_INFO_TYPE_MERGE) return sym->st_value + addend; return _bfd_merged_section_offset (abfd, psec, - elf_section_data (sec)->merge_info, + elf_section_data (sec)->sec_info, sym->st_value + addend, (bfd_vma) 0); } @@ -6435,9 +6461,15 @@ _bfd_elf_section_offset (abfd, info, sec, offset) struct bfd_elf_section_data *sec_data; sec_data = elf_section_data (sec); - if (sec_data->stab_info != NULL) - return _bfd_stab_section_offset - (abfd, &elf_hash_table (info)->stab_info, - sec, &sec_data->stab_info, offset); - return offset; + switch (sec_data->sec_info_type) + { + case ELF_INFO_TYPE_STABS: + return _bfd_stab_section_offset + (abfd, &elf_hash_table (info)->merge_info, sec, &sec_data->sec_info, + offset); + case ELF_INFO_TYPE_EH_FRAME: + return _bfd_elf_eh_frame_section_offset (abfd, sec, offset); + default: + return offset; + } } diff --git a/bfd/elf64-alpha.c b/bfd/elf64-alpha.c index e3720466f84..81be3506cb9 100644 --- a/bfd/elf64-alpha.c +++ b/bfd/elf64-alpha.c @@ -3406,7 +3406,8 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, unless it has been done already. */ if ((sec->flags & SEC_MERGE) && ELF_ST_TYPE (sym->st_info) == STT_SECTION - && elf_section_data (sec)->merge_info + && (elf_section_data (sec)->sec_info_type + == ELF_INFO_TYPE_MERGE) && (gotent->flags & ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED) == 0) { struct alpha_elf_got_entry *ent; @@ -3421,7 +3422,7 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, ent->addend = _bfd_merged_section_offset (output_bfd, &msec, elf_section_data (sec)-> - merge_info, + sec_info, sym->st_value + ent->addend, (bfd_vma) 0); diff --git a/bfd/elflink.h b/bfd/elflink.h index d78832d60a3..37ea63d4ece 100644 --- a/bfd/elflink.h +++ b/bfd/elflink.h @@ -2214,7 +2214,7 @@ elf_link_add_object_symbols (abfd, info) asection *stab, *stabstr; stab = bfd_get_section_by_name (abfd, ".stab"); - if (stab != NULL) + if (stab != NULL && !(stab->flags & SEC_MERGE)) { stabstr = bfd_get_section_by_name (abfd, ".stabstr"); @@ -2226,8 +2226,10 @@ elf_link_add_object_symbols (abfd, info) if (! _bfd_link_section_stabs (abfd, & hash_table->stab_info, stab, stabstr, - &secdata->stab_info)) + &secdata->sec_info)) goto error_return; + if (secdata->sec_info) + secdata->sec_info_type = ELF_INFO_TYPE_STABS; } } } @@ -2238,10 +2240,18 @@ elf_link_add_object_symbols (abfd, info) asection *s; for (s = abfd->sections; s != NULL; s = s->next) - if ((s->flags & SEC_MERGE) - && ! _bfd_merge_section (abfd, & hash_table->merge_info, s, - & elf_section_data (s)->merge_info)) - goto error_return; + if (s->flags & SEC_MERGE) + { + struct bfd_elf_section_data *secdata; + + secdata = elf_section_data (s); + if (! _bfd_merge_section (abfd, + & hash_table->merge_info, + s, &secdata->sec_info)) + goto error_return; + else if (secdata->sec_info) + secdata->sec_info_type = ELF_INFO_TYPE_MERGE; + } } return true; @@ -2300,6 +2310,16 @@ elf_link_create_dynamic_sections (abfd, info) return false; } + if (! info->traditional_format + && info->hash->creator->flavour == bfd_target_elf_flavour) + { + s = bfd_make_section (abfd, ".eh_frame_hdr"); + if (s == NULL + || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY) + || ! bfd_set_section_alignment (abfd, s, 2)) + return false; + } + /* Create sections to hold version informations. These are removed if they are not needed. */ s = bfd_make_section (abfd, ".gnu.version_d"); @@ -5522,6 +5542,19 @@ elf_bfd_final_link (abfd, info) goto error_return; } + if (info->eh_frame_hdr) + { + o = bfd_get_section_by_name (elf_hash_table (info)->dynobj, + ".eh_frame_hdr"); + if (o + && (elf_section_data (o)->sec_info_type + == ELF_INFO_TYPE_EH_FRAME_HDR)) + { + if (! _bfd_elf_write_section_eh_frame_hdr (abfd, o)) + goto error_return; + } + } + if (finfo.symstrtab != NULL) _bfd_stringtab_free (finfo.symstrtab); if (finfo.contents != NULL) @@ -5671,14 +5704,14 @@ elf_link_sec_merge_syms (h, data) if ((h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && ((sec = h->root.u.def.section)->flags & SEC_MERGE) - && elf_section_data (sec)->merge_info) + && elf_section_data (sec)->sec_info_type == ELF_INFO_TYPE_MERGE) { bfd *output_bfd = (bfd *) data; h->root.u.def.value = _bfd_merged_section_offset (output_bfd, &h->root.u.def.section, - elf_section_data (sec)->merge_info, + elf_section_data (sec)->sec_info, h->root.u.def.value, (bfd_vma) 0); } @@ -6170,11 +6203,12 @@ elf_link_input_bfd (finfo, input_bfd) else if (isym->st_shndx > 0 && isym->st_shndx < SHN_LORESERVE) { isec = section_from_elf_index (input_bfd, isym->st_shndx); - if (isec && elf_section_data (isec)->merge_info + if (isec + && elf_section_data (isec)->sec_info_type == ELF_INFO_TYPE_MERGE && ELF_ST_TYPE (isym->st_info) != STT_SECTION) isym->st_value = _bfd_merged_section_offset (output_bfd, &isec, - elf_section_data (isec)->merge_info, + elf_section_data (isec)->sec_info, isym->st_value, (bfd_vma) 0); } else if (isym->st_shndx == SHN_ABS) @@ -6358,8 +6392,8 @@ elf_link_input_bfd (finfo, input_bfd) && ! bfd_is_abs_section (h->root.u.def.section) && bfd_is_abs_section (h->root.u.def.section ->output_section) - && elf_section_data (h->root.u.def.section)->merge_info - == NULL) + && (elf_section_data (h->root.u.def.section) + ->sec_info_type != ELF_INFO_TYPE_MERGE)) { #if BFD_VERSION_DATE < 20031005 if ((o->flags & SEC_DEBUGGING) != 0) @@ -6391,7 +6425,8 @@ elf_link_input_bfd (finfo, input_bfd) if (sec != NULL && ! bfd_is_abs_section (sec) && bfd_is_abs_section (sec->output_section) - && elf_section_data (sec)->merge_info == NULL) + && (elf_section_data (sec)->sec_info_type + != ELF_INFO_TYPE_MERGE)) { #if BFD_VERSION_DATE < 20031005 if ((o->flags & SEC_DEBUGGING) != 0 @@ -6640,30 +6675,45 @@ elf_link_input_bfd (finfo, input_bfd) { /* Section written out. */ } - else if (elf_section_data (o)->stab_info) + else switch (elf_section_data (o)->sec_info_type) { + case ELF_INFO_TYPE_STABS: if (! (_bfd_write_section_stabs - (output_bfd, &elf_hash_table (finfo->info)->stab_info, - o, &elf_section_data (o)->stab_info, contents))) + (output_bfd, + &elf_hash_table (finfo->info)->stab_info, + o, &elf_section_data (o)->sec_info, contents))) return false; - } - else if (elf_section_data (o)->merge_info) - { + break; + case ELF_INFO_TYPE_MERGE: if (! (_bfd_write_merged_section - (output_bfd, o, elf_section_data (o)->merge_info))) - return false; - } - else - { - bfd_size_type sec_size; - - sec_size = (o->_cooked_size != 0 ? o->_cooked_size : o->_raw_size); - if (! (o->flags & SEC_EXCLUDE) - && ! bfd_set_section_contents (output_bfd, o->output_section, - contents, - (file_ptr) o->output_offset, - sec_size)) + (output_bfd, o, elf_section_data (o)->sec_info))) return false; + break; + case ELF_INFO_TYPE_EH_FRAME: + { + asection *ehdrsec; + + ehdrsec + = bfd_get_section_by_name (elf_hash_table (finfo->info)->dynobj, + ".eh_frame_hdr"); + if (! (_bfd_elf_write_section_eh_frame (output_bfd, o, ehdrsec, + contents))) + return false; + } + break; + default: + { + bfd_size_type sec_size; + + sec_size = (o->_cooked_size != 0 ? o->_cooked_size : o->_raw_size); + if (! (o->flags & SEC_EXCLUDE) + && ! bfd_set_section_contents (output_bfd, o->output_section, + contents, + (file_ptr) o->output_offset, + sec_size)) + return false; + } + break; } } @@ -7881,24 +7931,28 @@ elf_reloc_symbol_deleted_p (offset, cookie) which is true for all known assemblers. */ boolean -elf_bfd_discard_info (info) +elf_bfd_discard_info (output_bfd, info) + bfd *output_bfd; struct bfd_link_info *info; { struct elf_reloc_cookie cookie; - asection *o; + asection *stab, *eh, *ehdr; Elf_Internal_Shdr *symtab_hdr; Elf_External_Sym *freesyms; struct elf_backend_data *bed; bfd *abfd; boolean ret = false; + boolean strip = info->strip == strip_all || info->strip == strip_debugger; if (info->relocateable || info->traditional_format || info->hash->creator->flavour != bfd_target_elf_flavour - || ! is_elf_hash_table (info) - || info->strip == strip_all - || info->strip == strip_debugger) + || ! is_elf_hash_table (info)) return false; + + ehdr = bfd_get_section_by_name (elf_hash_table (info)->dynobj, + ".eh_frame_hdr"); + for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next) { if (bfd_get_flavour (abfd) != bfd_target_elf_flavour) @@ -7909,8 +7963,18 @@ elf_bfd_discard_info (info) if ((abfd->flags & DYNAMIC) != 0) continue; - o = bfd_get_section_by_name (abfd, ".stab"); - if (! o && ! bed->elf_backend_discard_info) + eh = NULL; + if (ehdr) + { + eh = bfd_get_section_by_name (abfd, ".eh_frame"); + if (eh && eh->_raw_size == 0) + eh = NULL; + } + + stab = strip ? NULL : bfd_get_section_by_name (abfd, ".stab"); + if ((! stab || elf_section_data(stab)->sec_info_type != ELF_INFO_TYPE_STABS) + && ! eh + && (strip || ! bed->elf_backend_discard_info)) continue; symtab_hdr = &elf_tdata (abfd)->symtab_hdr; @@ -7955,19 +8019,19 @@ elf_bfd_discard_info (info) } } - if (o) + if (stab) { cookie.rels = (NAME(_bfd_elf,link_read_relocs) - (abfd, o, (PTR) NULL, + (abfd, stab, (PTR) NULL, (Elf_Internal_Rela *) NULL, info->keep_memory)); if (cookie.rels) { cookie.rel = cookie.rels; cookie.relend = - cookie.rels + o->reloc_count * bed->s->int_rels_per_ext_rel; - if (_bfd_discard_section_stabs (abfd, o, - elf_section_data (o)->stab_info, + cookie.rels + stab->reloc_count * bed->s->int_rels_per_ext_rel; + if (_bfd_discard_section_stabs (abfd, stab, + elf_section_data (stab)->sec_info, elf_reloc_symbol_deleted_p, &cookie)) ret = true; @@ -7976,6 +8040,30 @@ elf_bfd_discard_info (info) } } + if (eh) + { + cookie.rels = NULL; + cookie.rel = NULL; + cookie.relend = NULL; + if (eh->reloc_count) + cookie.rels = (NAME(_bfd_elf,link_read_relocs) + (abfd, eh, (PTR) NULL, + (Elf_Internal_Rela *) NULL, + info->keep_memory)); + if (cookie.rels) + { + cookie.rel = cookie.rels; + cookie.relend = + cookie.rels + eh->reloc_count * bed->s->int_rels_per_ext_rel; + } + if (_bfd_elf_discard_section_eh_frame (abfd, info, eh, ehdr, + elf_reloc_symbol_deleted_p, + &cookie)) + ret = true; + if (! info->keep_memory) + free (cookie.rels); + } + if (bed->elf_backend_discard_info) { if (bed->elf_backend_discard_info (abfd, &cookie, info)) @@ -7985,6 +8073,11 @@ elf_bfd_discard_info (info) if (freesyms) free (freesyms); } + + if (ehdr + && _bfd_elf_discard_section_eh_frame_hdr (output_bfd, + info, ehdr)) + ret = true; return ret; } @@ -7992,13 +8085,19 @@ static boolean elf_section_ignore_discarded_relocs (sec) asection *sec; { - if (strcmp (sec->name, ".stab") == 0) - return true; - else if ((get_elf_backend_data (sec->owner) - ->elf_backend_ignore_discarded_relocs != NULL) - && (*get_elf_backend_data (sec->owner) - ->elf_backend_ignore_discarded_relocs) (sec)) + switch (elf_section_data (sec)->sec_info_type) + { + case ELF_INFO_TYPE_STABS: + case ELF_INFO_TYPE_EH_FRAME: + return true; + default: + break; + } + if ((get_elf_backend_data (sec->owner)->elf_backend_ignore_discarded_relocs + != NULL) + && (*get_elf_backend_data (sec->owner) + ->elf_backend_ignore_discarded_relocs) (sec)) return true; - else - return false; + + return false; } diff --git a/bfd/elfxx-ia64.c b/bfd/elfxx-ia64.c index ca1d7e1966c..6abd6909dd7 100644 --- a/bfd/elfxx-ia64.c +++ b/bfd/elfxx-ia64.c @@ -3489,7 +3489,8 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section, value = _bfd_elf_rela_local_sym (output_bfd, sym, sym_sec, rel); if ((sym_sec->flags & SEC_MERGE) && ELF_ST_TYPE (sym->st_info) == STT_SECTION - && elf_section_data (sym_sec)->merge_info) + && (elf_section_data (sym_sec)->sec_info_type + == ELF_INFO_TYPE_MERGE)) { struct elfNN_ia64_local_hash_entry *loc_h; @@ -3505,7 +3506,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section, dynent->addend = _bfd_merged_section_offset (output_bfd, &msec, elf_section_data (msec)-> - merge_info, + sec_info, sym->st_value + dynent->addend, (bfd_vma) 0); diff --git a/binutils/ChangeLog b/binutils/ChangeLog index dee8f60e950..f08b7d2b185 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,7 @@ +2001-12-13 Jakub Jelinek + + * readelf.c (get_segment_type): Support PT_GNU_EH_FRAME. + 2001-12-11 Alan Modra * readelf.c (process_file_header): Print extended e_shnum and diff --git a/binutils/readelf.c b/binutils/readelf.c index b9e3d83a489..73a98b5c44b 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -1827,6 +1827,9 @@ get_segment_type (p_type) case PT_SHLIB: return "SHLIB"; case PT_PHDR: return "PHDR"; + case PT_GNU_EH_FRAME: + return "GNU_EH_FRAME"; + default: if ((p_type >= PT_LOPROC) && (p_type <= PT_HIPROC)) { diff --git a/include/ChangeLog b/include/ChangeLog index 5a13fee45e9..2313517e409 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,8 @@ +2001-12-13 Jakub Jelinek + + * elf/common.h (PT_GNU_EH_FRAME): Define. + * bfdlink.h (struct bfd_link_info): Add eh_frame_hdr field. + 2001-12-07 Geoffrey Keating * dis-asm.h (print_insn_xstormy16): Declare. diff --git a/include/bfdlink.h b/include/bfdlink.h index 0865594380d..c02a1e82046 100644 --- a/include/bfdlink.h +++ b/include/bfdlink.h @@ -298,6 +298,10 @@ struct bfd_link_info Setting this true may result in a non-sharable text segment. */ boolean nocopyreloc; + /* True if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment + should be created. */ + boolean eh_frame_hdr; + /* How many spare .dynamic DT_NULL entries should be added? */ unsigned int spare_dynamic_tags; }; diff --git a/include/elf/common.h b/include/elf/common.h index 1c25f9ff39a..36a0b4dd81b 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -256,6 +256,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define PT_LOPROC 0x70000000 /* Processor-specific */ #define PT_HIPROC 0x7FFFFFFF /* Processor-specific */ +#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550) + /* Program segment permissions, in program header p_flags field. */ #define PF_X (1 << 0) /* Segment is executable */ diff --git a/ld/ChangeLog b/ld/ChangeLog index 9e05021beda..2b470a10e40 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,15 @@ +2001-12-13 Jakub Jelinek + + * emultempl/elf32.em (finish): Supply output_bfd + to bfd_elf*_discard_info. + (OPTION_EH_FRAME_HDR): Define. + (longopts): Add --eh-frame-hdr. + (parse_args): Handle it. + (list_options): Add --eh-frame-hdr to help. + * emultempl/hppaelf.em (finish): Supply output_bfd + to bfd_elf*_discard_info. + * scripttempl/elf.sc (.eh_frame_hdr): Add. + 2001-12-13 Alan Modra * lexsup.c (parse_args): Don't pass shortopts to second call to diff --git a/ld/emultempl/elf32.em b/ld/emultempl/elf32.em index 9974b24e889..ef715950595 100644 --- a/ld/emultempl/elf32.em +++ b/ld/emultempl/elf32.em @@ -1329,7 +1329,7 @@ cat >>e${EMULATION_NAME}.c <head, abs_output_section, @@ -1431,6 +1431,7 @@ cat >>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <>e${EMULATION_NAME}.c <