X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=bfd%2Felf-eh-frame.c;h=8c3712f7f23a906a5fa08df8955a686d47a0c222;hb=999bf65c4b277ffc40dc32f377cf99de0f9684b3;hp=f8386d340e3251b0b393d1f45331ef70940a6f30;hpb=fc802241f3609e0bda5f9bc0922880b8c092a114;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c index f8386d340e..8c3712f7f2 100644 --- a/bfd/elf-eh-frame.c +++ b/bfd/elf-eh-frame.c @@ -1,6 +1,5 @@ /* .eh_frame section optimization. - Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007 - Free Software Foundation, Inc. + Copyright (C) 2001-2014 Free Software Foundation, Inc. Written by Jakub Jelinek . This file is part of BFD, the Binary File Descriptor library. @@ -24,7 +23,7 @@ #include "bfd.h" #include "libbfd.h" #include "elf-bfd.h" -#include "elf/dwarf2.h" +#include "dwarf2.h" #define EH_FRAME_HDR_SIZE 8 @@ -42,6 +41,7 @@ struct cie union { struct elf_link_hash_entry *h; bfd_vma val; + unsigned int reloc_index; } personality; asection *output_sec; struct eh_cie_fde *cie_inf; @@ -214,8 +214,8 @@ write_value (bfd *abfd, bfd_byte *buf, bfd_vma value, int width) static int cie_eq (const void *e1, const void *e2) { - const struct cie *c1 = e1; - const struct cie *c2 = e2; + const struct cie *c1 = (const struct cie *) e1; + const struct cie *c2 = (const struct cie *) e2; if (c1->hash == c2->hash && c1->length == c2->length @@ -234,6 +234,7 @@ cie_eq (const void *e1, const void *e2) && c1->lsda_encoding == c2->lsda_encoding && c1->fde_encoding == c2->fde_encoding && c1->initial_insn_length == c2->initial_insn_length + && c1->initial_insn_length <= sizeof (c1->initial_instructions) && memcmp (c1->initial_instructions, c2->initial_instructions, c1->initial_insn_length) == 0) @@ -245,7 +246,7 @@ cie_eq (const void *e1, const void *e2) static hashval_t cie_hash (const void *e) { - const struct cie *c = e; + const struct cie *c = (const struct cie *) e; return c->hash; } @@ -253,6 +254,7 @@ static hashval_t cie_compute_hash (struct cie *c) { hashval_t h = 0; + size_t len; h = iterative_hash_object (c->length, h); h = iterative_hash_object (c->version, h); h = iterative_hash (c->augmentation, strlen (c->augmentation) + 1, h); @@ -266,7 +268,10 @@ cie_compute_hash (struct cie *c) 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); + len = c->initial_insn_length; + if (len > sizeof (c->initial_instructions)) + len = sizeof (c->initial_instructions); + h = iterative_hash (c->initial_instructions, len, h); c->hash = h; return h; } @@ -422,6 +427,28 @@ skip_non_nops (bfd_byte *buf, bfd_byte *end, unsigned int encoded_ptr_width, return last; } +/* Convert absolute encoding ENCODING into PC-relative form. + SIZE is the size of a pointer. */ + +static unsigned char +make_pc_relative (unsigned char encoding, unsigned int ptr_size) +{ + if ((encoding & 0x7f) == DW_EH_PE_absptr) + switch (ptr_size) + { + case 2: + encoding |= DW_EH_PE_sdata2; + break; + case 4: + encoding |= DW_EH_PE_sdata4; + break; + case 8: + encoding |= DW_EH_PE_sdata8; + break; + } + return encoding | DW_EH_PE_pcrel; +} + /* Called before calling _bfd_elf_parse_eh_frame on every input bfd's .eh_frame section. */ @@ -431,8 +458,7 @@ _bfd_elf_begin_eh_frame_parsing (struct bfd_link_info *info) struct eh_frame_hdr_info *hdr_info; hdr_info = &elf_hash_table (info)->eh_info; - if (!hdr_info->parsed_eh_frames && !info->relocatable) - hdr_info->cies = htab_try_create (1, cie_hash, cie_eq, free); + hdr_info->merge_cies = !info->relocatable; } /* Try to parse .eh_frame section SEC, which belongs to ABFD. Store the @@ -453,13 +479,8 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, bfd_byte *last_fde; struct eh_cie_fde *this_inf; unsigned int hdr_length, hdr_id; - struct extended_cie - { - struct cie *cie; - struct eh_cie_fde *local_cie; - } *ecies = NULL, *ecie; - unsigned int ecie_count; - struct cie *cie, *local_cies = NULL, tmp_cie; + unsigned int cie_count; + struct cie *cie, *local_cies = NULL; struct elf_link_hash_table *htab; struct eh_frame_hdr_info *hdr_info; struct eh_frame_sec_info *sec_info = NULL; @@ -473,7 +494,8 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, if (hdr_info->parsed_eh_frames) return; - if (sec->size == 0) + if (sec->size == 0 + || sec->sec_info_type != SEC_INFO_TYPE_NONE) { /* This file does not contain .eh_frame information. */ return; @@ -534,33 +556,30 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, REQUIRE (skip_bytes (&buf, end, hdr_length - 4)); } - sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info) - + (num_entries - 1) * sizeof (struct eh_cie_fde)); + sec_info = (struct eh_frame_sec_info *) + bfd_zmalloc (sizeof (struct eh_frame_sec_info) + + (num_entries - 1) * sizeof (struct eh_cie_fde)); REQUIRE (sec_info); - ecies = bfd_zmalloc (num_cies * sizeof (*ecies)); - REQUIRE (ecies); - - /* If we're not merging CIE entries (such as for a relocatable link), - we need to have a "struct cie" for each CIE in this section. */ - if (hdr_info->cies == NULL) - { - local_cies = bfd_zmalloc (num_cies * sizeof (*local_cies)); - REQUIRE (local_cies); - } + /* We need to have a "struct cie" for each CIE in this section. */ + local_cies = (struct cie *) bfd_zmalloc (num_cies * sizeof (*local_cies)); + REQUIRE (local_cies); + /* FIXME: octets_per_byte. */ #define ENSURE_NO_RELOCS(buf) \ REQUIRE (!(cookie->rel < cookie->relend \ && (cookie->rel->r_offset \ < (bfd_size_type) ((buf) - ehbuf)) \ && cookie->rel->r_info != 0)) + /* FIXME: octets_per_byte. */ #define SKIP_RELOCS(buf) \ while (cookie->rel < cookie->relend \ && (cookie->rel->r_offset \ < (bfd_size_type) ((buf) - ehbuf))) \ cookie->rel++ + /* FIXME: octets_per_byte. */ #define GET_RELOC(buf) \ ((cookie->rel < cookie->relend \ && (cookie->rel->r_offset \ @@ -568,7 +587,7 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, ? cookie->rel : NULL) buf = ehbuf; - ecie_count = 0; + cie_count = 0; gc_mark_hook = get_elf_backend_data (abfd)->gc_mark_hook; while ((bfd_size_type) (buf - ehbuf) != sec->size) { @@ -612,16 +631,9 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, /* CIE */ this_inf->cie = 1; - /* If we're merging CIEs, construct the struct cie in TMP_CIE; - we'll enter it into the global pool later. Otherwise point - CIE to one of the section-local cie structures. */ - if (local_cies) - cie = local_cies + ecie_count; - else - { - cie = &tmp_cie; - memset (cie, 0, sizeof (*cie)); - } + /* Point CIE to one of the section-local cie structures. */ + cie = local_cies + cie_count++; + cie->cie_inf = this_inf; cie->length = hdr_length; cie->output_sec = sec->output_section; @@ -629,7 +641,9 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, REQUIRE (read_byte (&buf, end, &cie->version)); /* Cannot handle unknown versions. */ - REQUIRE (cie->version == 1 || cie->version == 3); + REQUIRE (cie->version == 1 + || cie->version == 3 + || cie->version == 4); REQUIRE (strlen ((char *) buf) < sizeof (cie->augmentation)); strcpy (cie->augmentation, (char *) buf); @@ -644,6 +658,13 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, REQUIRE (skip_bytes (&buf, end, ptr_size)); SKIP_RELOCS (buf); } + if (cie->version >= 4) + { + REQUIRE (buf + 1 < end); + REQUIRE (buf[0] == ptr_size); + REQUIRE (buf[1] == 0); + buf += 2; + } REQUIRE (read_uleb128 (&buf, end, &cie->code_align)); REQUIRE (read_sleb128 (&buf, end, &cie->data_align)); if (cie->version == 1) @@ -690,71 +711,22 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, 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 & 0x70) == DW_EH_PE_aligned) { length = -(buf - ehbuf) & (per_width - 1); REQUIRE (skip_bytes (&buf, end, length)); } + this_inf->u.cie.personality_offset = buf - start; ENSURE_NO_RELOCS (buf); /* Ensure we have a reloc here. */ - if (GET_RELOC (buf) != NULL) - { - unsigned long r_symndx; - -#ifdef BFD64 - if (elf_elfheader (abfd)->e_ident[EI_CLASS] - == ELFCLASS64) - 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 - || ELF_ST_BIND (cookie->locsyms[r_symndx] - .st_info) != STB_LOCAL) - { - 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 = h; - } - else - { - Elf_Internal_Sym *sym; - asection *sym_sec; - bfd_vma val; - - sym = &cookie->locsyms[r_symndx]; - sym_sec = (bfd_section_from_elf_index - (abfd, sym->st_shndx)); - if (sym_sec != NULL) - { - if (sym_sec->kept_section != NULL) - sym_sec = sym_sec->kept_section; - if (sym_sec->output_section != NULL) - { - val = (sym->st_value - + sym_sec->output_offset - + sym_sec->output_section->vma); - cie->personality.val = val; - cie->local_personality = 1; - } - } - } - - /* Cope with MIPS-style composite relocations. */ - do - cookie->rel++; - while (GET_RELOC (buf) != NULL); - } + REQUIRE (GET_RELOC (buf)); + cie->personality.reloc_index + = cookie->rel - cookie->rels; + /* Cope with MIPS-style composite relocations. */ + do + cookie->rel++; + while (GET_RELOC (buf) != NULL); REQUIRE (skip_bytes (&buf, end, per_width)); - REQUIRE (cie->local_personality || cie->personality.h); } break; default: @@ -770,27 +742,23 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, ->elf_backend_can_make_relative_eh_frame (abfd, info, sec))) { - if ((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr) + if ((cie->fde_encoding & 0x70) == DW_EH_PE_absptr) this_inf->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) + && (cie->per_encoding & 0x70) != DW_EH_PE_aligned) { if (*cie->augmentation == 0) this_inf->add_augmentation_size = 1; this_inf->u.cie.add_fde_encoding = 1; this_inf->make_relative = 1; } - } - if (info->shared - && (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->can_make_lsda_relative = 1; + if ((cie->lsda_encoding & 0x70) == DW_EH_PE_absptr) + cie->can_make_lsda_relative = 1; + } /* If FDE encoding was not specified, it defaults to DW_EH_absptr. */ @@ -798,45 +766,53 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, cie->fde_encoding = DW_EH_PE_absptr; initial_insn_length = end - buf; - 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 <= sizeof (cie->initial_instructions) + ? initial_insn_length : sizeof (cie->initial_instructions)); insns = buf; buf += initial_insn_length; ENSURE_NO_RELOCS (buf); + if (hdr_info->merge_cies) + this_inf->u.cie.u.full_cie = cie; this_inf->u.cie.per_encoding_relative = (cie->per_encoding & 0x70) == DW_EH_PE_pcrel; } else { - asection *rsec; - /* 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->local_cie->offset) + for (cie = local_cies; cie < local_cies + cie_count; cie++) + if (cie_offset == cie->cie_inf->offset) break; /* Ensure this FDE references one of the CIEs in this input section. */ - REQUIRE (ecie != ecies + ecie_count); - cie = ecie->cie; - this_inf->u.fde.cie_inf = ecie->local_cie; - this_inf->make_relative = ecie->local_cie->make_relative; + REQUIRE (cie != local_cies + cie_count); + this_inf->u.fde.cie_inf = cie->cie_inf; + this_inf->make_relative = cie->cie_inf->make_relative; this_inf->add_augmentation_size - = ecie->local_cie->add_augmentation_size; + = cie->cie_inf->add_augmentation_size; ENSURE_NO_RELOCS (buf); - REQUIRE (GET_RELOC (buf)); + if ((sec->flags & SEC_LINKER_CREATED) == 0 || cookie->rels != NULL) + { + asection *rsec; - /* Chain together the FDEs for each section. */ - rsec = _bfd_elf_gc_mark_rsec (info, sec, gc_mark_hook, cookie); - REQUIRE (rsec && rsec->owner == abfd); - this_inf->u.fde.next_for_section = elf_fde_list (rsec); - elf_fde_list (rsec) = this_inf; + REQUIRE (GET_RELOC (buf)); + + /* Chain together the FDEs for each section. */ + rsec = _bfd_elf_gc_mark_rsec (info, sec, gc_mark_hook, cookie); + /* RSEC will be NULL if FDE was cleared out as it was belonging to + a discarded SHT_GROUP. */ + if (rsec) + { + REQUIRE (rsec->owner == abfd); + this_inf->u.fde.next_for_section = elf_fde_list (rsec); + elf_fde_list (rsec) = this_inf; + } + } /* Skip the initial location and address range. */ start = buf; @@ -869,6 +845,16 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, insns = buf; buf = last_fde + 4 + hdr_length; + + /* For NULL RSEC (cleared FDE belonging to a discarded section) + the relocations are commonly cleared. We do not sanity check if + all these relocations are cleared as (1) relocations to + .gcc_except_table will remain uncleared (they will get dropped + with the drop of this unused FDE) and (2) BFD already safely drops + relocations of any type to .eh_frame by + elf_section_ignore_discarded_relocs. + TODO: The .gcc_except_table entries should be also filtered as + .eh_frame entries; or GCC could rather use COMDAT for them. */ SKIP_RELOCS (buf); } @@ -892,14 +878,14 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, cie->length -= end - insns_end; } if (set_loc_count - && ((cie->fde_encoding & 0xf0) == DW_EH_PE_pcrel + && ((cie->fde_encoding & 0x70) == DW_EH_PE_pcrel || this_inf->make_relative)) { unsigned int cnt; bfd_byte *p; - this_inf->set_loc = bfd_malloc ((set_loc_count + 1) - * sizeof (unsigned int)); + this_inf->set_loc = (unsigned int *) + bfd_malloc ((set_loc_count + 1) * sizeof (unsigned int)); REQUIRE (this_inf->set_loc); this_inf->set_loc[0] = set_loc_count; p = insns; @@ -915,37 +901,18 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, this_inf->removed = 1; this_inf->fde_encoding = cie->fde_encoding; this_inf->lsda_encoding = cie->lsda_encoding; - if (this_inf->cie) - { - /* We have now finished constructing the struct cie. */ - if (hdr_info->cies != NULL) - { - /* See if we can merge this CIE with an earlier one. */ - void **loc; - - cie_compute_hash (cie); - loc = htab_find_slot_with_hash (hdr_info->cies, cie, - cie->hash, INSERT); - REQUIRE (loc); - if (*loc == HTAB_EMPTY_ENTRY) - { - *loc = malloc (sizeof (struct cie)); - REQUIRE (*loc); - memcpy (*loc, cie, sizeof (struct cie)); - } - cie = (struct cie *) *loc; - } - this_inf->u.cie.u.merged = cie->cie_inf; - ecies[ecie_count].cie = cie; - ecies[ecie_count++].local_cie = this_inf; - } sec_info->count++; } BFD_ASSERT (sec_info->count == num_entries); - BFD_ASSERT (ecie_count == num_cies); + BFD_ASSERT (cie_count == num_cies); elf_section_data (sec)->sec_info = sec_info; - sec->sec_info_type = ELF_INFO_TYPE_EH_FRAME; + sec->sec_info_type = SEC_INFO_TYPE_EH_FRAME; + if (hdr_info->merge_cies) + { + sec_info->cies = local_cies; + local_cies = NULL; + } goto success; free_no_table: @@ -958,8 +925,6 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, success: if (ehbuf) free (ehbuf); - if (ecies) - free (ecies); if (local_cies) free (local_cies); #undef REQUIRE @@ -973,11 +938,6 @@ _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info) struct eh_frame_hdr_info *hdr_info; hdr_info = &elf_hash_table (info)->eh_info; - if (hdr_info->cies != NULL) - { - htab_delete (hdr_info->cies); - hdr_info->cies = NULL; - } hdr_info->parsed_eh_frames = TRUE; } @@ -990,6 +950,7 @@ mark_entry (struct bfd_link_info *info, asection *sec, struct eh_cie_fde *ent, elf_gc_mark_hook_fn gc_mark_hook, struct elf_reloc_cookie *cookie) { + /* FIXME: octets_per_byte. */ for (cookie->rel = cookie->rels + ent->reloc_index; cookie->rel < cookie->relend && cookie->rel->r_offset < ent->offset + ent->size; @@ -1009,7 +970,7 @@ _bfd_elf_gc_mark_fdes (struct bfd_link_info *info, asection *sec, asection *eh_frame, elf_gc_mark_hook_fn gc_mark_hook, struct elf_reloc_cookie *cookie) { - struct eh_cie_fde *fde, *cie, *merged; + struct eh_cie_fde *fde, *cie; for (fde = elf_fde_list (sec); fde; fde = fde->u.fde.next_for_section) { @@ -1019,10 +980,9 @@ _bfd_elf_gc_mark_fdes (struct bfd_link_info *info, asection *sec, /* At this stage, all cie_inf fields point to local CIEs, so we can use the same cookie to refer to them. */ cie = fde->u.fde.cie_inf; - merged = cie->u.cie.u.merged; - if (!merged->u.cie.gc_mark) + if (!cie->u.cie.gc_mark) { - merged->u.cie.gc_mark = 1; + cie->u.cie.gc_mark = 1; if (!mark_entry (info, eh_frame, cie, gc_mark_hook, cookie)) return FALSE; } @@ -1030,6 +990,140 @@ _bfd_elf_gc_mark_fdes (struct bfd_link_info *info, asection *sec, return TRUE; } +/* Input section SEC of ABFD is an .eh_frame section that contains the + CIE described by CIE_INF. Return a version of CIE_INF that is going + to be kept in the output, adding CIE_INF to the output if necessary. + + HDR_INFO is the .eh_frame_hdr information and COOKIE describes the + relocations in REL. */ + +static struct eh_cie_fde * +find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec, + struct eh_frame_hdr_info *hdr_info, + struct elf_reloc_cookie *cookie, + struct eh_cie_fde *cie_inf) +{ + unsigned long r_symndx; + struct cie *cie, *new_cie; + Elf_Internal_Rela *rel; + void **loc; + + /* Use CIE_INF if we have already decided to keep it. */ + if (!cie_inf->removed) + return cie_inf; + + /* If we have merged CIE_INF with another CIE, use that CIE instead. */ + if (cie_inf->u.cie.merged) + return cie_inf->u.cie.u.merged_with; + + cie = cie_inf->u.cie.u.full_cie; + + /* Assume we will need to keep CIE_INF. */ + cie_inf->removed = 0; + cie_inf->u.cie.u.sec = sec; + + /* If we are not merging CIEs, use CIE_INF. */ + if (cie == NULL) + return cie_inf; + + if (cie->per_encoding != DW_EH_PE_omit) + { + bfd_boolean per_binds_local; + + /* Work out the address of personality routine, either as an absolute + value or as a symbol. */ + rel = cookie->rels + cie->personality.reloc_index; + memset (&cie->personality, 0, sizeof (cie->personality)); +#ifdef BFD64 + if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64) + r_symndx = ELF64_R_SYM (rel->r_info); + else +#endif + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx >= cookie->locsymcount + || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL) + { + 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 = h; + per_binds_local = SYMBOL_REFERENCES_LOCAL (info, h); + } + else + { + Elf_Internal_Sym *sym; + asection *sym_sec; + + sym = &cookie->locsyms[r_symndx]; + sym_sec = bfd_section_from_elf_index (abfd, sym->st_shndx); + if (sym_sec == NULL) + return cie_inf; + + if (sym_sec->kept_section != NULL) + sym_sec = sym_sec->kept_section; + if (sym_sec->output_section == NULL) + return cie_inf; + + cie->local_personality = 1; + cie->personality.val = (sym->st_value + + sym_sec->output_offset + + sym_sec->output_section->vma); + per_binds_local = TRUE; + } + + if (per_binds_local + && info->shared + && (cie->per_encoding & 0x70) == DW_EH_PE_absptr + && (get_elf_backend_data (abfd) + ->elf_backend_can_make_relative_eh_frame (abfd, info, sec))) + { + cie_inf->u.cie.make_per_encoding_relative = 1; + cie_inf->u.cie.per_encoding_relative = 1; + } + } + + /* See if we can merge this CIE with an earlier one. */ + cie->output_sec = sec->output_section; + cie_compute_hash (cie); + if (hdr_info->cies == NULL) + { + hdr_info->cies = htab_try_create (1, cie_hash, cie_eq, free); + if (hdr_info->cies == NULL) + return cie_inf; + } + loc = htab_find_slot_with_hash (hdr_info->cies, cie, cie->hash, INSERT); + if (loc == NULL) + return cie_inf; + + new_cie = (struct cie *) *loc; + if (new_cie == NULL) + { + /* Keep CIE_INF and record it in the hash table. */ + new_cie = (struct cie *) malloc (sizeof (struct cie)); + if (new_cie == NULL) + return cie_inf; + + memcpy (new_cie, cie, sizeof (struct cie)); + *loc = new_cie; + } + else + { + /* Merge CIE_INF with NEW_CIE->CIE_INF. */ + cie_inf->removed = 1; + cie_inf->u.cie.merged = 1; + cie_inf->u.cie.u.merged_with = new_cie->cie_inf; + if (cie_inf->u.cie.make_lsda_relative) + new_cie->cie_inf->u.cie.make_lsda_relative = 1; + } + return new_cie->cie_inf; +} + /* 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 @@ -1041,28 +1135,53 @@ _bfd_elf_discard_section_eh_frame bfd_boolean (*reloc_symbol_deleted_p) (bfd_vma, void *), struct elf_reloc_cookie *cookie) { - struct eh_cie_fde *ent, *cie, *merged; + struct eh_cie_fde *ent; struct eh_frame_sec_info *sec_info; struct eh_frame_hdr_info *hdr_info; unsigned int ptr_size, offset; + if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME) + return FALSE; + sec_info = (struct eh_frame_sec_info *) elf_section_data (sec)->sec_info; if (sec_info == NULL) return FALSE; + ptr_size = (get_elf_backend_data (sec->owner) + ->elf_backend_eh_frame_address_size (sec->owner, sec)); + hdr_info = &elf_hash_table (info)->eh_info; for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent) - if (!ent->cie) + if (ent->size == 4) + /* There should only be one zero terminator, on the last input + file supplying .eh_frame (crtend.o). Remove any others. */ + ent->removed = sec->map_head.s != NULL; + else if (!ent->cie) { - cookie->rel = cookie->rels + ent->reloc_index; - BFD_ASSERT (cookie->rel < cookie->relend - && cookie->rel->r_offset == ent->offset + 8); - if (!(*reloc_symbol_deleted_p) (ent->offset + 8, cookie)) + bfd_boolean keep; + if ((sec->flags & SEC_LINKER_CREATED) != 0 && cookie->rels == NULL) + { + unsigned int width + = get_DW_EH_PE_width (ent->fde_encoding, ptr_size); + bfd_vma value + = read_value (abfd, sec->contents + ent->offset + 8 + width, + width, get_DW_EH_PE_signed (ent->fde_encoding)); + keep = value != 0; + } + else + { + cookie->rel = cookie->rels + ent->reloc_index; + /* FIXME: octets_per_byte. */ + BFD_ASSERT (cookie->rel < cookie->relend + && cookie->rel->r_offset == ent->offset + 8); + keep = !(*reloc_symbol_deleted_p) (ent->offset + 8, cookie); + } + if (keep) { if (info->shared - && (((ent->fde_encoding & 0xf0) == DW_EH_PE_absptr + && (((ent->fde_encoding & 0x70) == DW_EH_PE_absptr && ent->make_relative == 0) - || (ent->fde_encoding & 0xf0) == DW_EH_PE_aligned)) + || (ent->fde_encoding & 0x70) == DW_EH_PE_aligned)) { /* If a shared library uses absolute pointers which we cannot turn into PC relative, @@ -1075,33 +1194,17 @@ _bfd_elf_discard_section_eh_frame } ent->removed = 0; hdr_info->fde_count++; - - cie = ent->u.fde.cie_inf; - if (cie->removed) - { - merged = cie->u.cie.u.merged; - if (!merged->removed) - /* We have decided to keep the group representative. */ - ent->u.fde.cie_inf = merged; - else if (merged->u.cie.u.merged != merged) - /* We didn't keep the original group representative, - but we did keep an alternative. */ - ent->u.fde.cie_inf = merged->u.cie.u.merged; - else - { - /* Make the local CIE represent the merged group. */ - merged->u.cie.u.merged = cie; - cie->removed = 0; - cie->u.cie.u.sec = sec; - cie->u.cie.make_lsda_relative - = merged->u.cie.make_lsda_relative; - } - } + ent->u.fde.cie_inf = find_merged_cie (abfd, info, sec, hdr_info, + cookie, ent->u.fde.cie_inf); } } - ptr_size = (get_elf_backend_data (sec->owner) - ->elf_backend_eh_frame_address_size (sec->owner, sec)); + if (sec_info->cies) + { + free (sec_info->cies); + sec_info->cies = NULL; + } + offset = 0; for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent) if (!ent->removed) @@ -1129,6 +1232,12 @@ _bfd_elf_discard_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info) 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; @@ -1137,10 +1246,30 @@ _bfd_elf_discard_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info) if (hdr_info->table) sec->size += 4 + hdr_info->fde_count * 8; - elf_tdata (abfd)->eh_frame_hdr = sec; + elf_eh_frame_hdr (abfd) = sec; return TRUE; } +/* Return true if there is at least one non-empty .eh_frame section in + input files. Can only be called after ld has mapped input to + output sections, and before sections are stripped. */ +bfd_boolean +_bfd_elf_eh_frame_present (struct bfd_link_info *info) +{ + asection *eh = bfd_get_section_by_name (info->output_bfd, ".eh_frame"); + + if (eh == NULL) + return FALSE; + + /* Count only sections which have at least a single CIE or FDE. + There cannot be any CIE or FDE <= 8 bytes. */ + for (eh = eh->map_head.s; eh != NULL; eh = eh->map_head.s) + if (eh->size > 8) + return TRUE; + + return FALSE; +} + /* This function is called from size_dynamic_sections. It needs to decide whether .eh_frame_hdr should be output or not, because when the dynamic symbol table has been sized it is too late @@ -1149,8 +1278,6 @@ _bfd_elf_discard_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info) bfd_boolean _bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info) { - asection *o; - bfd *abfd; struct elf_link_hash_table *htab; struct eh_frame_hdr_info *hdr_info; @@ -1159,24 +1286,9 @@ _bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info) if (hdr_info->hdr_sec == NULL) return TRUE; - if (bfd_is_abs_section (hdr_info->hdr_sec->output_section)) - { - hdr_info->hdr_sec = NULL; - return TRUE; - } - - abfd = NULL; - if (info->eh_frame_hdr) - for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next) - { - /* Count only sections which have at least a single CIE or FDE. - There cannot be any CIE or FDE <= 8 bytes. */ - o = bfd_get_section_by_name (abfd, ".eh_frame"); - if (o && o->size > 8 && !bfd_is_abs_section (o->output_section)) - break; - } - - if (abfd == NULL) + if (bfd_is_abs_section (hdr_info->hdr_sec->output_section) + || !info->eh_frame_hdr + || !_bfd_elf_eh_frame_present (info)) { hdr_info->hdr_sec->flags |= SEC_EXCLUDE; hdr_info->hdr_sec = NULL; @@ -1194,25 +1306,20 @@ _bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info) bfd_vma _bfd_elf_eh_frame_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED, - struct bfd_link_info *info, + struct bfd_link_info *info ATTRIBUTE_UNUSED, asection *sec, bfd_vma offset) { struct eh_frame_sec_info *sec_info; - struct elf_link_hash_table *htab; - struct eh_frame_hdr_info *hdr_info; unsigned int lo, hi, mid; - if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME) + if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME) return offset; - sec_info = elf_section_data (sec)->sec_info; + sec_info = (struct eh_frame_sec_info *) elf_section_data (sec)->sec_info; if (offset >= sec->rawsize) return offset - sec->rawsize + sec->size; - htab = elf_hash_table (info); - hdr_info = &htab->eh_info; - lo = 0; hi = sec_info->count; mid = 0; @@ -1234,6 +1341,14 @@ _bfd_elf_eh_frame_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED, if (sec_info->entry[mid].removed) return (bfd_vma) -1; + /* If converting personality pointers to DW_EH_PE_pcrel, there will be + no need for run-time relocation against the personality field. */ + if (sec_info->entry[mid].cie + && sec_info->entry[mid].u.cie.make_per_encoding_relative + && offset == (sec_info->entry[mid].offset + 8 + + sec_info->entry[mid].u.cie.personality_offset)) + return (bfd_vma) -2; + /* 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].cie @@ -1286,7 +1401,8 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, unsigned int ptr_size; struct eh_cie_fde *ent; - if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME) + if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME) + /* FIXME: octets_per_byte. */ return bfd_set_section_contents (abfd, sec->output_section, contents, sec->output_offset, sec->size); @@ -1294,13 +1410,13 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, ->elf_backend_eh_frame_address_size (abfd, sec)); BFD_ASSERT (ptr_size != 0); - sec_info = elf_section_data (sec)->sec_info; + sec_info = (struct eh_frame_sec_info *) elf_section_data (sec)->sec_info; htab = elf_hash_table (info); hdr_info = &htab->eh_info; if (hdr_info->table && hdr_info->array == NULL) - hdr_info->array - = bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array)); + hdr_info->array = (struct eh_frame_array_ent *) + bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array)); if (hdr_info->array == NULL) hdr_info = NULL; @@ -1392,7 +1508,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, { BFD_ASSERT (action & 1); *aug++ = 'R'; - *buf++ = DW_EH_PE_pcrel; + *buf++ = make_pc_relative (DW_EH_PE_absptr, ptr_size); action &= ~1; } @@ -1403,18 +1519,20 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, if (action & 2) { BFD_ASSERT (*buf == ent->lsda_encoding); - *buf |= DW_EH_PE_pcrel; + *buf = make_pc_relative (*buf, ptr_size); action &= ~2; } buf++; break; case 'P': + if (ent->u.cie.make_per_encoding_relative) + *buf = make_pc_relative (*buf, ptr_size); per_encoding = *buf++; per_width = get_DW_EH_PE_width (per_encoding, ptr_size); BFD_ASSERT (per_width != 0); BFD_ASSERT (((per_encoding & 0x70) == DW_EH_PE_pcrel) == ent->u.cie.per_encoding_relative); - if ((per_encoding & 0xf0) == DW_EH_PE_aligned) + if ((per_encoding & 0x70) == DW_EH_PE_aligned) buf = (contents + ((buf - contents + per_width - 1) & ~((bfd_size_type) per_width - 1))); @@ -1424,8 +1542,15 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, val = read_value (abfd, buf, per_width, get_DW_EH_PE_signed (per_encoding)); - val += ent->offset - ent->new_offset; - val -= extra_string + extra_data; + if (ent->u.cie.make_per_encoding_relative) + val -= (sec->output_section->vma + + sec->output_offset + + (buf - contents)); + else + { + val += (bfd_vma) ent->offset - ent->new_offset; + val -= extra_string + extra_data; + } write_value (abfd, buf, val, per_width); action &= ~4; } @@ -1435,7 +1560,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, if (action & 1) { BFD_ASSERT (*buf == ent->fde_encoding); - *buf |= DW_EH_PE_pcrel; + *buf = make_pc_relative (*buf, ptr_size); action &= ~1; } buf++; @@ -1468,22 +1593,42 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, address = value; if (value) { - switch (ent->fde_encoding & 0xf0) + switch (ent->fde_encoding & 0x70) { - 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; + switch (abfd->arch_info->arch) + { + case bfd_arch_ia64: + BFD_ASSERT (elf_gp (abfd) != 0); + address += elf_gp (abfd); + break; + default: + (*info->callbacks->einfo) + (_("%P: DW_EH_PE_datarel unspecified" + " for this architecture.\n")); + /* Fall thru */ + case bfd_arch_frv: + case bfd_arch_i386: + BFD_ASSERT (htab->hgot != NULL + && ((htab->hgot->root.type + == bfd_link_hash_defined) + || (htab->hgot->root.type + == bfd_link_hash_defweak))); + address + += (htab->hgot->root.u.def.value + + htab->hgot->root.u.def.section->output_offset + + (htab->hgot->root.u.def.section->output_section + ->vma)); + break; + } } break; case DW_EH_PE_pcrel: - value += ent->offset - ent->new_offset; + value += (bfd_vma) ent->offset - ent->new_offset; address += (sec->output_section->vma + sec->output_offset + ent->offset + 8); @@ -1500,6 +1645,11 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, if (hdr_info) { + /* The address calculation may overflow, giving us a + value greater than 4G on a 32-bit target when + dwarf_vma is 64-bit. */ + if (sizeof (address) > 4 && ptr_size == 4) + address &= 0xffffffff; hdr_info->array[hdr_info->array_count].initial_loc = address; hdr_info->array[hdr_info->array_count++].fde = (sec->output_section->vma @@ -1507,7 +1657,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, + ent->new_offset); } - if ((ent->lsda_encoding & 0xf0) == DW_EH_PE_pcrel + if ((ent->lsda_encoding & 0x70) == DW_EH_PE_pcrel || cie->u.cie.make_lsda_relative) { buf += ent->lsda_offset; @@ -1516,8 +1666,8 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, get_DW_EH_PE_signed (ent->lsda_encoding)); if (value) { - if ((ent->lsda_encoding & 0xf0) == DW_EH_PE_pcrel) - value += ent->offset - ent->new_offset; + if ((ent->lsda_encoding & 0x70) == DW_EH_PE_pcrel) + value += (bfd_vma) ent->offset - ent->new_offset; else if (cie->u.cie.make_lsda_relative) value -= (sec->output_section->vma + sec->output_offset @@ -1537,7 +1687,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, if (ent->set_loc) { /* Adjust DW_CFA_set_loc. */ - unsigned int cnt, width; + unsigned int cnt; bfd_vma new_offset; width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size); @@ -1547,7 +1697,6 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, for (cnt = 1; cnt <= ent->set_loc[0]; cnt++) { - bfd_vma value; buf = start + ent->set_loc[cnt]; value = read_value (abfd, buf, width, @@ -1555,8 +1704,8 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, if (!value) continue; - if ((ent->fde_encoding & 0xf0) == DW_EH_PE_pcrel) - value += ent->offset + 8 - new_offset; + if ((ent->fde_encoding & 0x70) == DW_EH_PE_pcrel) + value += (bfd_vma) ent->offset + 8 - new_offset; if (ent->make_relative) value -= (sec->output_section->vma + sec->output_offset @@ -1575,6 +1724,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, if ((sec->size % ptr_size) != 0) abort (); + /* FIXME: octets_per_byte. */ return bfd_set_section_contents (abfd, sec->output_section, contents, (file_ptr) sec->output_offset, sec->size); @@ -1586,8 +1736,8 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, static int vma_compare (const void *a, const void *b) { - const struct eh_frame_array_ent *p = a; - const struct eh_frame_array_ent *q = b; + const struct eh_frame_array_ent *p = (const struct eh_frame_array_ent *) a; + const struct eh_frame_array_ent *q = (const struct eh_frame_array_ent *) b; if (p->initial_loc > q->initial_loc) return 1; if (p->initial_loc < q->initial_loc) @@ -1623,73 +1773,81 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info) struct elf_link_hash_table *htab; struct eh_frame_hdr_info *hdr_info; asection *sec; - bfd_byte *contents; - asection *eh_frame_sec; - bfd_size_type size; - bfd_boolean retval; - bfd_vma encoded_eh_frame; + bfd_boolean retval = TRUE; htab = elf_hash_table (info); hdr_info = &htab->eh_info; sec = hdr_info->hdr_sec; - if (sec == NULL) - return TRUE; - - 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) + if (info->eh_frame_hdr && sec != NULL) { - free (contents); - return FALSE; - } + bfd_byte *contents; + asection *eh_frame_sec; + bfd_size_type size; + bfd_vma encoded_eh_frame; + + 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_byte *) bfd_malloc (size); + if (contents == NULL) + return FALSE; - memset (contents, 0, EH_FRAME_HDR_SIZE); - contents[0] = 1; /* Version. */ - contents[1] = get_elf_backend_data (abfd)->elf_backend_encode_eh_address - (abfd, info, eh_frame_sec, 0, sec, 4, - &encoded_eh_frame); /* .eh_frame offset. */ + eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame"); + if (eh_frame_sec == NULL) + { + free (contents); + return FALSE; + } - 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, encoded_eh_frame, contents + 4); + memset (contents, 0, EH_FRAME_HDR_SIZE); + /* Version. */ + contents[0] = 1; + /* .eh_frame offset. */ + contents[1] = get_elf_backend_data (abfd)->elf_backend_encode_eh_address + (abfd, info, eh_frame_sec, 0, sec, 4, &encoded_eh_frame); - if (contents[2] != DW_EH_PE_omit) - { - unsigned int i; + if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count) + { + /* FDE count encoding. */ + contents[2] = DW_EH_PE_udata4; + /* Search table encoding. */ + contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; + } + else + { + contents[2] = DW_EH_PE_omit; + contents[3] = DW_EH_PE_omit; + } + bfd_put_32 (abfd, encoded_eh_frame, contents + 4); - 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++) + if (contents[2] != DW_EH_PE_omit) { - 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); + 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); + } } - } - retval = bfd_set_section_contents (abfd, sec->output_section, - contents, (file_ptr) sec->output_offset, - sec->size); - free (contents); + /* FIXME: octets_per_byte. */ + retval = bfd_set_section_contents (abfd, sec->output_section, contents, + (file_ptr) sec->output_offset, + sec->size); + free (contents); + } + if (hdr_info->array != NULL) + free (hdr_info->array); return retval; }