At the time being, the end of a switch table can only be known once the start of the following switch table has ben located.
This is a problem when switch tables are nested because until the first switch table is properly added, the second one cannot be located as a the backward walk will abut on the dynamic switch of the previous one. So perform a first forward walk in the code in order to locate all possible relocations to switch tables and build a local table with those relocations. Later on once one switch table is found, go through this local table to know where next switch table starts. Signed-off-by: Christophe Leroy <christophe.le...@csgroup.eu> --- tools/objtool/check.c | 63 ++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 72b977f81dd6..0ad2bdd92232 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -2058,14 +2058,30 @@ static void find_jump_table(struct objtool_file *file, struct symbol *func, } } +static struct reloc *find_next_table(struct instruction *insn, + struct reloc **table, unsigned int size) +{ + unsigned long offset = reloc_offset(insn_jump_table(insn)); + int i; + struct reloc *reloc = NULL; + + for (i = 0; i < size; i++) { + if (reloc_offset(table[i]) > offset && + (!reloc || reloc_offset(table[i]) < reloc_offset(reloc))) + reloc = table[i]; + } + return reloc; +} + /* * First pass: Mark the head of each jump table so that in the next pass, * we know when a given jump table ends and the next one starts. */ static int mark_add_func_jump_tables(struct objtool_file *file, - struct symbol *func) + struct symbol *func, + struct reloc **table, unsigned int size) { - struct instruction *insn, *last = NULL, *insn_t1 = NULL, *insn_t2; + struct instruction *insn, *last = NULL; int ret = 0; func_for_each_insn(file, func, insn) { @@ -2094,23 +2110,11 @@ static int mark_add_func_jump_tables(struct objtool_file *file, if (!insn_jump_table(insn)) continue; - if (!insn_t1) { - insn_t1 = insn; - continue; - } - - insn_t2 = insn; - - ret = add_jump_table(file, insn_t1, insn_jump_table(insn_t2)); + ret = add_jump_table(file, insn, find_next_table(insn, table, size)); if (ret) return ret; - - insn_t1 = insn_t2; } - if (insn_t1) - ret = add_jump_table(file, insn_t1, NULL); - return ret; } @@ -2123,15 +2127,42 @@ static int add_jump_table_alts(struct objtool_file *file) { struct symbol *func; int ret; + struct instruction *insn; + unsigned int size = 0, i = 0; + struct reloc **table = NULL; if (!file->rodata) return 0; + for_each_insn(file, insn) { + struct instruction *dest_insn; + struct reloc *reloc; + unsigned long table_size; + + func = insn_func(insn) ? insn_func(insn)->pfunc : NULL; + reloc = arch_find_switch_table(file, insn, &table_size, NULL); + /* + * Each table entry has a rela associated with it. The rela + * should reference text in the same function as the original + * instruction. + */ + if (!reloc) + continue; + dest_insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); + if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func) + continue; + if (i == size) { + size += 1024; + table = realloc(table, size * sizeof(*table)); + } + table[i++] = reloc; + } + for_each_sym(file, func) { if (func->type != STT_FUNC) continue; - ret = mark_add_func_jump_tables(file, func); + ret = mark_add_func_jump_tables(file, func, table, i); if (ret) return ret; } -- 2.47.0