xref: /linux/tools/objtool/trace.c (revision 26a453fb5637907a538d6ea5ef23651142811e15)
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