Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
From 0bc2bc09d4c23cc5b48cea4bc2fb926a1fa72598 Mon Sep 17 00:00:00 2001
From: Jann Horn <jannh@google.com>
Date: Fri, 12 Apr 2019 16:13:30 +0200
Subject: [PATCH] objtool: Handle multiply-referenced and
out-of-order-referenced jump tables
With clang from git master, code can be generated where a function contains
two indirect jump instructions that use the same switch table. To deal with
this case and similar ones properly, convert the switch table parsing to
use two passes:
In the first pass, locate the heads of all switch tables for the function
and mark the first relocation in each switch table.
In the second pass, parse the switch tables, stopping when encountering a
marked relocation (since that indicates that the start of another switch
table has been reached).
Signed-off-by: Jann Horn <jannh@google.com>
---
tools/objtool/check.c | 50 +++++++++++++++++++++++++------------------
tools/objtool/elf.h | 1 +
2 files changed, 30 insertions(+), 21 deletions(-)
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 5dde107083c6..03a1594094f5 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -828,7 +828,7 @@ static int add_special_section_alts(struct objtool_file *file)
}
static int add_switch_table(struct objtool_file *file, struct instruction *insn,
- struct rela *table, struct rela *next_table)
+ struct rela *table)
{
struct rela *rela = table;
struct instruction *alt_insn;
@@ -836,8 +836,12 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
struct symbol *pfunc = insn->func->pfunc;
unsigned int prev_offset = 0;
+ /*
+ * Each @rela is a switch table entry, establishing the relationship
+ * between the table entry and the target instruction.
+ */
list_for_each_entry_from(rela, &table->rela_sec->rela_list, list) {
- if (rela == next_table)
+ if (rela != table && rela->jump_table_start)
break;
/* Make sure the switch table entries are consecutive: */
@@ -988,12 +992,31 @@ static struct rela *find_switch_table(struct objtool_file *file,
return NULL;
}
+/*
+ * First pass: Mark the head of each switch table so that in the next pass,
+ * we know when a given switch table ends and the next one starts.
+ */
+static void mark_func_switch_tables(struct objtool_file *file,
+ struct symbol *func)
+{
+ struct instruction *insn;
+ struct rela *rela;
+
+ func_for_each_insn_all(file, func, insn) {
+ if (insn->type != INSN_JUMP_DYNAMIC)
+ continue;
+
+ rela = find_switch_table(file, func, insn);
+ if (rela)
+ rela->jump_table_start = true;
+ }
+}
static int add_func_switch_tables(struct objtool_file *file,
struct symbol *func)
{
- struct instruction *insn, *last = NULL, *prev_jump = NULL;
- struct rela *rela, *prev_rela = NULL;
+ struct instruction *insn, *last = NULL;
+ struct rela *rela;
int ret;
func_for_each_insn_all(file, func, insn) {
@@ -1021,23 +1044,7 @@ static int add_func_switch_tables(struct objtool_file *file,
if (!rela)
continue;
- /*
- * We found a switch table, but we don't know yet how big it
- * is. Don't add it until we reach the end of the function or
- * the beginning of another switch table in the same function.
- */
- if (prev_jump) {
- ret = add_switch_table(file, prev_jump, prev_rela, rela);
- if (ret)
- return ret;
- }
-
- prev_jump = insn;
- prev_rela = rela;
- }
-
- if (prev_jump) {
- ret = add_switch_table(file, prev_jump, prev_rela, NULL);
+ ret = add_switch_table(file, insn, rela);
if (ret)
return ret;
}
@@ -1064,6 +1071,7 @@ static int add_switch_table_alts(struct objtool_file *file)
if (func->type != STT_FUNC)
continue;
+ mark_func_switch_tables(file, func);
ret = add_func_switch_tables(file, func);
if (ret)
return ret;
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index bc97ed86b9cd..c7b41e212f79 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -73,6 +73,7 @@ struct rela {
unsigned int type;
unsigned long offset;
int addend;
+ bool jump_table_start;
};
struct elf {
--
2.22.0.410.gd8fdbe21b5-goog
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.