aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf-eh-frame.c
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2017-03-05 14:49:54 +1030
committerAlan Modra <amodra@gmail.com>2017-03-05 14:49:54 +1030
commit2e0ce1c84d328bde4dca24b7cfc8b9c033ed271c (patch)
treeea600af380bd0013091dae62a33abc582fb000b5 /bfd/elf-eh-frame.c
parent0792e0e1199e76dce19a0f9fbdb29c75b8be8a9b (diff)
downloadfsf-binutils-gdb-2e0ce1c84d328bde4dca24b7cfc8b9c033ed271c.zip
fsf-binutils-gdb-2e0ce1c84d328bde4dca24b7cfc8b9c033ed271c.tar.gz
fsf-binutils-gdb-2e0ce1c84d328bde4dca24b7cfc8b9c033ed271c.tar.bz2
Align eh_frame FDEs according to their encoding
bfd/ * elf-bfd.h (struct eh_cie_fde): Add u.cie.per_encoding_aligned8. * elf-eh-frame.c (size_of_output_cie_fde): Don't align here. (next_cie_fde_offset): New function. (_bfd_elf_parse_eh_frame): Set u.cie.per_encoding_aligned8. (_bfd_elf_discard_section_eh_frame): Align zero terminator to four bytes. Align CIEs to four or eight bytes depending on per_encoding_aligned8. Align FDEs according to their encoding. Pad last FDE to output section alignment. (_bfd_elf_write_section_eh_frame): Adjust to suit. Remove assertion. * elf64-ppc.c (glink_eh_frame_cie): Delete padding. (ppc64_elf_size_stubs): Pad glink eh_frame as per elf-eh-frame.c. (ppc64_elf_finish_dynamic_sections): Adjust to suit. ld/ * testsuite/ld-elf/eh3.d: Adjust for eh_frame alignment change. * testsuite/ld-elf/eh6.d: Likewise. * testsuite/ld-alpha/tlsbin.dd: Likewise. * testsuite/ld-alpha/tlsbin.td: Likewise. * testsuite/ld-alpha/tlsbinr.dd: Likewise. * testsuite/ld-alpha/tlspic.dd: Likewise. * testsuite/ld-alpha/tlspic.rd: Likewise. * testsuite/ld-alpha/tlspic.sd: Likewise. * testsuite/ld-alpha/tlspic.td: Likewise. * testsuite/ld-mips-elf/eh-frame1-n64.d: Likewise. * testsuite/ld-mips-elf/eh-frame2-n64.d: Likewise. * testsuite/ld-mips-elf/eh-frame3.d: Likewise. * testsuite/ld-x86-64/pr20830a.d: Likewise. * testsuite/ld-x86-64/pr21038a.d: Likewise. * testsuite/ld-x86-64/pr21038b.d: Likewise. * testsuite/ld-x86-64/pr21038c.d: Likewise.
Diffstat (limited to 'bfd/elf-eh-frame.c')
-rw-r--r--bfd/elf-eh-frame.c83
1 files changed, 59 insertions, 24 deletions
diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c
index 1b03b9b..6967f37 100644
--- a/bfd/elf-eh-frame.c
+++ b/bfd/elf-eh-frame.c
@@ -309,11 +309,10 @@ extra_augmentation_data_bytes (struct eh_cie_fde *entry)
return size;
}
-/* Return the size that ENTRY will have in the output. ALIGNMENT is the
- required alignment of ENTRY in bytes. */
+/* Return the size that ENTRY will have in the output. */
static unsigned int
-size_of_output_cie_fde (struct eh_cie_fde *entry, unsigned int alignment)
+size_of_output_cie_fde (struct eh_cie_fde *entry)
{
if (entry->removed)
return 0;
@@ -321,8 +320,22 @@ size_of_output_cie_fde (struct eh_cie_fde *entry, unsigned int alignment)
return 4;
return (entry->size
+ extra_augmentation_string_bytes (entry)
- + extra_augmentation_data_bytes (entry)
- + alignment - 1) & -alignment;
+ + extra_augmentation_data_bytes (entry));
+}
+
+/* Return the offset of the FDE or CIE after ENT. */
+
+static unsigned int
+next_cie_fde_offset (struct eh_cie_fde *ent,
+ struct eh_cie_fde *last,
+ asection *sec)
+{
+ while (++ent < last)
+ {
+ if (!ent->removed)
+ return ent->new_offset;
+ }
+ return sec->size;
}
/* Assume that the bytes between *ITER and END are CFA instructions.
@@ -811,6 +824,8 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
{
length = -(buf - ehbuf) & (per_width - 1);
REQUIRE (skip_bytes (&buf, end, length));
+ if (per_width == 8)
+ this_inf->u.cie.per_encoding_aligned8 = 1;
}
this_inf->u.cie.personality_offset = buf - start;
ENSURE_NO_RELOCS (buf);
@@ -1326,7 +1341,7 @@ _bfd_elf_discard_section_eh_frame
struct eh_cie_fde *ent;
struct eh_frame_sec_info *sec_info;
struct eh_frame_hdr_info *hdr_info;
- unsigned int ptr_size, offset;
+ unsigned int ptr_size, offset, eh_alignment;
if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME)
return FALSE;
@@ -1406,14 +1421,46 @@ _bfd_elf_discard_section_eh_frame
sec_info->cies = NULL;
}
+ /* It may be that some .eh_frame input section has greater alignment
+ than other .eh_frame sections. In that case we run the risk of
+ padding with zeros before that section, which would be seen as a
+ zero terminator. Alignment padding must be added *inside* the
+ last FDE instead. For other FDEs we align according to their
+ encoding, in order to align FDE address range entries naturally. */
offset = 0;
for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
if (!ent->removed)
{
+ eh_alignment = 4;
+ if (ent->size == 4)
+ ;
+ else if (ent->cie)
+ {
+ if (ent->u.cie.per_encoding_aligned8)
+ eh_alignment = 8;
+ }
+ else
+ {
+ eh_alignment = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
+ if (eh_alignment < 4)
+ eh_alignment = 4;
+ }
+ offset = (offset + eh_alignment - 1) & -eh_alignment;
ent->new_offset = offset;
- offset += size_of_output_cie_fde (ent, ptr_size);
+ offset += size_of_output_cie_fde (ent);
}
+ /* Pad the last FDE out to the output section alignment if there are
+ following sections, in order to ensure no padding between this
+ section and the next. (Relies on the output section alignment
+ being the maximum of all input sections alignments, which is the
+ case unless someone is overriding alignment via scripts.) */
+ eh_alignment = 4;
+ if (sec->map_head.s != NULL
+ && (sec->map_head.s->size != 4
+ || sec->map_head.s->map_head.s != NULL))
+ eh_alignment = 1 << sec->output_section->alignment_power;
+ offset = (offset + eh_alignment - 1) & -eh_alignment;
sec->rawsize = sec->size;
sec->size = offset;
return offset != sec->rawsize;
@@ -1732,8 +1779,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
struct elf_link_hash_table *htab;
struct eh_frame_hdr_info *hdr_info;
unsigned int ptr_size;
- struct eh_cie_fde *ent;
- bfd_size_type sec_size;
+ struct eh_cie_fde *ent, *last_ent;
if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME)
/* FIXME: octets_per_byte. */
@@ -1771,7 +1817,8 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
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)
+ last_ent = sec_info->entry + sec_info->count;
+ for (ent = sec_info->entry; ent < last_ent; ++ent)
{
unsigned char *buf, *end;
unsigned int new_size;
@@ -1782,13 +1829,13 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
if (ent->size == 4)
{
/* Any terminating FDE must be at the end of the section. */
- BFD_ASSERT (ent == sec_info->entry + sec_info->count - 1);
+ BFD_ASSERT (ent == last_ent - 1);
continue;
}
buf = contents + ent->new_offset;
end = buf + ent->size;
- new_size = size_of_output_cie_fde (ent, ptr_size);
+ new_size = next_cie_fde_offset (ent, last_ent, sec) - ent->new_offset;
/* Update the size. It may be shrinked. */
bfd_put_32 (abfd, new_size - 4, buf);
@@ -2059,18 +2106,6 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
}
}
- /* 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. */
- sec_size = sec->size;
- if (sec_info->count != 0
- && sec_info->entry[sec_info->count - 1].size == 4)
- sec_size -= 4;
- 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,