* [PATCH v1 1/7] tools build: Add feature check for elfutils libasm
2026-06-09 5:17 [PATCH v1 0/7] perf annotate: Add elfutils libasm disassembler backend Ian Rogers
@ 2026-06-09 5:17 ` Ian Rogers
2026-06-09 5:28 ` sashiko-bot
2026-06-09 5:17 ` [PATCH v1 2/7] perf build: Add build support and capability " Ian Rogers
` (5 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Ian Rogers @ 2026-06-09 5:17 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Nick Terrell, David Sterba, Nathan Chancellor, Tomas Glozar,
Blake Jones, Dmitrii Dolgov, Alexandre Chartre, Costa Shulyupin,
Yuzhuo Jing, Michael Jeanson, Leo Yan, Tianyou Li, Zecheng Li,
Rong Bao, linux-kernel, linux-perf-users, bpf
Introduce the build-time feature check for elfutils libasm library
in the shared tools/build system.
Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/build/Makefile.feature | 2 ++
tools/build/feature/Makefile | 9 +++++++++
tools/build/feature/test-libasm.c | 19 +++++++++++++++++++
3 files changed, 30 insertions(+)
create mode 100644 tools/build/feature/test-libasm.c
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index ed1374af31c1..422329dbc20a 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -118,6 +118,7 @@ FEATURE_TESTS_EXTRA := \
hello \
babeltrace2-ctf-writer \
libcapstone \
+ libasm \
libcheck \
libbfd-liberty \
libbfd-liberty-z \
@@ -150,6 +151,7 @@ FEATURE_DISPLAY ?= \
numa_num_possible_cpus \
libpython \
libcapstone \
+ libasm \
llvm-perf \
zlib \
lzma \
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 62909a9c799d..f0f39a2ab203 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -47,6 +47,7 @@ FILES= \
test-timerfd.bin \
test-babeltrace2-ctf-writer.bin \
test-libcapstone.bin \
+ test-libasm.bin \
test-libcheck.bin \
test-compile-32.bin \
test-compile-x32.bin \
@@ -184,6 +185,11 @@ ifeq ($(findstring -static,${LDFLAGS}),-static)
DWARFLIBS += -ldl
endif
+ASMLIBS := -lasm -ldw -lelf
+ifeq ($(findstring -static,${LDFLAGS}),-static)
+ ASMLIBS += -lz -llzma -lbz2 -lzstd
+endif
+
$(OUTPUT)test-libdw.bin:
$(BUILD) $(DWLIBS)
@@ -311,6 +317,9 @@ $(OUTPUT)test-babeltrace2-ctf-writer.bin:
$(OUTPUT)test-libcapstone.bin:
$(BUILD) # -lcapstone provided by $(FEATURE_CHECK_LDFLAGS-libcapstone)
+$(OUTPUT)test-libasm.bin:
+ $(BUILD) $(ASMLIBS)
+
$(OUTPUT)test-libcheck.bin:
$(BUILD) # -lcheck is provided by $(FEATURE_CHECK_LDFLAGS-libcheck)
diff --git a/tools/build/feature/test-libasm.c b/tools/build/feature/test-libasm.c
new file mode 100644
index 000000000000..f3598ad1cc9e
--- /dev/null
+++ b/tools/build/feature/test-libasm.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stddef.h>
+#include <elfutils/libasm.h>
+#include <gelf.h>
+
+typedef struct ebl Ebl;
+extern Ebl *ebl_openbackend (Elf *elf);
+extern void ebl_closebackend (Ebl *ebl);
+
+int main(void)
+{
+ Elf *elf = elf_begin(0, ELF_C_READ, NULL);
+ Ebl *ebl = ebl_openbackend(elf);
+ DisasmCtx_t *ctx = disasm_begin(ebl, elf, NULL);
+ disasm_end(ctx);
+ ebl_closebackend(ebl);
+ elf_end(elf);
+ return 0;
+}
--
2.54.0.1064.gd145956f57-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v1 1/7] tools build: Add feature check for elfutils libasm
2026-06-09 5:17 ` [PATCH v1 1/7] tools build: Add feature check for elfutils libasm Ian Rogers
@ 2026-06-09 5:28 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-06-09 5:28 UTC (permalink / raw)
To: Ian Rogers; +Cc: bpf, linux-perf-users
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The `ASMLIBS` definition lacks required dependencies (`-lebl` and `-ldl`) for static builds and older `elfutils` versions, and relies on an unexported symbol in newer versions, guaranteeing spurious feature test failures.
- [Medium] Passing `NULL` to the third parameter of `disasm_begin` triggers a `-Wint-conversion` error, spuriously failing the feature test.
--
commit aae74fddf97207e82eca20f4dc0b26d8dfeda751
Author: Ian Rogers <irogers@google.com>
tools build: Add feature check for elfutils libasm
This commit introduces a build-time feature test for the elfutils libasm
library in the shared tools/build system. It adds the test program and
updates the Makefiles to attempt building it to detect support.
> diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
> index 62909a9c799d3..f0f39a2ab2035 100644
> --- a/tools/build/feature/Makefile
> +++ b/tools/build/feature/Makefile
[ ... ]
> @@ -184,6 +185,11 @@ ifeq ($(findstring -static,${LDFLAGS}),-static)
> DWARFLIBS += -ldl
> endif
>
> +ASMLIBS := -lasm -ldw -lelf
> +ifeq ($(findstring -static,${LDFLAGS}),-static)
> + ASMLIBS += -lz -llzma -lbz2 -lzstd
> +endif
[Severity: Medium]
Does this ASMLIBS definition miss required legacy dependencies? For static
builds on older elfutils versions, omitting -lebl and -ldl here might cause
the feature test to fail to link, silently disabling the libasm feature.
> diff --git a/tools/build/feature/test-libasm.c b/tools/build/feature/test-libasm.c
> new file mode 100644
> index 0000000000000..f3598ad1cc9e1
> --- /dev/null
> +++ b/tools/build/feature/test-libasm.c
> @@ -0,0 +1,19 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <stddef.h>
> +#include <elfutils/libasm.h>
> +#include <gelf.h>
> +
> +typedef struct ebl Ebl;
> +extern Ebl *ebl_openbackend (Elf *elf);
> +extern void ebl_closebackend (Ebl *ebl);
> +
> +int main(void)
> +{
> + Elf *elf = elf_begin(0, ELF_C_READ, NULL);
> + Ebl *ebl = ebl_openbackend(elf);
[Severity: Medium]
Is it safe to rely on ebl_openbackend() here? Modern versions of elfutils
(>= 0.177) do not export ebl_openbackend dynamically. Could this cause
unintended linker errors and spuriously fail the feature test?
> + DisasmCtx_t *ctx = disasm_begin(ebl, elf, NULL);
[Severity: Medium]
Does passing NULL as the third parameter here trigger a -Wint-conversion
compiler warning? Since the feature test is compiled with -Werror, expecting
an integer or boolean but receiving a pointer will cause a build failure
and silently disable the libasm feature.
> + disasm_end(ctx);
> + ebl_closebackend(ebl);
> + elf_end(elf);
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609051759.405027-1-irogers@google.com?part=1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v1 2/7] perf build: Add build support and capability for elfutils libasm
2026-06-09 5:17 [PATCH v1 0/7] perf annotate: Add elfutils libasm disassembler backend Ian Rogers
2026-06-09 5:17 ` [PATCH v1 1/7] tools build: Add feature check for elfutils libasm Ian Rogers
@ 2026-06-09 5:17 ` Ian Rogers
2026-06-09 5:28 ` sashiko-bot
2026-06-09 5:17 ` [PATCH v1 3/7] perf annotate: Implement elfutils libasm disassembler backend Ian Rogers
` (4 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Ian Rogers @ 2026-06-09 5:17 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Nick Terrell, David Sterba, Nathan Chancellor, Tomas Glozar,
Blake Jones, Dmitrii Dolgov, Alexandre Chartre, Costa Shulyupin,
Yuzhuo Jing, Michael Jeanson, Leo Yan, Tianyou Li, Zecheng Li,
Rong Bao, linux-kernel, linux-perf-users, bpf
Configure perf build system to utilize the libasm feature check
(supporting both static and dynamic builds) and export the config flags.
Add "libasm" capability detection to perf check.
Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Makefile.config | 23 +++++++++++++++++++++++
tools/perf/builtin-check.c | 1 +
2 files changed, 24 insertions(+)
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 6e7b15fab2ec..8b6c32392d61 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -199,6 +199,13 @@ endif
FEATURE_CHECK_CFLAGS-libcapstone := $(LIBCAPSTONE_CFLAGS)
FEATURE_CHECK_LDFLAGS-libcapstone := $(LIBCAPSTONE_LDFLAGS) -lcapstone
+ifdef LIBASM_DIR
+ LIBASM_CFLAGS := -I$(LIBASM_DIR)/include
+ LIBASM_LDFLAGS := -L$(LIBASM_DIR)/lib
+endif
+FEATURE_CHECK_CFLAGS-libasm := $(LIBASM_CFLAGS)
+FEATURE_CHECK_LDFLAGS-libasm := $(LIBASM_LDFLAGS) -lasm -ldw -lelf
+
ifdef LIBZSTD_DIR
LIBZSTD_CFLAGS := -I$(LIBZSTD_DIR)/lib
LIBZSTD_LDFLAGS := -L$(LIBZSTD_DIR)/lib
@@ -1060,6 +1067,22 @@ ifndef NO_CAPSTONE
endif
endif
+ifndef NO_LIBASM
+ $(call feature_check,libasm)
+ ifeq ($(feature-libasm), 1)
+ CFLAGS += -DHAVE_LIBASM_SUPPORT $(LIBASM_CFLAGS)
+ ifdef LIBASM_DLOPEN
+ CFLAGS += -DLIBASM_DLOPEN
+ else
+ LDFLAGS += $(LIBASM_LDFLAGS)
+ EXTLIBS += -lasm -ldw -lelf
+ endif
+ $(call detected,CONFIG_LIBASM)
+ else
+ msg := $(warning No libasm found, disables disasm engine support for 'perf script', please install elfutils-devel/elfutils-libelf-devel);
+ endif
+endif
+
ifdef EXTRA_TESTS
$(call detected,CONFIG_EXTRA_TESTS)
CFLAGS += -DHAVE_EXTRA_TESTS
diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c
index 60437650c50f..d2ffea4d9b8b 100644
--- a/tools/perf/builtin-check.c
+++ b/tools/perf/builtin-check.c
@@ -46,6 +46,7 @@ struct feature_status supported_features[] = {
FEATURE_STATUS("babeltrace2-ctf-writer", HAVE_BABELTRACE2_CTF_WRITER_SUPPORT),
FEATURE_STATUS("libbpf-strings", HAVE_LIBBPF_STRINGS_SUPPORT),
FEATURE_STATUS("libcapstone", HAVE_LIBCAPSTONE_SUPPORT),
+ FEATURE_STATUS("libasm", HAVE_LIBASM_SUPPORT),
FEATURE_STATUS("libdw-dwarf-unwind", HAVE_LIBDW_SUPPORT),
FEATURE_STATUS("libelf", HAVE_LIBELF_SUPPORT),
FEATURE_STATUS("libLLVM", HAVE_LIBLLVM_SUPPORT),
--
2.54.0.1064.gd145956f57-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v1 2/7] perf build: Add build support and capability for elfutils libasm
2026-06-09 5:17 ` [PATCH v1 2/7] perf build: Add build support and capability " Ian Rogers
@ 2026-06-09 5:28 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-06-09 5:28 UTC (permalink / raw)
To: Ian Rogers; +Cc: bpf, linux-perf-users
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The `libasm` feature detection bypasses `NO_LIBELF=1` and `NO_LIBDW=1` build flags, forcing unwanted linkage of `libelf` and `libdw`.
- [Low] The libasm feature check unconditionally prints a confusing and false build warning when libasm is absent, resulting from a copy-paste error from the libcapstone check.
--
commit f7a26566fa9af4fab8dbd2f6062cf9924751f23c
Author: Ian Rogers <irogers@google.com>
perf build: Add build support and capability for elfutils libasm
Configure perf build system to utilize the libasm feature check
(supporting both static and dynamic builds) and export the config flags.
Add "libasm" capability detection to perf check.
> diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
> index 6e7b15fab2ecc..8b6c32392d618 100644
> --- a/tools/perf/Makefile.config
> +++ b/tools/perf/Makefile.config
[ ... ]
> @@ -1060,6 +1067,22 @@ ifndef NO_CAPSTONE
> endif
> endif
>
> +ifndef NO_LIBASM
> + $(call feature_check,libasm)
> + ifeq ($(feature-libasm), 1)
> + CFLAGS += -DHAVE_LIBASM_SUPPORT $(LIBASM_CFLAGS)
> + ifdef LIBASM_DLOPEN
> + CFLAGS += -DLIBASM_DLOPEN
> + else
> + LDFLAGS += $(LIBASM_LDFLAGS)
> + EXTLIBS += -lasm -ldw -lelf
[Severity: Medium]
Will this force linkage of libelf and libdw even when the user builds with
NO_LIBELF=1 or NO_LIBDW=1?
It appears NO_LIBASM isn't automatically set when NO_LIBELF=1 is used, unlike
other ELF-dependent libraries:
tools/perf/Makefile.config:
ifdef NO_LIBELF
NO_LIBDW := 1
NO_LIBUNWIND := 1
NO_LIBBPF := 1
NO_JVMTI := 1
else
Because of this, the libasm feature check will still run and unconditionally
append -lelf and -ldw to EXTLIBS, bypassing the user's explicit build
constraints.
> + endif
> + $(call detected,CONFIG_LIBASM)
> + else
> + msg := $(warning No libasm found, disables disasm engine support for 'perf script', please install elfutils-devel/elfutils-libelf-devel);
[Severity: Low]
Is this warning statement evaluating and printing during Makefile parsing
whenever libasm is absent?
In GNU Make, the $(warning) function is evaluated immediately as it is parsed.
This means users compiling perf without the optional libasm library might be
shown this warning even if they did not explicitly request libasm support.
Additionally, is the warning text accurate? It seems this might be a copy-paste
from the libcapstone check, as it claims missing libasm disables the
'perf script' disasm engine, but libasm is used for 'perf annotate' rather than
'perf script'.
> + endif
> +endif
> +
> ifdef EXTRA_TESTS
> $(call detected,CONFIG_EXTRA_TESTS)
> CFLAGS += -DHAVE_EXTRA_TESTS
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609051759.405027-1-irogers@google.com?part=2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v1 3/7] perf annotate: Implement elfutils libasm disassembler backend
2026-06-09 5:17 [PATCH v1 0/7] perf annotate: Add elfutils libasm disassembler backend Ian Rogers
2026-06-09 5:17 ` [PATCH v1 1/7] tools build: Add feature check for elfutils libasm Ian Rogers
2026-06-09 5:17 ` [PATCH v1 2/7] perf build: Add build support and capability " Ian Rogers
@ 2026-06-09 5:17 ` Ian Rogers
2026-06-09 5:30 ` sashiko-bot
2026-06-09 5:17 ` [PATCH v1 4/7] perf annotate: Add --disassembler command-line option Ian Rogers
` (3 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Ian Rogers @ 2026-06-09 5:17 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Nick Terrell, David Sterba, Nathan Chancellor, Tomas Glozar,
Blake Jones, Dmitrii Dolgov, Alexandre Chartre, Costa Shulyupin,
Yuzhuo Jing, Michael Jeanson, Leo Yan, Tianyou Li, Zecheng Li,
Rong Bao, linux-kernel, linux-perf-users, bpf
Implement the core disassembler backend in libasm.c utilizing elfutils'
libasm API. Hook the backend into perf annotate disassembler routing.
Configure libasm as the first-choice disassembler backend by default
(ahead of LLVM/capstone) when available.
Also, expose annotation_options__add_disassemblers_str to allow
custom disassembler selection.
Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/Build | 1 +
tools/perf/util/annotate.c | 8 +-
tools/perf/util/annotate.h | 3 +
tools/perf/util/disasm.c | 5 +
tools/perf/util/libasm.c | 182 +++++++++++++++++++++++++++++++++++++
tools/perf/util/libasm.h | 27 ++++++
6 files changed, 224 insertions(+), 2 deletions(-)
create mode 100644 tools/perf/util/libasm.c
create mode 100644 tools/perf/util/libasm.h
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index b22cdc24082a..f1d5ebb2edfa 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -12,6 +12,7 @@ perf-util-y += block-range.o
perf-util-y += build-id.o
perf-util-y += cacheline.o
perf-util-$(CONFIG_LIBCAPSTONE) += capstone.o
+perf-util-$(CONFIG_LIBASM) += libasm.o
perf-util-y += config.o
perf-util-y += copyfile.o
perf-util-y += ctype.o
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 02505222d8c2..2259ac2c2cd6 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -2233,6 +2233,7 @@ const char * const perf_disassembler__strs[] = {
[PERF_DISASM_UNKNOWN] = "unknown",
[PERF_DISASM_LLVM] = "llvm",
[PERF_DISASM_CAPSTONE] = "capstone",
+ [PERF_DISASM_LIBASM] = "libasm",
[PERF_DISASM_OBJDUMP] = "objdump",
};
@@ -2254,8 +2255,8 @@ static void annotation_options__add_disassembler(struct annotation_options *opti
pr_err("Failed to add disassembler %d\n", dis);
}
-static int annotation_options__add_disassemblers_str(struct annotation_options *options,
- const char *str)
+int annotation_options__add_disassemblers_str(struct annotation_options *options,
+ const char *str)
{
while (str && *str != '\0') {
const char *comma = strchr(str, ',');
@@ -2372,6 +2373,9 @@ static void annotation_options__default_init_disassemblers(struct annotation_opt
/* Already initialized. */
return;
}
+#ifdef HAVE_LIBASM_SUPPORT
+ annotation_options__add_disassembler(options, PERF_DISASM_LIBASM);
+#endif
#ifdef HAVE_LIBLLVM_SUPPORT
annotation_options__add_disassembler(options, PERF_DISASM_LLVM);
#endif
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 1aa6df7d1618..21ac2b6472c1 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -38,6 +38,7 @@ enum perf_disassembler {
PERF_DISASM_UNKNOWN = 0,
PERF_DISASM_LLVM,
PERF_DISASM_CAPSTONE,
+ PERF_DISASM_LIBASM,
PERF_DISASM_OBJDUMP,
};
#define MAX_DISASSEMBLERS (PERF_DISASM_OBJDUMP + 1)
@@ -484,6 +485,8 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel);
void annotation_options__init(void);
void annotation_options__exit(void);
+int annotation_options__add_disassemblers_str(struct annotation_options *options,
+ const char *str);
void annotation_config__init(void);
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 59ba88e1f744..42af3603fdff 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -22,6 +22,7 @@
#include "capstone.h"
#include "debug.h"
#include "disasm.h"
+#include "libasm.h"
#include "dso.h"
#include "dwarf-regs.h"
#include "env.h"
@@ -1622,6 +1623,10 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
args->options->disassembler_used = PERF_DISASM_CAPSTONE;
err = symbol__disassemble_capstone(symfs_filename, sym, args);
break;
+ case PERF_DISASM_LIBASM:
+ args->options->disassembler_used = PERF_DISASM_LIBASM;
+ err = symbol__disassemble_libasm(symfs_filename, sym, args);
+ break;
case PERF_DISASM_OBJDUMP:
args->options->disassembler_used = PERF_DISASM_OBJDUMP;
err = symbol__disassemble_objdump(symfs_filename, sym, args);
diff --git a/tools/perf/util/libasm.c b/tools/perf/util/libasm.c
new file mode 100644
index 000000000000..6daac121b8f8
--- /dev/null
+++ b/tools/perf/util/libasm.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libasm.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <elfutils/libasm.h>
+#include <gelf.h>
+
+#include "annotate.h"
+#include "debug.h"
+#include "disasm.h"
+#include "dso.h"
+#include "map.h"
+#include "namespaces.h"
+#include "symbol.h"
+
+typedef struct ebl Ebl;
+extern Ebl *ebl_openbackend(Elf *elf);
+extern void ebl_closebackend(Ebl *ebl);
+
+struct disasm_output_arg {
+ char *buf;
+ size_t size;
+};
+
+static int disasm_output_cb(char *str, size_t len, void *arg)
+{
+ struct disasm_output_arg *oa = arg;
+ size_t to_copy = len < oa->size - 1 ? len : oa->size - 1;
+
+ memcpy(oa->buf, str, to_copy);
+ oa->buf[to_copy] = '\0';
+ return 1;
+}
+
+int symbol__disassemble_libasm(const char *filename, struct symbol *sym,
+ struct annotate_args *args)
+{
+ struct annotation *notes = symbol__annotation(sym);
+ struct map *map = args->ms->map;
+ struct dso *dso = map__dso(map);
+ u64 start = map__rip_2objdump(map, sym->start);
+ u64 offset;
+ bool is_64bit = false;
+ u8 *code_buf = NULL;
+ const u8 *buf;
+ u64 buf_len;
+ Elf *elf = NULL;
+ Ebl *ebl = NULL;
+ DisasmCtx_t *handle = NULL;
+ char disasm_buf[512];
+ struct disasm_line *dl;
+ struct nscookie nsc;
+ const uint8_t *pc;
+ const uint8_t *end;
+ u64 addr;
+ size_t insn_len;
+ int ret;
+ int fd = -1;
+ int count = 0;
+
+ if (args->options->objdump_path)
+ return -1;
+
+ buf = dso__read_symbol(dso, filename, map, sym,
+ &code_buf, &buf_len, &is_64bit);
+ if (buf == NULL)
+ return errno;
+
+ /* add the function address and name */
+ scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
+ start, sym->name);
+
+ args->offset = -1;
+ args->line = disasm_buf;
+ args->line_nr = 0;
+ args->fileloc = NULL;
+ args->ms->sym = sym;
+
+ dl = disasm_line__new(args);
+ if (dl == NULL)
+ goto err;
+
+ annotation_line__add(&dl->al, ¬es->src->source);
+
+ nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
+ fd = open(filename, O_RDONLY);
+ nsinfo__mountns_exit(&nsc);
+ if (fd < 0)
+ goto err;
+
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+ if (!elf)
+ goto err;
+
+ ebl = ebl_openbackend(elf);
+ if (!ebl)
+ goto err;
+
+ handle = disasm_begin(ebl, elf, NULL);
+ if (!handle)
+ goto err;
+
+ pc = buf;
+ end = buf + buf_len;
+ addr = start;
+
+ offset = 0;
+ while (pc < end) {
+ struct disasm_output_arg oa = {
+ .buf = disasm_buf,
+ .size = sizeof(disasm_buf),
+ };
+ const uint8_t *prev_pc = pc;
+
+ ret = disasm_cb(handle, &pc, end, addr, "%7m %.1o,%.2o,%.3o,%.4o,%.5o",
+ disasm_output_cb, &oa, NULL);
+ if (ret != 1 || pc == prev_pc) {
+ /* Disassembly failed or got stuck */
+ break;
+ }
+
+ args->offset = offset;
+ args->line = disasm_buf;
+
+ dl = disasm_line__new(args);
+ if (dl == NULL)
+ goto err;
+
+ annotation_line__add(&dl->al, ¬es->src->source);
+
+ insn_len = pc - prev_pc;
+ offset += insn_len;
+ addr += insn_len;
+ count++;
+ }
+
+ if (offset != buf_len) {
+ struct list_head *list = ¬es->src->source;
+
+ /* Discard all lines and fallback to objdump */
+ while (!list_empty(list)) {
+ dl = list_first_entry(list, struct disasm_line, al.node);
+
+ list_del_init(&dl->al.node);
+ disasm_line__free(dl);
+ }
+ count = -1;
+ }
+
+out:
+ if (handle)
+ disasm_end(handle);
+ if (ebl)
+ ebl_closebackend(ebl);
+ if (elf)
+ elf_end(elf);
+ if (fd >= 0)
+ close(fd);
+ free(code_buf);
+ return count < 0 ? count : 0;
+
+err:
+ {
+ struct disasm_line *tmp;
+
+ /*
+ * It probably failed in the middle of the above loop.
+ * Release any resources it might add.
+ */
+ list_for_each_entry_safe(dl, tmp, ¬es->src->source, al.node) {
+ list_del(&dl->al.node);
+ disasm_line__free(dl);
+ }
+ }
+
+ count = -1;
+ goto out;
+}
diff --git a/tools/perf/util/libasm.h b/tools/perf/util/libasm.h
new file mode 100644
index 000000000000..d4324edf059e
--- /dev/null
+++ b/tools/perf/util/libasm.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_LIBASM_H
+#define __PERF_LIBASM_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+struct annotate_args;
+struct symbol;
+
+#ifdef HAVE_LIBASM_SUPPORT
+int symbol__disassemble_libasm(const char *filename, struct symbol *sym,
+ struct annotate_args *args);
+#else /* !HAVE_LIBASM_SUPPORT */
+static inline int symbol__disassemble_libasm(const char *filename __maybe_unused,
+ struct symbol *sym __maybe_unused,
+ struct annotate_args *args __maybe_unused)
+{
+ return -1;
+}
+#endif /* HAVE_LIBASM_SUPPORT */
+
+#endif /* __PERF_LIBASM_H */
--
2.54.0.1064.gd145956f57-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v1 3/7] perf annotate: Implement elfutils libasm disassembler backend
2026-06-09 5:17 ` [PATCH v1 3/7] perf annotate: Implement elfutils libasm disassembler backend Ian Rogers
@ 2026-06-09 5:30 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-06-09 5:30 UTC (permalink / raw)
To: Ian Rogers; +Cc: bpf, linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Incorrect usage of the elfutils libasm API causes severely truncated disassembly output and incorrect error handling.
--
commit a3f0b9f1213f96fa27632210d1db364c7cad8286
Author: Ian Rogers <irogers@google.com>
perf annotate: Implement elfutils libasm disassembler backend
This commit implements the core disassembler backend in libasm.c utilizing
elfutils' libasm API. It hooks the backend into perf annotate disassembler
routing and configures libasm as the first-choice disassembler backend.
> diff --git a/tools/perf/util/libasm.c b/tools/perf/util/libasm.c
> new file mode 100644
> index 0000000000000..6daac121b8f81
> --- /dev/null
> +++ b/tools/perf/util/libasm.c
[ ... ]
> +struct disasm_output_arg {
> + char *buf;
> + size_t size;
> +};
> +
> +static int disasm_output_cb(char *str, size_t len, void *arg)
> +{
> + struct disasm_output_arg *oa = arg;
> + size_t to_copy = len < oa->size - 1 ? len : oa->size - 1;
> +
> + memcpy(oa->buf, str, to_copy);
> + oa->buf[to_copy] = '\0';
> + return 1;
[Severity: High]
Does returning 1 here cause the elfutils libasm API to abort parsing the
instruction? If elfutils expects 0 for success, this non-zero return
value might indicate an error and cause disassembly to stop prematurely.
Also, does this correctly append successive chunks to the buffer, or does it
overwrite the beginning each time it is called? It appears that oa->buf and
oa->size are not advanced by to_copy, which might cause successive strings
(like mnemonics and operands) to repeatedly overwrite the buffer at index 0.
> +}
[ ... ]
> + offset = 0;
> + while (pc < end) {
> + struct disasm_output_arg oa = {
> + .buf = disasm_buf,
> + .size = sizeof(disasm_buf),
> + };
> + const uint8_t *prev_pc = pc;
> +
> + ret = disasm_cb(handle, &pc, end, addr, "%7m %.1o,%.2o,%.3o,%.4o,%.5o",
> + disasm_output_cb, &oa, NULL);
> + if (ret != 1 || pc == prev_pc) {
[Severity: High]
If disasm_cb propagates the 1 returned by the callback, does checking for
ret != 1 instead of ret != 0 cause us to treat an aborted disassembly
as a success? This might mask the truncation error occurring in the
callback.
> + /* Disassembly failed or got stuck */
> + break;
> + }
> +
> + args->offset = offset;
> + args->line = disasm_buf;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609051759.405027-1-irogers@google.com?part=3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v1 4/7] perf annotate: Add --disassembler command-line option
2026-06-09 5:17 [PATCH v1 0/7] perf annotate: Add elfutils libasm disassembler backend Ian Rogers
` (2 preceding siblings ...)
2026-06-09 5:17 ` [PATCH v1 3/7] perf annotate: Implement elfutils libasm disassembler backend Ian Rogers
@ 2026-06-09 5:17 ` Ian Rogers
2026-06-09 5:17 ` [PATCH v1 5/7] perf test: Enhance annotate test coverage and isolate config Ian Rogers
` (2 subsequent siblings)
6 siblings, 0 replies; 14+ messages in thread
From: Ian Rogers @ 2026-06-09 5:17 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Nick Terrell, David Sterba, Nathan Chancellor, Tomas Glozar,
Blake Jones, Dmitrii Dolgov, Alexandre Chartre, Costa Shulyupin,
Yuzhuo Jing, Michael Jeanson, Leo Yan, Tianyou Li, Zecheng Li,
Rong Bao, linux-kernel, linux-perf-users, bpf
Add the --disassembler command-line option to perf annotate. This
allows users to explicitly request or override the disassembler
preferences (e.g. --disassembler libasm) directly on the command
line.
Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-annotate.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 8a0eb30eac24..57a88adfb130 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -699,6 +699,7 @@ int cmd_annotate(int argc, const char **argv)
.set = 0,
};
const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL;
+ const char *disassemblers_str = NULL;
struct option options[] = {
OPT_STRING('i', "input", &input_name, "file",
"input file name"),
@@ -782,6 +783,8 @@ int cmd_annotate(int argc, const char **argv)
"Do not display empty (or dummy) events in the output"),
OPT_BOOLEAN(0, "code-with-type", &annotate_opts.code_with_type,
"Show data type info in code annotation (memory instructions only)"),
+ OPT_STRING(0, "disassembler", &disassemblers_str, "names",
+ "comma separated list of disassemblers to use"),
OPT_END()
};
int ret;
@@ -825,6 +828,13 @@ int cmd_annotate(int argc, const char **argv)
return -ENOMEM;
}
+ if (disassemblers_str) {
+ memset(annotate_opts.disassemblers, 0, sizeof(annotate_opts.disassemblers));
+ ret = annotation_options__add_disassemblers_str(&annotate_opts, disassemblers_str);
+ if (ret)
+ return -EINVAL;
+ }
+
if (annotate_check_args() < 0)
return -EINVAL;
--
2.54.0.1064.gd145956f57-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v1 5/7] perf test: Enhance annotate test coverage and isolate config
2026-06-09 5:17 [PATCH v1 0/7] perf annotate: Add elfutils libasm disassembler backend Ian Rogers
` (3 preceding siblings ...)
2026-06-09 5:17 ` [PATCH v1 4/7] perf annotate: Add --disassembler command-line option Ian Rogers
@ 2026-06-09 5:17 ` Ian Rogers
2026-06-09 5:28 ` sashiko-bot
2026-06-09 5:17 ` [PATCH v1 6/7] perf annotate: Support BPF JIT disassembly via genelf Ian Rogers
2026-06-09 5:17 ` [PATCH v1 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers Ian Rogers
6 siblings, 1 reply; 14+ messages in thread
From: Ian Rogers @ 2026-06-09 5:17 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Nick Terrell, David Sterba, Nathan Chancellor, Tomas Glozar,
Blake Jones, Dmitrii Dolgov, Alexandre Chartre, Costa Shulyupin,
Yuzhuo Jing, Michael Jeanson, Leo Yan, Tianyou Li, Zecheng Li,
Rong Bao, linux-kernel, linux-perf-users, bpf
Update the annotate.sh shell test to test different disassembler
backends (objdump, llvm, capstone, libasm) utilizing the new
--disassembler command-line option.
Isolate the test script from host environment pollution by exporting
PERF_CONFIG=/dev/null at the start of the script, ensuring it runs
hermetically.
Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/shell/annotate.sh | 56 ++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh
index 689de58e9238..d3bafa7e3db5 100755
--- a/tools/perf/tests/shell/annotate.sh
+++ b/tools/perf/tests/shell/annotate.sh
@@ -4,6 +4,8 @@
set -e
+export PERF_CONFIG=/dev/null
+
shelldir=$(dirname "$0")
# shellcheck source=lib/perf_has_symbol.sh
@@ -23,6 +25,7 @@ disasm_regex="[0-9]*\.[0-9]* *: *\w*: *\w*"
cleanup() {
rm -rf "${perfdata}" "${perfout}"
rm -rf "${perfdata}".old
+ rm -f /tmp/__perf_test.config.*
trap - EXIT TERM INT
}
@@ -106,8 +109,61 @@ test_basic() {
echo "${mode} annotate test [Success]"
}
+test_disassembler() {
+ disassembler=$1
+ feature=$2
+
+ if [ -n "${feature}" ]
+ then
+ if ! perf check feature "${feature}" > /dev/null 2>&1
+ then
+ echo "Skip test for ${disassembler} (feature ${feature} not supported)"
+ return 0
+ fi
+ fi
+
+ echo "Test annotate with disassembler: ${disassembler}"
+
+ perf annotate --no-demangle -i "${perfdata}" --stdio --percent-limit 10 --disassembler "${disassembler}" 2> /dev/null > "${perfout}"
+ ret=$?
+
+ if [ "x${ret}" != "x0" ]
+ then
+ echo "annotate with ${disassembler} [Failed: perf annotate error]"
+ err=1
+ return 1
+ fi
+
+ # check if it has the target symbol
+ if ! grep -q "${testsym}" "${perfout}"
+ then
+ echo "annotate with ${disassembler} [Failed: missing target symbol]"
+ err=1
+ return 1
+ fi
+
+ # check if it has the disassembly lines
+ if ! grep -q "${disasm_regex}" "${perfout}"
+ then
+ echo "annotate with ${disassembler} [Failed: missing disasm output]"
+ err=1
+ return 1
+ fi
+
+ echo "annotate with ${disassembler} [Success]"
+ return 0
+}
+
test_basic Basic
test_basic Pipe
+if [ "${err}" -eq 0 ]
+then
+ test_disassembler "objdump" ""
+ test_disassembler "llvm" "libLLVM"
+ test_disassembler "capstone" "libcapstone"
+ test_disassembler "libasm" "libasm"
+fi
+
cleanup
exit $err
--
2.54.0.1064.gd145956f57-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v1 5/7] perf test: Enhance annotate test coverage and isolate config
2026-06-09 5:17 ` [PATCH v1 5/7] perf test: Enhance annotate test coverage and isolate config Ian Rogers
@ 2026-06-09 5:28 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-06-09 5:28 UTC (permalink / raw)
To: Ian Rogers; +Cc: bpf, linux-perf-users
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Premature script abort and unreachable error handling logic due to the global `set -e` shell option in `test_disassembler`.
- [Low] The cleanup function unnecessarily attempts to delete temporary configuration files that the script never creates.
--
commit 4dda72bcf9505cc21e386908e19f007d3ab5a6ec
Author: Ian Rogers <irogers@google.com>
perf test: Enhance annotate test coverage and isolate config
This commit updates the annotate.sh shell test to cover different disassembler
backends utilizing the new command-line option. It also attempts to isolate
the test script from host environment pollution by exporting
PERF_CONFIG=/dev/null at the start of the script.
> diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh
> index 689de58e92384..d3bafa7e3db55 100755
> --- a/tools/perf/tests/shell/annotate.sh
> +++ b/tools/perf/tests/shell/annotate.sh
[ ... ]
> @@ -23,6 +25,7 @@ disasm_regex="[0-9]*\.[0-9]* *: *\w*: *\w*"
> cleanup() {
> rm -rf "${perfdata}" "${perfout}"
> rm -rf "${perfdata}".old
> + rm -f /tmp/__perf_test.config.*
>
> trap - EXIT TERM INT
> }
[Severity: Low]
Is this cleanup necessary?
Since PERF_CONFIG is exported to /dev/null at the start of the script, it
doesn't appear that any temporary configuration files matching this pattern
are ever created by this test.
[ ... ]
> @@ -106,8 +109,61 @@ test_basic() {
> +test_disassembler() {
> + disassembler=$1
> + feature=$2
[ ... ]
> + perf annotate --no-demangle -i "${perfdata}" --stdio --percent-limit 10 --disassembler "${disassembler}" 2> /dev/null > "${perfout}"
> + ret=$?
> +
> + if [ "x${ret}" != "x0" ]
> + then
> + echo "annotate with ${disassembler} [Failed: perf annotate error]"
> + err=1
> + return 1
> + fi
[Severity: Medium]
Because this script operates with set -e globally, will a failure of perf
annotate immediately abort the script?
If the command fails, it seems the shell would terminate before reaching the
ret=$? assignment, making the explicit error handling and logging dead code.
> +
> + # check if it has the target symbol
> + if ! grep -q "${testsym}" "${perfout}"
> + then
> + echo "annotate with ${disassembler} [Failed: missing target symbol]"
> + err=1
> + return 1
> + fi
[Severity: Medium]
Similarly, since the test_disassembler function is called sequentially
without protection, will returning 1 here trigger a set -e abort at the call
site?
This might abruptly halt the entire test suite and prevent the remaining
disassembler backends from running, rather than continuing to the next test
as the err=1 assignment suggests.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609051759.405027-1-irogers@google.com?part=5
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v1 6/7] perf annotate: Support BPF JIT disassembly via genelf
2026-06-09 5:17 [PATCH v1 0/7] perf annotate: Add elfutils libasm disassembler backend Ian Rogers
` (4 preceding siblings ...)
2026-06-09 5:17 ` [PATCH v1 5/7] perf test: Enhance annotate test coverage and isolate config Ian Rogers
@ 2026-06-09 5:17 ` Ian Rogers
2026-06-09 5:33 ` sashiko-bot
2026-06-09 5:17 ` [PATCH v1 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers Ian Rogers
6 siblings, 1 reply; 14+ messages in thread
From: Ian Rogers @ 2026-06-09 5:17 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Nick Terrell, David Sterba, Nathan Chancellor, Tomas Glozar,
Blake Jones, Dmitrii Dolgov, Alexandre Chartre, Costa Shulyupin,
Yuzhuo Jing, Michael Jeanson, Leo Yan, Tianyou Li, Zecheng Li,
Rong Bao, linux-kernel, linux-perf-users, bpf
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@google.com>
---
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
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v1 6/7] perf annotate: Support BPF JIT disassembly via genelf
2026-06-09 5:17 ` [PATCH v1 6/7] perf annotate: Support BPF JIT disassembly via genelf Ian Rogers
@ 2026-06-09 5:33 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-06-09 5:33 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users, bpf
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The temporary ELF file for BPF JIT disassembly hardcodes the host architecture, breaking cross-platform analysis of perf.data files.
- [High] The temporary ELF file generated for BPF disassembly has a hardcoded Virtual Memory Address (VMA), causing objdump to output no instructions.
--
commit 13f48f3243b7525d8bdc81d7916d768a5bd50b37
Author: Ian Rogers <irogers@google.com>
perf annotate: Support BPF JIT disassembly via genelf
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.
> diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
> index 42af3603fdffd..8c78ef765787f 100644
> --- a/tools/perf/util/disasm.c
> +++ b/tools/perf/util/disasm.c
[ ... ]
> @@ -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) {
^^^^^^^^^^^^^
[Severity: High]
Does this break cross-platform analysis of perf.data files?
When analyzing a perf.data file from a different architecture (e.g., recorded
on ARM64 but analyzed on an x86_64 host), jit_write_elf() generates the
temporary ELF file using host compile-time macros (GEN_ELF_ARCH).
Because the ELF header incorrectly claims the host's architecture instead of
the target's, disassemblers like objdump will decode the BPF JIT instructions
using the host's instruction set, producing garbage output.
[Severity: High]
Will objdump fail to output any instructions due to a mismatch in addresses?
Here, the actual symbol start address is passed to jit_write_elf() as
load_addr. However, jit_write_elf() ignores load_addr and hardcodes the
ELF .text section VMA and symbol address to GEN_ELF_TEXT_OFFSET (typically
0x40).
Later, symbol__disassemble_objdump() invokes objdump with --start-address
restricted to the actual symbol start address:
err = asprintf(&command,
"%s %s%s --start-address=0x%016" PRIx64
" --stop-address=0x%016" PRIx64 ...
map__rip_2objdump(map, sym->start),
map__rip_2objdump(map, sym->end), ...)
Because the ELF file's contents reside at 0x40, objdump finds no instructions
within the requested start and stop address bounds, and fails the disassembly
process.
> + err = 0;
> + }
> + close(tmp_fd);
> + if (err)
> + unlink(tmp_elf);
> + }
> + free(code_buf);
> + return err;
> +}
> +#endif
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609051759.405027-1-irogers@google.com?part=6
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v1 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers
2026-06-09 5:17 [PATCH v1 0/7] perf annotate: Add elfutils libasm disassembler backend Ian Rogers
` (5 preceding siblings ...)
2026-06-09 5:17 ` [PATCH v1 6/7] perf annotate: Support BPF JIT disassembly via genelf Ian Rogers
@ 2026-06-09 5:17 ` Ian Rogers
2026-06-09 5:36 ` sashiko-bot
6 siblings, 1 reply; 14+ messages in thread
From: Ian Rogers @ 2026-06-09 5:17 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
Nick Terrell, David Sterba, Nathan Chancellor, Tomas Glozar,
Blake Jones, Dmitrii Dolgov, Alexandre Chartre, Costa Shulyupin,
Yuzhuo Jing, Michael Jeanson, Leo Yan, Tianyou Li, Zecheng Li,
Rong Bao, linux-kernel, linux-perf-users, bpf
Expand the annotate.sh shell test to verify BPF JIT disassembly.
If the test is run with sufficient privileges (root/CAP_BPF) and
captures system BPF programs, it will extract a JITted BPF program
symbol from the perf report and run perf annotate on it.
This validates the temporary ELF generation and disassembles it using
each of the supported disassembler backends (objdump, llvm, capstone,
and libasm).
Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/shell/annotate.sh | 69 ++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh
index d3bafa7e3db5..d8c710470c52 100755
--- a/tools/perf/tests/shell/annotate.sh
+++ b/tools/perf/tests/shell/annotate.sh
@@ -165,5 +165,74 @@ then
test_disassembler "libasm" "libasm"
fi
+test_bpf_disassembler() {
+ disassembler=$1
+ feature=$2
+
+ if [ -n "${feature}" ]
+ then
+ if ! perf check feature "${feature}" > /dev/null 2>&1
+ then
+ echo "Skip BPF JIT test for ${disassembler} (feature ${feature} not supported)"
+ return 0
+ fi
+ fi
+
+ echo "Test BPF JIT annotate with disassembler: ${disassembler}"
+
+ if ! perf annotate --no-demangle -i "${perfdata}" --stdio "${bpf_sym}" \
+ --disassembler "${disassembler}" 2> /dev/null > "${perfout}"
+ then
+ echo "BPF JIT annotate with ${disassembler} [Failed: perf annotate error]"
+ err=1
+ return 1
+ fi
+
+ if ! grep -q "${disasm_regex}" "${perfout}"
+ then
+ echo "BPF JIT annotate with ${disassembler} [Failed: missing disasm output]"
+ err=1
+ return 1
+ fi
+
+ echo "BPF JIT annotate with ${disassembler} [Success]"
+ return 0
+}
+
+test_bpf() {
+ echo "Test annotate with BPF JIT output"
+
+ if ! perf check -q feature libbpf-strings ; then
+ echo "BPF annotation test [Skipped - libbpf-strings not supported]"
+ return 0
+ fi
+
+ rm -f "${perfdata}"
+
+ if ! perf record -a -e cycles -F 4000 -o "${perfdata}" -- sleep 1 2> /dev/null
+ then
+ echo "BPF annotation test [Skipped - perf record -a failed, probably no privileges]"
+ return 0
+ fi
+
+ bpf_sym=$(perf report --stdio -i "${perfdata}" 2>/dev/null | \
+ grep -E -o 'bpf_prog_[0-9a-f]{16}_[0-9A-Za-z_]*' | head -1)
+
+ if [ -z "${bpf_sym}" ]; then
+ echo "BPF annotation test [Skipped - no JITted BPF symbols with samples found]"
+ return 0
+ fi
+
+ test_bpf_disassembler "objdump" ""
+ test_bpf_disassembler "llvm" "libLLVM"
+ test_bpf_disassembler "capstone" "libcapstone"
+ test_bpf_disassembler "libasm" "libasm"
+}
+
+if [ "${err}" -eq 0 ]
+then
+ test_bpf
+fi
+
cleanup
exit $err
--
2.54.0.1064.gd145956f57-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v1 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers
2026-06-09 5:17 ` [PATCH v1 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers Ian Rogers
@ 2026-06-09 5:36 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-06-09 5:36 UTC (permalink / raw)
To: Ian Rogers; +Cc: bpf, linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Removing a securely created temporary file before reusing its path creates a Time-Of-Check to Time-Of-Use (TOCTOU) race condition, allowing a local attacker to overwrite arbitrary system files or steal system-wide profiling data.
--
commit cc2ff8c9b984e996a4b88b05049833c644d20227
Author: Ian Rogers <irogers@google.com>
perf test: Add BPF JIT annotation test coverage for all disassemblers
This commit expands the annotate.sh shell test to verify BPF JIT disassembly.
It captures system BPF programs and validates the temporary ELF generation
using various supported disassembler backends.
> diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh
> index d3bafa7e3db55..d8c710470c528 100755
> --- a/tools/perf/tests/shell/annotate.sh
> +++ b/tools/perf/tests/shell/annotate.sh
[ ... ]
> +test_bpf() {
> + echo "Test annotate with BPF JIT output"
> +
> + if ! perf check -q feature libbpf-strings ; then
> + echo "BPF annotation test [Skipped - libbpf-strings not supported]"
> + return 0
> + fi
> +
> + rm -f "${perfdata}"
> +
> + if ! perf record -a -e cycles -F 4000 -o "${perfdata}" -- sleep 1 2> /dev/null
[Severity: High]
Does removing this securely created temporary file before reusing its path
create a Time-Of-Check to Time-Of-Use (TOCTOU) race condition?
By deleting the file explicitly and then allowing perf record to create it
again, it appears to open a window where a local attacker could place a
symlink at this path. Since perf record does not use O_EXCL when opening
output files, couldn't this result in overwriting arbitrary system files or
leaking system-wide profiling data if the test is run with privileges?
> + then
> + echo "BPF annotation test [Skipped - perf record -a failed, probably no privileges]"
> + return 0
> + fi
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260609051759.405027-1-irogers@google.com?part=7
^ permalink raw reply [flat|nested] 14+ messages in thread