[PATCH v4 01/23] perf capstone: Fix arm64 jump/adrp disassembly mismatch with objdump
Tengda Wu <[email protected]>
| Newsgroups | org.kernel.vger.linux-perf-users,dev.linux.lists.llvm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
The jump and adrp instructions parsed by libcapstone currently lack symbolic representation and use a '#' prefix for addresses. This format is inconsistent with objdump's output, which causes subsequent parsing in jump__parse() and arm64_mov__parse() to fail. Example mismatch: Current: b #0xffff8000800114c8 Fix: b ffff8000800114c8 <el0t_64_sync+0x108> Current: adrp x18, #0xffff800081f5f000 Fix: adrp x18, ffff800081f5f000 <this_cpu_vector> Fix this by implementing extended formatting for these arm64 instructions during symbol__disassemble_capstone(). This ensures the output matches objdump's expected style, including the raw address and the associated <symbol+offset> suffix. Signed-off-by: Tengda Wu <[email protected]> --- tools/perf/util/capstone.c | 136 +++++++++++++++++++++++++++++++++---- tools/perf/util/disasm.c | 5 ++ tools/perf/util/disasm.h | 1 + 3 files changed, 130 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c index 74213daf8786..fb8a2bc5558f 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 +#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; @@ -299,10 +310,6 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len, 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; @@ -354,6 +361,116 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len, } } +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 is_pc_relative_insn(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; + struct symbol *sym; + char *last_imm, *endptr; + u64 orig_addr, addr; + struct map *found_map = NULL; + + 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 (is_pc_relative_insn(insn)) { + /* Extract last immediate value as address */ + last_imm = strrchr(buf, '#'); + if (!last_imm) + return; + + orig_addr = strtoull(last_imm + 1, &endptr, 16); + if (endptr == last_imm + 1) + return; + + addr = map__objdump_2mem(map, orig_addr); + + /* Relocate map that contains the address */ + if (dso__kernel(map__dso(map))) { + found_map = maps__find(map__kmaps(map), addr); + if (found_map == NULL) + return; + 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); + return; + } + + /* Symbolize the resolved address */ + len = len - (last_imm - buf); + if (addr == sym->start) { + scnprintf(last_imm, len, "%"PRIx64" <%s>", + orig_addr, sym->name); + } else { + scnprintf(last_imm, len, "%"PRIx64" <%s+%#"PRIx64">", + orig_addr, sym->name, addr - sym->start); + } + map__put(found_map); + } +} + +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 +563,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