/* .eh_frame section optimization.
- Copyright 2001, 2002 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.
+ This file is part of BFD, the Binary File Descriptor library.
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
-#include "bfd.h"
#include "sysdep.h"
+#include "bfd.h"
#include "libbfd.h"
#include "elf-bfd.h"
-#include "elf/dwarf2.h"
+#include "dwarf2.h"
#define EH_FRAME_HDR_SIZE 8
-struct cie_header
-{
- unsigned int length;
- unsigned int id;
-};
-
struct cie
{
- struct cie_header hdr;
+ unsigned int length;
+ unsigned int hash;
unsigned char version;
- unsigned char augmentation[20];
- unsigned int code_align;
- int data_align;
- unsigned int ra_column;
- unsigned int augmentation_size;
- struct elf_link_hash_entry *personality;
+ unsigned char local_personality;
+ char augmentation[20];
+ bfd_vma code_align;
+ bfd_signed_vma data_align;
+ bfd_vma ra_column;
+ bfd_vma augmentation_size;
+ union {
+ struct elf_link_hash_entry *h;
+ 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;
unsigned char fde_encoding;
unsigned char initial_insn_length;
- unsigned char make_relative;
- unsigned char make_lsda_relative;
+ unsigned char can_make_lsda_relative;
unsigned char initial_instructions[50];
};
-struct eh_cie_fde
-{
- unsigned int offset;
- unsigned int size;
- asection *sec;
- unsigned int new_offset;
- unsigned char fde_encoding;
- unsigned char lsda_encoding;
- unsigned char lsda_offset;
- unsigned char cie : 1;
- unsigned char removed : 1;
- unsigned char make_relative : 1;
- unsigned char make_lsda_relative : 1;
- unsigned char per_encoding_relative : 1;
-};
-struct eh_frame_sec_info
-{
- unsigned int count;
- unsigned int alloced;
- struct eh_cie_fde entry[1];
-};
-struct eh_frame_array_ent
-{
- bfd_vma initial_loc;
- bfd_vma fde;
-};
+/* If *ITER hasn't reached END yet, read the next byte into *RESULT and
+ move onto the next byte. Return true on success. */
-struct eh_frame_hdr_info
+static inline bfd_boolean
+read_byte (bfd_byte **iter, bfd_byte *end, unsigned char *result)
{
- struct cie last_cie;
- asection *last_cie_sec;
- unsigned int last_cie_offset;
- unsigned int fde_count, array_count;
- struct eh_frame_array_ent *array;
- /* TRUE if .eh_frame_hdr should contain the sorted search table.
- We build it if we successfully read all .eh_frame input sections
- and recognize them. */
- boolean table;
- boolean strip;
-};
+ if (*iter >= end)
+ return FALSE;
+ *result = *((*iter)++);
+ return TRUE;
+}
-static bfd_vma read_unsigned_leb128
- PARAMS ((bfd *, char *, unsigned int *));
-static bfd_signed_vma read_signed_leb128
- PARAMS ((bfd *, char *, unsigned int *));
-static int get_DW_EH_PE_width
- PARAMS ((int, int));
-static bfd_vma read_value
- PARAMS ((bfd *, bfd_byte *, int));
-static void write_value
- PARAMS ((bfd *, bfd_byte *, bfd_vma, int));
-static int cie_compare
- PARAMS ((struct cie *, struct cie *));
-static int vma_compare
- PARAMS ((const PTR a, const PTR b));
-
-/* Helper function for reading uleb128 encoded data. */
+/* Move *ITER over LENGTH bytes, or up to END, whichever is closer.
+ Return true it was possible to move LENGTH bytes. */
-static bfd_vma
-read_unsigned_leb128 (abfd, buf, bytes_read_ptr)
- bfd *abfd ATTRIBUTE_UNUSED;
- char *buf;
- unsigned int *bytes_read_ptr;
+static inline bfd_boolean
+skip_bytes (bfd_byte **iter, bfd_byte *end, bfd_size_type length)
{
- bfd_vma result;
- unsigned int num_read;
- int shift;
- unsigned char byte;
-
- result = 0;
- shift = 0;
- num_read = 0;
- do
+ if ((bfd_size_type) (end - *iter) < length)
{
- byte = bfd_get_8 (abfd, (bfd_byte *) buf);
- buf ++;
- num_read ++;
- result |= (((bfd_vma) byte & 0x7f) << shift);
- shift += 7;
+ *iter = end;
+ return FALSE;
}
- while (byte & 0x80);
- * bytes_read_ptr = num_read;
- return result;
+ *iter += length;
+ return TRUE;
}
-/* Helper function for reading sleb128 encoded data. */
+/* Move *ITER over an leb128, stopping at END. Return true if the end
+ of the leb128 was found. */
-static bfd_signed_vma
-read_signed_leb128 (abfd, buf, bytes_read_ptr)
- bfd *abfd ATTRIBUTE_UNUSED;
- char *buf;
- unsigned int * bytes_read_ptr;
+static bfd_boolean
+skip_leb128 (bfd_byte **iter, bfd_byte *end)
{
- bfd_vma result;
- int shift;
- int num_read;
unsigned char byte;
-
- result = 0;
- shift = 0;
- num_read = 0;
do
- {
- byte = bfd_get_8 (abfd, (bfd_byte *) buf);
- buf ++;
- num_read ++;
- result |= (((bfd_vma) byte & 0x7f) << shift);
- shift += 7;
- }
+ if (!read_byte (iter, end, &byte))
+ return FALSE;
while (byte & 0x80);
- if (byte & 0x40)
- result |= (((bfd_vma) -1) << (shift - 7)) << 7;
- * bytes_read_ptr = num_read;
- return result;
+ return TRUE;
+}
+
+/* Like skip_leb128, but treat the leb128 as an unsigned value and
+ store it in *VALUE. */
+
+static bfd_boolean
+read_uleb128 (bfd_byte **iter, bfd_byte *end, bfd_vma *value)
+{
+ bfd_byte *start, *p;
+
+ start = *iter;
+ if (!skip_leb128 (iter, end))
+ return FALSE;
+
+ p = *iter;
+ *value = *--p;
+ while (p > start)
+ *value = (*value << 7) | (*--p & 0x7f);
+
+ return TRUE;
}
-#define read_uleb128(VAR, BUF) \
-do \
- { \
- (VAR) = read_unsigned_leb128 (abfd, buf, &leb128_tmp); \
- (BUF) += leb128_tmp; \
- } \
-while (0)
-
-#define read_sleb128(VAR, BUF) \
-do \
- { \
- (VAR) = read_signed_leb128 (abfd, buf, &leb128_tmp); \
- (BUF) += leb128_tmp; \
- } \
-while (0)
+/* Like read_uleb128, but for signed values. */
+
+static bfd_boolean
+read_sleb128 (bfd_byte **iter, bfd_byte *end, bfd_signed_vma *value)
+{
+ bfd_byte *start, *p;
+
+ start = *iter;
+ if (!skip_leb128 (iter, end))
+ return FALSE;
+
+ p = *iter;
+ *value = ((*--p & 0x7f) ^ 0x40) - 0x40;
+ while (p > start)
+ *value = (*value << 7) | (*--p & 0x7f);
+
+ return TRUE;
+}
/* Return 0 if either encoding is variable width, or not yet known to bfd. */
static
-int get_DW_EH_PE_width (encoding, ptr_size)
- int encoding, ptr_size;
+int get_DW_EH_PE_width (int encoding, int ptr_size)
{
/* DW_EH_PE_ values of 0x60 and 0x70 weren't defined at the time .eh_frame
was added to bfd. */
return 0;
}
+#define get_DW_EH_PE_signed(encoding) (((encoding) & DW_EH_PE_signed) != 0)
+
/* Read a width sized value from memory. */
static bfd_vma
-read_value (abfd, buf, width)
- bfd *abfd;
- bfd_byte *buf;
- int width;
+read_value (bfd *abfd, bfd_byte *buf, int width, int is_signed)
{
bfd_vma value;
switch (width)
{
- case 2: value = bfd_get_16 (abfd, buf); break;
- case 4: value = bfd_get_32 (abfd, buf); break;
- case 8: value = bfd_get_64 (abfd, buf); break;
- default: BFD_FAIL (); return 0;
+ case 2:
+ if (is_signed)
+ value = bfd_get_signed_16 (abfd, buf);
+ else
+ value = bfd_get_16 (abfd, buf);
+ break;
+ case 4:
+ if (is_signed)
+ value = bfd_get_signed_32 (abfd, buf);
+ else
+ value = bfd_get_32 (abfd, buf);
+ break;
+ case 8:
+ if (is_signed)
+ value = bfd_get_signed_64 (abfd, buf);
+ else
+ value = bfd_get_64 (abfd, buf);
+ break;
+ default:
+ BFD_FAIL ();
+ return 0;
}
return value;
}
-
+
/* Store a width sized value to memory. */
static void
-write_value (abfd, buf, value, width)
- bfd *abfd;
- bfd_byte *buf;
- bfd_vma value;
- int width;
+write_value (bfd *abfd, bfd_byte *buf, bfd_vma value, int width)
{
switch (width)
{
}
}
-/* Return zero if C1 and C2 CIEs can be merged. */
+/* Return one if C1 and C2 CIEs can be merged. */
-static
-int cie_compare (c1, c2)
- struct cie *c1, *c2;
+static int
+cie_eq (const void *e1, const void *e2)
{
- if (c1->hdr.length == c2->hdr.length
+ const struct cie *c1 = (const struct cie *) e1;
+ const struct cie *c2 = (const struct cie *) e2;
+
+ if (c1->hash == c2->hash
+ && c1->length == c2->length
&& c1->version == c2->version
+ && c1->local_personality == c2->local_personality
&& strcmp (c1->augmentation, c2->augmentation) == 0
&& strcmp (c1->augmentation, "eh") != 0
&& c1->code_align == c2->code_align
&& c1->data_align == c2->data_align
&& c1->ra_column == c2->ra_column
&& c1->augmentation_size == c2->augmentation_size
- && c1->personality == c2->personality
+ && memcmp (&c1->personality, &c2->personality,
+ sizeof (c1->personality)) == 0
+ && c1->output_sec == c2->output_sec
&& 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 == c2->initial_insn_length
+ && c1->initial_insn_length <= sizeof (c1->initial_instructions)
&& memcmp (c1->initial_instructions,
c2->initial_instructions,
c1->initial_insn_length) == 0)
+ return 1;
+
+ return 0;
+}
+
+static hashval_t
+cie_hash (const void *e)
+{
+ const struct cie *c = (const struct cie *) e;
+ return c->hash;
+}
+
+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);
+ h = iterative_hash_object (c->code_align, h);
+ h = iterative_hash_object (c->data_align, h);
+ 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->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);
+ 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;
+}
+
+/* Return the number of extra bytes that we'll be inserting into
+ ENTRY's augmentation string. */
+
+static INLINE unsigned int
+extra_augmentation_string_bytes (struct eh_cie_fde *entry)
+{
+ unsigned int size = 0;
+ if (entry->cie)
+ {
+ if (entry->add_augmentation_size)
+ size++;
+ if (entry->u.cie.add_fde_encoding)
+ size++;
+ }
+ return size;
+}
+
+/* Likewise ENTRY's augmentation data. */
+
+static INLINE unsigned int
+extra_augmentation_data_bytes (struct eh_cie_fde *entry)
+{
+ unsigned int size = 0;
+ if (entry->add_augmentation_size)
+ size++;
+ if (entry->cie && entry->u.cie.add_fde_encoding)
+ size++;
+ return size;
+}
+
+/* Return the size that ENTRY will have in the output. ALIGNMENT is the
+ required alignment of ENTRY in bytes. */
+
+static unsigned int
+size_of_output_cie_fde (struct eh_cie_fde *entry, unsigned int alignment)
+{
+ if (entry->removed)
return 0;
+ if (entry->size == 4)
+ return 4;
+ return (entry->size
+ + extra_augmentation_string_bytes (entry)
+ + extra_augmentation_data_bytes (entry)
+ + alignment - 1) & -alignment;
+}
+
+/* Assume that the bytes between *ITER and END are CFA instructions.
+ Try to move *ITER past the first instruction and return true on
+ success. ENCODED_PTR_WIDTH gives the width of pointer entries. */
+
+static bfd_boolean
+skip_cfa_op (bfd_byte **iter, bfd_byte *end, unsigned int encoded_ptr_width)
+{
+ bfd_byte op;
+ bfd_vma length;
+
+ if (!read_byte (iter, end, &op))
+ return FALSE;
+
+ switch (op & 0xc0 ? op & 0xc0 : op)
+ {
+ case DW_CFA_nop:
+ case DW_CFA_advance_loc:
+ case DW_CFA_restore:
+ case DW_CFA_remember_state:
+ case DW_CFA_restore_state:
+ case DW_CFA_GNU_window_save:
+ /* No arguments. */
+ return TRUE;
+
+ case DW_CFA_offset:
+ case DW_CFA_restore_extended:
+ case DW_CFA_undefined:
+ case DW_CFA_same_value:
+ case DW_CFA_def_cfa_register:
+ case DW_CFA_def_cfa_offset:
+ case DW_CFA_def_cfa_offset_sf:
+ case DW_CFA_GNU_args_size:
+ /* One leb128 argument. */
+ return skip_leb128 (iter, end);
+
+ case DW_CFA_val_offset:
+ case DW_CFA_val_offset_sf:
+ case DW_CFA_offset_extended:
+ case DW_CFA_register:
+ case DW_CFA_def_cfa:
+ case DW_CFA_offset_extended_sf:
+ case DW_CFA_GNU_negative_offset_extended:
+ case DW_CFA_def_cfa_sf:
+ /* Two leb128 arguments. */
+ return (skip_leb128 (iter, end)
+ && skip_leb128 (iter, end));
+
+ case DW_CFA_def_cfa_expression:
+ /* A variable-length argument. */
+ return (read_uleb128 (iter, end, &length)
+ && skip_bytes (iter, end, length));
+
+ case DW_CFA_expression:
+ case DW_CFA_val_expression:
+ /* A leb128 followed by a variable-length argument. */
+ return (skip_leb128 (iter, end)
+ && read_uleb128 (iter, end, &length)
+ && skip_bytes (iter, end, length));
+
+ case DW_CFA_set_loc:
+ return skip_bytes (iter, end, encoded_ptr_width);
+
+ case DW_CFA_advance_loc1:
+ return skip_bytes (iter, end, 1);
+
+ case DW_CFA_advance_loc2:
+ return skip_bytes (iter, end, 2);
+
+ case DW_CFA_advance_loc4:
+ return skip_bytes (iter, end, 4);
+
+ case DW_CFA_MIPS_advance_loc8:
+ return skip_bytes (iter, end, 8);
- return 1;
+ default:
+ return FALSE;
+ }
}
-/* This function is called for each input file before the .eh_frame
- section is relocated. It discards duplicate CIEs and FDEs for discarded
- functions. The function returns true iff any entries have been
- deleted. */
+/* Try to interpret the bytes between BUF and END as CFA instructions.
+ If every byte makes sense, return a pointer to the first DW_CFA_nop
+ padding byte, or END if there is no padding. Return null otherwise.
+ ENCODED_PTR_WIDTH is as for skip_cfa_op. */
-boolean
-_bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
- reloc_symbol_deleted_p, cookie)
- bfd *abfd;
- struct bfd_link_info *info;
- asection *sec, *ehdrsec;
- boolean (*reloc_symbol_deleted_p) (bfd_vma, PTR);
- struct elf_reloc_cookie *cookie;
+static bfd_byte *
+skip_non_nops (bfd_byte *buf, bfd_byte *end, unsigned int encoded_ptr_width,
+ unsigned int *set_loc_count)
{
- bfd_byte *ehbuf = NULL, *buf;
- bfd_byte *last_cie, *last_fde;
- struct cie_header hdr;
- struct cie cie;
+ bfd_byte *last;
+
+ last = buf;
+ while (buf < end)
+ if (*buf == DW_CFA_nop)
+ buf++;
+ else
+ {
+ if (*buf == DW_CFA_set_loc)
+ ++*set_loc_count;
+ if (!skip_cfa_op (&buf, end, encoded_ptr_width))
+ return 0;
+ last = buf;
+ }
+ 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. */
+
+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. */
+
+void
+_bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
+ asection *sec, struct elf_reloc_cookie *cookie)
+{
+#define REQUIRE(COND) \
+ do \
+ if (!(COND)) \
+ goto free_no_table; \
+ while (0)
+
+ bfd_byte *ehbuf = NULL, *buf, *end;
+ bfd_byte *last_fde;
+ struct eh_cie_fde *this_inf;
+ unsigned int hdr_length, hdr_id;
+ unsigned int cie_count;
+ struct cie *cie, *local_cies = NULL;
+ struct elf_link_hash_table *htab;
struct eh_frame_hdr_info *hdr_info;
struct eh_frame_sec_info *sec_info = NULL;
- unsigned int leb128_tmp;
- unsigned int cie_usage_count, last_cie_ndx, i, offset;
- unsigned int make_relative, make_lsda_relative;
- Elf_Internal_Rela *rel;
- bfd_size_type new_size;
unsigned int ptr_size;
+ unsigned int num_cies;
+ unsigned int num_entries;
+ elf_gc_mark_hook_fn gc_mark_hook;
- if (sec->_raw_size == 0)
+ 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)
{
/* This file does not contain .eh_frame information. */
- return false;
+ return;
}
- if ((sec->output_section != NULL
- && bfd_is_abs_section (sec->output_section)))
+ 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;
+ link, so we should just ignore them. */
+ return;
}
- BFD_ASSERT (elf_section_data (ehdrsec)->sec_info_type
- == ELF_INFO_TYPE_EH_FRAME_HDR);
- hdr_info = (struct eh_frame_hdr_info *)
- elf_section_data (ehdrsec)->sec_info;
-
/* Read the frame unwind information from abfd. */
- ehbuf = (bfd_byte *) bfd_malloc (sec->_raw_size);
- if (ehbuf == NULL)
- goto free_no_table;
-
- if (! bfd_get_section_contents (abfd, sec, ehbuf, (bfd_vma) 0,
- sec->_raw_size))
- goto free_no_table;
+ REQUIRE (bfd_malloc_and_get_section (abfd, sec, &ehbuf));
- if (sec->_raw_size >= 4
+ if (sec->size >= 4
&& bfd_get_32 (abfd, ehbuf) == 0
&& cookie->rel == cookie->relend)
{
/* Empty .eh_frame section. */
free (ehbuf);
- return false;
+ return;
}
/* 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). */
- if (sec->_raw_size != (unsigned int) sec->_raw_size)
- goto free_no_table;
+ REQUIRE (sec->size == (unsigned int) sec->size);
- ptr_size = (elf_elfheader (abfd)->e_ident[EI_CLASS]
- == ELFCLASS64) ? 8 : 4;
+ ptr_size = (get_elf_backend_data (abfd)
+ ->elf_backend_eh_frame_address_size (abfd, sec));
+ REQUIRE (ptr_size != 0);
+
+ /* Go through the section contents and work out how many FDEs and
+ CIEs there are. */
buf = ehbuf;
- last_cie = NULL;
- last_cie_ndx = 0;
- memset (&cie, 0, sizeof (cie));
- cie_usage_count = 0;
- new_size = sec->_raw_size;
- make_relative = hdr_info->last_cie.make_relative;
- make_lsda_relative = hdr_info->last_cie.make_lsda_relative;
- sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
- + 99 * sizeof (struct eh_cie_fde));
- if (sec_info == NULL)
- goto free_no_table;
- sec_info->alloced = 100;
+ end = ehbuf + sec->size;
+ num_cies = 0;
+ num_entries = 0;
+ while (buf != end)
+ {
+ num_entries++;
+
+ /* Read the length of the entry. */
+ REQUIRE (skip_bytes (&buf, end, 4));
+ hdr_length = bfd_get_32 (abfd, buf - 4);
+
+ /* 64-bit .eh_frame is not supported. */
+ REQUIRE (hdr_length != 0xffffffff);
+ if (hdr_length == 0)
+ break;
+ REQUIRE (skip_bytes (&buf, end, 4));
+ hdr_id = bfd_get_32 (abfd, buf - 4);
+ if (hdr_id == 0)
+ num_cies++;
+
+ 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));
+ 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. */
#define ENSURE_NO_RELOCS(buf) \
- if (cookie->rel < cookie->relend \
- && (cookie->rel->r_offset \
- < (bfd_size_type) ((buf) - ehbuf))) \
- goto free_no_table
+ REQUIRE (!(cookie->rel < cookie->relend \
+ && (cookie->rel->r_offset \
+ < (bfd_size_type) ((buf) - ehbuf)) \
+ && cookie->rel->r_info != 0))
+ /* FIXME: octets_per_byte. */
#define SKIP_RELOCS(buf) \
while (cookie->rel < cookie->relend \
- && (cookie->rel->r_offset \
+ && (cookie->rel->r_offset \
< (bfd_size_type) ((buf) - ehbuf))) \
cookie->rel++
+ /* FIXME: octets_per_byte. */
#define GET_RELOC(buf) \
((cookie->rel < cookie->relend \
&& (cookie->rel->r_offset \
- == (bfd_size_type) ((buf) - ehbuf))) \
+ == (bfd_size_type) ((buf) - ehbuf))) \
? cookie->rel : NULL)
- for (;;)
+ buf = ehbuf;
+ cie_count = 0;
+ gc_mark_hook = get_elf_backend_data (abfd)->gc_mark_hook;
+ while ((bfd_size_type) (buf - ehbuf) != sec->size)
{
- unsigned char *aug;
-
- if (sec_info->count == sec_info->alloced)
- {
- sec_info = bfd_realloc (sec_info,
- sizeof (struct eh_frame_sec_info)
- + (sec_info->alloced + 99)
- * sizeof (struct eh_cie_fde));
- if (sec_info == NULL)
- goto free_no_table;
-
- memset (&sec_info->entry[sec_info->alloced], 0,
- 100 * sizeof (struct eh_cie_fde));
- sec_info->alloced += 100;
- }
+ char *aug;
+ bfd_byte *start, *insns, *insns_end;
+ bfd_size_type length;
+ unsigned int set_loc_count;
+ this_inf = sec_info->entry + sec_info->count;
last_fde = buf;
- /* If we are at the end of the section, we still need to decide
- on whether to output or discard last encountered CIE (if any). */
- if ((bfd_size_type) (buf - ehbuf) == sec->_raw_size)
- hdr.id = (unsigned int) -1;
- else
- {
- if ((bfd_size_type) (buf + 4 - ehbuf) > sec->_raw_size)
- /* No space for CIE/FDE header length. */
- goto free_no_table;
-
- hdr.length = bfd_get_32 (abfd, buf);
- if (hdr.length == 0xffffffff)
- /* 64-bit .eh_frame is not supported. */
- goto free_no_table;
- buf += 4;
- if ((buf - ehbuf) + hdr.length > sec->_raw_size)
- /* CIE/FDE not contained fully in this .eh_frame input section. */
- goto free_no_table;
- sec_info->entry[sec_info->count].offset = last_fde - ehbuf;
- sec_info->entry[sec_info->count].size = 4 + hdr.length;
+ /* Read the length of the entry. */
+ REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4));
+ hdr_length = bfd_get_32 (abfd, buf - 4);
- if (hdr.length == 0)
- {
- /* CIE with length 0 must be only the last in the section. */
- if ((bfd_size_type) (buf - ehbuf) < sec->_raw_size)
- goto free_no_table;
- ENSURE_NO_RELOCS (buf);
- sec_info->count++;
- /* Now just finish last encountered CIE processing and break
- the loop. */
- hdr.id = (unsigned int) -1;
- }
- else
- {
- hdr.id = bfd_get_32 (abfd, buf);
- buf += 4;
- if (hdr.id == (unsigned int) -1)
- goto free_no_table;
- }
+ /* 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;
+ this_inf->reloc_index = cookie->rel - cookie->rels;
+
+ 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;
}
- if (hdr.id == 0 || hdr.id == (unsigned int) -1)
+ REQUIRE (skip_bytes (&buf, end, 4));
+ hdr_id = bfd_get_32 (abfd, buf - 4);
+
+ if (hdr_id == 0)
{
unsigned int initial_insn_length;
/* CIE */
- if (last_cie != NULL)
- {
- /* Now check if this CIE is identical to last CIE, in which case
- we can remove it, provided we adjust all FDEs.
- Also, it can be removed if we have removed all FDEs using
- that. */
- if (cie_compare (&cie, &hdr_info->last_cie) == 0
- || cie_usage_count == 0)
- {
- new_size -= cie.hdr.length + 4;
- sec_info->entry[last_cie_ndx].removed = 1;
- sec_info->entry[last_cie_ndx].sec = hdr_info->last_cie_sec;
- sec_info->entry[last_cie_ndx].new_offset
- = hdr_info->last_cie_offset;
- }
- else
- {
- hdr_info->last_cie = cie;
- hdr_info->last_cie_sec = sec;
- hdr_info->last_cie_offset = last_cie - ehbuf;
- sec_info->entry[last_cie_ndx].make_relative
- = cie.make_relative;
- sec_info->entry[last_cie_ndx].make_lsda_relative
- = cie.make_lsda_relative;
- sec_info->entry[last_cie_ndx].per_encoding_relative
- = (cie.per_encoding & 0x70) == DW_EH_PE_pcrel;
- }
- }
-
- if (hdr.id == (unsigned int) -1)
- break;
+ this_inf->cie = 1;
- last_cie_ndx = sec_info->count;
- sec_info->entry[sec_info->count].cie = 1;
+ /* Point CIE to one of the section-local cie structures. */
+ cie = local_cies + cie_count++;
- cie_usage_count = 0;
- memset (&cie, 0, sizeof (cie));
- cie.hdr = hdr;
- cie.version = *buf++;
+ cie->cie_inf = this_inf;
+ cie->length = hdr_length;
+ cie->output_sec = sec->output_section;
+ start = buf;
+ REQUIRE (read_byte (&buf, end, &cie->version));
/* Cannot handle unknown versions. */
- if (cie.version != 1)
- goto free_no_table;
- if (strlen (buf) > sizeof (cie.augmentation) - 1)
- goto free_no_table;
+ REQUIRE (cie->version == 1
+ || cie->version == 3
+ || cie->version == 4);
+ REQUIRE (strlen ((char *) buf) < sizeof (cie->augmentation));
- strcpy (cie.augmentation, buf);
- buf = strchr (buf, '\0') + 1;
+ strcpy (cie->augmentation, (char *) buf);
+ buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
ENSURE_NO_RELOCS (buf);
if (buf[0] == 'e' && buf[1] == 'h')
{
/* We cannot merge "eh" CIEs because __EXCEPTION_TABLE__
is private to each CIE, so we don't need it for anything.
Just skip it. */
- buf += ptr_size;
+ REQUIRE (skip_bytes (&buf, end, ptr_size));
SKIP_RELOCS (buf);
}
- read_uleb128 (cie.code_align, buf);
- read_sleb128 (cie.data_align, buf);
- read_uleb128 (cie.ra_column, 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)
+ {
+ 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;
+ 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++;
- read_uleb128 (cie.augmentation_size, buf);
+ REQUIRE (read_uleb128 (&buf, end, &cie->augmentation_size));
ENSURE_NO_RELOCS (buf);
}
switch (*aug++)
{
case 'L':
- cie.lsda_encoding = *buf++;
+ REQUIRE (read_byte (&buf, end, &cie->lsda_encoding));
ENSURE_NO_RELOCS (buf);
- if (get_DW_EH_PE_width (cie.lsda_encoding, ptr_size) == 0)
- goto free_no_table;
+ REQUIRE (get_DW_EH_PE_width (cie->lsda_encoding, ptr_size));
break;
case 'R':
- cie.fde_encoding = *buf++;
+ REQUIRE (read_byte (&buf, end, &cie->fde_encoding));
ENSURE_NO_RELOCS (buf);
- if (get_DW_EH_PE_width (cie.fde_encoding, ptr_size) == 0)
- goto free_no_table;
+ REQUIRE (get_DW_EH_PE_width (cie->fde_encoding, ptr_size));
+ break;
+ case 'S':
break;
case 'P':
{
int per_width;
- cie.per_encoding = *buf++;
- per_width = get_DW_EH_PE_width (cie.per_encoding,
+ REQUIRE (read_byte (&buf, end, &cie->per_encoding));
+ per_width = get_DW_EH_PE_width (cie->per_encoding,
ptr_size);
- if (per_width == 0)
- goto free_no_table;
- if ((cie.per_encoding & 0xf0) == DW_EH_PE_aligned)
- buf = (ehbuf
- + ((buf - ehbuf + per_width - 1)
- & ~((bfd_size_type) per_width - 1)));
- ENSURE_NO_RELOCS (buf);
- rel = GET_RELOC (buf);
- /* Ensure we have a reloc here, against
- a global symbol. */
- if (rel != NULL)
+ REQUIRE (per_width);
+ if ((cie->per_encoding & 0x70) == DW_EH_PE_aligned)
{
- 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)
- {
- 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;
- }
- cookie->rel++;
+ length = -(buf - ehbuf) & (per_width - 1);
+ REQUIRE (skip_bytes (&buf, end, length));
}
- buf += per_width;
+ this_inf->u.cie.personality_offset = buf - start;
+ ENSURE_NO_RELOCS (buf);
+ /* Ensure we have a reloc here. */
+ 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));
}
break;
default:
/* For shared libraries, try to get rid of as many RELATIVE relocs
as possible. */
- if (info->shared
- && (cie.fde_encoding & 0xf0) == DW_EH_PE_absptr)
- cie.make_relative = 1;
-
if (info->shared
- && (cie.lsda_encoding & 0xf0) == DW_EH_PE_absptr)
- cie.make_lsda_relative = 1;
+ && (get_elf_backend_data (abfd)
+ ->elf_backend_can_make_relative_eh_frame
+ (abfd, info, sec)))
+ {
+ if ((cie->fde_encoding & 0x70) == DW_EH_PE_absptr)
+ this_inf->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 & 0x70) != DW_EH_PE_aligned)
+ {
+ if (*cie->augmentation == 0)
+ this_inf->add_augmentation_size = 1;
+ this_inf->u.cie.add_fde_encoding = 1;
+ this_inf->make_relative = 1;
+ }
+
+ if ((cie->lsda_encoding & 0x70) == DW_EH_PE_absptr)
+ cie->can_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 = cie.hdr.length - (buf - last_fde - 4);
- if (initial_insn_length <= 50)
- {
- cie.initial_insn_length = initial_insn_length;
- memcpy (cie.initial_instructions, buf, initial_insn_length);
- }
+ if (cie->fde_encoding == DW_EH_PE_omit)
+ cie->fde_encoding = DW_EH_PE_absptr;
+
+ initial_insn_length = end - buf;
+ 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);
- last_cie = last_fde;
+
+ 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;
}
else
{
- /* Ensure this FDE uses the last CIE encountered. */
- if (last_cie == NULL
- || hdr.id != (unsigned int) (buf - 4 - last_cie))
- goto free_no_table;
+ /* Find the corresponding CIE. */
+ unsigned int cie_offset = this_inf->offset + 4 - hdr_id;
+ for (cie = local_cies; cie < local_cies + cie_count; cie++)
+ if (cie_offset == cie->cie_inf->offset)
+ break;
+
+ /* Ensure this FDE references one of the CIEs in this input
+ section. */
+ REQUIRE (cie != local_cies + cie_count);
+ this_inf->u.fde.cie_inf = cie->cie_inf;
+ this_inf->make_relative = cie->cie_inf->make_relative;
+ this_inf->add_augmentation_size
+ = cie->cie_inf->add_augmentation_size;
ENSURE_NO_RELOCS (buf);
- rel = GET_RELOC (buf);
- if (rel == NULL)
- /* This should not happen. */
- goto free_no_table;
- if ((*reloc_symbol_deleted_p) (buf - ehbuf, cookie))
+ if ((sec->flags & SEC_LINKER_CREATED) == 0 || cookie->rels != NULL)
{
- cookie->rel = rel;
- /* This is a FDE against discarded section, it should
- be deleted. */
- new_size -= hdr.length + 4;
- sec_info->entry[sec_info->count].removed = 1;
- }
- else
- {
- if (info->shared
- && (cie.fde_encoding & 0xf0) == DW_EH_PE_absptr
- && cie.make_relative == 0)
+ 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)
{
- /* If 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;
+ REQUIRE (rsec->owner == abfd);
+ this_inf->u.fde.next_for_section = elf_fde_list (rsec);
+ elf_fde_list (rsec) = this_inf;
}
- cie_usage_count++;
- hdr_info->fde_count++;
}
- cookie->rel = rel;
- if (cie.lsda_encoding != DW_EH_PE_omit)
+
+ /* 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)
{
- unsigned int dummy;
-
- aug = buf;
- buf += 2 * get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
- if (cie.augmentation[0] == 'z')
- read_uleb128 (dummy, buf);
- /* If some new augmentation data is added before LSDA
- in FDE augmentation area, this need to be adjusted. */
- sec_info->entry[sec_info->count].lsda_offset = (buf - aug);
+ SKIP_RELOCS (buf);
+ if (cie->can_make_lsda_relative && GET_RELOC (buf))
+ cie->cie_inf->u.cie.make_lsda_relative = 1;
+ 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;
}
- buf = last_fde + 4 + hdr.length;
+
+ /* Skip over the augmentation data. */
+ REQUIRE (skip_bytes (&buf, end, length));
+ insns = buf;
+
+ 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);
}
- sec_info->entry[sec_info->count].fde_encoding = cie.fde_encoding;
- sec_info->entry[sec_info->count].lsda_encoding = cie.lsda_encoding;
+ /* 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 & 0x70) == DW_EH_PE_pcrel
+ || this_inf->make_relative))
+ {
+ unsigned int cnt;
+ bfd_byte *p;
+
+ this_inf->set_loc = (unsigned int *)
+ 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->removed = 1;
+ this_inf->fde_encoding = cie->fde_encoding;
+ this_inf->lsda_encoding = cie->lsda_encoding;
sec_info->count++;
}
+ BFD_ASSERT (sec_info->count == num_entries);
+ BFD_ASSERT (cie_count == num_cies);
elf_section_data (sec)->sec_info = sec_info;
- elf_section_data (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;
+ local_cies = NULL;
+ }
+ goto success;
- /* Ok, now we can assign new offsets. */
- offset = 0;
- last_cie_ndx = 0;
- for (i = 0; i < sec_info->count; i++)
+ free_no_table:
+ (*info->callbacks->einfo)
+ (_("%P: error in %B(%A); no .eh_frame_hdr table will be created.\n"),
+ abfd, sec);
+ hdr_info->table = FALSE;
+ if (sec_info)
+ free (sec_info);
+ success:
+ if (ehbuf)
+ free (ehbuf);
+ if (local_cies)
+ free (local_cies);
+#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. */
+
+static bfd_boolean
+mark_entry (struct bfd_link_info *info, asection *sec,
+ struct eh_cie_fde *ent, elf_gc_mark_hook_fn gc_mark_hook,
+ struct elf_reloc_cookie *cookie)
+{
+ /* FIXME: octets_per_byte. */
+ for (cookie->rel = cookie->rels + ent->reloc_index;
+ cookie->rel < cookie->relend
+ && cookie->rel->r_offset < ent->offset + ent->size;
+ cookie->rel++)
+ if (!_bfd_elf_gc_mark_reloc (info, sec, gc_mark_hook, cookie))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Mark all the relocations against FDEs that relate to code in input
+ section SEC. The FDEs belong to .eh_frame section EH_FRAME, whose
+ relocations are described by COOKIE. */
+
+bfd_boolean
+_bfd_elf_gc_mark_fdes (struct bfd_link_info *info, asection *sec,
+ asection *eh_frame, elf_gc_mark_hook_fn gc_mark_hook,
+ struct elf_reloc_cookie *cookie)
+{
+ struct eh_cie_fde *fde, *cie;
+
+ for (fde = elf_fde_list (sec); fde; fde = fde->u.fde.next_for_section)
{
- if (! sec_info->entry[i].removed)
+ if (!mark_entry (info, eh_frame, fde, gc_mark_hook, cookie))
+ return FALSE;
+
+ /* 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)
{
- sec_info->entry[i].new_offset = offset;
- offset += sec_info->entry[i].size;
- if (sec_info->entry[i].cie)
- {
- last_cie_ndx = i;
- make_relative = sec_info->entry[i].make_relative;
- make_lsda_relative = sec_info->entry[i].make_lsda_relative;
- }
- else
- {
- sec_info->entry[i].make_relative = make_relative;
- sec_info->entry[i].make_lsda_relative = make_lsda_relative;
- sec_info->entry[i].per_encoding_relative = 0;
- }
+ cie->u.cie.gc_mark = 1;
+ if (!mark_entry (info, eh_frame, cie, gc_mark_hook, cookie))
+ return FALSE;
}
- else if (sec_info->entry[i].cie && sec_info->entry[i].sec == sec)
+ }
+ return TRUE;
+}
+
+/* Input section SEC of ABFD is an .eh_frame section that contains the
+ CIE described by CIE_INF. Return a version of CIE_INF that is going
+ to be kept in the output, adding CIE_INF to the output if necessary.
+
+ HDR_INFO is the .eh_frame_hdr information and COOKIE describes the
+ relocations in REL. */
+
+static struct eh_cie_fde *
+find_merged_cie (bfd *abfd, struct bfd_link_info *info, 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)
+ {
+ bfd_boolean per_binds_local;
+
+ /* 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
+ 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)
{
- /* Need to adjust new_offset too. */
- BFD_ASSERT (sec_info->entry[last_cie_ndx].offset
- == sec_info->entry[i].new_offset);
- sec_info->entry[i].new_offset
- = sec_info->entry[last_cie_ndx].new_offset;
+ 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;
+ per_binds_local = SYMBOL_REFERENCES_LOCAL (info, 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.sym.bfd_id = abfd->id;
+ cie->personality.sym.index = r_symndx;
+ per_binds_local = TRUE;
+ }
+
+ if (per_binds_local
+ && info->shared
+ && (cie->per_encoding & 0x70) == DW_EH_PE_absptr
+ && (get_elf_backend_data (abfd)
+ ->elf_backend_can_make_relative_eh_frame (abfd, info, sec)))
+ {
+ cie_inf->u.cie.make_per_encoding_relative = 1;
+ cie_inf->u.cie.per_encoding_relative = 1;
}
}
- if (hdr_info->last_cie_sec == 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)
{
- BFD_ASSERT (sec_info->entry[last_cie_ndx].offset
- == hdr_info->last_cie_offset);
- hdr_info->last_cie_offset = sec_info->entry[last_cie_ndx].new_offset;
+ 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;
- /* FIXME: Currently it is not possible to shrink sections to zero size at
- this point, so build a fake minimal CIE. */
- if (new_size == 0)
- new_size = 16;
+ new_cie = (struct cie *) *loc;
+ if (new_cie == NULL)
+ {
+ /* Keep CIE_INF and record it in the hash table. */
+ new_cie = (struct cie *) malloc (sizeof (struct cie));
+ if (new_cie == NULL)
+ return cie_inf;
- /* Shrink the sec as needed. */
- sec->_cooked_size = new_size;
- if (sec->_cooked_size == 0)
- sec->flags |= SEC_EXCLUDE;
+ 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;
+}
- free (ehbuf);
- return new_size != sec->_raw_size;
+/* This function is called for each input file before the .eh_frame
+ section is relocated. It discards duplicate CIEs and FDEs for discarded
+ functions. The function returns TRUE iff any entries have been
+ deleted. */
-free_no_table:
- if (ehbuf)
- free (ehbuf);
- if (sec_info)
- free (sec_info);
- hdr_info->table = false;
- hdr_info->last_cie.hdr.length = 0;
- return false;
+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)
+{
+ struct eh_cie_fde *ent;
+ struct eh_frame_sec_info *sec_info;
+ 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)
+ /* 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)
+ {
+ 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
+ && ent->make_relative == 0)
+ || (ent->fde_encoding & 0x70) == 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);
+ }
+ ent->removed = 0;
+ hdr_info->fde_count++;
+ ent->u.fde.cie_inf = find_merged_cie (abfd, info, sec, hdr_info,
+ cookie, ent->u.fde.cie_inf);
+ }
+ }
+
+ if (sec_info->cies)
+ {
+ free (sec_info->cies);
+ sec_info->cies = NULL;
+ }
+
+ offset = 0;
+ for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
+ if (!ent->removed)
+ {
+ ent->new_offset = offset;
+ offset += size_of_output_cie_fde (ent, ptr_size);
+ }
+
+ sec->rawsize = sec->size;
+ sec->size = offset;
+ return offset != sec->rawsize;
}
/* This function is called for .eh_frame_hdr section after
_bfd_elf_discard_section_eh_frame has been called on all .eh_frame
input sections. It finalizes the size of .eh_frame_hdr section. */
-boolean
-_bfd_elf_discard_section_eh_frame_hdr (abfd, info, sec)
- bfd *abfd;
- struct bfd_link_info *info;
- asection *sec;
+bfd_boolean
+_bfd_elf_discard_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
{
+ struct elf_link_hash_table *htab;
struct eh_frame_hdr_info *hdr_info;
- unsigned int ptr_size;
+ asection *sec;
- ptr_size = (elf_elfheader (abfd)->e_ident[EI_CLASS]
- == ELFCLASS64) ? 8 : 4;
+ htab = elf_hash_table (info);
+ hdr_info = &htab->eh_info;
- if ((elf_section_data (sec)->sec_info_type
- != ELF_INFO_TYPE_EH_FRAME_HDR)
- || ! info->eh_frame_hdr)
+ if (hdr_info->cies != NULL)
{
- _bfd_strip_section_from_output (info, sec);
- return false;
+ htab_delete (hdr_info->cies);
+ hdr_info->cies = NULL;
}
- hdr_info = (struct eh_frame_hdr_info *)
- elf_section_data (sec)->sec_info;
- if (hdr_info->strip)
- return false;
- sec->_cooked_size = EH_FRAME_HDR_SIZE;
+ sec = hdr_info->hdr_sec;
+ if (sec == NULL)
+ return FALSE;
+
+ sec->size = EH_FRAME_HDR_SIZE;
if (hdr_info->table)
- sec->_cooked_size += 4 + hdr_info->fde_count * 8;
+ sec->size += 4 + hdr_info->fde_count * 8;
+
+ elf_eh_frame_hdr (abfd) = sec;
+ 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");
- /* Request program headers to be recalculated. */
- elf_tdata (abfd)->program_header_size = 0;
- elf_tdata (abfd)->eh_frame_hdr = true;
- return true;
+ 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 later on it is too late for calling _bfd_strip_section_from_output,
- since dynamic symbol table has been sized. */
+ because when the dynamic symbol table has been sized it is too late
+ to strip sections. */
-boolean
-_bfd_elf_maybe_strip_eh_frame_hdr (info)
- struct bfd_link_info *info;
+bfd_boolean
+_bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info)
{
- asection *sec, *o;
- bfd *abfd;
+ struct elf_link_hash_table *htab;
struct eh_frame_hdr_info *hdr_info;
- sec = bfd_get_section_by_name (elf_hash_table (info)->dynobj, ".eh_frame_hdr");
- if (sec == NULL)
- return true;
-
- hdr_info
- = bfd_zmalloc (sizeof (struct eh_frame_hdr_info));
- if (hdr_info == NULL)
- return false;
-
- elf_section_data (sec)->sec_info = hdr_info;
- elf_section_data (sec)->sec_info_type = ELF_INFO_TYPE_EH_FRAME_HDR;
-
- 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->_raw_size > 8)
- break;
- }
+ htab = elf_hash_table (info);
+ hdr_info = &htab->eh_info;
+ if (hdr_info->hdr_sec == NULL)
+ return TRUE;
- if (abfd == NULL)
+ if (bfd_is_abs_section (hdr_info->hdr_sec->output_section)
+ || !info->eh_frame_hdr
+ || !_bfd_elf_eh_frame_present (info))
{
- _bfd_strip_section_from_output (info, sec);
- hdr_info->strip = true;
+ hdr_info->hdr_sec->flags |= SEC_EXCLUDE;
+ hdr_info->hdr_sec = NULL;
+ return TRUE;
}
- else
- hdr_info->table = true;
- return true;
+
+ hdr_info->table = TRUE;
+ return TRUE;
}
/* Adjust an address in the .eh_frame section. Given OFFSET within
or to offset with dynamic relocation which is no longer needed. */
bfd_vma
-_bfd_elf_eh_frame_section_offset (output_bfd, sec, offset)
- bfd *output_bfd ATTRIBUTE_UNUSED;
- asection *sec;
- bfd_vma offset;
+_bfd_elf_eh_frame_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ asection *sec,
+ bfd_vma offset)
{
struct eh_frame_sec_info *sec_info;
unsigned int lo, hi, mid;
- if (elf_section_data (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;
+ sec_info = (struct eh_frame_sec_info *) elf_section_data (sec)->sec_info;
- if (offset >= sec->_raw_size)
- return offset - (sec->_cooked_size - sec->_raw_size);
+ if (offset >= sec->rawsize)
+ return offset - sec->rawsize + sec->size;
lo = 0;
hi = sec_info->count;
if (sec_info->entry[mid].removed)
return (bfd_vma) -1;
+ /* If converting personality pointers to DW_EH_PE_pcrel, there will be
+ no need for run-time relocation against the personality field. */
+ if (sec_info->entry[mid].cie
+ && sec_info->entry[mid].u.cie.make_per_encoding_relative
+ && offset == (sec_info->entry[mid].offset + 8
+ + sec_info->entry[mid].u.cie.personality_offset))
+ return (bfd_vma) -2;
+
/* If converting to DW_EH_PE_pcrel, there will be no need for run-time
relocation against FDE's initial_location field. */
- if (sec_info->entry[mid].make_relative
- && ! sec_info->entry[mid].cie
+ if (!sec_info->entry[mid].cie
+ && sec_info->entry[mid].make_relative
&& offset == sec_info->entry[mid].offset + 8)
return (bfd_vma) -2;
/* If converting LSDA pointers to DW_EH_PE_pcrel, there will be no need
for run-time relocation against LSDA field. */
- if (sec_info->entry[mid].make_lsda_relative
- && ! sec_info->entry[mid].cie
- && (offset
- == (sec_info->entry[mid].offset + 8
- + sec_info->entry[mid].lsda_offset)))
+ if (!sec_info->entry[mid].cie
+ && sec_info->entry[mid].u.fde.cie_inf->u.cie.make_lsda_relative
+ && offset == (sec_info->entry[mid].offset + 8
+ + sec_info->entry[mid].lsda_offset))
return (bfd_vma) -2;
+ /* If converting to DW_EH_PE_pcrel, there will be no need for run-time
+ relocation against DW_CFA_set_loc's arguments. */
+ if (sec_info->entry[mid].set_loc
+ && sec_info->entry[mid].make_relative
+ && (offset >= sec_info->entry[mid].offset + 8
+ + sec_info->entry[mid].set_loc[1]))
+ {
+ unsigned int cnt;
+
+ for (cnt = 1; cnt <= sec_info->entry[mid].set_loc[0]; cnt++)
+ if (offset == sec_info->entry[mid].offset + 8
+ + sec_info->entry[mid].set_loc[cnt])
+ return (bfd_vma) -2;
+ }
+
+ /* Any new augmentation bytes go before the first relocation. */
return (offset + sec_info->entry[mid].new_offset
- - sec_info->entry[mid].offset);
+ - sec_info->entry[mid].offset
+ + extra_augmentation_string_bytes (sec_info->entry + mid)
+ + extra_augmentation_data_bytes (sec_info->entry + mid));
}
/* Write out .eh_frame section. This is called with the relocated
contents. */
-boolean
-_bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
- bfd *abfd;
- asection *sec, *ehdrsec;
- bfd_byte *contents;
+bfd_boolean
+_bfd_elf_write_section_eh_frame (bfd *abfd,
+ struct bfd_link_info *info,
+ asection *sec,
+ bfd_byte *contents)
{
struct eh_frame_sec_info *sec_info;
+ struct elf_link_hash_table *htab;
struct eh_frame_hdr_info *hdr_info;
- unsigned int i;
- bfd_byte *p, *buf;
- unsigned int leb128_tmp;
- unsigned int cie_offset = 0;
unsigned int ptr_size;
-
- ptr_size = (elf_elfheader (sec->owner)->e_ident[EI_CLASS]
- == ELFCLASS64) ? 8 : 4;
-
- if (elf_section_data (sec)->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
- return bfd_set_section_contents (abfd, sec->output_section,
- contents,
- (file_ptr) sec->output_offset,
- sec->_raw_size);
- sec_info = (struct eh_frame_sec_info *)
- elf_section_data (sec)->sec_info;
- hdr_info = NULL;
- if (ehdrsec
- && (elf_section_data (ehdrsec)->sec_info_type
- == ELF_INFO_TYPE_EH_FRAME_HDR))
+ struct eh_cie_fde *ent;
+
+ 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);
+
+ ptr_size = (get_elf_backend_data (abfd)
+ ->elf_backend_eh_frame_address_size (abfd, sec));
+ BFD_ASSERT (ptr_size != 0);
+
+ sec_info = (struct eh_frame_sec_info *) elf_section_data (sec)->sec_info;
+ htab = elf_hash_table (info);
+ hdr_info = &htab->eh_info;
+
+ if (hdr_info->table && hdr_info->array == NULL)
+ hdr_info->array = (struct eh_frame_array_ent *)
+ bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array));
+ if (hdr_info->array == NULL)
+ hdr_info = NULL;
+
+ /* The new offsets can be bigger or smaller than the original offsets.
+ We therefore need to make two passes over the section: one backward
+ pass to move entries up and one forward pass to move entries down.
+ The two passes won't interfere with each other because entries are
+ not reordered */
+ for (ent = sec_info->entry + sec_info->count; ent-- != sec_info->entry;)
+ if (!ent->removed && ent->new_offset > ent->offset)
+ memmove (contents + ent->new_offset, contents + ent->offset, ent->size);
+
+ for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
+ if (!ent->removed && ent->new_offset < ent->offset)
+ memmove (contents + ent->new_offset, contents + ent->offset, ent->size);
+
+ for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
{
- hdr_info = (struct eh_frame_hdr_info *)
- elf_section_data (ehdrsec)->sec_info;
- if (hdr_info->table && hdr_info->array == NULL)
- hdr_info->array
- = bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array));
- if (hdr_info->array == NULL)
- hdr_info = NULL;
- }
+ unsigned char *buf, *end;
+ unsigned int new_size;
- p = contents;
- for (i = 0; i < sec_info->count; ++i)
- {
- if (sec_info->entry[i].removed)
+ if (ent->removed)
+ continue;
+
+ if (ent->size == 4)
{
- if (sec_info->entry[i].cie)
- {
- /* If CIE is removed due to no remaining FDEs referencing it
- and there were no CIEs kept before it, sec_info->entry[i].sec
- will be zero. */
- if (sec_info->entry[i].sec == NULL)
- cie_offset = 0;
- else
- {
- cie_offset = sec_info->entry[i].new_offset;
- cie_offset += (sec_info->entry[i].sec->output_section->vma
- + sec_info->entry[i].sec->output_offset
- - sec->output_section->vma
- - sec->output_offset);
- }
- }
+ /* Any terminating FDE must be at the end of the section. */
+ BFD_ASSERT (ent == sec_info->entry + sec_info->count - 1);
continue;
}
- if (sec_info->entry[i].cie)
+ buf = contents + ent->new_offset;
+ end = buf + ent->size;
+ new_size = size_of_output_cie_fde (ent, ptr_size);
+
+ /* Update the size. It may be shrinked. */
+ bfd_put_32 (abfd, new_size - 4, buf);
+
+ /* Filling the extra bytes with DW_CFA_nops. */
+ if (new_size != ent->size)
+ memset (end, 0, new_size - ent->size);
+
+ if (ent->cie)
{
/* CIE */
- cie_offset = sec_info->entry[i].new_offset;
- if (sec_info->entry[i].make_relative
- || sec_info->entry[i].make_lsda_relative
- || sec_info->entry[i].per_encoding_relative)
+ if (ent->make_relative
+ || ent->u.cie.make_lsda_relative
+ || ent->u.cie.per_encoding_relative)
{
- unsigned char *aug;
- unsigned int action;
- unsigned int dummy, per_width, per_encoding;
+ char *aug;
+ unsigned int action, extra_string, extra_data;
+ unsigned int per_width, per_encoding;
/* Need to find 'R' or 'L' augmentation's argument and modify
DW_EH_PE_* value. */
- action = (sec_info->entry[i].make_relative ? 1 : 0)
- | (sec_info->entry[i].make_lsda_relative ? 2 : 0)
- | (sec_info->entry[i].per_encoding_relative ? 4 : 0);
- buf = contents + sec_info->entry[i].offset;
+ action = ((ent->make_relative ? 1 : 0)
+ | (ent->u.cie.make_lsda_relative ? 2 : 0)
+ | (ent->u.cie.per_encoding_relative ? 4 : 0));
+ extra_string = extra_augmentation_string_bytes (ent);
+ extra_data = extra_augmentation_data_bytes (ent);
+
/* Skip length, id and version. */
buf += 9;
- aug = buf;
- buf = strchr (buf, '\0') + 1;
- read_uleb128 (dummy, buf);
- read_sleb128 (dummy, buf);
- read_uleb128 (dummy, buf);
+ aug = (char *) buf;
+ buf += strlen (aug) + 1;
+ skip_leb128 (&buf, end);
+ skip_leb128 (&buf, end);
+ skip_leb128 (&buf, end);
if (*aug == 'z')
{
- read_uleb128 (dummy, buf);
+ /* The uleb128 will always be a single byte for the kind
+ of augmentation strings that we're prepared to handle. */
+ *buf++ += extra_data;
aug++;
}
+ /* Make room for the new augmentation string and data bytes. */
+ memmove (buf + extra_string + extra_data, buf, end - buf);
+ memmove (aug + extra_string, aug, buf - (bfd_byte *) aug);
+ buf += extra_string;
+ end += extra_string + extra_data;
+
+ if (ent->add_augmentation_size)
+ {
+ *aug++ = 'z';
+ *buf++ = extra_data - 1;
+ }
+ if (ent->u.cie.add_fde_encoding)
+ {
+ BFD_ASSERT (action & 1);
+ *aug++ = 'R';
+ *buf++ = make_pc_relative (DW_EH_PE_absptr, ptr_size);
+ action &= ~1;
+ }
+
while (action)
switch (*aug++)
{
case 'L':
if (action & 2)
{
- BFD_ASSERT (*buf == sec_info->entry[i].lsda_encoding);
- *buf |= DW_EH_PE_pcrel;
+ BFD_ASSERT (*buf == ent->lsda_encoding);
+ *buf = make_pc_relative (*buf, ptr_size);
action &= ~2;
}
buf++;
break;
case 'P':
+ if (ent->u.cie.make_per_encoding_relative)
+ *buf = make_pc_relative (*buf, ptr_size);
per_encoding = *buf++;
- per_width = get_DW_EH_PE_width (per_encoding,
- ptr_size);
+ per_width = get_DW_EH_PE_width (per_encoding, ptr_size);
BFD_ASSERT (per_width != 0);
BFD_ASSERT (((per_encoding & 0x70) == DW_EH_PE_pcrel)
- == sec_info->entry[i].per_encoding_relative);
- if ((per_encoding & 0xf0) == DW_EH_PE_aligned)
+ == ent->u.cie.per_encoding_relative);
+ if ((per_encoding & 0x70) == DW_EH_PE_aligned)
buf = (contents
+ ((buf - contents + per_width - 1)
& ~((bfd_size_type) per_width - 1)));
if (action & 4)
{
- bfd_vma value;
-
- value = read_value (abfd, buf, per_width);
- value += (sec_info->entry[i].offset
- - sec_info->entry[i].new_offset);
- write_value (abfd, buf, value, per_width);
+ bfd_vma val;
+
+ val = read_value (abfd, buf, per_width,
+ get_DW_EH_PE_signed (per_encoding));
+ if (ent->u.cie.make_per_encoding_relative)
+ val -= (sec->output_section->vma
+ + sec->output_offset
+ + (buf - contents));
+ else
+ {
+ val += (bfd_vma) ent->offset - ent->new_offset;
+ val -= extra_string + extra_data;
+ }
+ write_value (abfd, buf, val, per_width);
action &= ~4;
}
buf += per_width;
case 'R':
if (action & 1)
{
- BFD_ASSERT (*buf == sec_info->entry[i].fde_encoding);
- *buf |= DW_EH_PE_pcrel;
+ BFD_ASSERT (*buf == ent->fde_encoding);
+ *buf = make_pc_relative (*buf, ptr_size);
action &= ~1;
}
buf++;
break;
+ case 'S':
+ break;
default:
BFD_FAIL ();
}
else
{
/* FDE */
- bfd_vma value = 0, address;
+ bfd_vma value, address;
unsigned int width;
+ bfd_byte *start;
+ struct eh_cie_fde *cie;
- buf = contents + sec_info->entry[i].offset;
- /* Skip length. */
+ /* Skip length. */
+ cie = ent->u.fde.cie_inf;
buf += 4;
- bfd_put_32 (abfd,
- sec_info->entry[i].new_offset + 4 - cie_offset, buf);
+ value = ((ent->new_offset + sec->output_offset + 4)
+ - (cie->new_offset + cie->u.cie.u.sec->output_offset));
+ bfd_put_32 (abfd, value, buf);
buf += 4;
- width = get_DW_EH_PE_width (sec_info->entry[i].fde_encoding,
- ptr_size);
- address = value = read_value (abfd, buf, width);
+ width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
+ value = read_value (abfd, buf, width,
+ get_DW_EH_PE_signed (ent->fde_encoding));
+ address = value;
if (value)
{
- switch (sec_info->entry[i].fde_encoding & 0xf0)
+ switch (ent->fde_encoding & 0x70)
{
- case DW_EH_PE_indirect:
case DW_EH_PE_textrel:
BFD_ASSERT (hdr_info == NULL);
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:
- value += (sec_info->entry[i].offset
- - sec_info->entry[i].new_offset);
- address += (sec->output_section->vma + sec->output_offset
- + sec_info->entry[i].offset + 8);
+ value += (bfd_vma) ent->offset - ent->new_offset;
+ address += (sec->output_section->vma
+ + sec->output_offset
+ + ent->offset + 8);
break;
}
- if (sec_info->entry[i].make_relative)
- value -= (sec->output_section->vma + sec->output_offset
- + sec_info->entry[i].new_offset + 8);
+ if (ent->make_relative)
+ value -= (sec->output_section->vma
+ + sec->output_offset
+ + ent->new_offset + 8);
write_value (abfd, buf, value, width);
}
+ start = buf;
+
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 + sec->output_offset
- + sec_info->entry[i].new_offset);
+ = (sec->output_section->vma
+ + sec->output_offset
+ + ent->new_offset);
}
- if ((sec_info->entry[i].lsda_encoding & 0xf0) == DW_EH_PE_pcrel
- || sec_info->entry[i].make_lsda_relative)
+ if ((ent->lsda_encoding & 0x70) == DW_EH_PE_pcrel
+ || cie->u.cie.make_lsda_relative)
{
- buf += sec_info->entry[i].lsda_offset;
- width = get_DW_EH_PE_width (sec_info->entry[i].lsda_encoding,
- ptr_size);
- value = read_value (abfd, buf, width);
+ buf += ent->lsda_offset;
+ width = get_DW_EH_PE_width (ent->lsda_encoding, ptr_size);
+ value = read_value (abfd, buf, width,
+ get_DW_EH_PE_signed (ent->lsda_encoding));
if (value)
{
- if ((sec_info->entry[i].lsda_encoding & 0xf0)
- == DW_EH_PE_pcrel)
- value += (sec_info->entry[i].offset
- - sec_info->entry[i].new_offset);
- else if (sec_info->entry[i].make_lsda_relative)
- value -= (sec->output_section->vma + sec->output_offset
- + sec_info->entry[i].new_offset + 8
- + sec_info->entry[i].lsda_offset);
+ if ((ent->lsda_encoding & 0x70) == DW_EH_PE_pcrel)
+ value += (bfd_vma) ent->offset - ent->new_offset;
+ else if (cie->u.cie.make_lsda_relative)
+ value -= (sec->output_section->vma
+ + sec->output_offset
+ + ent->new_offset + 8 + ent->lsda_offset);
write_value (abfd, buf, value, width);
}
}
- }
+ else if (ent->add_augmentation_size)
+ {
+ /* Skip the PC and length and insert a zero byte for the
+ augmentation size. */
+ buf += width * 2;
+ memmove (buf + 1, buf, end - buf);
+ *buf = 0;
+ }
- BFD_ASSERT (p == contents + sec_info->entry[i].new_offset);
- memmove (p, contents + sec_info->entry[i].offset,
- sec_info->entry[i].size);
- p += sec_info->entry[i].size;
- }
+ if (ent->set_loc)
+ {
+ /* Adjust DW_CFA_set_loc. */
+ unsigned int cnt;
+ bfd_vma new_offset;
- /* FIXME: Once _bfd_elf_discard_section_eh_frame will be able to
- shrink sections to zero size, this won't be needed any more. */
- if (p == contents && sec->_cooked_size == 16)
- {
- bfd_put_32 (abfd, 12, p); /* Fake CIE length */
- bfd_put_32 (abfd, 0, p + 4); /* Fake CIE id */
- p[8] = 1; /* Fake CIE version */
- memset (p + 9, 0, 7); /* Fake CIE augmentation, 3xleb128
- and 3xDW_CFA_nop as pad */
- p += 16;
+ width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
+ new_offset = ent->new_offset + 8
+ + extra_augmentation_string_bytes (ent)
+ + extra_augmentation_data_bytes (ent);
+
+ for (cnt = 1; cnt <= ent->set_loc[0]; cnt++)
+ {
+ buf = start + ent->set_loc[cnt];
+
+ value = read_value (abfd, buf, width,
+ get_DW_EH_PE_signed (ent->fde_encoding));
+ if (!value)
+ continue;
+
+ if ((ent->fde_encoding & 0x70) == DW_EH_PE_pcrel)
+ value += (bfd_vma) ent->offset + 8 - new_offset;
+ if (ent->make_relative)
+ value -= (sec->output_section->vma
+ + sec->output_offset
+ + new_offset + ent->set_loc[cnt]);
+ write_value (abfd, buf, value, width);
+ }
+ }
+ }
}
- BFD_ASSERT ((bfd_size_type) (p - contents) == sec->_cooked_size);
+ /* We don't align the section to its section alignment since the
+ runtime library only expects all CIE/FDE records aligned at
+ 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)
+ abort ();
+ /* FIXME: octets_per_byte. */
return bfd_set_section_contents (abfd, sec->output_section,
- contents, (file_ptr) sec->output_offset,
- sec->_cooked_size);
+ contents, (file_ptr) sec->output_offset,
+ sec->size);
}
/* Helper function used to sort .eh_frame_hdr search table by increasing
VMA of FDE initial location. */
static int
-vma_compare (a, b)
- const PTR a;
- const PTR b;
+vma_compare (const void *a, const void *b)
{
- struct eh_frame_array_ent *p = (struct eh_frame_array_ent *) a;
- struct eh_frame_array_ent *q = (struct eh_frame_array_ent *) b;
+ const struct eh_frame_array_ent *p = (const struct eh_frame_array_ent *) a;
+ const struct eh_frame_array_ent *q = (const struct eh_frame_array_ent *) b;
if (p->initial_loc > q->initial_loc)
return 1;
if (p->initial_loc < q->initial_loc)
fde_count x [encoded] initial_loc, fde
(array of encoded pairs containing
FDE initial_location field and FDE address,
- sorted by increasing initial_loc) */
+ sorted by increasing initial_loc). */
-boolean
-_bfd_elf_write_section_eh_frame_hdr (abfd, sec)
- bfd *abfd;
- asection *sec;
+bfd_boolean
+_bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
{
+ struct elf_link_hash_table *htab;
struct eh_frame_hdr_info *hdr_info;
- unsigned int ptr_size;
- bfd_byte *contents;
- asection *eh_frame_sec;
- bfd_size_type size;
-
- ptr_size = (elf_elfheader (sec->owner)->e_ident[EI_CLASS]
- == ELFCLASS64) ? 8 : 4;
-
- BFD_ASSERT (elf_section_data (sec)->sec_info_type
- == ELF_INFO_TYPE_EH_FRAME_HDR);
- hdr_info = (struct eh_frame_hdr_info *)
- elf_section_data (sec)->sec_info;
- if (hdr_info->strip)
- return true;
-
- size = EH_FRAME_HDR_SIZE;
- if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
- size += 4 + hdr_info->fde_count * 8;
- contents = bfd_malloc (size);
- if (contents == NULL)
- return false;
-
- eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame");
- if (eh_frame_sec == NULL)
- return false;
-
- memset (contents, 0, EH_FRAME_HDR_SIZE);
- contents[0] = 1; /* Version */
- contents[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; /* .eh_frame offset */
- if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
- {
- contents[2] = DW_EH_PE_udata4; /* FDE count encoding */
- contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; /* search table enc */
- }
- else
- {
- contents[2] = DW_EH_PE_omit;
- contents[3] = DW_EH_PE_omit;
- }
- bfd_put_32 (abfd, eh_frame_sec->vma - sec->output_section->vma - 4,
- contents + 4);
- if (contents[2] != DW_EH_PE_omit)
+ asection *sec;
+ bfd_boolean retval = TRUE;
+
+ htab = elf_hash_table (info);
+ hdr_info = &htab->eh_info;
+ sec = hdr_info->hdr_sec;
+
+ if (info->eh_frame_hdr && sec != NULL)
{
- unsigned int i;
+ bfd_byte *contents;
+ asection *eh_frame_sec;
+ bfd_size_type size;
+ bfd_vma encoded_eh_frame;
+
+ size = EH_FRAME_HDR_SIZE;
+ if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
+ size += 4 + hdr_info->fde_count * 8;
+ contents = (bfd_byte *) bfd_malloc (size);
+ if (contents == NULL)
+ return FALSE;
+
+ eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame");
+ if (eh_frame_sec == NULL)
+ {
+ free (contents);
+ return FALSE;
+ }
+
+ memset (contents, 0, EH_FRAME_HDR_SIZE);
+ /* Version. */
+ contents[0] = 1;
+ /* .eh_frame offset. */
+ contents[1] = get_elf_backend_data (abfd)->elf_backend_encode_eh_address
+ (abfd, info, eh_frame_sec, 0, sec, 4, &encoded_eh_frame);
- bfd_put_32 (abfd, hdr_info->fde_count, contents + EH_FRAME_HDR_SIZE);
- qsort (hdr_info->array, hdr_info->fde_count, sizeof (*hdr_info->array),
- vma_compare);
- for (i = 0; i < hdr_info->fde_count; i++)
+ if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
{
- 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);
+ /* FDE count encoding. */
+ contents[2] = DW_EH_PE_udata4;
+ /* Search table encoding. */
+ contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
}
+ else
+ {
+ contents[2] = DW_EH_PE_omit;
+ contents[3] = DW_EH_PE_omit;
+ }
+ bfd_put_32 (abfd, encoded_eh_frame, contents + 4);
+
+ if (contents[2] != DW_EH_PE_omit)
+ {
+ unsigned int i;
+
+ bfd_put_32 (abfd, hdr_info->fde_count, contents + EH_FRAME_HDR_SIZE);
+ qsort (hdr_info->array, hdr_info->fde_count,
+ 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);
+ }
+ }
+
+ /* FIXME: octets_per_byte. */
+ retval = bfd_set_section_contents (abfd, sec->output_section, contents,
+ (file_ptr) sec->output_offset,
+ sec->size);
+ free (contents);
}
+ if (hdr_info->array != NULL)
+ free (hdr_info->array);
+ return retval;
+}
- return bfd_set_section_contents (abfd, sec->output_section,
- contents, (file_ptr) sec->output_offset,
- sec->_cooked_size);
+/* Return the width of FDE addresses. This is the default implementation. */
+
+unsigned int
+_bfd_elf_eh_frame_address_size (bfd *abfd, asection *sec ATTRIBUTE_UNUSED)
+{
+ return elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64 ? 8 : 4;
+}
+
+/* Decide whether we can use a PC-relative encoding within the given
+ EH frame section. This is the default implementation. */
+
+bfd_boolean
+_bfd_elf_can_make_relative (bfd *input_bfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ asection *eh_frame_section ATTRIBUTE_UNUSED)
+{
+ return TRUE;
+}
+
+/* Select an encoding for the given address. Preference is given to
+ PC-relative addressing modes. */
+
+bfd_byte
+_bfd_elf_encode_eh_address (bfd *abfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ asection *osec, bfd_vma offset,
+ asection *loc_sec, bfd_vma loc_offset,
+ bfd_vma *encoded)
+{
+ *encoded = osec->vma + offset -
+ (loc_sec->output_section->vma + loc_sec->output_offset + loc_offset);
+ return DW_EH_PE_pcrel | DW_EH_PE_sdata4;
}