diff --git a/kmod/core/core.c b/kmod/core/core.c index 21dd458..23c602e 100644 --- a/kmod/core/core.c +++ b/kmod/core/core.c @@ -69,6 +69,11 @@ struct kpatch_backtrace_args { int ret; }; +struct kpatch_kallsyms_args { + char *name; + unsigned long addr; +}; + /* * The kpatch core module has a state machine which allows for proper * synchronization with kpatch_ftrace_handler() when it runs in NMI context. @@ -421,41 +426,29 @@ static void kpatch_remove_funcs_from_filter(struct kpatch_func *funcs, } } -int kpatch_verify_symbol_match(char *name, unsigned long addr) +static int kpatch_kallsyms_callback(void *data, const char *name, + struct module *mod, + unsigned long addr) +{ + struct kpatch_kallsyms_args *args = data; + + if (args->addr == addr && !strcmp(args->name, name)) + return 1; + + return 0; +} + +static int kpatch_verify_symbol_match(char *name, unsigned long addr) { int ret; - unsigned long offset; - char buf[KSYM_SYMBOL_LEN], *symname, *offsetstr, *pos; - sprint_symbol(buf, addr); + struct kpatch_kallsyms_args args = { + .name = name, + .addr = addr, + }; - /* - * sprint_symbol() doesn't return an error if it can't find the symbol. - * It returns the addr we passed in as a hex string in 'name'. - * Check for this and, if so, error. - */ - if (!strncmp(buf, "0x", 2)) { - pr_err("failed to find named symbol\n"); - return -EINVAL; - } - - /* hack out the symbol name and offset */ - /* example format for buf 'printk+0x0/0x8e' */ - symname = buf; - pos = strchr(buf, '+'); - *pos = '\0'; - pos += 3; /* skip the "+0x" */ - offsetstr = pos; - pos = strchr(offsetstr, '/'); - *pos = '\0'; - - ret = kstrtoul(offsetstr, 16, &offset); - if (ret) { - pr_err("failed to parse symbol offset\n"); - return ret; - } - - if (strcmp(symname, name) || offset != 0) { + ret = kallsyms_on_each_symbol(kpatch_kallsyms_callback, &args); + if (!ret) { pr_err("base kernel mismatch for symbol '%s'\n", name); pr_err("expected address was 0x%016lx\n", addr); return -EINVAL; diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 8506ed6..251f420 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -688,20 +688,49 @@ void kpatch_replace_sections_syms(struct kpatch_elf *kelf) { struct section *sec; struct rela *rela; + struct symbol *sym; list_for_each_entry(sec, &kelf->sections, list) { if (!is_rela_section(sec)) continue; list_for_each_entry(rela, &sec->relas, list) { - if (rela->sym->type != STT_SECTION || - !rela->sym->sec || !rela->sym->sec->sym) + + /* + * Replace references to bundled sections with their + * symbols. + */ + if (rela->sym->type == STT_SECTION && + rela->sym->sec && rela->sym->sec->sym) { + + log_debug("replacing %s with %s\n", + rela->sym->name, + rela->sym->sec->sym->name); + + rela->sym = rela->sym->sec->sym; continue; + } - log_debug("replacing %s with %s\n", - rela->sym->name, rela->sym->sec->sym->name); + /* + * .data..percpu is a special data section whose data + * symbols aren't bundled with sections when using + * -fdata-sections. We need to replace the section + * references with their corresponding objects. + */ + if (strcmp(rela->sym->name, ".data..percpu")) + continue; + list_for_each_entry(sym, &kelf->symbols, list) { - rela->sym = rela->sym->sec->sym; + if (sym->sec != rela->sym->sec || + sym->sym.st_value != rela->addend) + continue; + + log_debug("replacing %s with %s\n", + rela->sym->name, sym->name); + + rela->sym = sym; + break; + } } } }