/* .eh_frame section optimization.
- Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
- Free Software Foundation, Inc.
+ Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
+ 2012 Free Software Foundation, Inc.
Written by Jakub Jelinek <jakub@redhat.com>.
This file is part of BFD, the Binary File Descriptor library.
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. */
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;
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);
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)
}
else
{
- asection *rsec;
-
/* Find the corresponding CIE. */
unsigned int cie_offset = this_inf->offset + 4 - hdr_id;
for (cie = local_cies; cie < local_cies + cie_count; cie++)
= cie->cie_inf->add_augmentation_size;
ENSURE_NO_RELOCS (buf);
- 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)
+ if ((sec->flags & SEC_LINKER_CREATED) == 0 || cookie->rels != NULL)
{
- REQUIRE (rsec->owner == abfd);
- this_inf->u.fde.next_for_section = elf_fde_list (rsec);
- elf_fde_list (rsec) = this_inf;
+ asection *rsec;
+
+ 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. */
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;
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->size == 4)
ent->removed = sec->map_head.s != NULL;
else if (!ent->cie)
{
- cookie->rel = cookie->rels + ent->reloc_index;
- /* FIXME: octets_per_byte. */
- 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 & 0x70) == DW_EH_PE_absptr
sec_info->cies = NULL;
}
- ptr_size = (get_elf_backend_data (sec->owner)
- ->elf_backend_eh_frame_address_size (sec->owner, sec));
offset = 0;
for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
if (!ent->removed)
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
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;
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;
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 = (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;
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);
{
BFD_ASSERT (action & 1);
*aug++ = 'R';
- *buf++ = DW_EH_PE_pcrel;
+ *buf++ = make_pc_relative (DW_EH_PE_absptr, ptr_size);
action &= ~1;
}
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 |= DW_EH_PE_pcrel;
+ *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);
if (action & 1)
{
BFD_ASSERT (*buf == ent->fde_encoding);
- *buf |= DW_EH_PE_pcrel;
+ *buf = make_pc_relative (*buf, ptr_size);
action &= ~1;
}
buf++;
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:
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
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);
for (cnt = 1; cnt <= ent->set_loc[0]; cnt++)
{
- bfd_vma value;
buf = start + ent->set_loc[cnt];
value = read_value (abfd, buf, width,