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