[PATCH v4 02/23] perf llvm: Fix arm64 adrp instruction disassembly mismatch with objdump
From: Tengda Wu
Date: Sat Aug 08 2026 - 08:45:16 EST
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
Fix: 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.
Signed-off-by: Tengda Wu <wutengda@xxxxxxxxxxxxxxx>
---
tools/perf/util/llvm.c | 50 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 46 insertions(+), 4 deletions(-)
diff --git a/tools/perf/util/llvm.c b/tools/perf/util/llvm.c
index a0deb742a733..6b53b283205a 100644
--- a/tools/perf/util/llvm.c
+++ b/tools/perf/util/llvm.c
@@ -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;
}
@@ -230,10 +242,40 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
if (storage.pcrel_load_addr != 0) {
char *name = llvm_name_for_data(dso, filename,
storage.pcrel_load_addr);
- disasm_len += scnprintf(disasm_buf + disasm_len,
- sizeof(disasm_buf) - disasm_len,
- " # %#"PRIx64,
- storage.pcrel_load_addr);
+
+ 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);
+ } 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 */
if (name) {
disasm_len += scnprintf(disasm_buf + disasm_len,
sizeof(disasm_buf) -
--
2.34.1