Re: [PATCH v4 01/23] perf capstone: Fix arm64 jump/adrp disassembly mismatch with objdump

Tengda Wu <[email protected]>
Newsgroups dev.linux.lists.llvm,org.kernel.vger.linux-kernel,org.kernel.vger.linux-perf-users
Message-ID <[email protected]>
Hi Shuai, thank you for your time.

On 2026/8/10 21:08, Shuai Xue wrote:
> 
> 
> On 8/8/26 8:23 PM, 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 <[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
> 
> Please add a comment explaining where '7' comes from
> (CS_GRP_BRANCH_RELATIVE in capstone v3), otherwise it reads like an
> arbitrary magic number.
> 

Um, this was done following Namhyung's approach. That said, adding a
comment would certainly make this definition clearer -- will add it.

Also, I couldn't find CS_GRP_BRANCH_RELATIVE in v3. From what I can see,
it was originally introduced in v4, together with ARM64_GRP_BRANCH_RELATIVE
(see https://github.com/capstone-engine/capstone/commit/a09a81813c83).
So the comment might look something like this:

  #define ARM64_GRP_BRANCH_RELATIVE  7  /* = CS_GRP_BRANCH_RELATIVE */

Is this acceptable?

Thanks,
Tengda

>> +#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);
> 
> 
> This whole sequence (objdump_2mem -> kmaps relocation -> map_ip ->
> find_symbol -> scnprintf the "<sym+off>" string) is almost identical to
> what print_capstone_detail() does for x86 RIP-relative operands.  Could
> you factor out a common helper so the two paths don't diverge over time?
> 
> Thanks.
> Shuai
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.