[RFC v5 22/57] objtool: arm64: Decode add/sub immediate instructions

From: Julien Thierry
Date: Thu Jan 09 2020 - 11:05:50 EST


Decode instruction adding immediates to registers. Create stack
operation for instructions interacting with the stack pointer or the
frame pointer.

Suggested-by: Raphael Gault <raphael.gault@xxxxxxx>
Signed-off-by: Julien Thierry <jthierry@xxxxxxxxxx>
---
tools/objtool/arch/arm64/decode.c | 84 +++++++++++++++++++
.../objtool/arch/arm64/include/insn_decode.h | 7 ++
2 files changed, 91 insertions(+)

diff --git a/tools/objtool/arch/arm64/decode.c b/tools/objtool/arch/arm64/decode.c
index 6c8db9335fc9..d240f29a2390 100644
--- a/tools/objtool/arch/arm64/decode.c
+++ b/tools/objtool/arch/arm64/decode.c
@@ -13,6 +13,11 @@
#include "../../elf.h"
#include "../../warn.h"

+static bool stack_related_reg(int reg)
+{
+ return reg == CFI_SP || reg == CFI_BP;
+}
+
bool arch_callee_saved_reg(unsigned char reg)
{
switch (reg) {
@@ -153,6 +158,8 @@ int arm_decode_unknown(u32 instr, enum insn_type *type,

static arm_decode_class aarch64_insn_dp_imm_decode_table[NR_DP_IMM_SUBCLASS] = {
[0 ... INSN_PCREL] = arm_decode_pcrel,
+ [INSN_ADD_SUB] = arm_decode_add_sub,
+ [INSN_ADD_TAG] = arm_decode_add_sub_tags,
[INSN_MOVE_WIDE] = arm_decode_move_wide,
[INSN_BITFIELD] = arm_decode_bitfield,
[INSN_EXTRACT] = arm_decode_extract,
@@ -189,6 +196,83 @@ int arm_decode_pcrel(u32 instr, enum insn_type *type,
return 0;
}

+int arm_decode_add_sub(u32 instr, enum insn_type *type,
+ unsigned long *immediate, struct list_head *ops_list)
+{
+ unsigned long imm12 = 0, imm = 0;
+ unsigned char sf = 0, sh = 0, S = 0, op_bit = 0;
+ unsigned char rn = 0, rd = 0;
+
+ S = EXTRACT_BIT(instr, 29);
+ op_bit = EXTRACT_BIT(instr, 30);
+ sf = EXTRACT_BIT(instr, 31);
+ sh = EXTRACT_BIT(instr, 22);
+ rd = instr & ONES(5);
+ rn = (instr >> 5) & ONES(5);
+ imm12 = (instr >> 10) & ONES(12);
+ imm = ZERO_EXTEND(imm12 << (sh * 12), (sf + 1) * 32);
+
+ *type = INSN_OTHER;
+
+ if (rd == CFI_BP || (!S && rd == CFI_SP) || stack_related_reg(rn)) {
+ struct stack_op *op;
+
+ *type = INSN_STACK;
+
+ op = calloc(1, sizeof(*op));
+ list_add_tail(&op->list, ops_list);
+
+ op->dest.type = OP_DEST_REG;
+ op->dest.offset = 0;
+ op->dest.reg = rd;
+ op->src.type = OP_SRC_ADD;
+ op->src.offset = op_bit ? -1 * imm : imm;
+ op->src.reg = rn;
+ }
+ return 0;
+}
+
+int arm_decode_add_sub_tags(u32 instr, enum insn_type *type,
+ unsigned long *immediate,
+ struct list_head *ops_list)
+{
+ unsigned char decode_field = 0, rn = 0, rd = 0, uimm6 = 0;
+
+ decode_field = (instr >> 29) & ONES(3);
+ rd = instr & ONES(5);
+ rn = (instr >> 5) & ONES(5);
+ uimm6 = (instr >> 16) & ONES(6);
+
+ *immediate = uimm6;
+ *type = INSN_OTHER;
+
+#define ADDG_DECODE 4
+#define SUBG_DECODE 5
+ if (decode_field != ADDG_DECODE && decode_field != SUBG_DECODE)
+ return arm_decode_unknown(instr, type, immediate, ops_list);
+
+#undef ADDG_DECODE
+#undef SUBG_DECODE
+
+ if (stack_related_reg(rd)) {
+ struct stack_op *op;
+
+ *type = INSN_STACK;
+
+ op = calloc(1, sizeof(*op));
+ list_add_tail(&op->list, ops_list);
+
+ op->dest.type = OP_DEST_REG;
+ op->dest.offset = 0;
+ op->dest.reg = rd;
+ op->src.type = OP_SRC_ADD;
+ op->src.offset = 0;
+ op->src.reg = rn;
+ }
+
+ return 0;
+}
+
int arm_decode_move_wide(u32 instr, enum insn_type *type,
unsigned long *immediate, struct list_head *ops_list)
{
diff --git a/tools/objtool/arch/arm64/include/insn_decode.h b/tools/objtool/arch/arm64/include/insn_decode.h
index 06235d81300c..65e60b293a07 100644
--- a/tools/objtool/arch/arm64/include/insn_decode.h
+++ b/tools/objtool/arch/arm64/include/insn_decode.h
@@ -14,6 +14,8 @@
#define INSN_CLASS(opcode) (((opcode) >> 25) & (NR_INSN_CLASS - 1))

#define INSN_PCREL 0b001 //0b00x
+#define INSN_ADD_SUB 0b010
+#define INSN_ADD_TAG 0b011
#define INSN_MOVE_WIDE 0b101
#define INSN_BITFIELD 0b110
#define INSN_EXTRACT 0b111
@@ -31,6 +33,11 @@ int arm_decode_unknown(u32 instr, enum insn_type *type,
/* arm64 data processing -- immediate subclasses */
int arm_decode_pcrel(u32 instr, enum insn_type *type,
unsigned long *immediate, struct list_head *ops_list);
+int arm_decode_add_sub(u32 instr, enum insn_type *type,
+ unsigned long *immediate, struct list_head *ops_list);
+int arm_decode_add_sub_tags(u32 instr, enum insn_type *type,
+ unsigned long *immediate,
+ struct list_head *ops_list);
int arm_decode_move_wide(u32 instr, enum insn_type *type,
unsigned long *immediate, struct list_head *ops_list);
int arm_decode_bitfield(u32 instr, enum insn_type *type,
--
2.21.0