Implement klp_resolve_symbol() and call it from the module loader. If we see the module being loaded is a livepatch module, and the symbol's shndx is SHN_UNDEF, we call klp_resolve_symbol_wait() to look up the symbol ourselves. Recall that we use the kallsyms API to look up symbols that are not exported and to find symbols specific to a module. We supply the "objname" (i.e. name of module) for the kallsyms lookup through kpatch-build, which will "tag" the right symbols. For symbols that need to be specially resolved by livepatch (i.e. they are non-exported local symbols) their names are appended with "tags" that contain their "objname." This will be already done through the kpatch-build system through create-diff-object [1] create-diff-object will no longer strip symbols and now keep the symbols associated with the former dynrelas. [1] https://github.com/flaming-toast/kpatch/blob/1dc8dddc514b59ee3d417c3b4e3ae890be223149/kpatch-build/create-diff-object.c#L2548 Signed-off-by: Jessica Yu <jeyu@xxxxxxxxxx> --- kernel/livepatch/core.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ kernel/module.c | 43 +++++++++++++++++++++++-- 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 3f9f1d6..7c1cd5c 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -195,6 +195,89 @@ static int klp_find_object_symbol(const char *objname, const char *name, return -EINVAL; } +/* Module loader stuff */ +/* Check if a symbol is tagged with form symname.kpatch.objname */ +static int klp_sym_tagged(const char *name) +{ + if (strstr(name, ".kpatch.")) + return 1; + + return 0; +} + +/* Extract objname from tagged symbol name (symname.kpatch.objname) + * Destructive to @name + */ +static char *klp_extract_objname(const char *name) +{ + char *objname; + + objname = strrchr(name, '.'); + if (objname) { + *objname = '\0'; + objname++; + } + + return objname; +} + +/* Extract real symbol name from tagged symbol name (symname.kpatch.objname) + * Destructive to @name + */ +static char *klp_extract_symname(const char *name) +{ + char *symname; + + symname = strrchr(name, '.'); + if (symname) { + *symname = '\0'; + symname = (char *)name; + } + + return symname; +} + +unsigned long klp_resolve_symbol(struct module *mod, + const char *name) +{ + const struct kernel_symbol *sym; + unsigned long addr; + char *objname; + char *symname; + int ret; + + addr = 0; + objname = NULL; + symname = kstrdup(name, GFP_KERNEL); /* work with a copy */ + + /* Extract tag (symname.kpatch.objname) */ + if (klp_sym_tagged(name)) { + objname = klp_extract_objname(symname); + symname = klp_extract_symname(symname); + } + + sched_annotate_sleep(); + mutex_lock(&module_mutex); + sym = find_symbol(symname, NULL, NULL, true, true); + if (sym) + addr = sym->value; + else { /* Attempt to find symbol in patch module or objname */ + ret = klp_find_object_symbol(mod->name, symname, &addr); + if (ret) { + ret = klp_find_object_symbol(objname, symname, &addr); + if (ret) + goto unlock; + } + } + +unlock: + mutex_unlock(&module_mutex); + kfree(symname); + return addr; /* could be 0x0 */ +} + +/* end of module loader stuff */ + struct klp_verify_args { const char *name; const unsigned long addr; diff --git a/kernel/module.c b/kernel/module.c index ec53f59..41b7838 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -92,6 +92,10 @@ /* If this is set, the section belongs in the init part of the module */ #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) +#ifdef CONFIG_LIVEPATCH +extern unsigned long klp_resolve_symbol(struct module *mod, + const char *name); +#endif /* * Mutex protects: * 1) List of modules (also safely readable with preempt_disable), @@ -183,6 +187,8 @@ struct load_info { } index; }; +static char *get_modinfo(struct load_info *info, const char *tag); + /* We require a truly strong try_module_get(): 0 means failure due to ongoing or failed initialization etc. */ static inline int strong_try_module_get(struct module *mod) @@ -1257,6 +1263,23 @@ unlock: return sym; } +#ifdef CONFIG_LIVEPATCH +static unsigned long +klp_resolve_symbol_wait(struct module *mod, + const char *name) +{ + unsigned long addr; + + if (wait_event_interruptible_timeout(module_wq, + (addr = klp_resolve_symbol(mod, name)) != (long) 0x0, 30 * HZ) <= 0) { + pr_warn("%s: gave up waiting for init of module.\n", + mod->name); + } + + return addr; /* could be 0x0 */ +} +#endif + static const struct kernel_symbol * resolve_symbol_wait(struct module *mod, const struct load_info *info, @@ -1934,11 +1957,15 @@ static int simplify_symbols(struct module *mod, const struct load_info *info) { Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; Elf_Sym *sym = (void *)symsec->sh_addr; + unsigned long addr; unsigned long secbase; unsigned int i; int ret = 0; const struct kernel_symbol *ksym; + ksym = NULL; + addr = 0; + for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) { const char *name = info->strtab + sym[i].st_name; @@ -1963,15 +1990,25 @@ static int simplify_symbols(struct module *mod, const struct load_info *info) break; case SHN_UNDEF: +#ifdef CONFIG_LIVEPATCH + if (get_modinfo((struct load_info *)info, "livepatch")) + addr = klp_resolve_symbol_wait(mod, name); + else + ksym = resolve_symbol_wait(mod, info, name); +#else ksym = resolve_symbol_wait(mod, info, name); +#endif /* Ok if resolved. */ - if (ksym && !IS_ERR(ksym)) { - sym[i].st_value = ksym->value; + if (ksym && !IS_ERR(ksym)) + addr = ksym->value; + + if (addr) { + sym[i].st_value = addr; break; } /* Ok if weak. */ - if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK) + if (!addr && ELF_ST_BIND(sym[i].st_info) == STB_WEAK) break; pr_warn("%s: Unknown symbol %s (err %li)\n", -- 2.4.2 -- To unsubscribe from this list: send the line "unsubscribe live-patching" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html