1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2025, Oracle and/or its affiliates.
*/
#include <objtool/trace.h>
bool trace;
int trace_depth;
/*
* Macros to trace CFI state attributes changes.
*/
#define TRACE_CFI_ATTR(attr, prev, next, fmt, ...) \
({ \
if ((prev)->attr != (next)->attr) \
TRACE("%s=" fmt " ", #attr, __VA_ARGS__); \
})
#define TRACE_CFI_ATTR_BOOL(attr, prev, next) \
TRACE_CFI_ATTR(attr, prev, next, \
"%s", (next)->attr ? "true" : "false")
#define TRACE_CFI_ATTR_NUM(attr, prev, next, fmt) \
TRACE_CFI_ATTR(attr, prev, next, fmt, (next)->attr)
#define CFI_REG_NAME_MAXLEN 16
/*
* Return the name of a register. Note that the same static buffer
* is returned if the name is dynamically generated.
*/
static const char *cfi_reg_name(unsigned int reg)
{
static char rname_buffer[CFI_REG_NAME_MAXLEN];
switch (reg) {
case CFI_UNDEFINED:
return "<undefined>";
case CFI_CFA:
return "cfa";
case CFI_SP_INDIRECT:
return "(sp)";
case CFI_BP_INDIRECT:
return "(bp)";
}
if (snprintf(rname_buffer, CFI_REG_NAME_MAXLEN, "r%d", reg) == -1)
return "<error>";
return (const char *)rname_buffer;
}
/*
* Functions and macros to trace CFI registers changes.
*/
static void trace_cfi_reg(const char *prefix, int reg, const char *fmt,
int base_prev, int offset_prev,
int base_next, int offset_next)
{
char *rname;
if (base_prev == base_next && offset_prev == offset_next)
return;
if (prefix)
TRACE("%s:", prefix);
if (base_next == CFI_UNDEFINED) {
TRACE("%1$s=<undef> ", cfi_reg_name(reg));
} else {
rname = strdup(cfi_reg_name(reg));
TRACE(fmt, rname, cfi_reg_name(base_next), offset_next);
free(rname);
}
}
static void trace_cfi_reg_val(const char *prefix, int reg,
int base_prev, int offset_prev,
int base_next, int offset_next)
{
trace_cfi_reg(prefix, reg, "%1$s=%2$s%3$+d ",
base_prev, offset_prev, base_next, offset_next);
}
static void trace_cfi_reg_ref(const char *prefix, int reg,
int base_prev, int offset_prev,
int base_next, int offset_next)
{
trace_cfi_reg(prefix, reg, "%1$s=(%2$s%3$+d) ",
base_prev, offset_prev, base_next, offset_next);
}
#define TRACE_CFI_REG_VAL(reg, prev, next) \
trace_cfi_reg_val(NULL, reg, prev.base, prev.offset, \
next.base, next.offset)
#define TRACE_CFI_REG_REF(reg, prev, next) \
trace_cfi_reg_ref(NULL, reg, prev.base, prev.offset, \
next.base, next.offset)
void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
struct insn_state *snext)
{
struct cfi_state *cprev, *cnext;
int i;
if (!memcmp(sprev, snext, sizeof(struct insn_state)))
return;
cprev = &sprev->cfi;
cnext = &snext->cfi;
disas_print_insn(stderr, objtool_disas_ctx, insn,
trace_depth - 1, "state: ");
/* print registers changes */
TRACE_CFI_REG_VAL(CFI_CFA, cprev->cfa, cnext->cfa);
for (i = 0; i < CFI_NUM_REGS; i++) {
TRACE_CFI_REG_VAL(i, cprev->vals[i], cnext->vals[i]);
TRACE_CFI_REG_REF(i, cprev->regs[i], cnext->regs[i]);
}
/* print attributes changes */
TRACE_CFI_ATTR_NUM(stack_size, cprev, cnext, "%d");
TRACE_CFI_ATTR_BOOL(drap, cprev, cnext);
if (cnext->drap) {
trace_cfi_reg_val("drap", cnext->drap_reg,
cprev->drap_reg, cprev->drap_offset,
cnext->drap_reg, cnext->drap_offset);
}
TRACE_CFI_ATTR_BOOL(bp_scratch, cprev, cnext);
TRACE_CFI_ATTR_NUM(instr, sprev, snext, "%d");
TRACE_CFI_ATTR_NUM(uaccess_stack, sprev, snext, "%u");
TRACE("\n");
insn->trace = 1;
}
|