1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2025, Oracle and/or its affiliates. 4 */ 5 6 #include <objtool/trace.h> 7 8 bool trace; 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 const char *rname; 38 39 switch (reg) { 40 case CFI_UNDEFINED: 41 return "<undefined>"; 42 case CFI_CFA: 43 return "cfa"; 44 case CFI_SP_INDIRECT: 45 return "(sp)"; 46 case CFI_BP_INDIRECT: 47 return "(bp)"; 48 } 49 50 if (reg < CFI_NUM_REGS) { 51 rname = arch_reg_name[reg]; 52 if (rname) 53 return rname; 54 } 55 56 if (snprintf(rname_buffer, CFI_REG_NAME_MAXLEN, "r%d", reg) == -1) 57 return "<error>"; 58 59 return (const char *)rname_buffer; 60 } 61 62 /* 63 * Functions and macros to trace CFI registers changes. 64 */ 65 66 static void trace_cfi_reg(const char *prefix, int reg, const char *fmt, 67 int base_prev, int offset_prev, 68 int base_next, int offset_next) 69 { 70 char *rname; 71 72 if (base_prev == base_next && offset_prev == offset_next) 73 return; 74 75 if (prefix) 76 TRACE("%s:", prefix); 77 78 if (base_next == CFI_UNDEFINED) { 79 TRACE("%1$s=<undef> ", cfi_reg_name(reg)); 80 } else { 81 rname = strdup(cfi_reg_name(reg)); 82 TRACE(fmt, rname, cfi_reg_name(base_next), offset_next); 83 free(rname); 84 } 85 } 86 87 static void trace_cfi_reg_val(const char *prefix, int reg, 88 int base_prev, int offset_prev, 89 int base_next, int offset_next) 90 { 91 trace_cfi_reg(prefix, reg, "%1$s=%2$s%3$+d ", 92 base_prev, offset_prev, base_next, offset_next); 93 } 94 95 static void trace_cfi_reg_ref(const char *prefix, int reg, 96 int base_prev, int offset_prev, 97 int base_next, int offset_next) 98 { 99 trace_cfi_reg(prefix, reg, "%1$s=(%2$s%3$+d) ", 100 base_prev, offset_prev, base_next, offset_next); 101 } 102 103 #define TRACE_CFI_REG_VAL(reg, prev, next) \ 104 trace_cfi_reg_val(NULL, reg, prev.base, prev.offset, \ 105 next.base, next.offset) 106 107 #define TRACE_CFI_REG_REF(reg, prev, next) \ 108 trace_cfi_reg_ref(NULL, reg, prev.base, prev.offset, \ 109 next.base, next.offset) 110 111 void trace_insn_state(struct instruction *insn, struct insn_state *sprev, 112 struct insn_state *snext) 113 { 114 struct cfi_state *cprev, *cnext; 115 int i; 116 117 if (!memcmp(sprev, snext, sizeof(struct insn_state))) 118 return; 119 120 cprev = &sprev->cfi; 121 cnext = &snext->cfi; 122 123 disas_print_insn(stderr, objtool_disas_ctx, insn, 124 trace_depth - 1, "state: "); 125 126 /* print registers changes */ 127 TRACE_CFI_REG_VAL(CFI_CFA, cprev->cfa, cnext->cfa); 128 for (i = 0; i < CFI_NUM_REGS; i++) { 129 TRACE_CFI_REG_VAL(i, cprev->vals[i], cnext->vals[i]); 130 TRACE_CFI_REG_REF(i, cprev->regs[i], cnext->regs[i]); 131 } 132 133 /* print attributes changes */ 134 TRACE_CFI_ATTR_NUM(stack_size, cprev, cnext, "%d"); 135 TRACE_CFI_ATTR_BOOL(drap, cprev, cnext); 136 if (cnext->drap) { 137 trace_cfi_reg_val("drap", cnext->drap_reg, 138 cprev->drap_reg, cprev->drap_offset, 139 cnext->drap_reg, cnext->drap_offset); 140 } 141 TRACE_CFI_ATTR_BOOL(bp_scratch, cprev, cnext); 142 TRACE_CFI_ATTR_NUM(instr, sprev, snext, "%d"); 143 TRACE_CFI_ATTR_NUM(uaccess_stack, sprev, snext, "%u"); 144 145 TRACE("\n"); 146 147 insn->trace = 1; 148 } 149