eBPF Talk: Fixing a 7‑Year‑Old Bug in bpftool
The article details how a long‑standing bug that displayed incorrect call‑address information in bpftool’s JIT disassembly was reproduced, analyzed, and fixed by correcting the PC parameter to use the function’s kernel symbol address, with patches applied to both LLVM and libbfd back‑ends.
Bug Reproduction
Running # bpftool prog dump jited name kprobe_skb_1 shows an incorrect call address (e.g., c70: call 0xffffffffd2702ab4). Examining the commit history confirms that every version of bpftool contains this error.
Investigation of the Disassembly Logic
Inspecting bpftool prog dump jited source reveals the core function disassemble_insn which ultimately calls LLVMDisasmInstruction(). The LLVM documentation states that the PC argument must be the instruction’s address.
size_t LLVMDisasmInstruction(LLVMDisasmContextRef DC, uint8_t *Bytes,
uint64_t BytesSize, uint64_t PC,
char *OutString, size_t OutStringSize);Therefore the existing call passes pc (the instruction index) instead of the real address.
Patch Attempt – LLVM Path
The fix changes the pc argument to func_ksym + pc, where func_ksym is the start address of the BPF program image.
-count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, pc,
+count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, func_ksym + pc,
buf, sizeof(buf));The function signature is extended accordingly:
int disassemble_insn(disasm_ctx_t *ctx, unsigned char *image,
ssize_t len, int pc, __u64 func_ksym)Building LLVM and bpftool with debugging symbols ( -g) and testing in gdb confirms that the corrected address is now used.
Patch Attempt – libbfd Path
When libbfd is the disassembly backend, relative‑address instructions (e.g., call, je) are also displayed incorrectly because the default print_address_func does not add the function’s base address.
The solution defines a custom disasm_print_addr that adds func_ksym before delegating to generic_print_address:
struct disasm_info { struct disassemble_info info; __u64 func_ksym; };
static void disasm_print_addr(bfd_vma addr, struct disassemble_info *info) {
struct disasm_info *dinfo = container_of(info, struct disasm_info, info);
addr += dinfo->func_ksym;
generic_print_address(addr, info);
}The info->print_address_func is set to this custom function, ensuring correct address calculation for relative instructions.
Conclusion
The patches correcting the PC calculation for both LLVM and libbfd backends have been merged into the Linux kernel’s BPF repository, eliminating a seven‑year‑old bug that caused misleading disassembly output.
References
Wrong callq address displayed: https://github.com/libbpf/bpftool/issues/109
Patch fixing disasm PC: https://lore.kernel.org/bpf/[email protected]/
LLVMDisasmInstruction documentation: https://llvm.org/doxygen/group__LLVMCDisassembler.html#gad1cbbd5aa7b51f04687926e8f9e4aebb
bpftool: Add LLVM as default library for disassembling JIT‑ed programs: https://github.com/torvalds/linux/commit/eb9d1acf634baf6401dfb4f67dc895290713a357
tools: bpf: add bpftool: https://github.com/torvalds/linux/commit/71bb428fe2c19512ac671d5ee16ef3e73e1b49a8
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
