Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/objtool/arch/loongarch/decode.c
26285 views
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
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
326
const char *arch_nop_insn(int len)
327
{
328
static u32 nop;
329
330
if (len != LOONGARCH_INSN_SIZE) {
331
ERROR("invalid NOP size: %d\n", len);
332
return NULL;
333
}
334
335
nop = LOONGARCH_INSN_NOP;
336
337
return (const char *)&nop;
338
}
339
340
const char *arch_ret_insn(int len)
341
{
342
static u32 ret;
343
344
if (len != LOONGARCH_INSN_SIZE) {
345
ERROR("invalid RET size: %d\n", len);
346
return NULL;
347
}
348
349
emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0);
350
351
return (const char *)&ret;
352
}
353
354
void arch_initial_func_cfi_state(struct cfi_init_state *state)
355
{
356
int i;
357
358
for (i = 0; i < CFI_NUM_REGS; i++) {
359
state->regs[i].base = CFI_UNDEFINED;
360
state->regs[i].offset = 0;
361
}
362
363
/* initial CFA (call frame address) */
364
state->cfa.base = CFI_SP;
365
state->cfa.offset = 0;
366
}
367
368
unsigned int arch_reloc_size(struct reloc *reloc)
369
{
370
switch (reloc_type(reloc)) {
371
case R_LARCH_32:
372
case R_LARCH_32_PCREL:
373
return 4;
374
default:
375
return 8;
376
}
377
}
378
379
unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)
380
{
381
switch (reloc_type(reloc)) {
382
case R_LARCH_32_PCREL:
383
case R_LARCH_64_PCREL:
384
return reloc->sym->offset + reloc_addend(reloc) -
385
(reloc_offset(reloc) - reloc_offset(table));
386
default:
387
return reloc->sym->offset + reloc_addend(reloc);
388
}
389
}
390
391