1 // SPDX-License-Identifier: GPL-2.0-or-later
2 #include <string.h>
3 #include <objtool/check.h>
4 #include <objtool/warn.h>
5 #include <asm/inst.h>
6 #include <asm/orc_types.h>
7 #include <linux/objtool_types.h>
8 #include <arch/elf.h>
9
arch_ftrace_match(char * name)10 int arch_ftrace_match(char *name)
11 {
12 return !strcmp(name, "_mcount");
13 }
14
arch_jump_destination(struct instruction * insn)15 unsigned long arch_jump_destination(struct instruction *insn)
16 {
17 return insn->offset + (insn->immediate << 2);
18 }
19
arch_dest_reloc_offset(int addend)20 unsigned long arch_dest_reloc_offset(int addend)
21 {
22 return addend;
23 }
24
arch_pc_relative_reloc(struct reloc * reloc)25 bool arch_pc_relative_reloc(struct reloc *reloc)
26 {
27 return false;
28 }
29
arch_callee_saved_reg(unsigned char reg)30 bool arch_callee_saved_reg(unsigned char reg)
31 {
32 switch (reg) {
33 case CFI_RA:
34 case CFI_FP:
35 case CFI_S0 ... CFI_S8:
36 return true;
37 default:
38 return false;
39 }
40 }
41
arch_decode_hint_reg(u8 sp_reg,int * base)42 int arch_decode_hint_reg(u8 sp_reg, int *base)
43 {
44 switch (sp_reg) {
45 case ORC_REG_UNDEFINED:
46 *base = CFI_UNDEFINED;
47 break;
48 case ORC_REG_SP:
49 *base = CFI_SP;
50 break;
51 case ORC_REG_FP:
52 *base = CFI_FP;
53 break;
54 default:
55 return -1;
56 }
57
58 return 0;
59 }
60
is_loongarch(const struct elf * elf)61 static bool is_loongarch(const struct elf *elf)
62 {
63 if (elf->ehdr.e_machine == EM_LOONGARCH)
64 return true;
65
66 WARN("unexpected ELF machine type %d", elf->ehdr.e_machine);
67 return false;
68 }
69
70 #define ADD_OP(op) \
71 if (!(op = calloc(1, sizeof(*op)))) \
72 return -1; \
73 else for (*ops_list = op, ops_list = &op->next; op; op = NULL)
74
decode_insn_reg0i26_fomat(union loongarch_instruction inst,struct instruction * insn)75 static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst,
76 struct instruction *insn)
77 {
78 switch (inst.reg0i26_format.opcode) {
79 case b_op:
80 insn->type = INSN_JUMP_UNCONDITIONAL;
81 insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
82 inst.reg0i26_format.immediate_l, 25);
83 break;
84 case bl_op:
85 insn->type = INSN_CALL;
86 insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
87 inst.reg0i26_format.immediate_l, 25);
88 break;
89 default:
90 return false;
91 }
92
93 return true;
94 }
95
decode_insn_reg1i21_fomat(union loongarch_instruction inst,struct instruction * insn)96 static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst,
97 struct instruction *insn)
98 {
99 switch (inst.reg1i21_format.opcode) {
100 case beqz_op:
101 case bnez_op:
102 case bceqz_op:
103 insn->type = INSN_JUMP_CONDITIONAL;
104 insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 |
105 inst.reg1i21_format.immediate_l, 20);
106 break;
107 default:
108 return false;
109 }
110
111 return true;
112 }
113
decode_insn_reg2i12_fomat(union loongarch_instruction inst,struct instruction * insn,struct stack_op ** ops_list,struct stack_op * op)114 static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst,
115 struct instruction *insn,
116 struct stack_op **ops_list,
117 struct stack_op *op)
118 {
119 switch (inst.reg2i12_format.opcode) {
120 case addid_op:
121 if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) {
122 /* addi.d sp,sp,si12 or addi.d fp,sp,si12 or addi.d sp,fp,si12 */
123 insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
124 ADD_OP(op) {
125 op->src.type = OP_SRC_ADD;
126 op->src.reg = inst.reg2i12_format.rj;
127 op->src.offset = insn->immediate;
128 op->dest.type = OP_DEST_REG;
129 op->dest.reg = inst.reg2i12_format.rd;
130 }
131 }
132 if ((inst.reg2i12_format.rd == CFI_SP) && (inst.reg2i12_format.rj == CFI_FP)) {
133 /* addi.d sp,fp,si12 */
134 struct symbol *func = find_func_containing(insn->sec, insn->offset);
135
136 if (!func)
137 return false;
138
139 func->frame_pointer = true;
140 }
141 break;
142 case ldd_op:
143 if (inst.reg2i12_format.rj == CFI_SP) {
144 /* ld.d rd,sp,si12 */
145 insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
146 ADD_OP(op) {
147 op->src.type = OP_SRC_REG_INDIRECT;
148 op->src.reg = CFI_SP;
149 op->src.offset = insn->immediate;
150 op->dest.type = OP_DEST_REG;
151 op->dest.reg = inst.reg2i12_format.rd;
152 }
153 }
154 break;
155 case std_op:
156 if (inst.reg2i12_format.rj == CFI_SP) {
157 /* st.d rd,sp,si12 */
158 insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
159 ADD_OP(op) {
160 op->src.type = OP_SRC_REG;
161 op->src.reg = inst.reg2i12_format.rd;
162 op->dest.type = OP_DEST_REG_INDIRECT;
163 op->dest.reg = CFI_SP;
164 op->dest.offset = insn->immediate;
165 }
166 }
167 break;
168 case andi_op:
169 if (inst.reg2i12_format.rd == 0 &&
170 inst.reg2i12_format.rj == 0 &&
171 inst.reg2i12_format.immediate == 0)
172 /* andi r0,r0,0 */
173 insn->type = INSN_NOP;
174 break;
175 default:
176 return false;
177 }
178
179 return true;
180 }
181
decode_insn_reg2i14_fomat(union loongarch_instruction inst,struct instruction * insn,struct stack_op ** ops_list,struct stack_op * op)182 static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst,
183 struct instruction *insn,
184 struct stack_op **ops_list,
185 struct stack_op *op)
186 {
187 switch (inst.reg2i14_format.opcode) {
188 case ldptrd_op:
189 if (inst.reg2i14_format.rj == CFI_SP) {
190 /* ldptr.d rd,sp,si14 */
191 insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
192 ADD_OP(op) {
193 op->src.type = OP_SRC_REG_INDIRECT;
194 op->src.reg = CFI_SP;
195 op->src.offset = insn->immediate;
196 op->dest.type = OP_DEST_REG;
197 op->dest.reg = inst.reg2i14_format.rd;
198 }
199 }
200 break;
201 case stptrd_op:
202 if (inst.reg2i14_format.rj == CFI_SP) {
203 /* stptr.d ra,sp,0 */
204 if (inst.reg2i14_format.rd == LOONGARCH_GPR_RA &&
205 inst.reg2i14_format.immediate == 0)
206 break;
207
208 /* stptr.d rd,sp,si14 */
209 insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
210 ADD_OP(op) {
211 op->src.type = OP_SRC_REG;
212 op->src.reg = inst.reg2i14_format.rd;
213 op->dest.type = OP_DEST_REG_INDIRECT;
214 op->dest.reg = CFI_SP;
215 op->dest.offset = insn->immediate;
216 }
217 }
218 break;
219 default:
220 return false;
221 }
222
223 return true;
224 }
225
decode_insn_reg2i16_fomat(union loongarch_instruction inst,struct instruction * insn)226 static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst,
227 struct instruction *insn)
228 {
229 switch (inst.reg2i16_format.opcode) {
230 case jirl_op:
231 if (inst.reg2i16_format.rd == 0 &&
232 inst.reg2i16_format.rj == CFI_RA &&
233 inst.reg2i16_format.immediate == 0) {
234 /* jirl r0,ra,0 */
235 insn->type = INSN_RETURN;
236 } else if (inst.reg2i16_format.rd == CFI_RA) {
237 /* jirl ra,rj,offs16 */
238 insn->type = INSN_CALL_DYNAMIC;
239 } else if (inst.reg2i16_format.rd == CFI_A0 &&
240 inst.reg2i16_format.immediate == 0) {
241 /*
242 * jirl a0,t0,0
243 * this is a special case in loongarch_suspend_enter,
244 * just treat it as a call instruction.
245 */
246 insn->type = INSN_CALL_DYNAMIC;
247 } else if (inst.reg2i16_format.rd == 0 &&
248 inst.reg2i16_format.immediate == 0) {
249 /* jirl r0,rj,0 */
250 insn->type = INSN_JUMP_DYNAMIC;
251 } else if (inst.reg2i16_format.rd == 0 &&
252 inst.reg2i16_format.immediate != 0) {
253 /*
254 * jirl r0,t0,12
255 * this is a rare case in JUMP_VIRT_ADDR,
256 * just ignore it due to it is harmless for tracing.
257 */
258 break;
259 } else {
260 /* jirl rd,rj,offs16 */
261 insn->type = INSN_JUMP_UNCONDITIONAL;
262 insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
263 }
264 break;
265 case beq_op:
266 case bne_op:
267 case blt_op:
268 case bge_op:
269 case bltu_op:
270 case bgeu_op:
271 insn->type = INSN_JUMP_CONDITIONAL;
272 insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
273 break;
274 default:
275 return false;
276 }
277
278 return true;
279 }
280
arch_decode_instruction(struct objtool_file * file,const struct section * sec,unsigned long offset,unsigned int maxlen,struct instruction * insn)281 int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
282 unsigned long offset, unsigned int maxlen,
283 struct instruction *insn)
284 {
285 struct stack_op **ops_list = &insn->stack_ops;
286 const struct elf *elf = file->elf;
287 struct stack_op *op = NULL;
288 union loongarch_instruction inst;
289
290 if (!is_loongarch(elf))
291 return -1;
292
293 if (maxlen < LOONGARCH_INSN_SIZE)
294 return 0;
295
296 insn->len = LOONGARCH_INSN_SIZE;
297 insn->type = INSN_OTHER;
298 insn->immediate = 0;
299
300 inst = *(union loongarch_instruction *)(sec->data->d_buf + offset);
301
302 if (decode_insn_reg0i26_fomat(inst, insn))
303 return 0;
304 if (decode_insn_reg1i21_fomat(inst, insn))
305 return 0;
306 if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op))
307 return 0;
308 if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op))
309 return 0;
310 if (decode_insn_reg2i16_fomat(inst, insn))
311 return 0;
312
313 if (inst.word == 0)
314 insn->type = INSN_NOP;
315 else if (inst.reg0i15_format.opcode == break_op) {
316 /* break */
317 insn->type = INSN_BUG;
318 } else if (inst.reg2_format.opcode == ertn_op) {
319 /* ertn */
320 insn->type = INSN_RETURN;
321 }
322
323 return 0;
324 }
325
arch_nop_insn(int len)326 const char *arch_nop_insn(int len)
327 {
328 static u32 nop;
329
330 if (len != LOONGARCH_INSN_SIZE)
331 WARN("invalid NOP size: %d\n", len);
332
333 nop = LOONGARCH_INSN_NOP;
334
335 return (const char *)&nop;
336 }
337
arch_ret_insn(int len)338 const char *arch_ret_insn(int len)
339 {
340 static u32 ret;
341
342 if (len != LOONGARCH_INSN_SIZE)
343 WARN("invalid RET size: %d\n", len);
344
345 emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0);
346
347 return (const char *)&ret;
348 }
349
arch_initial_func_cfi_state(struct cfi_init_state * state)350 void arch_initial_func_cfi_state(struct cfi_init_state *state)
351 {
352 int i;
353
354 for (i = 0; i < CFI_NUM_REGS; i++) {
355 state->regs[i].base = CFI_UNDEFINED;
356 state->regs[i].offset = 0;
357 }
358
359 /* initial CFA (call frame address) */
360 state->cfa.base = CFI_SP;
361 state->cfa.offset = 0;
362 }
363
arch_reloc_size(struct reloc * reloc)364 unsigned int arch_reloc_size(struct reloc *reloc)
365 {
366 switch (reloc_type(reloc)) {
367 case R_LARCH_32:
368 case R_LARCH_32_PCREL:
369 return 4;
370 default:
371 return 8;
372 }
373 }
374
arch_jump_table_sym_offset(struct reloc * reloc,struct reloc * table)375 unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)
376 {
377 switch (reloc_type(reloc)) {
378 case R_LARCH_32_PCREL:
379 case R_LARCH_64_PCREL:
380 return reloc->sym->offset + reloc_addend(reloc) -
381 (reloc_offset(reloc) - reloc_offset(table));
382 default:
383 return reloc->sym->offset + reloc_addend(reloc);
384 }
385 }
386