Path: blob/master/tools/objtool/arch/loongarch/decode.c
26285 views
// SPDX-License-Identifier: GPL-2.0-or-later1#include <string.h>2#include <objtool/check.h>3#include <objtool/warn.h>4#include <asm/inst.h>5#include <asm/orc_types.h>6#include <linux/objtool_types.h>7#include <arch/elf.h>89int arch_ftrace_match(char *name)10{11return !strcmp(name, "_mcount");12}1314unsigned long arch_jump_destination(struct instruction *insn)15{16return insn->offset + (insn->immediate << 2);17}1819unsigned long arch_dest_reloc_offset(int addend)20{21return addend;22}2324bool arch_pc_relative_reloc(struct reloc *reloc)25{26return false;27}2829bool arch_callee_saved_reg(unsigned char reg)30{31switch (reg) {32case CFI_RA:33case CFI_FP:34case CFI_S0 ... CFI_S8:35return true;36default:37return false;38}39}4041int arch_decode_hint_reg(u8 sp_reg, int *base)42{43switch (sp_reg) {44case ORC_REG_UNDEFINED:45*base = CFI_UNDEFINED;46break;47case ORC_REG_SP:48*base = CFI_SP;49break;50case ORC_REG_FP:51*base = CFI_FP;52break;53default:54return -1;55}5657return 0;58}5960static bool is_loongarch(const struct elf *elf)61{62if (elf->ehdr.e_machine == EM_LOONGARCH)63return true;6465ERROR("unexpected ELF machine type %d", elf->ehdr.e_machine);66return false;67}6869#define ADD_OP(op) \70if (!(op = calloc(1, sizeof(*op)))) \71return -1; \72else for (*ops_list = op, ops_list = &op->next; op; op = NULL)7374static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst,75struct instruction *insn)76{77switch (inst.reg0i26_format.opcode) {78case b_op:79insn->type = INSN_JUMP_UNCONDITIONAL;80insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |81inst.reg0i26_format.immediate_l, 25);82break;83case bl_op:84insn->type = INSN_CALL;85insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |86inst.reg0i26_format.immediate_l, 25);87break;88default:89return false;90}9192return true;93}9495static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst,96struct instruction *insn)97{98switch (inst.reg1i21_format.opcode) {99case beqz_op:100case bnez_op:101case bceqz_op:102insn->type = INSN_JUMP_CONDITIONAL;103insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 |104inst.reg1i21_format.immediate_l, 20);105break;106default:107return false;108}109110return true;111}112113static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst,114struct instruction *insn,115struct stack_op **ops_list,116struct stack_op *op)117{118switch (inst.reg2i12_format.opcode) {119case addid_op:120if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) {121/* addi.d sp,sp,si12 or addi.d fp,sp,si12 or addi.d sp,fp,si12 */122insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);123ADD_OP(op) {124op->src.type = OP_SRC_ADD;125op->src.reg = inst.reg2i12_format.rj;126op->src.offset = insn->immediate;127op->dest.type = OP_DEST_REG;128op->dest.reg = inst.reg2i12_format.rd;129}130}131if ((inst.reg2i12_format.rd == CFI_SP) && (inst.reg2i12_format.rj == CFI_FP)) {132/* addi.d sp,fp,si12 */133struct symbol *func = find_func_containing(insn->sec, insn->offset);134135if (!func)136return false;137138func->frame_pointer = true;139}140break;141case ldd_op:142if (inst.reg2i12_format.rj == CFI_SP) {143/* ld.d rd,sp,si12 */144insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);145ADD_OP(op) {146op->src.type = OP_SRC_REG_INDIRECT;147op->src.reg = CFI_SP;148op->src.offset = insn->immediate;149op->dest.type = OP_DEST_REG;150op->dest.reg = inst.reg2i12_format.rd;151}152}153break;154case std_op:155if (inst.reg2i12_format.rj == CFI_SP) {156/* st.d rd,sp,si12 */157insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);158ADD_OP(op) {159op->src.type = OP_SRC_REG;160op->src.reg = inst.reg2i12_format.rd;161op->dest.type = OP_DEST_REG_INDIRECT;162op->dest.reg = CFI_SP;163op->dest.offset = insn->immediate;164}165}166break;167case andi_op:168if (inst.reg2i12_format.rd == 0 &&169inst.reg2i12_format.rj == 0 &&170inst.reg2i12_format.immediate == 0)171/* andi r0,r0,0 */172insn->type = INSN_NOP;173break;174default:175return false;176}177178return true;179}180181static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst,182struct instruction *insn,183struct stack_op **ops_list,184struct stack_op *op)185{186switch (inst.reg2i14_format.opcode) {187case ldptrd_op:188if (inst.reg2i14_format.rj == CFI_SP) {189/* ldptr.d rd,sp,si14 */190insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);191ADD_OP(op) {192op->src.type = OP_SRC_REG_INDIRECT;193op->src.reg = CFI_SP;194op->src.offset = insn->immediate;195op->dest.type = OP_DEST_REG;196op->dest.reg = inst.reg2i14_format.rd;197}198}199break;200case stptrd_op:201if (inst.reg2i14_format.rj == CFI_SP) {202/* stptr.d ra,sp,0 */203if (inst.reg2i14_format.rd == LOONGARCH_GPR_RA &&204inst.reg2i14_format.immediate == 0)205break;206207/* stptr.d rd,sp,si14 */208insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);209ADD_OP(op) {210op->src.type = OP_SRC_REG;211op->src.reg = inst.reg2i14_format.rd;212op->dest.type = OP_DEST_REG_INDIRECT;213op->dest.reg = CFI_SP;214op->dest.offset = insn->immediate;215}216}217break;218default:219return false;220}221222return true;223}224225static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst,226struct instruction *insn)227{228switch (inst.reg2i16_format.opcode) {229case jirl_op:230if (inst.reg2i16_format.rd == 0 &&231inst.reg2i16_format.rj == CFI_RA &&232inst.reg2i16_format.immediate == 0) {233/* jirl r0,ra,0 */234insn->type = INSN_RETURN;235} else if (inst.reg2i16_format.rd == CFI_RA) {236/* jirl ra,rj,offs16 */237insn->type = INSN_CALL_DYNAMIC;238} else if (inst.reg2i16_format.rd == CFI_A0 &&239inst.reg2i16_format.immediate == 0) {240/*241* jirl a0,t0,0242* this is a special case in loongarch_suspend_enter,243* just treat it as a call instruction.244*/245insn->type = INSN_CALL_DYNAMIC;246} else if (inst.reg2i16_format.rd == 0 &&247inst.reg2i16_format.immediate == 0) {248/* jirl r0,rj,0 */249insn->type = INSN_JUMP_DYNAMIC;250} else if (inst.reg2i16_format.rd == 0 &&251inst.reg2i16_format.immediate != 0) {252/*253* jirl r0,t0,12254* this is a rare case in JUMP_VIRT_ADDR,255* just ignore it due to it is harmless for tracing.256*/257break;258} else {259/* jirl rd,rj,offs16 */260insn->type = INSN_JUMP_UNCONDITIONAL;261insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);262}263break;264case beq_op:265case bne_op:266case blt_op:267case bge_op:268case bltu_op:269case bgeu_op:270insn->type = INSN_JUMP_CONDITIONAL;271insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);272break;273default:274return false;275}276277return true;278}279280int arch_decode_instruction(struct objtool_file *file, const struct section *sec,281unsigned long offset, unsigned int maxlen,282struct instruction *insn)283{284struct stack_op **ops_list = &insn->stack_ops;285const struct elf *elf = file->elf;286struct stack_op *op = NULL;287union loongarch_instruction inst;288289if (!is_loongarch(elf))290return -1;291292if (maxlen < LOONGARCH_INSN_SIZE)293return 0;294295insn->len = LOONGARCH_INSN_SIZE;296insn->type = INSN_OTHER;297insn->immediate = 0;298299inst = *(union loongarch_instruction *)(sec->data->d_buf + offset);300301if (decode_insn_reg0i26_fomat(inst, insn))302return 0;303if (decode_insn_reg1i21_fomat(inst, insn))304return 0;305if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op))306return 0;307if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op))308return 0;309if (decode_insn_reg2i16_fomat(inst, insn))310return 0;311312if (inst.word == 0)313insn->type = INSN_NOP;314else if (inst.reg0i15_format.opcode == break_op) {315/* break */316insn->type = INSN_BUG;317} else if (inst.reg2_format.opcode == ertn_op) {318/* ertn */319insn->type = INSN_RETURN;320}321322return 0;323}324325const char *arch_nop_insn(int len)326{327static u32 nop;328329if (len != LOONGARCH_INSN_SIZE) {330ERROR("invalid NOP size: %d\n", len);331return NULL;332}333334nop = LOONGARCH_INSN_NOP;335336return (const char *)&nop;337}338339const char *arch_ret_insn(int len)340{341static u32 ret;342343if (len != LOONGARCH_INSN_SIZE) {344ERROR("invalid RET size: %d\n", len);345return NULL;346}347348emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0);349350return (const char *)&ret;351}352353void arch_initial_func_cfi_state(struct cfi_init_state *state)354{355int i;356357for (i = 0; i < CFI_NUM_REGS; i++) {358state->regs[i].base = CFI_UNDEFINED;359state->regs[i].offset = 0;360}361362/* initial CFA (call frame address) */363state->cfa.base = CFI_SP;364state->cfa.offset = 0;365}366367unsigned int arch_reloc_size(struct reloc *reloc)368{369switch (reloc_type(reloc)) {370case R_LARCH_32:371case R_LARCH_32_PCREL:372return 4;373default:374return 8;375}376}377378unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)379{380switch (reloc_type(reloc)) {381case R_LARCH_32_PCREL:382case R_LARCH_64_PCREL:383return reloc->sym->offset + reloc_addend(reloc) -384(reloc_offset(reloc) - reloc_offset(table));385default:386return reloc->sym->offset + reloc_addend(reloc);387}388}389390391