Line 1... |
Line 1... |
/* .eh_frame section optimization.
|
/* .eh_frame section optimization.
|
Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
|
Free Software Foundation, Inc.
|
Free Software Foundation, Inc.
|
Written by Jakub Jelinek <jakub@redhat.com>.
|
Written by Jakub Jelinek <jakub@redhat.com>.
|
|
|
This file is part of BFD, the Binary File Descriptor library.
|
This file is part of BFD, the Binary File Descriptor library.
|
|
|
Line 22... |
Line 22... |
|
|
#include "sysdep.h"
|
#include "sysdep.h"
|
#include "bfd.h"
|
#include "bfd.h"
|
#include "libbfd.h"
|
#include "libbfd.h"
|
#include "elf-bfd.h"
|
#include "elf-bfd.h"
|
#include "elf/dwarf2.h"
|
#include "dwarf2.h"
|
|
|
#define EH_FRAME_HDR_SIZE 8
|
#define EH_FRAME_HDR_SIZE 8
|
|
|
struct cie
|
struct cie
|
{
|
{
|
Line 213... |
Line 213... |
/* Return one if C1 and C2 CIEs can be merged. */
|
/* Return one if C1 and C2 CIEs can be merged. */
|
|
|
static int
|
static int
|
cie_eq (const void *e1, const void *e2)
|
cie_eq (const void *e1, const void *e2)
|
{
|
{
|
const struct cie *c1 = e1;
|
const struct cie *c1 = (const struct cie *) e1;
|
const struct cie *c2 = e2;
|
const struct cie *c2 = (const struct cie *) e2;
|
|
|
if (c1->hash == c2->hash
|
if (c1->hash == c2->hash
|
&& c1->length == c2->length
|
&& c1->length == c2->length
|
&& c1->version == c2->version
|
&& c1->version == c2->version
|
&& c1->local_personality == c2->local_personality
|
&& c1->local_personality == c2->local_personality
|
Line 244... |
Line 244... |
}
|
}
|
|
|
static hashval_t
|
static hashval_t
|
cie_hash (const void *e)
|
cie_hash (const void *e)
|
{
|
{
|
const struct cie *c = e;
|
const struct cie *c = (const struct cie *) e;
|
return c->hash;
|
return c->hash;
|
}
|
}
|
|
|
static hashval_t
|
static hashval_t
|
cie_compute_hash (struct cie *c)
|
cie_compute_hash (struct cie *c)
|
Line 527... |
Line 527... |
num_cies++;
|
num_cies++;
|
|
|
REQUIRE (skip_bytes (&buf, end, hdr_length - 4));
|
REQUIRE (skip_bytes (&buf, end, hdr_length - 4));
|
}
|
}
|
|
|
sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
|
sec_info = (struct eh_frame_sec_info *)
|
|
bfd_zmalloc (sizeof (struct eh_frame_sec_info)
|
+ (num_entries - 1) * sizeof (struct eh_cie_fde));
|
+ (num_entries - 1) * sizeof (struct eh_cie_fde));
|
REQUIRE (sec_info);
|
REQUIRE (sec_info);
|
|
|
/* We need to have a "struct cie" for each CIE in this section. */
|
/* We need to have a "struct cie" for each CIE in this section. */
|
local_cies = bfd_zmalloc (num_cies * sizeof (*local_cies));
|
local_cies = (struct cie *) bfd_zmalloc (num_cies * sizeof (*local_cies));
|
REQUIRE (local_cies);
|
REQUIRE (local_cies);
|
|
|
|
/* FIXME: octets_per_byte. */
|
#define ENSURE_NO_RELOCS(buf) \
|
#define ENSURE_NO_RELOCS(buf) \
|
REQUIRE (!(cookie->rel < cookie->relend \
|
REQUIRE (!(cookie->rel < cookie->relend \
|
&& (cookie->rel->r_offset \
|
&& (cookie->rel->r_offset \
|
< (bfd_size_type) ((buf) - ehbuf)) \
|
< (bfd_size_type) ((buf) - ehbuf)) \
|
&& cookie->rel->r_info != 0))
|
&& cookie->rel->r_info != 0))
|
|
|
|
/* FIXME: octets_per_byte. */
|
#define SKIP_RELOCS(buf) \
|
#define SKIP_RELOCS(buf) \
|
while (cookie->rel < cookie->relend \
|
while (cookie->rel < cookie->relend \
|
&& (cookie->rel->r_offset \
|
&& (cookie->rel->r_offset \
|
< (bfd_size_type) ((buf) - ehbuf))) \
|
< (bfd_size_type) ((buf) - ehbuf))) \
|
cookie->rel++
|
cookie->rel++
|
|
|
|
/* FIXME: octets_per_byte. */
|
#define GET_RELOC(buf) \
|
#define GET_RELOC(buf) \
|
((cookie->rel < cookie->relend \
|
((cookie->rel < cookie->relend \
|
&& (cookie->rel->r_offset \
|
&& (cookie->rel->r_offset \
|
== (bfd_size_type) ((buf) - ehbuf))) \
|
== (bfd_size_type) ((buf) - ehbuf))) \
|
? cookie->rel : NULL)
|
? cookie->rel : NULL)
|
Line 764... |
Line 768... |
ENSURE_NO_RELOCS (buf);
|
ENSURE_NO_RELOCS (buf);
|
REQUIRE (GET_RELOC (buf));
|
REQUIRE (GET_RELOC (buf));
|
|
|
/* Chain together the FDEs for each section. */
|
/* Chain together the FDEs for each section. */
|
rsec = _bfd_elf_gc_mark_rsec (info, sec, gc_mark_hook, cookie);
|
rsec = _bfd_elf_gc_mark_rsec (info, sec, gc_mark_hook, cookie);
|
REQUIRE (rsec && rsec->owner == abfd);
|
/* 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);
|
this_inf->u.fde.next_for_section = elf_fde_list (rsec);
|
elf_fde_list (rsec) = this_inf;
|
elf_fde_list (rsec) = this_inf;
|
|
}
|
|
|
/* Skip the initial location and address range. */
|
/* Skip the initial location and address range. */
|
start = buf;
|
start = buf;
|
length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
|
length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
|
REQUIRE (skip_bytes (&buf, end, 2 * length));
|
REQUIRE (skip_bytes (&buf, end, 2 * length));
|
Line 799... |
Line 808... |
/* Skip over the augmentation data. */
|
/* Skip over the augmentation data. */
|
REQUIRE (skip_bytes (&buf, end, length));
|
REQUIRE (skip_bytes (&buf, end, length));
|
insns = buf;
|
insns = buf;
|
|
|
buf = last_fde + 4 + hdr_length;
|
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);
|
SKIP_RELOCS (buf);
|
}
|
}
|
|
|
/* Try to interpret the CFA instructions and find the first
|
/* Try to interpret the CFA instructions and find the first
|
padding nop. Shrink this_inf's size so that it doesn't
|
padding nop. Shrink this_inf's size so that it doesn't
|
Line 828... |
Line 847... |
|| this_inf->make_relative))
|
|| this_inf->make_relative))
|
{
|
{
|
unsigned int cnt;
|
unsigned int cnt;
|
bfd_byte *p;
|
bfd_byte *p;
|
|
|
this_inf->set_loc = bfd_malloc ((set_loc_count + 1)
|
this_inf->set_loc = (unsigned int *)
|
* sizeof (unsigned int));
|
bfd_malloc ((set_loc_count + 1) * sizeof (unsigned int));
|
REQUIRE (this_inf->set_loc);
|
REQUIRE (this_inf->set_loc);
|
this_inf->set_loc[0] = set_loc_count;
|
this_inf->set_loc[0] = set_loc_count;
|
p = insns;
|
p = insns;
|
cnt = 0;
|
cnt = 0;
|
while (p < end)
|
while (p < end)
|
Line 894... |
Line 913... |
static bfd_boolean
|
static bfd_boolean
|
mark_entry (struct bfd_link_info *info, asection *sec,
|
mark_entry (struct bfd_link_info *info, asection *sec,
|
struct eh_cie_fde *ent, elf_gc_mark_hook_fn gc_mark_hook,
|
struct eh_cie_fde *ent, elf_gc_mark_hook_fn gc_mark_hook,
|
struct elf_reloc_cookie *cookie)
|
struct elf_reloc_cookie *cookie)
|
{
|
{
|
|
/* FIXME: octets_per_byte. */
|
for (cookie->rel = cookie->rels + ent->reloc_index;
|
for (cookie->rel = cookie->rels + ent->reloc_index;
|
cookie->rel < cookie->relend
|
cookie->rel < cookie->relend
|
&& cookie->rel->r_offset < ent->offset + ent->size;
|
&& cookie->rel->r_offset < ent->offset + ent->size;
|
cookie->rel++)
|
cookie->rel++)
|
if (!_bfd_elf_gc_mark_reloc (info, sec, gc_mark_hook, cookie))
|
if (!_bfd_elf_gc_mark_reloc (info, sec, gc_mark_hook, cookie))
|
Line 1034... |
Line 1054... |
|
|
new_cie = (struct cie *) *loc;
|
new_cie = (struct cie *) *loc;
|
if (new_cie == NULL)
|
if (new_cie == NULL)
|
{
|
{
|
/* Keep CIE_INF and record it in the hash table. */
|
/* Keep CIE_INF and record it in the hash table. */
|
new_cie = malloc (sizeof (struct cie));
|
new_cie = (struct cie *) malloc (sizeof (struct cie));
|
if (new_cie == NULL)
|
if (new_cie == NULL)
|
return cie_inf;
|
return cie_inf;
|
|
|
memcpy (new_cie, cie, sizeof (struct cie));
|
memcpy (new_cie, cie, sizeof (struct cie));
|
*loc = new_cie;
|
*loc = new_cie;
|
Line 1075... |
Line 1095... |
if (sec_info == NULL)
|
if (sec_info == NULL)
|
return FALSE;
|
return FALSE;
|
|
|
hdr_info = &elf_hash_table (info)->eh_info;
|
hdr_info = &elf_hash_table (info)->eh_info;
|
for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
|
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;
|
cookie->rel = cookie->rels + ent->reloc_index;
|
|
/* FIXME: octets_per_byte. */
|
BFD_ASSERT (cookie->rel < cookie->relend
|
BFD_ASSERT (cookie->rel < cookie->relend
|
&& cookie->rel->r_offset == ent->offset + 8);
|
&& cookie->rel->r_offset == ent->offset + 8);
|
if (!(*reloc_symbol_deleted_p) (ent->offset + 8, cookie))
|
if (!(*reloc_symbol_deleted_p) (ent->offset + 8, cookie))
|
{
|
{
|
if (info->shared
|
if (info->shared
|
Line 1220... |
Line 1245... |
struct eh_frame_hdr_info *hdr_info;
|
struct eh_frame_hdr_info *hdr_info;
|
unsigned int lo, hi, mid;
|
unsigned int lo, hi, mid;
|
|
|
if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
|
if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
|
return offset;
|
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)
|
if (offset >= sec->rawsize)
|
return offset - sec->rawsize + sec->size;
|
return offset - sec->rawsize + sec->size;
|
|
|
htab = elf_hash_table (info);
|
htab = elf_hash_table (info);
|
Line 1302... |
Line 1327... |
struct eh_frame_hdr_info *hdr_info;
|
struct eh_frame_hdr_info *hdr_info;
|
unsigned int ptr_size;
|
unsigned int ptr_size;
|
struct eh_cie_fde *ent;
|
struct eh_cie_fde *ent;
|
|
|
if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
|
if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
|
|
/* FIXME: octets_per_byte. */
|
return bfd_set_section_contents (abfd, sec->output_section, contents,
|
return bfd_set_section_contents (abfd, sec->output_section, contents,
|
sec->output_offset, sec->size);
|
sec->output_offset, sec->size);
|
|
|
ptr_size = (get_elf_backend_data (abfd)
|
ptr_size = (get_elf_backend_data (abfd)
|
->elf_backend_eh_frame_address_size (abfd, sec));
|
->elf_backend_eh_frame_address_size (abfd, sec));
|
BFD_ASSERT (ptr_size != 0);
|
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);
|
htab = elf_hash_table (info);
|
hdr_info = &htab->eh_info;
|
hdr_info = &htab->eh_info;
|
|
|
if (hdr_info->table && hdr_info->array == NULL)
|
if (hdr_info->table && hdr_info->array == NULL)
|
hdr_info->array
|
hdr_info->array = (struct eh_frame_array_ent *)
|
= bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array));
|
bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array));
|
if (hdr_info->array == NULL)
|
if (hdr_info->array == NULL)
|
hdr_info = NULL;
|
hdr_info = NULL;
|
|
|
/* The new offsets can be bigger or smaller than the original offsets.
|
/* The new offsets can be bigger or smaller than the original offsets.
|
We therefore need to make two passes over the section: one backward
|
We therefore need to make two passes over the section: one backward
|
Line 1439... |
Line 1465... |
{
|
{
|
bfd_vma val;
|
bfd_vma val;
|
|
|
val = read_value (abfd, buf, per_width,
|
val = read_value (abfd, buf, per_width,
|
get_DW_EH_PE_signed (per_encoding));
|
get_DW_EH_PE_signed (per_encoding));
|
val += ent->offset - ent->new_offset;
|
val += (bfd_vma) ent->offset - ent->new_offset;
|
val -= extra_string + extra_data;
|
val -= extra_string + extra_data;
|
write_value (abfd, buf, val, per_width);
|
write_value (abfd, buf, val, per_width);
|
action &= ~4;
|
action &= ~4;
|
}
|
}
|
buf += per_width;
|
buf += per_width;
|
Line 1498... |
Line 1524... |
BFD_ASSERT (got != NULL);
|
BFD_ASSERT (got != NULL);
|
address += got->vma;
|
address += got->vma;
|
}
|
}
|
break;
|
break;
|
case DW_EH_PE_pcrel:
|
case DW_EH_PE_pcrel:
|
value += ent->offset - ent->new_offset;
|
value += (bfd_vma) ent->offset - ent->new_offset;
|
address += (sec->output_section->vma
|
address += (sec->output_section->vma
|
+ sec->output_offset
|
+ sec->output_offset
|
+ ent->offset + 8);
|
+ ent->offset + 8);
|
break;
|
break;
|
}
|
}
|
Line 1532... |
Line 1558... |
value = read_value (abfd, buf, width,
|
value = read_value (abfd, buf, width,
|
get_DW_EH_PE_signed (ent->lsda_encoding));
|
get_DW_EH_PE_signed (ent->lsda_encoding));
|
if (value)
|
if (value)
|
{
|
{
|
if ((ent->lsda_encoding & 0xf0) == DW_EH_PE_pcrel)
|
if ((ent->lsda_encoding & 0xf0) == DW_EH_PE_pcrel)
|
value += ent->offset - ent->new_offset;
|
value += (bfd_vma) ent->offset - ent->new_offset;
|
else if (cie->u.cie.make_lsda_relative)
|
else if (cie->u.cie.make_lsda_relative)
|
value -= (sec->output_section->vma
|
value -= (sec->output_section->vma
|
+ sec->output_offset
|
+ sec->output_offset
|
+ ent->new_offset + 8 + ent->lsda_offset);
|
+ ent->new_offset + 8 + ent->lsda_offset);
|
write_value (abfd, buf, value, width);
|
write_value (abfd, buf, value, width);
|
Line 1571... |
Line 1597... |
get_DW_EH_PE_signed (ent->fde_encoding));
|
get_DW_EH_PE_signed (ent->fde_encoding));
|
if (!value)
|
if (!value)
|
continue;
|
continue;
|
|
|
if ((ent->fde_encoding & 0xf0) == DW_EH_PE_pcrel)
|
if ((ent->fde_encoding & 0xf0) == DW_EH_PE_pcrel)
|
value += ent->offset + 8 - new_offset;
|
value += (bfd_vma) ent->offset + 8 - new_offset;
|
if (ent->make_relative)
|
if (ent->make_relative)
|
value -= (sec->output_section->vma
|
value -= (sec->output_section->vma
|
+ sec->output_offset
|
+ sec->output_offset
|
+ new_offset + ent->set_loc[cnt]);
|
+ new_offset + ent->set_loc[cnt]);
|
write_value (abfd, buf, value, width);
|
write_value (abfd, buf, value, width);
|
Line 1590... |
Line 1616... |
have padded CIE/FDE records to multiple of pointer size with
|
have padded CIE/FDE records to multiple of pointer size with
|
size_of_output_cie_fde. */
|
size_of_output_cie_fde. */
|
if ((sec->size % ptr_size) != 0)
|
if ((sec->size % ptr_size) != 0)
|
abort ();
|
abort ();
|
|
|
|
/* FIXME: octets_per_byte. */
|
return bfd_set_section_contents (abfd, sec->output_section,
|
return bfd_set_section_contents (abfd, sec->output_section,
|
contents, (file_ptr) sec->output_offset,
|
contents, (file_ptr) sec->output_offset,
|
sec->size);
|
sec->size);
|
}
|
}
|
|
|
Line 1601... |
Line 1628... |
VMA of FDE initial location. */
|
VMA of FDE initial location. */
|
|
|
static int
|
static int
|
vma_compare (const void *a, const void *b)
|
vma_compare (const void *a, const void *b)
|
{
|
{
|
const struct eh_frame_array_ent *p = a;
|
const struct eh_frame_array_ent *p = (const struct eh_frame_array_ent *) a;
|
const struct eh_frame_array_ent *q = b;
|
const struct eh_frame_array_ent *q = (const struct eh_frame_array_ent *) b;
|
if (p->initial_loc > q->initial_loc)
|
if (p->initial_loc > q->initial_loc)
|
return 1;
|
return 1;
|
if (p->initial_loc < q->initial_loc)
|
if (p->initial_loc < q->initial_loc)
|
return -1;
|
return -1;
|
return 0;
|
return 0;
|
Line 1653... |
Line 1680... |
return TRUE;
|
return TRUE;
|
|
|
size = EH_FRAME_HDR_SIZE;
|
size = EH_FRAME_HDR_SIZE;
|
if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
|
if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
|
size += 4 + hdr_info->fde_count * 8;
|
size += 4 + hdr_info->fde_count * 8;
|
contents = bfd_malloc (size);
|
contents = (bfd_byte *) bfd_malloc (size);
|
if (contents == NULL)
|
if (contents == NULL)
|
return FALSE;
|
return FALSE;
|
|
|
eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame");
|
eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame");
|
if (eh_frame_sec == NULL)
|
if (eh_frame_sec == NULL)
|
Line 1701... |
Line 1728... |
hdr_info->array[i].fde - sec->output_section->vma,
|
hdr_info->array[i].fde - sec->output_section->vma,
|
contents + EH_FRAME_HDR_SIZE + i * 8 + 8);
|
contents + EH_FRAME_HDR_SIZE + i * 8 + 8);
|
}
|
}
|
}
|
}
|
|
|
|
/* FIXME: octets_per_byte. */
|
retval = bfd_set_section_contents (abfd, sec->output_section,
|
retval = bfd_set_section_contents (abfd, sec->output_section,
|
contents, (file_ptr) sec->output_offset,
|
contents, (file_ptr) sec->output_offset,
|
sec->size);
|
sec->size);
|
free (contents);
|
free (contents);
|
return retval;
|
return retval;
|