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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.