mirror of
https://github.com/dynup/kpatch
synced 2025-03-05 18:29:59 +00:00
create-diff-object: percpu support
The -fdata-sections gcc flag doesn't work with objects in the .data..percpu section. Any function which uses a percpu variable references this section, causing the section to get incorrectly included in the patch module. Manually convert these section references to object symbol references so that the needed symbol can be found in vmlinux. Also, the core module symbol verification code will fail when looking up a percpu variable, because sprint_symbol doesn't think a percpu address is a valid kernel address. So rewrite the symbol verification code to use kallsyms_on_each_symbol() instead. It's not ideal performance-wise: it seems to cost about 1ms per symbol lookup. I think that's acceptable for now. In the future we may want to try to get a better upstream kallsyms interface.
This commit is contained in:
parent
6aee215b04
commit
0ebbed244e
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user