From b0f93dfaf020622dc3d13f77fe095bdae6c01a64 Mon Sep 17 00:00:00 2001 From: Sumanth Korikkar Date: Tue, 10 Feb 2026 05:18:17 +0100 Subject: [PATCH] create-diff-object: Remove undefined function symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building shadow-pid.patch on a debug kernel, it generates __bug_table, which contains an array of struct bug_entries. .rela__bug_table contains references to bug address, line number and column. create-diff-object identifies that .text.kernel_clone has changed and it includes .rela.text.kernel_clone rela section. Then later, it includes all symbols (in kpatch_include_symbols()) associated with it, which ends up including __bug_table and its rela section .rela__bug_table. Then, all the function symbols associated with .rela__bug_table is included irrespective of whether it's section is included or not. This leads to the following modpost errors: kernel/fork.o: changed function: kernel_clone kernel/exit.o: changed function: do_exit fs/proc/array.o: changed function: proc_pid_status make -C /root/linux M=/root/.kpatch/tmp/patch CFLAGS_MODULE='' make[1]: Entering directory '/root/linux' make[2]: Entering directory '/root/.kpatch/tmp/patch' LDS kpatch.lds CC [M] patch-hook.o LD [M] test-shadow-newpid.o MODPOST Module.symvers WARNING: modpost: missing MODULE_DESCRIPTION() in test-shadow-newpid.o ERROR: modpost: "replace_mm_exe_file" [test-shadow-newpid.ko] undefined! ERROR: modpost: "put_task_stack" [test-shadow-newpid.ko] undefined! ERROR: modpost: "release_task" [test-shadow-newpid.ko] undefined! ERROR: modpost: "set_mm_exe_file" [test-shadow-newpid.ko] undefined! Examining the /root/.kpatch/patch/ directory reveals, these symbols are never referenced in any relas. readelf -Ws output.o | grep -E 'put_task_stack|replace_mm_exe_file|release_task|set_mm_exe_file' 27: 0000000000000000 0 SECTION LOCAL DEFAULT 36 .rodata.release_task.str1.2 45: 0000000000000000 0 SECTION LOCAL DEFAULT 55 .rodata.set_mm_exe_file.str1.2 47: 0000000000000000 0 SECTION LOCAL DEFAULT 57 .rodata.replace_mm_exe_file.str1.2 234: 0000000000000000 0 FUNC GLOBAL DEFAULT UND replace_mm_exe_file 254: 0000000000000000 0 FUNC GLOBAL DEFAULT UND put_task_stack 263: 0000000000000000 0 FUNC GLOBAL DEFAULT UND release_task 269: 0000000000000000 0 FUNC GLOBAL DEFAULT UND set_mm_exe_file readelf -Wr output.o | grep -E 'put_task_stack|replace_mm_exe_file|release_task|set_mm_exe_file' Hence, exclude these unreferenced symbols to avoid modpost errors. Fix: * Identify all function symbols present in __bug_table and track their symbol indices. * Exclude .rela__bug_table and .rela__mcount_loc, and for all other relocation sections, check whether any of these symbol indices are actually referenced. * If a symbol index is never referenced in any relevant relocation section and the symbol’s section is not included in the patch, exclude the symbol from being added. Note: Skipped need_klp_reloc()/kpatch_create_intermediate_sections() check for .rela__bug_table section. Reason: The function symbols that were not referenced by any sections other than .rela__bug_table were being initialized with include = 0 (via rela->sym->include = 0). As a result, kpatch_migrate_included_elements() did not migrate these function symbols into kelf_out. However, later in kpatch_create_intermediate_sections(), when parsing the .rela__bug_table relasec and evaluating each symbol in need_klp_reloc(), the code ended up using the previous rela->sym reference (which had already been torn down). Since that symbol had its include field set to 0, the dereference led to a segmentation fault. To prevent this, the .rela__bug_table section is excluded from consideration in kpatch_migrate_included_elements(). Additionally, if a function is modified, the assumption is that, it will be referenced by other relasec. Signed-off-by: Sumanth Korikkar --- kpatch-build/create-diff-object.c | 68 ++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 56ade7ef..6aa5dea2 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -2462,6 +2462,69 @@ static int fixup_group_size(struct kpatch_elf *kelf, int offset) return (int)(rela->addend - offset); } +/* + * Exclude function symbols absent from any rela sections (except + * .rela__bug_table) to eliminate undefined symbols modpost errors. + */ +static void kpatch_exclude_unreferenced_symbols(struct kpatch_elf *kelf) +{ + struct section *sec, *relasec; + unsigned int i = 0, count = 0; + unsigned int *symindex; + struct symbol *sym; + struct rela *rela; + bool found; + + relasec = find_section_by_name(&kelf->sections, ".rela__bug_table"); + if (!relasec) + return; + list_for_each_entry(rela, &relasec->relas, list) { + if (rela->sym->type == STT_FUNC && rela->sym->sec && + !rela->sym->sec->include) + count++; + } + symindex = (unsigned int *)malloc(sizeof(unsigned int) * count); + if (!symindex) + ERROR("malloc"); + list_for_each_entry(rela, &relasec->relas, list) { + if (rela->sym->type == STT_FUNC && rela->sym->sec && + !rela->sym->sec->include) + symindex[i++] = rela->sym->index; + } + for (i = 0; i < count; i++) { + found = false; + list_for_each_entry(sec, &kelf->sections, list) { + if (!is_rela_section(sec) || + is_debug_section(sec)) + continue; + if (!strcmp(sec->name, relasec->name) || + !strcmp(sec->name, ".rela__mcount_loc") || + !sec->include) + continue; + list_for_each_entry(rela, &sec->relas, list) { + if (symindex[i] == rela->sym->index) { + found = true; + break; + } + } + } + if (!found) { + /* + * Function symbol is present in only __bug_table. + * i.e. function symbol was included earlier, but its + * associated section is not included and not + * referenced in any relas. Exclude the function symbol + * to eliminate UND symbols modpost errors. + */ + sym = find_symbol_by_index(&kelf->symbols, symindex[i]); + if (!sym) + ERROR("could not find function symbol\n"); + sym->include = 0; + } + } + free(symindex); +} + static bool jump_table_group_filter(struct lookup_table *lookup, struct section *relasec, unsigned int group_offset, @@ -3684,10 +3747,10 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, list_for_each_entry(relasec, &kelf->sections, list) { if (!is_rela_section(relasec)) continue; - if (!strcmp(relasec->name, ".rela.kpatch.funcs")) + if (!strcmp(relasec->name, ".rela.kpatch.funcs") || + !strcmp(relasec->name, ".rela__bug_table")) continue; list_for_each_entry(rela, &relasec->relas, list) { - /* upper bound on number of kpatch relas and symbols */ nr++; @@ -4499,6 +4562,7 @@ int main(int argc, char *argv[]) kpatch_include_force_elements(kelf_patched); new_globals_exist = kpatch_include_new_globals(kelf_patched); kpatch_include_debug_sections(kelf_patched); + kpatch_exclude_unreferenced_symbols(kelf_patched); kpatch_process_special_sections(kelf_patched, lookup);