From 63984be23dee3df45590f25e176ce4985691ebb3 Mon Sep 17 00:00:00 2001 From: Alexey Neyman Date: Wed, 18 Dec 2013 12:11:29 -0800 Subject: [PATCH] Allow .eh_frame reader to cope with unordered relocations Unordered relocations can result from partial linking of file with different text sections (e.g., .text and .init). --- bfd/elf-eh-frame.c | 149 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 127 insertions(+), 22 deletions(-) diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c index 832a991..2457c4f 100644 --- a/bfd/elf-eh-frame.c +++ b/bfd/elf-eh-frame.c @@ -457,6 +457,107 @@ _bfd_elf_begin_eh_frame_parsing (struct bfd_link_info *info) hdr_info->merge_cies = !info->relocatable; } +struct reloc_read_ops +{ + Elf_Internal_Rela * (*chk_relocs) (struct elf_reloc_cookie *, + bfd_vma, bfd_vma); + Elf_Internal_Rela * (*get_reloc) (struct elf_reloc_cookie *, bfd_vma); + void (*skip_relocs) (struct elf_reloc_cookie *, bfd_vma, bfd_vma *); +}; + +/* Implementation of the ENSURE_NO_RELOCS macro for ordered relocations. */ +static Elf_Internal_Rela * +_bfd_chk_relocs_ordered (struct elf_reloc_cookie *cookie, + bfd_vma rels_offset ATTRIBUTE_UNUSED, + bfd_vma offset) +{ + /* FIXME: octets_per_byte. */ + if (cookie->rel < cookie->relend + && cookie->rel->r_offset < offset + && cookie->rel->r_info != 0) + return cookie->rel; + return NULL; +} + +/* Implementation of the GET_RELOC macro for ordered relocations. */ +static Elf_Internal_Rela * +_bfd_get_reloc_ordered (struct elf_reloc_cookie *cookie, bfd_vma offset) +{ + /* FIXME: octets_per_byte. */ + return cookie->rel < cookie->relend && cookie->rel->r_offset == offset + ? cookie->rel : NULL; +} + +/* Implementation of the SKIP_RELOCS macro for ordered relocations. */ +static void +_bfd_skip_relocs_ordered (struct elf_reloc_cookie *cookie, bfd_vma offset, + bfd_vma *rels_offset ATTRIBUTE_UNUSED) +{ + /* FIXME: octets_per_byte. */ + while (cookie->rel < cookie->relend && cookie->rel->r_offset < offset) + cookie->rel++; +} + +static struct reloc_read_ops _bfd_eh_ordered_relocs = { + .chk_relocs = _bfd_chk_relocs_ordered, + .get_reloc = _bfd_get_reloc_ordered, + .skip_relocs = _bfd_skip_relocs_ordered, +}; + +/* Implementation of the ENSURE_NO_RELOCS macro for unordered relocations. */ +static Elf_Internal_Rela * +_bfd_chk_relocs_unordered (struct elf_reloc_cookie *cookie, + bfd_vma rels_offset, bfd_vma offset) +{ + Elf_Internal_Rela *rel; + + /* FIXME: octets_per_byte. */ + for (rel = cookie->rels; rel < cookie->relend; rel++) + if (rel->r_offset >= rels_offset + && rel->r_offset < offset + && rel->r_info != 0) + return rel; + return NULL; +} + +/* Implementation of the GET_RELOC macro for unordered relocations. */ +static Elf_Internal_Rela * +_bfd_get_reloc_unordered (struct elf_reloc_cookie *cookie, bfd_vma offset) +{ + Elf_Internal_Rela *rel; + + /* FIXME: octets_per_byte. */ + for (rel = cookie->rels; rel < cookie->relend; rel++) + if (rel->r_offset == offset) + return rel; + return NULL; +} + +/* Implementation of the SKIP_RELOCS macro for unordered relocations. */ +static void +_bfd_skip_relocs_unordered (struct elf_reloc_cookie *cookie, bfd_vma offset, + bfd_vma *rels_offset) +{ + Elf_Internal_Rela *rel, *best = NULL; + bfd_vma best_offs = -1; + + /* FIXME: octets_per_byte. */ + *rels_offset = offset; + for (rel = cookie->rels; rel < cookie->relend; rel++) + if (rel->r_offset >= offset && rel->r_offset - offset < best_offs) + { + best = rel; + best_offs = rel->r_offset - offset; + } + cookie->rel = best ? best : cookie->relend; +} + +static struct reloc_read_ops _bfd_eh_unordered_relocs = { + .chk_relocs = _bfd_chk_relocs_unordered, + .get_reloc = _bfd_get_reloc_unordered, + .skip_relocs = _bfd_skip_relocs_unordered, +}; + /* 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. */ @@ -473,6 +574,8 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, bfd_byte *ehbuf = NULL, *buf, *end; bfd_byte *last_fde; + bfd_size_type sec_info_size = 0; + bfd_vma rels_offset = 0; struct eh_cie_fde *this_inf; unsigned int hdr_length, hdr_id; unsigned int cie_count; @@ -484,6 +587,7 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, unsigned int num_cies; unsigned int num_entries; elf_gc_mark_hook_fn gc_mark_hook; + struct reloc_read_ops *rr_ops = NULL; htab = elf_hash_table (info); hdr_info = &htab->eh_info; @@ -552,37 +656,32 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, REQUIRE (skip_bytes (&buf, end, hdr_length - 4)); } - sec_info = (struct eh_frame_sec_info *) - bfd_zmalloc (sizeof (struct eh_frame_sec_info) - + (num_entries - 1) * sizeof (struct eh_cie_fde)); + sec_info_size = sizeof (struct eh_frame_sec_info) + + (num_entries - 1) * sizeof (struct eh_cie_fde); + sec_info = (struct eh_frame_sec_info *) bfd_zmalloc (sec_info_size); REQUIRE (sec_info); /* 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. */ + /* Start with the assumption that relocations are ordered by offset */ + rr_ops = &_bfd_eh_ordered_relocs; + #define ENSURE_NO_RELOCS(buf) \ - REQUIRE (!(cookie->rel < cookie->relend \ - && (cookie->rel->r_offset \ - < (bfd_size_type) ((buf) - ehbuf)) \ - && cookie->rel->r_info != 0)) + REQUIRE (!(rr_ops->chk_relocs (cookie, rels_offset, \ + (buf) - ehbuf))) - /* FIXME: octets_per_byte. */ #define SKIP_RELOCS(buf) \ - while (cookie->rel < cookie->relend \ - && (cookie->rel->r_offset \ - < (bfd_size_type) ((buf) - ehbuf))) \ - cookie->rel++ + (rr_ops->skip_relocs (cookie, (buf) - ehbuf, \ + &rels_offset)) - /* FIXME: octets_per_byte. */ #define GET_RELOC(buf) \ - ((cookie->rel < cookie->relend \ - && (cookie->rel->r_offset \ - == (bfd_size_type) ((buf) - ehbuf))) \ - ? cookie->rel : NULL) + (rr_ops->get_reloc (cookie, (buf) - ehbuf)) +restart_reading: buf = ehbuf; + SKIP_RELOCS(buf); cie_count = 0; gc_mark_hook = get_elf_backend_data (abfd)->gc_mark_hook; while ((bfd_size_type) (buf - ehbuf) != sec->size) @@ -718,11 +817,9 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, 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)); + /* Cope with MIPS-style composite relocations. */ + SKIP_RELOCS(buf); } break; default: @@ -913,6 +1010,14 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info, goto success; free_no_table: + if (rr_ops == &_bfd_eh_ordered_relocs) + { + /* Perhaps, relocations are out of order. Retry with slower version + that does not require ordering. */ + rr_ops = &_bfd_eh_unordered_relocs; + memset(sec_info, 0, sec_info_size); + goto restart_reading; + } (*info->callbacks->einfo) (_("%P: error in %B(%A); no .eh_frame_hdr table will be created.\n"), abfd, sec); -- 1.8.4.1