Re: [PATCH v2 02/16] perf capstone: Fix arm64 jump/adrp disassembly mismatch with objdump

From: Tengda Wu

Date: Fri Apr 10 2026 - 05:25:53 EST




On 2026/4/7 14:43, Namhyung Kim wrote:
> On Fri, Apr 03, 2026 at 09:47:46AM +0000, Tengda Wu wrote:
>> 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 <wutengda@xxxxxxxxxxxxxxx>
>> ---
>> tools/perf/util/capstone.c | 107 ++++++++++++++++++++++++++++++++-----
>> tools/perf/util/disasm.c | 5 ++
>> tools/perf/util/disasm.h | 1 +
>> 3 files changed, 101 insertions(+), 12 deletions(-)
>>
>> diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
>> index 25cf6e15ec27..1d8421d2d98c 100644
>> --- a/tools/perf/util/capstone.c
>> +++ b/tools/perf/util/capstone.c
>> @@ -255,10 +255,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;
>>
>> @@ -305,6 +301,98 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
>> }
>> }
>>
>> +static void format_capstone_insn_x86(struct cs_insn *insn, char *buf,
>> + size_t len, struct annotate_args *args,
>> + u64 addr)
>> +{
>> + int printed;
>> +
>> + printed = scnprintf(buf, len, " %-7s %s",
>> + insn->mnemonic, insn->op_str);
>> + buf += printed;
>> + len -= printed;
>> +
>> + print_capstone_detail(insn, buf, len, args, addr);
>> +}
>> +
>> +static void format_capstone_insn_arm64(struct cs_insn *insn, char *buf,
>> + size_t len, struct annotate_args *args,
>> + u64 addr)
>> +{
>> + struct map *map = args->ms->map;
>> + struct symbol *sym;
>> + char *last_imm, *endptr;
>> + u64 orig_addr;
>> +
>> + scnprintf(buf, len, " %-7s %s",
>> + insn->mnemonic, insn->op_str);
>> + /*
>> + * Adjust instructions to keep the existing behavior with objdump.
>> + *
>> + * Example conversion:
>> + * From: b #0xffff8000800114c8
>> + * To: b ffff8000800114c8 <el0t_64_sync+0x108>
>> + */
>> + switch (insn->id) {
>> + case ARM64_INS_B:
>> + case ARM64_INS_BL:
>> + case ARM64_INS_CBNZ:
>> + case ARM64_INS_CBZ:
>> + case ARM64_INS_TBNZ:
>> + case ARM64_INS_TBZ:
>> + case ARM64_INS_ADRP:
>> + /* 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;
>> +
>> + /* Relocate map that contains the address */
>> + if (dso__kernel(map__dso(map))) {
>> + map = maps__find(map__kmaps(map), orig_addr);
>> + if (map == NULL)
>> + return;
>
> I know you copied the logic from x86, but I've realized that it leaks a
> refcount for the new kernel map returned from maps__find(). This needs
> to be fixed separately.
>
> Thanks,
> Namhyung
>

Oh, right.
I will send a separate patch to fix this issue in the existing x86 logic and
will make sure the arm64 implementation follows the fix.

-- Tengda

>
>> + }
>> +
>> + /* Convert it to map-relative address for search */
>> + addr = map__map_ip(map, orig_addr);
>> +
>> + sym = map__find_symbol(map, addr);
>> + if (sym == NULL)
>> + 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);
>> + }
>> + break;
>> + default:
>> + break;
>> + }
>> +}
>> +
>> +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, addr);
>> + else {
>> + scnprintf(buf, len, " %-7s %s",
>> + insn->mnemonic, insn->op_str);
>> + }
>> +}
>> +
>> struct find_file_offset_data {
>> u64 ip;
>> u64 offset;
>> @@ -381,14 +469,9 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
>>
>> 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 40fcaed5d0b1..988b2b748e11 100644
>> --- a/tools/perf/util/disasm.c
>> +++ b/tools/perf/util/disasm.c
>> @@ -202,6 +202,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 a6e478caf61a..d3730ed86dba 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
>>