@@ -13,7 +13,8 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
}
struct reloc *arch_find_switch_table(struct objtool_file *file,
- struct instruction *insn)
+ struct instruction *insn,
+ struct instruction *orig_insn)
{
exit(-1);
}
@@ -86,7 +86,8 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
* NOTE: RETPOLINE made it harder still to decode dynamic jumps.
*/
struct reloc *arch_find_switch_table(struct objtool_file *file,
- struct instruction *insn)
+ struct instruction *insn,
+ struct instruction *orig_insn)
{
struct reloc *text_reloc, *rodata_reloc;
struct section *table_sec;
@@ -80,8 +80,8 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
return find_insn(file, func->cfunc->sec, func->cfunc->offset);
}
-static struct instruction *prev_insn_same_sec(struct objtool_file *file,
- struct instruction *insn)
+struct instruction *prev_insn_same_sec(struct objtool_file *file,
+ struct instruction *insn)
{
if (insn->idx == 0) {
if (insn->prev_len)
@@ -2064,7 +2064,8 @@ static struct reloc *find_jump_table(struct objtool_file *file,
insn && insn_func(insn) && insn_func(insn)->pfunc == func;
insn = insn->first_jump_src ?: prev_insn_same_sym(file, insn)) {
- if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
+ if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC &&
+ insn->gpr == orig_insn->gpr)
break;
/* allow small jumps within the range */
@@ -2074,7 +2075,7 @@ static struct reloc *find_jump_table(struct objtool_file *file,
insn->jump_dest->offset > orig_insn->offset))
break;
- table_reloc = arch_find_switch_table(file, insn);
+ table_reloc = arch_find_switch_table(file, insn, orig_insn);
if (!table_reloc)
continue;
@@ -63,8 +63,9 @@ struct instruction {
noendbr : 1,
unret : 1,
visited : 4,
- no_reloc : 1;
- /* 10 bit hole */
+ no_reloc : 1,
+ gpr : 5;
+ /* 5 bit hole */
struct alt_group *alt_group;
struct instruction *jump_dest;
@@ -115,6 +116,7 @@ struct instruction *find_insn(struct objtool_file *file,
struct section *sec, unsigned long offset);
struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *insn);
+struct instruction *prev_insn_same_sec(struct objtool_file *file, struct instruction *insn);
#define sec_for_each_insn(file, _sec, insn) \
for (insn = find_insn(file, _sec, 0); \
@@ -38,5 +38,6 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
struct instruction *insn,
struct reloc *reloc);
struct reloc *arch_find_switch_table(struct objtool_file *file,
- struct instruction *insn);
+ struct instruction *insn,
+ struct instruction *orig_insn);
#endif /* _SPECIAL_H */
A function can contain nested switch tables using different registers as base address. In order to avoid failure in tracking those switch tables, the register containing the base address needs to be taken into account. To do so, add a 5 bits field in struct instruction that will hold the ID of the register containing the base address of the switch table and take that register into account during the backward search in order to not stop the walk when encountering a jump related to another switch table. On architectures not handling it, the ID stays nul and has no impact on the search. To enable that, also provide to arch_find_switch_table() the dynamic instruction related to a table search. Also allow prev_insn_same_sec() to be used outside check.c so that architectures can backward walk through instruction to find out which register is used as base address for a switch table. Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu> --- tools/objtool/arch/powerpc/special.c | 3 ++- tools/objtool/arch/x86/special.c | 3 ++- tools/objtool/check.c | 9 +++++---- tools/objtool/include/objtool/check.h | 6 ++++-- tools/objtool/include/objtool/special.h | 3 ++- 5 files changed, 15 insertions(+), 9 deletions(-)