https://gcc.gnu.org/g:a87863edc04379e5d2ce67c4dc97bb0f4c046d59
commit r17-5014-ga87863edc04379e5d2ce67c4dc97bb0f4c046d59 Author: Vineet Gupta <[email protected]> Date: Fri Oct 9 14:15:11 2026 +0530 bpf: report indirect calls at the call, not at the end of the function This showed up when testing the callx Pseudo-C fix. Indirect calls (out of a register) are diagnosed with -mno-callx, and the diagnostic output was confusing callx.c: In function 'prog': callx.c:12:1: error: indirect call in function, ... 12 | } callx.c:12:1: error: indirect call in function, ... 1. Error comes out at the closing brace of the function. Reason: diagnosed at the time of instruction output, when the location has been lost. Instead needs to be done at the time of expansion. 2. The msg is printed twice. Reason: A call duplicated by a later RTL pass is reported once per copy rather than once per call. Fix by checking the call target in the expansion of the 'call' and 'call_value' patterns instead, where the call still has a location, and return a call to address zero so the expansion can proceed. The above becomes: callx.c:11:16: error: indirect call in function, ... 11 | return f(3, 4); | ^~~~~~~ The canonical pattern of gating the call patterns themselves on bpf_has_callx is not an option: expanding a call cannot fail, so the call would be left unrecognized, turning the user error into an ICE. The test in bpf_output_call is kept as a safety net, for an indirect call reaching the output stage anyway. The error message no longer claims eBPF does not support indirect calls, and names -mno-callx instead. gcc/ChangeLog: * config/bpf/bpf-protos.h (bpf_check_call_target): New prototype. * config/bpf/bpf.cc (bpf_check_call_target): New function. (bpf_output_call): Update comment and error message. * config/bpf/bpf.md (call): Check the call target with bpf_check_call_target. (call_value): Likewise. gcc/testsuite/ChangeLog: * gcc.target/bpf/diag-indcalls.c: Expect the error at the call instead of at the end of the function. * gcc.target/bpf/diag-indcalls-2.c: New test. Signed-off-by: Vineet Gupta <[email protected]> Diff: --- gcc/config/bpf/bpf-protos.h | 1 + gcc/config/bpf/bpf.cc | 32 ++++++++++++++++++++++++-- gcc/config/bpf/bpf.md | 4 ++-- gcc/testsuite/gcc.target/bpf/diag-indcalls-2.c | 13 +++++++++++ gcc/testsuite/gcc.target/bpf/diag-indcalls.c | 4 ++-- 5 files changed, 48 insertions(+), 6 deletions(-) diff --git a/gcc/config/bpf/bpf-protos.h b/gcc/config/bpf/bpf-protos.h index 1113cc3e6d17..eee260b799bf 100644 --- a/gcc/config/bpf/bpf-protos.h +++ b/gcc/config/bpf/bpf-protos.h @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see /* Routines implemented in bpf.cc. */ extern HOST_WIDE_INT bpf_initial_elimination_offset (int, int); +extern rtx bpf_check_call_target (rtx); extern const char *bpf_output_call (const char *templ, rtx *, int target_index); extern void bpf_target_macros (cpp_reader *); extern void bpf_print_operand (FILE *, rtx, int); diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc index d63341022f4d..d852e2fbd70d 100644 --- a/gcc/config/bpf/bpf.cc +++ b/gcc/config/bpf/bpf.cc @@ -758,11 +758,39 @@ bpf_output_destructor (rtx symbol, int priority ATTRIBUTE_UNUSED) #undef TARGET_ASM_DESTRUCTOR #define TARGET_ASM_DESTRUCTOR bpf_output_destructor +/* Check the TARGET of a call, an RTX denoting the address of the called + function, and return the target to actually call. + + Calling through anything but a constant address requires an indirect + CALL instruction, which is not available with -mno-callx. Diagnose + that here, at expansion time, so the error is reported at the call + itself and only once per call; by the time the instruction is output + the location of the call is no longer available. + + This function is called from the expansion of the 'call' and + 'call_value' patterns in bpf.md. */ + +rtx +bpf_check_call_target (rtx target) +{ + if (bpf_has_callx + || GET_CODE (target) == CONST_INT + || GET_CODE (target) == SYMBOL_REF) + return target; + + error_at (curr_insn_location (), + "indirect call in function, not supported with %<-mno-callx%>"); + + /* Call address zero instead, so the expansion can continue. */ + return const0_rtx; +} + /* Return the appropriate instruction to CALL to a function. TARGET is an RTX denoting the address of the called function. The main purposes of this function are: - - To reject indirect CALL instructions when disabled by -mno-callx. + - To reject indirect CALL instructions that reached this point + despite bpf_check_call_target, which is not expected to happen. - To recognize calls to kernel helper functions and emit the corresponding CALL N instruction. @@ -796,7 +824,7 @@ bpf_output_call (const char *templ, rtx *operands, int target_index) default: if (!bpf_has_callx) { - error ("indirect call in function, which are not supported by eBPF"); + error ("indirect call in function, not supported with %<-mno-callx%>"); operands[target_index] = GEN_INT (0); } break; diff --git a/gcc/config/bpf/bpf.md b/gcc/config/bpf/bpf.md index 15d0f95cf0d0..a34e685df127 100644 --- a/gcc/config/bpf/bpf.md +++ b/gcc/config/bpf/bpf.md @@ -534,7 +534,7 @@ (use (match_operand 3 ""))])] ;; struct_value_size_rtx "" { - rtx target = XEXP (operands[0], 0); + rtx target = bpf_check_call_target (XEXP (operands[0], 0)); emit_call_insn (gen_call_internal (target, operands[1])); DONE; }) @@ -561,7 +561,7 @@ (use (match_operand 3 ""))])] ;; next_arg_reg "" { - rtx target = XEXP (operands[1], 0); + rtx target = bpf_check_call_target (XEXP (operands[1], 0)); emit_call_insn (gen_call_value_internal (operands[0], target, operands[2])); DONE; diff --git a/gcc/testsuite/gcc.target/bpf/diag-indcalls-2.c b/gcc/testsuite/gcc.target/bpf/diag-indcalls-2.c new file mode 100644 index 000000000000..f0fab6c430ed --- /dev/null +++ b/gcc/testsuite/gcc.target/bpf/diag-indcalls-2.c @@ -0,0 +1,13 @@ +/* Verify indirect calls are diagnosed at the call itself, and once per + call, also when optimizing. */ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-callx" } */ + +int (*fnp) (int, int); + +int +foo (void (*arg) (void)) +{ + arg (); /* { dg-error "indirect call in function" } */ + return (*fnp) (1, 2); /* { dg-error "indirect call in function" } */ +} diff --git a/gcc/testsuite/gcc.target/bpf/diag-indcalls.c b/gcc/testsuite/gcc.target/bpf/diag-indcalls.c index e83b03fd9987..c0fe4a8eac69 100644 --- a/gcc/testsuite/gcc.target/bpf/diag-indcalls.c +++ b/gcc/testsuite/gcc.target/bpf/diag-indcalls.c @@ -7,5 +7,5 @@ void (*fnp) (void); void foo () { - (*fnp) (); -} /* { dg-error "indirect call in function" } */ + (*fnp) (); /* { dg-error "indirect call in function" } */ +}
