[PATCH v5 01/26] perf capstone: Symbolize address operands to match objdump on arm64
From: Tengda Wu <hidden>
Date: 2026-09-08 13:05:46
Also in:
lkml, llvm
Subsystem:
performance events subsystem, the rest · Maintainers:
Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Linus Torvalds
Capstone currently outputs address-bearing instructions with a raw hex value prefixed by '#', without symbolic resolution. This differs from objdump output and leads to parse failures in jump__parse() and arm64_mov__parse() on arm64. Example of the mismatch: Current: b #0xffff8000800114c8 Objdump: b ffff8000800114c8 <el0t_64_sync+0x108> Current: adrp x18, #0xffff800081f5f000 Objdump: adrp x18, ffff800081f5f000 <this_cpu_vector> Fix this by extending symbol__disassemble_capstone() to: - Enable CS_OPT_DETAIL for arm64 to access instruction group info. - Detect instructions with address operands requiring symbolization (jump/call/branch/adr/adrp) via opcode or Capstone group ID. - Resolve target addresses to symbols via resolve_symbol_from_addr(). - Format output to match objdump's style (strip '#' prefix, add symbol and offset). Signed-off-by: Tengda Wu <redacted> --- tools/perf/util/capstone.c | 185 +++++++++++++++++++++++++++++-------- tools/perf/util/disasm.c | 5 + tools/perf/util/disasm.h | 1 + 3 files changed, 153 insertions(+), 38 deletions(-)
diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
index 74213daf8786..7f82c33e3464 100644
--- a/tools/perf/util/capstone.c
+++ b/tools/perf/util/capstone.c@@ -3,6 +3,7 @@ #include <errno.h> #include <inttypes.h> +#include <stdlib.h> #include <string.h> #include <dlfcn.h>
@@ -31,6 +32,10 @@ #define CS_MODE_RISCVC 4 #endif +#if CS_VERSION_MAJOR < 4 +#define ARM64_GRP_BRANCH_RELATIVE 7 /* = CS_GRP_BRANCH_RELATIVE */ +#endif + #ifdef LIBCAPSTONE_DLOPEN static void *perf_cs_dll_handle(void) {
@@ -225,6 +230,12 @@ static int capstone_init(uint16_t e_machine, csh *cs_handle, bool is64, bool is_ * on x86 by investigating instruction details. */ perf_cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON); + } else if (arch == CS_ARCH_ARM64) { + /* + * Same as x86: arm64 needs instruction details to resolve + * symbolic addresses. + */ + perf_cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON); } return 0;
@@ -292,16 +303,44 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine, struct thread *threa return printed; } +static int resolve_symbol_from_addr(struct map *map, u64 addr, + const char **sym_name, u64 *sym_offset) +{ + struct map *found_map = NULL; + struct symbol *sym; + u64 al_addr; + u64 ip = map__objdump_2mem(map, addr); + + if (dso__kernel(map__dso(map))) { + /* + * The kernel maps can be split into sections, let's + * find the map first and then search the symbol. + */ + found_map = maps__find(map__kmaps(map), ip); + if (found_map == NULL) + return -1; + map = found_map; + } + + /* convert it to map-relative address for search */ + al_addr = map__map_ip(map, ip); + + sym = map__find_symbol(map, al_addr); + map__put(found_map); + + if (sym == NULL) + return -1; + + *sym_name = sym->name; + *sym_offset = al_addr - sym->start; + return 0; +} + static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len, struct annotate_args *args, u64 addr) { int i; struct map *map = args->ms->map; - struct symbol *sym; - - /* TODO: support more architectures */ - if (!arch__is_x86(args->arch)) - return; if (insn->detail == NULL) return;
@@ -309,7 +348,8 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len, for (i = 0; i < insn->detail->x86.op_count; i++) { struct cs_x86_op *op = &insn->detail->x86.operands[i]; u64 orig_addr; - struct map *found_map = NULL; + const char *sym_name = NULL; + u64 sym_offset; if (op->type != X86_OP_MEM) continue;
@@ -320,40 +360,114 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len, /* get the target address */ orig_addr = addr + insn->size + op->mem.disp; - addr = map__objdump_2mem(map, orig_addr); - - if (dso__kernel(map__dso(map))) { - /* - * The kernel maps can be split into sections, let's - * find the map first and then search the symbol. - */ - found_map = maps__find(map__kmaps(map), addr); - if (found_map == NULL) - continue; - map = found_map; - } - - /* convert it to map-relative address for search */ - addr = map__map_ip(map, addr); - - sym = map__find_symbol(map, addr); - if (sym == NULL) { - map__put(found_map); + if (resolve_symbol_from_addr(map, orig_addr, &sym_name, &sym_offset)) continue; - } - if (addr == sym->start) { + if (sym_offset == 0) { scnprintf(buf, len, "\t# %"PRIx64" <%s>", - orig_addr, sym->name); + orig_addr, sym_name); } else { scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">", - orig_addr, sym->name, addr - sym->start); + orig_addr, sym_name, sym_offset); } - map__put(found_map); break; } } +static int print_default_format(struct cs_insn *insn, char *buf, size_t len) +{ + return scnprintf(buf, len, " %-7s %s", + insn->mnemonic, insn->op_str); +} + +static void format_capstone_insn_x86(struct cs_insn *insn, char *buf, + size_t len, struct annotate_args *args, + u64 addr) +{ + int printed; + + printed = print_default_format(insn, buf, len); + buf += printed; + len -= printed; + + print_capstone_detail(insn, buf, len, args, addr); +} + +static bool needs_symbolic_address(struct cs_insn *insn) +{ + int i; + + if (insn->id == ARM64_INS_ADR || insn->id == ARM64_INS_ADRP) + return true; + + if (insn->detail == NULL) + return false; + + for (i = 0; i < insn->detail->groups_count; i++) { + if (insn->detail->groups[i] == ARM64_GRP_JUMP || + insn->detail->groups[i] == ARM64_GRP_CALL || + insn->detail->groups[i] == ARM64_GRP_BRANCH_RELATIVE) + return true; + } + + return false; +} + +static void format_capstone_insn_arm64(struct cs_insn *insn, char *buf, + size_t len, struct annotate_args *args) +{ + struct map *map = args->ms->map; + char *last_imm, *endptr; + u64 addr; + + print_default_format(insn, buf, len); + /* + * Adjust instructions to keep the existing behavior with objdump. + * + * Example conversion: + * From: b #0xffff8000800114c8 + * To: b ffff8000800114c8 <el0t_64_sync+0x108> + */ + if (needs_symbolic_address(insn)) { + const char *sym_name = NULL; + u64 sym_offset; + + /* Extract last immediate value as address */ + last_imm = strrchr(buf, '#'); + if (!last_imm) + return; + + addr = strtoull(last_imm + 1, &endptr, 16); + if (endptr == last_imm + 1) + return; + + if (resolve_symbol_from_addr(map, addr, &sym_name, &sym_offset)) + return; + + /* Symbolize the resolved address */ + len = len - (last_imm - buf); + if (sym_offset == 0) { + scnprintf(last_imm, len, "%"PRIx64" <%s>", + addr, sym_name); + } else { + scnprintf(last_imm, len, "%"PRIx64" <%s+%#"PRIx64">", + addr, sym_name, sym_offset); + } + } +} + +static void format_capstone_insn(struct cs_insn *insn, char *buf, size_t len, + struct annotate_args *args, u64 addr) +{ + /* TODO: support more architectures */ + if (arch__is_x86(args->arch)) + format_capstone_insn_x86(insn, buf, len, args, addr); + else if (arch__is_arm64(args->arch)) + format_capstone_insn_arm64(insn, buf, len, args); + else + print_default_format(insn, buf, len); +} + struct find_file_offset_data { u64 ip; u64 offset;
@@ -446,14 +560,9 @@ int symbol__disassemble_capstone(const char *filename, struct symbol *sym, free_count = count = perf_cs_disasm(handle, buf, buf_len, start, buf_len, &insn); for (i = 0, offset = 0; i < count; i++) { - int printed; - - printed = scnprintf(disasm_buf, sizeof(disasm_buf), - " %-7s %s", - insn[i].mnemonic, insn[i].op_str); - print_capstone_detail(&insn[i], disasm_buf + printed, - sizeof(disasm_buf) - printed, args, - start + offset); + format_capstone_insn(&insn[i], disasm_buf, + sizeof(disasm_buf), args, + start + offset); args->offset = offset; args->line = disasm_buf;
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 6cfdbabbb8c7..0ba38f70fe1a 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c@@ -203,6 +203,11 @@ bool arch__is_powerpc(const struct arch *arch) return arch->id.e_machine == EM_PPC || arch->id.e_machine == EM_PPC64; } +bool arch__is_arm64(const struct arch *arch) +{ + return arch->id.e_machine == EM_AARCH64; +} + static void ins_ops__delete(struct ins_operands *ops) { if (ops == NULL)
diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h
index 25756e3f47e4..06c664fd4fc2 100644
--- a/tools/perf/util/disasm.h
+++ b/tools/perf/util/disasm.h@@ -111,6 +111,7 @@ struct annotate_args { const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *cpuid); bool arch__is_x86(const struct arch *arch); bool arch__is_powerpc(const struct arch *arch); +bool arch__is_arm64(const struct arch *arch); extern const struct ins_ops call_ops; extern const struct ins_ops dec_ops;
--
2.34.1