xref: /linux/tools/objtool/trace.c (revision 63e6995005be8ceb8a1d56a18df1a1a40c28356d)
170589843SAlexandre Chartre // SPDX-License-Identifier: GPL-2.0-or-later
270589843SAlexandre Chartre /*
370589843SAlexandre Chartre  * Copyright (c) 2025, Oracle and/or its affiliates.
470589843SAlexandre Chartre  */
570589843SAlexandre Chartre 
670589843SAlexandre Chartre #include <objtool/trace.h>
770589843SAlexandre Chartre 
870589843SAlexandre Chartre bool trace;
970589843SAlexandre Chartre int trace_depth;
10fcb268b4SAlexandre Chartre 
11fcb268b4SAlexandre Chartre /*
12fcb268b4SAlexandre Chartre  * Macros to trace CFI state attributes changes.
13fcb268b4SAlexandre Chartre  */
14fcb268b4SAlexandre Chartre 
15fcb268b4SAlexandre Chartre #define TRACE_CFI_ATTR(attr, prev, next, fmt, ...)		\
16fcb268b4SAlexandre Chartre ({								\
17fcb268b4SAlexandre Chartre 	if ((prev)->attr != (next)->attr)			\
18fcb268b4SAlexandre Chartre 		TRACE("%s=" fmt " ", #attr, __VA_ARGS__);	\
19fcb268b4SAlexandre Chartre })
20fcb268b4SAlexandre Chartre 
21fcb268b4SAlexandre Chartre #define TRACE_CFI_ATTR_BOOL(attr, prev, next)			\
22fcb268b4SAlexandre Chartre 	TRACE_CFI_ATTR(attr, prev, next,			\
23fcb268b4SAlexandre Chartre 		       "%s", (next)->attr ? "true" : "false")
24fcb268b4SAlexandre Chartre 
25fcb268b4SAlexandre Chartre #define TRACE_CFI_ATTR_NUM(attr, prev, next, fmt)		\
26fcb268b4SAlexandre Chartre 	TRACE_CFI_ATTR(attr, prev, next, fmt, (next)->attr)
27fcb268b4SAlexandre Chartre 
28fcb268b4SAlexandre Chartre #define CFI_REG_NAME_MAXLEN   16
29fcb268b4SAlexandre Chartre 
30fcb268b4SAlexandre Chartre /*
31fcb268b4SAlexandre Chartre  * Return the name of a register. Note that the same static buffer
32fcb268b4SAlexandre Chartre  * is returned if the name is dynamically generated.
33fcb268b4SAlexandre Chartre  */
cfi_reg_name(unsigned int reg)34fcb268b4SAlexandre Chartre static const char *cfi_reg_name(unsigned int reg)
35fcb268b4SAlexandre Chartre {
36fcb268b4SAlexandre Chartre 	static char rname_buffer[CFI_REG_NAME_MAXLEN];
3726a453fbSAlexandre Chartre 	const char *rname;
38fcb268b4SAlexandre Chartre 
39fcb268b4SAlexandre Chartre 	switch (reg) {
40fcb268b4SAlexandre Chartre 	case CFI_UNDEFINED:
41fcb268b4SAlexandre Chartre 		return "<undefined>";
42fcb268b4SAlexandre Chartre 	case CFI_CFA:
43fcb268b4SAlexandre Chartre 		return "cfa";
44fcb268b4SAlexandre Chartre 	case CFI_SP_INDIRECT:
45fcb268b4SAlexandre Chartre 		return "(sp)";
46fcb268b4SAlexandre Chartre 	case CFI_BP_INDIRECT:
47fcb268b4SAlexandre Chartre 		return "(bp)";
48fcb268b4SAlexandre Chartre 	}
49fcb268b4SAlexandre Chartre 
5026a453fbSAlexandre Chartre 	if (reg < CFI_NUM_REGS) {
5126a453fbSAlexandre Chartre 		rname = arch_reg_name[reg];
5226a453fbSAlexandre Chartre 		if (rname)
5326a453fbSAlexandre Chartre 			return rname;
5426a453fbSAlexandre Chartre 	}
5526a453fbSAlexandre Chartre 
56fcb268b4SAlexandre Chartre 	if (snprintf(rname_buffer, CFI_REG_NAME_MAXLEN, "r%d", reg) == -1)
57fcb268b4SAlexandre Chartre 		return "<error>";
58fcb268b4SAlexandre Chartre 
59fcb268b4SAlexandre Chartre 	return (const char *)rname_buffer;
60fcb268b4SAlexandre Chartre }
61fcb268b4SAlexandre Chartre 
62fcb268b4SAlexandre Chartre /*
63fcb268b4SAlexandre Chartre  * Functions and macros to trace CFI registers changes.
64fcb268b4SAlexandre Chartre  */
65fcb268b4SAlexandre Chartre 
trace_cfi_reg(const char * prefix,int reg,const char * fmt,int base_prev,int offset_prev,int base_next,int offset_next)66fcb268b4SAlexandre Chartre static void trace_cfi_reg(const char *prefix, int reg, const char *fmt,
67fcb268b4SAlexandre Chartre 			  int base_prev, int offset_prev,
68fcb268b4SAlexandre Chartre 			  int base_next, int offset_next)
69fcb268b4SAlexandre Chartre {
70fcb268b4SAlexandre Chartre 	char *rname;
71fcb268b4SAlexandre Chartre 
72fcb268b4SAlexandre Chartre 	if (base_prev == base_next && offset_prev == offset_next)
73fcb268b4SAlexandre Chartre 		return;
74fcb268b4SAlexandre Chartre 
75fcb268b4SAlexandre Chartre 	if (prefix)
76fcb268b4SAlexandre Chartre 		TRACE("%s:", prefix);
77fcb268b4SAlexandre Chartre 
78fcb268b4SAlexandre Chartre 	if (base_next == CFI_UNDEFINED) {
79fcb268b4SAlexandre Chartre 		TRACE("%1$s=<undef> ", cfi_reg_name(reg));
80fcb268b4SAlexandre Chartre 	} else {
81fcb268b4SAlexandre Chartre 		rname = strdup(cfi_reg_name(reg));
82fcb268b4SAlexandre Chartre 		TRACE(fmt, rname, cfi_reg_name(base_next), offset_next);
83fcb268b4SAlexandre Chartre 		free(rname);
84fcb268b4SAlexandre Chartre 	}
85fcb268b4SAlexandre Chartre }
86fcb268b4SAlexandre Chartre 
trace_cfi_reg_val(const char * prefix,int reg,int base_prev,int offset_prev,int base_next,int offset_next)87fcb268b4SAlexandre Chartre static void trace_cfi_reg_val(const char *prefix, int reg,
88fcb268b4SAlexandre Chartre 			      int base_prev, int offset_prev,
89fcb268b4SAlexandre Chartre 			      int base_next, int offset_next)
90fcb268b4SAlexandre Chartre {
91fcb268b4SAlexandre Chartre 	trace_cfi_reg(prefix, reg, "%1$s=%2$s%3$+d ",
92fcb268b4SAlexandre Chartre 		      base_prev, offset_prev, base_next, offset_next);
93fcb268b4SAlexandre Chartre }
94fcb268b4SAlexandre Chartre 
trace_cfi_reg_ref(const char * prefix,int reg,int base_prev,int offset_prev,int base_next,int offset_next)95fcb268b4SAlexandre Chartre static void trace_cfi_reg_ref(const char *prefix, int reg,
96fcb268b4SAlexandre Chartre 			      int base_prev, int offset_prev,
97fcb268b4SAlexandre Chartre 			      int base_next, int offset_next)
98fcb268b4SAlexandre Chartre {
99fcb268b4SAlexandre Chartre 	trace_cfi_reg(prefix, reg, "%1$s=(%2$s%3$+d) ",
100fcb268b4SAlexandre Chartre 		      base_prev, offset_prev, base_next, offset_next);
101fcb268b4SAlexandre Chartre }
102fcb268b4SAlexandre Chartre 
103fcb268b4SAlexandre Chartre #define TRACE_CFI_REG_VAL(reg, prev, next)				\
104fcb268b4SAlexandre Chartre 	trace_cfi_reg_val(NULL, reg, prev.base, prev.offset,		\
105fcb268b4SAlexandre Chartre 			  next.base, next.offset)
106fcb268b4SAlexandre Chartre 
107fcb268b4SAlexandre Chartre #define TRACE_CFI_REG_REF(reg, prev, next)				\
108fcb268b4SAlexandre Chartre 	trace_cfi_reg_ref(NULL, reg, prev.base, prev.offset,		\
109fcb268b4SAlexandre Chartre 			  next.base, next.offset)
110fcb268b4SAlexandre Chartre 
trace_insn_state(struct instruction * insn,struct insn_state * sprev,struct insn_state * snext)111fcb268b4SAlexandre Chartre void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
112fcb268b4SAlexandre Chartre 		      struct insn_state *snext)
113fcb268b4SAlexandre Chartre {
114fcb268b4SAlexandre Chartre 	struct cfi_state *cprev, *cnext;
115fcb268b4SAlexandre Chartre 	int i;
116fcb268b4SAlexandre Chartre 
117fcb268b4SAlexandre Chartre 	if (!memcmp(sprev, snext, sizeof(struct insn_state)))
118fcb268b4SAlexandre Chartre 		return;
119fcb268b4SAlexandre Chartre 
120fcb268b4SAlexandre Chartre 	cprev = &sprev->cfi;
121fcb268b4SAlexandre Chartre 	cnext = &snext->cfi;
122fcb268b4SAlexandre Chartre 
123fcb268b4SAlexandre Chartre 	disas_print_insn(stderr, objtool_disas_ctx, insn,
124fcb268b4SAlexandre Chartre 			 trace_depth - 1, "state: ");
125fcb268b4SAlexandre Chartre 
126fcb268b4SAlexandre Chartre 	/* print registers changes */
127fcb268b4SAlexandre Chartre 	TRACE_CFI_REG_VAL(CFI_CFA, cprev->cfa, cnext->cfa);
128fcb268b4SAlexandre Chartre 	for (i = 0; i < CFI_NUM_REGS; i++) {
129fcb268b4SAlexandre Chartre 		TRACE_CFI_REG_VAL(i, cprev->vals[i], cnext->vals[i]);
130fcb268b4SAlexandre Chartre 		TRACE_CFI_REG_REF(i, cprev->regs[i], cnext->regs[i]);
131fcb268b4SAlexandre Chartre 	}
132fcb268b4SAlexandre Chartre 
133fcb268b4SAlexandre Chartre 	/* print attributes changes */
134fcb268b4SAlexandre Chartre 	TRACE_CFI_ATTR_NUM(stack_size, cprev, cnext, "%d");
135fcb268b4SAlexandre Chartre 	TRACE_CFI_ATTR_BOOL(drap, cprev, cnext);
136fcb268b4SAlexandre Chartre 	if (cnext->drap) {
137fcb268b4SAlexandre Chartre 		trace_cfi_reg_val("drap", cnext->drap_reg,
138fcb268b4SAlexandre Chartre 				  cprev->drap_reg, cprev->drap_offset,
139fcb268b4SAlexandre Chartre 				  cnext->drap_reg, cnext->drap_offset);
140fcb268b4SAlexandre Chartre 	}
141fcb268b4SAlexandre Chartre 	TRACE_CFI_ATTR_BOOL(bp_scratch, cprev, cnext);
142fcb268b4SAlexandre Chartre 	TRACE_CFI_ATTR_NUM(instr, sprev, snext, "%d");
143fcb268b4SAlexandre Chartre 	TRACE_CFI_ATTR_NUM(uaccess_stack, sprev, snext, "%u");
144fcb268b4SAlexandre Chartre 
145fcb268b4SAlexandre Chartre 	TRACE("\n");
146fcb268b4SAlexandre Chartre 
147fcb268b4SAlexandre Chartre 	insn->trace = 1;
148fcb268b4SAlexandre Chartre }
149*350c7ab8SAlexandre Chartre 
trace_alt_begin(struct instruction * orig_insn,struct alternative * alt,char * alt_name)150*350c7ab8SAlexandre Chartre void trace_alt_begin(struct instruction *orig_insn, struct alternative *alt,
151*350c7ab8SAlexandre Chartre 		     char *alt_name)
152*350c7ab8SAlexandre Chartre {
153*350c7ab8SAlexandre Chartre 	struct instruction *alt_insn;
154*350c7ab8SAlexandre Chartre 	char suffix[2];
155*350c7ab8SAlexandre Chartre 
156*350c7ab8SAlexandre Chartre 	alt_insn = alt->insn;
157*350c7ab8SAlexandre Chartre 
158*350c7ab8SAlexandre Chartre 	if (alt->type == ALT_TYPE_EX_TABLE) {
159*350c7ab8SAlexandre Chartre 		/*
160*350c7ab8SAlexandre Chartre 		 * When there is an exception table then the instruction
161*350c7ab8SAlexandre Chartre 		 * at the original location is executed but it can cause
162*350c7ab8SAlexandre Chartre 		 * an exception. In that case, the execution will be
163*350c7ab8SAlexandre Chartre 		 * redirected to the alternative instruction.
164*350c7ab8SAlexandre Chartre 		 *
165*350c7ab8SAlexandre Chartre 		 * The instruction at the original location can have
166*350c7ab8SAlexandre Chartre 		 * instruction alternatives, so we just print the location
167*350c7ab8SAlexandre Chartre 		 * of the instruction that can cause the exception and
168*350c7ab8SAlexandre Chartre 		 * not the instruction itself.
169*350c7ab8SAlexandre Chartre 		 */
170*350c7ab8SAlexandre Chartre 		TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s for instruction at 0x%lx <%s+0x%lx>",
171*350c7ab8SAlexandre Chartre 				      alt_name,
172*350c7ab8SAlexandre Chartre 				      orig_insn->offset, orig_insn->sym->name,
173*350c7ab8SAlexandre Chartre 				      orig_insn->offset - orig_insn->sym->offset);
174*350c7ab8SAlexandre Chartre 	} else {
175*350c7ab8SAlexandre Chartre 		TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s", alt_name);
176*350c7ab8SAlexandre Chartre 	}
177*350c7ab8SAlexandre Chartre 
178*350c7ab8SAlexandre Chartre 	if (alt->type == ALT_TYPE_JUMP_TABLE) {
179*350c7ab8SAlexandre Chartre 		/*
180*350c7ab8SAlexandre Chartre 		 * For a jump alternative, if the default instruction is
181*350c7ab8SAlexandre Chartre 		 * a NOP then it is replaced with the jmp instruction,
182*350c7ab8SAlexandre Chartre 		 * otherwise it is replaced with a NOP instruction.
183*350c7ab8SAlexandre Chartre 		 */
184*350c7ab8SAlexandre Chartre 		trace_depth++;
185*350c7ab8SAlexandre Chartre 		if (orig_insn->type == INSN_NOP) {
186*350c7ab8SAlexandre Chartre 			suffix[0] = (orig_insn->len == 5) ? 'q' : '\0';
187*350c7ab8SAlexandre Chartre 			TRACE_ADDR(orig_insn, "jmp%-3s %lx <%s+0x%lx>", suffix,
188*350c7ab8SAlexandre Chartre 				   alt_insn->offset, alt_insn->sym->name,
189*350c7ab8SAlexandre Chartre 				   alt_insn->offset - alt_insn->sym->offset);
190*350c7ab8SAlexandre Chartre 		} else {
191*350c7ab8SAlexandre Chartre 			TRACE_ADDR(orig_insn, "nop%d", orig_insn->len);
192*350c7ab8SAlexandre Chartre 			trace_depth--;
193*350c7ab8SAlexandre Chartre 		}
194*350c7ab8SAlexandre Chartre 	}
195*350c7ab8SAlexandre Chartre }
196*350c7ab8SAlexandre Chartre 
trace_alt_end(struct instruction * orig_insn,struct alternative * alt,char * alt_name)197*350c7ab8SAlexandre Chartre void trace_alt_end(struct instruction *orig_insn, struct alternative *alt,
198*350c7ab8SAlexandre Chartre 		   char *alt_name)
199*350c7ab8SAlexandre Chartre {
200*350c7ab8SAlexandre Chartre 	if (alt->type == ALT_TYPE_JUMP_TABLE && orig_insn->type == INSN_NOP)
201*350c7ab8SAlexandre Chartre 		trace_depth--;
202*350c7ab8SAlexandre Chartre 	TRACE_ALT_INFO_NOADDR(orig_insn, "\\ ", "%s", alt_name);
203*350c7ab8SAlexandre Chartre }
204