mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-12-31 19:19:39 -06:00
When clang encounters indirect calls in eBPF programs, it emits a call
instruction with a register parameter (`BPF_X`) instead of an immediate
value (`BPF_K`). This encoding (`BPF_JMP | BPF_CALL | BPF_X = 0x8d`) is
decoded by llvm-objdump as `callx`.
For example, here is a simple C program with an indirect call:
extern void (*ptr_to_some_function)(void);
void call_ptr_to_some_function(void) {
ptr_to_some_function();
}
Compiling and disassembling it gives with clang 14.0 (and LLVM 14.0):
$ clang -O2 -target bpf -c indirect_call.c -o indirect_call.ebpf
$ llvm-objdump -rd indirect_call.ebpf
indirect_call.ebpf: file format elf64-bpf
Disassembly of section .text:
0000000000000000 <call_ptr_to_some_function>:
0: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
0000000000000000: R_BPF_64_64 ptr_to_some_function
2: 79 11 00 00 00 00 00 00 r1 = *(u64 *)(r1 + 0)
3: 8d 00 00 00 01 00 00 00 callx r1
4: 95 00 00 00 00 00 00 00 exit
Contrary to usual eBPF instructions, `callx`'s register operand is
encoded in the immediate field. This encoding is actually specific to
LLVM (and clang). GCC used the destination register to store the target
register.
LLVM 19.1 was modified to use GCC's encoding:
https://github.com/llvm/llvm-project/pull/81546 ("BPF: Change callx insn
encoding"). For example, in an Alpine Linux 3.21 system:
$ clang -target bpf --version
Alpine clang version 19.1.4
Target: bpf
Thread model: posix
InstalledDir: /usr/lib/llvm19/bin
$ clang -O2 -target bpf -c indirect_call.c -o indirect_call.ebpf
$ llvm-objdump -rd indirect_call.ebpf
indirect_call.ebpf: file format elf64-bpf
Disassembly of section .text:
0000000000000000 <call_ptr_to_some_function>:
0: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0x0 ll
0000000000000000: R_BPF_64_64 ptr_to_some_function
2: 79 11 00 00 00 00 00 00 r1 = *(u64 *)(r1 + 0x0)
3: 8d 01 00 00 00 00 00 00 callx r1
4: 95 00 00 00 00 00 00 00 exit
The instruction is now encoded `8d 01 00...`.
For reference, here are similar commands using GCC showing it is using
the same encoding (here, compiler option `-mxbpf` is required to enable
several features including indirect calls, cf.
https://gcc.gnu.org/onlinedocs/gcc-12.4.0/gcc/eBPF-Options.html ).
$ bpf-gcc --version
bpf-gcc (12-20220319-1ubuntu1+2) 12.0.1 20220319 (experimental) [master r12-7719-g8ca61ad148f]
$ bpf-gcc -O2 -c indirect_call.c -o indirect_call.ebpf -mxbpf
$ bpf-objdump -mxbpf -rd indirect_call.ebpf
indirect_call_gcc-12.ebpf: file format elf64-bpfle
Disassembly of section .text:
0000000000000000 <call_ptr_to_some_function>:
0: 18 00 00 00 00 00 00 00 lddw %r0,0
8: 00 00 00 00 00 00 00 00
0: R_BPF_INSN_64 ptr_to_some_function
10: 79 01 00 00 00 00 00 00 ldxdw %r1,[%r0+0]
18: 8d 01 00 00 00 00 00 00 call %r1
20: 95 00 00 00 00 00 00 00 exit
Add both `callx` instruction encodings to eBPF processor.
By the way, the eBPF Verifier used by Linux kernel currently forbids
indirect calls (it fails when `BPF_SRC(insn->code) != BPF_K`, in
https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/bpf/verifier.c?h=v6.14#n19141
). But other deployments of eBPF may already support this feature.