aboutsummaryrefslogtreecommitdiff
path: root/ld
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2014-04-05 17:55:13 +1030
committerAlan Modra <amodra@gmail.com>2014-04-09 07:07:06 +0930
commitb4ab4364239efcf0cec74e89a85d844d628138c0 (patch)
tree926ab41676f2482a78260bc59758606a53e4d1f3 /ld
parent9c97a0704e03c662b6afd92032c171fe6714b42d (diff)
downloadfsf-binutils-gdb-b4ab4364239efcf0cec74e89a85d844d628138c0.zip
fsf-binutils-gdb-b4ab4364239efcf0cec74e89a85d844d628138c0.tar.gz
fsf-binutils-gdb-b4ab4364239efcf0cec74e89a85d844d628138c0.tar.bz2
ppc476 icache workaround fix for bctr
I got the ppc476 workaround wrong. bctr (and bctrl) as the last instruction in a page can hit the icache bug if the preceding mtctr insn is close by, and the destination is in the first few instructions on the next page. This scenario can occur with code generated by gcc to implement switch statements, or in code generated to call by function pointer. To prevent the bctr problem it is also necessary to remove other instructions that otherwise would be safe. bfd/ * elf32-ppc.c (ppc_elf_relocate_section): Remove bctr from list of safe ppc476 insns at end of page. Also remove non-branch insns. Expand comments. ld/ * emultempl/ppc32elf.em (no_zero_padding, ppc_finish): New functions. (LDEMUL_FINISH): Define.
Diffstat (limited to 'ld')
-rw-r--r--ld/ChangeLog5
-rw-r--r--ld/emultempl/ppc32elf.em41
2 files changed, 46 insertions, 0 deletions
diff --git a/ld/ChangeLog b/ld/ChangeLog
index 54aa6cd..b64c2e6 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,8 @@
+2014-04-09 Alan Modra <amodra@gmail.com>
+
+ * emultempl/ppc32elf.em (no_zero_padding, ppc_finish): New functions.
+ (LDEMUL_FINISH): Define.
+
2014-04-08 Nick Clifton <nickc@redhat.com>
* scripttempl/pe.sc (R_RSRC): Remove default manifest.
diff --git a/ld/emultempl/ppc32elf.em b/ld/emultempl/ppc32elf.em
index 00a29e2..069acd2 100644
--- a/ld/emultempl/ppc32elf.em
+++ b/ld/emultempl/ppc32elf.em
@@ -27,6 +27,7 @@ fragment <<EOF
#include "libbfd.h"
#include "elf32-ppc.h"
#include "ldlex.h"
+#include "ldlang.h"
#define is_ppc_elf(bfd) \
(bfd_get_flavour (bfd) == bfd_target_elf_flavour \
@@ -173,6 +174,45 @@ ppc_before_allocation (void)
ENABLE_RELAXATION;
}
+/* Replaces default zero fill padding in executable sections with
+ "ba 0" instructions. This works around the ppc476 icache bug if we
+ have a function pointer tail call near the end of a page, some
+ small amount of padding, then the function called at the beginning
+ of the next page. If the "ba 0" is ever executed we should hit a
+ segv, so it's almost as good as an illegal instruction (zero). */
+
+static void
+no_zero_padding (lang_statement_union_type *l)
+{
+ if (l->header.type == lang_padding_statement_enum
+ && l->padding_statement.size != 0
+ && l->padding_statement.output_section != NULL
+ && (l->padding_statement.output_section->flags & SEC_CODE) != 0
+ && l->padding_statement.fill->size == 0)
+ {
+ struct _ppc_fill_type
+ {
+ size_t size;
+ unsigned char data[4];
+ };
+ static struct _ppc_fill_type fill_be = { 4, {0x48, 0, 0, 2} };
+ static struct _ppc_fill_type fill_le = { 4, {2, 0, 0, 0x48} };
+
+ if (bfd_big_endian (link_info.output_bfd))
+ l->padding_statement.fill = (struct _fill_type *) &fill_be;
+ else
+ l->padding_statement.fill = (struct _fill_type *) &fill_le;
+ }
+}
+
+static void
+ppc_finish (void)
+{
+ if (params.ppc476_workaround)
+ lang_for_each_statement (no_zero_padding);
+ finish_default ();
+}
+
EOF
if grep -q 'ld_elf32_spu_emulation' ldemul-list.h; then
@@ -303,3 +343,4 @@ if test -z "$VXWORKS_BASE_EM_FILE" ; then
LDEMUL_AFTER_OPEN=ppc_after_open
fi
LDEMUL_BEFORE_ALLOCATION=ppc_before_allocation
+LDEMUL_FINISH=ppc_finish