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