All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64
@ 2015-12-14 12:49 Li Bin
  2015-12-14 12:49 ` [RFC PATCH 1/3] livepatch: allow arch specific implementation Li Bin
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Li Bin @ 2015-12-14 12:49 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset depends on the on-going gcc feature "-fprolog-pad=N",
which will generate a pad of N nops at the beginning of each function.

Livepatch on arm64 can using the feature (that always placing one nop
at the beginning of the function). And when enable/disable func patching,
just modify the pad code to nop or branch. And that NOP and B instruction
are both safe instructions on arm64 which called "concurrent modification
and execution of instructions", that can be executed by one thread of
execution as they are being modified by another thread of execution without
requiring explicit synchronization.

And this method will improve performance significantly compared with the
method based on ftrace, especially for the critical function being frequently
called.

Li Bin (3):
  livepatch: allow arch specific implementation
  livepatch: module: arm64: extract the relocation code for reuse
  livepatch: arm64: add support for livepatch on arm64

 Makefile                           |    7 +-
 arch/arm64/Kconfig                 |    4 +
 arch/arm64/include/asm/livepatch.h |   45 +++++
 arch/arm64/include/asm/module.h    |    3 +
 arch/arm64/kernel/Makefile         |    1 +
 arch/arm64/kernel/livepatch.c      |  123 ++++++++++++
 arch/arm64/kernel/module.c         |  360 ++++++++++++++++++------------------
 kernel/livepatch/Kconfig           |   10 +-
 kernel/livepatch/core.c            |   45 +++--
 9 files changed, 403 insertions(+), 195 deletions(-)
 create mode 100644 arch/arm64/include/asm/livepatch.h
 create mode 100644 arch/arm64/kernel/livepatch.c

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 1/3] livepatch: allow arch specific implementation
  2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin
@ 2015-12-14 12:49 ` Li Bin
  2015-12-14 12:49 ` [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse Li Bin
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Li Bin @ 2015-12-14 12:49 UTC (permalink / raw)
  To: linux-arm-kernel

Allow klp_enable_func/klp_disable_func be specific implementation
for different arch.

Signed-off-by: Li Bin <huawei.libin@huawei.com>
---
 kernel/livepatch/core.c |   45 +++++++++++++++++++++++++++++----------------
 1 files changed, 29 insertions(+), 16 deletions(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index db545cb..0a8cf96 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -350,15 +350,10 @@ unlock:
 	rcu_read_unlock();
 }
 
-static void klp_disable_func(struct klp_func *func)
+void __weak arch_klp_disable_func(struct klp_func *func)
 {
 	struct klp_ops *ops;
 
-	if (WARN_ON(func->state != KLP_ENABLED))
-		return;
-	if (WARN_ON(!func->old_addr))
-		return;
-
 	ops = klp_find_ops(func->old_addr);
 	if (WARN_ON(!ops))
 		return;
@@ -373,21 +368,23 @@ static void klp_disable_func(struct klp_func *func)
 	} else {
 		list_del_rcu(&func->stack_node);
 	}
+}
+static void klp_disable_func(struct klp_func *func)
+{
+	if (WARN_ON(func->state != KLP_ENABLED))
+		return;
+	if (WARN_ON(!func->old_addr))
+		return;
 
+	arch_klp_disable_func(func);
 	func->state = KLP_DISABLED;
 }
 
-static int klp_enable_func(struct klp_func *func)
+int __weak arch_klp_enable_func(struct klp_func *func)
 {
 	struct klp_ops *ops;
 	int ret;
 
-	if (WARN_ON(!func->old_addr))
-		return -EINVAL;
-
-	if (WARN_ON(func->state != KLP_DISABLED))
-		return -EINVAL;
-
 	ops = klp_find_ops(func->old_addr);
 	if (!ops) {
 		ops = kzalloc(sizeof(*ops), GFP_KERNEL);
@@ -424,10 +421,7 @@ static int klp_enable_func(struct klp_func *func)
 		list_add_rcu(&func->stack_node, &ops->func_stack);
 	}
 
-	func->state = KLP_ENABLED;
-
 	return 0;
-
 err:
 	list_del_rcu(&func->stack_node);
 	list_del(&ops->node);
@@ -435,6 +429,25 @@ err:
 	return ret;
 }
 
+static int klp_enable_func(struct klp_func *func)
+{
+	int ret;
+
+	if (WARN_ON(!func->old_addr))
+		return -EINVAL;
+
+	if (WARN_ON(func->state != KLP_DISABLED))
+		return -EINVAL;
+
+	ret = arch_klp_enable_func(func);
+	if (ret)
+		return ret;
+
+	func->state = KLP_ENABLED;
+
+	return 0;
+}
+
 static void klp_disable_object(struct klp_object *obj)
 {
 	struct klp_func *func;
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse
  2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin
  2015-12-14 12:49 ` [RFC PATCH 1/3] livepatch: allow arch specific implementation Li Bin
@ 2015-12-14 12:49 ` Li Bin
  2015-12-14 15:29   ` Josh Poimboeuf
  2015-12-14 12:49 ` [RFC PATCH 3/3] livepatch: arm64: add support for livepatch on arm64 Li Bin
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Li Bin @ 2015-12-14 12:49 UTC (permalink / raw)
  To: linux-arm-kernel

Livepatch can reuse the relocation codes of module loader, this
patch extract it.

Signed-off-by: Li Bin <huawei.libin@huawei.com>
---
 arch/arm64/include/asm/module.h |    3 +
 arch/arm64/kernel/module.c      |  360 ++++++++++++++++++++-------------------
 2 files changed, 187 insertions(+), 176 deletions(-)

diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h
index e80e232..78ac36e 100644
--- a/arch/arm64/include/asm/module.h
+++ b/arch/arm64/include/asm/module.h
@@ -20,4 +20,7 @@
 
 #define MODULE_ARCH_VERMAGIC	"aarch64"
 
+extern int static_relocate(struct module *mod, unsigned long type,
+			   void * loc, unsigned long value);
+
 #endif /* __ASM_MODULE_H */
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index f4bc779..6d1a1e3 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -203,6 +203,184 @@ static int reloc_insn_imm(enum aarch64_reloc_op op, void *place, u64 val,
 	return 0;
 }
 
+int static_relocate(struct module *me, unsigned long type, void *loc,
+		    unsigned long val)
+{
+	int ovf = 0;
+	bool overflow_check = true;
+	/* Perform the static relocation. */
+	switch (type) {
+	/* Null relocations. */
+	case R_ARM_NONE:
+	case R_AARCH64_NONE:
+		ovf = 0;
+		break;
+
+		/* Data relocations. */
+	case R_AARCH64_ABS64:
+		overflow_check = false;
+		ovf = reloc_data(RELOC_OP_ABS, loc, val, 64);
+		break;
+	case R_AARCH64_ABS32:
+		ovf = reloc_data(RELOC_OP_ABS, loc, val, 32);
+		break;
+	case R_AARCH64_ABS16:
+		ovf = reloc_data(RELOC_OP_ABS, loc, val, 16);
+		break;
+	case R_AARCH64_PREL64:
+		overflow_check = false;
+		ovf = reloc_data(RELOC_OP_PREL, loc, val, 64);
+		break;
+	case R_AARCH64_PREL32:
+		ovf = reloc_data(RELOC_OP_PREL, loc, val, 32);
+		break;
+	case R_AARCH64_PREL16:
+		ovf = reloc_data(RELOC_OP_PREL, loc, val, 16);
+		break;
+
+		/* MOVW instruction relocations. */
+	case R_AARCH64_MOVW_UABS_G0_NC:
+		overflow_check = false;
+	case R_AARCH64_MOVW_UABS_G0:
+		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
+				      AARCH64_INSN_IMM_16);
+		break;
+	case R_AARCH64_MOVW_UABS_G1_NC:
+		overflow_check = false;
+	case R_AARCH64_MOVW_UABS_G1:
+		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
+				      AARCH64_INSN_IMM_16);
+		break;
+	case R_AARCH64_MOVW_UABS_G2_NC:
+		overflow_check = false;
+	case R_AARCH64_MOVW_UABS_G2:
+		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
+				      AARCH64_INSN_IMM_16);
+		break;
+	case R_AARCH64_MOVW_UABS_G3:
+		/* We're using the top bits so we can't overflow. */
+		overflow_check = false;
+		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48,
+				      AARCH64_INSN_IMM_16);
+		break;
+	case R_AARCH64_MOVW_SABS_G0:
+		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
+				      AARCH64_INSN_IMM_MOVNZ);
+		break;
+	case R_AARCH64_MOVW_SABS_G1:
+		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
+				      AARCH64_INSN_IMM_MOVNZ);
+		break;
+	case R_AARCH64_MOVW_SABS_G2:
+		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
+				      AARCH64_INSN_IMM_MOVNZ);
+		break;
+	case R_AARCH64_MOVW_PREL_G0_NC:
+		overflow_check = false;
+		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
+				      AARCH64_INSN_IMM_MOVK);
+		break;
+	case R_AARCH64_MOVW_PREL_G0:
+		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
+				      AARCH64_INSN_IMM_MOVNZ);
+		break;
+	case R_AARCH64_MOVW_PREL_G1_NC:
+		overflow_check = false;
+		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
+				      AARCH64_INSN_IMM_MOVK);
+		break;
+	case R_AARCH64_MOVW_PREL_G1:
+		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
+				      AARCH64_INSN_IMM_MOVNZ);
+		break;
+	case R_AARCH64_MOVW_PREL_G2_NC:
+		overflow_check = false;
+		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
+				      AARCH64_INSN_IMM_MOVK);
+		break;
+	case R_AARCH64_MOVW_PREL_G2:
+		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
+				      AARCH64_INSN_IMM_MOVNZ);
+		break;
+	case R_AARCH64_MOVW_PREL_G3:
+		/* We're using the top bits so we can't overflow. */
+		overflow_check = false;
+		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48,
+				      AARCH64_INSN_IMM_MOVNZ);
+		break;
+
+		/* Immediate instruction relocations. */
+	case R_AARCH64_LD_PREL_LO19:
+		ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
+				     AARCH64_INSN_IMM_19);
+		break;
+	case R_AARCH64_ADR_PREL_LO21:
+		ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21,
+				     AARCH64_INSN_IMM_ADR);
+		break;
+#ifndef CONFIG_ARM64_ERRATUM_843419
+	case R_AARCH64_ADR_PREL_PG_HI21_NC:
+		overflow_check = false;
+	case R_AARCH64_ADR_PREL_PG_HI21:
+		ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21,
+				     AARCH64_INSN_IMM_ADR);
+		break;
+#endif
+	case R_AARCH64_ADD_ABS_LO12_NC:
+	case R_AARCH64_LDST8_ABS_LO12_NC:
+		overflow_check = false;
+		ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12,
+				     AARCH64_INSN_IMM_12);
+		break;
+	case R_AARCH64_LDST16_ABS_LO12_NC:
+		overflow_check = false;
+		ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11,
+				     AARCH64_INSN_IMM_12);
+		break;
+	case R_AARCH64_LDST32_ABS_LO12_NC:
+		overflow_check = false;
+		ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10,
+				     AARCH64_INSN_IMM_12);
+		break;
+	case R_AARCH64_LDST64_ABS_LO12_NC:
+		overflow_check = false;
+		ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9,
+				     AARCH64_INSN_IMM_12);
+		break;
+	case R_AARCH64_LDST128_ABS_LO12_NC:
+		overflow_check = false;
+		ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8,
+				     AARCH64_INSN_IMM_12);
+		break;
+	case R_AARCH64_TSTBR14:
+		ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14,
+				     AARCH64_INSN_IMM_14);
+		break;
+	case R_AARCH64_CONDBR19:
+		ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
+				     AARCH64_INSN_IMM_19);
+		break;
+	case R_AARCH64_JUMP26:
+	case R_AARCH64_CALL26:
+		ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26,
+				     AARCH64_INSN_IMM_26);
+		break;
+
+	default:
+		pr_err("module %s: unsupported RELA relocation: %lu\n",
+			me->name, type);
+		return -ENOEXEC;
+	}
+
+	if (overflow_check && ovf == -ERANGE) {
+		pr_err("module %s: overflow in relocation type %lu val %lx\n",
+			me->name, type, val);
+		return -ENOEXEC;
+	}
+
+	return 0;
+}
+
 int apply_relocate_add(Elf64_Shdr *sechdrs,
 		       const char *strtab,
 		       unsigned int symindex,
@@ -210,12 +388,11 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
 		       struct module *me)
 {
 	unsigned int i;
-	int ovf;
-	bool overflow_check;
 	Elf64_Sym *sym;
 	void *loc;
 	u64 val;
 	Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
+	int type, ret;
 
 	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
 		/* loc corresponds to P in the AArch64 ELF document. */
@@ -229,184 +406,15 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
 		/* val corresponds to (S + A) in the AArch64 ELF document. */
 		val = sym->st_value + rel[i].r_addend;
 
-		/* Check for overflow by default. */
-		overflow_check = true;
-
-		/* Perform the static relocation. */
-		switch (ELF64_R_TYPE(rel[i].r_info)) {
-		/* Null relocations. */
-		case R_ARM_NONE:
-		case R_AARCH64_NONE:
-			ovf = 0;
-			break;
-
-		/* Data relocations. */
-		case R_AARCH64_ABS64:
-			overflow_check = false;
-			ovf = reloc_data(RELOC_OP_ABS, loc, val, 64);
-			break;
-		case R_AARCH64_ABS32:
-			ovf = reloc_data(RELOC_OP_ABS, loc, val, 32);
-			break;
-		case R_AARCH64_ABS16:
-			ovf = reloc_data(RELOC_OP_ABS, loc, val, 16);
-			break;
-		case R_AARCH64_PREL64:
-			overflow_check = false;
-			ovf = reloc_data(RELOC_OP_PREL, loc, val, 64);
-			break;
-		case R_AARCH64_PREL32:
-			ovf = reloc_data(RELOC_OP_PREL, loc, val, 32);
-			break;
-		case R_AARCH64_PREL16:
-			ovf = reloc_data(RELOC_OP_PREL, loc, val, 16);
-			break;
-
-		/* MOVW instruction relocations. */
-		case R_AARCH64_MOVW_UABS_G0_NC:
-			overflow_check = false;
-		case R_AARCH64_MOVW_UABS_G0:
-			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
-					      AARCH64_INSN_IMM_16);
-			break;
-		case R_AARCH64_MOVW_UABS_G1_NC:
-			overflow_check = false;
-		case R_AARCH64_MOVW_UABS_G1:
-			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
-					      AARCH64_INSN_IMM_16);
-			break;
-		case R_AARCH64_MOVW_UABS_G2_NC:
-			overflow_check = false;
-		case R_AARCH64_MOVW_UABS_G2:
-			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
-					      AARCH64_INSN_IMM_16);
-			break;
-		case R_AARCH64_MOVW_UABS_G3:
-			/* We're using the top bits so we can't overflow. */
-			overflow_check = false;
-			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48,
-					      AARCH64_INSN_IMM_16);
-			break;
-		case R_AARCH64_MOVW_SABS_G0:
-			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
-					      AARCH64_INSN_IMM_MOVNZ);
-			break;
-		case R_AARCH64_MOVW_SABS_G1:
-			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
-					      AARCH64_INSN_IMM_MOVNZ);
-			break;
-		case R_AARCH64_MOVW_SABS_G2:
-			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
-					      AARCH64_INSN_IMM_MOVNZ);
-			break;
-		case R_AARCH64_MOVW_PREL_G0_NC:
-			overflow_check = false;
-			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
-					      AARCH64_INSN_IMM_MOVK);
-			break;
-		case R_AARCH64_MOVW_PREL_G0:
-			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
-					      AARCH64_INSN_IMM_MOVNZ);
-			break;
-		case R_AARCH64_MOVW_PREL_G1_NC:
-			overflow_check = false;
-			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
-					      AARCH64_INSN_IMM_MOVK);
-			break;
-		case R_AARCH64_MOVW_PREL_G1:
-			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
-					      AARCH64_INSN_IMM_MOVNZ);
-			break;
-		case R_AARCH64_MOVW_PREL_G2_NC:
-			overflow_check = false;
-			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
-					      AARCH64_INSN_IMM_MOVK);
-			break;
-		case R_AARCH64_MOVW_PREL_G2:
-			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
-					      AARCH64_INSN_IMM_MOVNZ);
-			break;
-		case R_AARCH64_MOVW_PREL_G3:
-			/* We're using the top bits so we can't overflow. */
-			overflow_check = false;
-			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48,
-					      AARCH64_INSN_IMM_MOVNZ);
-			break;
-
-		/* Immediate instruction relocations. */
-		case R_AARCH64_LD_PREL_LO19:
-			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
-					     AARCH64_INSN_IMM_19);
-			break;
-		case R_AARCH64_ADR_PREL_LO21:
-			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21,
-					     AARCH64_INSN_IMM_ADR);
-			break;
-#ifndef CONFIG_ARM64_ERRATUM_843419
-		case R_AARCH64_ADR_PREL_PG_HI21_NC:
-			overflow_check = false;
-		case R_AARCH64_ADR_PREL_PG_HI21:
-			ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21,
-					     AARCH64_INSN_IMM_ADR);
-			break;
-#endif
-		case R_AARCH64_ADD_ABS_LO12_NC:
-		case R_AARCH64_LDST8_ABS_LO12_NC:
-			overflow_check = false;
-			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12,
-					     AARCH64_INSN_IMM_12);
-			break;
-		case R_AARCH64_LDST16_ABS_LO12_NC:
-			overflow_check = false;
-			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11,
-					     AARCH64_INSN_IMM_12);
-			break;
-		case R_AARCH64_LDST32_ABS_LO12_NC:
-			overflow_check = false;
-			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10,
-					     AARCH64_INSN_IMM_12);
-			break;
-		case R_AARCH64_LDST64_ABS_LO12_NC:
-			overflow_check = false;
-			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9,
-					     AARCH64_INSN_IMM_12);
-			break;
-		case R_AARCH64_LDST128_ABS_LO12_NC:
-			overflow_check = false;
-			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8,
-					     AARCH64_INSN_IMM_12);
-			break;
-		case R_AARCH64_TSTBR14:
-			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14,
-					     AARCH64_INSN_IMM_14);
-			break;
-		case R_AARCH64_CONDBR19:
-			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
-					     AARCH64_INSN_IMM_19);
-			break;
-		case R_AARCH64_JUMP26:
-		case R_AARCH64_CALL26:
-			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26,
-					     AARCH64_INSN_IMM_26);
-			break;
-
-		default:
-			pr_err("module %s: unsupported RELA relocation: %llu\n",
-			       me->name, ELF64_R_TYPE(rel[i].r_info));
-			return -ENOEXEC;
-		}
-
-		if (overflow_check && ovf == -ERANGE)
-			goto overflow;
+		type = ELF64_R_TYPE(rel[i].r_info);
 
+		/* Check for overflow by default. */
+		ret = static_relocate(me, type, loc, val);
+		if (ret)
+			return ret;
 	}
 
 	return 0;
-
-overflow:
-	pr_err("module %s: overflow in relocation type %d val %Lx\n",
-	       me->name, (int)ELF64_R_TYPE(rel[i].r_info), val);
-	return -ENOEXEC;
 }
 
 int module_finalize(const Elf_Ehdr *hdr,
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 3/3] livepatch: arm64: add support for livepatch on arm64
  2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin
  2015-12-14 12:49 ` [RFC PATCH 1/3] livepatch: allow arch specific implementation Li Bin
  2015-12-14 12:49 ` [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse Li Bin
@ 2015-12-14 12:49 ` Li Bin
  2015-12-14 15:27 ` [RFC PATCH 0/3] " Josh Poimboeuf
  2015-12-15 15:43 ` Petr Mladek
  4 siblings, 0 replies; 9+ messages in thread
From: Li Bin @ 2015-12-14 12:49 UTC (permalink / raw)
  To: linux-arm-kernel

This patch add support for livepach on arm64 based on the gcc
-fprolog-pad feature (that always placing one nop at the beginning
of the function). And when enable/disable func patching, just
modify the pad code to nop or branch. And that NOP and B instruction
are both safe instructions on arm64 which called "concurrent
modification and execution of instructions", that can be executed
by one thread of execution as they are being modified by another
thread of execution without requiring explicit synchronization.

Signed-off-by: Li Bin <huawei.libin@huawei.com>
---
 Makefile                           |    7 ++-
 arch/arm64/Kconfig                 |    4 +
 arch/arm64/include/asm/livepatch.h |   45 +++++++++++++
 arch/arm64/kernel/Makefile         |    1 +
 arch/arm64/kernel/livepatch.c      |  123 ++++++++++++++++++++++++++++++++++++
 kernel/livepatch/Kconfig           |   10 +++-
 6 files changed, 187 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm64/include/asm/livepatch.h
 create mode 100644 arch/arm64/kernel/livepatch.c

diff --git a/Makefile b/Makefile
index d644f6e..6171fb8 100644
--- a/Makefile
+++ b/Makefile
@@ -727,8 +727,11 @@ export CC_FLAGS_FTRACE
 ifdef CONFIG_HAVE_FENTRY
 CC_USING_FENTRY	:= $(call cc-option, -mfentry -DCC_USING_FENTRY)
 endif
-KBUILD_CFLAGS	+= $(CC_FLAGS_FTRACE) $(CC_USING_FENTRY)
-KBUILD_AFLAGS	+= $(CC_USING_FENTRY)
+ifdef CONFIG_HAVE_PROLOG_PAD
+CC_USING_PROLOG_PAD := $(call cc-option, -fprolog-pad=1 -DCC_USING_PROLOG_PAD)
+endif
+KBUILD_CFLAGS  += $(CC_FLAGS_FTRACE) $(CC_USING_FENTRY) $(CC_USING_PROLOG_PAD)
+KBUILD_AFLAGS  += $(CC_USING_FENTRY) $(CC_USING_PROLOG_PAD)
 ifdef CONFIG_DYNAMIC_FTRACE
 	ifdef CONFIG_HAVE_C_RECORDMCOUNT
 		BUILD_C_RECORDMCOUNT := y
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 871f217..85e01b1 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -66,6 +66,7 @@ config ARM64
 	select HAVE_DYNAMIC_FTRACE
 	select HAVE_EFFICIENT_UNALIGNED_ACCESS
 	select HAVE_FTRACE_MCOUNT_RECORD
+	select HAVE_PROLOG_PAD
 	select HAVE_FUNCTION_TRACER
 	select HAVE_FUNCTION_GRAPH_TRACER
 	select HAVE_GENERIC_DMA_COHERENT
@@ -92,6 +93,7 @@ config ARM64
 	select SPARSE_IRQ
 	select SYSCTL_EXCEPTION_TRACE
 	select HAVE_CONTEXT_TRACKING
+	select HAVE_LIVEPATCH
 	help
 	  ARM 64-bit (AArch64) Linux support.
 
@@ -183,6 +185,8 @@ source "init/Kconfig"
 
 source "kernel/Kconfig.freezer"
 
+source "kernel/livepatch/Kconfig"
+
 source "arch/arm64/Kconfig.platforms"
 
 menu "Bus support"
diff --git a/arch/arm64/include/asm/livepatch.h b/arch/arm64/include/asm/livepatch.h
new file mode 100644
index 0000000..9bf6c0b
--- /dev/null
+++ b/arch/arm64/include/asm/livepatch.h
@@ -0,0 +1,45 @@
+/*
+ * livepatch.h - arm64-specific Kernel Live Patching Core
+ *
+ * Copyright (C) 2015 Li Bin <huawei.libin@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ASM_ARM64_LIVEPATCH_H
+#define _ASM_ARM64_LIVEPATCH_H
+
+#include <linux/module.h>
+#include <linux/ftrace.h>
+
+#ifdef CONFIG_LIVEPATCH
+static inline int klp_check_compiler_support(void)
+{
+#ifndef CC_USING_PROLOG_PAD
+	return 1;
+#endif
+	return 0;
+}
+extern int klp_write_module_reloc(struct module *mod, unsigned long type,
+				  unsigned long loc, unsigned long value);
+
+static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long pc)
+{
+	BUG();
+}
+#else
+#error Live patching support is disabled; check CONFIG_LIVEPATCH
+#endif
+
+#endif /* _ASM_ARM64_LIVEPATCH_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 474691f..59a415d 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -29,6 +29,7 @@ arm64-obj-$(CONFIG_COMPAT)		+= sys32.o kuser32.o signal32.o 	\
 					   sys_compat.o entry32.o		\
 					   ../../arm/kernel/opcodes.o
 arm64-obj-$(CONFIG_FUNCTION_TRACER)	+= ftrace.o entry-ftrace.o
+arm64-obj-$(CONFIG_LIVEPATCH) += livepatch.o
 arm64-obj-$(CONFIG_MODULES)		+= arm64ksyms.o module.o
 arm64-obj-$(CONFIG_PERF_EVENTS)		+= perf_regs.o perf_callchain.o
 arm64-obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
diff --git a/arch/arm64/kernel/livepatch.c b/arch/arm64/kernel/livepatch.c
new file mode 100644
index 0000000..a06f710
--- /dev/null
+++ b/arch/arm64/kernel/livepatch.c
@@ -0,0 +1,123 @@
+/*
+ * livepatch.c - arm64-specific Kernel Live Patching Core
+ *
+ * Copyright (C) 2015 Li Bin <huawei.libin@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/livepatch.h>
+#include <linux/slab.h>
+#include <asm/livepatch.h>
+#include <asm/insn.h>
+
+struct klp_func_node {
+	struct list_head node;
+	struct list_head func_stack;
+	unsigned long old_addr;
+};
+
+static LIST_HEAD(klp_func_list);
+
+/**
+ * klp_write_module_reloc() - write a relocation in a module
+ * @mod:	module in which the section to be modified is found
+ * @type:	ELF relocation type (see asm/elf.h)
+ * @loc:	address that the relocation should be written to
+ * @value:	relocation value (sym address + addend)
+ *
+ * This function writes a relocation to the specified location for
+ * a particular module.
+ */
+int klp_write_module_reloc(struct module *mod, unsigned long type,
+			   unsigned long loc, unsigned long value)
+{
+	/* Perform the static relocation. */
+	return static_relocate(mod, type, (void *)loc, value);
+}
+
+#ifdef CC_USING_PROLOG_PAD
+static struct klp_func_node *klp_find_func_node(unsigned long old_addr)
+{
+	struct klp_func_node *func_node;
+
+	list_for_each_entry(func_node, &klp_func_list, node) {
+		if (func_node->old_addr == old_addr)
+			return func_node;
+	}
+
+	return NULL;
+}
+
+void arch_klp_disable_func(struct klp_func *func)
+{
+	struct klp_func_node *func_node;
+	struct klp_func *next_func;
+	unsigned long pc, new_addr;
+	u32 insn;
+
+	func_node = klp_find_func_node(func->old_addr);
+	pc = func_node->old_addr;
+	if (list_is_singular(&func_node->func_stack)) {
+		list_del_rcu(&func->stack_node);
+		list_del(&func_node->node);
+		kfree(func_node);
+
+		insn = aarch64_insn_gen_nop();
+		aarch64_insn_patch_text_nosync((void *)pc, insn);
+	} else {
+		list_del_rcu(&func->stack_node);
+		next_func = list_first_or_null_rcu(&func_node->func_stack,
+				struct klp_func, stack_node);
+
+		new_addr = (unsigned long)next_func->new_func;
+		insn = aarch64_insn_gen_branch_imm(pc, new_addr,
+				AARCH64_INSN_BRANCH_NOLINK);
+
+		aarch64_insn_patch_text_nosync((void *)pc, insn);
+	}
+}
+
+int arch_klp_enable_func(struct klp_func *func)
+{
+	struct klp_func_node *func_node;
+	unsigned long pc, new_addr;
+	u32 insn;
+
+	func_node = klp_find_func_node(func->old_addr);
+	if (!func_node) {
+		func_node = kzalloc(sizeof(*func_node), GFP_KERNEL);
+		if (!func_node)
+			return -ENOMEM;
+
+		INIT_LIST_HEAD(&func_node->func_stack);
+		func_node->old_addr = func->old_addr;
+		list_add(&func_node->node, &klp_func_list);
+	}
+
+	list_add_rcu(&func->stack_node, &func_node->func_stack);
+
+	pc = func->old_addr;
+	new_addr = (unsigned long)func->new_func;
+	insn = aarch64_insn_gen_branch_imm(pc, new_addr,
+			                AARCH64_INSN_BRANCH_NOLINK);
+
+	if (aarch64_insn_patch_text_nosync((void *)pc, insn))
+		        return -EPERM;
+
+	return 0;
+}
+#endif
diff --git a/kernel/livepatch/Kconfig b/kernel/livepatch/Kconfig
index 0450225..287bb79 100644
--- a/kernel/livepatch/Kconfig
+++ b/kernel/livepatch/Kconfig
@@ -1,3 +1,11 @@
+config HAVE_PROLOG_PAD
+	bool
+
+config PROLOG_PAD
+	bool
+	depends on HAVE_PROLOG_PAD
+	default y
+
 config HAVE_LIVEPATCH
 	bool
 	help
@@ -5,7 +13,7 @@ config HAVE_LIVEPATCH
 
 config LIVEPATCH
 	bool "Kernel Live Patching"
-	depends on DYNAMIC_FTRACE_WITH_REGS
+	depends on DYNAMIC_FTRACE_WITH_REGS || PROLOG_PAD
 	depends on MODULES
 	depends on SYSFS
 	depends on KALLSYMS_ALL
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64
  2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin
                   ` (2 preceding siblings ...)
  2015-12-14 12:49 ` [RFC PATCH 3/3] livepatch: arm64: add support for livepatch on arm64 Li Bin
@ 2015-12-14 15:27 ` Josh Poimboeuf
  2015-12-15 15:43 ` Petr Mladek
  4 siblings, 0 replies; 9+ messages in thread
From: Josh Poimboeuf @ 2015-12-14 15:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Dec 14, 2015 at 08:49:35PM +0800, Li Bin wrote:
> This patchset depends on the on-going gcc feature "-fprolog-pad=N",
> which will generate a pad of N nops at the beginning of each function.
> 
> Livepatch on arm64 can using the feature (that always placing one nop
> at the beginning of the function). And when enable/disable func patching,
> just modify the pad code to nop or branch. And that NOP and B instruction
> are both safe instructions on arm64 which called "concurrent modification
> and execution of instructions", that can be executed by one thread of
> execution as they are being modified by another thread of execution without
> requiring explicit synchronization.
> 
> And this method will improve performance significantly compared with the
> method based on ftrace, especially for the critical function being frequently
> called.

I think the arch-specific code in this patch set would be greatly
affected by the consistency model when it finally arrives (but that's
currently gated by getting stacktool merged, which should be soon).

So I think it would be a good idea to wait until then before doing
something like this.

Is it possible to use ftrace instead?  That would be a much better short
term solution.

-- 
Josh

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse
  2015-12-14 12:49 ` [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse Li Bin
@ 2015-12-14 15:29   ` Josh Poimboeuf
  0 siblings, 0 replies; 9+ messages in thread
From: Josh Poimboeuf @ 2015-12-14 15:29 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Dec 14, 2015 at 08:49:37PM +0800, Li Bin wrote:
> Livepatch can reuse the relocation codes of module loader, this
> patch extract it.
> 
> Signed-off-by: Li Bin <huawei.libin@huawei.com>

FYI, this patch may be obsoleted by Jessica Yu's patches which are still
under discussion:

  [RFC PATCH v2 0/6] (mostly) Arch-independent livepatch


> ---
>  arch/arm64/include/asm/module.h |    3 +
>  arch/arm64/kernel/module.c      |  360 ++++++++++++++++++++-------------------
>  2 files changed, 187 insertions(+), 176 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h
> index e80e232..78ac36e 100644
> --- a/arch/arm64/include/asm/module.h
> +++ b/arch/arm64/include/asm/module.h
> @@ -20,4 +20,7 @@
>  
>  #define MODULE_ARCH_VERMAGIC	"aarch64"
>  
> +extern int static_relocate(struct module *mod, unsigned long type,
> +			   void * loc, unsigned long value);
> +
>  #endif /* __ASM_MODULE_H */
> diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
> index f4bc779..6d1a1e3 100644
> --- a/arch/arm64/kernel/module.c
> +++ b/arch/arm64/kernel/module.c
> @@ -203,6 +203,184 @@ static int reloc_insn_imm(enum aarch64_reloc_op op, void *place, u64 val,
>  	return 0;
>  }
>  
> +int static_relocate(struct module *me, unsigned long type, void *loc,
> +		    unsigned long val)
> +{
> +	int ovf = 0;
> +	bool overflow_check = true;
> +	/* Perform the static relocation. */
> +	switch (type) {
> +	/* Null relocations. */
> +	case R_ARM_NONE:
> +	case R_AARCH64_NONE:
> +		ovf = 0;
> +		break;
> +
> +		/* Data relocations. */
> +	case R_AARCH64_ABS64:
> +		overflow_check = false;
> +		ovf = reloc_data(RELOC_OP_ABS, loc, val, 64);
> +		break;
> +	case R_AARCH64_ABS32:
> +		ovf = reloc_data(RELOC_OP_ABS, loc, val, 32);
> +		break;
> +	case R_AARCH64_ABS16:
> +		ovf = reloc_data(RELOC_OP_ABS, loc, val, 16);
> +		break;
> +	case R_AARCH64_PREL64:
> +		overflow_check = false;
> +		ovf = reloc_data(RELOC_OP_PREL, loc, val, 64);
> +		break;
> +	case R_AARCH64_PREL32:
> +		ovf = reloc_data(RELOC_OP_PREL, loc, val, 32);
> +		break;
> +	case R_AARCH64_PREL16:
> +		ovf = reloc_data(RELOC_OP_PREL, loc, val, 16);
> +		break;
> +
> +		/* MOVW instruction relocations. */
> +	case R_AARCH64_MOVW_UABS_G0_NC:
> +		overflow_check = false;
> +	case R_AARCH64_MOVW_UABS_G0:
> +		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
> +				      AARCH64_INSN_IMM_16);
> +		break;
> +	case R_AARCH64_MOVW_UABS_G1_NC:
> +		overflow_check = false;
> +	case R_AARCH64_MOVW_UABS_G1:
> +		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
> +				      AARCH64_INSN_IMM_16);
> +		break;
> +	case R_AARCH64_MOVW_UABS_G2_NC:
> +		overflow_check = false;
> +	case R_AARCH64_MOVW_UABS_G2:
> +		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
> +				      AARCH64_INSN_IMM_16);
> +		break;
> +	case R_AARCH64_MOVW_UABS_G3:
> +		/* We're using the top bits so we can't overflow. */
> +		overflow_check = false;
> +		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48,
> +				      AARCH64_INSN_IMM_16);
> +		break;
> +	case R_AARCH64_MOVW_SABS_G0:
> +		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
> +				      AARCH64_INSN_IMM_MOVNZ);
> +		break;
> +	case R_AARCH64_MOVW_SABS_G1:
> +		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
> +				      AARCH64_INSN_IMM_MOVNZ);
> +		break;
> +	case R_AARCH64_MOVW_SABS_G2:
> +		ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
> +				      AARCH64_INSN_IMM_MOVNZ);
> +		break;
> +	case R_AARCH64_MOVW_PREL_G0_NC:
> +		overflow_check = false;
> +		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
> +				      AARCH64_INSN_IMM_MOVK);
> +		break;
> +	case R_AARCH64_MOVW_PREL_G0:
> +		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
> +				      AARCH64_INSN_IMM_MOVNZ);
> +		break;
> +	case R_AARCH64_MOVW_PREL_G1_NC:
> +		overflow_check = false;
> +		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
> +				      AARCH64_INSN_IMM_MOVK);
> +		break;
> +	case R_AARCH64_MOVW_PREL_G1:
> +		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
> +				      AARCH64_INSN_IMM_MOVNZ);
> +		break;
> +	case R_AARCH64_MOVW_PREL_G2_NC:
> +		overflow_check = false;
> +		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
> +				      AARCH64_INSN_IMM_MOVK);
> +		break;
> +	case R_AARCH64_MOVW_PREL_G2:
> +		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
> +				      AARCH64_INSN_IMM_MOVNZ);
> +		break;
> +	case R_AARCH64_MOVW_PREL_G3:
> +		/* We're using the top bits so we can't overflow. */
> +		overflow_check = false;
> +		ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48,
> +				      AARCH64_INSN_IMM_MOVNZ);
> +		break;
> +
> +		/* Immediate instruction relocations. */
> +	case R_AARCH64_LD_PREL_LO19:
> +		ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
> +				     AARCH64_INSN_IMM_19);
> +		break;
> +	case R_AARCH64_ADR_PREL_LO21:
> +		ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21,
> +				     AARCH64_INSN_IMM_ADR);
> +		break;
> +#ifndef CONFIG_ARM64_ERRATUM_843419
> +	case R_AARCH64_ADR_PREL_PG_HI21_NC:
> +		overflow_check = false;
> +	case R_AARCH64_ADR_PREL_PG_HI21:
> +		ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21,
> +				     AARCH64_INSN_IMM_ADR);
> +		break;
> +#endif
> +	case R_AARCH64_ADD_ABS_LO12_NC:
> +	case R_AARCH64_LDST8_ABS_LO12_NC:
> +		overflow_check = false;
> +		ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12,
> +				     AARCH64_INSN_IMM_12);
> +		break;
> +	case R_AARCH64_LDST16_ABS_LO12_NC:
> +		overflow_check = false;
> +		ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11,
> +				     AARCH64_INSN_IMM_12);
> +		break;
> +	case R_AARCH64_LDST32_ABS_LO12_NC:
> +		overflow_check = false;
> +		ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10,
> +				     AARCH64_INSN_IMM_12);
> +		break;
> +	case R_AARCH64_LDST64_ABS_LO12_NC:
> +		overflow_check = false;
> +		ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9,
> +				     AARCH64_INSN_IMM_12);
> +		break;
> +	case R_AARCH64_LDST128_ABS_LO12_NC:
> +		overflow_check = false;
> +		ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8,
> +				     AARCH64_INSN_IMM_12);
> +		break;
> +	case R_AARCH64_TSTBR14:
> +		ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14,
> +				     AARCH64_INSN_IMM_14);
> +		break;
> +	case R_AARCH64_CONDBR19:
> +		ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
> +				     AARCH64_INSN_IMM_19);
> +		break;
> +	case R_AARCH64_JUMP26:
> +	case R_AARCH64_CALL26:
> +		ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26,
> +				     AARCH64_INSN_IMM_26);
> +		break;
> +
> +	default:
> +		pr_err("module %s: unsupported RELA relocation: %lu\n",
> +			me->name, type);
> +		return -ENOEXEC;
> +	}
> +
> +	if (overflow_check && ovf == -ERANGE) {
> +		pr_err("module %s: overflow in relocation type %lu val %lx\n",
> +			me->name, type, val);
> +		return -ENOEXEC;
> +	}
> +
> +	return 0;
> +}
> +
>  int apply_relocate_add(Elf64_Shdr *sechdrs,
>  		       const char *strtab,
>  		       unsigned int symindex,
> @@ -210,12 +388,11 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
>  		       struct module *me)
>  {
>  	unsigned int i;
> -	int ovf;
> -	bool overflow_check;
>  	Elf64_Sym *sym;
>  	void *loc;
>  	u64 val;
>  	Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
> +	int type, ret;
>  
>  	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
>  		/* loc corresponds to P in the AArch64 ELF document. */
> @@ -229,184 +406,15 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
>  		/* val corresponds to (S + A) in the AArch64 ELF document. */
>  		val = sym->st_value + rel[i].r_addend;
>  
> -		/* Check for overflow by default. */
> -		overflow_check = true;
> -
> -		/* Perform the static relocation. */
> -		switch (ELF64_R_TYPE(rel[i].r_info)) {
> -		/* Null relocations. */
> -		case R_ARM_NONE:
> -		case R_AARCH64_NONE:
> -			ovf = 0;
> -			break;
> -
> -		/* Data relocations. */
> -		case R_AARCH64_ABS64:
> -			overflow_check = false;
> -			ovf = reloc_data(RELOC_OP_ABS, loc, val, 64);
> -			break;
> -		case R_AARCH64_ABS32:
> -			ovf = reloc_data(RELOC_OP_ABS, loc, val, 32);
> -			break;
> -		case R_AARCH64_ABS16:
> -			ovf = reloc_data(RELOC_OP_ABS, loc, val, 16);
> -			break;
> -		case R_AARCH64_PREL64:
> -			overflow_check = false;
> -			ovf = reloc_data(RELOC_OP_PREL, loc, val, 64);
> -			break;
> -		case R_AARCH64_PREL32:
> -			ovf = reloc_data(RELOC_OP_PREL, loc, val, 32);
> -			break;
> -		case R_AARCH64_PREL16:
> -			ovf = reloc_data(RELOC_OP_PREL, loc, val, 16);
> -			break;
> -
> -		/* MOVW instruction relocations. */
> -		case R_AARCH64_MOVW_UABS_G0_NC:
> -			overflow_check = false;
> -		case R_AARCH64_MOVW_UABS_G0:
> -			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
> -					      AARCH64_INSN_IMM_16);
> -			break;
> -		case R_AARCH64_MOVW_UABS_G1_NC:
> -			overflow_check = false;
> -		case R_AARCH64_MOVW_UABS_G1:
> -			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
> -					      AARCH64_INSN_IMM_16);
> -			break;
> -		case R_AARCH64_MOVW_UABS_G2_NC:
> -			overflow_check = false;
> -		case R_AARCH64_MOVW_UABS_G2:
> -			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
> -					      AARCH64_INSN_IMM_16);
> -			break;
> -		case R_AARCH64_MOVW_UABS_G3:
> -			/* We're using the top bits so we can't overflow. */
> -			overflow_check = false;
> -			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48,
> -					      AARCH64_INSN_IMM_16);
> -			break;
> -		case R_AARCH64_MOVW_SABS_G0:
> -			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
> -					      AARCH64_INSN_IMM_MOVNZ);
> -			break;
> -		case R_AARCH64_MOVW_SABS_G1:
> -			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
> -					      AARCH64_INSN_IMM_MOVNZ);
> -			break;
> -		case R_AARCH64_MOVW_SABS_G2:
> -			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
> -					      AARCH64_INSN_IMM_MOVNZ);
> -			break;
> -		case R_AARCH64_MOVW_PREL_G0_NC:
> -			overflow_check = false;
> -			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
> -					      AARCH64_INSN_IMM_MOVK);
> -			break;
> -		case R_AARCH64_MOVW_PREL_G0:
> -			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
> -					      AARCH64_INSN_IMM_MOVNZ);
> -			break;
> -		case R_AARCH64_MOVW_PREL_G1_NC:
> -			overflow_check = false;
> -			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
> -					      AARCH64_INSN_IMM_MOVK);
> -			break;
> -		case R_AARCH64_MOVW_PREL_G1:
> -			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
> -					      AARCH64_INSN_IMM_MOVNZ);
> -			break;
> -		case R_AARCH64_MOVW_PREL_G2_NC:
> -			overflow_check = false;
> -			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
> -					      AARCH64_INSN_IMM_MOVK);
> -			break;
> -		case R_AARCH64_MOVW_PREL_G2:
> -			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
> -					      AARCH64_INSN_IMM_MOVNZ);
> -			break;
> -		case R_AARCH64_MOVW_PREL_G3:
> -			/* We're using the top bits so we can't overflow. */
> -			overflow_check = false;
> -			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48,
> -					      AARCH64_INSN_IMM_MOVNZ);
> -			break;
> -
> -		/* Immediate instruction relocations. */
> -		case R_AARCH64_LD_PREL_LO19:
> -			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
> -					     AARCH64_INSN_IMM_19);
> -			break;
> -		case R_AARCH64_ADR_PREL_LO21:
> -			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21,
> -					     AARCH64_INSN_IMM_ADR);
> -			break;
> -#ifndef CONFIG_ARM64_ERRATUM_843419
> -		case R_AARCH64_ADR_PREL_PG_HI21_NC:
> -			overflow_check = false;
> -		case R_AARCH64_ADR_PREL_PG_HI21:
> -			ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21,
> -					     AARCH64_INSN_IMM_ADR);
> -			break;
> -#endif
> -		case R_AARCH64_ADD_ABS_LO12_NC:
> -		case R_AARCH64_LDST8_ABS_LO12_NC:
> -			overflow_check = false;
> -			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12,
> -					     AARCH64_INSN_IMM_12);
> -			break;
> -		case R_AARCH64_LDST16_ABS_LO12_NC:
> -			overflow_check = false;
> -			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11,
> -					     AARCH64_INSN_IMM_12);
> -			break;
> -		case R_AARCH64_LDST32_ABS_LO12_NC:
> -			overflow_check = false;
> -			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10,
> -					     AARCH64_INSN_IMM_12);
> -			break;
> -		case R_AARCH64_LDST64_ABS_LO12_NC:
> -			overflow_check = false;
> -			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9,
> -					     AARCH64_INSN_IMM_12);
> -			break;
> -		case R_AARCH64_LDST128_ABS_LO12_NC:
> -			overflow_check = false;
> -			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8,
> -					     AARCH64_INSN_IMM_12);
> -			break;
> -		case R_AARCH64_TSTBR14:
> -			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14,
> -					     AARCH64_INSN_IMM_14);
> -			break;
> -		case R_AARCH64_CONDBR19:
> -			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
> -					     AARCH64_INSN_IMM_19);
> -			break;
> -		case R_AARCH64_JUMP26:
> -		case R_AARCH64_CALL26:
> -			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26,
> -					     AARCH64_INSN_IMM_26);
> -			break;
> -
> -		default:
> -			pr_err("module %s: unsupported RELA relocation: %llu\n",
> -			       me->name, ELF64_R_TYPE(rel[i].r_info));
> -			return -ENOEXEC;
> -		}
> -
> -		if (overflow_check && ovf == -ERANGE)
> -			goto overflow;
> +		type = ELF64_R_TYPE(rel[i].r_info);
>  
> +		/* Check for overflow by default. */
> +		ret = static_relocate(me, type, loc, val);
> +		if (ret)
> +			return ret;
>  	}
>  
>  	return 0;
> -
> -overflow:
> -	pr_err("module %s: overflow in relocation type %d val %Lx\n",
> -	       me->name, (int)ELF64_R_TYPE(rel[i].r_info), val);
> -	return -ENOEXEC;
>  }
>  
>  int module_finalize(const Elf_Ehdr *hdr,
> -- 
> 1.7.1
> 

-- 
Josh

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64
  2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin
                   ` (3 preceding siblings ...)
  2015-12-14 15:27 ` [RFC PATCH 0/3] " Josh Poimboeuf
@ 2015-12-15 15:43 ` Petr Mladek
  2015-12-16  6:04   ` Li Bin
  4 siblings, 1 reply; 9+ messages in thread
From: Petr Mladek @ 2015-12-15 15:43 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon 2015-12-14 20:49:35, Li Bin wrote:
> This patchset depends on the on-going gcc feature "-fprolog-pad=N",
> which will generate a pad of N nops at the beginning of each function.
> 
> Livepatch on arm64 can using the feature (that always placing one nop
> at the beginning of the function). And when enable/disable func patching,
> just modify the pad code to nop or branch. And that NOP and B instruction
> are both safe instructions on arm64 which called "concurrent modification
> and execution of instructions", that can be executed by one thread of
> execution as they are being modified by another thread of execution without
> requiring explicit synchronization.
> 
> And this method will improve performance significantly compared with the
> method based on ftrace, especially for the critical function being frequently
> called.

It sounds like a great feature for ftrace. If the new prologue is usable
for LivePatching, it should be usable to call the ftrace handler as
well. If you teach ftrace to use the new prologue, you will not need
all these crazy arch-specific hacks for LivePatching. Then both ftrace
and livepatch will benefit from the feature.

I suggest to read the ftrace documentation in Documentation/trace/,
especially ftrace-design.txt. I have never ported ftrace to a new
architecture. I guess that you need to teach scripts/recordmcount.c
 to find the new location. Also you might need to update
arch/arm/kernel/ftrace.c. Also please make sure that ftrace
supports DYNAMIC_FTRACE_WITH_REGS on Arm.

It is harder but the right way to go. Your current patch set
looks like a hack.

Best Regards,
Petr

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64
  2015-12-15 15:43 ` Petr Mladek
@ 2015-12-16  6:04   ` Li Bin
  2015-12-17 12:36     ` Petr Mladek
  0 siblings, 1 reply; 9+ messages in thread
From: Li Bin @ 2015-12-16  6:04 UTC (permalink / raw)
  To: linux-arm-kernel



on 2015/12/15 23:43, Petr Mladek wrote:
> On Mon 2015-12-14 20:49:35, Li Bin wrote:
>> This patchset depends on the on-going gcc feature "-fprolog-pad=N",
>> which will generate a pad of N nops at the beginning of each function.
>>
>> Livepatch on arm64 can using the feature (that always placing one nop
>> at the beginning of the function). And when enable/disable func patching,
>> just modify the pad code to nop or branch. And that NOP and B instruction
>> are both safe instructions on arm64 which called "concurrent modification
>> and execution of instructions", that can be executed by one thread of
>> execution as they are being modified by another thread of execution without
>> requiring explicit synchronization.
>>
>> And this method will improve performance significantly compared with the
>> method based on ftrace, especially for the critical function being frequently
>> called.
> It sounds like a great feature for ftrace. If the new prologue is usable
> for LivePatching, it should be usable to call the ftrace handler as
> well. If you teach ftrace to use the new prologue, you will not need
> all these crazy arch-specific hacks for LivePatching. Then both ftrace
> and livepatch will benefit from the feature.
>
> I suggest to read the ftrace documentation in Documentation/trace/,
> especially ftrace-design.txt. I have never ported ftrace to a new
> architecture. I guess that you need to teach scripts/recordmcount.c
>  to find the new location. Also you might need to update
> arch/arm/kernel/ftrace.c. Also please make sure that ftrace
> supports DYNAMIC_FTRACE_WITH_REGS on Arm.

Hi Petr,
I had posted one method for livepatch implementaion on arm64 based on ftrace with
DYNAMIC_FTRACE_WITH_REGS.
https://lwn.net/Articles/646317/

This requires GCC changes to support it, and I propose a method that implement -mfentry
feature which following some other arch such as x86/mips/s390 etc. This method is
architecture-specific, and Maxim Kuvyrkov propose a new method for gcc that implement
a target-independent option -fprolog-pad=N, which will generate a pad of N nops at the
beginning of each function.
https://gcc.gnu.org/ml/gcc/2015-05/msg00267.html

And based on this, DYNAMIC_FTRACE_WITH_REGS feature can be implemented as
mentioned in:
http://lists.infradead.org/pipermail/linux-arm-kernel/2015-November/386815.html

And in this patchset, I only concern the performance for livepatch on arm64, and I
hope this also can be resolved by improving ftrace, and thanks to Steve that he pointed
that he is working on a way to make ftrace a bit better for livepatch.

Thanks,
Li Bin

> It is harder but the right way to go. Your current patch set
> looks like a hack.
>
> Best Regards,
> Petr
>
> .
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64
  2015-12-16  6:04   ` Li Bin
@ 2015-12-17 12:36     ` Petr Mladek
  0 siblings, 0 replies; 9+ messages in thread
From: Petr Mladek @ 2015-12-17 12:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed 2015-12-16 14:04:33, Li Bin wrote:
> 
> 
> on 2015/12/15 23:43, Petr Mladek wrote:
> > On Mon 2015-12-14 20:49:35, Li Bin wrote:
> >> This patchset depends on the on-going gcc feature "-fprolog-pad=N",
> >> which will generate a pad of N nops at the beginning of each function.
> >>
> >> Livepatch on arm64 can using the feature (that always placing one nop
> >> at the beginning of the function). And when enable/disable func patching,
> >> just modify the pad code to nop or branch. And that NOP and B instruction
> >> are both safe instructions on arm64 which called "concurrent modification
> >> and execution of instructions", that can be executed by one thread of
> >> execution as they are being modified by another thread of execution without
> >> requiring explicit synchronization.
> >>
> >> And this method will improve performance significantly compared with the
> >> method based on ftrace, especially for the critical function being frequently
> >> called.
> > It sounds like a great feature for ftrace. If the new prologue is usable
> > for LivePatching, it should be usable to call the ftrace handler as
> > well. If you teach ftrace to use the new prologue, you will not need
> > all these crazy arch-specific hacks for LivePatching. Then both ftrace
> > and livepatch will benefit from the feature.
> >
> > I suggest to read the ftrace documentation in Documentation/trace/,
> > especially ftrace-design.txt. I have never ported ftrace to a new
> > architecture. I guess that you need to teach scripts/recordmcount.c
> >  to find the new location. Also you might need to update
> > arch/arm/kernel/ftrace.c. Also please make sure that ftrace
> > supports DYNAMIC_FTRACE_WITH_REGS on Arm.
> 
> Hi Petr,
> I had posted one method for livepatch implementaion on arm64 based on ftrace with
> DYNAMIC_FTRACE_WITH_REGS.
> https://lwn.net/Articles/646317/

I see. What are the plans with this patchset, please?


> This requires GCC changes to support it, and I propose a method that implement -mfentry
> feature which following some other arch such as x86/mips/s390 etc. This method is
> architecture-specific, and Maxim Kuvyrkov propose a new method for gcc that implement
> a target-independent option -fprolog-pad=N, which will generate a pad of N nops at the
> beginning of each function.
> https://gcc.gnu.org/ml/gcc/2015-05/msg00267.html
>
> And based on this, DYNAMIC_FTRACE_WITH_REGS feature can be implemented as
> mentioned in:
> http://lists.infradead.org/pipermail/linux-arm-kernel/2015-November/386815.html

What will happen if DYNAMIC_FTRACE_WITH_REGS is implemented using
-fprolog-pad=N, please? Will ftrace and livepatch try to modify the same
location? Or do you plan to revert all these changes then?


> And in this patchset, I only concern the performance for livepatch on arm64, and I
> hope this also can be resolved by improving ftrace, and thanks to Steve that he pointed
> that he is working on a way to make ftrace a bit better for livepatch.

What is the exact performance optimization? Is it that you call the
new functiion (from the patch) dirrectly instead of using the generic
ftrace handler? This will break once ftrace start using this location.
Also this will get more problematic once we have the consistency
mode as pointed out by Josh.

Please, note that there are currently three features that need to
modify the function entry code: Ftrace, Kprobes, and LivePatch.
They need to be coordinated. This is why also Kprobes are called via
Ftrace when available.

Your patch set looks like a temporary hack to me. I suggest you to
concentrace on implemention DYNAMIC_FTRACE_WITH_REGS on Arm. If you
are not happy with the ftrace performance, you might work on improving
it. All three features: Ftrace, Kprobes, and LivePatch will benefit
from it. Modyfying the code directly from LivePatch looks like a way
to a maintenance hell.

Best Regards,
Petr

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2015-12-17 12:36 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin
2015-12-14 12:49 ` [RFC PATCH 1/3] livepatch: allow arch specific implementation Li Bin
2015-12-14 12:49 ` [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse Li Bin
2015-12-14 15:29   ` Josh Poimboeuf
2015-12-14 12:49 ` [RFC PATCH 3/3] livepatch: arm64: add support for livepatch on arm64 Li Bin
2015-12-14 15:27 ` [RFC PATCH 0/3] " Josh Poimboeuf
2015-12-15 15:43 ` Petr Mladek
2015-12-16  6:04   ` Li Bin
2015-12-17 12:36     ` Petr Mladek

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.