On Mon, Feb 3, 2020 at 10:35 AM H.J. Lu <hjl.to...@gmail.com> wrote: > > Define TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY to make sure that the > ENDBR are emitted before the patch area. When -mfentry -pg is also used > together, there should be no ENDBR before "call __fentry__". > > OK for master if there is no regression? > > Thanks. > > H.J. > -- > gcc/ > > PR target/93492 > * config/i386/i386.c (ix86_asm_output_function_label): Set > function_label_emitted to true. > (ix86_print_patchable_function_entry): New function. > > gcc/testsuite/ > > PR target/93492 > * gcc.target/i386/pr93492-1.c: New test. > * gcc.target/i386/pr93492-2.c: Likewise. > * gcc.target/i386/pr93492-3.c: Likewise. >
This version works with both .cfi_startproc and DWARF debug info. -- H.J.
From c4660acd1555f90f0f76f32a59f043a51c866553 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" <hjl.to...@gmail.com> Date: Mon, 3 Feb 2020 10:22:57 -0800 Subject: [PATCH] i386: Define TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY Define TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY to delay patchable area generation to ENDBR generation. It works with both .cfi_startproc and DWARF debug info. gcc/ PR target/93492 * config/i386/i386-features.c (rest_of_insert_endbranch): Set endbr_queued_at_entrance to TYPE_ENDBR. * config/i386/i386-protos.h (ix86_output_endbr): New. * config/i386/i386.c (ix86_asm_output_function_label): Set function_label_emitted to true. (ix86_print_patchable_function_entry): New function. (ix86_output_endbr): Likewise. (x86_function_profiler): Call ix86_output_endbr to generate ENDBR. (TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY): New. * i386.h (endbr_type): New. (machine_function): Add patch_area_size, record_patch_area and function_label_emitted. Change endbr_queued_at_entrance to enum. * config/i386/i386.md (UNSPECV_PATCH_ENDBR): New. (patch_endbr): New. gcc/testsuite/ PR target/93492 * gcc.target/i386/pr93492-1.c: New test. * gcc.target/i386/pr93492-2.c: Likewise. * gcc.target/i386/pr93492-3.c: Likewise. --- gcc/config/i386/i386-features.c | 2 +- gcc/config/i386/i386-protos.h | 2 + gcc/config/i386/i386.c | 77 ++++++++++++++++++++++- gcc/config/i386/i386.h | 18 +++++- gcc/config/i386/i386.md | 9 +++ gcc/testsuite/gcc.target/i386/pr93492-1.c | 73 +++++++++++++++++++++ gcc/testsuite/gcc.target/i386/pr93492-2.c | 12 ++++ gcc/testsuite/gcc.target/i386/pr93492-3.c | 13 ++++ 8 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/pr93492-1.c create mode 100644 gcc/testsuite/gcc.target/i386/pr93492-2.c create mode 100644 gcc/testsuite/gcc.target/i386/pr93492-3.c diff --git a/gcc/config/i386/i386-features.c b/gcc/config/i386/i386-features.c index b49e6f8d408..4d3d36e9ade 100644 --- a/gcc/config/i386/i386-features.c +++ b/gcc/config/i386/i386-features.c @@ -1963,7 +1963,7 @@ rest_of_insert_endbranch (void) { /* Queue ENDBR insertion to x86_function_profiler. */ if (crtl->profile && flag_fentry) - cfun->machine->endbr_queued_at_entrance = true; + cfun->machine->endbr_queued_at_entrance = TYPE_ENDBR; else { cet_eb = gen_nop_endbr (); diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 266381ca5a6..f9f5a243714 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -38,6 +38,8 @@ extern void ix86_expand_split_stack_prologue (void); extern void ix86_output_addr_vec_elt (FILE *, int); extern void ix86_output_addr_diff_elt (FILE *, int, int); +extern void ix86_output_endbr (bool); + extern enum calling_abi ix86_cfun_abi (void); extern enum calling_abi ix86_function_type_abi (const_tree); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index ffda3e8fd21..e5b2565d5bd 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -1563,6 +1563,9 @@ ix86_asm_output_function_label (FILE *asm_out_file, const char *fname, { bool is_ms_hook = ix86_function_ms_hook_prologue (decl); + if (cfun) + cfun->machine->function_label_emitted = true; + if (is_ms_hook) { int i, filler_count = (TARGET_64BIT ? 32 : 16); @@ -9118,6 +9121,73 @@ ix86_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED) } } +/* Implement TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY. */ + +void +ix86_print_patchable_function_entry (FILE *file, + unsigned HOST_WIDE_INT patch_area_size, + bool record_p) +{ + if (cfun->machine->function_label_emitted) + { + if ((flag_cf_protection & CF_BRANCH) + && !lookup_attribute ("nocf_check", + TYPE_ATTRIBUTES (TREE_TYPE (cfun->decl))) + && (!flag_manual_endbr + || lookup_attribute ("cf_check", + DECL_ATTRIBUTES (cfun->decl))) + && !cgraph_node::get (cfun->decl)->only_called_directly_p ()) + { + /* Change the queued and normal ENDBR to ENDBR with + patchable area. */ + if (cfun->machine->endbr_queued_at_entrance) + cfun->machine->endbr_queued_at_entrance = TYPE_PATCH_ENDBR; + else + { + /* Replace UNSPECV_NOP_ENDBR with UNSPECV_PATCH_ENDBR. */ + rtx_insn *insn = next_real_nondebug_insn (get_insns ()); + if (insn + && INSN_P (insn) + && GET_CODE (PATTERN (insn)) == UNSPEC_VOLATILE + && XINT (PATTERN (insn), 1) == UNSPECV_NOP_ENDBR) + { + PATTERN (insn) = gen_patch_endbr (); + INSN_CODE (insn) = -1; + recog_memoized (insn); + } + } + + /* Record the patchable area. */ + cfun->machine->patch_area_size = patch_area_size; + cfun->machine->record_patch_area = record_p; + + return; + } + } + + default_print_patchable_function_entry (file, patch_area_size, + record_p); +} + +void +ix86_output_endbr (bool patchable) +{ + if (patchable & !cfun->machine->patch_area_size) + sorry ("only one patchable area is supported at functon entry"); + + fprintf (asm_out_file, "\t%s\n", + TARGET_64BIT ? "endbr64" : "endbr32"); + + if (patchable) + { + default_print_patchable_function_entry + (asm_out_file, cfun->machine->patch_area_size, + cfun->machine->record_patch_area); + + cfun->machine->patch_area_size = 0; + } +} + /* Return a scratch register to use in the split stack prologue. The split stack prologue is used for -fsplit-stack. It is the first instructions in the function, even before the regular prologue. @@ -20152,7 +20222,8 @@ void x86_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED) { if (cfun->machine->endbr_queued_at_entrance) - fprintf (file, "\t%s\n", TARGET_64BIT ? "endbr64" : "endbr32"); + ix86_output_endbr (cfun->machine->endbr_queued_at_entrance + == TYPE_PATCH_ENDBR); const char *mcount_name = MCOUNT_NAME; @@ -22744,6 +22815,10 @@ ix86_run_selftests (void) #undef TARGET_ASM_FUNCTION_EPILOGUE #define TARGET_ASM_FUNCTION_EPILOGUE ix86_output_function_epilogue +#undef TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY +#define TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY \ + ix86_print_patchable_function_entry + #undef TARGET_ENCODE_SECTION_INFO #ifndef SUBTARGET_ENCODE_SECTION_INFO #define TARGET_ENCODE_SECTION_INFO ix86_encode_section_info diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 943e9a5c783..bf6855124a4 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -2751,6 +2751,13 @@ enum function_type TYPE_EXCEPTION }; +enum endbr_type +{ + TYPE_NONE = 0, + TYPE_ENDBR, + TYPE_PATCH_ENDBR +}; + struct GTY(()) machine_function { struct stack_local_entry *stack_locals; int varargs_gpr_size; @@ -2767,6 +2774,12 @@ struct GTY(()) machine_function { structure. */ rtx split_stack_varargs_pointer; + /* The size of the patchable area at function entry. */ + unsigned HOST_WIDE_INT patch_area_size; + + /* If true, record patchable area at function entry. */ + BOOL_BITFIELD record_patch_area : 1; + /* This value is used for amd64 targets and specifies the current abi to be used. MS_ABI means ms abi. Otherwise SYSV_ABI means sysv abi. */ ENUM_BITFIELD(calling_abi) call_abi : 8; @@ -2842,7 +2855,10 @@ struct GTY(()) machine_function { BOOL_BITFIELD outgoing_args_on_stack : 1; /* If true, ENDBR is queued at function entrance. */ - BOOL_BITFIELD endbr_queued_at_entrance : 1; + ENUM_BITFIELD(endbr_type) endbr_queued_at_entrance : 2; + + /* If true, the function label has been emitted. */ + BOOL_BITFIELD function_label_emitted : 1; /* True if the function needs a stack frame. */ BOOL_BITFIELD stack_frame_required : 1; diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 46b442dae51..ee476080182 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -271,6 +271,7 @@ ;; For CET support UNSPECV_NOP_ENDBR + UNSPECV_PATCH_ENDBR UNSPECV_NOP_RDSSP UNSPECV_INCSSP UNSPECV_SAVEPREVSSP @@ -20991,6 +20992,14 @@ (set_attr "length_immediate" "0") (set_attr "modrm" "0")]) +(define_insn "patch_endbr" + [(unspec_volatile [(const_int 0)] UNSPECV_PATCH_ENDBR)] + "(flag_cf_protection & CF_BRANCH)" +{ + ix86_output_endbr (true); + return ""; +}) + ;; For RTM support (define_expand "xbegin" [(set (match_operand:SI 0 "register_operand") diff --git a/gcc/testsuite/gcc.target/i386/pr93492-1.c b/gcc/testsuite/gcc.target/i386/pr93492-1.c new file mode 100644 index 00000000000..478c9ba7c04 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr93492-1.c @@ -0,0 +1,73 @@ +/* { dg-do "compile" } */ +/* { dg-options "-O1 -fcf-protection -mmanual-endbr" } */ +/* { dg-final { check-function-bodies "**" "" } } */ + +/* Note: this test only checks the instructions in the function bodies, + not the placement of the patch label or nops before the function. */ + +/* +**f10_none: +** nop +** ret +*/ +void +__attribute__ ((nocf_check,patchable_function_entry (1, 0))) +f10_none () +{ +} + +/* +**f10_endbr: +** endbr(32|64) +** nop +** ret +*/ +void +__attribute__ ((cf_check,patchable_function_entry (1, 0))) +f10_endbr (void) +{ +} + +/* +**f11_none: +** ret +*/ +void +__attribute__ ((nocf_check,patchable_function_entry (1, 1))) +f11_none () +{ +} + +/* +**f11_endbr: +** endbr(32|64) +** ret +*/ +void +__attribute__ ((cf_check,patchable_function_entry (1, 1))) +f11_endbr (void) +{ +} + +/* +**f21_none: +** nop +** ret +*/ +void +__attribute__ ((nocf_check,patchable_function_entry (2, 1))) +f21_none () +{ +} + +/* +**f21_endbr: +** endbr(32|64) +** nop +** ret +*/ +void +__attribute__ ((cf_check,patchable_function_entry (2, 1))) +f21_endbr () +{ +} diff --git a/gcc/testsuite/gcc.target/i386/pr93492-2.c b/gcc/testsuite/gcc.target/i386/pr93492-2.c new file mode 100644 index 00000000000..77279ee957f --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr93492-2.c @@ -0,0 +1,12 @@ +/* { dg-do "compile" } */ +/* { dg-options "-O1 -fcf-protection -mmanual-endbr -fasynchronous-unwind-tables" } */ + +/* Test the placement of the .LPFE1 label. */ + +void +__attribute__ ((cf_check,patchable_function_entry (1, 0))) +f10_endbr () +{ +} + +/* { dg-final { scan-assembler "\t\.cfi_startproc\n\tendbr(32|64)\n.*\.LPFE1:\n\tnop\n\tret\n" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr93492-3.c b/gcc/testsuite/gcc.target/i386/pr93492-3.c new file mode 100644 index 00000000000..ed5129a6a67 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr93492-3.c @@ -0,0 +1,13 @@ +/* { dg-do "compile" } */ +/* { dg-require-effective-target mfentry } */ +/* { dg-options "-O1 -fcf-protection -mmanual-endbr -mfentry -pg -fasynchronous-unwind-tables" } */ + +/* Verify that there is no ENDBR before "call __fentry__". */ + +void +__attribute__ ((cf_check,patchable_function_entry (1, 0))) +f10_endbr () +{ +} + +/* { dg-final { scan-assembler "\t\.cfi_startproc\n\tendbr(32|64)\n.*\.LPFE1:\n\tnop\n1:\tcall\t__fentry__\n\tret\n" } } */ -- 2.24.1