kpatch/9009-kmod-kpatch-build-support-build-patch-for-old-kernel.patch

350 lines
10 KiB
Diff
Raw Normal View History

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