350 lines
10 KiB
Diff
350 lines
10 KiB
Diff
|
|
From e91d2d2c6778275b16249f7e9d7439018fed70fe Mon Sep 17 00:00:00 2001
|
||
|
|
From: Zhipeng Xie <xiezhipeng1@huawei.com>
|
||
|
|
Date: Fri, 2 Nov 2018 17:25:08 +0000
|
||
|
|
Subject: [PATCH 1009/1015] kmod/kpatch-build: support build patch for
|
||
|
|
old kernels
|
||
|
|
|
||
|
|
Forward compatible for old kernels
|
||
|
|
|
||
|
|
Signed-off-by: Zhipeng Xie <xiezhipeng1@huawei.com>
|
||
|
|
---
|
||
|
|
kmod/patch/kpatch-macros.h | 48 ++++++++++++++
|
||
|
|
kmod/patch/kpatch-patch.h | 4 +
|
||
|
|
kmod/patch/kpatch.lds.S | 12 ++++
|
||
|
|
kmod/patch/livepatch-patch-hook.c | 124 +++++++++++++++++++++++++++++++++++++
|
||
|
|
kpatch-build/create-diff-object.c | 8 +++
|
||
|
|
5 files changed, 196 insertions(+), 0 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/kmod/patch/kpatch-macros.h b/kmod/patch/kpatch-macros.h
|
||
|
|
index a60a267..569a18d 100644
|
||
|
|
--- a/kmod/patch/kpatch-macros.h
|
||
|
|
+++ b/kmod/patch/kpatch-macros.h
|
||
|
|
@@ -133,4 +133,52 @@ struct kpatch_post_unpatch_callback {
|
||
|
|
printk(_fmt, ## __VA_ARGS__); \
|
||
|
|
})
|
||
|
|
|
||
|
|
+typedef void (*kpatch_loadcall_t)(void);
|
||
|
|
+typedef void (*kpatch_unloadcall_t)(void);
|
||
|
|
+
|
||
|
|
+struct kpatch_load {
|
||
|
|
+ kpatch_loadcall_t fn;
|
||
|
|
+ char *objname; /* filled in by create-diff-object */
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+struct kpatch_unload {
|
||
|
|
+ kpatch_unloadcall_t fn;
|
||
|
|
+ char *objname; /* filled in by create-diff-object */
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+/*
|
||
|
|
+ * KPATCH_LOAD_HOOK macro
|
||
|
|
+ *
|
||
|
|
+ * The first line only ensures that the hook being registered has the required
|
||
|
|
+ * function signature. If not, there is compile error on this line.
|
||
|
|
+ *
|
||
|
|
+ * The section line declares a struct kpatch_load to be allocated in a new
|
||
|
|
+ * .kpatch.hook.load section. This kpatch_load_data symbol is later stripped
|
||
|
|
+ * by create-diff-object so that it can be declared in multiple objects that
|
||
|
|
+ * are later linked together, avoiding global symbol collision. Since multiple
|
||
|
|
+ * hooks can be registered, the .kpatch.hook.load section is a table of struct
|
||
|
|
+ * kpatch_load elements that will be executed in series by the kpatch core
|
||
|
|
+ * module at load time, assuming the kernel object (module) is currently
|
||
|
|
+ * loaded; otherwise, the hook is called when module to be patched is loaded
|
||
|
|
+ * via the module load notifier.
|
||
|
|
+ */
|
||
|
|
+#define KPATCH_LOAD_HOOK(_fn) \
|
||
|
|
+ static inline kpatch_loadcall_t __loadtest(void) { return _fn; } \
|
||
|
|
+ struct kpatch_load kpatch_load_data __section(.kpatch.hooks.load) = { \
|
||
|
|
+ .fn = _fn, \
|
||
|
|
+ .objname = NULL \
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+/*
|
||
|
|
+ * KPATCH_UNLOAD_HOOK macro
|
||
|
|
+ *
|
||
|
|
+ * Same as LOAD hook with s/load/unload/
|
||
|
|
+ */
|
||
|
|
+#define KPATCH_UNLOAD_HOOK(_fn) \
|
||
|
|
+ static inline kpatch_unloadcall_t __unloadtest(void) { return _fn; } \
|
||
|
|
+ struct kpatch_unload kpatch_unload_data __section(.kpatch.hooks.unload) = { \
|
||
|
|
+ .fn = _fn, \
|
||
|
|
+ .objname = NULL \
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
#endif /* __KPATCH_MACROS_H_ */
|
||
|
|
diff --git a/kmod/patch/kpatch-patch.h b/kmod/patch/kpatch-patch.h
|
||
|
|
index 917ea32..4d47d30 100644
|
||
|
|
--- a/kmod/patch/kpatch-patch.h
|
||
|
|
+++ b/kmod/patch/kpatch-patch.h
|
||
|
|
@@ -59,5 +59,9 @@ struct kpatch_post_unpatch_callback {
|
||
|
|
void (*callback)(void *obj);
|
||
|
|
char *objname;
|
||
|
|
};
|
||
|
|
+struct kpatch_patch_hook {
|
||
|
|
+ void (*hook)(void);
|
||
|
|
+ char *objname;
|
||
|
|
+};
|
||
|
|
|
||
|
|
#endif /* _KPATCH_PATCH_H_ */
|
||
|
|
diff --git a/kmod/patch/kpatch.lds.S b/kmod/patch/kpatch.lds.S
|
||
|
|
index bc5de82..e3c4e97 100644
|
||
|
|
--- a/kmod/patch/kpatch.lds.S
|
||
|
|
+++ b/kmod/patch/kpatch.lds.S
|
||
|
|
@@ -47,4 +47,16 @@ SECTIONS
|
||
|
|
__kpatch_force_funcs_end = . ;
|
||
|
|
QUAD(0);
|
||
|
|
}
|
||
|
|
+ .kpatch.hooks.load : {
|
||
|
|
+ __kpatch_hooks_load = . ;
|
||
|
|
+ *(.kpatch.hooks.load)
|
||
|
|
+ __kpatch_hooks_load_end = . ;
|
||
|
|
+ QUAD(0);
|
||
|
|
+ }
|
||
|
|
+ .kpatch.hooks.unload : {
|
||
|
|
+ __kpatch_hooks_unload = . ;
|
||
|
|
+ *(.kpatch.hooks.unload)
|
||
|
|
+ __kpatch_hooks_unload_end = . ;
|
||
|
|
+ QUAD(0);
|
||
|
|
+ }
|
||
|
|
}
|
||
|
|
diff --git a/kmod/patch/livepatch-patch-hook.c b/kmod/patch/livepatch-patch-hook.c
|
||
|
|
index 625ffbd..8f7d044 100644
|
||
|
|
--- a/kmod/patch/livepatch-patch-hook.c
|
||
|
|
+++ b/kmod/patch/livepatch-patch-hook.c
|
||
|
|
@@ -64,6 +64,15 @@
|
||
|
|
# define HAVE_CALLBACKS
|
||
|
|
#endif
|
||
|
|
|
||
|
|
+#ifdef EULER_RELEASE_CODE
|
||
|
|
+# if EULER_RELEASE_CODE <= EULER_RELEASE_VERSION(2, 3)
|
||
|
|
+# undef HAVE_SYMPOS
|
||
|
|
+# undef HAVE_IMMEDIATE
|
||
|
|
+# undef HAVE_ELF_RELOCS
|
||
|
|
+# define HAVE_LOADHOOKS
|
||
|
|
+# endif
|
||
|
|
+#endif
|
||
|
|
+
|
||
|
|
/*
|
||
|
|
* There are quite a few similar structures at play in this file:
|
||
|
|
* - livepatch.h structs prefixed with klp_*
|
||
|
|
@@ -95,6 +104,11 @@ struct patch_object {
|
||
|
|
#endif
|
||
|
|
const char *name;
|
||
|
|
int funcs_nr, relocs_nr;
|
||
|
|
+#ifdef HAVE_LOADHOOKS
|
||
|
|
+ struct list_head hooks_load;
|
||
|
|
+ struct list_head hooks_unload;
|
||
|
|
+ int hooks_load_nr, hooks_unload_nr;
|
||
|
|
+#endif
|
||
|
|
};
|
||
|
|
|
||
|
|
struct patch_func {
|
||
|
|
@@ -107,6 +121,13 @@ struct patch_reloc {
|
||
|
|
struct kpatch_patch_dynrela *kdynrela;
|
||
|
|
};
|
||
|
|
|
||
|
|
+#ifdef HAVE_LOADHOOKS
|
||
|
|
+struct patch_hook {
|
||
|
|
+ struct list_head list;
|
||
|
|
+ struct kpatch_patch_hook *khook;
|
||
|
|
+};
|
||
|
|
+#endif
|
||
|
|
+
|
||
|
|
static struct patch_object *patch_alloc_new_object(const char *name)
|
||
|
|
{
|
||
|
|
struct patch_object *object;
|
||
|
|
@@ -118,6 +139,10 @@ static struct patch_object *patch_alloc_new_object(const char *name)
|
||
|
|
#ifndef HAVE_ELF_RELOCS
|
||
|
|
INIT_LIST_HEAD(&object->relocs);
|
||
|
|
#endif
|
||
|
|
+#ifdef HAVE_LOADHOOKS
|
||
|
|
+ INIT_LIST_HEAD(&object->hooks_load);
|
||
|
|
+ INIT_LIST_HEAD(&object->hooks_unload);
|
||
|
|
+#endif
|
||
|
|
if (strcmp(name, "vmlinux"))
|
||
|
|
object->name = name;
|
||
|
|
list_add(&object->list, &patch_objects);
|
||
|
|
@@ -180,6 +205,37 @@ static int patch_add_reloc_to_object(struct kpatch_patch_dynrela *kdynrela)
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
+#ifdef HAVE_LOADHOOKS
|
||
|
|
+static int patch_add_hook_to_object(struct kpatch_patch_hook *khook, bool load)
|
||
|
|
+{
|
||
|
|
+ struct patch_hook *hook;
|
||
|
|
+ struct patch_object *object;
|
||
|
|
+
|
||
|
|
+ hook = kzalloc(sizeof(*hook), GFP_KERNEL);
|
||
|
|
+ if (!hook)
|
||
|
|
+ return -ENOMEM;
|
||
|
|
+ INIT_LIST_HEAD(&hook->list);
|
||
|
|
+ hook->khook = khook;
|
||
|
|
+
|
||
|
|
+ object = patch_find_object_by_name(khook->objname);
|
||
|
|
+ if (!object) {
|
||
|
|
+ kfree(hook);
|
||
|
|
+ return -ENOMEM;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if (load) {
|
||
|
|
+ list_add(&hook->list, &object->hooks_load);
|
||
|
|
+ object->hooks_load_nr++;
|
||
|
|
+ }
|
||
|
|
+ else {
|
||
|
|
+ list_add(&hook->list, &object->hooks_unload);
|
||
|
|
+ object->hooks_unload_nr++;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ return 0;
|
||
|
|
+}
|
||
|
|
+#endif
|
||
|
|
+
|
||
|
|
static void patch_free_scaffold(void) {
|
||
|
|
struct patch_func *func, *safefunc;
|
||
|
|
struct patch_object *object, *safeobject;
|
||
|
|
@@ -217,6 +273,12 @@ static void patch_free_livepatch(struct klp_patch *patch)
|
||
|
|
if (object->relocs)
|
||
|
|
kfree(object->relocs);
|
||
|
|
#endif
|
||
|
|
+#ifdef HAVE_LOADHOOKS
|
||
|
|
+ if (object->hooks_load)
|
||
|
|
+ kfree(object->hooks_load);
|
||
|
|
+ if (object->hooks_unload)
|
||
|
|
+ kfree(object->hooks_unload);
|
||
|
|
+#endif
|
||
|
|
}
|
||
|
|
if (patch->objs)
|
||
|
|
kfree(patch->objs);
|
||
|
|
@@ -228,6 +290,10 @@ extern struct kpatch_pre_patch_callback __kpatch_callbacks_pre_patch[], __kpatch
|
||
|
|
extern struct kpatch_post_patch_callback __kpatch_callbacks_post_patch[], __kpatch_callbacks_post_patch_end[];
|
||
|
|
extern struct kpatch_pre_unpatch_callback __kpatch_callbacks_pre_unpatch[], __kpatch_callbacks_pre_unpatch_end[];
|
||
|
|
extern struct kpatch_post_unpatch_callback __kpatch_callbacks_post_unpatch[], __kpatch_callbacks_post_unpatch_end[];
|
||
|
|
+#ifdef HAVE_LOADHOOKS
|
||
|
|
+extern struct kpatch_patch_hook __kpatch_hooks_load[], __kpatch_hooks_load_end[];
|
||
|
|
+extern struct kpatch_patch_hook __kpatch_hooks_unload[], __kpatch_hooks_unload_end[];
|
||
|
|
+#endif
|
||
|
|
extern unsigned long __kpatch_force_funcs[], __kpatch_force_funcs_end[];
|
||
|
|
|
||
|
|
static int patch_is_func_forced(unsigned long addr)
|
||
|
|
@@ -347,6 +413,11 @@ static int __init patch_init(void)
|
||
|
|
struct patch_reloc *reloc;
|
||
|
|
struct klp_reloc *lrelocs, *lreloc;
|
||
|
|
#endif
|
||
|
|
+#ifdef HAVE_LOADHOOKS
|
||
|
|
+ struct patch_hook *hook;
|
||
|
|
+ struct kpatch_patch_hook *khook_load, *khook_unload;
|
||
|
|
+ struct klp_hook *lhooks_load, *lhooks_unload, *lhook;
|
||
|
|
+#endif
|
||
|
|
|
||
|
|
/* organize functions and relocs by object in scaffold */
|
||
|
|
for (kfunc = __kpatch_funcs;
|
||
|
|
@@ -371,6 +442,24 @@ static int __init patch_init(void)
|
||
|
|
if (ret)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
+#ifdef HAVE_LOADHOOKS
|
||
|
|
+ for (khook_load = __kpatch_hooks_load;
|
||
|
|
+ khook_load != __kpatch_hooks_load_end;
|
||
|
|
+ khook_load++) {
|
||
|
|
+ ret = patch_add_hook_to_object(khook_load, true);
|
||
|
|
+ if (ret)
|
||
|
|
+ goto out;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ for (khook_unload = __kpatch_hooks_unload;
|
||
|
|
+ khook_unload != __kpatch_hooks_unload_end;
|
||
|
|
+ khook_unload++) {
|
||
|
|
+ ret = patch_add_hook_to_object(khook_unload, false);
|
||
|
|
+ if (ret)
|
||
|
|
+ goto out;
|
||
|
|
+ }
|
||
|
|
+#endif
|
||
|
|
+
|
||
|
|
/* past this point, only possible return code is -ENOMEM */
|
||
|
|
ret = -ENOMEM;
|
||
|
|
|
||
|
|
@@ -403,12 +492,21 @@ static int __init patch_init(void)
|
||
|
|
lfunc = &lfuncs[j];
|
||
|
|
lfunc->old_name = func->kfunc->name;
|
||
|
|
lfunc->new_func = (void *)func->kfunc->new_addr;
|
||
|
|
+#if defined(HAVE_IMMEDIATE)
|
||
|
|
lfunc->immediate = patch_is_func_forced(lfunc->new_func);
|
||
|
|
+#endif
|
||
|
|
#ifdef HAVE_SYMPOS
|
||
|
|
lfunc->old_sympos = func->kfunc->sympos;
|
||
|
|
#else
|
||
|
|
lfunc->old_addr = func->kfunc->old_addr;
|
||
|
|
#endif
|
||
|
|
+#ifdef EULER_RELEASE_CODE
|
||
|
|
+# if EULER_RELEASE_CODE <= EULER_RELEASE_VERSION(2, 3)
|
||
|
|
+ lfunc->old_size = func->kfunc->old_size;
|
||
|
|
+ lfunc->new_size = func->kfunc->new_size;
|
||
|
|
+ lfunc->force = patch_is_func_forced(func->kfunc->new_addr);
|
||
|
|
+# endif
|
||
|
|
+#endif
|
||
|
|
j++;
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -439,6 +537,32 @@ static int __init patch_init(void)
|
||
|
|
lobject->callbacks = object->callbacks;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
+#ifdef HAVE_LOADHOOKS
|
||
|
|
+ lhooks_load = kzalloc(sizeof(struct klp_hook) *
|
||
|
|
+ (object->hooks_load_nr+1), GFP_KERNEL);
|
||
|
|
+ if (!lhooks_load)
|
||
|
|
+ goto out;
|
||
|
|
+ lobject->hooks_load = lhooks_load;
|
||
|
|
+ j = 0;
|
||
|
|
+ list_for_each_entry(hook, &object->hooks_load, list) {
|
||
|
|
+ lhook = &lhooks_load[j];
|
||
|
|
+ lhook->hook = hook->khook->hook;
|
||
|
|
+ j++;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ lhooks_unload = kzalloc(sizeof(struct klp_hook) *
|
||
|
|
+ (object->hooks_unload_nr+1), GFP_KERNEL);
|
||
|
|
+ if (!lhooks_unload)
|
||
|
|
+ goto out;
|
||
|
|
+ lobject->hooks_unload = lhooks_unload;
|
||
|
|
+ j = 0;
|
||
|
|
+ list_for_each_entry(hook, &object->hooks_unload, list) {
|
||
|
|
+ lhook = &lhooks_unload[j];
|
||
|
|
+ lhook->hook = hook->khook->hook;
|
||
|
|
+ j++;
|
||
|
|
+ }
|
||
|
|
+#endif
|
||
|
|
+
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
|
||
|
|
diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c
|
||
|
|
index 349e483..ed96758 100644
|
||
|
|
--- a/kpatch-build/create-diff-object.c
|
||
|
|
+++ b/kpatch-build/create-diff-object.c
|
||
|
|
@@ -1463,6 +1463,10 @@ static int kpatch_include_callback_elements(struct kpatch_elf *kelf)
|
||
|
|
".rela.kpatch.callbacks.post_patch",
|
||
|
|
".rela.kpatch.callbacks.pre_unpatch",
|
||
|
|
".rela.kpatch.callbacks.post_unpatch",
|
||
|
|
+ ".kpatch.hooks.load",
|
||
|
|
+ ".kpatch.hooks.unload",
|
||
|
|
+ ".rela.kpatch.hooks.load",
|
||
|
|
+ ".rela.kpatch.hooks.unload",
|
||
|
|
NULL,
|
||
|
|
};
|
||
|
|
char **callback_section;
|
||
|
|
@@ -2921,6 +2925,10 @@ static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char *
|
||
|
|
.offset = offsetof(struct kpatch_pre_unpatch_callback, objname) },
|
||
|
|
{ .name = ".rela.kpatch.callbacks.post_unpatch",
|
||
|
|
.offset = offsetof(struct kpatch_post_patch_callback, objname) },
|
||
|
|
+ { .name = ".rela.kpatch.hooks.load",
|
||
|
|
+ .offset = offsetof(struct kpatch_patch_hook, objname) },
|
||
|
|
+ { .name = ".rela.kpatch.hooks.unload",
|
||
|
|
+ .offset = offsetof(struct kpatch_patch_hook, objname) },
|
||
|
|
{ .name = NULL, .offset = 0 },
|
||
|
|
};
|
||
|
|
struct callback *callbackp;
|
||
|
|
--
|
||
|
|
1.7.5.4
|
||
|
|
|