Switch to per-file lookup table pointers.

So far create-diff-object worked only with objectfiles built from a
single source file. To support object-files built from multiple sources
such as linked vmlinux.o, we call locals_match() for each of STT_FILE
symbols and store the pointer to the beginning of appropriate symbol
block in lookup table in each symbol.

Signed-off-by: Artem Savkov <asavkov@redhat.com>
This commit is contained in:
Artem Savkov 2021-08-02 11:32:23 +02:00
parent db442d1405
commit 720768767d
4 changed files with 79 additions and 108 deletions

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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);