|
|
d465ec |
From 358743e6d8748510f4c9a71511d7ceea7c72f7aa Mon Sep 17 00:00:00 2001
|
|
|
d465ec |
From: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
d465ec |
Date: Mon, 21 Nov 2022 19:23:07 -0800
|
|
|
d465ec |
Subject: [PATCH] v0.9.7 backport: MR!1315 ("Static call fixes")
|
|
|
d465ec |
|
|
|
d465ec |
commit 87ad96760a3af0db294d44865dfa1703f57f5595
|
|
|
d465ec |
Author: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
d465ec |
Date: Mon Nov 21 19:23:07 2022 -0800
|
|
|
d465ec |
|
|
|
d465ec |
create-diff-object: fix s390 special_section initializer spacing
|
|
|
d465ec |
|
|
|
d465ec |
Align the s390 special_section initializers to improve readability and
|
|
|
d465ec |
for consistency with the rest.
|
|
|
d465ec |
|
|
|
d465ec |
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
d465ec |
|
|
|
d465ec |
commit 56bd8c4d0da1634f8549e7269f77a53e9d936a57
|
|
|
d465ec |
Author: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
d465ec |
Date: Mon Nov 21 19:27:23 2022 -0800
|
|
|
d465ec |
|
|
|
d465ec |
create-diff-object: refactor jump label filtering
|
|
|
d465ec |
|
|
|
d465ec |
Convert the hard-coded should_keep_jump_label() to a proper callback,
|
|
|
d465ec |
since static calls will need a similar filter.
|
|
|
d465ec |
|
|
|
d465ec |
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
d465ec |
|
|
|
d465ec |
commit f83218ad12a2d9e20d99d379c78974a576aa558c
|
|
|
d465ec |
Author: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
d465ec |
Date: Mon Nov 21 19:29:53 2022 -0800
|
|
|
d465ec |
|
|
|
d465ec |
create-diff-object: detect unsupported static calls
|
|
|
d465ec |
|
|
|
d465ec |
Similar to jump labels, static calls aren't supported when the static
|
|
|
d465ec |
call key was originally defined in a module rather than in vmlinux.
|
|
|
d465ec |
Detect those cases and either remove them (in the case of tracepoints)
|
|
|
d465ec |
or error out.
|
|
|
d465ec |
|
|
|
d465ec |
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
d465ec |
|
|
|
d465ec |
commit ab2397c03e31f0f697aa8bf943d70b4e5a7def54
|
|
|
d465ec |
Author: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
d465ec |
Date: Mon Nov 21 19:41:30 2022 -0800
|
|
|
d465ec |
|
|
|
d465ec |
kpatch-macros: add KPATCH_STATIC_CALL()
|
|
|
d465ec |
|
|
|
d465ec |
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
d465ec |
|
|
|
d465ec |
commit 92c178b6a30a827c48db46ff4238501ec406a28e
|
|
|
d465ec |
Author: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
d465ec |
Date: Tue Nov 22 12:53:09 2022 -0800
|
|
|
d465ec |
|
|
|
d465ec |
create-diff-object: use errx() instead of err()
|
|
|
d465ec |
|
|
|
d465ec |
Otherwise on recent distros it appends the errno to the error message,
|
|
|
d465ec |
like:
|
|
|
d465ec |
|
|
|
d465ec |
create-diff-object: ERROR: x86.o: kpatch_regenerate_special_section: 2633: Found 1 unsupported static call(s) in the patched code. Use KPATCH_STATIC_CALL() instead.: Success
|
|
|
d465ec |
|
|
|
d465ec |
which is not what we want in most cases.
|
|
|
d465ec |
|
|
|
d465ec |
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
d465ec |
|
|
|
d465ec |
Signed-off-by: Yannick Cote <ycote@redhat.com>
|
|
|
d465ec |
---
|
|
|
d465ec |
kmod/patch/kpatch-macros.h | 11 +
|
|
|
d465ec |
kpatch-build/create-diff-object.c | 328 ++++++++++++++++++------------
|
|
|
d465ec |
kpatch-build/log.h | 2 +-
|
|
|
d465ec |
3 files changed, 211 insertions(+), 130 deletions(-)
|
|
|
d465ec |
|
|
|
d465ec |
diff --git a/kmod/patch/kpatch-macros.h b/kmod/patch/kpatch-macros.h
|
|
|
d465ec |
index 8e09702ea001..b797838849ca 100644
|
|
|
d465ec |
--- a/kmod/patch/kpatch-macros.h
|
|
|
d465ec |
+++ b/kmod/patch/kpatch-macros.h
|
|
|
d465ec |
@@ -141,4 +141,15 @@ struct kpatch_post_unpatch_callback {
|
|
|
d465ec |
printk(_fmt, ## __VA_ARGS__); \
|
|
|
d465ec |
})
|
|
|
d465ec |
|
|
|
d465ec |
+/*
|
|
|
d465ec |
+ * KPATCH_STATIC_CALL macro
|
|
|
d465ec |
+ *
|
|
|
d465ec |
+ * Replace usages of static_call() with this macro, when create-diff-object
|
|
|
d465ec |
+ * recommends it due to the original static call key living in a module.
|
|
|
d465ec |
+ *
|
|
|
d465ec |
+ * This converts the static call to a regular indirect call.
|
|
|
d465ec |
+ */
|
|
|
d465ec |
+#define KPATCH_STATIC_CALL(name) \
|
|
|
d465ec |
+ ((typeof(STATIC_CALL_TRAMP(name))*)(STATIC_CALL_KEY(name).func))
|
|
|
d465ec |
+
|
|
|
d465ec |
#endif /* __KPATCH_MACROS_H_ */
|
|
|
d465ec |
diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c
|
|
|
d465ec |
index 7106b67cfd25..ddaa9b44f11e 100644
|
|
|
d465ec |
--- a/kpatch-build/create-diff-object.c
|
|
|
d465ec |
+++ b/kpatch-build/create-diff-object.c
|
|
|
d465ec |
@@ -56,7 +56,7 @@
|
|
|
d465ec |
#define DIFF_FATAL(format, ...) \
|
|
|
d465ec |
({ \
|
|
|
d465ec |
fprintf(stderr, "ERROR: %s: " format "\n", childobj, ##__VA_ARGS__); \
|
|
|
d465ec |
- err(EXIT_STATUS_DIFF_FATAL, "unreconcilable difference"); \
|
|
|
d465ec |
+ errx(EXIT_STATUS_DIFF_FATAL, "unreconcilable difference"); \
|
|
|
d465ec |
})
|
|
|
d465ec |
|
|
|
d465ec |
char *childobj;
|
|
|
d465ec |
@@ -71,6 +71,8 @@ enum loglevel loglevel = NORMAL;
|
|
|
d465ec |
|
|
|
d465ec |
bool KLP_ARCH;
|
|
|
d465ec |
|
|
|
d465ec |
+int jump_label_errors, static_call_errors;
|
|
|
d465ec |
+
|
|
|
d465ec |
/*******************
|
|
|
d465ec |
* Data structures
|
|
|
d465ec |
* ****************/
|
|
|
d465ec |
@@ -78,6 +80,9 @@ struct special_section {
|
|
|
d465ec |
char *name;
|
|
|
d465ec |
enum architecture arch;
|
|
|
d465ec |
int (*group_size)(struct kpatch_elf *kelf, int offset);
|
|
|
d465ec |
+ bool (*group_filter)(struct lookup_table *lookup,
|
|
|
d465ec |
+ struct section *relasec, unsigned int offset,
|
|
|
d465ec |
+ unsigned int size);
|
|
|
d465ec |
};
|
|
|
d465ec |
|
|
|
d465ec |
/*************
|
|
|
d465ec |
@@ -2215,6 +2220,169 @@ static int fixup_group_size(struct kpatch_elf *kelf, int offset)
|
|
|
d465ec |
return (int)(rela->addend - offset);
|
|
|
d465ec |
}
|
|
|
d465ec |
|
|
|
d465ec |
+static bool jump_table_group_filter(struct lookup_table *lookup,
|
|
|
d465ec |
+ struct section *relasec,
|
|
|
d465ec |
+ unsigned int group_offset,
|
|
|
d465ec |
+ unsigned int group_size)
|
|
|
d465ec |
+{
|
|
|
d465ec |
+ struct rela *code = NULL, *key = NULL, *rela;
|
|
|
d465ec |
+ bool tracepoint = false, dynamic_debug = false;
|
|
|
d465ec |
+ struct lookup_result symbol;
|
|
|
d465ec |
+ int i = 0;
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /*
|
|
|
d465ec |
+ * Here we hard-code knowledge about the contents of the jump_entry
|
|
|
d465ec |
+ * struct. It has three fields: code, target, and key. Each field has
|
|
|
d465ec |
+ * a relocation associated with it.
|
|
|
d465ec |
+ */
|
|
|
d465ec |
+ list_for_each_entry(rela, &relasec->relas, list) {
|
|
|
d465ec |
+ if (rela->offset >= group_offset &&
|
|
|
d465ec |
+ rela->offset < group_offset + group_size) {
|
|
|
d465ec |
+ if (i == 0)
|
|
|
d465ec |
+ code = rela;
|
|
|
d465ec |
+ else if (i == 2)
|
|
|
d465ec |
+ key = rela;
|
|
|
d465ec |
+ i++;
|
|
|
d465ec |
+ }
|
|
|
d465ec |
+ }
|
|
|
d465ec |
+
|
|
|
d465ec |
+ if (i != 3 || !key || !code)
|
|
|
d465ec |
+ ERROR("BUG: __jump_table has an unexpected format");
|
|
|
d465ec |
+
|
|
|
d465ec |
+ if (!strncmp(key->sym->name, "__tracepoint_", 13))
|
|
|
d465ec |
+ tracepoint = true;
|
|
|
d465ec |
+
|
|
|
d465ec |
+ if (is_dynamic_debug_symbol(key->sym))
|
|
|
d465ec |
+ dynamic_debug = true;
|
|
|
d465ec |
+
|
|
|
d465ec |
+ if (KLP_ARCH) {
|
|
|
d465ec |
+ /*
|
|
|
d465ec |
+ * On older kernels (with .klp.arch support), jump labels
|
|
|
d465ec |
+ * aren't supported at all. Error out when they occur in a
|
|
|
d465ec |
+ * replacement function, with the exception of tracepoints and
|
|
|
d465ec |
+ * dynamic debug printks. An inert tracepoint or printk is
|
|
|
d465ec |
+ * harmless enough, but a broken jump label can cause
|
|
|
d465ec |
+ * unexpected behavior.
|
|
|
d465ec |
+ */
|
|
|
d465ec |
+ if (tracepoint || dynamic_debug)
|
|
|
d465ec |
+ return false;
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /*
|
|
|
d465ec |
+ * This will be upgraded to an error after all jump labels have
|
|
|
d465ec |
+ * been reported.
|
|
|
d465ec |
+ */
|
|
|
d465ec |
+ log_normal("Found a jump label at %s()+0x%lx, using key %s. Jump labels aren't supported with this kernel. Use static_key_enabled() instead.\n",
|
|
|
d465ec |
+ code->sym->name, code->addend, key->sym->name);
|
|
|
d465ec |
+ jump_label_errors++;
|
|
|
d465ec |
+ return false;
|
|
|
d465ec |
+ }
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /*
|
|
|
d465ec |
+ * On newer (5.8+) kernels, jump labels are supported in the case where
|
|
|
d465ec |
+ * the corresponding static key lives in vmlinux. That's because such
|
|
|
d465ec |
+ * kernels apply vmlinux-specific .klp.rela sections at the same time
|
|
|
d465ec |
+ * (in the klp module load) as normal relas, before jump label init.
|
|
|
d465ec |
+ * On the other hand, jump labels based on static keys which are
|
|
|
d465ec |
+ * defined in modules aren't supported, because late module patching
|
|
|
d465ec |
+ * can result in the klp relas getting applied *after* the klp module's
|
|
|
d465ec |
+ * jump label init.
|
|
|
d465ec |
+ */
|
|
|
d465ec |
+
|
|
|
d465ec |
+ if (lookup_symbol(lookup, key->sym, &symbol) &&
|
|
|
d465ec |
+ strcmp(symbol.objname, "vmlinux")) {
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /* The static key lives in a module -- not supported */
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /* Inert tracepoints and dynamic debug printks are harmless */
|
|
|
d465ec |
+ if (tracepoint || dynamic_debug)
|
|
|
d465ec |
+ return false;
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /*
|
|
|
d465ec |
+ * This will be upgraded to an error after all jump label
|
|
|
d465ec |
+ * errors have been reported.
|
|
|
d465ec |
+ */
|
|
|
d465ec |
+ log_normal("Found a jump label at %s()+0x%lx, using key %s, which is defined in a module. Use static_key_enabled() instead.\n",
|
|
|
d465ec |
+ code->sym->name, code->addend, key->sym->name);
|
|
|
d465ec |
+ jump_label_errors++;
|
|
|
d465ec |
+ return false;
|
|
|
d465ec |
+ }
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /* The static key lives in vmlinux or the patch module itself */
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /*
|
|
|
d465ec |
+ * If the jump label key lives in the '__dyndbg' section, make sure
|
|
|
d465ec |
+ * the section gets included, because we don't use klp relocs for
|
|
|
d465ec |
+ * dynamic debug symbols. For an example of such a key, see
|
|
|
d465ec |
+ * DYNAMIC_DEBUG_BRANCH().
|
|
|
d465ec |
+ */
|
|
|
d465ec |
+ if (dynamic_debug)
|
|
|
d465ec |
+ kpatch_include_symbol(key->sym);
|
|
|
d465ec |
+
|
|
|
d465ec |
+ return true;
|
|
|
d465ec |
+}
|
|
|
d465ec |
+
|
|
|
d465ec |
+static bool static_call_sites_group_filter(struct lookup_table *lookup,
|
|
|
d465ec |
+ struct section *relasec,
|
|
|
d465ec |
+ unsigned int group_offset,
|
|
|
d465ec |
+ unsigned int group_size)
|
|
|
d465ec |
+{
|
|
|
d465ec |
+ struct rela *code = NULL, *key = NULL, *rela;
|
|
|
d465ec |
+ bool tracepoint = false;
|
|
|
d465ec |
+ struct lookup_result symbol;
|
|
|
d465ec |
+ int i = 0;
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /*
|
|
|
d465ec |
+ * Here we hard-code knowledge about the contents of the jump_entry
|
|
|
d465ec |
+ * struct. It has three fields: code, target, and key. Each field has
|
|
|
d465ec |
+ * a relocation associated with it.
|
|
|
d465ec |
+ */
|
|
|
d465ec |
+ list_for_each_entry(rela, &relasec->relas, list) {
|
|
|
d465ec |
+ if (rela->offset >= group_offset &&
|
|
|
d465ec |
+ rela->offset < group_offset + group_size) {
|
|
|
d465ec |
+ if (i == 0)
|
|
|
d465ec |
+ code = rela;
|
|
|
d465ec |
+ else if (i == 1)
|
|
|
d465ec |
+ key = rela;
|
|
|
d465ec |
+ i++;
|
|
|
d465ec |
+ }
|
|
|
d465ec |
+ }
|
|
|
d465ec |
+
|
|
|
d465ec |
+ if (i != 2 || !key || !code)
|
|
|
d465ec |
+ ERROR("BUG: .static_call_sites has an unexpected format");
|
|
|
d465ec |
+
|
|
|
d465ec |
+ if (!strncmp(key->sym->name, "__SCK__tp_func_", 15))
|
|
|
d465ec |
+ tracepoint = true;
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /*
|
|
|
d465ec |
+ * Static calls are only supported in the case where the corresponding
|
|
|
d465ec |
+ * static call key lives in vmlinux (see explanation in
|
|
|
d465ec |
+ * jump_table_group_filter).
|
|
|
d465ec |
+ */
|
|
|
d465ec |
+
|
|
|
d465ec |
+ if (lookup_symbol(lookup, key->sym, &symbol) &&
|
|
|
d465ec |
+ strcmp(symbol.objname, "vmlinux")) {
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /* The key lives in a module -- not supported */
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /* Inert tracepoints are harmless */
|
|
|
d465ec |
+ if (tracepoint)
|
|
|
d465ec |
+ return false;
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /*
|
|
|
d465ec |
+ * This will be upgraded to an error after all static call
|
|
|
d465ec |
+ * errors have been reported.
|
|
|
d465ec |
+ */
|
|
|
d465ec |
+ log_normal("Found a static call at %s()+0x%lx, using key %s, which is defined in a module. Use KPATCH_STATIC_CALL() instead.\n",
|
|
|
d465ec |
+ code->sym->name, code->addend, key->sym->name);
|
|
|
d465ec |
+ static_call_errors++;
|
|
|
d465ec |
+ return false;
|
|
|
d465ec |
+ }
|
|
|
d465ec |
+
|
|
|
d465ec |
+ /* The key lives in vmlinux or the patch module itself */
|
|
|
d465ec |
+ return true;
|
|
|
d465ec |
+}
|
|
|
d465ec |
+
|
|
|
d465ec |
+
|
|
|
d465ec |
static struct special_section special_sections[] = {
|
|
|
d465ec |
{
|
|
|
d465ec |
.name = "__bug_table",
|
|
|
d465ec |
@@ -2235,6 +2403,7 @@ static struct special_section special_sections[] = {
|
|
|
d465ec |
.name = "__jump_table",
|
|
|
d465ec |
.arch = X86_64 | PPC64 | S390,
|
|
|
d465ec |
.group_size = jump_table_group_size,
|
|
|
d465ec |
+ .group_filter = jump_table_group_filter,
|
|
|
d465ec |
},
|
|
|
d465ec |
{
|
|
|
d465ec |
.name = ".printk_index",
|
|
|
d465ec |
@@ -2260,6 +2429,7 @@ static struct special_section special_sections[] = {
|
|
|
d465ec |
.name = ".static_call_sites",
|
|
|
d465ec |
.arch = X86_64,
|
|
|
d465ec |
.group_size = static_call_sites_group_size,
|
|
|
d465ec |
+ .group_filter = static_call_sites_group_filter,
|
|
|
d465ec |
},
|
|
|
d465ec |
{
|
|
|
d465ec |
.name = ".retpoline_sites",
|
|
|
d465ec |
@@ -2297,138 +2467,36 @@ static struct special_section special_sections[] = {
|
|
|
d465ec |
.group_size = fixup_barrier_nospec_group_size,
|
|
|
d465ec |
},
|
|
|
d465ec |
{
|
|
|
d465ec |
- .name = ".s390_return_mem",
|
|
|
d465ec |
- .arch = S390,
|
|
|
d465ec |
- .group_size = s390_expolines_group_size,
|
|
|
d465ec |
+ .name = ".s390_return_mem",
|
|
|
d465ec |
+ .arch = S390,
|
|
|
d465ec |
+ .group_size = s390_expolines_group_size,
|
|
|
d465ec |
},
|
|
|
d465ec |
{
|
|
|
d465ec |
- .name = ".s390_return_reg",
|
|
|
d465ec |
- .arch = S390,
|
|
|
d465ec |
- .group_size = s390_expolines_group_size,
|
|
|
d465ec |
+ .name = ".s390_return_reg",
|
|
|
d465ec |
+ .arch = S390,
|
|
|
d465ec |
+ .group_size = s390_expolines_group_size,
|
|
|
d465ec |
},
|
|
|
d465ec |
{
|
|
|
d465ec |
- .name = ".s390_indirect_call",
|
|
|
d465ec |
- .arch = S390,
|
|
|
d465ec |
- .group_size = s390_expolines_group_size,
|
|
|
d465ec |
+ .name = ".s390_indirect_call",
|
|
|
d465ec |
+ .arch = S390,
|
|
|
d465ec |
+ .group_size = s390_expolines_group_size,
|
|
|
d465ec |
},
|
|
|
d465ec |
{
|
|
|
d465ec |
- .name = ".s390_indirect_branches",
|
|
|
d465ec |
- .arch = S390,
|
|
|
d465ec |
- .group_size = s390_expolines_group_size,
|
|
|
d465ec |
+ .name = ".s390_indirect_branches",
|
|
|
d465ec |
+ .arch = S390,
|
|
|
d465ec |
+ .group_size = s390_expolines_group_size,
|
|
|
d465ec |
},
|
|
|
d465ec |
{
|
|
|
d465ec |
- .name = ".s390_indirect_jump",
|
|
|
d465ec |
- .arch = S390,
|
|
|
d465ec |
- .group_size = s390_expolines_group_size,
|
|
|
d465ec |
+ .name = ".s390_indirect_jump",
|
|
|
d465ec |
+ .arch = S390,
|
|
|
d465ec |
+ .group_size = s390_expolines_group_size,
|
|
|
d465ec |
},
|
|
|
d465ec |
{},
|
|
|
d465ec |
};
|
|
|
d465ec |
|
|
|
d465ec |
-static bool should_keep_jump_label(struct lookup_table *lookup,
|
|
|
d465ec |
- struct section *relasec,
|
|
|
d465ec |
- unsigned int group_offset,
|
|
|
d465ec |
- unsigned int group_size,
|
|
|
d465ec |
- int *jump_labels_found)
|
|
|
d465ec |
-{
|
|
|
d465ec |
- struct rela *code = NULL, *key = NULL, *rela;
|
|
|
d465ec |
- bool tracepoint = false, dynamic_debug = false;
|
|
|
d465ec |
- struct lookup_result symbol;
|
|
|
d465ec |
- int i = 0;
|
|
|
d465ec |
-
|
|
|
d465ec |
- /*
|
|
|
d465ec |
- * Here we hard-code knowledge about the contents of the jump_entry
|
|
|
d465ec |
- * struct. It has three fields: code, target, and key. Each field has
|
|
|
d465ec |
- * a relocation associated with it.
|
|
|
d465ec |
- */
|
|
|
d465ec |
- list_for_each_entry(rela, &relasec->relas, list) {
|
|
|
d465ec |
- if (rela->offset >= group_offset &&
|
|
|
d465ec |
- rela->offset < group_offset + group_size) {
|
|
|
d465ec |
- if (i == 0)
|
|
|
d465ec |
- code = rela;
|
|
|
d465ec |
- else if (i == 2)
|
|
|
d465ec |
- key = rela;
|
|
|
d465ec |
- i++;
|
|
|
d465ec |
- }
|
|
|
d465ec |
- }
|
|
|
d465ec |
-
|
|
|
d465ec |
- if (i != 3 || !key || !code)
|
|
|
d465ec |
- ERROR("BUG: __jump_table has an unexpected format");
|
|
|
d465ec |
-
|
|
|
d465ec |
- if (!strncmp(key->sym->name, "__tracepoint_", 13))
|
|
|
d465ec |
- tracepoint = true;
|
|
|
d465ec |
-
|
|
|
d465ec |
- if (is_dynamic_debug_symbol(key->sym))
|
|
|
d465ec |
- dynamic_debug = true;
|
|
|
d465ec |
-
|
|
|
d465ec |
- if (KLP_ARCH) {
|
|
|
d465ec |
- /*
|
|
|
d465ec |
- * On older kernels (with .klp.arch support), jump labels
|
|
|
d465ec |
- * aren't supported at all. Error out when they occur in a
|
|
|
d465ec |
- * replacement function, with the exception of tracepoints and
|
|
|
d465ec |
- * dynamic debug printks. An inert tracepoint or printk is
|
|
|
d465ec |
- * harmless enough, but a broken jump label can cause
|
|
|
d465ec |
- * unexpected behavior.
|
|
|
d465ec |
- */
|
|
|
d465ec |
- if (tracepoint || dynamic_debug)
|
|
|
d465ec |
- return false;
|
|
|
d465ec |
-
|
|
|
d465ec |
- /*
|
|
|
d465ec |
- * This will be upgraded to an error after all jump labels have
|
|
|
d465ec |
- * been reported.
|
|
|
d465ec |
- */
|
|
|
d465ec |
- log_normal("Found a jump label at %s()+0x%lx, using key %s. Jump labels aren't supported with this kernel. Use static_key_enabled() instead.\n",
|
|
|
d465ec |
- code->sym->name, code->addend, key->sym->name);
|
|
|
d465ec |
- (*jump_labels_found)++;
|
|
|
d465ec |
- return false;
|
|
|
d465ec |
- }
|
|
|
d465ec |
-
|
|
|
d465ec |
- /*
|
|
|
d465ec |
- * On newer (5.8+) kernels, jump labels are supported in the case where
|
|
|
d465ec |
- * the corresponding static key lives in vmlinux. That's because such
|
|
|
d465ec |
- * kernels apply vmlinux-specific .klp.rela sections at the same time
|
|
|
d465ec |
- * (in the klp module load) as normal relas, before jump label init.
|
|
|
d465ec |
- * On the other hand, jump labels based on static keys which are
|
|
|
d465ec |
- * defined in modules aren't supported, because late module patching
|
|
|
d465ec |
- * can result in the klp relas getting applied *after* the klp module's
|
|
|
d465ec |
- * jump label init.
|
|
|
d465ec |
- */
|
|
|
d465ec |
-
|
|
|
d465ec |
- if (lookup_symbol(lookup, key->sym, &symbol) &&
|
|
|
d465ec |
- strcmp(symbol.objname, "vmlinux")) {
|
|
|
d465ec |
-
|
|
|
d465ec |
- /* The static key lives in a module -- not supported */
|
|
|
d465ec |
-
|
|
|
d465ec |
- /* Inert tracepoints and dynamic debug printks are harmless */
|
|
|
d465ec |
- if (tracepoint || dynamic_debug)
|
|
|
d465ec |
- return false;
|
|
|
d465ec |
-
|
|
|
d465ec |
- /*
|
|
|
d465ec |
- * This will be upgraded to an error after all jump labels have
|
|
|
d465ec |
- * been reported.
|
|
|
d465ec |
- */
|
|
|
d465ec |
- log_normal("Found a jump label at %s()+0x%lx, using key %s, which is defined in a module. Use static_key_enabled() instead.\n",
|
|
|
d465ec |
- code->sym->name, code->addend, key->sym->name);
|
|
|
d465ec |
- (*jump_labels_found)++;
|
|
|
d465ec |
- return false;
|
|
|
d465ec |
- }
|
|
|
d465ec |
-
|
|
|
d465ec |
- /* The static key lives in vmlinux or the patch module itself */
|
|
|
d465ec |
-
|
|
|
d465ec |
- /*
|
|
|
d465ec |
- * If the jump label key lives in the '__dyndbg' section, make sure
|
|
|
d465ec |
- * the section gets included, because we don't use klp relocs for
|
|
|
d465ec |
- * dynamic debug symbols. For an example of such a key, see
|
|
|
d465ec |
- * DYNAMIC_DEBUG_BRANCH().
|
|
|
d465ec |
- */
|
|
|
d465ec |
- if (dynamic_debug)
|
|
|
d465ec |
- kpatch_include_symbol(key->sym);
|
|
|
d465ec |
-
|
|
|
d465ec |
- return true;
|
|
|
d465ec |
-}
|
|
|
d465ec |
-
|
|
|
d465ec |
static bool should_keep_rela_group(struct lookup_table *lookup,
|
|
|
d465ec |
struct section *relasec, unsigned int offset,
|
|
|
d465ec |
- unsigned int size, int *jump_labels_found)
|
|
|
d465ec |
+ unsigned int size)
|
|
|
d465ec |
{
|
|
|
d465ec |
struct rela *rela;
|
|
|
d465ec |
bool found = false;
|
|
|
d465ec |
@@ -2448,10 +2516,6 @@ static bool should_keep_rela_group(struct lookup_table *lookup,
|
|
|
d465ec |
if (!found)
|
|
|
d465ec |
return false;
|
|
|
d465ec |
|
|
|
d465ec |
- if (!strcmp(relasec->name, ".rela__jump_table"))
|
|
|
d465ec |
- return should_keep_jump_label(lookup, relasec, offset, size,
|
|
|
d465ec |
- jump_labels_found);
|
|
|
d465ec |
-
|
|
|
d465ec |
return true;
|
|
|
d465ec |
}
|
|
|
d465ec |
|
|
|
d465ec |
@@ -2488,7 +2552,6 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
|
|
d465ec |
struct rela *rela, *safe;
|
|
|
d465ec |
char *src, *dest;
|
|
|
d465ec |
unsigned int group_size, src_offset, dest_offset;
|
|
|
d465ec |
- int jump_labels_found = 0;
|
|
|
d465ec |
|
|
|
d465ec |
LIST_HEAD(newrelas);
|
|
|
d465ec |
|
|
|
d465ec |
@@ -2523,8 +2586,11 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
|
|
d465ec |
if (src_offset + group_size > relasec->base->sh.sh_size)
|
|
|
d465ec |
group_size = (unsigned int)(relasec->base->sh.sh_size - src_offset);
|
|
|
d465ec |
|
|
|
d465ec |
- if (!should_keep_rela_group(lookup, relasec, src_offset, group_size,
|
|
|
d465ec |
- &jump_labels_found))
|
|
|
d465ec |
+ if (!should_keep_rela_group(lookup, relasec, src_offset, group_size))
|
|
|
d465ec |
+ continue;
|
|
|
d465ec |
+
|
|
|
d465ec |
+ if (special->group_filter &&
|
|
|
d465ec |
+ !special->group_filter(lookup, relasec, src_offset, group_size))
|
|
|
d465ec |
continue;
|
|
|
d465ec |
|
|
|
d465ec |
/*
|
|
|
d465ec |
@@ -2557,9 +2623,13 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
|
|
|
d465ec |
dest_offset += group_size;
|
|
|
d465ec |
}
|
|
|
d465ec |
|
|
|
d465ec |
- if (jump_labels_found)
|
|
|
d465ec |
- ERROR("Found %d jump label(s) in the patched code. Jump labels aren't currently supported. Use static_key_enabled() instead.",
|
|
|
d465ec |
- jump_labels_found);
|
|
|
d465ec |
+ if (jump_label_errors)
|
|
|
d465ec |
+ ERROR("Found %d unsupported jump label(s) in the patched code. Use static_key_enabled() instead.",
|
|
|
d465ec |
+ jump_label_errors);
|
|
|
d465ec |
+
|
|
|
d465ec |
+ if (static_call_errors)
|
|
|
d465ec |
+ ERROR("Found %d unsupported static call(s) in the patched code. Use KPATCH_STATIC_CALL() instead.",
|
|
|
d465ec |
+ static_call_errors);
|
|
|
d465ec |
|
|
|
d465ec |
if (!dest_offset) {
|
|
|
d465ec |
/* no changed or global functions referenced */
|
|
|
d465ec |
diff --git a/kpatch-build/log.h b/kpatch-build/log.h
|
|
|
d465ec |
index eefa0fce7b08..dbdc212713e1 100644
|
|
|
d465ec |
--- a/kpatch-build/log.h
|
|
|
d465ec |
+++ b/kpatch-build/log.h
|
|
|
d465ec |
@@ -9,7 +9,7 @@ extern enum loglevel loglevel;
|
|
|
d465ec |
extern char *childobj;
|
|
|
d465ec |
|
|
|
d465ec |
#define ERROR(format, ...) \
|
|
|
d465ec |
- err(EXIT_STATUS_ERROR, "ERROR: %s: %s: %d: " format, childobj, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
|
|
d465ec |
+ errx(EXIT_STATUS_ERROR, "ERROR: %s: %s: %d: " format, childobj, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
|
|
d465ec |
|
|
|
d465ec |
#define log_debug(format, ...) log(DEBUG, format, ##__VA_ARGS__)
|
|
|
d465ec |
#define log_normal(format, ...) log(NORMAL, "%s: " format, childobj, ##__VA_ARGS__)
|
|
|
d465ec |
--
|
|
|
d465ec |
2.38.1
|
|
|
d465ec |
|