|
@@ -851,8 +851,14 @@ static int add_switch_table(struct objtool_file *file, struct symbol *func,
|
|
* This is a fairly uncommon pattern which is new for GCC 6. As of this
|
|
* This is a fairly uncommon pattern which is new for GCC 6. As of this
|
|
* writing, there are 11 occurrences of it in the allmodconfig kernel.
|
|
* writing, there are 11 occurrences of it in the allmodconfig kernel.
|
|
*
|
|
*
|
|
|
|
+ * As of GCC 7 there are quite a few more of these and the 'in between' code
|
|
|
|
+ * is significant. Esp. with KASAN enabled some of the code between the mov
|
|
|
|
+ * and jmpq uses .rodata itself, which can confuse things.
|
|
|
|
+ *
|
|
* TODO: Once we have DWARF CFI and smarter instruction decoding logic,
|
|
* TODO: Once we have DWARF CFI and smarter instruction decoding logic,
|
|
* ensure the same register is used in the mov and jump instructions.
|
|
* ensure the same register is used in the mov and jump instructions.
|
|
|
|
+ *
|
|
|
|
+ * NOTE: RETPOLINE made it harder still to decode dynamic jumps.
|
|
*/
|
|
*/
|
|
static struct rela *find_switch_table(struct objtool_file *file,
|
|
static struct rela *find_switch_table(struct objtool_file *file,
|
|
struct symbol *func,
|
|
struct symbol *func,
|
|
@@ -874,12 +880,25 @@ static struct rela *find_switch_table(struct objtool_file *file,
|
|
text_rela->addend + 4);
|
|
text_rela->addend + 4);
|
|
if (!rodata_rela)
|
|
if (!rodata_rela)
|
|
return NULL;
|
|
return NULL;
|
|
|
|
+
|
|
file->ignore_unreachables = true;
|
|
file->ignore_unreachables = true;
|
|
return rodata_rela;
|
|
return rodata_rela;
|
|
}
|
|
}
|
|
|
|
|
|
/* case 3 */
|
|
/* case 3 */
|
|
- func_for_each_insn_continue_reverse(file, func, insn) {
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Backward search using the @first_jump_src links, these help avoid
|
|
|
|
+ * much of the 'in between' code. Which avoids us getting confused by
|
|
|
|
+ * it.
|
|
|
|
+ */
|
|
|
|
+ for (insn = list_prev_entry(insn, list);
|
|
|
|
+
|
|
|
|
+ &insn->list != &file->insn_list &&
|
|
|
|
+ insn->sec == func->sec &&
|
|
|
|
+ insn->offset >= func->offset;
|
|
|
|
+
|
|
|
|
+ insn = insn->first_jump_src ?: list_prev_entry(insn, list)) {
|
|
|
|
+
|
|
if (insn->type == INSN_JUMP_DYNAMIC)
|
|
if (insn->type == INSN_JUMP_DYNAMIC)
|
|
break;
|
|
break;
|
|
|
|
|
|
@@ -909,14 +928,32 @@ static struct rela *find_switch_table(struct objtool_file *file,
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
static int add_func_switch_tables(struct objtool_file *file,
|
|
static int add_func_switch_tables(struct objtool_file *file,
|
|
struct symbol *func)
|
|
struct symbol *func)
|
|
{
|
|
{
|
|
- struct instruction *insn, *prev_jump = NULL;
|
|
|
|
|
|
+ struct instruction *insn, *last = NULL, *prev_jump = NULL;
|
|
struct rela *rela, *prev_rela = NULL;
|
|
struct rela *rela, *prev_rela = NULL;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
func_for_each_insn(file, func, insn) {
|
|
func_for_each_insn(file, func, insn) {
|
|
|
|
+ if (!last)
|
|
|
|
+ last = insn;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Store back-pointers for unconditional forward jumps such
|
|
|
|
+ * that find_switch_table() can back-track using those and
|
|
|
|
+ * avoid some potentially confusing code.
|
|
|
|
+ */
|
|
|
|
+ if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
|
|
|
|
+ insn->offset > last->offset &&
|
|
|
|
+ insn->jump_dest->offset > insn->offset &&
|
|
|
|
+ !insn->jump_dest->first_jump_src) {
|
|
|
|
+
|
|
|
|
+ insn->jump_dest->first_jump_src = insn;
|
|
|
|
+ last = insn->jump_dest;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (insn->type != INSN_JUMP_DYNAMIC)
|
|
if (insn->type != INSN_JUMP_DYNAMIC)
|
|
continue;
|
|
continue;
|
|
|
|
|