diff --git a/tools/perf/arch/arm64/annotate/instructions.c b/tools/perf/arch/arm64/annotate/instructions.c
index c212eb7341bd..54497b72a5c5 100644
--- a/tools/perf/arch/arm64/annotate/instructions.c
+++ b/tools/perf/arch/arm64/annotate/instructions.c
@@ -188,3 +188,65 @@ static int arm64__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
 	free(arm);
 	return SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP;
 }
+
+
+/*
+ * Get the base register number and access offset in load/store instructions.
+ * At present, only the following two addressing modes are supported:
+ *
+ *  1. Base register only (no offset): [base{, #0}]
+ *  2. Base plus offset (immediate): [base{, #imm}]
+ *
+ * For addressing modes where the offset needs to be calculated from the
+ * register value, it is difficult to know the specific value of the offset
+ * register, making it impossible to calculate the offset.
+ *
+ * Fills @reg and @offset when return 0.
+ */
+static int
+extract_reg_offset_arm64(struct arch *arch __maybe_unused,
+			 struct disasm_line *dl __maybe_unused,
+			 const char *insn_str, int insn_ops __maybe_unused,
+			 struct annotated_op_loc *op_loc)
+{
+	char *str;
+	regmatch_t match[4];
+	static regex_t reg_off_regex;
+	static bool regex_compiled;
+
+	if (!regex_compiled) {
+		regcomp(&reg_off_regex, "^\\[(sp|[xw][0-9]{1,2})(, #(-?[0-9]+))?\\].*",
+			REG_EXTENDED);
+		regex_compiled = true;
+	}
+
+	if (!op_loc->mem_ref)
+		return 0;
+
+	if (regexec(&reg_off_regex, insn_str, 4, match, 0))
+		return -1;
+
+	str = strdup(insn_str);
+	if (!str)
+		return -1;
+
+	/* Get the base register number. */
+	str[match[1].rm_eo] = '\0';
+	op_loc->reg1 = get_arm64_regnum(str + match[1].rm_so);
+
+	/*
+	 * If there is an immediate offset, match[2] records the start and end
+	 * positions of "#imm".
+	 */
+	if (match[2].rm_so == -1) {
+		free(str);
+		return 0;
+	}
+
+	/* Get the immediate offset. */
+	str[match[3].rm_eo] = '\0';
+	op_loc->offset = strtol(str + match[3].rm_so, NULL, 0);
+
+	free(str);
+	return 0;
+}
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 5ec97e8d6b6d..d408cbe94fdd 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -210,6 +210,7 @@ perf-util-$(CONFIG_LIBDW) += dwarf-regs.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-csky.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-powerpc.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-x86.o
+perf-util-$(CONFIG_LIBDW) += dwarf-regs-arm64.o
 perf-util-$(CONFIG_LIBDW) += debuginfo.o
 perf-util-$(CONFIG_LIBDW) += annotate-data.o
 
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 905eceb824a4..1035c60a8545 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -128,6 +128,7 @@ static struct arch architectures[] = {
 	{
 		.name = "arm64",
 		.init = arm64__annotate_init,
+		.extract_reg_offset = extract_reg_offset_arm64,
 	},
 	{
 		.name = "csky",
diff --git a/tools/perf/util/dwarf-regs-arm64.c b/tools/perf/util/dwarf-regs-arm64.c
new file mode 100644
index 000000000000..edf41c059967
--- /dev/null
+++ b/tools/perf/util/dwarf-regs-arm64.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mapping of DWARF debug register numbers into register names.
+ *
+ * Copyright (c) 2025  Huawei Inc, Li Huafei <lihuafei1@huawei.com>
+ */
+#include <errno.h>
+#include <string.h>
+#include <dwarf-regs.h>
+
+int get_arm64_regnum(const char *name)
+{
+	int reg;
+
+	if (!strcmp(name, "sp"))
+		return 31;
+
+	if (*name != 'x' && *name != 'w')
+		return -EINVAL;
+
+	name++;
+	reg = strtol(name, NULL, 0);
+
+	return reg >= 0 && reg <= 30 ? reg : -EINVAL;
+}
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index 6f1b9f6b2466..81cc5f69a391 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -101,6 +101,8 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int
 
 int get_x86_regnum(const char *name);
 
+int get_arm64_regnum(const char *name);
+
 #if !defined(__x86_64__) && !defined(__i386__)
 int get_arch_regnum(const char *name);
 #endif
@@ -128,6 +130,11 @@ static inline void get_powerpc_regs(u32 raw_insn __maybe_unused, int is_source _
 {
 	return;
 }
+
+static inline int get_arm64_regnum(const char *name __maybe_unused)
+{
+	return -1;
+}
 #endif
 
 #endif
