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 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 } 142