xref: /linux/tools/objtool/arch/loongarch/decode.c (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
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 
10 int arch_ftrace_match(char *name)
11 {
12 	return !strcmp(name, "_mcount");
13 }
14 
15 unsigned long arch_jump_destination(struct instruction *insn)
16 {
17 	return insn->offset + (insn->immediate << 2);
18 }
19 
20 unsigned long arch_dest_reloc_offset(int addend)
21 {
22 	return addend;
23 }
24 
25 bool arch_pc_relative_reloc(struct reloc *reloc)
26 {
27 	return false;
28 }
29 
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 
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 
61 static bool is_loongarch(const struct elf *elf)
62 {
63 	if (elf->ehdr.e_machine == EM_LOONGARCH)
64 		return true;
65 
66 	ERROR("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 
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 
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 
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 
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 
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 
281 static bool decode_insn_reg3_fomat(union loongarch_instruction inst,
282 				   struct instruction *insn)
283 {
284 	switch (inst.reg3_format.opcode) {
285 	case amswapw_op:
286 		if (inst.reg3_format.rd == LOONGARCH_GPR_ZERO &&
287 		    inst.reg3_format.rk == LOONGARCH_GPR_RA &&
288 		    inst.reg3_format.rj == LOONGARCH_GPR_ZERO) {
289 			/* amswap.w $zero, $ra, $zero */
290 			insn->type = INSN_BUG;
291 		}
292 		break;
293 	default:
294 		return false;
295 	}
296 
297 	return true;
298 }
299 
300 int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
301 			    unsigned long offset, unsigned int maxlen,
302 			    struct instruction *insn)
303 {
304 	struct stack_op **ops_list = &insn->stack_ops;
305 	const struct elf *elf = file->elf;
306 	struct stack_op *op = NULL;
307 	union loongarch_instruction inst;
308 
309 	if (!is_loongarch(elf))
310 		return -1;
311 
312 	if (maxlen < LOONGARCH_INSN_SIZE)
313 		return 0;
314 
315 	insn->len = LOONGARCH_INSN_SIZE;
316 	insn->type = INSN_OTHER;
317 	insn->immediate = 0;
318 
319 	inst = *(union loongarch_instruction *)(sec->data->d_buf + offset);
320 
321 	if (decode_insn_reg0i26_fomat(inst, insn))
322 		return 0;
323 	if (decode_insn_reg1i21_fomat(inst, insn))
324 		return 0;
325 	if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op))
326 		return 0;
327 	if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op))
328 		return 0;
329 	if (decode_insn_reg2i16_fomat(inst, insn))
330 		return 0;
331 	if (decode_insn_reg3_fomat(inst, insn))
332 		return 0;
333 
334 	if (inst.word == 0) {
335 		/* andi $zero, $zero, 0x0 */
336 		insn->type = INSN_NOP;
337 	} else if (inst.reg0i15_format.opcode == break_op &&
338 		   inst.reg0i15_format.immediate == 0x0) {
339 		/* break 0x0 */
340 		insn->type = INSN_TRAP;
341 	} else if (inst.reg0i15_format.opcode == break_op &&
342 		   inst.reg0i15_format.immediate == 0x1) {
343 		/* break 0x1 */
344 		insn->type = INSN_BUG;
345 	} else if (inst.reg2_format.opcode == ertn_op) {
346 		/* ertn */
347 		insn->type = INSN_RETURN;
348 	}
349 
350 	return 0;
351 }
352 
353 const char *arch_nop_insn(int len)
354 {
355 	static u32 nop;
356 
357 	if (len != LOONGARCH_INSN_SIZE) {
358 		ERROR("invalid NOP size: %d\n", len);
359 		return NULL;
360 	}
361 
362 	nop = LOONGARCH_INSN_NOP;
363 
364 	return (const char *)&nop;
365 }
366 
367 const char *arch_ret_insn(int len)
368 {
369 	static u32 ret;
370 
371 	if (len != LOONGARCH_INSN_SIZE) {
372 		ERROR("invalid RET size: %d\n", len);
373 		return NULL;
374 	}
375 
376 	emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0);
377 
378 	return (const char *)&ret;
379 }
380 
381 void arch_initial_func_cfi_state(struct cfi_init_state *state)
382 {
383 	int i;
384 
385 	for (i = 0; i < CFI_NUM_REGS; i++) {
386 		state->regs[i].base = CFI_UNDEFINED;
387 		state->regs[i].offset = 0;
388 	}
389 
390 	/* initial CFA (call frame address) */
391 	state->cfa.base = CFI_SP;
392 	state->cfa.offset = 0;
393 }
394 
395 unsigned int arch_reloc_size(struct reloc *reloc)
396 {
397 	switch (reloc_type(reloc)) {
398 	case R_LARCH_32:
399 	case R_LARCH_32_PCREL:
400 		return 4;
401 	default:
402 		return 8;
403 	}
404 }
405 
406 unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)
407 {
408 	switch (reloc_type(reloc)) {
409 	case R_LARCH_32_PCREL:
410 	case R_LARCH_64_PCREL:
411 		return reloc->sym->offset + reloc_addend(reloc) -
412 		       (reloc_offset(reloc) - reloc_offset(table));
413 	default:
414 		return reloc->sym->offset + reloc_addend(reloc);
415 	}
416 }
417