Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

objtool: Trace instruction state changes during function validation

During function validation, objtool maintains a per-instruction state,
in particular to track call frame information. When tracing validation,
print any instruction state changes.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@kernel.org>
Link: https://patch.msgid.link/20251121095340.464045-12-alexandre.chartre@oracle.com

authored by

Alexandre Chartre and committed by
Peter Zijlstra
fcb268b4 70589843

+149 -1
+7 -1
tools/objtool/check.c
··· 3677 3677 struct instruction *prev_insn, struct instruction *next_insn, 3678 3678 bool *dead_end) 3679 3679 { 3680 + /* prev_state is not used if there is no disassembly support */ 3681 + struct insn_state prev_state __maybe_unused; 3680 3682 struct alternative *alt; 3681 3683 u8 visited; 3682 3684 int ret; ··· 3787 3785 if (skip_alt_group(insn)) 3788 3786 return 0; 3789 3787 3790 - if (handle_insn_ops(insn, next_insn, statep)) 3788 + prev_state = *statep; 3789 + ret = handle_insn_ops(insn, next_insn, statep); 3790 + TRACE_INSN_STATE(insn, &prev_state, statep); 3791 + 3792 + if (ret) 3791 3793 return 1; 3792 3794 3793 3795 switch (insn->type) {
+10
tools/objtool/include/objtool/trace.h
··· 30 30 } \ 31 31 }) 32 32 33 + #define TRACE_INSN_STATE(insn, sprev, snext) \ 34 + ({ \ 35 + if (trace) \ 36 + trace_insn_state(insn, sprev, snext); \ 37 + }) 38 + 33 39 static inline void trace_enable(void) 34 40 { 35 41 trace = true; ··· 59 53 trace_depth--; 60 54 } 61 55 56 + void trace_insn_state(struct instruction *insn, struct insn_state *sprev, 57 + struct insn_state *snext); 58 + 62 59 #else /* DISAS */ 63 60 64 61 #define TRACE(fmt, ...) ({}) 65 62 #define TRACE_INSN(insn, fmt, ...) ({}) 63 + #define TRACE_INSN_STATE(insn, sprev, snext) ({}) 66 64 67 65 static inline void trace_enable(void) {} 68 66 static inline void trace_disable(void) {}
+132
tools/objtool/trace.c
··· 7 7 8 8 bool trace; 9 9 int trace_depth; 10 + 11 + /* 12 + * Macros to trace CFI state attributes changes. 13 + */ 14 + 15 + #define TRACE_CFI_ATTR(attr, prev, next, fmt, ...) \ 16 + ({ \ 17 + if ((prev)->attr != (next)->attr) \ 18 + TRACE("%s=" fmt " ", #attr, __VA_ARGS__); \ 19 + }) 20 + 21 + #define TRACE_CFI_ATTR_BOOL(attr, prev, next) \ 22 + TRACE_CFI_ATTR(attr, prev, next, \ 23 + "%s", (next)->attr ? "true" : "false") 24 + 25 + #define TRACE_CFI_ATTR_NUM(attr, prev, next, fmt) \ 26 + TRACE_CFI_ATTR(attr, prev, next, fmt, (next)->attr) 27 + 28 + #define CFI_REG_NAME_MAXLEN 16 29 + 30 + /* 31 + * Return the name of a register. Note that the same static buffer 32 + * is returned if the name is dynamically generated. 33 + */ 34 + static const char *cfi_reg_name(unsigned int reg) 35 + { 36 + static char rname_buffer[CFI_REG_NAME_MAXLEN]; 37 + 38 + switch (reg) { 39 + case CFI_UNDEFINED: 40 + return "<undefined>"; 41 + case CFI_CFA: 42 + return "cfa"; 43 + case CFI_SP_INDIRECT: 44 + return "(sp)"; 45 + case CFI_BP_INDIRECT: 46 + return "(bp)"; 47 + } 48 + 49 + if (snprintf(rname_buffer, CFI_REG_NAME_MAXLEN, "r%d", reg) == -1) 50 + return "<error>"; 51 + 52 + return (const char *)rname_buffer; 53 + } 54 + 55 + /* 56 + * Functions and macros to trace CFI registers changes. 57 + */ 58 + 59 + static void trace_cfi_reg(const char *prefix, int reg, const char *fmt, 60 + int base_prev, int offset_prev, 61 + int base_next, int offset_next) 62 + { 63 + char *rname; 64 + 65 + if (base_prev == base_next && offset_prev == offset_next) 66 + return; 67 + 68 + if (prefix) 69 + TRACE("%s:", prefix); 70 + 71 + if (base_next == CFI_UNDEFINED) { 72 + TRACE("%1$s=<undef> ", cfi_reg_name(reg)); 73 + } else { 74 + rname = strdup(cfi_reg_name(reg)); 75 + TRACE(fmt, rname, cfi_reg_name(base_next), offset_next); 76 + free(rname); 77 + } 78 + } 79 + 80 + static void trace_cfi_reg_val(const char *prefix, int reg, 81 + int base_prev, int offset_prev, 82 + int base_next, int offset_next) 83 + { 84 + trace_cfi_reg(prefix, reg, "%1$s=%2$s%3$+d ", 85 + base_prev, offset_prev, base_next, offset_next); 86 + } 87 + 88 + static void trace_cfi_reg_ref(const char *prefix, int reg, 89 + int base_prev, int offset_prev, 90 + int base_next, int offset_next) 91 + { 92 + trace_cfi_reg(prefix, reg, "%1$s=(%2$s%3$+d) ", 93 + base_prev, offset_prev, base_next, offset_next); 94 + } 95 + 96 + #define TRACE_CFI_REG_VAL(reg, prev, next) \ 97 + trace_cfi_reg_val(NULL, reg, prev.base, prev.offset, \ 98 + next.base, next.offset) 99 + 100 + #define TRACE_CFI_REG_REF(reg, prev, next) \ 101 + trace_cfi_reg_ref(NULL, reg, prev.base, prev.offset, \ 102 + next.base, next.offset) 103 + 104 + void trace_insn_state(struct instruction *insn, struct insn_state *sprev, 105 + struct insn_state *snext) 106 + { 107 + struct cfi_state *cprev, *cnext; 108 + int i; 109 + 110 + if (!memcmp(sprev, snext, sizeof(struct insn_state))) 111 + return; 112 + 113 + cprev = &sprev->cfi; 114 + cnext = &snext->cfi; 115 + 116 + disas_print_insn(stderr, objtool_disas_ctx, insn, 117 + trace_depth - 1, "state: "); 118 + 119 + /* print registers changes */ 120 + TRACE_CFI_REG_VAL(CFI_CFA, cprev->cfa, cnext->cfa); 121 + for (i = 0; i < CFI_NUM_REGS; i++) { 122 + TRACE_CFI_REG_VAL(i, cprev->vals[i], cnext->vals[i]); 123 + TRACE_CFI_REG_REF(i, cprev->regs[i], cnext->regs[i]); 124 + } 125 + 126 + /* print attributes changes */ 127 + TRACE_CFI_ATTR_NUM(stack_size, cprev, cnext, "%d"); 128 + TRACE_CFI_ATTR_BOOL(drap, cprev, cnext); 129 + if (cnext->drap) { 130 + trace_cfi_reg_val("drap", cnext->drap_reg, 131 + cprev->drap_reg, cprev->drap_offset, 132 + cnext->drap_reg, cnext->drap_offset); 133 + } 134 + TRACE_CFI_ATTR_BOOL(bp_scratch, cprev, cnext); 135 + TRACE_CFI_ATTR_NUM(instr, sprev, snext, "%d"); 136 + TRACE_CFI_ATTR_NUM(uaccess_stack, sprev, snext, "%u"); 137 + 138 + TRACE("\n"); 139 + 140 + insn->trace = 1; 141 + }