Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/objtool/arch/loongarch/decode.c
49628 views
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
11
const 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
22
int arch_ftrace_match(const char *name)
23
{
24
return !strcmp(name, "_mcount");
25
}
26
27
unsigned long arch_jump_destination(struct instruction *insn)
28
{
29
return insn->offset + (insn->immediate << 2);
30
}
31
32
s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
33
{
34
return reloc_addend(reloc);
35
}
36
37
bool arch_pc_relative_reloc(struct reloc *reloc)
38
{
39
return false;
40
}
41
42
bool 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
54
int 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
73
static 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
87
static 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
108
static 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
126
static 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
194
static 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
238
static 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
293
static 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
312
int 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
365
const 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
379
const 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
393
void 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
407
unsigned 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
418
unsigned 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
432
int 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 */
440
441