From b502e5b1cc29ef9ce23d0a93d8e212de8b82d51c Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Mon, 16 Sep 2019 11:57:05 +0100 Subject: [PATCH] kpatch-build: Allow function to have multiple child functions A symbol associated to a function can be split into multiple sub-functions. Currently, kpatch only supports one child per function. Extend this to support an arbitrary number of sub-function per function. Signed-off-by: Julien Thierry --- kpatch-build/create-diff-object.c | 46 +++++++++++++++++++++++++------ kpatch-build/kpatch-elf.c | 2 ++ kpatch-build/kpatch-elf.h | 3 +- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 243af93..5f33fd6 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -221,6 +221,7 @@ static void kpatch_detect_child_functions(struct kpatch_elf *kelf) list_for_each_entry(sym, &kelf->symbols, list) { char *coldstr; + struct symbol *parent; coldstr = strstr(sym->name, ".cold."); if (coldstr != NULL) { @@ -230,13 +231,14 @@ static void kpatch_detect_child_functions(struct kpatch_elf *kelf) if (!pname) ERROR("strndup"); - sym->parent = find_symbol_by_name(&kelf->symbols, pname); + parent = find_symbol_by_name(&kelf->symbols, pname); free(pname); - if (!sym->parent) + if (!parent) ERROR("failed to find parent function for %s", sym->name); - sym->parent->child = sym; + list_add_tail(&sym->subfunction_node, &parent->children); + sym->parent = parent; } } } @@ -659,6 +661,26 @@ static int kpatch_line_macro_change_only(struct section *sec) } #endif +/* + * Child functions with "*.cold" names don't have _fentry_ calls, but "*.part", + * often do. In the later case, it is not necessary to include the parent + * in the output object when the child function has changed. + */ +static bool kpatch_changed_child_needs_parent_profiling(struct symbol *sym) +{ + struct symbol *child; + + list_for_each_entry(child, &sym->children, subfunction_node) { + if (child->has_func_profiling) + continue; + if (child->sec->status == CHANGED || + kpatch_changed_child_needs_parent_profiling(child)) + return true; + } + + return false; +} + static void kpatch_compare_sections(struct list_head *seclist) { struct section *sec; @@ -691,9 +713,8 @@ static void kpatch_compare_sections(struct list_head *seclist) if (sym && sym->status != CHANGED) sym->status = sec->status; - if (sym && sym->child && sym->status == SAME && - sym->child->sec->status == CHANGED && - !sym->child->has_func_profiling) + if (sym && sym->status == SAME && + kpatch_changed_child_needs_parent_profiling(sym)) sym->status = CHANGED; } } @@ -2364,6 +2385,16 @@ static void kpatch_mark_ignored_sections_same(struct kpatch_elf *kelf) sym->status = SAME; } +static void kpatch_mark_ignored_children_same(struct symbol *sym) +{ + struct symbol *child; + + list_for_each_entry(child, &sym->children, subfunction_node) { + child->status = SAME; + kpatch_mark_ignored_children_same(child); + } +} + static void kpatch_mark_ignored_functions_same(struct kpatch_elf *kelf) { struct section *sec; @@ -2385,8 +2416,7 @@ static void kpatch_mark_ignored_functions_same(struct kpatch_elf *kelf) rela->sym->status = SAME; rela->sym->sec->status = SAME; - if (rela->sym->child) - rela->sym->child->status = SAME; + kpatch_mark_ignored_children_same(rela->sym); if (rela->sym->sec->secsym) rela->sym->sec->secsym->status = SAME; diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index c6af59e..377dc7f 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -277,6 +277,8 @@ void kpatch_create_symbol_list(struct kpatch_elf *kelf) while (symbols_nr--) { ALLOC_LINK(sym, &kelf->symbols); + INIT_LIST_HEAD(&sym->children); + sym->index = index; if (!gelf_getsym(symtab->data, index, &sym->sym)) ERROR("gelf_getsym"); diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index 3a0dc4b..d2bb454 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -71,7 +71,8 @@ struct symbol { struct list_head list; struct symbol *twin; struct symbol *parent; - struct symbol *child; + struct list_head children; + struct list_head subfunction_node; struct section *sec; GElf_Sym sym; char *name;