diff --git a/kmod/core/core.c b/kmod/core/core.c index 2b09c84..7fbcae0 100644 --- a/kmod/core/core.c +++ b/kmod/core/core.c @@ -526,11 +526,15 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod, } else { /* module, dynrela->src needs to be discovered */ - src = kpatch_find_module_symbol(object->mod, - dynrela->name); + if (dynrela->exported) + src = (unsigned long)__symbol_get(dynrela->name); + else + src = kpatch_find_module_symbol(object->mod, + dynrela->name); + if (!src) { - pr_err("unable to find symbol '%s' in module '%s'\n", - dynrela->name, object->name); + pr_err("unable to find symbol '%s'\n", + dynrela->name); return -EINVAL; } @@ -538,6 +542,8 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod, } switch (dynrela->type) { + case R_X86_64_NONE: + continue; case R_X86_64_PC32: loc = dynrela->dest; val = (u32)(dynrela->src + dynrela->addend - @@ -592,6 +598,7 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod, static int kpatch_unlink_object(struct kpatch_object *object) { struct kpatch_func *func; + struct kpatch_dynrela *dynrela; int ret; list_for_each_entry(func, &object->funcs, list) { @@ -605,6 +612,10 @@ static int kpatch_unlink_object(struct kpatch_object *object) } } + list_for_each_entry(dynrela, &object->dynrelas, list) + if (dynrela->src && dynrela->exported) + __symbol_put(dynrela->name); + if (object->mod) module_put(object->mod); diff --git a/kmod/core/kpatch.h b/kmod/core/kpatch.h index 3f5a639..db15392 100644 --- a/kmod/core/kpatch.h +++ b/kmod/core/kpatch.h @@ -54,6 +54,7 @@ struct kpatch_dynrela { const char *name; const char *objname; int addend; + bool exported; struct list_head list; }; diff --git a/kmod/patch/kpatch-patch-hook.c b/kmod/patch/kpatch-patch-hook.c index 60400d7..bd2627a 100644 --- a/kmod/patch/kpatch-patch-hook.c +++ b/kmod/patch/kpatch-patch-hook.c @@ -287,6 +287,7 @@ static int patch_make_dynrelas_list(struct list_head *objects) dynrela->type = p_dynrela->type; dynrela->name = p_dynrela->name; dynrela->objname = p_dynrela->objname; + dynrela->exported = p_dynrela->exported; dynrela->addend = p_dynrela->addend; list_add_tail(&dynrela->list, &object->dynrelas); } diff --git a/kmod/patch/kpatch-patch.h b/kmod/patch/kpatch-patch.h index 0a73c17..b93618d 100644 --- a/kmod/patch/kpatch-patch.h +++ b/kmod/patch/kpatch-patch.h @@ -19,6 +19,10 @@ * Contains the structs used for the patch module special sections */ +#ifndef __KERNEL__ +#include +#endif + struct kpatch_patch_func { unsigned long new_addr; unsigned long new_size; @@ -34,5 +38,6 @@ struct kpatch_patch_dynrela { unsigned long type; char *name; char *objname; + bool exported; int addend; }; diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 99c528a..ca76a85 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -1654,6 +1654,9 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf, struct symbol *strsym; struct lookup_result result; struct kpatch_patch_dynrela *dynrelas; + bool vmlinux, exported; + + vmlinux = !strcmp(objname, "vmlinux"); /* count rela entries that need to be dynamic */ nr = 0; @@ -1726,48 +1729,55 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf, list_for_each_entry_safe(rela, safe, &sec2->relas, list) { if (rela->sym->sec) continue; + + exported = false; + if (rela->sym->bind == STB_LOCAL) { /* An unchanged local symbol */ if (lookup_local_symbol(table, rela->sym->name, hint, &result)) ERROR("lookup_local_symbol %s (%s) needed for %s", rela->sym->name, hint, sec2->base->name); + } + else if (vmlinux) { + /* + * We have a patch to vmlinux which references + * a global symbol. Use a normal rela for + * exported symbols and a dynrela otherwise. + */ + if (lookup_is_exported_symbol(table, rela->sym->name)) + continue; + if (lookup_global_symbol(table, rela->sym->name, + &result)) + ERROR("lookup_global_symbol failed for %s, needed for %s\n", + rela->sym->name, + sec2->base->name); } else { /* - * Symbol is global, could be an object-level - * global not defined in this file or exported - * by another object. - */ - if (!strcmp(objname, "vmlinux") && - lookup_is_exported_symbol(table, rela->sym->name)) - continue; - - /* - * Try to find global symbol. If none is - * found, assume it is exported and no dynrela - * is needed. If the symbol is not exported, - * the final patch module will fail to load. + * We have a patch to a module which references + * a global symbol. First try to find it in + * the module being patched. */ if (lookup_global_symbol(table, rela->sym->name, - &result)) + &result)) /* - * Symbol is not a kernel object - * global. Assume symbol is exported by - * vmlinux or another kernel module. + * Not there, assume it's exported by + * another object. */ - continue; + exported = true; } log_debug("lookup for %s @ 0x%016lx len %lu\n", rela->sym->name, result.value, result.size); /* dest filed in by rela entry below */ - if (!strcmp(objname, "vmlinux")) + if (vmlinux) dynrelas[index].src = result.value; else /* for modules, src is discovered at runtime */ dynrelas[index].src = 0; dynrelas[index].addend = rela->addend; dynrelas[index].type = rela->type; + dynrelas[index].exported = exported; /* add rela to fill in dest field */ ALLOC_LINK(dynrela, &relasec->relas);