File Index
Files and directories
Sign in
FILE

decode.c

File preview with syntax highlighting.

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