1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | #include <linux/objtool_types.h> |
3 | #include <asm/orc_types.h> |
4 | |
5 | #include <objtool/check.h> |
6 | #include <objtool/orc.h> |
7 | #include <objtool/warn.h> |
8 | #include <objtool/endianness.h> |
9 | |
10 | int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn) |
11 | { |
12 | struct cfi_reg *bp = &cfi->regs[CFI_BP]; |
13 | |
14 | memset(orc, 0, sizeof(*orc)); |
15 | |
16 | if (!cfi) { |
17 | /* |
18 | * This is usually either unreachable nops/traps (which don't |
19 | * trigger unreachable instruction warnings), or |
20 | * STACK_FRAME_NON_STANDARD functions. |
21 | */ |
22 | orc->type = ORC_TYPE_UNDEFINED; |
23 | return 0; |
24 | } |
25 | |
26 | switch (cfi->type) { |
27 | case UNWIND_HINT_TYPE_UNDEFINED: |
28 | orc->type = ORC_TYPE_UNDEFINED; |
29 | return 0; |
30 | case UNWIND_HINT_TYPE_END_OF_STACK: |
31 | orc->type = ORC_TYPE_END_OF_STACK; |
32 | return 0; |
33 | case UNWIND_HINT_TYPE_CALL: |
34 | orc->type = ORC_TYPE_CALL; |
35 | break; |
36 | case UNWIND_HINT_TYPE_REGS: |
37 | orc->type = ORC_TYPE_REGS; |
38 | break; |
39 | case UNWIND_HINT_TYPE_REGS_PARTIAL: |
40 | orc->type = ORC_TYPE_REGS_PARTIAL; |
41 | break; |
42 | default: |
43 | WARN_INSN(insn, "unknown unwind hint type %d" , cfi->type); |
44 | return -1; |
45 | } |
46 | |
47 | orc->signal = cfi->signal; |
48 | |
49 | switch (cfi->cfa.base) { |
50 | case CFI_SP: |
51 | orc->sp_reg = ORC_REG_SP; |
52 | break; |
53 | case CFI_SP_INDIRECT: |
54 | orc->sp_reg = ORC_REG_SP_INDIRECT; |
55 | break; |
56 | case CFI_BP: |
57 | orc->sp_reg = ORC_REG_BP; |
58 | break; |
59 | case CFI_BP_INDIRECT: |
60 | orc->sp_reg = ORC_REG_BP_INDIRECT; |
61 | break; |
62 | case CFI_R10: |
63 | orc->sp_reg = ORC_REG_R10; |
64 | break; |
65 | case CFI_R13: |
66 | orc->sp_reg = ORC_REG_R13; |
67 | break; |
68 | case CFI_DI: |
69 | orc->sp_reg = ORC_REG_DI; |
70 | break; |
71 | case CFI_DX: |
72 | orc->sp_reg = ORC_REG_DX; |
73 | break; |
74 | default: |
75 | WARN_INSN(insn, "unknown CFA base reg %d" , cfi->cfa.base); |
76 | return -1; |
77 | } |
78 | |
79 | switch (bp->base) { |
80 | case CFI_UNDEFINED: |
81 | orc->bp_reg = ORC_REG_UNDEFINED; |
82 | break; |
83 | case CFI_CFA: |
84 | orc->bp_reg = ORC_REG_PREV_SP; |
85 | break; |
86 | case CFI_BP: |
87 | orc->bp_reg = ORC_REG_BP; |
88 | break; |
89 | default: |
90 | WARN_INSN(insn, "unknown BP base reg %d" , bp->base); |
91 | return -1; |
92 | } |
93 | |
94 | orc->sp_offset = cfi->cfa.offset; |
95 | orc->bp_offset = bp->offset; |
96 | |
97 | return 0; |
98 | } |
99 | |
100 | int write_orc_entry(struct elf *elf, struct section *orc_sec, |
101 | struct section *ip_sec, unsigned int idx, |
102 | struct section *insn_sec, unsigned long insn_off, |
103 | struct orc_entry *o) |
104 | { |
105 | struct orc_entry *orc; |
106 | |
107 | /* populate ORC data */ |
108 | orc = (struct orc_entry *)orc_sec->data->d_buf + idx; |
109 | memcpy(orc, o, sizeof(*orc)); |
110 | orc->sp_offset = bswap_if_needed(elf, orc->sp_offset); |
111 | orc->bp_offset = bswap_if_needed(elf, orc->bp_offset); |
112 | |
113 | /* populate reloc for ip */ |
114 | if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, |
115 | insn_sec, insn_off)) |
116 | return -1; |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | static const char *reg_name(unsigned int reg) |
122 | { |
123 | switch (reg) { |
124 | case ORC_REG_PREV_SP: |
125 | return "prevsp" ; |
126 | case ORC_REG_DX: |
127 | return "dx" ; |
128 | case ORC_REG_DI: |
129 | return "di" ; |
130 | case ORC_REG_BP: |
131 | return "bp" ; |
132 | case ORC_REG_SP: |
133 | return "sp" ; |
134 | case ORC_REG_R10: |
135 | return "r10" ; |
136 | case ORC_REG_R13: |
137 | return "r13" ; |
138 | case ORC_REG_BP_INDIRECT: |
139 | return "bp(ind)" ; |
140 | case ORC_REG_SP_INDIRECT: |
141 | return "sp(ind)" ; |
142 | default: |
143 | return "?" ; |
144 | } |
145 | } |
146 | |
147 | static const char *orc_type_name(unsigned int type) |
148 | { |
149 | switch (type) { |
150 | case ORC_TYPE_UNDEFINED: |
151 | return "(und)" ; |
152 | case ORC_TYPE_END_OF_STACK: |
153 | return "end" ; |
154 | case ORC_TYPE_CALL: |
155 | return "call" ; |
156 | case ORC_TYPE_REGS: |
157 | return "regs" ; |
158 | case ORC_TYPE_REGS_PARTIAL: |
159 | return "regs (partial)" ; |
160 | default: |
161 | return "?" ; |
162 | } |
163 | } |
164 | |
165 | static void print_reg(unsigned int reg, int offset) |
166 | { |
167 | if (reg == ORC_REG_BP_INDIRECT) |
168 | printf("(bp%+d)" , offset); |
169 | else if (reg == ORC_REG_SP_INDIRECT) |
170 | printf("(sp)%+d" , offset); |
171 | else if (reg == ORC_REG_UNDEFINED) |
172 | printf("(und)" ); |
173 | else |
174 | printf("%s%+d" , reg_name(reg), offset); |
175 | } |
176 | |
177 | void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i) |
178 | { |
179 | printf("type:%s" , orc_type_name(type: orc[i].type)); |
180 | |
181 | printf(" sp:" ); |
182 | print_reg(reg: orc[i].sp_reg, offset: bswap_if_needed(dummy_elf, orc[i].sp_offset)); |
183 | |
184 | printf(" bp:" ); |
185 | print_reg(reg: orc[i].bp_reg, offset: bswap_if_needed(dummy_elf, orc[i].bp_offset)); |
186 | |
187 | printf(" signal:%d\n" , orc[i].signal); |
188 | } |
189 | |