aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfd/ChangeLog6
-rw-r--r--bfd/elf32-ppc.c67
-rw-r--r--ld/ChangeLog5
-rw-r--r--ld/emultempl/ppc32elf.em41
4 files changed, 100 insertions, 19 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 70d23c5..b95d49c 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,9 @@
+2014-04-09 Alan Modra <amodra@gmail.com>
+
+ * 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.
+
2014-04-08 Jon TURNEY <jon.turney@dronecode.org.uk>
* peXXigen.c (pe_print_debugdata): New function: Displays the
diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c
index 868fe50..61e94a0 100644
--- a/bfd/elf32-ppc.c
+++ b/bfd/elf32-ppc.c
@@ -9279,27 +9279,56 @@ ppc_elf_relocate_section (bfd *output_bfd,
if (is_data)
continue;
- /* Some instructions can be left alone too. In this
- category are most insns that unconditionally change
- control flow, and isync. Of these, some *must* be left
- alone, for example, the "bcl 20, 31, label" used in pic
- sequences to give the address of the next insn. twui
- and twu apparently are not safe. */
+ /* Some instructions can be left alone too. Unconditional
+ branches, except for bcctr with BO=0x14 (bctr, bctrl),
+ avoid the icache failure.
+
+ The problem occurs due to prefetch across a page boundary
+ where stale instructions can be fetched from the next
+ page, and the mechanism for flushing these bad
+ instructions fails under certain circumstances. The
+ unconditional branches:
+ 1) Branch: b, bl, ba, bla,
+ 2) Branch Conditional: bc, bca, bcl, bcla,
+ 3) Branch Conditional to Link Register: bclr, bclrl,
+ where (2) and (3) have BO=0x14 making them unconditional,
+ prevent the bad prefetch because the prefetch itself is
+ affected by these instructions. This happens even if the
+ instruction is not executed.
+
+ A bctr example:
+ .
+ . lis 9,new_page@ha
+ . addi 9,9,new_page@l
+ . mtctr 9
+ . bctr
+ . nop
+ . nop
+ . new_page:
+ .
+ The bctr is not predicted taken due to ctr not being
+ ready, so prefetch continues on past the bctr into the
+ new page which might have stale instructions. If they
+ fail to be flushed, then they will be executed after the
+ bctr executes. Either of the following modifications
+ prevent the bad prefetch from happening in the first
+ place:
+ .
+ . lis 9,new_page@ha lis 9,new_page@ha
+ . addi 9,9,new_page@l addi 9,9,new_page@l
+ . mtctr 9 mtctr 9
+ . bctr bctr
+ . nop b somewhere_else
+ . b somewhere_else nop
+ . new_page: new_page:
+ . */
insn = bfd_get_32 (input_bfd, contents + offset);
- if (insn == 0
- || (insn & (0x3f << 26)) == (18u << 26) /* b */
- || ((insn & (0x3f << 26)) == (16u << 26) /* bc always */
- && (insn & (0x14 << 21)) == (0x14 << 21))
- || ((insn & (0x3f << 26)) == (19u << 26) /* blr, bctr */
- && (insn & (0x14 << 21)) == (0x14 << 21)
- && (insn & (0x1ff << 1)) == (16u << 1))
- || (insn & (0x3f << 26)) == (17u << 26) /* sc */
+ if ((insn & (0x3f << 26)) == (18u << 26) /* b,bl,ba,bla */
+ || ((insn & (0x3f << 26)) == (16u << 26) /* bc,bcl,bca,bcla*/
+ && (insn & (0x14 << 21)) == (0x14 << 21)) /* with BO=0x14 */
|| ((insn & (0x3f << 26)) == (19u << 26)
- && ((insn & (0x3ff << 1)) == (38u << 1) /* rfmci */
- || (insn & (0x3ff << 1)) == (50u << 1) /* rfi */
- || (insn & (0x3ff << 1)) == (51u << 1) /* rfci */
- || (insn & (0x3ff << 1)) == (82u << 1) /* rfsvc */
- || (insn & (0x3ff << 1)) == (150u << 1))) /* isync */)
+ && (insn & (0x3ff << 1)) == (16u << 1) /* bclr,bclrl */
+ && (insn & (0x14 << 21)) == (0x14 << 21)))/* with BO=0x14 */
continue;
patch_addr = (start_addr + input_section->size
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