From 51b0321943a0833b3febf528fc01a4464e8739f4 Mon Sep 17 00:00:00 2001 From: Benjamin Donnachie Date: Sat, 26 Sep 2026 20:08:31 +0100 Subject: [PATCH] create-diff-object: register patchable child functions Compiler-generated child functions such as *.part.* can have their own profiling call. In that case kpatch_compare_sections() deliberately leaves an unchanged parent unmodified because the child can be patched independently. However, kpatch_create_patches_sections() unconditionally skips every symbol with a parent. The replacement child text and relocations are retained in the module, but no .kpatch.funcs record is emitted, so the kernel never registers or redirects the changed function. Use one predicate for change reporting and patch metadata generation. Register a changed child when it has its own profiling entry and no changed ancestor already carries its replacement code. Continue excluding unpatchable children and children covered by a changed ancestor. Fixes: af1fe267c5c2 ("create-diff-object: Avoid unnecessary parent symbol inclusion") Signed-off-by: Benjamin Donnachie Co-authored-by: OpenAI Codex --- kpatch-build/create-diff-object.c | 36 +++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 3d01e71b..4b811f44 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -2175,13 +2175,39 @@ static int kpatch_include_changed_functions(struct kpatch_elf *kelf) return changed_nr; } +/* + * A compiler-generated child function is independently patchable when it has + * its own profiling call and no changed ancestor already redirects execution + * to the replacement child. Children without a profiling call are carried by + * their changed parent instead. + */ +static bool kpatch_is_patch_func(struct symbol *sym) +{ + struct symbol *parent; + + if (sym->type != STT_FUNC || sym->status != CHANGED) + return false; + + if (!sym->parent) + return true; + + if (!sym->has_func_profiling) + return false; + + for (parent = sym->parent; parent; parent = parent->parent) + if (parent->status == CHANGED) + return false; + + return true; +} + static void kpatch_print_changes(struct kpatch_elf *kelf) { struct symbol *sym; list_for_each_entry(sym, &kelf->symbols, list) { - if (!sym->include || !sym->sec || sym->type != STT_FUNC || - sym->parent || sym->is_pfx) + if (!sym->include || !sym->sec || !kpatch_is_patch_func(sym) || + sym->is_pfx) continue; if (sym->status == NEW) log_normal("new function: %s\n", sym->name); @@ -3452,8 +3478,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf, /* count patched functions */ nr = 0; list_for_each_entry(sym, &kelf->symbols, list) { - if (sym->type != STT_FUNC || sym->status != CHANGED || - sym->parent) + if (!kpatch_is_patch_func(sym)) continue; nr++; } @@ -3474,8 +3499,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf, /* populate sections */ index = 0; list_for_each_entry(sym, &kelf->symbols, list) { - if (sym->type != STT_FUNC || sym->status != CHANGED || - sym->parent) + if (!kpatch_is_patch_func(sym)) continue; if (!lookup_symbol(table, sym, &symbol))