[PATCH v1 6/7] perf annotate: Support BPF JIT disassembly via genelf
From: Ian Rogers
Date: Tue Jun 09 2026 - 01:24:06 EST
For in-memory BPF DSOs (DSO_BINARY_TYPE__BPF_PROG_INFO), write the
JITted instruction buffer to a temporary ELF file on disk using the
existing genelf framework (jit_write_elf). Reroute disassembly to
this temporary ELF file, allowing objdump and libasm to disassemble it
natively. Clean up the temporary file afterward.
Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/util/disasm.c | 107 ++++++++++++++++++++++++++++++++++++---
tools/perf/util/disasm.h | 1 +
2 files changed, 101 insertions(+), 7 deletions(-)
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 42af3603fdff..8c78ef765787 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -23,6 +23,9 @@
#include "debug.h"
#include "disasm.h"
#include "libasm.h"
+#ifdef HAVE_LIBELF_SUPPORT
+#include "genelf.h"
+#endif
#include "dso.h"
#include "dwarf-regs.h"
#include "env.h"
@@ -1420,7 +1423,7 @@ static int symbol__disassemble_objdump(const char *filename, struct symbol *sym,
struct child_process objdump_process;
int err;
- if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO)
+ if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO && !args->is_temp_elf)
return symbol__disassemble_bpf_libbfd(sym, args);
if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE)
@@ -1540,6 +1543,41 @@ static int symbol__disassemble_objdump(const char *filename, struct symbol *sym,
return err;
}
+#ifdef HAVE_LIBELF_SUPPORT
+static int symbol__create_bpf_temp_elf(const char *filename, struct symbol *sym,
+ struct annotate_args *args,
+ char *tmp_elf, size_t tmp_elf_sz)
+{
+ struct map *map = args->ms->map;
+ struct dso *dso = map__dso(map);
+ u8 *code_buf = NULL;
+ const u8 *buf;
+ u64 buf_len;
+ bool is_64bit;
+ int tmp_fd;
+ int err = -1;
+
+ buf = dso__read_symbol(dso, filename, map, sym, &code_buf, &buf_len, &is_64bit);
+ if (!buf)
+ return -1;
+
+ snprintf(tmp_elf, tmp_elf_sz, "/tmp/perf-bpf-XXXXXX");
+ tmp_fd = mkstemp(tmp_elf);
+ if (tmp_fd >= 0) {
+ if (jit_write_elf(tmp_fd, map__rip_2objdump(map, sym->start),
+ sym->name, buf, buf_len,
+ NULL, 0, NULL, 0, 0) == 0) {
+ err = 0;
+ }
+ close(tmp_fd);
+ if (err)
+ unlink(tmp_elf);
+ }
+ free(code_buf);
+ return err;
+}
+#endif
+
int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
{
struct annotation_options *options = args->options;
@@ -1549,8 +1587,11 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
bool delete_extract = false;
struct kcore_extract kce;
bool decomp = false;
- int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
+ int err;
+ args->is_temp_elf = false;
+
+ err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
if (err)
return err;
@@ -1605,7 +1646,25 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
/* FIXME: LLVM and CAPSTONE should support source code */
if (options->annotate_src && !options->hide_src_code) {
- err = symbol__disassemble_objdump(symfs_filename, sym, args);
+ const char *disasm_filename = symfs_filename;
+ bool is_temp = false;
+ char tmp_elf[PATH_MAX];
+
+#ifdef HAVE_LIBELF_SUPPORT
+ if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) {
+ if (symbol__create_bpf_temp_elf(symfs_filename, sym, args,
+ tmp_elf, sizeof(tmp_elf)) == 0) {
+ disasm_filename = tmp_elf;
+ is_temp = true;
+ args->is_temp_elf = true;
+ }
+ }
+#endif
+ err = symbol__disassemble_objdump(disasm_filename, sym, args);
+ if (is_temp) {
+ unlink(tmp_elf);
+ args->is_temp_elf = false;
+ }
if (err == 0)
goto out_remove_tmp;
}
@@ -1613,29 +1672,63 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
err = -1;
for (u8 i = 0; i < ARRAY_SIZE(options->disassemblers) && err != 0; i++) {
enum perf_disassembler dis = options->disassemblers[i];
+ const char *disasm_filename = symfs_filename;
+ bool is_temp = false;
+ char tmp_elf[PATH_MAX];
+
+ switch (dis) {
+ case PERF_DISASM_LIBASM:
+ case PERF_DISASM_OBJDUMP:
+#ifdef HAVE_LIBELF_SUPPORT
+ if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) {
+ if (symbol__create_bpf_temp_elf(symfs_filename, sym, args,
+ tmp_elf, sizeof(tmp_elf)) == 0) {
+ disasm_filename = tmp_elf;
+ is_temp = true;
+ args->is_temp_elf = true;
+ }
+ }
+#endif
+ break;
+ case PERF_DISASM_LLVM:
+ case PERF_DISASM_CAPSTONE:
+ case PERF_DISASM_UNKNOWN:
+ default:
+ break;
+ }
switch (dis) {
case PERF_DISASM_LLVM:
args->options->disassembler_used = PERF_DISASM_LLVM;
- err = symbol__disassemble_llvm(symfs_filename, sym, args);
+ err = symbol__disassemble_llvm(disasm_filename, sym, args);
break;
case PERF_DISASM_CAPSTONE:
args->options->disassembler_used = PERF_DISASM_CAPSTONE;
- err = symbol__disassemble_capstone(symfs_filename, sym, args);
+ err = symbol__disassemble_capstone(disasm_filename, sym, args);
break;
case PERF_DISASM_LIBASM:
args->options->disassembler_used = PERF_DISASM_LIBASM;
- err = symbol__disassemble_libasm(symfs_filename, sym, args);
+ err = symbol__disassemble_libasm(disasm_filename, sym, args);
break;
case PERF_DISASM_OBJDUMP:
args->options->disassembler_used = PERF_DISASM_OBJDUMP;
- err = symbol__disassemble_objdump(symfs_filename, sym, args);
+ err = symbol__disassemble_objdump(disasm_filename, sym, args);
break;
case PERF_DISASM_UNKNOWN: /* End of disassemblers. */
default:
args->options->disassembler_used = PERF_DISASM_UNKNOWN;
+ if (is_temp) {
+ unlink(tmp_elf);
+ args->is_temp_elf = false;
+ }
goto out_remove_tmp;
}
+
+ if (is_temp) {
+ unlink(tmp_elf);
+ args->is_temp_elf = false;
+ }
+
if (err == 0)
pr_debug("Disassembled with %s\n", perf_disassembler__strs[dis]);
}
diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h
index 25756e3f47e4..32a5b3f5d1c6 100644
--- a/tools/perf/util/disasm.h
+++ b/tools/perf/util/disasm.h
@@ -106,6 +106,7 @@ struct annotate_args {
char *line;
int line_nr;
char *fileloc;
+ bool is_temp_elf;
};
const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *cpuid);
--
2.54.0.1064.gd145956f57-goog