1 // SPDX-License-Identifier: GPL-2.0-or-later
3 #include <objtool/check.h>
4 #include <objtool/warn.h>
6 #include <asm/orc_types.h>
7 #include <linux/objtool_types.h>
10 #define EM_LOONGARCH 258
13 int arch_ftrace_match(char *name
)
15 return !strcmp(name
, "_mcount");
18 unsigned long arch_jump_destination(struct instruction
*insn
)
20 return insn
->offset
+ (insn
->immediate
<< 2);
23 unsigned long arch_dest_reloc_offset(int addend
)
28 bool arch_pc_relative_reloc(struct reloc
*reloc
)
33 bool arch_callee_saved_reg(unsigned char reg
)
38 case CFI_S0
... CFI_S8
:
45 int arch_decode_hint_reg(u8 sp_reg
, int *base
)
48 case ORC_REG_UNDEFINED
:
49 *base
= CFI_UNDEFINED
;
64 static bool is_loongarch(const struct elf
*elf
)
66 if (elf
->ehdr
.e_machine
== EM_LOONGARCH
)
69 WARN("unexpected ELF machine type %d", elf
->ehdr
.e_machine
);
74 if (!(op = calloc(1, sizeof(*op)))) \
76 else for (*ops_list = op, ops_list = &op->next; op; op = NULL)
78 static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst
,
79 struct instruction
*insn
)
81 switch (inst
.reg0i26_format
.opcode
) {
83 insn
->type
= INSN_JUMP_UNCONDITIONAL
;
84 insn
->immediate
= sign_extend64(inst
.reg0i26_format
.immediate_h
<< 16 |
85 inst
.reg0i26_format
.immediate_l
, 25);
88 insn
->type
= INSN_CALL
;
89 insn
->immediate
= sign_extend64(inst
.reg0i26_format
.immediate_h
<< 16 |
90 inst
.reg0i26_format
.immediate_l
, 25);
99 static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst
,
100 struct instruction
*insn
)
102 switch (inst
.reg1i21_format
.opcode
) {
106 insn
->type
= INSN_JUMP_CONDITIONAL
;
107 insn
->immediate
= sign_extend64(inst
.reg1i21_format
.immediate_h
<< 16 |
108 inst
.reg1i21_format
.immediate_l
, 20);
117 static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst
,
118 struct instruction
*insn
,
119 struct stack_op
**ops_list
,
122 switch (inst
.reg2i12_format
.opcode
) {
124 if ((inst
.reg2i12_format
.rd
== CFI_SP
) || (inst
.reg2i12_format
.rj
== CFI_SP
)) {
125 /* addi.d sp,sp,si12 or addi.d fp,sp,si12 or addi.d sp,fp,si12 */
126 insn
->immediate
= sign_extend64(inst
.reg2i12_format
.immediate
, 11);
128 op
->src
.type
= OP_SRC_ADD
;
129 op
->src
.reg
= inst
.reg2i12_format
.rj
;
130 op
->src
.offset
= insn
->immediate
;
131 op
->dest
.type
= OP_DEST_REG
;
132 op
->dest
.reg
= inst
.reg2i12_format
.rd
;
135 if ((inst
.reg2i12_format
.rd
== CFI_SP
) && (inst
.reg2i12_format
.rj
== CFI_FP
)) {
136 /* addi.d sp,fp,si12 */
137 struct symbol
*func
= find_func_containing(insn
->sec
, insn
->offset
);
142 func
->frame_pointer
= true;
146 if (inst
.reg2i12_format
.rj
== CFI_SP
) {
147 /* ld.d rd,sp,si12 */
148 insn
->immediate
= sign_extend64(inst
.reg2i12_format
.immediate
, 11);
150 op
->src
.type
= OP_SRC_REG_INDIRECT
;
151 op
->src
.reg
= CFI_SP
;
152 op
->src
.offset
= insn
->immediate
;
153 op
->dest
.type
= OP_DEST_REG
;
154 op
->dest
.reg
= inst
.reg2i12_format
.rd
;
159 if (inst
.reg2i12_format
.rj
== CFI_SP
) {
160 /* st.d rd,sp,si12 */
161 insn
->immediate
= sign_extend64(inst
.reg2i12_format
.immediate
, 11);
163 op
->src
.type
= OP_SRC_REG
;
164 op
->src
.reg
= inst
.reg2i12_format
.rd
;
165 op
->dest
.type
= OP_DEST_REG_INDIRECT
;
166 op
->dest
.reg
= CFI_SP
;
167 op
->dest
.offset
= insn
->immediate
;
172 if (inst
.reg2i12_format
.rd
== 0 &&
173 inst
.reg2i12_format
.rj
== 0 &&
174 inst
.reg2i12_format
.immediate
== 0)
176 insn
->type
= INSN_NOP
;
185 static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst
,
186 struct instruction
*insn
,
187 struct stack_op
**ops_list
,
190 switch (inst
.reg2i14_format
.opcode
) {
192 if (inst
.reg2i14_format
.rj
== CFI_SP
) {
193 /* ldptr.d rd,sp,si14 */
194 insn
->immediate
= sign_extend64(inst
.reg2i14_format
.immediate
, 13);
196 op
->src
.type
= OP_SRC_REG_INDIRECT
;
197 op
->src
.reg
= CFI_SP
;
198 op
->src
.offset
= insn
->immediate
;
199 op
->dest
.type
= OP_DEST_REG
;
200 op
->dest
.reg
= inst
.reg2i14_format
.rd
;
205 if (inst
.reg2i14_format
.rj
== CFI_SP
) {
206 /* stptr.d ra,sp,0 */
207 if (inst
.reg2i14_format
.rd
== LOONGARCH_GPR_RA
&&
208 inst
.reg2i14_format
.immediate
== 0)
211 /* stptr.d rd,sp,si14 */
212 insn
->immediate
= sign_extend64(inst
.reg2i14_format
.immediate
, 13);
214 op
->src
.type
= OP_SRC_REG
;
215 op
->src
.reg
= inst
.reg2i14_format
.rd
;
216 op
->dest
.type
= OP_DEST_REG_INDIRECT
;
217 op
->dest
.reg
= CFI_SP
;
218 op
->dest
.offset
= insn
->immediate
;
229 static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst
,
230 struct instruction
*insn
)
232 switch (inst
.reg2i16_format
.opcode
) {
234 if (inst
.reg2i16_format
.rd
== 0 &&
235 inst
.reg2i16_format
.rj
== CFI_RA
&&
236 inst
.reg2i16_format
.immediate
== 0) {
238 insn
->type
= INSN_RETURN
;
239 } else if (inst
.reg2i16_format
.rd
== CFI_RA
) {
240 /* jirl ra,rj,offs16 */
241 insn
->type
= INSN_CALL_DYNAMIC
;
242 } else if (inst
.reg2i16_format
.rd
== CFI_A0
&&
243 inst
.reg2i16_format
.immediate
== 0) {
246 * this is a special case in loongarch_suspend_enter,
247 * just treat it as a call instruction.
249 insn
->type
= INSN_CALL_DYNAMIC
;
250 } else if (inst
.reg2i16_format
.rd
== 0 &&
251 inst
.reg2i16_format
.immediate
== 0) {
253 insn
->type
= INSN_JUMP_DYNAMIC
;
254 } else if (inst
.reg2i16_format
.rd
== 0 &&
255 inst
.reg2i16_format
.immediate
!= 0) {
258 * this is a rare case in JUMP_VIRT_ADDR,
259 * just ignore it due to it is harmless for tracing.
263 /* jirl rd,rj,offs16 */
264 insn
->type
= INSN_JUMP_UNCONDITIONAL
;
265 insn
->immediate
= sign_extend64(inst
.reg2i16_format
.immediate
, 15);
274 insn
->type
= INSN_JUMP_CONDITIONAL
;
275 insn
->immediate
= sign_extend64(inst
.reg2i16_format
.immediate
, 15);
284 int arch_decode_instruction(struct objtool_file
*file
, const struct section
*sec
,
285 unsigned long offset
, unsigned int maxlen
,
286 struct instruction
*insn
)
288 struct stack_op
**ops_list
= &insn
->stack_ops
;
289 const struct elf
*elf
= file
->elf
;
290 struct stack_op
*op
= NULL
;
291 union loongarch_instruction inst
;
293 if (!is_loongarch(elf
))
296 if (maxlen
< LOONGARCH_INSN_SIZE
)
299 insn
->len
= LOONGARCH_INSN_SIZE
;
300 insn
->type
= INSN_OTHER
;
303 inst
= *(union loongarch_instruction
*)(sec
->data
->d_buf
+ offset
);
305 if (decode_insn_reg0i26_fomat(inst
, insn
))
307 if (decode_insn_reg1i21_fomat(inst
, insn
))
309 if (decode_insn_reg2i12_fomat(inst
, insn
, ops_list
, op
))
311 if (decode_insn_reg2i14_fomat(inst
, insn
, ops_list
, op
))
313 if (decode_insn_reg2i16_fomat(inst
, insn
))
317 insn
->type
= INSN_NOP
;
318 else if (inst
.reg0i15_format
.opcode
== break_op
) {
320 insn
->type
= INSN_BUG
;
321 } else if (inst
.reg2_format
.opcode
== ertn_op
) {
323 insn
->type
= INSN_RETURN
;
329 const char *arch_nop_insn(int len
)
333 if (len
!= LOONGARCH_INSN_SIZE
)
334 WARN("invalid NOP size: %d\n", len
);
336 nop
= LOONGARCH_INSN_NOP
;
338 return (const char *)&nop
;
341 const char *arch_ret_insn(int len
)
345 if (len
!= LOONGARCH_INSN_SIZE
)
346 WARN("invalid RET size: %d\n", len
);
348 emit_jirl((union loongarch_instruction
*)&ret
, LOONGARCH_GPR_RA
, LOONGARCH_GPR_ZERO
, 0);
350 return (const char *)&ret
;
353 void arch_initial_func_cfi_state(struct cfi_init_state
*state
)
357 for (i
= 0; i
< CFI_NUM_REGS
; i
++) {
358 state
->regs
[i
].base
= CFI_UNDEFINED
;
359 state
->regs
[i
].offset
= 0;
362 /* initial CFA (call frame address) */
363 state
->cfa
.base
= CFI_SP
;
364 state
->cfa
.offset
= 0;