diff options
-rw-r--r-- | bfd/ChangeLog | 6 | ||||
-rw-r--r-- | bfd/elf32-ppc.c | 67 | ||||
-rw-r--r-- | ld/ChangeLog | 5 | ||||
-rw-r--r-- | ld/emultempl/ppc32elf.em | 41 |
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 |