Path: blob/master/tools/objtool/arch/loongarch/decode.c
49628 views
// SPDX-License-Identifier: GPL-2.0-or-later1#include <string.h>2#include <objtool/check.h>3#include <objtool/disas.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>910const char *arch_reg_name[CFI_NUM_REGS] = {11"zero", "ra", "tp", "sp",12"a0", "a1", "a2", "a3",13"a4", "a5", "a6", "a7",14"t0", "t1", "t2", "t3",15"t4", "t5", "t6", "t7",16"t8", "u0", "fp", "s0",17"s1", "s2", "s3", "s4",18"s5", "s6", "s7", "s8"19};2021int arch_ftrace_match(const char *name)22{23return !strcmp(name, "_mcount");24}2526unsigned long arch_jump_destination(struct instruction *insn)27{28return insn->offset + (insn->immediate << 2);29}3031s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)32{33return reloc_addend(reloc);34}3536bool arch_pc_relative_reloc(struct reloc *reloc)37{38return false;39}4041bool arch_callee_saved_reg(unsigned char reg)42{43switch (reg) {44case CFI_RA:45case CFI_FP:46case CFI_S0 ... CFI_S8:47return true;48default:49return false;50}51}5253int arch_decode_hint_reg(u8 sp_reg, int *base)54{55switch (sp_reg) {56case ORC_REG_UNDEFINED:57*base = CFI_UNDEFINED;58break;59case ORC_REG_SP:60*base = CFI_SP;61break;62case ORC_REG_FP:63*base = CFI_FP;64break;65default:66return -1;67}6869return 0;70}7172static bool is_loongarch(const struct elf *elf)73{74if (elf->ehdr.e_machine == EM_LOONGARCH)75return true;7677ERROR("unexpected ELF machine type %d", elf->ehdr.e_machine);78return false;79}8081#define ADD_OP(op) \82if (!(op = calloc(1, sizeof(*op)))) \83return -1; \84else for (*ops_list = op, ops_list = &op->next; op; op = NULL)8586static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst,87struct instruction *insn)88{89switch (inst.reg0i26_format.opcode) {90case b_op:91insn->type = INSN_JUMP_UNCONDITIONAL;92insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |93inst.reg0i26_format.immediate_l, 25);94break;95case bl_op:96insn->type = INSN_CALL;97insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |98inst.reg0i26_format.immediate_l, 25);99break;100default:101return false;102}103104return true;105}106107static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst,108struct instruction *insn)109{110switch (inst.reg1i21_format.opcode) {111case beqz_op:112case bnez_op:113case bceqz_op:114insn->type = INSN_JUMP_CONDITIONAL;115insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 |116inst.reg1i21_format.immediate_l, 20);117break;118default:119return false;120}121122return true;123}124125static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst,126struct instruction *insn,127struct stack_op **ops_list,128struct stack_op *op)129{130switch (inst.reg2i12_format.opcode) {131case addid_op:132if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) {133/* addi.d sp,sp,si12 or addi.d fp,sp,si12 or addi.d sp,fp,si12 */134insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);135ADD_OP(op) {136op->src.type = OP_SRC_ADD;137op->src.reg = inst.reg2i12_format.rj;138op->src.offset = insn->immediate;139op->dest.type = OP_DEST_REG;140op->dest.reg = inst.reg2i12_format.rd;141}142}143if ((inst.reg2i12_format.rd == CFI_SP) && (inst.reg2i12_format.rj == CFI_FP)) {144/* addi.d sp,fp,si12 */145struct symbol *func = find_func_containing(insn->sec, insn->offset);146147if (!func)148return false;149150func->frame_pointer = true;151}152break;153case ldd_op:154if (inst.reg2i12_format.rj == CFI_SP) {155/* ld.d rd,sp,si12 */156insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);157ADD_OP(op) {158op->src.type = OP_SRC_REG_INDIRECT;159op->src.reg = CFI_SP;160op->src.offset = insn->immediate;161op->dest.type = OP_DEST_REG;162op->dest.reg = inst.reg2i12_format.rd;163}164}165break;166case std_op:167if (inst.reg2i12_format.rj == CFI_SP) {168/* st.d rd,sp,si12 */169insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);170ADD_OP(op) {171op->src.type = OP_SRC_REG;172op->src.reg = inst.reg2i12_format.rd;173op->dest.type = OP_DEST_REG_INDIRECT;174op->dest.reg = CFI_SP;175op->dest.offset = insn->immediate;176}177}178break;179case andi_op:180if (inst.reg2i12_format.rd == 0 &&181inst.reg2i12_format.rj == 0 &&182inst.reg2i12_format.immediate == 0)183/* andi r0,r0,0 */184insn->type = INSN_NOP;185break;186default:187return false;188}189190return true;191}192193static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst,194struct instruction *insn,195struct stack_op **ops_list,196struct stack_op *op)197{198switch (inst.reg2i14_format.opcode) {199case ldptrd_op:200if (inst.reg2i14_format.rj == CFI_SP) {201/* ldptr.d rd,sp,si14 */202insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);203ADD_OP(op) {204op->src.type = OP_SRC_REG_INDIRECT;205op->src.reg = CFI_SP;206op->src.offset = insn->immediate;207op->dest.type = OP_DEST_REG;208op->dest.reg = inst.reg2i14_format.rd;209}210}211break;212case stptrd_op:213if (inst.reg2i14_format.rj == CFI_SP) {214/* stptr.d ra,sp,0 */215if (inst.reg2i14_format.rd == LOONGARCH_GPR_RA &&216inst.reg2i14_format.immediate == 0)217break;218219/* stptr.d rd,sp,si14 */220insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);221ADD_OP(op) {222op->src.type = OP_SRC_REG;223op->src.reg = inst.reg2i14_format.rd;224op->dest.type = OP_DEST_REG_INDIRECT;225op->dest.reg = CFI_SP;226op->dest.offset = insn->immediate;227}228}229break;230default:231return false;232}233234return true;235}236237static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst,238struct instruction *insn)239{240switch (inst.reg2i16_format.opcode) {241case jirl_op:242if (inst.reg2i16_format.rd == 0 &&243inst.reg2i16_format.rj == CFI_RA &&244inst.reg2i16_format.immediate == 0) {245/* jirl r0,ra,0 */246insn->type = INSN_RETURN;247} else if (inst.reg2i16_format.rd == CFI_RA) {248/* jirl ra,rj,offs16 */249insn->type = INSN_CALL_DYNAMIC;250} else if (inst.reg2i16_format.rd == CFI_A0 &&251inst.reg2i16_format.immediate == 0) {252/*253* jirl a0,t0,0254* this is a special case in loongarch_suspend_enter,255* just treat it as a call instruction.256*/257insn->type = INSN_CALL_DYNAMIC;258} else if (inst.reg2i16_format.rd == 0 &&259inst.reg2i16_format.immediate == 0) {260/* jirl r0,rj,0 */261insn->type = INSN_JUMP_DYNAMIC;262} else if (inst.reg2i16_format.rd == 0 &&263inst.reg2i16_format.immediate != 0) {264/*265* jirl r0,t0,12266* this is a rare case in JUMP_VIRT_ADDR,267* just ignore it due to it is harmless for tracing.268*/269break;270} else {271/* jirl rd,rj,offs16 */272insn->type = INSN_JUMP_UNCONDITIONAL;273insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);274}275break;276case beq_op:277case bne_op:278case blt_op:279case bge_op:280case bltu_op:281case bgeu_op:282insn->type = INSN_JUMP_CONDITIONAL;283insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);284break;285default:286return false;287}288289return true;290}291292static bool decode_insn_reg3_fomat(union loongarch_instruction inst,293struct instruction *insn)294{295switch (inst.reg3_format.opcode) {296case amswapw_op:297if (inst.reg3_format.rd == LOONGARCH_GPR_ZERO &&298inst.reg3_format.rk == LOONGARCH_GPR_RA &&299inst.reg3_format.rj == LOONGARCH_GPR_ZERO) {300/* amswap.w $zero, $ra, $zero */301insn->type = INSN_BUG;302}303break;304default:305return false;306}307308return true;309}310311int arch_decode_instruction(struct objtool_file *file, const struct section *sec,312unsigned long offset, unsigned int maxlen,313struct instruction *insn)314{315struct stack_op **ops_list = &insn->stack_ops;316const struct elf *elf = file->elf;317struct stack_op *op = NULL;318union loongarch_instruction inst;319320if (!is_loongarch(elf))321return -1;322323if (maxlen < LOONGARCH_INSN_SIZE)324return 0;325326insn->len = LOONGARCH_INSN_SIZE;327insn->type = INSN_OTHER;328insn->immediate = 0;329330inst = *(union loongarch_instruction *)(sec->data->d_buf + offset);331332if (decode_insn_reg0i26_fomat(inst, insn))333return 0;334if (decode_insn_reg1i21_fomat(inst, insn))335return 0;336if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op))337return 0;338if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op))339return 0;340if (decode_insn_reg2i16_fomat(inst, insn))341return 0;342if (decode_insn_reg3_fomat(inst, insn))343return 0;344345if (inst.word == 0) {346/* andi $zero, $zero, 0x0 */347insn->type = INSN_NOP;348} else if (inst.reg0i15_format.opcode == break_op &&349inst.reg0i15_format.immediate == 0x0) {350/* break 0x0 */351insn->type = INSN_TRAP;352} else if (inst.reg0i15_format.opcode == break_op &&353inst.reg0i15_format.immediate == 0x1) {354/* break 0x1 */355insn->type = INSN_BUG;356} else if (inst.reg2_format.opcode == ertn_op) {357/* ertn */358insn->type = INSN_RETURN;359}360361return 0;362}363364const char *arch_nop_insn(int len)365{366static u32 nop;367368if (len != LOONGARCH_INSN_SIZE) {369ERROR("invalid NOP size: %d\n", len);370return NULL;371}372373nop = LOONGARCH_INSN_NOP;374375return (const char *)&nop;376}377378const char *arch_ret_insn(int len)379{380static u32 ret;381382if (len != LOONGARCH_INSN_SIZE) {383ERROR("invalid RET size: %d\n", len);384return NULL;385}386387emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0);388389return (const char *)&ret;390}391392void arch_initial_func_cfi_state(struct cfi_init_state *state)393{394int i;395396for (i = 0; i < CFI_NUM_REGS; i++) {397state->regs[i].base = CFI_UNDEFINED;398state->regs[i].offset = 0;399}400401/* initial CFA (call frame address) */402state->cfa.base = CFI_SP;403state->cfa.offset = 0;404}405406unsigned int arch_reloc_size(struct reloc *reloc)407{408switch (reloc_type(reloc)) {409case R_LARCH_32:410case R_LARCH_32_PCREL:411return 4;412default:413return 8;414}415}416417unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)418{419switch (reloc_type(reloc)) {420case R_LARCH_32_PCREL:421case R_LARCH_64_PCREL:422return reloc->sym->offset + reloc_addend(reloc) -423(reloc_offset(reloc) - reloc_offset(table));424default:425return reloc->sym->offset + reloc_addend(reloc);426}427}428429#ifdef DISAS430431int arch_disas_info_init(struct disassemble_info *dinfo)432{433return disas_info_init(dinfo, bfd_arch_loongarch,434bfd_mach_loongarch32, bfd_mach_loongarch64,435NULL);436}437438#endif /* DISAS */439440441