'Retpoline' mitigation technique for Spectre (branch target injection) [CVE-2017-5715]: https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html https://support.google.com/faqs/answer/7625886 https://spectreattack.com/ https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715 Patch copied from the 'retpoline-20180107' branch of upstream source repository (please add new / update existing patches when new 'retpoline-xxxxxxxx' branch appears): http://git.infradead.org/users/dwmw2/gcc-retpoline.git From 63024dad9c00f1613738fd766e2f0afd455b76d1 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Wed, 1 Nov 2017 16:05:50 -0700 Subject: [PATCH 04/17] Add -mindirect-branch=thunk Add tests for -mindirect-branch=thunk --- gcc/config/i386/i386-opts.h | 5 + gcc/config/i386/i386-protos.h | 1 + gcc/config/i386/i386.c | 318 ++++++++++++++++++++++- gcc/config/i386/i386.md | 6 +- gcc/config/i386/i386.opt | 14 + gcc/doc/invoke.texi | 9 +- gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 19 ++ gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 19 ++ gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 20 ++ gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 20 ++ gcc/testsuite/gcc.target/i386/indirect-thunk-5.c | 16 ++ gcc/testsuite/gcc.target/i386/indirect-thunk-6.c | 17 ++ gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 43 +++ 13 files changed, 495 insertions(+), 12 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-1.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-2.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-3.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-4.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-5.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-6.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-7.c diff --git a/gcc/config/i386/i386-opts.h b/gcc/config/i386/i386-opts.h index 542cd0f3d67..1565d8fdc65 100644 --- a/gcc/config/i386/i386-opts.h +++ b/gcc/config/i386/i386-opts.h @@ -99,4 +99,9 @@ enum stack_protector_guard { SSP_GLOBAL /* global canary */ }; +enum indirect_branch { + indirect_branch_keep, + indirect_branch_thunk +}; + #endif diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 8bdd67eb608..b746429f420 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -315,6 +315,7 @@ extern enum attr_cpu ix86_schedule; #endif extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op); +extern const char * ix86_output_indirect_jmp (rtx call_op); extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load, enum machine_mode mode); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 504530a00cf..96424361a1c 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -11909,6 +11909,145 @@ ix86_setup_frame_addresses (void) # endif #endif +static int indirectlabelno; +static bool indirect_thunk_needed = false; +static bool indirect_thunk_bnd_needed = false; + +#ifndef INDIRECT_LABEL +# define INDIRECT_LABEL "LIND" +#endif + +/* Fills in the label name that should be used for the indirect thunk. */ + +static void +indirect_thunk_name (char name[32], bool need_bnd_p) +{ + if (USE_HIDDEN_LINKONCE) + { + const char *bnd = need_bnd_p ? "_bnd" : ""; + sprintf (name, "__x86.indirect_thunk%s", bnd); + } + else + { + if (need_bnd_p) + ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0); + else + ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0); + } +} + +static void +output_indirect_thunk (bool need_bnd_p) +{ + char indirectlabel1[32]; + char indirectlabel2[32]; + + ASM_GENERATE_INTERNAL_LABEL (indirectlabel1, INDIRECT_LABEL, + indirectlabelno++); + ASM_GENERATE_INTERNAL_LABEL (indirectlabel2, INDIRECT_LABEL, + indirectlabelno++); + + /* Call */ + if (need_bnd_p) + fputs ("\tbnd call\t", asm_out_file); + else + fputs ("\tcall\t", asm_out_file); + assemble_name_raw (asm_out_file, indirectlabel2); + fputc ('\n', asm_out_file); + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1); + + /* lfence . */ + fprintf (asm_out_file, "\tlfence\n"); + + /* Jump. */ + fputs ("\tjmp\t", asm_out_file); + assemble_name_raw (asm_out_file, indirectlabel1); + fputc ('\n', asm_out_file); + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2); + + /* LEA. */ + rtx xops[2]; + xops[0] = stack_pointer_rtx; + xops[1] = plus_constant (Pmode, stack_pointer_rtx, UNITS_PER_WORD); + output_asm_insn ("lea\t{%E1, %0|%0, %E1}", xops); + + if (need_bnd_p) + fputs ("\tbnd ret\n", asm_out_file); + else + fputs ("\tret\n", asm_out_file); +} + +static void +output_indirect_thunk_function (bool need_bnd_p) +{ + char name[32]; + tree decl; + + indirect_thunk_name (name, need_bnd_p); + decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL, + get_identifier (name), + build_function_type_list (void_type_node, NULL_TREE)); + DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL, + NULL_TREE, void_type_node); + TREE_PUBLIC (decl) = 1; + TREE_STATIC (decl) = 1; + DECL_IGNORED_P (decl) = 1; + +#if TARGET_MACHO + if (TARGET_MACHO) + { + switch_to_section (darwin_sections[picbase_thunk_section]); + fputs ("\t.weak_definition\t", asm_out_file); + assemble_name (asm_out_file, name); + fputs ("\n\t.private_extern\t", asm_out_file); + assemble_name (asm_out_file, name); + putc ('\n', asm_out_file); + ASM_OUTPUT_LABEL (asm_out_file, name); + DECL_WEAK (decl) = 1; + } + else +#endif + if (USE_HIDDEN_LINKONCE) + { + cgraph_node::create (decl)->set_comdat_group (DECL_ASSEMBLER_NAME (decl)); + + targetm.asm_out.unique_section (decl, 0); + switch_to_section (get_named_section (decl, NULL, 0)); + + targetm.asm_out.globalize_label (asm_out_file, name); + fputs ("\t.hidden\t", asm_out_file); + assemble_name (asm_out_file, name); + putc ('\n', asm_out_file); + ASM_DECLARE_FUNCTION_NAME (asm_out_file, name, decl); + } + else + { + switch_to_section (text_section); + ASM_OUTPUT_LABEL (asm_out_file, name); + } + + DECL_INITIAL (decl) = make_node (BLOCK); + current_function_decl = decl; + allocate_struct_function (decl, false); + init_function_start (decl); + /* We're about to hide the function body from callees of final_* by + emitting it directly; tell them we're a thunk, if they care. */ + cfun->is_thunk = true; + first_function_block_is_cold = false; + /* Make sure unwind info is emitted for the thunk if needed. */ + final_start_function (emit_barrier (), asm_out_file, 1); + + output_indirect_thunk (need_bnd_p); + + final_end_function (); + init_insn_lengths (); + free_after_compilation (cfun); + set_cfun (NULL); + current_function_decl = NULL; +} + static int pic_labels_used; /* Fills in the label name that should be used for a pc thunk for @@ -11935,6 +12074,11 @@ ix86_code_end (void) rtx xops[2]; int regno; + if (indirect_thunk_needed) + output_indirect_thunk_function (false); + if (indirect_thunk_bnd_needed) + output_indirect_thunk_function (true); + for (regno = AX_REG; regno <= SP_REG; regno++) { char name[32]; @@ -28452,12 +28596,132 @@ ix86_nopic_noplt_attribute_p (rtx call_op) return false; } +static void +ix86_output_indirect_branch (rtx call_op, const char *xasm, + bool sibcall_p) +{ + char thunk_name[32]; + char push_buf[64]; + bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn); + + bool need_thunk = ix86_indirect_branch == indirect_branch_thunk; + if (need_bnd_p) + indirect_thunk_bnd_needed |= need_thunk; + else + indirect_thunk_needed |= need_thunk; + indirect_thunk_name (thunk_name, need_bnd_p); + + snprintf (push_buf, sizeof (push_buf), "push{%c}\t%s", + TARGET_64BIT ? 'q' : 'l', xasm); + + if (sibcall_p) + { + output_asm_insn (push_buf, &call_op); + if (thunk_name != NULL) + { + if (need_bnd_p) + fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name); + else + fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name); + } + else + output_indirect_thunk (need_bnd_p); + } + else + { + char indirectlabel1[32]; + char indirectlabel2[32]; + + ASM_GENERATE_INTERNAL_LABEL (indirectlabel1, + INDIRECT_LABEL, + indirectlabelno++); + ASM_GENERATE_INTERNAL_LABEL (indirectlabel2, + INDIRECT_LABEL, + indirectlabelno++); + + /* Jump. */ + if (need_bnd_p) + fputs ("\tbnd jmp\t", asm_out_file); + else + fputs ("\tjmp\t", asm_out_file); + assemble_name_raw (asm_out_file, indirectlabel2); + fputc ('\n', asm_out_file); + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1); + + if (MEM_P (call_op)) + { + struct ix86_address parts; + rtx addr = XEXP (call_op, 0); + if (ix86_decompose_address (addr, &parts) + && parts.base == stack_pointer_rtx) + { + /* Since call will adjust stack by -UNITS_PER_WORD, + we must convert "disp(stack, index, scale)" to + "disp+UNITS_PER_WORD(stack, index, scale)". */ + if (parts.index) + { + addr = gen_rtx_MULT (Pmode, parts.index, + GEN_INT (parts.scale)); + addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx, + addr); + } + else + addr = stack_pointer_rtx; + + rtx disp; + if (parts.disp != NULL_RTX) + disp = plus_constant (Pmode, parts.disp, + UNITS_PER_WORD); + else + disp = GEN_INT (UNITS_PER_WORD); + + addr = gen_rtx_PLUS (Pmode, addr, disp); + call_op = gen_rtx_MEM (GET_MODE (call_op), addr); + } + } + + output_asm_insn (push_buf, &call_op); + + if (need_bnd_p) + fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name); + else + fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name); + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2); + + /* Call. */ + if (need_bnd_p) + fputs ("\tbnd call\t", asm_out_file); + else + fputs ("\tcall\t", asm_out_file); + assemble_name_raw (asm_out_file, indirectlabel1); + fputc ('\n', asm_out_file); + } +} + +const char * +ix86_output_indirect_jmp (rtx call_op) +{ + if (ix86_red_zone_size == 0 + && ix86_indirect_branch != indirect_branch_keep) + { + ix86_output_indirect_branch (call_op, "%0", true); + return ""; + } + else + return "%!jmp\t%A0"; +} + /* Output the assembly for a call instruction. */ const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op) { bool direct_p = constant_call_address_operand (call_op, VOIDmode); + bool output_indirect_p + = (!TARGET_SEH + && ix86_indirect_branch != indirect_branch_keep); bool seh_nop_p = false; const char *xasm; @@ -28467,10 +28731,21 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op) { if (ix86_nopic_noplt_attribute_p (call_op)) { + direct_p = false; if (TARGET_64BIT) - xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}"; + { + if (output_indirect_p) + xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}"; + else + xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}"; + } else - xasm = "%!jmp\t{*%p0@GOT|[DWORD PTR %p0@GOT]}"; + { + if (output_indirect_p) + xasm = "{%p0@GOT|[DWORD PTR %p0@GOT]}"; + else + xasm = "%!jmp\t{*%p0@GOT|[DWORD PTR %p0@GOT]}"; + } } else xasm = "%!jmp\t%P0"; @@ -28480,9 +28755,17 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op) else if (TARGET_SEH) xasm = "%!rex.W jmp\t%A0"; else - xasm = "%!jmp\t%A0"; + { + if (output_indirect_p) + xasm = "%0"; + else + xasm = "%!jmp\t%A0"; + } - output_asm_insn (xasm, &call_op); + if (output_indirect_p && !direct_p) + ix86_output_indirect_branch (call_op, xasm, true); + else + output_asm_insn (xasm, &call_op); return ""; } @@ -28520,18 +28803,37 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op) { if (ix86_nopic_noplt_attribute_p (call_op)) { + direct_p = false; if (TARGET_64BIT) - xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}"; + { + if (output_indirect_p) + xasm = "{%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}"; + else + xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}"; + } else - xasm = "%!call\t{*%p0@GOT|[DWORD PTR %p0@GOT]}"; + { + if (output_indirect_p) + xasm = "{%p0@GOT|[DWORD PTR %p0@GOT]}"; + else + xasm = "%!call\t{*%p0@GOT|[DWORD PTR %p0@GOT]}"; + } } else xasm = "%!call\t%P0"; } else - xasm = "%!call\t%A0"; + { + if (output_indirect_p) + xasm = "%0"; + else + xasm = "%!call\t%A0"; + } - output_asm_insn (xasm, &call_op); + if (output_indirect_p && !direct_p) + ix86_output_indirect_branch (call_op, xasm, false); + else + output_asm_insn (xasm, &call_op); if (seh_nop_p) return "nop"; diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 81cfba57afc..01b7b2039e6 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -11615,7 +11615,7 @@ (define_insn "*indirect_jump" [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))] "" - "%!jmp\t%A0" + "* return ix86_output_indirect_jmp (operands[0]);" [(set_attr "type" "ibr") (set_attr "length_immediate" "0") (set_attr "maybe_prefix_bnd" "1")]) @@ -11665,7 +11665,7 @@ [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw")) (use (label_ref (match_operand 1)))] "" - "%!jmp\t%A0" + "* return ix86_output_indirect_jmp (operands[0]);" [(set_attr "type" "ibr") (set_attr "length_immediate" "0") (set_attr "maybe_prefix_bnd" "1")]) @@ -12337,7 +12337,7 @@ [(simple_return) (use (match_operand:SI 0 "register_operand" "r"))] "reload_completed" - "%!jmp\t%A0" + "* return ix86_output_indirect_jmp (operands[0]);" [(set_attr "type" "ibr") (set_attr "length_immediate" "0") (set_attr "maybe_prefix_bnd" "1")]) diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt index 9384e29b1de..1773e5614cf 100644 --- a/gcc/config/i386/i386.opt +++ b/gcc/config/i386/i386.opt @@ -927,3 +927,17 @@ Attempt to avoid generating instruction sequences containing ret bytes. mgeneral-regs-only Target Report RejectNegative Mask(GENERAL_REGS_ONLY) Var(ix86_target_flags) Save Generate code which uses only the general registers. + +mindirect-branch= +Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_indirect_branch) Init(indirect_branch_keep) +Update indirect call and jump. + +Enum +Name(indirect_branch) Type(enum indirect_branch) +Known indirect branch choices (for use with the -mindirect-branch= option): + +EnumValue +Enum(indirect_branch) String(keep) Value(indirect_branch_keep) + +EnumValue +Enum(indirect_branch) String(thunk) Value(indirect_branch_thunk) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index a0fb09eb9e1..fafda2926bd 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -1210,7 +1210,7 @@ See RS/6000 and PowerPC Options. -msse2avx -mfentry -mrecord-mcount -mnop-mcount -m8bit-idiv @gol -mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol -malign-data=@var{type} -mstack-protector-guard=@var{guard} @gol --mmitigate-rop -mgeneral-regs-only} +-mmitigate-rop -mgeneral-regs-only -mindirect-branch=@var{choice}} @emph{x86 Windows Options} @gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol @@ -25648,6 +25648,13 @@ Generate code that uses only the general-purpose registers. This prevents the compiler from using floating-point, vector, mask and bound registers. +@item -mindirect-branch=@var{choice} +@opindex -mindirect-branch +Update indirect call and jump with @var{choice}. The default is +@samp{keep}, which keeps indirect call and jump unmodified. +@samp{thunk} converts indirect call and jump to push and +PC-relative call thunk. + @end table These @samp{-m} switches are supported in addition to the above diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c new file mode 100644 index 00000000000..d8b6f5a06a5 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ + +typedef void (*dispatch_t)(long offset); + +dispatch_t dispatch; + +void +male_indirect_jump (long offset) +{ + dispatch(offset); +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c new file mode 100644 index 00000000000..f7d5cb315a8 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ + +typedef void (*dispatch_t)(long offset); + +dispatch_t dispatch[256]; + +void +male_indirect_jump (long offset) +{ + dispatch[offset](offset); +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c new file mode 100644 index 00000000000..736d7cda058 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ + +typedef void (*dispatch_t)(long offset); + +dispatch_t dispatch; + +int +male_indirect_jump (long offset) +{ + dispatch(offset); + return 0; +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c new file mode 100644 index 00000000000..cef9b10513e --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ + +typedef void (*dispatch_t)(long offset); + +dispatch_t dispatch[256]; + +int +male_indirect_jump (long offset) +{ + dispatch[offset](offset); + return 0; +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c new file mode 100644 index 00000000000..1a9bb0e431e --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c @@ -0,0 +1,16 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */ + +extern void bar (void); + +void +foo (void) +{ + bar (); +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c new file mode 100644 index 00000000000..bc7d20ec6ad --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c @@ -0,0 +1,17 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */ + +extern void bar (void); + +int +foo (void) +{ + bar (); + return 0; +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar@GOT" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c new file mode 100644 index 00000000000..ea0fa312f64 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c @@ -0,0 +1,43 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ + +void func0 (void); +void func1 (void); +void func2 (void); +void func3 (void); +void func4 (void); +void func4 (void); +void func5 (void); + +void +bar (int i) +{ + switch (i) + { + default: + func0 (); + break; + case 1: + func1 (); + break; + case 2: + func2 (); + break; + case 3: + func3 (); + break; + case 4: + func4 (); + break; + case 5: + func5 (); + break; + } +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ -- 2.15.1