> diff --git a/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c
> b/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c
> index 48fa34d2959f4..9880ce4f1931a 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c
> @@ -91,6 +91,84 @@ __jited(" popq %rax")
> __jited(" jmp {{.*}}") /* jump to tail call tgt */
> __jited("L0: leave")
> __jited(" {{(retq|jmp 0x)}}") /* return or jump to rethunk */
> +__arch_powerpc64
> +/* program entry for main(), regular function prologue */
> +__jited(" nop")
Will this test work on CONFIG_PPC64_ELF_ABI_V1 builds? On big-endian ppc64
kernels, arch/powerpc/net/bpf_jit_comp.c prepends a 24-byte function
descriptor to the JIT image:
#ifdef CONFIG_PPC64_ELF_ABI_V1
/* Function descriptor nastiness: Address + TOC */
((u64 *)image)[0] = (u64)fcode_base;
((u64 *)image)[1] = local_paca->kernel_toc;
#endif
fp->bpf_func = (void *)fimage;
fp->jited_len = cgctx.idx * 4 + FUNCTION_DESCR_SIZE;
and bpf_prog_get_info_by_fd() copies jited_len bytes starting at
prog->bpf_func. The disassembler then receives those 24 descriptor bytes as
if they were instructions. The low half of a typical kernel_toc address
such as 0xc000000001b70000 decodes from primary opcode 0, which is illegal,
causing disasm_insn() to call PRINT_FAIL("Can't disasm instruction...") and
fail the test.
The companion commit 9e3a62e5e00a6 truncates the trailing 8 bytes for this
reason, but nothing strips the leading descriptor. Should the test be
restricted to ELFv2, or should jit_disasm_helpers.c skip
FUNCTION_DESCR_SIZE at the head of each function on ppc64 ELFv1?
> +__jited("...") /* ld 2, 16(13) absent with
> CONFIG_PPC_KERNEL_PCREL */
Is this comment accurate? The TOC load is guarded by both conditions:
#ifndef CONFIG_PPC_KERNEL_PCREL
if (IS_ENABLED(CONFIG_PPC64_ELF_ABI_V2))
EMIT(PPC_RAW_LD(_R2, _R13, offsetof(struct paca_struct,
kernel_toc)));
#endif
so it is also absent on CONFIG_PPC64_ELF_ABI_V1 builds.
Also, is the displacement always 16? The offsetof(struct paca_struct,
kernel_toc) is 16 only when CONFIG_PPC_PSERIES=y, because the leading
'struct lppaca *lppaca_ptr' member is inside '#ifdef CONFIG_PPC_PSERIES'.
Without PSERIES the offset is 8, giving 'ld 2, 8(13)'.
> +__jited(" li 9, 0")
> +__jited(" std 9, -8(1)")
> +__jited(" mflr 0")
> +__jited(" std 0, 16(1)")
> +__jited(" stdu 1, {{.*}}(1)")
> +/*
> + * load address and call sub() via count register
> + *
> + * Address materialization differs between PCREL and non-PCREL kernels.
> + * Skip the address generation sequence and verify only that the call
> + * target is loaded into CTR before branching.
> + */
> +__jited("...")
> +__jited("...")
> +__jited("...")
> +__jited("...")
> +__jited("...")
> +__jited("...")
Does the framework need six consecutive '...' entries? In
tools/testing/selftests/bpf/test_loader.c, the push_disasm_msg()
function handles '...' by setting on_next_line = false and returning
immediately without pushing a pattern:
if (strcmp(regex_str, "...") == 0) {
*on_next_line = false;
return 0;
}
So '...' pushes no pattern at all; it only clears the on_next_line flag
for the following real pattern, which then matches anywhere later in the
log. Consecutive '...' entries are idempotent. These six lines behave
identically to a single '...', and five of them are dead. The comment
describes skipping 6 instructions, but the framework doesn't implement
per-line skips this way.
> +__jited(" mtctr {{.*}}")
[ ... ]
> +__jited("...")
> +__jited("func #1")
> +/* subprogram entry for sub() */
> +__jited(" nop")
> +__jited("...") /* ld 2, 16(13) absent with
> CONFIG_PPC_KERNEL_PCREL */
This comment is repeated verbatim from the main() prologue and has the same
issues: the TOC load is also absent on ELFv1, and the displacement varies
with CONFIG_PPC_PSERIES.
> +/* tail call prologue for subprogram */
> +__jited(" ld 10, 0(1)")
> +__jited(" ld 9, -8(10)")
> +__jited(" cmpldi 9, 33")
> +__jited(" bt {{.*}}, {{.*}}")
> +__jited(" addi 9, 10, -8")
> +__jited(" std 9, -8(1)")
> +__jited(" lis {{.*}}, {{.*}}")
> +__jited(" sldi {{.*}}, {{.*}}, 32")
> +__jited(" oris {{.*}}, {{.*}}, {{.*}}")
> +__jited(" ori {{.*}}, {{.*}}, {{.*}}")
Will this lis/sldi/oris/ori sequence always appear in exactly this form?
PPC_LI64() in arch/powerpc/net/bpf_jit.h emits between 2 and 5
instructions, conditionally, based on the runtime value:
EMIT(PPC_RAW_LIS(d, (uintptr_t)(i) >> 48));
if ((uintptr_t)(i) & 0x0000ffff00000000ULL)
EMIT(PPC_RAW_ORI(d, d, ((uintptr_t)(i) >> 32) & 0xffff));
EMIT(PPC_RAW_SLDI(d, d, 32));
if ((uintptr_t)(i) & 0x00000000ffff0000ULL)
EMIT(PPC_RAW_ORIS(d, d, ((uintptr_t)(i) >> 16) & 0xffff));
if ((uintptr_t)(i) & 0x000000000000ffffULL)
EMIT(PPC_RAW_ORI(d, d, (uintptr_t)(i) & 0xffff));
Each of these four patterns follows a non-'...' pattern, so test_loader.c
sets on_next_line=true and requires strict line adjacency. The verifier
rewrites BPF_LD_IMM64 to the kernel address of the kvzalloc()'d 'struct
bpf_array', which is a linear-map address 0xc000000000000000 + physaddr.
When that object lands above the 4 GiB boundary (routine with >4 GiB RAM),
PPC_LI64() emits an extra 'ori' between 'lis' and 'sldi', and the 'sldi'
pattern then matches one line late, causing validate_msgs() to report
WRONG LINE.
Symmetrically, a 64 KiB-aligned map address drops the trailing 'ori', and a
map whose bits 16..31 are zero drops the 'oris', both producing
EXPECTED/no-match failures. The test already recognizes this problem for
the call-target address (the comment about PCREL vs non-PCREL address
materialization plus '...' wildcards). Should the map-address
materialization use a single '__jited("...")' instead?
> +__jited(" li {{.*}}, 0")
> +__jited(" lwz 9, {{.*}}({{.*}})")
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33368376983