[RFC PATCH v1 10/23] objtool: LoongArch: Implement decoder

From: Youling Tang
Date: Tue Jun 20 2023 - 03:51:13 EST


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