diff --git a/kmod/core/core.c b/kmod/core/core.c index 52861d6..bd50d3d 100644 --- a/kmod/core/core.c +++ b/kmod/core/core.c @@ -534,16 +534,6 @@ static int kpatch_verify_symbol_match(const char *name, unsigned long addr) return 0; } -static unsigned long kpatch_find_exported_symbol(const char *name) -{ - const struct kernel_symbol *sym; - - preempt_disable(); - sym = find_symbol(name, NULL, NULL, true, true); - preempt_enable(); - return sym ? sym->value : 0; -} - static unsigned long kpatch_find_module_symbol(struct module *mod, const char *name) { @@ -564,6 +554,26 @@ static unsigned long kpatch_find_module_symbol(struct module *mod, return kallsyms_lookup_name(buf); } +/* + * External symbols are located outside the parent object (where the parent + * object is either vmlinux or the kmod being patched). + */ +static unsigned long kpatch_find_external_symbol(struct kpatch_module *kpmod, + const char *name) +{ + const struct kernel_symbol *sym; + + /* first, check if it's an exported symbol */ + preempt_disable(); + sym = find_symbol(name, NULL, NULL, true, true); + preempt_enable(); + if (sym) + return sym->value; + + /* otherwise check if it's in another .o within the patch module */ + return kpatch_find_module_symbol(kpmod->mod, name); +} + static int kpatch_write_relocations(struct kpatch_module *kpmod, struct kpatch_object *object) { @@ -584,8 +594,9 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod, } else { /* module, dynrela->src needs to be discovered */ - if (dynrela->exported) - src = kpatch_find_exported_symbol(dynrela->name); + if (dynrela->external) + src = kpatch_find_external_symbol(kpmod, + dynrela->name); else src = kpatch_find_module_symbol(object->mod, dynrela->name); diff --git a/kmod/core/kpatch.h b/kmod/core/kpatch.h index c00203e..d08aecf 100644 --- a/kmod/core/kpatch.h +++ b/kmod/core/kpatch.h @@ -53,7 +53,7 @@ struct kpatch_dynrela { unsigned long type; const char *name; int addend; - int exported; + int external; struct list_head list; }; diff --git a/kmod/patch/kpatch-patch-hook.c b/kmod/patch/kpatch-patch-hook.c index c79970a..3c38f02 100644 --- a/kmod/patch/kpatch-patch-hook.c +++ b/kmod/patch/kpatch-patch-hook.c @@ -318,7 +318,7 @@ static int patch_make_dynrelas_list(struct list_head *objects) dynrela->src = p_dynrela->src; dynrela->type = p_dynrela->type; dynrela->name = p_dynrela->name; - dynrela->exported = p_dynrela->exported; + dynrela->external = p_dynrela->external; 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 3ce4832..953ac5e 100644 --- a/kmod/patch/kpatch-patch.h +++ b/kmod/patch/kpatch-patch.h @@ -37,7 +37,7 @@ struct kpatch_patch_dynrela { unsigned long type; char *name; char *objname; - int exported; + int external; int addend; }; diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 893b200..9fe8102 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -2259,7 +2259,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf, struct symbol *strsym; struct lookup_result result; struct kpatch_patch_dynrela *dynrelas; - int vmlinux, exported; + int vmlinux, external; vmlinux = !strcmp(objname, "vmlinux"); @@ -2307,7 +2307,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf, if (kpatch_is_core_module_symbol(rela->sym->name)) continue; - exported = 0; + external = 0; if (rela->sym->bind == STB_LOCAL) { /* An unchanged local symbol */ @@ -2356,10 +2356,11 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf, if (lookup_global_symbol(table, rela->sym->name, &result)) /* - * Not there, assume it's exported by - * another object. + * Not there, assume it's either an + * exported symbol or provided by + * another .o in the patch module. */ - exported = 1; + external = 1; } log_debug("lookup for %s @ 0x%016lx len %lu\n", rela->sym->name, result.value, result.size); @@ -2372,7 +2373,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf, dynrelas[index].src = 0; dynrelas[index].addend = rela->addend; dynrelas[index].type = rela->type; - dynrelas[index].exported = exported; + dynrelas[index].external = external; /* add rela to fill in dest field */ ALLOC_LINK(dynrela, &relasec->relas); diff --git a/test/integration/module-call-external.patch b/test/integration/module-call-external.patch new file mode 100644 index 0000000..7a1834f --- /dev/null +++ b/test/integration/module-call-external.patch @@ -0,0 +1,35 @@ +Index: src/fs/nfsd/export.c +=================================================================== +--- src.orig/fs/nfsd/export.c ++++ src/fs/nfsd/export.c +@@ -1241,6 +1241,8 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1250,6 +1252,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +Index: src/net/netlink/af_netlink.c +=================================================================== +--- src.orig/net/netlink/af_netlink.c ++++ src/net/netlink/af_netlink.c +@@ -3228,4 +3228,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init);