[llvm] [RFC][BPF] Support Jump Table (PR #133856)

via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 3 09:09:06 PDT 2025


yonghong-song wrote:

> Thanks @yonghong-song, that size/offset section is really useful! This looks sufficient for me to continue with a PoC.
> 
> > you do not need to care about gotox at all.
> 
> Unfortunately, I do, this is required for verification. For indirect jumps to work, two things should be verified:

You are right. Verification does need to connect jump table map and gotox insn.
When I say 'don't care gotox at all', I actually mean libbpf part where you need to go through various elf sections to connect them together.

> 
> ```
>     rX <- jump_table_x[i] # here jump_table_x[i] should be converted to M[i]
>     ...
>     gotox rX              # here on load imm=fd(M)
> ```
> 
> The `gotox` should reference an instance of an instruction set map `M` directly. This is required to 1) verify that we take each branch in `visit_insn` 2) verify the instruction when we symbolically run the verifier. In the latter case `gotox rX` is allowed iff `imm=M` and `rX` was loaded from the same map `M`.
> 
> So, in order to construct a verifiable program, libbpf should:
> 
>     * find all jump tables, for each jump table `X`:
>       
>       * create an instruction set map `M(X)`
>       * find all loads from table `X`, replace by a map value load from map `M(X)`
> 
>     * find all `gotox`, backtrack the register load from map `M(X)`, set `imm=M(X)` in the `gotox`

Backtrack certainly work. But maybe there is an alternative not to do backtrack.
For example,

            r1 = .LJTI6_0 ll
                <== we will know this is from a map (representing a jump table)
                <== so mark r1 with some info like (jump_table_addr, offset 0)
            r1 += r6
                <== r1 still like jump_table_addr, need to ensure [r6, r6 + 8] is 
                        within jump table range
            r1 = *(u64 *)(r1 + 0)
                <== r1 will be jump_table target, but still keep jump_table reference in r1.
            gotox r1
                <== goto jump_table_target (still has jump_table reference,
                        from verifier perspective, all jump table targets
                        in jump table should be verified).


> 
> 
> (Haven't checked yet for real, but this looks to be enough for "custom", e.g., user-defined, jump tables to work. Just declare it as `static const`, initialize with label addresses, and it will be present in `.rodata`. Maybe the only change that the corresponding `size` section should be pushed manually in this case?)

Your user-defined jump table may work. But it would be great if we can just allow the current common switch statements from code cleanness and developer productivity.

https://github.com/llvm/llvm-project/pull/133856


More information about the llvm-commits mailing list