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.
This commit is contained in:
Alan Modra 2014-04-05 17:55:13 +10:30
parent 9c97a0704e
commit b4ab436423
4 changed files with 100 additions and 19 deletions

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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