From 645d694dd01ce44d6ae0ebf5acd962b681bb0c1f Mon Sep 17 00:00:00 2001 From: Tengda Wu Date: Tue, 20 Oct 2026 13:45:11 +0000 Subject: [PATCH] perf annotate-arm64: Support 'mrs' instruction to track 'current' pointer Extend update_insn_state_arm64() to handle the 'mrs' instruction, enabling the tracking of the 'current' task pointer in the kernel. On arm64, the kernel uses the 'sp_el0' system register to store the address of the currently executing 'struct task_struct'. This is typically accessed via the 'get_current()' inline function, resulting in the instruction 'mrs xN, sp_el0'. To resolve the data type of the target register, first verify the access is to 'sp_el0' within a kernel DSO. Then, locate the 'get_current()' inline function's DWARF Die at the current PC and extract its return type (which should be 'struct task_struct*'). A real-world example is shown below: ffff8000800deee8 : ffff8000800deef0: mrs x0, sp_el0 // x0 = current * ffff8000800deef4: ldr w1, [x0, #44] Before this commit, the type flow starts with no information: chk [c] reg0 offset=0x2c ok=0 kind=0 cfa : no type information final result: no type information After this commit, the tracker identifies the 'current' pointer from the system register: mrs [8] sp_el0 -> reg0 type='struct task_struct*' chk [c] reg0 offset=0x2c ok=1 kind=1 (struct task_struct*) : Good! found by insn track: 0x2c(reg0) type-offset=0x2c final result: type='struct task_struct' Reviewed-by: Ian Rogers Reviewed-by: Namhyung Kim Signed-off-by: Li Huafei Signed-off-by: Tengda Wu Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/util/annotate-arch/annotate-arm64.c | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tools/perf/util/annotate-arch/annotate-arm64.c b/tools/perf/util/annotate-arch/annotate-arm64.c index 270f35876511..81369ae47290 100644 --- a/tools/perf/util/annotate-arch/annotate-arm64.c +++ b/tools/perf/util/annotate-arch/annotate-arm64.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,7 @@ #include "../dso.h" #include "../map.h" #include "../symbol.h" +#include "../strbuf.h" struct arch_arm64 { struct arch arch; @@ -1114,6 +1116,73 @@ static void update_adrp_insn_state(struct type_state *state, insn_offset, tsr->imm_value, dreg); } +static void update_mrs_insn_state(struct type_state *state, + struct data_loc_info *dloc, Dwarf_Die *cu_die, + struct disasm_line *dl, + struct annotated_op_loc *dst) +{ + struct type_state_reg *tsr; + Dwarf_Die type_die; + u32 insn_offset = dl->al.offset; + int dreg = dst->reg1; + Dwarf_Die func_die; + Dwarf_Attribute attr; + u64 ip, pc; + struct strbuf sb; + char *type_name; + + if (!has_reg_type(state, dreg)) + return; + + tsr = &state->regs[dreg]; + tsr->copied_from = -1; + + /* Handle case difference: LLVM (SP_EL0) vs objdump (sp_el0) */ + if (!dso__kernel(map__dso(dloc->ms->map)) || + strcasecmp(dl->ops.source.raw, "sp_el0")) { + invalidate_reg_state(tsr); + return; + } + + ip = dloc->ms->sym->start + dl->al.offset; + pc = map__rip_2objdump(dloc->ms->map, ip); + + /* + * Find the inline function 'get_current()' Dwarf_Die and obtain its + * return value data type, which should be 'struct task_struct*'. + */ + if (!die_find_inlinefunc(cu_die, pc, &func_die) || + !die_compare_name(&func_die, "get_current") || + !dwarf_attr_integrate(&func_die, DW_AT_type, &attr) || + !dwarf_formref_die(&attr, &type_die)) { + invalidate_reg_state(tsr); + return; + } + + strbuf_init(&sb, 32); + die_get_typename_from_type(&type_die, &sb); + type_name = strbuf_detach(&sb, NULL); + + if (!type_name || strcmp(type_name, "struct task_struct*")) { + invalidate_reg_state(tsr); + goto out_free; + } + + tsr->type = type_die; + tsr->kind = TSR_KIND_TYPE; + tsr->offset = 0; + tsr->imm_value = 0; + tsr->lifetime_active = false; + tsr->lifetime_end = 0; + tsr->ok = true; + + pr_debug_dtp("mrs [%x] sp_el0 -> reg%d", insn_offset, dreg); + pr_debug_type_name(&type_die, tsr->kind); + +out_free: + free(type_name); +} + static void update_insn_state_arm64(struct type_state *state, struct data_loc_info *dloc, Dwarf_Die *cu_die, struct disasm_line *dl) @@ -1130,6 +1199,8 @@ static void update_insn_state_arm64(struct type_state *state, if (ins__is_call(&dl->ins)) update_call_insn_state(state, dloc, cu_die, dl); + else if (!strcmp(dl->ins.name, "mrs")) + update_mrs_insn_state(state, dloc, cu_die, dl, dst); else if (!strcmp(dl->ins.name, "adrp")) update_adrp_insn_state(state, dl, dst); else if (!strcmp(dl->ins.name, "add"))