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 int arch_ftrace_match(const char *name) 12 { 13 return !strcmp(name, "_mcount"); 14 } 15 16 unsigned long arch_jump_destination(struct instruction *insn) 17 { 18 return insn->offset + (insn->immediate << 2); 19 } 20 21 s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc) 22 { 23 return reloc_addend(reloc); 24 } 25 26 bool arch_pc_relative_reloc(struct reloc *reloc) 27 { 28 return false; 29 } 30 31 bool arch_callee_saved_reg(unsigned char reg) 32 { 33 switch (reg) { 34 case CFI_RA: 35 case CFI_FP: 36 case CFI_S0 ... CFI_S8: 37 return true; 38 default: 39 return false; 40 } 41 } 42 43 int arch_decode_hint_reg(u8 sp_reg, int *base) 44 { 45 switch (sp_reg) { 46 case ORC_REG_UNDEFINED: 47 *base = CFI_UNDEFINED; 48 break; 49 case ORC_REG_SP: 50 *base = CFI_SP; 51 break; 52 case ORC_REG_FP: 53 *base = CFI_FP; 54 break; 55 default: 56 return -1; 57 } 58 59 return 0; 60 } 61 62 static bool is_loongarch(const struct elf *elf) 63 { 64 if (elf->ehdr.e_machine == EM_LOONGARCH) 65 return true; 66 67 ERROR("unexpected ELF machine type %d", elf->ehdr.e_machine); 68 return false; 69 } 70 71 #define ADD_OP(op) \ 72 if (!(op = calloc(1, sizeof(*op)))) \ 73 return -1; \ 74 else for (*ops_list = op, ops_list = &op->next; op; op = NULL) 75 76 static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst, 77 struct instruction *insn) 78 { 79 switch (inst.reg0i26_format.opcode) { 80 case b_op: 81 insn->type = INSN_JUMP_UNCONDITIONAL; 82 insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 | 83 inst.reg0i26_format.immediate_l, 25); 84 break; 85 case bl_op: 86 insn->type = INSN_CALL; 87 insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 | 88 inst.reg0i26_format.immediate_l, 25); 89 break; 90 default: 91 return false; 92 } 93 94 return true; 95 } 96 97 static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst, 98 struct instruction *insn) 99 { 100 switch (inst.reg1i21_format.opcode) { 101 case beqz_op: 102 case bnez_op: 103 case bceqz_op: 104 insn->type = INSN_JUMP_CONDITIONAL; 105 insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 | 106 inst.reg1i21_format.immediate_l, 20); 107 break; 108 default: 109 return false; 110 } 111 112 return true; 113 } 114 115 static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst, 116 struct instruction *insn, 117 struct stack_op **ops_list, 118 struct stack_op *op) 119 { 120 switch (inst.reg2i12_format.opcode) { 121 case addid_op: 122 if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) { 123 /* addi.d sp,sp,si12 or addi.d fp,sp,si12 or addi.d sp,fp,si12 */ 124 insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); 125 ADD_OP(op) { 126 op->src.type = OP_SRC_ADD; 127 op->src.reg = inst.reg2i12_format.rj; 128 op->src.offset = insn->immediate; 129 op->dest.type = OP_DEST_REG; 130 op->dest.reg = inst.reg2i12_format.rd; 131 } 132 } 133 if ((inst.reg2i12_format.rd == CFI_SP) && (inst.reg2i12_format.rj == CFI_FP)) { 134 /* addi.d sp,fp,si12 */ 135 struct symbol *func = find_func_containing(insn->sec, insn->offset); 136 137 if (!func) 138 return false; 139 140 func->frame_pointer = true; 141 } 142 break; 143 case ldd_op: 144 if (inst.reg2i12_format.rj == CFI_SP) { 145 /* ld.d rd,sp,si12 */ 146 insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); 147 ADD_OP(op) { 148 op->src.type = OP_SRC_REG_INDIRECT; 149 op->src.reg = CFI_SP; 150 op->src.offset = insn->immediate; 151 op->dest.type = OP_DEST_REG; 152 op->dest.reg = inst.reg2i12_format.rd; 153 } 154 } 155 break; 156 case std_op: 157 if (inst.reg2i12_format.rj == CFI_SP) { 158 /* st.d rd,sp,si12 */ 159 insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); 160 ADD_OP(op) { 161 op->src.type = OP_SRC_REG; 162 op->src.reg = inst.reg2i12_format.rd; 163 op->dest.type = OP_DEST_REG_INDIRECT; 164 op->dest.reg = CFI_SP; 165 op->dest.offset = insn->immediate; 166 } 167 } 168 break; 169 case andi_op: 170 if (inst.reg2i12_format.rd == 0 && 171 inst.reg2i12_format.rj == 0 && 172 inst.reg2i12_format.immediate == 0) 173 /* andi r0,r0,0 */ 174 insn->type = INSN_NOP; 175 break; 176 default: 177 return false; 178 } 179 180 return true; 181 } 182 183 static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst, 184 struct instruction *insn, 185 struct stack_op **ops_list, 186 struct stack_op *op) 187 { 188 switch (inst.reg2i14_format.opcode) { 189 case ldptrd_op: 190 if (inst.reg2i14_format.rj == CFI_SP) { 191 /* ldptr.d rd,sp,si14 */ 192 insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13); 193 ADD_OP(op) { 194 op->src.type = OP_SRC_REG_INDIRECT; 195 op->src.reg = CFI_SP; 196 op->src.offset = insn->immediate; 197 op->dest.type = OP_DEST_REG; 198 op->dest.reg = inst.reg2i14_format.rd; 199 } 200 } 201 break; 202 case stptrd_op: 203 if (inst.reg2i14_format.rj == CFI_SP) { 204 /* stptr.d ra,sp,0 */ 205 if (inst.reg2i14_format.rd == LOONGARCH_GPR_RA && 206 inst.reg2i14_format.immediate == 0) 207 break; 208 209 /* stptr.d rd,sp,si14 */ 210 insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13); 211 ADD_OP(op) { 212 op->src.type = OP_SRC_REG; 213 op->src.reg = inst.reg2i14_format.rd; 214 op->dest.type = OP_DEST_REG_INDIRECT; 215 op->dest.reg = CFI_SP; 216 op->dest.offset = insn->immediate; 217 } 218 } 219 break; 220 default: 221 return false; 222 } 223 224 return true; 225 } 226 227 static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst, 228 struct instruction *insn) 229 { 230 switch (inst.reg2i16_format.opcode) { 231 case jirl_op: 232 if (inst.reg2i16_format.rd == 0 && 233 inst.reg2i16_format.rj == CFI_RA && 234 inst.reg2i16_format.immediate == 0) { 235 /* jirl r0,ra,0 */ 236 insn->type = INSN_RETURN; 237 } else if (inst.reg2i16_format.rd == CFI_RA) { 238 /* jirl ra,rj,offs16 */ 239 insn->type = INSN_CALL_DYNAMIC; 240 } else if (inst.reg2i16_format.rd == CFI_A0 && 241 inst.reg2i16_format.immediate == 0) { 242 /* 243 * jirl a0,t0,0 244 * this is a special case in loongarch_suspend_enter, 245 * just treat it as a call instruction. 246 */ 247 insn->type = INSN_CALL_DYNAMIC; 248 } else if (inst.reg2i16_format.rd == 0 && 249 inst.reg2i16_format.immediate == 0) { 250 /* jirl r0,rj,0 */ 251 insn->type = INSN_JUMP_DYNAMIC; 252 } else if (inst.reg2i16_format.rd == 0 && 253 inst.reg2i16_format.immediate != 0) { 254 /* 255 * jirl r0,t0,12 256 * this is a rare case in JUMP_VIRT_ADDR, 257 * just ignore it due to it is harmless for tracing. 258 */ 259 break; 260 } else { 261 /* jirl rd,rj,offs16 */ 262 insn->type = INSN_JUMP_UNCONDITIONAL; 263 insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15); 264 } 265 break; 266 case beq_op: 267 case bne_op: 268 case blt_op: 269 case bge_op: 270 case bltu_op: 271 case bgeu_op: 272 insn->type = INSN_JUMP_CONDITIONAL; 273 insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15); 274 break; 275 default: 276 return false; 277 } 278 279 return true; 280 } 281 282 static bool decode_insn_reg3_fomat(union loongarch_instruction inst, 283 struct instruction *insn) 284 { 285 switch (inst.reg3_format.opcode) { 286 case amswapw_op: 287 if (inst.reg3_format.rd == LOONGARCH_GPR_ZERO && 288 inst.reg3_format.rk == LOONGARCH_GPR_RA && 289 inst.reg3_format.rj == LOONGARCH_GPR_ZERO) { 290 /* amswap.w $zero, $ra, $zero */ 291 insn->type = INSN_BUG; 292 } 293 break; 294 default: 295 return false; 296 } 297 298 return true; 299 } 300 301 int arch_decode_instruction(struct objtool_file *file, const struct section *sec, 302 unsigned long offset, unsigned int maxlen, 303 struct instruction *insn) 304 { 305 struct stack_op **ops_list = &insn->stack_ops; 306 const struct elf *elf = file->elf; 307 struct stack_op *op = NULL; 308 union loongarch_instruction inst; 309 310 if (!is_loongarch(elf)) 311 return -1; 312 313 if (maxlen < LOONGARCH_INSN_SIZE) 314 return 0; 315 316 insn->len = LOONGARCH_INSN_SIZE; 317 insn->type = INSN_OTHER; 318 insn->immediate = 0; 319 320 inst = *(union loongarch_instruction *)(sec->data->d_buf + offset); 321 322 if (decode_insn_reg0i26_fomat(inst, insn)) 323 return 0; 324 if (decode_insn_reg1i21_fomat(inst, insn)) 325 return 0; 326 if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op)) 327 return 0; 328 if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op)) 329 return 0; 330 if (decode_insn_reg2i16_fomat(inst, insn)) 331 return 0; 332 if (decode_insn_reg3_fomat(inst, insn)) 333 return 0; 334 335 if (inst.word == 0) { 336 /* andi $zero, $zero, 0x0 */ 337 insn->type = INSN_NOP; 338 } else if (inst.reg0i15_format.opcode == break_op && 339 inst.reg0i15_format.immediate == 0x0) { 340 /* break 0x0 */ 341 insn->type = INSN_TRAP; 342 } else if (inst.reg0i15_format.opcode == break_op && 343 inst.reg0i15_format.immediate == 0x1) { 344 /* break 0x1 */ 345 insn->type = INSN_BUG; 346 } else if (inst.reg2_format.opcode == ertn_op) { 347 /* ertn */ 348 insn->type = INSN_RETURN; 349 } 350 351 return 0; 352 } 353 354 const char *arch_nop_insn(int len) 355 { 356 static u32 nop; 357 358 if (len != LOONGARCH_INSN_SIZE) { 359 ERROR("invalid NOP size: %d\n", len); 360 return NULL; 361 } 362 363 nop = LOONGARCH_INSN_NOP; 364 365 return (const char *)&nop; 366 } 367 368 const char *arch_ret_insn(int len) 369 { 370 static u32 ret; 371 372 if (len != LOONGARCH_INSN_SIZE) { 373 ERROR("invalid RET size: %d\n", len); 374 return NULL; 375 } 376 377 emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0); 378 379 return (const char *)&ret; 380 } 381 382 void arch_initial_func_cfi_state(struct cfi_init_state *state) 383 { 384 int i; 385 386 for (i = 0; i < CFI_NUM_REGS; i++) { 387 state->regs[i].base = CFI_UNDEFINED; 388 state->regs[i].offset = 0; 389 } 390 391 /* initial CFA (call frame address) */ 392 state->cfa.base = CFI_SP; 393 state->cfa.offset = 0; 394 } 395 396 unsigned int arch_reloc_size(struct reloc *reloc) 397 { 398 switch (reloc_type(reloc)) { 399 case R_LARCH_32: 400 case R_LARCH_32_PCREL: 401 return 4; 402 default: 403 return 8; 404 } 405 } 406 407 unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table) 408 { 409 switch (reloc_type(reloc)) { 410 case R_LARCH_32_PCREL: 411 case R_LARCH_64_PCREL: 412 return reloc->sym->offset + reloc_addend(reloc) - 413 (reloc_offset(reloc) - reloc_offset(table)); 414 default: 415 return reloc->sym->offset + reloc_addend(reloc); 416 } 417 } 418 419 #ifdef DISAS 420 421 int arch_disas_info_init(struct disassemble_info *dinfo) 422 { 423 return disas_info_init(dinfo, bfd_arch_loongarch, 424 bfd_mach_loongarch32, bfd_mach_loongarch64, 425 NULL); 426 } 427 428 #endif /* DISAS */ 429