diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index e57fab9..96a92c0 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -926,6 +926,10 @@ static void __kpatch_correlate_section(struct section *sec1, struct section *sec static void kpatch_correlate_symbol(struct symbol *sym1, struct symbol *sym2) { CORRELATE_ELEMENT(sym1, sym2, "symbol"); + if (sym1->lookup_table_file_sym && !sym2->lookup_table_file_sym) + sym2->lookup_table_file_sym = sym1->lookup_table_file_sym; + else if (!sym1->lookup_table_file_sym && sym2->lookup_table_file_sym) + sym1->lookup_table_file_sym = sym2->lookup_table_file_sym; } static void kpatch_correlate_static_local(struct symbol *sym1, struct symbol *sym2) @@ -2858,43 +2862,6 @@ static void kpatch_process_special_sections(struct kpatch_elf *kelf, kpatch_regenerate_orc_sections(kelf); } -static struct sym_compare_type *kpatch_elf_locals(struct kpatch_elf *kelf) -{ - struct symbol *sym; - int i = 0, sym_num = 0; - struct sym_compare_type *sym_array; - - list_for_each_entry(sym, &kelf->symbols, list) { - if (sym->bind != STB_LOCAL) - continue; - if (sym->type != STT_FUNC && sym->type != STT_OBJECT) - continue; - - sym_num++; - } - - if (!sym_num) - return NULL; - - sym_array = malloc((sym_num + 1) * sizeof(struct sym_compare_type)); - if (!sym_array) - ERROR("malloc"); - - list_for_each_entry(sym, &kelf->symbols, list) { - if (sym->bind != STB_LOCAL) - continue; - if (sym->type != STT_FUNC && sym->type != STT_OBJECT) - continue; - - sym_array[i].type = sym->type; - sym_array[i++].name = strdup(sym->name); - } - sym_array[i].type = 0; - sym_array[i].name = NULL; - - return sym_array; -} - static void kpatch_create_patches_sections(struct kpatch_elf *kelf, struct lookup_table *table, char *objname) @@ -3724,10 +3691,8 @@ int main(int argc, char *argv[]) int num_changed, callbacks_exist, new_globals_exist; struct lookup_table *lookup; struct section *sec, *symtab; - struct symbol *sym; - char *hint = NULL, *orig_obj, *patched_obj, *parent_name; + char *orig_obj, *patched_obj, *parent_name; char *parent_symtab, *mod_symvers, *patch_name, *output_obj; - struct sym_compare_type *base_locals, *sym_comp; memset(&arguments, 0, sizeof(arguments)); argp_parse (&argp, argc, argv, 0, NULL, &arguments); @@ -3761,21 +3726,7 @@ int main(int argc, char *argv[]) kpatch_detect_child_functions(kelf_base); kpatch_detect_child_functions(kelf_patched); - list_for_each_entry(sym, &kelf_base->symbols, list) { - if (sym->type == STT_FILE) { - hint = strdup(sym->name); - break; - } - } - if (!hint) { - log_normal("WARNING: FILE symbol not found in base. Stripped object file or assembly source?\n"); - return EXIT_STATUS_NO_CHANGE; - } - - base_locals = kpatch_elf_locals(kelf_base); - - lookup = lookup_open(parent_symtab, parent_name, mod_symvers, hint, - base_locals); + lookup = lookup_open(parent_symtab, parent_name, mod_symvers, kelf_base); kpatch_mark_grouped_sections(kelf_patched); kpatch_replace_sections_syms(kelf_base); @@ -3816,7 +3767,6 @@ int main(int argc, char *argv[]) log_debug("no changed functions were found, but callbacks exist\n"); else { log_debug("no changed functions were found\n"); - free(hint); return EXIT_STATUS_NO_CHANGE; } } @@ -3832,11 +3782,6 @@ int main(int argc, char *argv[]) */ kpatch_elf_teardown(kelf_patched); - for (sym_comp = base_locals; sym_comp && sym_comp->name; sym_comp++) - free(sym_comp->name); - free(base_locals); - free(hint); - kpatch_no_sibling_calls_ppc64le(kelf_out); /* create strings, patches, and dynrelas sections */ diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index 36f500a..244866b 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -76,6 +76,7 @@ struct symbol { struct section *sec; GElf_Sym sym; char *name; + struct object_symbol *lookup_table_file_sym; unsigned int index; unsigned char bind, type; enum status status; diff --git a/kpatch-build/lookup.c b/kpatch-build/lookup.c index d9a3560..49e1561 100644 --- a/kpatch-build/lookup.c +++ b/kpatch-build/lookup.c @@ -56,7 +56,6 @@ struct lookup_table { int obj_nr, exp_nr; struct object_symbol *obj_syms; struct export_symbol *exp_syms; - struct object_symbol *local_syms; char *objname; }; @@ -92,25 +91,31 @@ static int maybe_discarded_sym(const char *name) } static int locals_match(struct lookup_table *table, int idx, - struct sym_compare_type *child_locals) + struct symbol *file_sym, struct list_head *sym_list) { - struct sym_compare_type *child; - struct object_symbol *sym; + struct symbol *sym; + struct object_symbol *table_sym; int i, found; i = idx + 1; - for_each_obj_symbol_continue(i, sym, table) { - if (sym->type == STT_FILE) + for_each_obj_symbol_continue(i, table_sym, table) { + if (table_sym->type == STT_FILE) break; - if (sym->bind != STB_LOCAL) + if (table_sym->bind != STB_LOCAL) continue; - if (sym->type != STT_FUNC && sym->type != STT_OBJECT) + if (table_sym->type != STT_FUNC && table_sym->type != STT_OBJECT) continue; found = 0; - for (child = child_locals; child->name; child++) { - if (child->type == sym->type && - !strcmp(child->name, sym->name)) { + sym = file_sym; + list_for_each_entry_continue(sym, sym_list, list) { + if (sym->type == STT_FILE) + break; + if (sym->bind != STB_LOCAL) + continue; + + if (sym->type == table_sym->type && + !strcmp(sym->name, table_sym->name)) { found = 1; break; } @@ -120,27 +125,33 @@ static int locals_match(struct lookup_table *table, int idx, return 0; } - for (child = child_locals; child->name; child++) { + sym = file_sym; + list_for_each_entry_continue(sym, sym_list, list) { + if (sym->type == STT_FILE) + break; + if (sym->bind != STB_LOCAL) + continue; + if (sym->type != STT_FUNC && table_sym->type != STT_OBJECT) + continue; /* * Symbols which get discarded at link time are missing from * the lookup table, so skip them. */ - if (maybe_discarded_sym(child->name)) + if (maybe_discarded_sym(sym->name)) continue; found = 0; i = idx + 1; - for_each_obj_symbol_continue(i, sym, table) { - if (sym->type == STT_FILE) + for_each_obj_symbol_continue(i, table_sym, table) { + if (table_sym->type == STT_FILE) break; - if (sym->bind != STB_LOCAL) + if (table_sym->bind != STB_LOCAL) continue; - if (sym->type != STT_FUNC && sym->type != STT_OBJECT) - continue; - if (maybe_discarded_sym(sym->name)) + if (maybe_discarded_sym(table_sym->name)) continue; - if (!strcmp(child->name, sym->name)) { + if (sym->type == table_sym->type && + !strcmp(sym->name, table_sym->name)) { found = 1; break; } @@ -153,32 +164,56 @@ static int locals_match(struct lookup_table *table, int idx, return 1; } -static void find_local_syms(struct lookup_table *table, char *hint, - struct sym_compare_type *child_locals) +static void find_local_syms(struct lookup_table *table, struct symbol *file_sym, + struct list_head *sym_list) { struct object_symbol *sym; + struct object_symbol *lookup_table_file_sym = NULL; int i; - if (!child_locals) - return; - for_each_obj_symbol(i, sym, table) { if (sym->type != STT_FILE) continue; - if (strcmp(hint, sym->name)) + if (strcmp(file_sym->name, sym->name)) continue; - if (!locals_match(table, i, child_locals)) + if (!locals_match(table, i, file_sym, sym_list)) continue; - if (table->local_syms) + if (lookup_table_file_sym) ERROR("found duplicate matches for %s local symbols in %s symbol table", - hint, table->objname); + file_sym->name, table->objname); - table->local_syms = sym; + lookup_table_file_sym = sym; } - if (!table->local_syms) + if (!lookup_table_file_sym) ERROR("couldn't find matching %s local symbols in %s symbol table", - hint, table->objname); + file_sym->name, table->objname); + + list_for_each_entry_continue(file_sym, sym_list, list) { + if (file_sym->type == STT_FILE) + break; + file_sym->lookup_table_file_sym = lookup_table_file_sym; + } +} + +/* + * Because there can be duplicate symbols and duplicate filenames we need to + * correlate each symbol from the elf file to it's corresponding symbol in + * lookup table. Both the elf file and the lookup table can be split on + * STT_FILE symbols into blocks of symbols originating from a single source + * file. We then compare local symbol lists from both blocks and store the + * pointer to STT_FILE symbol in lookup table for later use in + * lookup_local_symbol(). + */ +static void find_local_syms_multiple(struct lookup_table *table, + struct kpatch_elf *kelf) +{ + struct symbol *sym; + + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type == STT_FILE) + find_local_syms(table, sym, &kelf->symbols); + } } /* Strip the path and replace '-' with '_' */ @@ -371,8 +406,7 @@ static void symvers_read(struct lookup_table *table, char *path) } struct lookup_table *lookup_open(char *symtab_path, char *objname, - char *symvers_path, char *hint, - struct sym_compare_type *locals) + char *symvers_path, struct kpatch_elf *kelf) { struct lookup_table *table; @@ -384,7 +418,8 @@ struct lookup_table *lookup_open(char *symtab_path, char *objname, table->objname = objname; symtab_read(table, symtab_path); symvers_read(table, symvers_path); - find_local_syms(table, hint, locals); + + find_local_syms_multiple(table, kelf); return table; } @@ -415,16 +450,13 @@ static bool lookup_local_symbol(struct lookup_table *table, unsigned long sympos = 0; int i, in_file = 0; - if (!table->local_syms) - return false; - memset(result, 0, sizeof(*result)); for_each_obj_symbol(i, sym, table) { if (sym->bind == STB_LOCAL && !strcmp(sym->name, lookup_sym->name)) sympos++; - if (table->local_syms == sym) { + if (lookup_sym->lookup_table_file_sym == sym) { in_file = 1; continue; } @@ -437,7 +469,6 @@ static bool lookup_local_symbol(struct lookup_table *table, if (sym->bind == STB_LOCAL && !strcmp(sym->name, lookup_sym->name)) { - if (result->objname) ERROR("duplicate local symbol found for %s", lookup_sym->name); diff --git a/kpatch-build/lookup.h b/kpatch-build/lookup.h index ae92880..e1277f1 100644 --- a/kpatch-build/lookup.h +++ b/kpatch-build/lookup.h @@ -14,14 +14,8 @@ struct lookup_result { bool global, exported; }; -struct sym_compare_type { - char *name; - int type; -}; - struct lookup_table *lookup_open(char *symtab_path, char *objname, - char *symvers_path, char *hint, - struct sym_compare_type *locals); + char *symvers_path, struct kpatch_elf *kelf); void lookup_close(struct lookup_table *table); bool lookup_symbol(struct lookup_table *table, struct symbol *sym, struct lookup_result *result);