[PATCH v4 09/15] objtool: Find end of switch table directly

From: Christophe Leroy
Date: Tue Jul 11 2023 - 12:09:04 EST


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.leroy@xxxxxxxxxx>
---
tools/objtool/check.c | 62 ++++++++++++++++++++++++++++++++-----------
1 file changed, 46 insertions(+), 16 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index be413c578588..361c832aefc8 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2094,14 +2094,30 @@ static struct reloc *find_jump_table(struct objtool_file *file,
return NULL;
}

+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;
struct reloc *reloc;
int ret = 0;

@@ -2132,23 +2148,11 @@ static int mark_add_func_jump_tables(struct objtool_file *file,
else
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;
}

@@ -2161,15 +2165,41 @@ 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;
+
+ func = insn_func(insn) ? insn_func(insn)->pfunc : NULL;
+ reloc = arch_find_switch_table(file, insn, 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.41.0