diff options
Diffstat (limited to 'binutils-2.25/bfd/elf-eh-frame.c')
-rw-r--r-- | binutils-2.25/bfd/elf-eh-frame.c | 171 |
1 files changed, 104 insertions, 67 deletions
diff --git a/binutils-2.25/bfd/elf-eh-frame.c b/binutils-2.25/bfd/elf-eh-frame.c index 832a9919..002932d1 100644 --- a/binutils-2.25/bfd/elf-eh-frame.c +++ b/binutils-2.25/bfd/elf-eh-frame.c @@ -1,6 +1,5 @@ /* .eh_frame section optimization. - Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, - 2012 Free Software Foundation, Inc. + Copyright (C) 2001-2014 Free Software Foundation, Inc. Written by Jakub Jelinek <jakub@redhat.com>. This file is part of BFD, the Binary File Descriptor library. @@ -41,10 +40,12 @@ struct cie bfd_vma augmentation_size; union { struct elf_link_hash_entry *h; - bfd_vma val; + struct { + unsigned int bfd_id; + unsigned int index; + } sym; unsigned int reloc_index; } personality; - asection *output_sec; struct eh_cie_fde *cie_inf; unsigned char per_encoding; unsigned char lsda_encoding; @@ -230,11 +231,13 @@ cie_eq (const void *e1, const void *e2) && c1->augmentation_size == c2->augmentation_size && memcmp (&c1->personality, &c2->personality, sizeof (c1->personality)) == 0 - && c1->output_sec == c2->output_sec + && (c1->cie_inf->u.cie.u.sec->output_section + == c2->cie_inf->u.cie.u.sec->output_section) && 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 + && c1->initial_insn_length <= sizeof (c1->initial_instructions) && memcmp (c1->initial_instructions, c2->initial_instructions, c1->initial_insn_length) == 0) @@ -254,6 +257,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); @@ -262,12 +266,15 @@ cie_compute_hash (struct cie *c) h = iterative_hash_object (c->ra_column, h); h = iterative_hash_object (c->augmentation_size, h); h = iterative_hash_object (c->personality, h); - h = iterative_hash_object (c->output_sec, h); + h = iterative_hash_object (c->cie_inf->u.cie.u.sec->output_section, h); h = iterative_hash_object (c->per_encoding, h); 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; } @@ -445,18 +452,6 @@ make_pc_relative (unsigned char encoding, unsigned int ptr_size) return encoding | DW_EH_PE_pcrel; } -/* Called before calling _bfd_elf_parse_eh_frame on every input bfd's - .eh_frame section. */ - -void -_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; - hdr_info->merge_cies = !info->relocatable; -} - /* Try to parse .eh_frame section SEC, which belongs to ABFD. Store the information in the section's sec_info field on success. COOKIE describes the relocations in SEC. */ @@ -487,8 +482,6 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, htab = elf_hash_table (info); hdr_info = &htab->eh_info; - if (hdr_info->parsed_eh_frames) - return; if (sec->size == 0 || sec->sec_info_type != SEC_INFO_TYPE_NONE) @@ -563,10 +556,13 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, /* 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)) + while (cookie->rel < cookie->relend \ + && (cookie->rel->r_offset \ + < (bfd_size_type) ((buf) - ehbuf))) \ + { \ + REQUIRE (cookie->rel->r_info == 0); \ + cookie->rel++; \ + } /* FIXME: octets_per_byte. */ #define SKIP_RELOCS(buf) \ @@ -632,7 +628,6 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, cie->cie_inf = this_inf; cie->length = hdr_length; - cie->output_sec = sec->output_section; start = buf; REQUIRE (read_byte (&buf, end, &cie->version)); @@ -734,6 +729,7 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, /* For shared libraries, try to get rid of as many RELATIVE relocs as possible. */ if (info->shared + && !info->relocatable && (get_elf_backend_data (abfd) ->elf_backend_can_make_relative_eh_frame (abfd, info, sec))) @@ -762,19 +758,21 @@ _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; + if (!info->relocatable) + { + /* Keep info for merging 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 { @@ -816,6 +814,16 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size); REQUIRE (skip_bytes (&buf, end, 2 * length)); + SKIP_RELOCS (buf - length); + if (!GET_RELOC (buf - length) + && read_value (abfd, buf - length, length, FALSE) == 0) + { + (*info->callbacks->minfo) + (_("discarding zero address range FDE in %B(%A).\n"), + abfd, sec); + this_inf->u.fde.cie_inf = NULL; + } + /* Skip the augmentation size, if present. */ if (cie->augmentation[0] == 'z') REQUIRE (read_uleb128 (&buf, end, &length)); @@ -905,8 +913,9 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, elf_section_data (sec)->sec_info = sec_info; sec->sec_info_type = SEC_INFO_TYPE_EH_FRAME; - if (hdr_info->merge_cies) + if (!info->relocatable) { + /* Keep info for merging cies. */ sec_info->cies = local_cies; local_cies = NULL; } @@ -927,17 +936,6 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, #undef REQUIRE } -/* Finish a pass over all .eh_frame sections. */ - -void -_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; - hdr_info->parsed_eh_frames = TRUE; -} - /* Mark all relocations against CIE or FDE ENT, which occurs in .eh_frame section SEC. COOKIE describes the relocations in SEC; its "rel" field can be changed freely. */ @@ -977,7 +975,7 @@ _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; - if (!cie->u.cie.gc_mark) + if (cie != NULL && !cie->u.cie.gc_mark) { cie->u.cie.gc_mark = 1; if (!mark_entry (info, eh_frame, cie, gc_mark_hook, cookie)) @@ -1027,8 +1025,12 @@ find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec, { bfd_boolean per_binds_local; - /* Work out the address of personality routine, either as an absolute - value or as a symbol. */ + /* Work out the address of personality routine, or at least + enough info that we could calculate the address had we made a + final section layout. The symbol on the reloc is enough, + either the hash for a global, or (bfd id, index) pair for a + local. The assumption here is that no one uses addends on + the reloc. */ rel = cookie->rels + cie->personality.reloc_index; memset (&cie->personality, 0, sizeof (cie->personality)); #ifdef BFD64 @@ -1068,14 +1070,14 @@ find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec, return cie_inf; cie->local_personality = 1; - cie->personality.val = (sym->st_value - + sym_sec->output_offset - + sym_sec->output_section->vma); + cie->personality.sym.bfd_id = abfd->id; + cie->personality.sym.index = r_symndx; per_binds_local = TRUE; } if (per_binds_local && info->shared + && !info->relocatable && (cie->per_encoding & 0x70) == DW_EH_PE_absptr && (get_elf_backend_data (abfd) ->elf_backend_can_make_relative_eh_frame (abfd, info, sec))) @@ -1086,7 +1088,6 @@ find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec, } /* 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) { @@ -1153,7 +1154,7 @@ _bfd_elf_discard_section_eh_frame /* 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) + else if (!ent->cie && ent->u.fde.cie_inf != NULL) { bfd_boolean keep; if ((sec->flags & SEC_LINKER_CREATED) != 0 && cookie->rels == NULL) @@ -1186,7 +1187,7 @@ _bfd_elf_discard_section_eh_frame since it is affected by runtime relocations. */ hdr_info->table = FALSE; (*info->callbacks->einfo) - (_("%P: fde encoding in %B(%A) prevents .eh_frame_hdr" + (_("%P: FDE encoding in %B(%A) prevents .eh_frame_hdr" " table being created.\n"), abfd, sec); } ent->removed = 0; @@ -1397,6 +1398,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, struct eh_frame_hdr_info *hdr_info; unsigned int ptr_size; struct eh_cie_fde *ent; + bfd_size_type sec_size; if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME) /* FIXME: octets_per_byte. */ @@ -1583,6 +1585,8 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, value = ((ent->new_offset + sec->output_offset + 4) - (cie->new_offset + cie->u.cie.u.sec->output_offset)); bfd_put_32 (abfd, value, buf); + if (info->relocatable) + continue; buf += 4; width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size); value = read_value (abfd, buf, width, @@ -1648,6 +1652,8 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, 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].range + = read_value (abfd, buf + width, width, FALSE); hdr_info->array[hdr_info->array_count++].fde = (sec->output_section->vma + sec->output_offset @@ -1718,7 +1724,11 @@ _bfd_elf_write_section_eh_frame (bfd *abfd, the pointer size. _bfd_elf_discard_section_eh_frame should have padded CIE/FDE records to multiple of pointer size with size_of_output_cie_fde. */ - if ((sec->size % ptr_size) != 0) + sec_size = sec->size; + if (sec_info->count != 0 + && sec_info->entry[sec_info->count - 1].size == 4) + sec_size -= 4; + if ((sec_size % ptr_size) != 0) abort (); /* FIXME: octets_per_byte. */ @@ -1739,6 +1749,10 @@ vma_compare (const void *a, const void *b) return 1; if (p->initial_loc < q->initial_loc) return -1; + if (p->range > q->range) + return 1; + if (p->range < q->range) + return -1; return 0; } @@ -1827,20 +1841,43 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info) 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); + bfd_vma val; + + val = hdr_info->array[i].initial_loc - sec->output_section->vma; + val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000; + if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64 + && (hdr_info->array[i].initial_loc + != sec->output_section->vma + val)) + (*info->callbacks->einfo) + (_("%X%P: .eh_frame_hdr table[%u] PC overflow.\n"), i); + bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 4); + + val = hdr_info->array[i].fde - sec->output_section->vma; + val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000; + if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64 + && (hdr_info->array[i].fde + != sec->output_section->vma + val)) + (*info->callbacks->einfo) + (_("%X%P: .eh_frame_hdr table[%u] FDE overflow.\n"), i); + bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 8); + + if (i != 0 + && (hdr_info->array[i].initial_loc + < (hdr_info->array[i - 1].initial_loc + + hdr_info->array[i - 1].range))) + (*info->callbacks->einfo) + (_("%X%P: .eh_frame_hdr table[%u] FDE at %V overlaps " + "table[%u] FDE at %V.\n"), + i - 1, hdr_info->array[i - 1].fde, + i, hdr_info->array[i].fde); } } /* FIXME: octets_per_byte. */ - retval = bfd_set_section_contents (abfd, sec->output_section, contents, - (file_ptr) sec->output_offset, - sec->size); + if (!bfd_set_section_contents (abfd, sec->output_section, contents, + (file_ptr) sec->output_offset, + sec->size)) + retval = FALSE; free (contents); } if (hdr_info->array != NULL) |