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


Reply via email to