mirror of
https://github.com/dynup/kpatch
synced 2025-04-10 03:01:36 +00:00
implement per-object patching/relocations
The recent module patching code has exposed some problems with our data structures. We currently patch the funcs and dynrelas individually, which is kind of scary now that different objects can be patched at different times. Instead it's cleaner and safer to group them by patched object. This patch implements per-object patching and relocations by refactoring the interfaces: - Completely separate the create-diff-object <-> patch module interface from the patch module <-> core module interface. create-diff-object will include "kpatch-patch.h" but not "kpatch.h". Thus, create-diff-object has no knowledge about the core module's interfaces, and the core module has no knowledge about the patch module's special sections. - Newly added kpatch-patch.h defines the format of the patch module special sections. It's used by create-diff-object to create the special sections and used by the patch module to read them. - kpatch.h still defines the core module interfaces. Each kpatch_module has a list of kpatch_objects for each module object to be patched. Each kpatch_object has a list of kpatch_funcs and a list of kpatch_dynrelas. The patch module creates these lists when populating kpatch_module. This way of structuring the data allows us to patch funcs and dynrelas on a per patched object basis, which will allow us to catch more error scenarios and make the code easier to manage going forward. It also allows the use of much more common code between kpatch_register() and kpatch_module_notify().
This commit is contained in:
parent
d22ddec366
commit
84c34ff584
410
kmod/core/core.c
410
kmod/core/core.c
@ -72,10 +72,24 @@ struct kpatch_backtrace_args {
|
||||
};
|
||||
|
||||
struct kpatch_kallsyms_args {
|
||||
char *name;
|
||||
const char *name;
|
||||
unsigned long addr;
|
||||
};
|
||||
|
||||
/* this is a double loop, use goto instead of break */
|
||||
#define do_for_each_linked_func(kpmod, func) { \
|
||||
struct kpatch_object *_object; \
|
||||
list_for_each_entry(_object, &kpmod->objects, list) { \
|
||||
if (!kpatch_object_linked(_object)) \
|
||||
continue; \
|
||||
list_for_each_entry(func, &_object->funcs, list) {
|
||||
|
||||
#define while_for_each_linked_func() \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The kpatch core module has a state machine which allows for proper
|
||||
* synchronization with kpatch_ftrace_handler() when it runs in NMI context.
|
||||
@ -114,21 +128,6 @@ enum {
|
||||
};
|
||||
static atomic_t kpatch_state;
|
||||
|
||||
enum kpatch_op {
|
||||
KPATCH_OP_NONE,
|
||||
KPATCH_OP_PATCH,
|
||||
KPATCH_OP_UNPATCH,
|
||||
};
|
||||
|
||||
struct kpatch_func {
|
||||
struct kpatch_patch *patch;
|
||||
unsigned long old_addr;
|
||||
struct hlist_node node;
|
||||
struct kpatch_module *kpmod;
|
||||
enum kpatch_op op;
|
||||
struct module *mod;
|
||||
};
|
||||
|
||||
static inline void kpatch_state_idle(void)
|
||||
{
|
||||
int state = atomic_read(&kpatch_state);
|
||||
@ -171,10 +170,15 @@ static struct kpatch_func *kpatch_get_prev_func(struct kpatch_func *f,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool kpatch_object_linked(struct kpatch_object *object)
|
||||
{
|
||||
return object->mod || !strcmp(object->name, "vmlinux");
|
||||
}
|
||||
|
||||
static inline int kpatch_compare_addresses(unsigned long stack_addr,
|
||||
unsigned long func_addr,
|
||||
unsigned long func_size,
|
||||
char *func_name)
|
||||
const char *func_name)
|
||||
{
|
||||
if (stack_addr >= func_addr && stack_addr < func_addr + func_size) {
|
||||
pr_err("activeness safety check failed for %s\n", func_name);
|
||||
@ -195,43 +199,37 @@ static void kpatch_backtrace_address_verify(void *data, unsigned long address,
|
||||
return;
|
||||
|
||||
/* check kpmod funcs */
|
||||
for (i = 0; i < kpmod->patches_nr; i++) {
|
||||
do_for_each_linked_func(kpmod, func) {
|
||||
unsigned long func_addr, func_size;
|
||||
char *func_name;
|
||||
const char *func_name;
|
||||
struct kpatch_func *active_func;
|
||||
|
||||
func = &kpmod->funcs[i];
|
||||
|
||||
/* check if patch depends on a future module load */
|
||||
if (!func->old_addr)
|
||||
continue;
|
||||
|
||||
active_func = kpatch_get_func(func->old_addr);
|
||||
if (!active_func) {
|
||||
/* patching an unpatched func */
|
||||
func_addr = func->old_addr;
|
||||
func_size = func->patch->old_size;
|
||||
func_name = func->patch->name;
|
||||
func_size = func->old_size;
|
||||
func_name = func->name;
|
||||
} else {
|
||||
/* repatching or unpatching */
|
||||
func_addr = active_func->patch->new_addr;
|
||||
func_size = active_func->patch->new_size;
|
||||
func_name = active_func->patch->name;
|
||||
func_addr = active_func->new_addr;
|
||||
func_size = active_func->new_size;
|
||||
func_name = active_func->name;
|
||||
}
|
||||
|
||||
args->ret = kpatch_compare_addresses(address, func_addr,
|
||||
func_size, func_name);
|
||||
if (args->ret)
|
||||
return;
|
||||
}
|
||||
} while_for_each_linked_func();
|
||||
|
||||
/* in the replace case, need to check the func hash as well */
|
||||
hash_for_each_rcu(kpatch_func_hash, i, func, node) {
|
||||
if (func->op == KPATCH_OP_UNPATCH) {
|
||||
args->ret = kpatch_compare_addresses(address,
|
||||
func->patch->new_addr,
|
||||
func->patch->new_size,
|
||||
func->patch->name);
|
||||
func->new_addr,
|
||||
func->new_size,
|
||||
func->name);
|
||||
if (args->ret)
|
||||
return;
|
||||
}
|
||||
@ -282,9 +280,8 @@ out:
|
||||
static int kpatch_apply_patch(void *data)
|
||||
{
|
||||
struct kpatch_module *kpmod = data;
|
||||
struct kpatch_func *funcs = kpmod->funcs;
|
||||
int num_funcs = kpmod->patches_nr;
|
||||
int i, ret;
|
||||
struct kpatch_func *func;
|
||||
int ret;
|
||||
|
||||
ret = kpatch_verify_activeness_safety(kpmod);
|
||||
if (ret) {
|
||||
@ -293,10 +290,9 @@ static int kpatch_apply_patch(void *data)
|
||||
}
|
||||
|
||||
/* tentatively add the new funcs to the global func hash */
|
||||
for (i = 0; i < num_funcs; i++)
|
||||
if (funcs[i].old_addr)
|
||||
hash_add_rcu(kpatch_func_hash, &funcs[i].node,
|
||||
funcs[i].old_addr);
|
||||
do_for_each_linked_func(kpmod, func) {
|
||||
hash_add_rcu(kpatch_func_hash, &func->node, func->old_addr);
|
||||
} while_for_each_linked_func();
|
||||
|
||||
/* memory barrier between func hash add and state change */
|
||||
smp_wmb();
|
||||
@ -310,9 +306,9 @@ static int kpatch_apply_patch(void *data)
|
||||
pr_err("NMI activeness safety check failed\n");
|
||||
|
||||
/* Failed, we have to rollback patching process */
|
||||
for (i = 0; i < num_funcs; i++)
|
||||
if (funcs[i].old_addr)
|
||||
hash_del_rcu(&funcs[i].node);
|
||||
do_for_each_linked_func(kpmod, func) {
|
||||
hash_del_rcu(&func->node);
|
||||
} while_for_each_linked_func();
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -324,9 +320,8 @@ static int kpatch_apply_patch(void *data)
|
||||
static int kpatch_remove_patch(void *data)
|
||||
{
|
||||
struct kpatch_module *kpmod = data;
|
||||
struct kpatch_func *funcs = kpmod->funcs;
|
||||
int num_funcs = kpmod->patches_nr;
|
||||
int ret, i;
|
||||
struct kpatch_func *func;
|
||||
int ret;
|
||||
|
||||
ret = kpatch_verify_activeness_safety(kpmod);
|
||||
if (ret) {
|
||||
@ -340,9 +335,9 @@ static int kpatch_remove_patch(void *data)
|
||||
return -EBUSY;
|
||||
|
||||
/* Succeeded, remove all updating funcs from hash table */
|
||||
for (i = 0; i < num_funcs; i++)
|
||||
if (funcs[i].old_addr)
|
||||
hash_del_rcu(&funcs[i].node);
|
||||
do_for_each_linked_func(kpmod, func) {
|
||||
hash_del_rcu(&func->node);
|
||||
} while_for_each_linked_func();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -396,7 +391,7 @@ kpatch_ftrace_handler(unsigned long ip, unsigned long parent_ip,
|
||||
}
|
||||
done:
|
||||
if (func)
|
||||
regs->ip = func->patch->new_addr;
|
||||
regs->ip = func->new_addr;
|
||||
|
||||
preempt_enable_notrace();
|
||||
}
|
||||
@ -460,18 +455,6 @@ static int kpatch_ftrace_remove_func(unsigned long ip)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kpatch_put_modules(struct kpatch_func *funcs, int num_funcs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_funcs; i++) {
|
||||
struct kpatch_func *func = &funcs[i];
|
||||
|
||||
if (func->mod)
|
||||
module_put(func->mod);
|
||||
}
|
||||
}
|
||||
|
||||
static int kpatch_kallsyms_callback(void *data, const char *name,
|
||||
struct module *mod,
|
||||
unsigned long addr)
|
||||
@ -484,7 +467,7 @@ static int kpatch_kallsyms_callback(void *data, const char *name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kpatch_verify_symbol_match(char *name, unsigned long addr)
|
||||
static int kpatch_verify_symbol_match(const char *name, unsigned long addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -503,7 +486,8 @@ static int kpatch_verify_symbol_match(char *name, unsigned long addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long kpatch_find_module_symbol(struct module *mod, char *name)
|
||||
static unsigned long kpatch_find_module_symbol(struct module *mod,
|
||||
const char *name)
|
||||
{
|
||||
char buf[KSYM_SYMBOL_LEN];
|
||||
|
||||
@ -522,27 +506,20 @@ static unsigned long kpatch_find_module_symbol(struct module *mod, char *name)
|
||||
return kallsyms_lookup_name(buf);
|
||||
}
|
||||
|
||||
static int kpatch_write_relocations(struct kpatch_module *kpmod)
|
||||
static int kpatch_write_relocations(struct kpatch_module *kpmod,
|
||||
struct kpatch_object *object)
|
||||
{
|
||||
int ret, i, size, readonly = 0;
|
||||
int ret, size, readonly = 0;
|
||||
struct kpatch_dynrela *dynrela;
|
||||
u64 loc, val;
|
||||
unsigned long core = (unsigned long)kpmod->mod->module_core;
|
||||
unsigned long core_ro_size = kpmod->mod->core_ro_size;
|
||||
unsigned long core_size = kpmod->mod->core_size;
|
||||
unsigned long src;
|
||||
struct module *mod;
|
||||
|
||||
for (i = 0; i < kpmod->dynrelas_nr; i++) {
|
||||
dynrela = &kpmod->dynrelas[i];
|
||||
|
||||
if (!strcmp(dynrela->objname, "vmlinux")) {
|
||||
/* vmlinux */
|
||||
|
||||
/* check if reloc has already been written */
|
||||
if (kpmod->enabled)
|
||||
continue;
|
||||
bool vmlinux = !strcmp(object->name, "vmlinux");
|
||||
|
||||
list_for_each_entry(dynrela, &object->dynrelas, list) {
|
||||
if (vmlinux) {
|
||||
ret = kpatch_verify_symbol_match(dynrela->name,
|
||||
dynrela->src);
|
||||
if (ret)
|
||||
@ -550,21 +527,11 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod)
|
||||
} else {
|
||||
/* module, dynrela->src needs to be discovered */
|
||||
|
||||
/* check if reloc has already been written */
|
||||
if (dynrela->src)
|
||||
continue;
|
||||
|
||||
/* check if dynrela depends on a future loaded module */
|
||||
mutex_lock(&module_mutex);
|
||||
mod = find_module(dynrela->objname);
|
||||
mutex_unlock(&module_mutex);
|
||||
if (!mod)
|
||||
continue;
|
||||
|
||||
src = kpatch_find_module_symbol(mod, dynrela->name);
|
||||
src = kpatch_find_module_symbol(object->mod,
|
||||
dynrela->name);
|
||||
if (!src) {
|
||||
pr_err("unable to find symbol '%s' in module '%s'\n",
|
||||
dynrela->name, mod->name);
|
||||
dynrela->name, object->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -589,9 +556,9 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod)
|
||||
size = 8;
|
||||
break;
|
||||
default:
|
||||
printk("unsupported rela type %ld for source %s (0x%lx <- 0x%lx) at index %d\n",
|
||||
printk("unsupported rela type %ld for source %s (0x%lx <- 0x%lx)\n",
|
||||
dynrela->type, dynrela->name,
|
||||
dynrela->dest, dynrela->src, i);
|
||||
dynrela->dest, dynrela->src);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -623,33 +590,93 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kpatch_calculate_old_addr(struct kpatch_func *func)
|
||||
static int kpatch_unlink_object(struct kpatch_object *object)
|
||||
{
|
||||
struct module *module;
|
||||
struct kpatch_func *func;
|
||||
int ret;
|
||||
|
||||
if (!strcmp(func->patch->objname, "vmlinux")) {
|
||||
func->old_addr = func->patch->old_offset;
|
||||
return;
|
||||
list_for_each_entry(func, &object->funcs, list) {
|
||||
if (!func->old_addr)
|
||||
continue;
|
||||
ret = kpatch_ftrace_remove_func(func->old_addr);
|
||||
if (ret) {
|
||||
WARN(1, "can't unregister ftrace for address 0x%lx\n",
|
||||
func->old_addr);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the module hasn't been loaded yet, we'll patch it later in
|
||||
* kpatch_module_notify().
|
||||
*/
|
||||
mutex_lock(&module_mutex);
|
||||
module = find_module(func->patch->objname);
|
||||
if (!module) {
|
||||
if (object->mod)
|
||||
module_put(object->mod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Link to a to-be-patched object in preparation for patching it.
|
||||
*
|
||||
* - Find the object module
|
||||
* - Write patch module relocations which reference the object
|
||||
* - Calculate the patched functions' addresses
|
||||
* - Register them with ftrace
|
||||
*/
|
||||
static int kpatch_link_object(struct kpatch_module *kpmod,
|
||||
struct kpatch_object *object)
|
||||
{
|
||||
struct module *mod = NULL;
|
||||
struct kpatch_func *func;
|
||||
int ret;
|
||||
bool vmlinux = !strcmp(object->name, "vmlinux");
|
||||
|
||||
if (!vmlinux) {
|
||||
mutex_lock(&module_mutex);
|
||||
mod = find_module(object->name);
|
||||
if (!mod) {
|
||||
/*
|
||||
* The module hasn't been loaded yet. We can patch it
|
||||
* later in kpatch_module_notify().
|
||||
*/
|
||||
mutex_unlock(&module_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* should never fail because we have the mutex */
|
||||
WARN_ON(!try_module_get(mod));
|
||||
mutex_unlock(&module_mutex);
|
||||
return;
|
||||
object->mod = mod;
|
||||
}
|
||||
|
||||
/* should never fail because we have the mutex */
|
||||
WARN_ON(!try_module_get(module));
|
||||
mutex_unlock(&module_mutex);
|
||||
ret = kpatch_write_relocations(kpmod, object);
|
||||
if (ret)
|
||||
goto err_unlink;
|
||||
|
||||
func->mod = module;
|
||||
func->old_addr = (unsigned long)module->module_core +
|
||||
func->patch->old_offset;
|
||||
list_for_each_entry(func, &object->funcs, list) {
|
||||
unsigned long old_addr;
|
||||
|
||||
/* calculate actual old location */
|
||||
if (vmlinux) {
|
||||
old_addr = func->old_offset;
|
||||
ret = kpatch_verify_symbol_match(func->name,
|
||||
old_addr);
|
||||
if (ret)
|
||||
goto err_unlink;
|
||||
} else
|
||||
old_addr = (unsigned long)mod->module_core +
|
||||
func->old_offset;
|
||||
|
||||
/* add to ftrace filter and register handler if needed */
|
||||
ret = kpatch_ftrace_add_func(old_addr);
|
||||
if (ret)
|
||||
goto err_unlink;
|
||||
|
||||
func->old_addr = old_addr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unlink:
|
||||
kpatch_unlink_object(object);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kpatch_module_notify(struct notifier_block *nb, unsigned long action,
|
||||
@ -657,83 +684,63 @@ static int kpatch_module_notify(struct notifier_block *nb, unsigned long action,
|
||||
{
|
||||
struct module *mod = data;
|
||||
struct kpatch_module *kpmod;
|
||||
int printed = 0, ret = 0, i;
|
||||
struct kpatch_object *object;
|
||||
struct kpatch_func *func;
|
||||
int ret = 0;
|
||||
bool found = false;
|
||||
|
||||
if (action != MODULE_STATE_COMING)
|
||||
return ret;
|
||||
return 0;
|
||||
|
||||
down(&kpatch_mutex);
|
||||
|
||||
list_for_each_entry(kpmod, &kpmod_list, list) {
|
||||
if (!kpmod->pending)
|
||||
continue;
|
||||
|
||||
/* temporarily clear pending, can be set again below */
|
||||
kpmod->pending = 0;
|
||||
|
||||
for (i = 0; i < kpmod->patches_nr; i++) {
|
||||
struct kpatch_func *func = &kpmod->funcs[i];
|
||||
|
||||
/* check if it has already been patched */
|
||||
if (func->old_addr)
|
||||
continue;
|
||||
|
||||
/* calculate actual old location (refs if module) */
|
||||
kpatch_calculate_old_addr(func);
|
||||
|
||||
if (!func->old_addr) {
|
||||
kpmod->pending = 1;
|
||||
list_for_each_entry(object, &kpmod->objects, list) {
|
||||
if (kpatch_object_linked(object))
|
||||
continue;
|
||||
if (!strcmp(object->name, mod->name)) {
|
||||
found = true;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!printed) {
|
||||
pr_notice("patching newly loaded module '%s'\n",
|
||||
mod->name);
|
||||
printed = 1;
|
||||
}
|
||||
|
||||
ret = kpatch_ftrace_add_func(func->old_addr);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* add to the global func hash */
|
||||
hash_add_rcu(kpatch_func_hash, &func->node,
|
||||
func->old_addr);
|
||||
}
|
||||
|
||||
/* write any newly valid relocations */
|
||||
ret = kpatch_write_relocations(kpmod);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
done:
|
||||
if (!found)
|
||||
goto out;
|
||||
|
||||
ret = kpatch_link_object(kpmod, object);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
BUG_ON(!object->mod);
|
||||
|
||||
pr_notice("patching newly loaded module '%s'\n", object->name);
|
||||
|
||||
/* add to the global func hash */
|
||||
list_for_each_entry(func, &object->funcs, list)
|
||||
hash_add_rcu(kpatch_func_hash, &func->node, func->old_addr);
|
||||
|
||||
out:
|
||||
up(&kpatch_mutex);
|
||||
|
||||
/* no way to stop the module load on error */
|
||||
WARN(ret, "error (%d) patching newly loaded module '%s'\n", ret,
|
||||
mod->name);
|
||||
object->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
||||
{
|
||||
int ret, i;
|
||||
struct kpatch_func *funcs, *func;
|
||||
int num_funcs = kpmod->patches_nr;
|
||||
struct kpatch_object *object;
|
||||
struct kpatch_func *func;
|
||||
|
||||
if (!kpmod->mod || !kpmod->patches || !num_funcs)
|
||||
if (!kpmod->mod || list_empty(&kpmod->objects))
|
||||
return -EINVAL;
|
||||
|
||||
funcs = kzalloc(sizeof(*funcs) * num_funcs, GFP_KERNEL);
|
||||
if (!funcs)
|
||||
return -ENOMEM;
|
||||
|
||||
down(&kpatch_mutex);
|
||||
|
||||
kpmod->enabled = false;
|
||||
kpmod->pending = 0;
|
||||
kpmod->funcs = funcs;
|
||||
list_add_tail(&kpmod->list, &kpmod_list);
|
||||
|
||||
if (!try_module_get(kpmod->mod)) {
|
||||
@ -741,40 +748,24 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
||||
goto err_up;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_funcs; i++) {
|
||||
func = &funcs[i];
|
||||
list_for_each_entry(object, &kpmod->objects, list) {
|
||||
|
||||
func->op = KPATCH_OP_PATCH;
|
||||
func->kpmod = kpmod;
|
||||
func->patch = &kpmod->patches[i];
|
||||
ret = kpatch_link_object(kpmod, object);
|
||||
if (ret)
|
||||
goto err_unlink;
|
||||
|
||||
/* calculate actual old location (refs if module) */
|
||||
kpatch_calculate_old_addr(func);
|
||||
|
||||
/* check if patch depends on a future loaded module */
|
||||
if (!func->old_addr) {
|
||||
kpmod->pending = 1;
|
||||
if (!object->mod) {
|
||||
pr_notice("delaying patch of unloaded module '%s'\n",
|
||||
object->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = kpatch_verify_symbol_match(func->patch->name,
|
||||
func->old_addr);
|
||||
if (ret) {
|
||||
num_funcs = i;
|
||||
goto err_rollback;
|
||||
}
|
||||
pr_notice("patching module '%s\n", object->name);
|
||||
|
||||
ret = kpatch_ftrace_add_func(func->old_addr);
|
||||
if (ret) {
|
||||
num_funcs = i;
|
||||
goto err_rollback;
|
||||
}
|
||||
list_for_each_entry(func, &object->funcs, list)
|
||||
func->op = KPATCH_OP_PATCH;
|
||||
}
|
||||
|
||||
ret = kpatch_write_relocations(kpmod);
|
||||
if (ret)
|
||||
goto err_rollback;
|
||||
|
||||
if (replace)
|
||||
hash_for_each_rcu(kpatch_func_hash, i, func, node)
|
||||
func->op = KPATCH_OP_UNPATCH;
|
||||
@ -836,8 +827,9 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace)
|
||||
if (ret)
|
||||
goto err_ops;
|
||||
|
||||
for (i = 0; i < num_funcs; i++)
|
||||
funcs[i].op = KPATCH_OP_NONE;
|
||||
do_for_each_linked_func(kpmod, func) {
|
||||
func->op = KPATCH_OP_NONE;
|
||||
} while_for_each_linked_func();
|
||||
|
||||
/* TODO: need TAINT_KPATCH */
|
||||
pr_notice_once("tainting kernel with TAINT_USER\n");
|
||||
@ -854,32 +846,32 @@ err_ops:
|
||||
if (replace)
|
||||
hash_for_each_rcu(kpatch_func_hash, i, func, node)
|
||||
func->op = KPATCH_OP_NONE;
|
||||
err_rollback:
|
||||
for (i = 0; i < num_funcs; i++)
|
||||
kpatch_ftrace_remove_func(funcs[i].old_addr);
|
||||
kpatch_put_modules(funcs, kpmod->patches_nr);
|
||||
err_unlink:
|
||||
list_for_each_entry(object, &kpmod->objects, list)
|
||||
if (kpatch_object_linked(object))
|
||||
kpatch_unlink_object(object);
|
||||
module_put(kpmod->mod);
|
||||
err_up:
|
||||
list_del(&kpmod->list);
|
||||
up(&kpatch_mutex);
|
||||
kfree(kpmod->funcs);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(kpatch_register);
|
||||
|
||||
int kpatch_unregister(struct kpatch_module *kpmod)
|
||||
{
|
||||
struct kpatch_func *funcs = kpmod->funcs;
|
||||
int num_funcs = kpmod->patches_nr;
|
||||
int i, ret;
|
||||
struct kpatch_object *object;
|
||||
struct kpatch_func *func;
|
||||
int ret;
|
||||
|
||||
if (!kpmod->enabled)
|
||||
return -EINVAL;
|
||||
|
||||
down(&kpatch_mutex);
|
||||
|
||||
for (i = 0; i < num_funcs; i++)
|
||||
funcs[i].op = KPATCH_OP_UNPATCH;
|
||||
do_for_each_linked_func(kpmod, func) {
|
||||
func->op = KPATCH_OP_UNPATCH;
|
||||
} while_for_each_linked_func();
|
||||
|
||||
/* memory barrier between func hash and state write */
|
||||
smp_wmb();
|
||||
@ -901,26 +893,20 @@ int kpatch_unregister(struct kpatch_module *kpmod)
|
||||
synchronize_rcu();
|
||||
|
||||
if (ret) {
|
||||
for (i = 0; i < num_funcs; i++)
|
||||
funcs[i].op = KPATCH_OP_NONE;
|
||||
do_for_each_linked_func(kpmod, func) {
|
||||
func->op = KPATCH_OP_NONE;
|
||||
} while_for_each_linked_func();
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_funcs; i++) {
|
||||
if (!funcs[i].old_addr)
|
||||
list_for_each_entry(object, &kpmod->objects, list) {
|
||||
if (!kpatch_object_linked(object))
|
||||
continue;
|
||||
ret = kpatch_ftrace_remove_func(funcs[i].old_addr);
|
||||
if (ret) {
|
||||
WARN(1, "can't unregister ftrace for address 0x%lx\n",
|
||||
funcs[i].old_addr);
|
||||
ret = kpatch_unlink_object(object);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
kpatch_put_modules(funcs, num_funcs);
|
||||
|
||||
kfree(kpmod->funcs);
|
||||
|
||||
pr_notice("unloaded patch module '%s'\n", kpmod->mod->name);
|
||||
|
||||
kpmod->enabled = false;
|
||||
|
@ -23,42 +23,60 @@
|
||||
#ifndef _KPATCH_H_
|
||||
#define _KPATCH_H_
|
||||
|
||||
struct kpatch_patch {
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
enum kpatch_op {
|
||||
KPATCH_OP_NONE,
|
||||
KPATCH_OP_PATCH,
|
||||
KPATCH_OP_UNPATCH,
|
||||
};
|
||||
|
||||
struct kpatch_func {
|
||||
/* public */
|
||||
unsigned long new_addr;
|
||||
unsigned long new_size;
|
||||
unsigned long old_offset;
|
||||
unsigned long old_size;
|
||||
char *name;
|
||||
char *objname;
|
||||
const char *name;
|
||||
struct list_head list;
|
||||
|
||||
/* private */
|
||||
struct hlist_node node;
|
||||
unsigned long old_addr;
|
||||
enum kpatch_op op;
|
||||
};
|
||||
|
||||
struct kpatch_dynrela {
|
||||
unsigned long dest;
|
||||
unsigned long src;
|
||||
unsigned long type;
|
||||
char *name;
|
||||
char *objname;
|
||||
const char *name;
|
||||
const char *objname;
|
||||
int addend;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
struct kpatch_object {
|
||||
struct list_head list;
|
||||
const char *name;
|
||||
struct list_head funcs;
|
||||
struct list_head dynrelas;
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
/* private */
|
||||
struct module *mod;
|
||||
};
|
||||
|
||||
struct kpatch_module {
|
||||
/* public */
|
||||
struct module *mod;
|
||||
struct kpatch_patch *patches;
|
||||
struct kpatch_dynrela *dynrelas;
|
||||
int patches_nr;
|
||||
int dynrelas_nr;
|
||||
struct list_head objects;
|
||||
|
||||
/* public read-only */
|
||||
bool enabled;
|
||||
|
||||
/* private */
|
||||
struct list_head list;
|
||||
struct kpatch_func *funcs;
|
||||
bool pending;
|
||||
};
|
||||
|
||||
extern struct kobject *kpatch_patches_kobj;
|
||||
@ -66,6 +84,4 @@ extern struct kobject *kpatch_patches_kobj;
|
||||
extern int kpatch_register(struct kpatch_module *kpmod, bool replace);
|
||||
extern int kpatch_unregister(struct kpatch_module *kpmod);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _KPATCH_H_ */
|
||||
|
@ -24,13 +24,14 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include "kpatch.h"
|
||||
#include "kpatch-patch.h"
|
||||
|
||||
static bool replace;
|
||||
module_param(replace, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(replace, "replace all previously loaded patch modules");
|
||||
|
||||
extern char __kpatch_patches, __kpatch_patches_end;
|
||||
extern char __kpatch_dynrelas, __kpatch_dynrelas_end;
|
||||
extern struct kpatch_patch_func __kpatch_funcs[], __kpatch_funcs_end[];
|
||||
extern struct kpatch_patch_dynrela __kpatch_dynrelas[], __kpatch_dynrelas_end[];
|
||||
|
||||
static struct kpatch_module kpmod;
|
||||
static struct kobject *patch_kobj;
|
||||
@ -38,11 +39,11 @@ static struct kobject *functions_kobj;
|
||||
|
||||
struct kpatch_func_obj {
|
||||
struct kobject kobj;
|
||||
struct kpatch_patch *patch;
|
||||
struct kpatch_patch_func *func;
|
||||
char name[KSYM_NAME_LEN];
|
||||
};
|
||||
|
||||
static struct kpatch_func_obj **funcs = NULL;
|
||||
static struct kpatch_func_obj **func_objs = NULL;
|
||||
|
||||
static ssize_t patch_enabled_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
@ -87,7 +88,7 @@ static ssize_t func_old_addr_show(struct kobject *kobj,
|
||||
struct kpatch_func_obj *func =
|
||||
container_of(kobj, struct kpatch_func_obj, kobj);
|
||||
|
||||
return sprintf(buf, "0x%lx\n", func->patch->old_offset);
|
||||
return sprintf(buf, "0x%lx\n", func->func->old_offset);
|
||||
}
|
||||
|
||||
static ssize_t func_new_addr_show(struct kobject *kobj,
|
||||
@ -96,7 +97,7 @@ static ssize_t func_new_addr_show(struct kobject *kobj,
|
||||
struct kpatch_func_obj *func =
|
||||
container_of(kobj, struct kpatch_func_obj, kobj);
|
||||
|
||||
return sprintf(buf, "0x%lx\n", func->patch->new_addr);
|
||||
return sprintf(buf, "0x%lx\n", func->func->new_addr);
|
||||
}
|
||||
|
||||
static struct kobj_attribute old_addr_attr =
|
||||
@ -149,26 +150,66 @@ static struct kpatch_func_obj *func_kobj_alloc(void)
|
||||
return func;
|
||||
}
|
||||
|
||||
static struct kpatch_object *patch_find_or_add_object(struct list_head *head,
|
||||
const char *name)
|
||||
{
|
||||
struct kpatch_object *object;
|
||||
|
||||
list_for_each_entry(object, head, list) {
|
||||
if (!strcmp(object->name, name))
|
||||
return object;
|
||||
}
|
||||
|
||||
object = kzalloc(sizeof(*object), GFP_KERNEL);
|
||||
if (!object)
|
||||
return NULL;
|
||||
|
||||
object->name = name;
|
||||
INIT_LIST_HEAD(&object->funcs);
|
||||
INIT_LIST_HEAD(&object->dynrelas);
|
||||
|
||||
list_add_tail(&object->list, head);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static void patch_free_objects(void)
|
||||
{
|
||||
struct kpatch_object *object, *object_safe;
|
||||
struct kpatch_func *func, *func_safe;
|
||||
struct kpatch_dynrela *dynrela, *dynrela_safe;
|
||||
|
||||
list_for_each_entry_safe(object, object_safe, &kpmod.objects, list) {
|
||||
list_for_each_entry_safe(func, func_safe, &object->funcs,
|
||||
list) {
|
||||
list_del(&func->list);
|
||||
kfree(func);
|
||||
}
|
||||
list_for_each_entry_safe(dynrela, dynrela_safe,
|
||||
&object->dynrelas, list) {
|
||||
list_del(&dynrela->list);
|
||||
kfree(dynrela);
|
||||
}
|
||||
list_del(&object->list);
|
||||
kfree(object);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init patch_init(void)
|
||||
{
|
||||
int ret;
|
||||
int i = 0;
|
||||
struct kpatch_func_obj *func = NULL;
|
||||
int ret, funcs_nr, i;
|
||||
struct kpatch_func_obj *func_obj = NULL;
|
||||
struct kpatch_object *object;
|
||||
struct kpatch_patch_func *p_func;
|
||||
struct kpatch_func *func;
|
||||
struct kpatch_patch_dynrela *p_dynrela;
|
||||
struct kpatch_dynrela *dynrela;
|
||||
|
||||
kpmod.mod = THIS_MODULE;
|
||||
kpmod.patches = (struct kpatch_patch *)&__kpatch_patches;
|
||||
kpmod.patches_nr = (&__kpatch_patches_end - &__kpatch_patches) /
|
||||
sizeof(*kpmod.patches);
|
||||
kpmod.dynrelas = (struct kpatch_dynrela *)&__kpatch_dynrelas;
|
||||
kpmod.dynrelas_nr = (&__kpatch_dynrelas_end - &__kpatch_dynrelas) /
|
||||
sizeof(*kpmod.dynrelas);
|
||||
|
||||
funcs = kzalloc(kpmod.patches_nr * sizeof(struct kpatch_func_obj*),
|
||||
GFP_KERNEL);
|
||||
if (!funcs) {
|
||||
ret = -ENOMEM;
|
||||
goto err_ret;
|
||||
}
|
||||
funcs_nr = __kpatch_funcs_end - __kpatch_funcs;
|
||||
func_objs = kzalloc(funcs_nr * sizeof(struct kpatch_func_obj*),
|
||||
GFP_KERNEL);
|
||||
if (!func_objs)
|
||||
return -ENOMEM;
|
||||
|
||||
patch_kobj = kobject_create_and_add(THIS_MODULE->name,
|
||||
kpatch_patches_kobj);
|
||||
@ -187,58 +228,106 @@ static int __init patch_init(void)
|
||||
goto err_sysfs;
|
||||
}
|
||||
|
||||
for (i = 0; i < kpmod.patches_nr; i++) {
|
||||
func = func_kobj_alloc();
|
||||
kpmod.mod = THIS_MODULE;
|
||||
INIT_LIST_HEAD(&kpmod.objects);
|
||||
|
||||
i = 0;
|
||||
for (p_func = __kpatch_funcs; p_func < __kpatch_funcs_end; p_func++) {
|
||||
object = patch_find_or_add_object(&kpmod.objects,
|
||||
p_func->objname);
|
||||
if (!object) {
|
||||
ret = -ENOMEM;
|
||||
goto err_objects;
|
||||
}
|
||||
|
||||
func = kzalloc(sizeof(*func), GFP_KERNEL);
|
||||
if (!func) {
|
||||
ret = -ENOMEM;
|
||||
goto err_sysfs2;
|
||||
goto err_objects;
|
||||
}
|
||||
funcs[i] = func;
|
||||
|
||||
sprint_symbol_no_offset(func->name, kpmod.patches[i].old_offset);
|
||||
func->new_addr = p_func->new_addr;
|
||||
func->new_size = p_func->new_size;
|
||||
func->old_offset = p_func->old_offset;
|
||||
func->old_size = p_func->old_size;
|
||||
func->name = p_func->name;
|
||||
list_add_tail(&func->list, &object->funcs);
|
||||
|
||||
ret = kobject_add(&func->kobj, functions_kobj,
|
||||
"%s", func->name);
|
||||
func_obj = func_kobj_alloc();
|
||||
if (!func_obj) {
|
||||
ret = -ENOMEM;
|
||||
goto err_objects;
|
||||
}
|
||||
|
||||
func_obj->func = p_func;
|
||||
func_objs[i++] = func_obj;
|
||||
sprint_symbol_no_offset(func_obj->name,
|
||||
p_func->old_offset);
|
||||
|
||||
ret = kobject_add(&func_obj->kobj, functions_kobj,
|
||||
"%s", func_obj->name);
|
||||
if (ret)
|
||||
goto err_sysfs2;
|
||||
goto err_objects;
|
||||
}
|
||||
|
||||
func->patch = &kpmod.patches[i];
|
||||
for (p_dynrela = __kpatch_dynrelas; p_dynrela < __kpatch_dynrelas_end;
|
||||
p_dynrela++) {
|
||||
object = patch_find_or_add_object(&kpmod.objects,
|
||||
p_dynrela->objname);
|
||||
if (!object) {
|
||||
ret = -ENOMEM;
|
||||
goto err_objects;
|
||||
}
|
||||
|
||||
dynrela = kzalloc(sizeof(*dynrela), GFP_KERNEL);
|
||||
if (!dynrela) {
|
||||
ret = -ENOMEM;
|
||||
goto err_objects;
|
||||
}
|
||||
|
||||
dynrela->dest = p_dynrela->dest;
|
||||
dynrela->src = p_dynrela->src;
|
||||
dynrela->type = p_dynrela->type;
|
||||
dynrela->name = p_dynrela->name;
|
||||
dynrela->objname = p_dynrela->objname;
|
||||
dynrela->addend = p_dynrela->addend;
|
||||
list_add_tail(&dynrela->list, &object->dynrelas);
|
||||
}
|
||||
|
||||
ret = kpatch_register(&kpmod, replace);
|
||||
if (ret)
|
||||
goto err_sysfs2;
|
||||
goto err_objects;
|
||||
|
||||
return 0;
|
||||
|
||||
err_sysfs2:
|
||||
for (i = 0; i < kpmod.patches_nr; i++) {
|
||||
if (funcs[i] != NULL)
|
||||
kobject_put(&funcs[i]->kobj);
|
||||
}
|
||||
err_objects:
|
||||
for (i = 0; i < funcs_nr; i++)
|
||||
if (func_objs[i] != NULL)
|
||||
kobject_put(&func_objs[i]->kobj);
|
||||
patch_free_objects();
|
||||
kobject_put(functions_kobj);
|
||||
err_sysfs:
|
||||
sysfs_remove_file(patch_kobj, &patch_enabled_attr.attr);
|
||||
err_put:
|
||||
kobject_put(patch_kobj);
|
||||
err_free:
|
||||
kfree(funcs);
|
||||
err_ret:
|
||||
kfree(func_objs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit patch_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
WARN_ON(kpmod.enabled);
|
||||
|
||||
for (i = 0; i < kpmod.patches_nr; i++) {
|
||||
kobject_put(&funcs[i]->kobj);
|
||||
}
|
||||
kfree(funcs);
|
||||
for (i = 0; i < __kpatch_funcs_end - __kpatch_funcs; i++)
|
||||
kobject_put(&func_objs[i]->kobj);
|
||||
patch_free_objects();
|
||||
kobject_put(functions_kobj);
|
||||
sysfs_remove_file(patch_kobj, &patch_enabled_attr.attr);
|
||||
kobject_put(patch_kobj);
|
||||
kfree(func_objs);
|
||||
}
|
||||
|
||||
module_init(patch_init);
|
||||
|
38
kmod/patch/kpatch-patch.h
Normal file
38
kmod/patch/kpatch-patch.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* kpatch-patch.h
|
||||
*
|
||||
* Copyright (C) 2014 Josh Poimboeuf <jpoimboe@redhat.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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Contains the structs used for the patch module special sections
|
||||
*/
|
||||
|
||||
struct kpatch_patch_func {
|
||||
unsigned long new_addr;
|
||||
unsigned long new_size;
|
||||
unsigned long old_offset;
|
||||
unsigned long old_size;
|
||||
char *name;
|
||||
char *objname;
|
||||
};
|
||||
|
||||
struct kpatch_patch_dynrela {
|
||||
unsigned long dest;
|
||||
unsigned long src;
|
||||
unsigned long type;
|
||||
char *name;
|
||||
char *objname;
|
||||
int addend;
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
__kpatch_patches = ADDR(.kpatch.patches);
|
||||
__kpatch_patches_end = ADDR(.kpatch.patches) + SIZEOF(.kpatch.patches);
|
||||
__kpatch_funcs = ADDR(.kpatch.funcs);
|
||||
__kpatch_funcs_end = ADDR(.kpatch.funcs) + SIZEOF(.kpatch.funcs);
|
||||
__kpatch_dynrelas = ADDR(.kpatch.dynrelas);
|
||||
__kpatch_dynrelas_end = ADDR(.kpatch.dynrelas) + SIZEOF(.kpatch.dynrelas);
|
||||
|
@ -49,7 +49,7 @@
|
||||
#include "list.h"
|
||||
#include "lookup.h"
|
||||
#include "asm/insn.h"
|
||||
#include "kpatch.h"
|
||||
#include "kpatch-patch.h"
|
||||
|
||||
#define ERROR(format, ...) \
|
||||
error(1, 0, "%s: %d: " format, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
@ -1492,7 +1492,7 @@ void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
||||
struct symbol *sym, *strsym;
|
||||
struct rela *rela;
|
||||
struct lookup_result result;
|
||||
struct kpatch_patch *patches;
|
||||
struct kpatch_patch_func *funcs;
|
||||
|
||||
/* count patched functions */
|
||||
nr = 0;
|
||||
@ -1500,36 +1500,36 @@ void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
||||
if (sym->type == STT_FUNC && sym->status == CHANGED)
|
||||
nr++;
|
||||
|
||||
/* create .kpatch.patches */
|
||||
/* create .kpatch.funcs */
|
||||
|
||||
/* allocate section resources */
|
||||
ALLOC_LINK(sec, &kelf->sections);
|
||||
size = nr * sizeof(*patches);
|
||||
patches = malloc(size);
|
||||
if (!patches)
|
||||
size = nr * sizeof(*funcs);
|
||||
funcs = malloc(size);
|
||||
if (!funcs)
|
||||
ERROR("malloc");
|
||||
sec->name = ".kpatch.patches";
|
||||
sec->name = ".kpatch.funcs";
|
||||
|
||||
/* set data */
|
||||
sec->data = malloc(sizeof(*sec->data));
|
||||
if (!sec->data)
|
||||
ERROR("malloc");
|
||||
sec->data->d_buf = patches;
|
||||
sec->data->d_buf = funcs;
|
||||
sec->data->d_size = size;
|
||||
sec->data->d_type = ELF_T_BYTE;
|
||||
|
||||
/* set section header */
|
||||
sec->sh.sh_type = SHT_PROGBITS;
|
||||
sec->sh.sh_entsize = sizeof(*patches);
|
||||
sec->sh.sh_entsize = sizeof(*funcs);
|
||||
sec->sh.sh_addralign = 8;
|
||||
sec->sh.sh_flags = SHF_ALLOC;
|
||||
sec->sh.sh_size = size;
|
||||
|
||||
/* create .rela.patches */
|
||||
/* create .rela.funcs */
|
||||
|
||||
/* allocate section resources */
|
||||
ALLOC_LINK(relasec, &kelf->sections);
|
||||
relasec->name = ".rela.kpatch.patches";
|
||||
relasec->name = ".rela.kpatch.funcs";
|
||||
relasec->base = sec;
|
||||
INIT_LIST_HEAD(&relasec->relas);
|
||||
|
||||
@ -1570,42 +1570,42 @@ void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
||||
sym->name, result.value, result.size);
|
||||
|
||||
/* add entry in text section */
|
||||
patches[index].old_offset = result.value;
|
||||
patches[index].old_size = result.size;
|
||||
patches[index].new_size = sym->sym.st_size;
|
||||
funcs[index].old_offset = result.value;
|
||||
funcs[index].old_size = result.size;
|
||||
funcs[index].new_size = sym->sym.st_size;
|
||||
|
||||
/*
|
||||
* Add a relocation that will populate
|
||||
* the patches[index].new_addr field at
|
||||
* the funcs[index].new_addr field at
|
||||
* module load time.
|
||||
*/
|
||||
ALLOC_LINK(rela, &relasec->relas);
|
||||
rela->sym = sym;
|
||||
rela->type = R_X86_64_64;
|
||||
rela->addend = 0;
|
||||
rela->offset = index * sizeof(*patches);
|
||||
rela->offset = index * sizeof(*funcs);
|
||||
|
||||
/*
|
||||
* Add a relocation that will populate
|
||||
* the patches[index].name field.
|
||||
* the funcs[index].name field.
|
||||
*/
|
||||
ALLOC_LINK(rela, &relasec->relas);
|
||||
rela->sym = strsym;
|
||||
rela->type = R_X86_64_64;
|
||||
rela->addend = offset_of_string(&kelf->strings, sym->name);
|
||||
rela->offset = index * sizeof(*patches) +
|
||||
offsetof(struct kpatch_patch, name);
|
||||
rela->offset = index * sizeof(*funcs) +
|
||||
offsetof(struct kpatch_patch_func, name);
|
||||
|
||||
/*
|
||||
* Add a relocation that will populate
|
||||
* the patches[index].objname field.
|
||||
* the funcs[index].objname field.
|
||||
*/
|
||||
ALLOC_LINK(rela, &relasec->relas);
|
||||
rela->sym = strsym;
|
||||
rela->type = R_X86_64_64;
|
||||
rela->addend = objname_offset;
|
||||
rela->offset = index * sizeof(*patches) +
|
||||
offsetof(struct kpatch_patch, objname);
|
||||
rela->offset = index * sizeof(*funcs) +
|
||||
offsetof(struct kpatch_patch_func,objname);
|
||||
|
||||
index++;
|
||||
}
|
||||
@ -1613,7 +1613,7 @@ void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
||||
|
||||
/* sanity check, index should equal nr */
|
||||
if (index != nr)
|
||||
ERROR("size mismatch in patches sections");
|
||||
ERROR("size mismatch in funcs sections");
|
||||
|
||||
}
|
||||
|
||||
@ -1626,14 +1626,14 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
|
||||
struct rela *rela, *dynrela, *safe;
|
||||
struct symbol *strsym;
|
||||
struct lookup_result result;
|
||||
struct kpatch_dynrela *dynrelas;
|
||||
struct kpatch_patch_dynrela *dynrelas;
|
||||
|
||||
/* count rela entries that need to be dynamic */
|
||||
nr = 0;
|
||||
list_for_each_entry(sec, &kelf->sections, list) {
|
||||
if (!is_rela_section(sec))
|
||||
continue;
|
||||
if (!strcmp(sec->name, ".rela.kpatch.patches"))
|
||||
if (!strcmp(sec->name, ".rela.kpatch.funcs"))
|
||||
continue;
|
||||
list_for_each_entry(rela, &sec->relas, list)
|
||||
nr++; /* upper bound on number of dynrelas */
|
||||
@ -1757,7 +1757,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
|
||||
dynrela->sym = strsym;
|
||||
dynrela->type = R_X86_64_64;
|
||||
dynrela->addend = offset_of_string(&kelf->strings, rela->sym->name);
|
||||
dynrela->offset = index * sizeof(*dynrelas) + offsetof(struct kpatch_dynrela, name);
|
||||
dynrela->offset = index * sizeof(*dynrelas) + offsetof(struct kpatch_patch_dynrela, name);
|
||||
|
||||
/* add rela to fill in objname field */
|
||||
ALLOC_LINK(dynrela, &relasec->relas);
|
||||
@ -1765,7 +1765,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
|
||||
dynrela->type = R_X86_64_64;
|
||||
dynrela->addend = objname_offset;
|
||||
dynrela->offset = index * sizeof(*dynrelas) +
|
||||
offsetof(struct kpatch_dynrela, objname);
|
||||
offsetof(struct kpatch_patch_dynrela, objname);
|
||||
|
||||
list_del(&rela->list);
|
||||
free(rela);
|
||||
@ -1776,7 +1776,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf,
|
||||
}
|
||||
|
||||
/* set size to actual number of dynrelas */
|
||||
sec->data->d_size = index * sizeof(struct kpatch_dynrela);
|
||||
sec->data->d_size = index * sizeof(struct kpatch_patch_dynrela);
|
||||
sec->sh.sh_size = sec->data->d_size;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user