~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/objtool/arch/loongarch/decode.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
  9 #ifndef EM_LOONGARCH
 10 #define EM_LOONGARCH    258
 11 #endif
 12 
 13 int arch_ftrace_match(char *name)
 14 {
 15         return !strcmp(name, "_mcount");
 16 }
 17 
 18 unsigned long arch_jump_destination(struct instruction *insn)
 19 {
 20         return insn->offset + (insn->immediate << 2);
 21 }
 22 
 23 unsigned long arch_dest_reloc_offset(int addend)
 24 {
 25         return addend;
 26 }
 27 
 28 bool arch_pc_relative_reloc(struct reloc *reloc)
 29 {
 30         return false;
 31 }
 32 
 33 bool arch_callee_saved_reg(unsigned char reg)
 34 {
 35         switch (reg) {
 36         case CFI_RA:
 37         case CFI_FP:
 38         case CFI_S0 ... CFI_S8:
 39                 return true;
 40         default:
 41                 return false;
 42         }
 43 }
 44 
 45 int arch_decode_hint_reg(u8 sp_reg, int *base)
 46 {
 47         switch (sp_reg) {
 48         case ORC_REG_UNDEFINED:
 49                 *base = CFI_UNDEFINED;
 50                 break;
 51         case ORC_REG_SP:
 52                 *base = CFI_SP;
 53                 break;
 54         case ORC_REG_FP:
 55                 *base = CFI_FP;
 56                 break;
 57         default:
 58                 return -1;
 59         }
 60 
 61         return 0;
 62 }
 63 
 64 static bool is_loongarch(const struct elf *elf)
 65 {
 66         if (elf->ehdr.e_machine == EM_LOONGARCH)
 67                 return true;
 68 
 69         WARN("unexpected ELF machine type %d", elf->ehdr.e_machine);
 70         return false;
 71 }
 72 
 73 #define ADD_OP(op) \
 74         if (!(op = calloc(1, sizeof(*op)))) \
 75                 return -1; \
 76         else for (*ops_list = op, ops_list = &op->next; op; op = NULL)
 77 
 78 static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst,
 79                                       struct instruction *insn)
 80 {
 81         switch (inst.reg0i26_format.opcode) {
 82         case b_op:
 83                 insn->type = INSN_JUMP_UNCONDITIONAL;
 84                 insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
 85                                                 inst.reg0i26_format.immediate_l, 25);
 86                 break;
 87         case bl_op:
 88                 insn->type = INSN_CALL;
 89                 insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
 90                                                 inst.reg0i26_format.immediate_l, 25);
 91                 break;
 92         default:
 93                 return false;
 94         }
 95 
 96         return true;
 97 }
 98 
 99 static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst,
100                                       struct instruction *insn)
101 {
102         switch (inst.reg1i21_format.opcode) {
103         case beqz_op:
104         case bnez_op:
105         case bceqz_op:
106                 insn->type = INSN_JUMP_CONDITIONAL;
107                 insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 |
108                                                 inst.reg1i21_format.immediate_l, 20);
109                 break;
110         default:
111                 return false;
112         }
113 
114         return true;
115 }
116 
117 static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst,
118                                       struct instruction *insn,
119                                       struct stack_op **ops_list,
120                                       struct stack_op *op)
121 {
122         switch (inst.reg2i12_format.opcode) {
123         case addid_op:
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);
127                         ADD_OP(op) {
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;
133                         }
134                 }
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);
138 
139                         if (!func)
140                                 return false;
141 
142                         func->frame_pointer = true;
143                 }
144                 break;
145         case ldd_op:
146                 if (inst.reg2i12_format.rj == CFI_SP) {
147                         /* ld.d rd,sp,si12 */
148                         insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
149                         ADD_OP(op) {
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;
155                         }
156                 }
157                 break;
158         case std_op:
159                 if (inst.reg2i12_format.rj == CFI_SP) {
160                         /* st.d rd,sp,si12 */
161                         insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
162                         ADD_OP(op) {
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;
168                         }
169                 }
170                 break;
171         case andi_op:
172                 if (inst.reg2i12_format.rd == 0 &&
173                     inst.reg2i12_format.rj == 0 &&
174                     inst.reg2i12_format.immediate == 0)
175                         /* andi r0,r0,0 */
176                         insn->type = INSN_NOP;
177                 break;
178         default:
179                 return false;
180         }
181 
182         return true;
183 }
184 
185 static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst,
186                                       struct instruction *insn,
187                                       struct stack_op **ops_list,
188                                       struct stack_op *op)
189 {
190         switch (inst.reg2i14_format.opcode) {
191         case ldptrd_op:
192                 if (inst.reg2i14_format.rj == CFI_SP) {
193                         /* ldptr.d rd,sp,si14 */
194                         insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
195                         ADD_OP(op) {
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;
201                         }
202                 }
203                 break;
204         case stptrd_op:
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)
209                                 break;
210 
211                         /* stptr.d rd,sp,si14 */
212                         insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
213                         ADD_OP(op) {
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;
219                         }
220                 }
221                 break;
222         default:
223                 return false;
224         }
225 
226         return true;
227 }
228 
229 static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst,
230                                       struct instruction *insn)
231 {
232         switch (inst.reg2i16_format.opcode) {
233         case jirl_op:
234                 if (inst.reg2i16_format.rd == 0 &&
235                     inst.reg2i16_format.rj == CFI_RA &&
236                     inst.reg2i16_format.immediate == 0) {
237                         /* jirl r0,ra,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) {
244                         /*
245                          * jirl a0,t0,0
246                          * this is a special case in loongarch_suspend_enter,
247                          * just treat it as a call instruction.
248                          */
249                         insn->type = INSN_CALL_DYNAMIC;
250                 } else if (inst.reg2i16_format.rd == 0 &&
251                            inst.reg2i16_format.immediate == 0) {
252                         /* jirl r0,rj,0 */
253                         insn->type = INSN_JUMP_DYNAMIC;
254                 } else if (inst.reg2i16_format.rd == 0 &&
255                            inst.reg2i16_format.immediate != 0) {
256                         /*
257                          * jirl r0,t0,12
258                          * this is a rare case in JUMP_VIRT_ADDR,
259                          * just ignore it due to it is harmless for tracing.
260                          */
261                         break;
262                 } else {
263                         /* jirl rd,rj,offs16 */
264                         insn->type = INSN_JUMP_UNCONDITIONAL;
265                         insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
266                 }
267                 break;
268         case beq_op:
269         case bne_op:
270         case blt_op:
271         case bge_op:
272         case bltu_op:
273         case bgeu_op:
274                 insn->type = INSN_JUMP_CONDITIONAL;
275                 insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
276                 break;
277         default:
278                 return false;
279         }
280 
281         return true;
282 }
283 
284 int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
285                             unsigned long offset, unsigned int maxlen,
286                             struct instruction *insn)
287 {
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;
292 
293         if (!is_loongarch(elf))
294                 return -1;
295 
296         if (maxlen < LOONGARCH_INSN_SIZE)
297                 return 0;
298 
299         insn->len = LOONGARCH_INSN_SIZE;
300         insn->type = INSN_OTHER;
301         insn->immediate = 0;
302 
303         inst = *(union loongarch_instruction *)(sec->data->d_buf + offset);
304 
305         if (decode_insn_reg0i26_fomat(inst, insn))
306                 return 0;
307         if (decode_insn_reg1i21_fomat(inst, insn))
308                 return 0;
309         if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op))
310                 return 0;
311         if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op))
312                 return 0;
313         if (decode_insn_reg2i16_fomat(inst, insn))
314                 return 0;
315 
316         if (inst.word == 0)
317                 insn->type = INSN_NOP;
318         else if (inst.reg0i15_format.opcode == break_op) {
319                 /* break */
320                 insn->type = INSN_BUG;
321         } else if (inst.reg2_format.opcode == ertn_op) {
322                 /* ertn */
323                 insn->type = INSN_RETURN;
324         }
325 
326         return 0;
327 }
328 
329 const char *arch_nop_insn(int len)
330 {
331         static u32 nop;
332 
333         if (len != LOONGARCH_INSN_SIZE)
334                 WARN("invalid NOP size: %d\n", len);
335 
336         nop = LOONGARCH_INSN_NOP;
337 
338         return (const char *)&nop;
339 }
340 
341 const char *arch_ret_insn(int len)
342 {
343         static u32 ret;
344 
345         if (len != LOONGARCH_INSN_SIZE)
346                 WARN("invalid RET size: %d\n", len);
347 
348         emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0);
349 
350         return (const char *)&ret;
351 }
352 
353 void arch_initial_func_cfi_state(struct cfi_init_state *state)
354 {
355         int i;
356 
357         for (i = 0; i < CFI_NUM_REGS; i++) {
358                 state->regs[i].base = CFI_UNDEFINED;
359                 state->regs[i].offset = 0;
360         }
361 
362         /* initial CFA (call frame address) */
363         state->cfa.base = CFI_SP;
364         state->cfa.offset = 0;
365 }
366 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php