From b64ab2b5e4bf1b89843721c8e454d5fa06a97ce5 Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Thu, 11 Feb 2016 11:27:12 -0600 Subject: [PATCH] livepatch-patch-hook: add support for livepatch sympos Support patching objects that have duplicated function names. This feature was introduced upstream in Linux v4.5. This patch appends the symbol position to the symbol structure when lookup_local_symbol is called. This pos variable is then used when creating the funcs and dynrelas sections. Finally, incorporate sympos into the livepatch patch hook only if the kernel version is greater than v4.5. In other cases the older format is used. Fixes: #493 Signed-off-by: Chris J Arges --- kmod/patch/kpatch-patch.h | 2 ++ kmod/patch/livepatch-patch-hook.c | 9 +++++++++ kpatch-build/create-diff-object.c | 2 ++ kpatch-build/lookup.c | 31 ++++++++++++++++++++++--------- kpatch-build/lookup.h | 1 + 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/kmod/patch/kpatch-patch.h b/kmod/patch/kpatch-patch.h index 953ac5e..d0d6aeb 100644 --- a/kmod/patch/kpatch-patch.h +++ b/kmod/patch/kpatch-patch.h @@ -27,6 +27,7 @@ struct kpatch_patch_func { unsigned long new_size; unsigned long old_addr; unsigned long old_size; + unsigned long sympos; char *name; char *objname; }; @@ -35,6 +36,7 @@ struct kpatch_patch_dynrela { unsigned long dest; unsigned long src; unsigned long type; + unsigned long sympos; char *name; char *objname; int external; diff --git a/kmod/patch/livepatch-patch-hook.c b/kmod/patch/livepatch-patch-hook.c index 34d7fd2..327ec93 100644 --- a/kmod/patch/livepatch-patch-hook.c +++ b/kmod/patch/livepatch-patch-hook.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -237,7 +238,11 @@ static int __init patch_init(void) lfunc = &lfuncs[j]; lfunc->old_name = func->kfunc->name; lfunc->new_func = (void *)func->kfunc->new_addr; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + lfunc->old_sympos = func->kfunc->sympos; +#else lfunc->old_addr = func->kfunc->old_addr; +#endif j++; } @@ -250,7 +255,11 @@ static int __init patch_init(void) list_for_each_entry(reloc, &object->relocs, list) { lreloc = &lrelocs[j]; lreloc->loc = reloc->kdynrela->dest; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + lreloc->sympos = reloc->kdynrela->sympos; +#else lreloc->val = reloc->kdynrela->src; +#endif lreloc->type = reloc->kdynrela->type; lreloc->name = reloc->kdynrela->name; lreloc->addend = reloc->kdynrela->addend; diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 93ed193..34a8ee2 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -2539,6 +2539,7 @@ void kpatch_create_patches_sections(struct kpatch_elf *kelf, funcs[index].old_addr = result.value; funcs[index].old_size = result.size; funcs[index].new_size = sym->sym.st_size; + funcs[index].sympos = result.pos; /* * Add a relocation that will populate @@ -2715,6 +2716,7 @@ void kpatch_create_dynamic_rela_sections(struct kpatch_elf *kelf, dynrelas[index].addend = rela->addend; dynrelas[index].type = rela->type; dynrelas[index].external = external; + dynrelas[index].sympos = result.pos; /* add rela to fill in dest field */ ALLOC_LINK(dynrela, &relasec->relas); diff --git a/kpatch-build/lookup.c b/kpatch-build/lookup.c index 5a43dd1..b411326 100644 --- a/kpatch-build/lookup.c +++ b/kpatch-build/lookup.c @@ -148,6 +148,7 @@ int lookup_local_symbol(struct lookup_table *table, char *name, char *hint, { struct symbol *sym, *match = NULL; int i; + unsigned long pos = 0; char *curfile = NULL; memset(result, 0, sizeof(*result)); @@ -159,19 +160,30 @@ int lookup_local_symbol(struct lookup_table *table, char *name, char *hint, } else if (curfile) curfile = NULL; /* end hint file symbols */ } - if (!curfile) - continue; - if (sym->bind == STB_LOCAL && !strcmp(sym->name, name)) { - if (match) - /* dup file+symbol, unresolvable ambiguity */ - return 1; - match = sym; + if (sym->bind == STB_LOCAL) { + if (sym->name && !strcmp(sym->name, name)) { + /* + * need to count any occurrence of the symbol + * name, unless we've already found a match + */ + if (!match) + pos++; + + if (!curfile) + continue; + + if (match) + /* dup file+symbol, unresolvable ambiguity */ + return 1; + match = sym; + } } } if (!match) return 1; + result->pos = pos; result->value = match->value; result->size = match->size; return 0; @@ -189,6 +201,7 @@ int lookup_global_symbol(struct lookup_table *table, char *name, !strcmp(sym->name, name)) { result->value = sym->value; result->size = sym->size; + result->pos = 0; /* always 0 for global symbols */ return 0; } @@ -220,9 +233,9 @@ static void find_this(struct lookup_table *table, char *sym, char *hint) else lookup_global_symbol(table, sym, &result); - printf("%s %s w/ %s hint at 0x%016lx len %lu\n", + printf("%s %s w/ %s hint at 0x%016lx len %lu pos %lu\n", hint ? "local" : "global", sym, hint ? hint : "no", - result.value, result.size); + result.value, result.size, result.pos); } int main(int argc, char **argv) diff --git a/kpatch-build/lookup.h b/kpatch-build/lookup.h index cbb3dae..90b7767 100644 --- a/kpatch-build/lookup.h +++ b/kpatch-build/lookup.h @@ -6,6 +6,7 @@ struct lookup_table; struct lookup_result { unsigned long value; unsigned long size; + unsigned long pos; }; struct lookup_table *lookup_open(char *path);