Implement arch_decode_instruction() for LoongArch. Add the decoding of the following part of the instruction, Instructions that affect the SP: - Add instruction: addi.d - Load-Store instructions: st.d/ld.d stptr.d/ldptr.d Instructions that affect control flow: - Branch and Jump instructions: beq/bne/blt/bge/bltu/bgeu/beqz/bnez/b jirl - Call instructions: bl - Return instructions: jr ra Miscellaneous instructions: - Break instructionw: break - Nop instruction: nop - System instruction: ertn Co-developed-by: Jinyang He <hejinyang@xxxxxxxxxxx> Signed-off-by: Jinyang He <hejinyang@xxxxxxxxxxx> Signed-off-by: Youling Tang <tangyouling@xxxxxxxxxxx> --- tools/arch/loongarch/include/asm/inst.h | 1 + tools/include/linux/bitops.h | 10 ++ tools/objtool/arch/loongarch/decode.c | 136 ++++++++++++++++++++++++ 3 files changed, 147 insertions(+) diff --git a/tools/arch/loongarch/include/asm/inst.h b/tools/arch/loongarch/include/asm/inst.h index f0533fbc1e63..23d041cd76bf 100644 --- a/tools/arch/loongarch/include/asm/inst.h +++ b/tools/arch/loongarch/include/asm/inst.h @@ -56,6 +56,7 @@ enum reg2_op { revbd_op = 0x0f, revh2w_op = 0x10, revhd_op = 0x11, + ertn_op = 0x1920e, }; enum reg2i5_op { diff --git a/tools/include/linux/bitops.h b/tools/include/linux/bitops.h index f18683b95ea6..d81b52c070f5 100644 --- a/tools/include/linux/bitops.h +++ b/tools/include/linux/bitops.h @@ -87,4 +87,14 @@ static inline __u32 rol32(__u32 word, unsigned int shift) return (word << shift) | (word >> ((-shift) & 31)); } +/** + * sign_extend64 - sign extend a 64-bit value using specified bit as sign-bit + * @value: value to sign extend + * @index: 0 based bit index (0<=index<64) to sign bit + */ +static __always_inline __s64 sign_extend64(__u64 value, int index) +{ + __u8 shift = 63 - index; + return (__s64)(value << shift) >> shift; +} #endif diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c index 3f795f57e914..fc24efd6dba2 100644 --- a/tools/objtool/arch/loongarch/decode.c +++ b/tools/objtool/arch/loongarch/decode.c @@ -3,6 +3,7 @@ #include <stdio.h> #include <stdlib.h> +#include <linux/bitops.h> #include <asm/inst.h> #include <objtool/check.h> @@ -13,6 +14,8 @@ #include <objtool/endianness.h> #include <arch/cfi_regs.h> +#define to_cfi_reg(reg) (reg) + int arch_ftrace_match(char *name) { return !strcmp(name, "_mcount"); @@ -74,11 +77,18 @@ const char *arch_ret_insn(int len) return (const char *)&ret; } +#define ADD_OP(op) \ + if (!(op = calloc(1, sizeof(*op)))) \ + return -1; \ + else for (*ops_list = op, ops_list = &op->next; op; op = NULL) + int arch_decode_instruction(struct objtool_file *file, const struct section *sec, unsigned long offset, unsigned int maxlen, struct instruction *insn) { + struct stack_op **ops_list = &insn->stack_ops; const struct elf *elf = file->elf; + struct stack_op *op = NULL; union loongarch_instruction inst; if (!is_loongarch(elf)) @@ -97,6 +107,132 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec if (inst.word == 0) insn->type = INSN_NOP; + switch (inst.reg2i12_format.opcode) { + case addid_op: + if ((inst.reg2i12_format.rj == CFI_SP) || (inst.reg2i12_format.rd == CFI_SP)) { + /* addi.d reg1,reg2,imm */ + insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); + ADD_OP(op) { + op->src.type = OP_SRC_ADD; + op->src.reg = to_cfi_reg(inst.reg2i12_format.rj); + op->src.offset = insn->immediate; + op->dest.type = OP_DEST_REG; + op->dest.reg = to_cfi_reg(inst.reg2i12_format.rd); + } + } + break; + case std_op: + if (inst.reg2i12_format.rj == CFI_SP) { + /* st.d reg,sp,imm */ + insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); + ADD_OP(op) { + op->src.type = OP_SRC_REG; + op->src.reg = to_cfi_reg(inst.reg2i12_format.rd); + op->dest.type = OP_DEST_REG_INDIRECT; + op->dest.reg = CFI_SP; + op->dest.offset = insn->immediate; + } + } + break; + case ldd_op: + if (inst.reg2i12_format.rj == CFI_SP) { + /* ld.d reg,sp,imm */ + insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); + ADD_OP(op) { + op->src.type = OP_SRC_REG_INDIRECT; + op->src.reg = CFI_SP; + op->src.offset = insn->immediate; + op->dest.type = OP_DEST_REG; + op->dest.reg = to_cfi_reg(inst.reg2i12_format.rd); + } + } + break; + case andi_op: + if (inst.reg2i12_format.immediate == 0 && + inst.reg2i12_format.rj == 0 && + inst.reg2i12_format.rd == 0) + /* nop */ + insn->type = INSN_NOP; + break; + default: + switch (inst.reg2i16_format.opcode) { + case jirl_op: + if (inst.reg2i16_format.rj == CFI_RA && + inst.reg2i16_format.rd == 0) { + /* jr ra */ + insn->type = INSN_RETURN; + } else if (inst.reg2i16_format.rd == CFI_RA) { + /* jalr reg */ + insn->type = INSN_CALL_DYNAMIC; + } else if (inst.reg2i16_format.rd == 0) { + /* jr reg */ + insn->type = INSN_JUMP_DYNAMIC; + } else if (!inst.reg2i16_format.immediate) { + /* jirl */ + insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15); + insn->type = INSN_JUMP_UNCONDITIONAL; + } + break; + case beq_op: + case bne_op: + case blt_op: + case bge_op: + case bltu_op: + case bgeu_op: + insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15); + insn->type = INSN_JUMP_CONDITIONAL; + break; + case beqz_op: + case bnez_op: + insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 | + inst.reg1i21_format.immediate_l, 20); + insn->type = INSN_JUMP_CONDITIONAL; + break; + case bl_op: + insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 | + inst.reg0i26_format.immediate_l, 25); + insn->type = INSN_CALL; + break; + case b_op: + insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 | + inst.reg0i26_format.immediate_l, 25); + insn->type = INSN_JUMP_UNCONDITIONAL; + break; + default: + if (inst.reg2i14_format.opcode == stptrd_op && + inst.reg2i14_format.rj == CFI_SP) { + /* stptr.d reg,sp,imm */ + insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13); + ADD_OP(op) { + op->src.type = OP_SRC_REG; + op->src.reg = to_cfi_reg(inst.reg2i14_format.rd); + op->dest.type = OP_DEST_REG_INDIRECT; + op->dest.reg = CFI_SP; + op->dest.offset = insn->immediate; + } + } else if (inst.reg2i14_format.opcode == ldptrd_op && + inst.reg2i14_format.rj == CFI_SP) { + /* ldptr.d reg,sp,imm */ + insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13); + ADD_OP(op) { + op->src.type = OP_SRC_REG_INDIRECT; + op->src.reg = CFI_SP; + op->src.offset = insn->immediate; + op->dest.type = OP_DEST_REG; + op->dest.reg = to_cfi_reg(inst.reg2i14_format.rd); + } + } else if (inst.reg0i15_format.opcode == break_op) { + /* break */ + insn->type = INSN_BUG; + } else if (inst.reg2_format.opcode == ertn_op) { + /* ertn */ + insn->type = INSN_RETURN; + } + break; + } + break; + } + return 0; } -- 2.39.2