All of lore.kernel.org
 help / color / mirror / Atom feed
From: madvenka@linux.microsoft.com
To: jpoimboe@redhat.com, peterz@infradead.org,
	chenzhongjin@huawei.com, mark.rutland@arm.com,
	broonie@kernel.org, nobuta.keiya@fujitsu.com,
	sjitindarsingh@gmail.com, catalin.marinas@arm.com,
	will@kernel.org, jamorris@linux.microsoft.com,
	linux-arm-kernel@lists.infradead.org,
	live-patching@vger.kernel.org, linux-kernel@vger.kernel.org,
	madvenka@linux.microsoft.com
Subject: [RFC PATCH v3 10/22] objtool: arm64: Implement decoder for Dynamic FP validation
Date: Thu,  2 Feb 2023 01:40:24 -0600	[thread overview]
Message-ID: <20230202074036.507249-11-madvenka@linux.microsoft.com> (raw)
In-Reply-To: <20230202074036.507249-1-madvenka@linux.microsoft.com>

From: "Madhavan T. Venkataraman" <madvenka@linux.microsoft.com>

Implement arch_decode_instruction() for ARM64. For Dynamic FP validation,
we need to walk each function's code and determine the stack and frame
offsets at each instruction. So, the following instructions are completely
decoded:

	Instructions that affect the SP and FP:

	- Load-Store instructions
	- Add/Sub/Mov instructions

	Instructions that affect control flow:

	- Branch instructions
	- Call instructions
	- Return instructions

	Miscellaneous instructions:

	- Break instruction used for bugs
	- Paciasp instruction that occurs at the beginning of the frame
	  pointer prolog

The rest of the instructions are either dont-care from an unwind
perspective or unexpected from the compiler. Add checks for the unexpected
ones to catch them if the compiler ever generates them.

Signed-off-by: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
---
 tools/objtool/arch/arm64/decode.c    | 506 ++++++++++++++++++++++++++-
 tools/objtool/include/objtool/arch.h |   2 +
 2 files changed, 507 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/arch/arm64/decode.c b/tools/objtool/arch/arm64/decode.c
index 69f851337537..aaae16791807 100644
--- a/tools/objtool/arch/arm64/decode.c
+++ b/tools/objtool/arch/arm64/decode.c
@@ -1,5 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
+ * decode.c - ARM64 instruction decoder for dynamic FP validation. Only a
+ *            small subset of the instructions need to be decoded. The rest
+ *            only need to be sanity checked.
+ *
  * Author: Madhavan T. Venkataraman (madvenka@linux.microsoft.com)
  *
  * Copyright (C) 2022 Microsoft Corporation
@@ -7,15 +11,515 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdint.h>
 
 #include <objtool/check.h>
+#include <objtool/elf.h>
+#include <objtool/warn.h>
+
+/* ARM64 instructions are all 4 bytes wide. */
+#define INSN_SIZE	4
+
+/* --------------------- instruction decode structs ------------------------ */
+
+struct decode_var {
+	u32			insn;
+	enum insn_type		type;
+	s64			imm;
+	unsigned int		mode1;
+	unsigned int		mode2;
+	unsigned int		check_reg;
+	struct list_head	*ops;
+};
+
+struct decode {
+	unsigned long	opmask;
+	unsigned long	op;
+	unsigned int	width;
+	unsigned int	shift;
+	unsigned int	bits;
+	unsigned int	sign_extend;
+	unsigned int	mult;
+	unsigned int	mode1;
+	unsigned int	mode2;
+	void		(*func)(struct decode *decode, struct decode_var *var);
+};
+
+struct class {
+	unsigned long	opmask;
+	unsigned long	op;
+	void		(*check)(struct decode_var *var);
+};
+
+/* ------------------------ stack operations ------------------------------- */
+
+static void add_stack_op(unsigned char src_reg, enum op_src_type src_type,
+			 s64 src_offset,
+			 unsigned char dest_reg, enum op_dest_type dest_type,
+			 s64 dest_offset,
+			 struct list_head *ops)
+{
+	struct stack_op *op;
+
+	op = calloc(1, sizeof(*op));
+	if (!op) {
+		WARN("calloc failed");
+		return;
+	}
+
+	op->src.reg = src_reg;
+	op->src.type = src_type;
+	op->src.offset = src_offset;
+	op->dest.reg = dest_reg;
+	op->dest.type = dest_type;
+	op->dest.offset = dest_offset;
+
+	list_add_tail(&op->list, ops);
+}
+
+static void add_op(struct decode_var *var,
+		   unsigned char rn, s64 offset, unsigned char rd)
+{
+	add_stack_op(rn, OP_SRC_ADD, offset, rd, OP_DEST_REG, 0, var->ops);
+}
+
+static void load_op(struct decode_var *var, s64 offset, unsigned char rd)
+{
+	add_stack_op(CFI_SP, OP_SRC_REG_INDIRECT, offset, rd, OP_DEST_REG, 0,
+		     var->ops);
+}
+
+static void store_op(struct decode_var *var, s64 offset, unsigned char rd)
+{
+	add_stack_op(CFI_SP, OP_SRC_REG, 0, rd, OP_DEST_REG_INDIRECT, offset,
+		     var->ops);
+}
+
+/* ------------------------ decode functions ------------------------------- */
+
+#define is_saved_reg(rt)	((rt) == CFI_FP || (rt) == CFI_RA)
+#define is_frame_reg(rt)	((rt) == CFI_FP || (rt) == CFI_SP)
+
+/* ----- Add/Subtract instructions. ----- */
+
+#define CMN_OP		0x31000000	/* Alias of ADDS imm */
+#define CMP_OP		0x71000000	/* Alias of SUBS imm */
+
+static void add(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rd = var->insn & 0x1F;
+	unsigned int	rn = (var->insn >> 5) & 0x1F;
+	unsigned int	shift = (var->insn >> 22) & 1;
+
+	if (decode->op == CMN_OP || decode->op == CMP_OP)
+		return;
+
+	if (!is_frame_reg(rd))
+		return;
+
+	if (is_frame_reg(rn)) {
+		if (shift)
+			var->imm <<= 12;
+		add_op(var, rn, var->imm, rd);
+	} else {
+		var->type = INSN_UNRELIABLE;
+	}
+}
+
+#define CMN_EXT_OP	0x2B200000	/* Alias of ADDS ext */
+#define CMP_EXT_OP	0x6B200000	/* Alias of SUBS ext */
+
+static void addc(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rd = var->insn & 0x1F;
+
+	if (decode->op == CMN_EXT_OP || decode->op == CMP_EXT_OP)
+		return;
+
+	if (is_frame_reg(rd))
+		var->type = INSN_UNRELIABLE;
+}
+
+static void sub(struct decode *decode, struct decode_var *var)
+{
+	var->imm = -var->imm;
+	return add(decode, var);
+}
+
+/* ----- Load instructions. ----- */
+
+/*
+ * For some instructions, the target register cannot be FP. There are 3 cases:
+ *
+ *	- The register width is 32 bits. FP cannot be 32 bits.
+ *	- The register is loaded from one that is not the SP. We do not track
+ *	  the value of other registers in static analysis.
+ *	- The instruction does not make sense for the FP to be the target.
+ */
+static void check_reg(unsigned int reg, struct decode_var *var)
+{
+	if (reg == CFI_FP)
+		var->type = INSN_UNRELIABLE;
+}
+
+static void ldp(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rt1 = var->insn & 0x1F;
+	unsigned int	rt2 = (var->insn >> 10) & 0x1F;
+	unsigned int	rn = (var->insn >> 5) & 0x1F;
+	s64		imm;
+
+	if (rn != CFI_SP || var->check_reg) {
+		check_reg(rt1, var);
+		check_reg(rt2, var);
+	}
+
+	if (rn == CFI_SP) {
+		if (var->mode1 && var->mode2)	/* Pre-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+
+		imm = var->mode1 ? 0 : var->imm;
+		if (is_saved_reg(rt1))
+			load_op(var, imm, rt1);
+		if (is_saved_reg(rt2))
+			load_op(var, imm + 8, rt2);
+
+		if (var->mode1 && !var->mode2)	/* Post-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+	}
+}
+
+static void ldpc(struct decode *decode, struct decode_var *var)
+{
+	var->check_reg = 1;
+	ldp(decode, var);
+}
+
+static void ldr(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rd = var->insn & 0x1F;
+	unsigned int	rn = (var->insn >> 5) & 0x1F;
+	s64		imm;
+
+	if (rn != CFI_SP || var->check_reg)
+		check_reg(rd, var);
+
+	if (rn == CFI_SP) {
+		if (var->mode1 && var->mode2)	/* Pre-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+
+		imm = var->mode1 ? 0 : var->imm;
+		if (is_saved_reg(rd))
+			load_op(var, imm, rd);
+
+		if (var->mode1 && !var->mode2)	/* Post-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+	}
+}
+
+/* ----- Store instructions. ----- */
+
+static void stp(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rt1 = var->insn & 0x1F;
+	unsigned int	rt2 = (var->insn >> 10) & 0x1F;
+	unsigned int	rn = (var->insn >> 5) & 0x1F;
+	s64		imm;
+
+	if (var->check_reg) {
+		check_reg(rt1, var);
+		check_reg(rt2, var);
+	}
+
+	if (rn == CFI_SP) {
+		if (var->mode1 && var->mode2)	/* Pre-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+
+		imm = var->mode1 ? 0 : var->imm;
+		if (is_saved_reg(rt1))
+			store_op(var, imm, rt1);
+		if (is_saved_reg(rt2))
+			store_op(var, imm + 8, rt2);
+
+		if (var->mode1 && !var->mode2)	/* Post-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+	}
+}
+
+static void stpc(struct decode *decode, struct decode_var *var)
+{
+	var->check_reg = 1;
+	stp(decode, var);
+}
+
+static void str(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rd = var->insn & 0x1F;
+	unsigned int	rn = (var->insn >> 5) & 0x1F;
+	s64		imm;
+
+	if (var->check_reg)
+		check_reg(rd, var);
+
+	if (rn == CFI_SP) {
+		if (var->mode1 && var->mode2)	/* Pre-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+
+		imm = var->mode1 ? 0 : var->imm;
+		if (is_saved_reg(rd))
+			store_op(var, imm, rd);
+
+		if (var->mode1 && !var->mode2)	/* Post-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+	}
+}
+
+static void strc(struct decode *decode, struct decode_var *var)
+{
+	var->check_reg = 1;
+	str(decode, var);
+}
+
+/* ----- Control transfer instructions. ----- */
+
+#define BR_UNCONDITIONAL		0x14000000
+
+static void bra(struct decode *decode, struct decode_var *var)
+{
+	if (var->imm) {
+		if (decode->op == BR_UNCONDITIONAL)
+			var->type = INSN_JUMP_UNCONDITIONAL;
+		else
+			var->type = INSN_JUMP_CONDITIONAL;
+	} else {
+		var->type = INSN_JUMP_DYNAMIC;
+	}
+}
+
+static void call(struct decode *decode, struct decode_var *var)
+{
+	var->type = var->imm ? INSN_CALL : INSN_CALL_DYNAMIC;
+}
+
+static void ret(struct decode *decode, struct decode_var *var)
+{
+	var->type = INSN_RETURN;
+}
+
+/* ----- Miscellaneous instructions. ----- */
+
+static void bug(struct decode *decode, struct decode_var *var)
+{
+	var->type = INSN_BUG;
+}
+
+static void pac(struct decode *decode, struct decode_var *var)
+{
+	var->type = INSN_START;
+}
+
+/* ------------------------ Instruction decode ----------------------------- */
+
+struct decode	decode_array[] = {
+/*
+ * mask		OP code mask
+ * opcode	OP code
+ * width	Target register width. Values can be:
+ *			64 (64-bit)
+ *			32 (32-bit),
+ *			 X (64-bit if bit X in the instruction is set)
+ *			-X (32-bit if bit X in the instruction is set)
+ * shift	Shift for the immediate value
+ * bits		Number of bits in the immediate value
+ * sign		Sign extend the immediate value
+ * mult		Multiplier for the immediate value
+ * am1		Addressing mode bit 1
+ * am2		Addressing mode bit 2
+ * func		Decode function
+ *
+ * =============================== INSTRUCTIONS ===============================
+ * mask       opcode   width shift bits sign mult am1 am2 func
+ * ============================================================================
+ */
+{ 0x7E400000, 0x28400000,  31, 15,  7,  1,   0,   23, 24, ldp  /* LDP       */},
+{ 0x7E400000, 0x68400000,  32, 15,  7,  1,   4,   23, 24, ldp  /* LDPSW     */},
+{ 0x7FC00000, 0x28400000,  31, 15,  7,  1,   0,    0,  0, ldpc /* LDNP      */},
+{ 0xBFE00000, 0xB8400000,  30, 12,  9,  1,   1,   10, 11, ldr  /* LDR       */},
+{ 0xBFC00000, 0xB9400000,  30, 10, 12,  0,   0,    0,  0, ldr  /* LDR off   */},
+{ 0xFF200400, 0xF8200400,  64, 12,  9,  1,   8,   11, 11, ldr  /* LDRA      */},
+{ 0xFFC00000, 0x39400000,  32, 10, 12,  0,   1,    0,  0, ldr  /* LDRB off  */},
+{ 0xFFE00000, 0x38400000,  32, 12,  9,  1,   1,   10, 11, ldr  /* LDRB      */},
+{ 0xFFC00000, 0x79400000,  32, 10, 12,  0,   2,    0,  0, ldr  /* LDRH off  */},
+{ 0xFFE00000, 0x78400000,  32, 12,  9,  1,   1,   10, 11, ldr  /* LDRH      */},
+{ 0xFF800000, 0x39800000, -22, 10, 12,  0,   1,    0,  0, ldr  /* LDRSB off */},
+{ 0xFFA00000, 0x38800000, -22, 12,  9,  1,   1,   10, 11, ldr  /* LDRSB     */},
+{ 0xFF800000, 0x79800000, -22, 10, 12,  0,   2,    0,  0, ldr  /* LDRSH off */},
+{ 0xFFA00000, 0x78800000, -22, 12,  9,  1,   1,   10, 11, ldr  /* LDRSH     */},
+{ 0xFFC00000, 0xB9800000,  32, 10, 12,  0,   4,    0,  0, ldr  /* LDRSW off */},
+{ 0xFFE00000, 0xB8800000,  32, 12,  9,  1,   1,   10, 11, ldr  /* LDRSW     */},
+{ 0x7E000000, 0x28000000,  31, 15,  7,  1,   0,   23, 24, stp  /* STP       */},
+{ 0x7E400000, 0x28000000,  31, 15,  7,  1,   0,   23, 24, stp  /* STG       */},
+{ 0xFE400000, 0x68000000,  64, 15,  7,  1,  16,   23, 24, stpc /* STGP      */},
+{ 0x7FC00000, 0x28000000,  31, 15,  7,  1,   0,    0,  0, stpc /* STNP      */},
+{ 0xBFC00000, 0xB9000000,  30, 10, 12,  0,   0,    0,  0, str  /* STR off   */},
+{ 0xBFE00000, 0xB8000000,  30, 12,  9,  1,   1,   10, 11, str  /* STR       */},
+{ 0xFFE00000, 0xD9200000,  64, 12,  9,  1,  16,   10, 11, strc /* STG       */},
+{ 0xFFE00000, 0xD9A00000,  64, 12,  9,  1,  16,   10, 11, strc /* ST2G      */},
+{ 0x7F800000, 0x11000000,  31, 10, 12,  0,   1,    0,  0, add  /* ADD imm   */},
+{ 0x7FE00000, 0x0B200000,  31, 10,  3,  0,   1,    0,  0, addc /* ADD ext   */},
+{ 0x7F800000, 0x31000000,  31, 10, 12,  0,   1,    0,  0, add  /* ADDS imm  */},
+{ 0x7FE00000, 0x2B200000,  31, 10,  3,  0,   1,    0,  0, addc /* ADDS ext  */},
+{ 0x7F800000, 0x51000000,  31, 10, 12,  0,   1,    0,  0, sub  /* SUB imm   */},
+{ 0x7FE00000, 0x4B200000,  31, 10,  3,  0,   1,    0,  0, addc /* SUB ext   */},
+{ 0x7F800000, 0x71000000,  31, 10, 12,  0,   1,    0,  0, sub  /* SUBS imm  */},
+{ 0x7FE00000, 0x6B200000,  31, 10,  3,  0,   1,    0,  0, addc /* SUBS ext  */},
+{ 0xFC000000, 0x14000000,  64,  0, 26,  1,   4,    0,  0, bra  /* B         */},
+{ 0xFF000010, 0x54000000,  64,  5, 19,  1,   4,    0,  0, bra  /* B.cond    */},
+{ 0xFF000010, 0x54000010,  64,  5, 19,  1,   4,    0,  0, bra  /* BC.cond   */},
+{ 0xFFFFFC1F, 0xD61F0000,  64,  0,  0,  0,   0,    0,  0, bra  /* BR        */},
+{ 0xFEFFF800, 0xD61F0800,  64,  0,  0,  0,   0,    0,  0, bra  /* BRA       */},
+{ 0x7E000000, 0x34000000,  31,  5, 19,  1,   4,    0,  0, bra  /* CBZ/CBNZ  */},
+{ 0x7E000000, 0x36000000,  31,  5, 14,  1,   4,    0,  0, bra  /* TBZ/TBNZ  */},
+{ 0xFC000000, 0x94000000,  64,  0, 26,  1,   4,    0,  0, call /* BL        */},
+{ 0xFFFFFC1F, 0xD63F0000,  64,  0,  0,  0,   0,    0,  0, call /* BLR       */},
+{ 0xFEFFF800, 0xD63F0800,  64,  0,  0,  0,   0,    0,  0, call /* BLRA      */},
+{ 0xFFFFFC1F, 0xD65F0000,  64,  0,  0,  0,   0,    0,  0, ret  /* RET       */},
+{ 0xFFFFFBFF, 0xD65F0BFF,  64,  0,  0,  0,   0,    0,  0, ret  /* RETA      */},
+{ 0xFFFFFFFF, 0xD69F03E0,  64,  0,  0,  0,   0,    0,  0, ret  /* ERET      */},
+{ 0xFFFFFBFF, 0xD69F0BFF,  64,  0,  0,  0,   0,    0,  0, ret  /* ERETA     */},
+{ 0xFFE00000, 0xD4200000,  64,  5, 16,  0,   1,    0,  0, bug  /* BRK       */},
+{ 0xFFFFFFFF, 0xD503233F,  64,  0,  0,  0,   1,    0,  0, pac  /* PACIASP   */},
+};
+unsigned int	ndecode = ARRAY_SIZE(decode_array);
+
+static void ignore(struct decode_var *var)
+{
+}
+
+static void check_target(struct decode_var *var)
+{
+	unsigned int	rd = var->insn & 0x1F;
+
+	check_reg(rd, var);
+}
+
+struct class	class_array[] = {
+/*
+ * mask		Class OP mask
+ * opcode	Class OP code
+ * check	Function to perform checks
+ *
+ * ========================== INSTRUCTION CLASSES =============================
+ * mask       opcode       check
+ * ============================================================================
+ */
+{ 0x1E000000, 0x00000000,  ignore        /* RSVD_00 */         },
+{ 0x1E000000, 0x02000000,  ignore        /* UNALLOC_01 */      },
+{ 0x1E000000, 0x04000000,  ignore        /* SVE_02 */          },
+{ 0x1E000000, 0x06000000,  ignore        /* UNALLOC_03 */      },
+{ 0x1E000000, 0x08000000,  check_target  /* LOAD_STORE_04 */   },
+{ 0x1E000000, 0x0A000000,  check_target  /* DP_REGISTER_05 */  },
+{ 0x1E000000, 0x0C000000,  ignore        /* LOAD_STORE_06 */   },
+{ 0x1E000000, 0x0E000000,  ignore        /* SIMD_FP_07 */      },
+{ 0x1E000000, 0x12000000,  check_target  /* DP_IMMEDIATE_09 */ },
+{ 0x1E000000, 0x10000000,  check_target  /* DP_IMMEDIATE_08 */ },
+{ 0x1E000000, 0x14000000,  check_target  /* BR_SYS_10 */       },
+{ 0x1E000000, 0x16000000,  check_target  /* BR_SYS_11 */       },
+{ 0x1E000000, 0x18000000,  check_target  /* LOAD_STORE_12 */   },
+{ 0x1E000000, 0x1A000000,  ignore        /* DP_REGISTER_13 */  },
+{ 0x1E000000, 0x1C000000,  check_target  /* LOAD_STORE_14 */   },
+{ 0x1E000000, 0x1E000000,  ignore        /* SIMD_FP_15 */      },
+};
+unsigned int	nclass = ARRAY_SIZE(class_array);
+
+static inline s64 sign_extend(s64 imm, unsigned int bits)
+{
+	return (imm << (64 - bits)) >> (64 - bits);
+}
 
 int arch_decode_instruction(struct objtool_file *file,
 			    const struct section *sec,
 			    unsigned long offset, unsigned int maxlen,
 			    unsigned int *len, enum insn_type *type,
 			    unsigned long *immediate,
-			    struct list_head *ops_list)
+			    struct list_head *ops)
 {
+	struct decode		*decode;
+	struct decode_var	var;
+	struct class		*class;
+	unsigned int		width, mask, mult, i;
+
+	if (maxlen < INSN_SIZE)
+		return -1;
+	*len = INSN_SIZE;
+
+	var.insn = *(u32 *)(sec->data->d_buf + offset);
+	var.type = INSN_OTHER;
+	var.imm = 0;
+	var.ops = ops;
+
+	*type = INSN_OTHER;
+
+	/* Decode the instruction, if listed. */
+	for (i = 0; i < ndecode; i++) {
+		decode = &decode_array[i];
+
+		if ((var.insn & decode->opmask) != decode->op)
+			continue;
+
+		/* Extract addressing mode (for some instructions). */
+		var.mode1 = 0;
+		var.mode2 = 0;
+		if (decode->mode1)
+			var.mode1 = (var.insn >> decode->mode1) & 1;
+		if (decode->mode2)
+			var.mode2 = (var.insn >> decode->mode2) & 1;
+
+		/* Determine target register width. */
+		width = decode->width;
+		if (width < 0)
+			width = (var.insn & (1 << -width)) ? 32 : 64;
+		else if (width < 32)
+			width = (var.insn & (1 << width)) ? 64 : 32;
+
+		/*
+		 * If the target register width is 32 bits, set the check flag
+		 * so that the target registers are checked to make sure they
+		 * are not the FP or the RA. We should not be using 32-bit
+		 * values in these registers.
+		 */
+		var.check_reg = (width == 32);
+
+		/* Extract the immediate value. */
+		mask = (1 << decode->bits) - 1;
+		var.imm = (var.insn >> decode->shift) & mask;
+		if (decode->sign_extend)
+			var.imm = sign_extend(var.imm, decode->bits);
+
+		/* Scale the immediate value. */
+		mult = decode->mult;
+		if (!mult)
+			mult = (width == 32) ? 4 : 8;
+		var.imm *= mult;
+
+		/* Decode the instruction. */
+		decode->func(decode, &var);
+		goto out;
+	}
+
+	/*
+	 * Sanity check to make sure that the compiler has not generated
+	 * code that modifies the FP or the RA in an unexpected way.
+	 */
+	for (i = 0; i < nclass; i++) {
+		class = &class_array[i];
+		if ((var.insn & class->opmask) == class->op) {
+			class->check(&var);
+			goto out;
+		}
+	}
+out:
+	*immediate = var.imm;
+	*type = var.type;
 	return 0;
 }
diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
index beb2f3aa94ff..3c2f8c1b8265 100644
--- a/tools/objtool/include/objtool/arch.h
+++ b/tools/objtool/include/objtool/arch.h
@@ -29,6 +29,8 @@ enum insn_type {
 	INSN_TRAP,
 	INSN_ENDBR,
 	INSN_OTHER,
+	INSN_START,
+	INSN_UNRELIABLE,
 };
 
 enum op_dest_type {
-- 
2.25.1


WARNING: multiple messages have this Message-ID (diff)
From: madvenka@linux.microsoft.com
To: jpoimboe@redhat.com, peterz@infradead.org,
	chenzhongjin@huawei.com, mark.rutland@arm.com,
	broonie@kernel.org, nobuta.keiya@fujitsu.com,
	sjitindarsingh@gmail.com, catalin.marinas@arm.com,
	will@kernel.org, jamorris@linux.microsoft.com,
	linux-arm-kernel@lists.infradead.org,
	live-patching@vger.kernel.org, linux-kernel@vger.kernel.org,
	madvenka@linux.microsoft.com
Subject: [RFC PATCH v3 10/22] objtool: arm64: Implement decoder for Dynamic FP validation
Date: Thu,  2 Feb 2023 01:40:24 -0600	[thread overview]
Message-ID: <20230202074036.507249-11-madvenka@linux.microsoft.com> (raw)
In-Reply-To: <20230202074036.507249-1-madvenka@linux.microsoft.com>

From: "Madhavan T. Venkataraman" <madvenka@linux.microsoft.com>

Implement arch_decode_instruction() for ARM64. For Dynamic FP validation,
we need to walk each function's code and determine the stack and frame
offsets at each instruction. So, the following instructions are completely
decoded:

	Instructions that affect the SP and FP:

	- Load-Store instructions
	- Add/Sub/Mov instructions

	Instructions that affect control flow:

	- Branch instructions
	- Call instructions
	- Return instructions

	Miscellaneous instructions:

	- Break instruction used for bugs
	- Paciasp instruction that occurs at the beginning of the frame
	  pointer prolog

The rest of the instructions are either dont-care from an unwind
perspective or unexpected from the compiler. Add checks for the unexpected
ones to catch them if the compiler ever generates them.

Signed-off-by: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
---
 tools/objtool/arch/arm64/decode.c    | 506 ++++++++++++++++++++++++++-
 tools/objtool/include/objtool/arch.h |   2 +
 2 files changed, 507 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/arch/arm64/decode.c b/tools/objtool/arch/arm64/decode.c
index 69f851337537..aaae16791807 100644
--- a/tools/objtool/arch/arm64/decode.c
+++ b/tools/objtool/arch/arm64/decode.c
@@ -1,5 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
+ * decode.c - ARM64 instruction decoder for dynamic FP validation. Only a
+ *            small subset of the instructions need to be decoded. The rest
+ *            only need to be sanity checked.
+ *
  * Author: Madhavan T. Venkataraman (madvenka@linux.microsoft.com)
  *
  * Copyright (C) 2022 Microsoft Corporation
@@ -7,15 +11,515 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdint.h>
 
 #include <objtool/check.h>
+#include <objtool/elf.h>
+#include <objtool/warn.h>
+
+/* ARM64 instructions are all 4 bytes wide. */
+#define INSN_SIZE	4
+
+/* --------------------- instruction decode structs ------------------------ */
+
+struct decode_var {
+	u32			insn;
+	enum insn_type		type;
+	s64			imm;
+	unsigned int		mode1;
+	unsigned int		mode2;
+	unsigned int		check_reg;
+	struct list_head	*ops;
+};
+
+struct decode {
+	unsigned long	opmask;
+	unsigned long	op;
+	unsigned int	width;
+	unsigned int	shift;
+	unsigned int	bits;
+	unsigned int	sign_extend;
+	unsigned int	mult;
+	unsigned int	mode1;
+	unsigned int	mode2;
+	void		(*func)(struct decode *decode, struct decode_var *var);
+};
+
+struct class {
+	unsigned long	opmask;
+	unsigned long	op;
+	void		(*check)(struct decode_var *var);
+};
+
+/* ------------------------ stack operations ------------------------------- */
+
+static void add_stack_op(unsigned char src_reg, enum op_src_type src_type,
+			 s64 src_offset,
+			 unsigned char dest_reg, enum op_dest_type dest_type,
+			 s64 dest_offset,
+			 struct list_head *ops)
+{
+	struct stack_op *op;
+
+	op = calloc(1, sizeof(*op));
+	if (!op) {
+		WARN("calloc failed");
+		return;
+	}
+
+	op->src.reg = src_reg;
+	op->src.type = src_type;
+	op->src.offset = src_offset;
+	op->dest.reg = dest_reg;
+	op->dest.type = dest_type;
+	op->dest.offset = dest_offset;
+
+	list_add_tail(&op->list, ops);
+}
+
+static void add_op(struct decode_var *var,
+		   unsigned char rn, s64 offset, unsigned char rd)
+{
+	add_stack_op(rn, OP_SRC_ADD, offset, rd, OP_DEST_REG, 0, var->ops);
+}
+
+static void load_op(struct decode_var *var, s64 offset, unsigned char rd)
+{
+	add_stack_op(CFI_SP, OP_SRC_REG_INDIRECT, offset, rd, OP_DEST_REG, 0,
+		     var->ops);
+}
+
+static void store_op(struct decode_var *var, s64 offset, unsigned char rd)
+{
+	add_stack_op(CFI_SP, OP_SRC_REG, 0, rd, OP_DEST_REG_INDIRECT, offset,
+		     var->ops);
+}
+
+/* ------------------------ decode functions ------------------------------- */
+
+#define is_saved_reg(rt)	((rt) == CFI_FP || (rt) == CFI_RA)
+#define is_frame_reg(rt)	((rt) == CFI_FP || (rt) == CFI_SP)
+
+/* ----- Add/Subtract instructions. ----- */
+
+#define CMN_OP		0x31000000	/* Alias of ADDS imm */
+#define CMP_OP		0x71000000	/* Alias of SUBS imm */
+
+static void add(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rd = var->insn & 0x1F;
+	unsigned int	rn = (var->insn >> 5) & 0x1F;
+	unsigned int	shift = (var->insn >> 22) & 1;
+
+	if (decode->op == CMN_OP || decode->op == CMP_OP)
+		return;
+
+	if (!is_frame_reg(rd))
+		return;
+
+	if (is_frame_reg(rn)) {
+		if (shift)
+			var->imm <<= 12;
+		add_op(var, rn, var->imm, rd);
+	} else {
+		var->type = INSN_UNRELIABLE;
+	}
+}
+
+#define CMN_EXT_OP	0x2B200000	/* Alias of ADDS ext */
+#define CMP_EXT_OP	0x6B200000	/* Alias of SUBS ext */
+
+static void addc(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rd = var->insn & 0x1F;
+
+	if (decode->op == CMN_EXT_OP || decode->op == CMP_EXT_OP)
+		return;
+
+	if (is_frame_reg(rd))
+		var->type = INSN_UNRELIABLE;
+}
+
+static void sub(struct decode *decode, struct decode_var *var)
+{
+	var->imm = -var->imm;
+	return add(decode, var);
+}
+
+/* ----- Load instructions. ----- */
+
+/*
+ * For some instructions, the target register cannot be FP. There are 3 cases:
+ *
+ *	- The register width is 32 bits. FP cannot be 32 bits.
+ *	- The register is loaded from one that is not the SP. We do not track
+ *	  the value of other registers in static analysis.
+ *	- The instruction does not make sense for the FP to be the target.
+ */
+static void check_reg(unsigned int reg, struct decode_var *var)
+{
+	if (reg == CFI_FP)
+		var->type = INSN_UNRELIABLE;
+}
+
+static void ldp(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rt1 = var->insn & 0x1F;
+	unsigned int	rt2 = (var->insn >> 10) & 0x1F;
+	unsigned int	rn = (var->insn >> 5) & 0x1F;
+	s64		imm;
+
+	if (rn != CFI_SP || var->check_reg) {
+		check_reg(rt1, var);
+		check_reg(rt2, var);
+	}
+
+	if (rn == CFI_SP) {
+		if (var->mode1 && var->mode2)	/* Pre-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+
+		imm = var->mode1 ? 0 : var->imm;
+		if (is_saved_reg(rt1))
+			load_op(var, imm, rt1);
+		if (is_saved_reg(rt2))
+			load_op(var, imm + 8, rt2);
+
+		if (var->mode1 && !var->mode2)	/* Post-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+	}
+}
+
+static void ldpc(struct decode *decode, struct decode_var *var)
+{
+	var->check_reg = 1;
+	ldp(decode, var);
+}
+
+static void ldr(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rd = var->insn & 0x1F;
+	unsigned int	rn = (var->insn >> 5) & 0x1F;
+	s64		imm;
+
+	if (rn != CFI_SP || var->check_reg)
+		check_reg(rd, var);
+
+	if (rn == CFI_SP) {
+		if (var->mode1 && var->mode2)	/* Pre-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+
+		imm = var->mode1 ? 0 : var->imm;
+		if (is_saved_reg(rd))
+			load_op(var, imm, rd);
+
+		if (var->mode1 && !var->mode2)	/* Post-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+	}
+}
+
+/* ----- Store instructions. ----- */
+
+static void stp(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rt1 = var->insn & 0x1F;
+	unsigned int	rt2 = (var->insn >> 10) & 0x1F;
+	unsigned int	rn = (var->insn >> 5) & 0x1F;
+	s64		imm;
+
+	if (var->check_reg) {
+		check_reg(rt1, var);
+		check_reg(rt2, var);
+	}
+
+	if (rn == CFI_SP) {
+		if (var->mode1 && var->mode2)	/* Pre-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+
+		imm = var->mode1 ? 0 : var->imm;
+		if (is_saved_reg(rt1))
+			store_op(var, imm, rt1);
+		if (is_saved_reg(rt2))
+			store_op(var, imm + 8, rt2);
+
+		if (var->mode1 && !var->mode2)	/* Post-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+	}
+}
+
+static void stpc(struct decode *decode, struct decode_var *var)
+{
+	var->check_reg = 1;
+	stp(decode, var);
+}
+
+static void str(struct decode *decode, struct decode_var *var)
+{
+	unsigned int	rd = var->insn & 0x1F;
+	unsigned int	rn = (var->insn >> 5) & 0x1F;
+	s64		imm;
+
+	if (var->check_reg)
+		check_reg(rd, var);
+
+	if (rn == CFI_SP) {
+		if (var->mode1 && var->mode2)	/* Pre-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+
+		imm = var->mode1 ? 0 : var->imm;
+		if (is_saved_reg(rd))
+			store_op(var, imm, rd);
+
+		if (var->mode1 && !var->mode2)	/* Post-index */
+			add_op(var, CFI_SP, var->imm, CFI_SP);
+	}
+}
+
+static void strc(struct decode *decode, struct decode_var *var)
+{
+	var->check_reg = 1;
+	str(decode, var);
+}
+
+/* ----- Control transfer instructions. ----- */
+
+#define BR_UNCONDITIONAL		0x14000000
+
+static void bra(struct decode *decode, struct decode_var *var)
+{
+	if (var->imm) {
+		if (decode->op == BR_UNCONDITIONAL)
+			var->type = INSN_JUMP_UNCONDITIONAL;
+		else
+			var->type = INSN_JUMP_CONDITIONAL;
+	} else {
+		var->type = INSN_JUMP_DYNAMIC;
+	}
+}
+
+static void call(struct decode *decode, struct decode_var *var)
+{
+	var->type = var->imm ? INSN_CALL : INSN_CALL_DYNAMIC;
+}
+
+static void ret(struct decode *decode, struct decode_var *var)
+{
+	var->type = INSN_RETURN;
+}
+
+/* ----- Miscellaneous instructions. ----- */
+
+static void bug(struct decode *decode, struct decode_var *var)
+{
+	var->type = INSN_BUG;
+}
+
+static void pac(struct decode *decode, struct decode_var *var)
+{
+	var->type = INSN_START;
+}
+
+/* ------------------------ Instruction decode ----------------------------- */
+
+struct decode	decode_array[] = {
+/*
+ * mask		OP code mask
+ * opcode	OP code
+ * width	Target register width. Values can be:
+ *			64 (64-bit)
+ *			32 (32-bit),
+ *			 X (64-bit if bit X in the instruction is set)
+ *			-X (32-bit if bit X in the instruction is set)
+ * shift	Shift for the immediate value
+ * bits		Number of bits in the immediate value
+ * sign		Sign extend the immediate value
+ * mult		Multiplier for the immediate value
+ * am1		Addressing mode bit 1
+ * am2		Addressing mode bit 2
+ * func		Decode function
+ *
+ * =============================== INSTRUCTIONS ===============================
+ * mask       opcode   width shift bits sign mult am1 am2 func
+ * ============================================================================
+ */
+{ 0x7E400000, 0x28400000,  31, 15,  7,  1,   0,   23, 24, ldp  /* LDP       */},
+{ 0x7E400000, 0x68400000,  32, 15,  7,  1,   4,   23, 24, ldp  /* LDPSW     */},
+{ 0x7FC00000, 0x28400000,  31, 15,  7,  1,   0,    0,  0, ldpc /* LDNP      */},
+{ 0xBFE00000, 0xB8400000,  30, 12,  9,  1,   1,   10, 11, ldr  /* LDR       */},
+{ 0xBFC00000, 0xB9400000,  30, 10, 12,  0,   0,    0,  0, ldr  /* LDR off   */},
+{ 0xFF200400, 0xF8200400,  64, 12,  9,  1,   8,   11, 11, ldr  /* LDRA      */},
+{ 0xFFC00000, 0x39400000,  32, 10, 12,  0,   1,    0,  0, ldr  /* LDRB off  */},
+{ 0xFFE00000, 0x38400000,  32, 12,  9,  1,   1,   10, 11, ldr  /* LDRB      */},
+{ 0xFFC00000, 0x79400000,  32, 10, 12,  0,   2,    0,  0, ldr  /* LDRH off  */},
+{ 0xFFE00000, 0x78400000,  32, 12,  9,  1,   1,   10, 11, ldr  /* LDRH      */},
+{ 0xFF800000, 0x39800000, -22, 10, 12,  0,   1,    0,  0, ldr  /* LDRSB off */},
+{ 0xFFA00000, 0x38800000, -22, 12,  9,  1,   1,   10, 11, ldr  /* LDRSB     */},
+{ 0xFF800000, 0x79800000, -22, 10, 12,  0,   2,    0,  0, ldr  /* LDRSH off */},
+{ 0xFFA00000, 0x78800000, -22, 12,  9,  1,   1,   10, 11, ldr  /* LDRSH     */},
+{ 0xFFC00000, 0xB9800000,  32, 10, 12,  0,   4,    0,  0, ldr  /* LDRSW off */},
+{ 0xFFE00000, 0xB8800000,  32, 12,  9,  1,   1,   10, 11, ldr  /* LDRSW     */},
+{ 0x7E000000, 0x28000000,  31, 15,  7,  1,   0,   23, 24, stp  /* STP       */},
+{ 0x7E400000, 0x28000000,  31, 15,  7,  1,   0,   23, 24, stp  /* STG       */},
+{ 0xFE400000, 0x68000000,  64, 15,  7,  1,  16,   23, 24, stpc /* STGP      */},
+{ 0x7FC00000, 0x28000000,  31, 15,  7,  1,   0,    0,  0, stpc /* STNP      */},
+{ 0xBFC00000, 0xB9000000,  30, 10, 12,  0,   0,    0,  0, str  /* STR off   */},
+{ 0xBFE00000, 0xB8000000,  30, 12,  9,  1,   1,   10, 11, str  /* STR       */},
+{ 0xFFE00000, 0xD9200000,  64, 12,  9,  1,  16,   10, 11, strc /* STG       */},
+{ 0xFFE00000, 0xD9A00000,  64, 12,  9,  1,  16,   10, 11, strc /* ST2G      */},
+{ 0x7F800000, 0x11000000,  31, 10, 12,  0,   1,    0,  0, add  /* ADD imm   */},
+{ 0x7FE00000, 0x0B200000,  31, 10,  3,  0,   1,    0,  0, addc /* ADD ext   */},
+{ 0x7F800000, 0x31000000,  31, 10, 12,  0,   1,    0,  0, add  /* ADDS imm  */},
+{ 0x7FE00000, 0x2B200000,  31, 10,  3,  0,   1,    0,  0, addc /* ADDS ext  */},
+{ 0x7F800000, 0x51000000,  31, 10, 12,  0,   1,    0,  0, sub  /* SUB imm   */},
+{ 0x7FE00000, 0x4B200000,  31, 10,  3,  0,   1,    0,  0, addc /* SUB ext   */},
+{ 0x7F800000, 0x71000000,  31, 10, 12,  0,   1,    0,  0, sub  /* SUBS imm  */},
+{ 0x7FE00000, 0x6B200000,  31, 10,  3,  0,   1,    0,  0, addc /* SUBS ext  */},
+{ 0xFC000000, 0x14000000,  64,  0, 26,  1,   4,    0,  0, bra  /* B         */},
+{ 0xFF000010, 0x54000000,  64,  5, 19,  1,   4,    0,  0, bra  /* B.cond    */},
+{ 0xFF000010, 0x54000010,  64,  5, 19,  1,   4,    0,  0, bra  /* BC.cond   */},
+{ 0xFFFFFC1F, 0xD61F0000,  64,  0,  0,  0,   0,    0,  0, bra  /* BR        */},
+{ 0xFEFFF800, 0xD61F0800,  64,  0,  0,  0,   0,    0,  0, bra  /* BRA       */},
+{ 0x7E000000, 0x34000000,  31,  5, 19,  1,   4,    0,  0, bra  /* CBZ/CBNZ  */},
+{ 0x7E000000, 0x36000000,  31,  5, 14,  1,   4,    0,  0, bra  /* TBZ/TBNZ  */},
+{ 0xFC000000, 0x94000000,  64,  0, 26,  1,   4,    0,  0, call /* BL        */},
+{ 0xFFFFFC1F, 0xD63F0000,  64,  0,  0,  0,   0,    0,  0, call /* BLR       */},
+{ 0xFEFFF800, 0xD63F0800,  64,  0,  0,  0,   0,    0,  0, call /* BLRA      */},
+{ 0xFFFFFC1F, 0xD65F0000,  64,  0,  0,  0,   0,    0,  0, ret  /* RET       */},
+{ 0xFFFFFBFF, 0xD65F0BFF,  64,  0,  0,  0,   0,    0,  0, ret  /* RETA      */},
+{ 0xFFFFFFFF, 0xD69F03E0,  64,  0,  0,  0,   0,    0,  0, ret  /* ERET      */},
+{ 0xFFFFFBFF, 0xD69F0BFF,  64,  0,  0,  0,   0,    0,  0, ret  /* ERETA     */},
+{ 0xFFE00000, 0xD4200000,  64,  5, 16,  0,   1,    0,  0, bug  /* BRK       */},
+{ 0xFFFFFFFF, 0xD503233F,  64,  0,  0,  0,   1,    0,  0, pac  /* PACIASP   */},
+};
+unsigned int	ndecode = ARRAY_SIZE(decode_array);
+
+static void ignore(struct decode_var *var)
+{
+}
+
+static void check_target(struct decode_var *var)
+{
+	unsigned int	rd = var->insn & 0x1F;
+
+	check_reg(rd, var);
+}
+
+struct class	class_array[] = {
+/*
+ * mask		Class OP mask
+ * opcode	Class OP code
+ * check	Function to perform checks
+ *
+ * ========================== INSTRUCTION CLASSES =============================
+ * mask       opcode       check
+ * ============================================================================
+ */
+{ 0x1E000000, 0x00000000,  ignore        /* RSVD_00 */         },
+{ 0x1E000000, 0x02000000,  ignore        /* UNALLOC_01 */      },
+{ 0x1E000000, 0x04000000,  ignore        /* SVE_02 */          },
+{ 0x1E000000, 0x06000000,  ignore        /* UNALLOC_03 */      },
+{ 0x1E000000, 0x08000000,  check_target  /* LOAD_STORE_04 */   },
+{ 0x1E000000, 0x0A000000,  check_target  /* DP_REGISTER_05 */  },
+{ 0x1E000000, 0x0C000000,  ignore        /* LOAD_STORE_06 */   },
+{ 0x1E000000, 0x0E000000,  ignore        /* SIMD_FP_07 */      },
+{ 0x1E000000, 0x12000000,  check_target  /* DP_IMMEDIATE_09 */ },
+{ 0x1E000000, 0x10000000,  check_target  /* DP_IMMEDIATE_08 */ },
+{ 0x1E000000, 0x14000000,  check_target  /* BR_SYS_10 */       },
+{ 0x1E000000, 0x16000000,  check_target  /* BR_SYS_11 */       },
+{ 0x1E000000, 0x18000000,  check_target  /* LOAD_STORE_12 */   },
+{ 0x1E000000, 0x1A000000,  ignore        /* DP_REGISTER_13 */  },
+{ 0x1E000000, 0x1C000000,  check_target  /* LOAD_STORE_14 */   },
+{ 0x1E000000, 0x1E000000,  ignore        /* SIMD_FP_15 */      },
+};
+unsigned int	nclass = ARRAY_SIZE(class_array);
+
+static inline s64 sign_extend(s64 imm, unsigned int bits)
+{
+	return (imm << (64 - bits)) >> (64 - bits);
+}
 
 int arch_decode_instruction(struct objtool_file *file,
 			    const struct section *sec,
 			    unsigned long offset, unsigned int maxlen,
 			    unsigned int *len, enum insn_type *type,
 			    unsigned long *immediate,
-			    struct list_head *ops_list)
+			    struct list_head *ops)
 {
+	struct decode		*decode;
+	struct decode_var	var;
+	struct class		*class;
+	unsigned int		width, mask, mult, i;
+
+	if (maxlen < INSN_SIZE)
+		return -1;
+	*len = INSN_SIZE;
+
+	var.insn = *(u32 *)(sec->data->d_buf + offset);
+	var.type = INSN_OTHER;
+	var.imm = 0;
+	var.ops = ops;
+
+	*type = INSN_OTHER;
+
+	/* Decode the instruction, if listed. */
+	for (i = 0; i < ndecode; i++) {
+		decode = &decode_array[i];
+
+		if ((var.insn & decode->opmask) != decode->op)
+			continue;
+
+		/* Extract addressing mode (for some instructions). */
+		var.mode1 = 0;
+		var.mode2 = 0;
+		if (decode->mode1)
+			var.mode1 = (var.insn >> decode->mode1) & 1;
+		if (decode->mode2)
+			var.mode2 = (var.insn >> decode->mode2) & 1;
+
+		/* Determine target register width. */
+		width = decode->width;
+		if (width < 0)
+			width = (var.insn & (1 << -width)) ? 32 : 64;
+		else if (width < 32)
+			width = (var.insn & (1 << width)) ? 64 : 32;
+
+		/*
+		 * If the target register width is 32 bits, set the check flag
+		 * so that the target registers are checked to make sure they
+		 * are not the FP or the RA. We should not be using 32-bit
+		 * values in these registers.
+		 */
+		var.check_reg = (width == 32);
+
+		/* Extract the immediate value. */
+		mask = (1 << decode->bits) - 1;
+		var.imm = (var.insn >> decode->shift) & mask;
+		if (decode->sign_extend)
+			var.imm = sign_extend(var.imm, decode->bits);
+
+		/* Scale the immediate value. */
+		mult = decode->mult;
+		if (!mult)
+			mult = (width == 32) ? 4 : 8;
+		var.imm *= mult;
+
+		/* Decode the instruction. */
+		decode->func(decode, &var);
+		goto out;
+	}
+
+	/*
+	 * Sanity check to make sure that the compiler has not generated
+	 * code that modifies the FP or the RA in an unexpected way.
+	 */
+	for (i = 0; i < nclass; i++) {
+		class = &class_array[i];
+		if ((var.insn & class->opmask) == class->op) {
+			class->check(&var);
+			goto out;
+		}
+	}
+out:
+	*immediate = var.imm;
+	*type = var.type;
 	return 0;
 }
diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
index beb2f3aa94ff..3c2f8c1b8265 100644
--- a/tools/objtool/include/objtool/arch.h
+++ b/tools/objtool/include/objtool/arch.h
@@ -29,6 +29,8 @@ enum insn_type {
 	INSN_TRAP,
 	INSN_ENDBR,
 	INSN_OTHER,
+	INSN_START,
+	INSN_UNRELIABLE,
 };
 
 enum op_dest_type {
-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  parent reply	other threads:[~2023-02-02  7:42 UTC|newest]

Thread overview: 110+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <0337266cf19f4c98388e3f6d09f590d9de258dc7>
2023-02-02  7:40 ` [RFC PATCH v3 00/22] arm64: livepatch: Use ORC for dynamic frame pointer validation madvenka
2023-02-02  7:40   ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 01/22] objtool: Reorganize CFI code madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 02/22] objtool: Reorganize instruction-related code madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 03/22] objtool: Move decode_instructions() to a separate file madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 04/22] objtool: Reorganize Unwind hint code madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 05/22] objtool: Reorganize ORC types madvenka
2023-02-02  7:40     ` madvenka
2023-02-18  9:30     ` Suraj Jitindar Singh
2023-02-18  9:30       ` Suraj Jitindar Singh
2023-03-06 16:45       ` Madhavan T. Venkataraman
2023-03-06 16:45         ` Madhavan T. Venkataraman
2023-02-02  7:40   ` [RFC PATCH v3 06/22] objtool: Reorganize ORC code madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 07/22] objtool: Reorganize ORC kernel code madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 08/22] objtool: Introduce STATIC_CHECK madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 09/22] objtool: arm64: Add basic definitions and compile madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` madvenka [this message]
2023-02-02  7:40     ` [RFC PATCH v3 10/22] objtool: arm64: Implement decoder for Dynamic FP validation madvenka
2023-02-02  7:40   ` [RFC PATCH v3 11/22] objtool: arm64: Invoke the decoder madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 12/22] objtool: arm64: Compute destinations for call and jump instructions madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 13/22] objtool: arm64: Walk instructions and compute CFI for each instruction madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 14/22] objtool: arm64: Generate ORC data from CFI for object files madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 15/22] objtool: arm64: Add unwind hint support madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 16/22] arm64: Add unwind hints to exception handlers madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 17/22] arm64: Add kernel and module support for ORC madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 18/22] arm64: Build the kernel with ORC information madvenka
2023-02-02  7:40     ` madvenka
2023-02-10  7:52     ` Tomohiro Misono (Fujitsu)
2023-02-10  7:52       ` Tomohiro Misono (Fujitsu)
2023-02-11  4:34       ` Madhavan T. Venkataraman
2023-02-11  4:34         ` Madhavan T. Venkataraman
2023-02-02  7:40   ` [RFC PATCH v3 19/22] arm64: unwinder: Add a reliability check in the unwinder based on ORC madvenka
2023-02-02  7:40     ` madvenka
2023-02-23  4:07     ` Suraj Jitindar Singh
2023-02-23  4:07       ` Suraj Jitindar Singh
2023-03-06 16:52       ` Madhavan T. Venkataraman
2023-03-06 16:52         ` Madhavan T. Venkataraman
2023-02-02  7:40   ` [RFC PATCH v3 20/22] arm64: Define HAVE_DYNAMIC_FTRACE_WITH_ARGS madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 21/22] arm64: Define TIF_PATCH_PENDING for livepatch madvenka
2023-02-02  7:40     ` madvenka
2023-02-02  7:40   ` [RFC PATCH v3 22/22] arm64: Enable livepatch for ARM64 madvenka
2023-02-02  7:40     ` madvenka
2023-03-01  3:12   ` [RFC PATCH v3 00/22] arm64: livepatch: Use ORC for dynamic frame pointer validation Tomohiro Misono (Fujitsu)
2023-03-01  3:12     ` Tomohiro Misono (Fujitsu)
2023-03-02 16:23     ` Petr Mladek
2023-03-02 16:23       ` Petr Mladek
2023-03-03  9:40       ` Tomohiro Misono (Fujitsu)
2023-03-03  9:40         ` Tomohiro Misono (Fujitsu)
2023-03-06 16:58       ` Madhavan T. Venkataraman
2023-03-06 16:58         ` Madhavan T. Venkataraman
2023-03-06 16:57     ` Madhavan T. Venkataraman
2023-03-06 16:57       ` Madhavan T. Venkataraman
2023-03-23 17:17   ` Mark Rutland
2023-03-23 17:17     ` Mark Rutland
2023-04-08  3:40     ` Madhavan T. Venkataraman
2023-04-08  3:40       ` Madhavan T. Venkataraman
2023-04-11 13:25       ` Mark Rutland
2023-04-11 13:25         ` Mark Rutland
2023-04-12  4:17         ` Josh Poimboeuf
2023-04-12  4:17           ` Josh Poimboeuf
2023-04-12  4:48           ` Madhavan T. Venkataraman
2023-04-12  4:48             ` Madhavan T. Venkataraman
2023-04-12  4:50             ` Madhavan T. Venkataraman
2023-04-12  4:50               ` Madhavan T. Venkataraman
2023-04-12  5:01             ` Josh Poimboeuf
2023-04-12  5:01               ` Josh Poimboeuf
2023-04-12 14:50               ` Madhavan T. Venkataraman
2023-04-12 14:50                 ` Madhavan T. Venkataraman
2023-04-12 15:52                 ` Josh Poimboeuf
2023-04-12 15:52                   ` Josh Poimboeuf
2023-04-13 14:59                   ` Madhavan T. Venkataraman
2023-04-13 14:59                     ` Madhavan T. Venkataraman
2023-04-13 16:30                     ` Josh Poimboeuf
2023-04-13 16:30                       ` Josh Poimboeuf
2023-04-15  4:27                       ` Madhavan T. Venkataraman
2023-04-15  4:27                         ` Madhavan T. Venkataraman
2023-04-15  5:05                         ` Josh Poimboeuf
2023-04-15  5:05                           ` Josh Poimboeuf
2023-04-15 16:15                           ` Madhavan T. Venkataraman
2023-04-15 16:15                             ` Madhavan T. Venkataraman
2023-04-16  8:21                       ` Indu Bhagat
2023-04-16  8:21                         ` Indu Bhagat
2023-04-13 17:04     ` Nick Desaulniers
2023-04-13 17:04       ` Nick Desaulniers
2023-04-13 18:15       ` Jose E. Marchesi
2023-04-13 18:15         ` Jose E. Marchesi
2023-04-15  4:14         ` Madhavan T. Venkataraman
2023-04-15  4:14           ` Madhavan T. Venkataraman
2023-12-14 20:49     ` ARM64 Livepatch based on SFrame Madhavan T. Venkataraman
2023-12-14 20:49       ` Madhavan T. Venkataraman
2023-12-15 13:04       ` Mark Rutland
2023-12-15 13:04         ` Mark Rutland
2023-12-15 15:15         ` Madhavan T. Venkataraman
2023-12-15 15:15           ` Madhavan T. Venkataraman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230202074036.507249-11-madvenka@linux.microsoft.com \
    --to=madvenka@linux.microsoft.com \
    --cc=broonie@kernel.org \
    --cc=catalin.marinas@arm.com \
    --cc=chenzhongjin@huawei.com \
    --cc=jamorris@linux.microsoft.com \
    --cc=jpoimboe@redhat.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=nobuta.keiya@fujitsu.com \
    --cc=peterz@infradead.org \
    --cc=sjitindarsingh@gmail.com \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.