perf llvm: Fix arm64 adrp instruction disassembly mismatch with objdump

The operands of 'adrp' instructions parsed by libllvm are currently
represented as raw immediates rather than the "address <symbol+offset>"
format used by objdump. This inconsistency causes arm64_mov__parse()
to fail when parsing these instructions during post-processing.

Example of the mismatch:
  Current: adrp  x18, 8014
  Objdump: adrp  x18, ffff800081f5f000 <this_cpu_vector>

Fix this by manually extracting the target address from the raw adrp
instruction via symbol_lookup_callback(). The address is then converted
to a specific symbol during symbol__disassemble_llvm() and formatted
to match objdump's output, ensuring compatibility with existing
parsers.

Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Tengda Wu <wutengda@huaweicloud.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Tengda Wu 2026-09-16 01:29:16 +00:00 committed by Arnaldo Carvalho de Melo
parent 268d5c3979
commit 705390bdcf

View File

@ -9,8 +9,10 @@
#include "symbol.h"
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <linux/zalloc.h>
#include <linux/bitops.h>
#ifdef HAVE_LIBLLVM_SUPPORT
#include "llvm-c-helpers.h"
@ -108,6 +110,16 @@ symbol_lookup_callback(void *disinfo, uint64_t value,
storage->branch_addr = value;
else if (*ref_type == LLVMDisassembler_ReferenceType_In_PCrel_Load)
storage->pcrel_load_addr = value;
else if (*ref_type == LLVMDisassembler_ReferenceType_In_ARM64_ADRP) {
uint64_t adrp_imm;
/* immhi (bits 23:5) and immlo (bits 30:29) */
adrp_imm = ((value & 0x00ffffe0) >> 3) | ((value >> 29) & 0x3);
adrp_imm = sign_extend64(adrp_imm, 20);
/* Calculate the target page address */
storage->pcrel_load_addr = (address & ~0xfffULL) + (adrp_imm << 12);
}
*ref_type = LLVMDisassembler_ReferenceType_InOut_None;
return NULL;
}
@ -228,12 +240,42 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
}
}
if (storage.pcrel_load_addr != 0) {
char *name = llvm_name_for_data(dso, filename,
char *name;
if (arch__is_arm64(args->arch)) {
/*
* For arm64, replace the immediate operand with
* the resolved address to match objdump's output.
*
* Example conversion:
* From: adrp x18, 8014
* To: adrp x18, ffff800081f5f000
*/
char *s = strchr(disasm_buf, ',');
if (s == NULL)
goto err;
s++;
*s = '\0';
disasm_len = strlen(disasm_buf);
disasm_len += scnprintf(disasm_buf + disasm_len,
sizeof(disasm_buf) - disasm_len,
" %"PRIx64,
storage.pcrel_load_addr);
disasm_len += scnprintf(disasm_buf + disasm_len,
sizeof(disasm_buf) - disasm_len,
" # %#"PRIx64,
storage.pcrel_load_addr);
} else {
/*
* For other archs, append the resolved address
* as an inline comment.
*/
disasm_len += scnprintf(disasm_buf + disasm_len,
sizeof(disasm_buf) - disasm_len,
" # %#"PRIx64,
storage.pcrel_load_addr);
}
/* Append the resolved symbol name if available */
name = llvm_name_for_data(dso, filename, storage.pcrel_load_addr);
if (name) {
disasm_len += scnprintf(disasm_buf + disasm_len,
sizeof(disasm_buf) -