[Bug target/106733] New: bpf: facilitate constant propagation of function addresses

jose.marchesi at oracle dot com gcc-bugzilla@gcc.gnu.org
Wed Aug 24 13:22:02 GMT 2022


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106733

            Bug ID: 106733
           Summary: bpf: facilitate constant propagation of function
                    addresses
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: jose.marchesi at oracle dot com
  Target Milestone: ---

eBPF effectively supports two kind of call instructions:

- The so called pseudo-calls ("bpf to bpf").
- External calls ("bpf to kernel").

The BPF call instruction always gets an immediate argument, whose
interpretation varies depending on the purpose of the instruction:

- For pseudo-calls, the immediate argument is interpreted as a
  32-bit PC-relative displacement measured in number of 64-bit words
  minus one.

- For external calls, the immediate argument is interpreted as the
  identification of a kernel helper.

In order to differenciate both flavors of CALL instructions the SRC
field of the instruction (otherwise unused) is abused as an opcode;
if the field holds 0 the instruction is an external call, if it holds
BPF_PSEUDO_CALL the instruction is a pseudo-call.

C-to-BPF toolchains, including the GNU toolchain, use the following
practical heuristic at assembly time in order to determine what kind
of CALL instruction to generate: call instructions requiring a fixup
at assembly time are interpreted as pseudo-calls.  This means that in
practice a call instruction involving symbols at assembly time (such
as `call foo') is assembled into a pseudo-call instruction, whereas
something like `call 12' is assembled into an external call
instruction.

In both cases, the argument of CALL is an immediate: at the time of
writing eBPF lacks support for indirect calls, i.e. there is no
call-to-register instruction.

This is the reason why BPF programs, in practice, rely on certain
optimizations to happen in order to generate calls to immediates.
This is a typical example involving a kernel helper:

  static void * (*bpf_map_lookup_elem)(void *map, const void *key
        = (void *) 1;

  int foo (...)
  {
    char *ret;

    ret = bpf_map_lookup_elem (args...);
    if (ret)
      return 1;
    return 0;
  }

Note how the code above relies on the compiler to do constant
propagation so the call to bpf_map_lookup_elem can be compiled to a
`call 1' instruction.

While GCC provides a kernel_helper function declaration attribute that
can be used in a robust way to tell GCC to generate an external call
despite of optimization level and any other consideration, the Linux
kernel bpf_helpers.h file relies on tricks like the above.

The BPF backend is currently causing the expander to "undo" SSA constant
propagations like the above by loading function addresses into registers.  This
makes code like the above to not compile properly even with optimization levels
of O2 or more.


More information about the Gcc-bugs mailing list