perf annotate-arm64: Support 'add' instruction tracking

Extend update_insn_state_arm64() to track 'add' instructions for
structure member address calculation.

Unlike x86, the arm64 'add' instruction has an extra base register among
its source operands. Therefore, in terms of propagating the data type,
it is essentially performing a 'mov', except that before the 'mov', it
first needs to be updated by adding the offset or reg2.

A real-world example is shown below:

  ffff80008001c9a8 <flush_ptrace_hw_breakpoint>:
  ffff80008001c9c4:  add  x19, x0, #0xeb8   // x0 (task_struct*) + 0xeb8 -> x19
* ffff80008001c9d0:  ldr  x0, [x19]

Before this commit, the type flow broke at the 'add' instruction,
leaving the subsequent load with no type information:

  chk [28] reg19 offset=0 ok=0 kind=0 cfa : no type information
  final result: no type information

After this commit, the tracker correctly follows the member address
calculation:

  var [0] reg0 offset 0 type='struct task_struct*'
  add [1c] address of 0xeb8(reg0) -> reg19 type='struct task_struct*'
  chk [28] reg19 offset=0 ok=1 kind=1 (struct task_struct*) : Good!
  found by insn track: 0(reg19) type-offset=0xeb8
  final result: type='struct task_struct'

Reviewed-by: Ian Rogers <irogers@google.com>
Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Tengda Wu <wutengda@huaweicloud.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Tengda Wu 2026-10-20 13:45:08 +00:00 committed by Arnaldo Carvalho de Melo
parent cf66ee3a5d
commit 143ad11b37

View File

@ -959,6 +959,74 @@ static void update_mov_insn_state(struct type_state *state,
pr_debug_type_name(&tsr->type, tsr->kind);
}
static void update_add_insn_state(struct type_state *state,
struct data_loc_info *dloc,
struct disasm_line *dl,
struct annotated_op_loc *src,
struct annotated_op_loc *dst)
{
struct type_state_reg *tsr;
struct type_state_reg src_tsr;
u32 insn_offset = dl->al.offset;
int sreg = src->reg1;
int dreg = dst->reg1;
int reg_offset;
if (!has_reg_type(state, dreg))
return;
tsr = &state->regs[dreg];
tsr->copied_from = -1;
tsr->lifetime_active = false;
tsr->lifetime_end = 0;
retry:
if (!has_reg_type(state, sreg) || !state->regs[sreg].ok) {
invalidate_reg_state(tsr);
return;
}
src_tsr = state->regs[sreg];
if (arch_get_reg_offset(dloc->arch, src, sreg, state, false, &reg_offset))
goto add_non_regoff;
if (src_tsr.kind == TSR_KIND_CONST) {
tsr->kind = src_tsr.kind;
tsr->imm_value = src_tsr.imm_value + reg_offset;
tsr->offset = 0;
tsr->ok = src_tsr.ok;
pr_debug_dtp("add [%x] imm %#x(reg%d) -> reg%d\n",
insn_offset, reg_offset, sreg, dreg);
return;
}
if (src_tsr.kind == TSR_KIND_POINTER ||
(src_tsr.kind == TSR_KIND_TYPE &&
dwarf_tag(&src_tsr.type) == DW_TAG_pointer_type)) {
tsr->type = src_tsr.type;
tsr->kind = src_tsr.kind;
tsr->imm_value = src_tsr.imm_value;
tsr->offset = src_tsr.offset + reg_offset;
tsr->ok = src_tsr.ok;
pr_debug_dtp("add [%x] address of %#x(reg%d) -> reg%d",
insn_offset, reg_offset, sreg, dreg);
pr_debug_type_name(&tsr->type, tsr->kind);
return;
}
add_non_regoff:
/* Try another register if any */
if (src->multi_regs && src->reg1 != src->reg2 && sreg != src->reg2 &&
!(src->extend_type || src->shift_type)) {
sreg = src->reg2;
goto retry;
}
invalidate_reg_state(tsr);
}
static void update_insn_state_arm64(struct type_state *state,
struct data_loc_info *dloc, Dwarf_Die *cu_die,
struct disasm_line *dl)
@ -975,6 +1043,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, "add"))
update_add_insn_state(state, dloc, dl, src, dst);
/* Register to register or imm value to register transfers */
else if (!strcmp(dl->ins.name, "mov"))
update_mov_insn_state(state, dl, src, dst);