bfd_boolean _bfd_elf_discard_section_eh_frame (bfd *abfd, struct bfd_link_info *info, asection *sec, bfd_boolean (*reloc_symbol_deleted_p) (bfd_vma, void *), struct elf_reloc_cookie *cookie) { #define REQUIRE(COND) \ do \ if (!(COND)) \ goto free_no_table; \ while (0) bfd_byte *ehbuf = NULL, *buf; bfd_byte *last_fde; struct eh_cie_fde *ent, *this_inf; unsigned int hdr_length, hdr_id; struct extended_cie { struct cie cie; unsigned int offset; unsigned int usage_count; unsigned int entry; } *ecies = NULL, *ecie; unsigned int ecie_count = 0, ecie_alloced = 0; struct cie *cie; struct elf_link_hash_table *htab; struct eh_frame_hdr_info *hdr_info; struct eh_frame_sec_info *sec_info = NULL; unsigned int offset; unsigned int ptr_size; unsigned int entry_alloced; if (sec->size == 0) { /* This file does not contain .eh_frame information. */ return FALSE; } if (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; } htab = elf_hash_table (info); hdr_info = &htab->eh_info; if (hdr_info->cies == NULL && !info->relocatable) hdr_info->cies = htab_try_create (1, cie_hash, cie_eq, free); /* Read the frame unwind information from abfd. */ REQUIRE (bfd_malloc_and_get_section (abfd, sec, &ehbuf)); if (sec->size >= 4 && bfd_get_32 (abfd, ehbuf) == 0 && cookie->rel == cookie->relend) { /* Empty .eh_frame section. */ free (ehbuf); return FALSE; } /* 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). */ REQUIRE (sec->size == (unsigned int) sec->size); ptr_size = (get_elf_backend_data (abfd) ->elf_backend_eh_frame_address_size (abfd, sec)); REQUIRE (ptr_size != 0); buf = ehbuf; sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info) + 99 * sizeof (struct eh_cie_fde)); REQUIRE (sec_info); entry_alloced = 100; #define ENSURE_NO_RELOCS(buf) \ REQUIRE (!(cookie->rel < cookie->relend \ && (cookie->rel->r_offset \ < (bfd_size_type) ((buf) - ehbuf)) \ && cookie->rel->r_info != 0)) #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 (;;) { char *aug; bfd_byte *start, *end, *insns, *insns_end; bfd_size_type length; unsigned int set_loc_count; if (sec_info->count == entry_alloced) { sec_info = bfd_realloc (sec_info, sizeof (struct eh_frame_sec_info) + ((entry_alloced + 99) * sizeof (struct eh_cie_fde))); REQUIRE (sec_info); memset (&sec_info->entry[entry_alloced], 0, 100 * sizeof (struct eh_cie_fde)); entry_alloced += 100; } this_inf = sec_info->entry + sec_info->count; last_fde = buf; if ((bfd_size_type) (buf - ehbuf) == sec->size) break; /* Read the length of the entry. */ REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4)); hdr_length = bfd_get_32 (abfd, buf - 4); /* 64-bit .eh_frame is not supported. */ REQUIRE (hdr_length != 0xffffffff); /* The CIE/FDE must be fully contained in this input section. */ REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr_length <= sec->size); end = buf + hdr_length; this_inf->offset = last_fde - ehbuf; this_inf->size = 4 + hdr_length; if (hdr_length == 0) { /* A zero-length CIE should only be found at the end of the section. */ REQUIRE ((bfd_size_type) (buf - ehbuf) == sec->size); ENSURE_NO_RELOCS (buf); sec_info->count++; break; } REQUIRE (skip_bytes (&buf, end, 4)); hdr_id = bfd_get_32 (abfd, buf - 4); if (hdr_id == 0) { unsigned int initial_insn_length; /* CIE */ this_inf->cie = 1; if (ecie_count == ecie_alloced) { ecies = bfd_realloc (ecies, (ecie_alloced + 20) * sizeof (*ecies)); REQUIRE (ecies); memset (&ecies[ecie_alloced], 0, 20 * sizeof (*ecies)); ecie_alloced += 20; } cie = &ecies[ecie_count].cie; ecies[ecie_count].offset = this_inf->offset; ecies[ecie_count++].entry = sec_info->count; cie->length = hdr_length; start = buf; REQUIRE (read_byte (&buf, end, &cie->version)); /* Cannot handle unknown versions. */ REQUIRE (cie->version == 1 || cie->version == 3); REQUIRE (strlen ((char *) buf) < sizeof (cie->augmentation)); strcpy (cie->augmentation, (char *) buf); buf = (bfd_byte *) strchr ((char *) 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. */ REQUIRE (skip_bytes (&buf, end, ptr_size)); SKIP_RELOCS (buf); } REQUIRE (read_uleb128 (&buf, end, &cie->code_align)); REQUIRE (read_sleb128 (&buf, end, &cie->data_align)); if (cie->version == 1) { REQUIRE (buf < end); cie->ra_column = *buf++; } else REQUIRE (read_uleb128 (&buf, end, &cie->ra_column)); 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++; REQUIRE (read_uleb128 (&buf, end, &cie->augmentation_size)); ENSURE_NO_RELOCS (buf); } while (*aug != '\0') switch (*aug++) { case 'L': REQUIRE (read_byte (&buf, end, &cie->lsda_encoding)); ENSURE_NO_RELOCS (buf); REQUIRE (get_DW_EH_PE_width (cie->lsda_encoding, ptr_size)); break; case 'R': REQUIRE (read_byte (&buf, end, &cie->fde_encoding)); ENSURE_NO_RELOCS (buf); REQUIRE (get_DW_EH_PE_width (cie->fde_encoding, ptr_size)); break; case 'S': break; case 'P': { int per_width; REQUIRE (read_byte (&buf, end, &cie->per_encoding)); per_width = get_DW_EH_PE_width (cie->per_encoding, ptr_size); REQUIRE (per_width); if ((cie->per_encoding & 0xf0) == DW_EH_PE_aligned) { length = -(buf - ehbuf) & (per_width - 1); REQUIRE (skip_bytes (&buf, end, length)); } ENSURE_NO_RELOCS (buf); /* Ensure we have a reloc here. */ if (GET_RELOC (buf) != 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 || 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 (skip_bytes (&buf, end, per_width)); REQUIRE (cie->local_personality || cie->personality.h); } 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. */ if (info->shared && (get_elf_backend_data (abfd) ->elf_backend_can_make_relative_eh_frame (abfd, info, sec))) { if ((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr) cie->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) { if (*cie->augmentation == 0) this_inf->add_augmentation_size = 1; this_inf->add_fde_encoding = 1; cie->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->make_lsda_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 = 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); } insns = buf; buf += initial_insn_length; ENSURE_NO_RELOCS (buf); } else { /* 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->offset) break; /* Ensure this FDE references one of the CIEs in this input section. */ REQUIRE (ecie != ecies + ecie_count); cie = &ecie->cie; ENSURE_NO_RELOCS (buf); REQUIRE (GET_RELOC (buf)); if ((*reloc_symbol_deleted_p) (buf - ehbuf, cookie)) /* This is a FDE against a discarded section. It should be deleted. */ this_inf->removed = 1; else { if (info->shared && (((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr && cie->make_relative == 0) || (cie->fde_encoding & 0xf0) == DW_EH_PE_aligned)) { /* If a shared library uses absolute pointers which we cannot turn into PC relative, don't create the binary search table, since it is affected by runtime relocations. */ hdr_info->table = FALSE; (*info->callbacks->einfo) (_("%P: fde encoding in %B(%A) prevents .eh_frame_hdr" " table being created.\n"), abfd, sec); } ecie->usage_count++; hdr_info->fde_count++; this_inf->cie_inf = (void *) (ecie - ecies); } /* Skip the initial location and address range. */ start = buf; length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size); REQUIRE (skip_bytes (&buf, end, 2 * length)); /* Skip the augmentation size, if present. */ if (cie->augmentation[0] == 'z') REQUIRE (read_uleb128 (&buf, end, &length)); else length = 0; /* Of the supported augmentation characters above, only 'L' adds augmentation data to the FDE. This code would need to be adjusted if any future augmentations do the same thing. */ if (cie->lsda_encoding != DW_EH_PE_omit) { this_inf->lsda_offset = buf - start; /* If there's no 'z' augmentation, we don't know where the CFA insns begin. Assume no padding. */ if (cie->augmentation[0] != 'z') length = end - buf; } /* Skip over the augmentation data. */ REQUIRE (skip_bytes (&buf, end, length)); insns = buf; buf = last_fde + 4 + hdr_length; SKIP_RELOCS (buf); } /* Try to interpret the CFA instructions and find the first padding nop. Shrink this_inf's size so that it doesn't include the padding. */ length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size); set_loc_count = 0; insns_end = skip_non_nops (insns, end, length, &set_loc_count); /* If we don't understand the CFA instructions, we can't know what needs to be adjusted there. */ if (insns_end == NULL /* For the time being we don't support DW_CFA_set_loc in CIE instructions. */ || (set_loc_count && this_inf->cie)) goto free_no_table; this_inf->size -= end - insns_end; if (insns_end != end && this_inf->cie) { cie->initial_insn_length -= end - insns_end; cie->length -= end - insns_end; } if (set_loc_count && ((cie->fde_encoding & 0xf0) == DW_EH_PE_pcrel || cie->make_relative)) { unsigned int cnt; bfd_byte *p; this_inf->set_loc = bfd_malloc ((set_loc_count + 1) * sizeof (unsigned int)); REQUIRE (this_inf->set_loc); this_inf->set_loc[0] = set_loc_count; p = insns; cnt = 0; while (p < end) { if (*p == DW_CFA_set_loc) this_inf->set_loc[++cnt] = p + 1 - start; REQUIRE (skip_cfa_op (&p, end, length)); } } this_inf->fde_encoding = cie->fde_encoding; this_inf->lsda_encoding = cie->lsda_encoding; sec_info->count++; } elf_section_data (sec)->sec_info = sec_info; sec->sec_info_type = ELF_INFO_TYPE_EH_FRAME; /* Look at all CIEs in this section and determine which can be removed as unused, which can be merged with previous duplicate CIEs and which need to be kept. */ for (ecie = ecies; ecie < ecies + ecie_count; ++ecie) { if (ecie->usage_count == 0) { sec_info->entry[ecie->entry].removed = 1; continue; } ecie->cie.output_sec = sec->output_section; ecie->cie.cie_inf = sec_info->entry + ecie->entry; cie_compute_hash (&ecie->cie); if (hdr_info->cies != NULL) { void **loc = htab_find_slot_with_hash (hdr_info->cies, &ecie->cie, ecie->cie.hash, INSERT); if (loc != NULL) { if (*loc != HTAB_EMPTY_ENTRY) { sec_info->entry[ecie->entry].removed = 1; ecie->cie.cie_inf = ((struct cie *) *loc)->cie_inf; continue; } *loc = malloc (sizeof (struct cie)); if (*loc == NULL) *loc = HTAB_DELETED_ENTRY; else memcpy (*loc, &ecie->cie, sizeof (struct cie)); } } ecie->cie.cie_inf->make_relative = ecie->cie.make_relative; ecie->cie.cie_inf->make_lsda_relative = ecie->cie.make_lsda_relative; ecie->cie.cie_inf->per_encoding_relative = (ecie->cie.per_encoding & 0x70) == DW_EH_PE_pcrel; } /* Ok, now we can assign new offsets. */ offset = 0; for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent) if (!ent->removed) { if (!ent->cie) { ecie = ecies + (bfd_hostptr_t) ent->cie_inf; ent->cie_inf = ecie->cie.cie_inf; } ent->new_offset = offset; offset += size_of_output_cie_fde (ent, ptr_size); } /* Resize the sec as needed. */ sec->rawsize = sec->size; sec->size = offset; free (ehbuf); if (ecies) free (ecies); return offset != sec->rawsize; free_no_table: (*info->callbacks->einfo) (_("%P: error in %B(%A); no .eh_frame_hdr table will be created.\n"), abfd, sec); if (ehbuf) free (ehbuf); if (sec_info) free (sec_info); if (ecies) free (ecies); hdr_info->table = FALSE; return FALSE; #undef REQUIRE }
static struct eh_cie_fde * find_merged_cie (bfd *abfd, 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) { /* 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; } 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); } } /* 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 = 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; }