forked from Minki/linux
objtool: Refactor jump table code
Now that C jump tables are supported, call them "jump tables" instead of "switch tables". Also rename some other variables, add comments, and simplify the code flow a bit. Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Tested-by: Nick Desaulniers <ndesaulniers@google.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lkml.kernel.org/r/cf951b0c0641628e0b9b81f7ceccd9bcabcb4bd8.1563413318.git.jpoimboe@redhat.com
This commit is contained in:
parent
0c1ddd3317
commit
e7c2bc37bf
@ -627,7 +627,7 @@ static int add_jump_destinations(struct objtool_file *file)
|
||||
* However this code can't completely replace the
|
||||
* read_symbols() code because this doesn't detect the
|
||||
* case where the parent function's only reference to a
|
||||
* subfunction is through a switch table.
|
||||
* subfunction is through a jump table.
|
||||
*/
|
||||
if (!strstr(insn->func->name, ".cold.") &&
|
||||
strstr(insn->jump_dest->func->name, ".cold.")) {
|
||||
@ -899,20 +899,24 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int add_switch_table(struct objtool_file *file, struct instruction *insn,
|
||||
static int add_jump_table(struct objtool_file *file, struct instruction *insn,
|
||||
struct rela *table, struct rela *next_table)
|
||||
{
|
||||
struct rela *rela = table;
|
||||
struct instruction *alt_insn;
|
||||
struct instruction *dest_insn;
|
||||
struct alternative *alt;
|
||||
struct symbol *pfunc = insn->func->pfunc;
|
||||
unsigned int prev_offset = 0;
|
||||
|
||||
list_for_each_entry_from(rela, &table->rela_sec->rela_list, list) {
|
||||
/*
|
||||
* Each @rela is a switch table relocation which points to the target
|
||||
* instruction.
|
||||
*/
|
||||
list_for_each_entry_from(rela, &table->sec->rela_list, list) {
|
||||
if (rela == next_table)
|
||||
break;
|
||||
|
||||
/* Make sure the switch table entries are consecutive: */
|
||||
/* Make sure the table entries are consecutive: */
|
||||
if (prev_offset && rela->offset != prev_offset + 8)
|
||||
break;
|
||||
|
||||
@ -921,12 +925,12 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
|
||||
rela->addend == pfunc->offset)
|
||||
break;
|
||||
|
||||
alt_insn = find_insn(file, rela->sym->sec, rela->addend);
|
||||
if (!alt_insn)
|
||||
dest_insn = find_insn(file, rela->sym->sec, rela->addend);
|
||||
if (!dest_insn)
|
||||
break;
|
||||
|
||||
/* Make sure the jmp dest is in the function or subfunction: */
|
||||
if (alt_insn->func->pfunc != pfunc)
|
||||
/* Make sure the destination is in the same function: */
|
||||
if (dest_insn->func->pfunc != pfunc)
|
||||
break;
|
||||
|
||||
alt = malloc(sizeof(*alt));
|
||||
@ -935,7 +939,7 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
|
||||
return -1;
|
||||
}
|
||||
|
||||
alt->insn = alt_insn;
|
||||
alt->insn = dest_insn;
|
||||
list_add_tail(&alt->list, &insn->alts);
|
||||
prev_offset = rela->offset;
|
||||
}
|
||||
@ -950,7 +954,7 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
|
||||
}
|
||||
|
||||
/*
|
||||
* find_switch_table() - Given a dynamic jump, find the switch jump table in
|
||||
* find_jump_table() - Given a dynamic jump, find the switch jump table in
|
||||
* .rodata associated with it.
|
||||
*
|
||||
* There are 3 basic patterns:
|
||||
@ -992,13 +996,13 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
|
||||
*
|
||||
* NOTE: RETPOLINE made it harder still to decode dynamic jumps.
|
||||
*/
|
||||
static struct rela *find_switch_table(struct objtool_file *file,
|
||||
static struct rela *find_jump_table(struct objtool_file *file,
|
||||
struct symbol *func,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct rela *text_rela, *rodata_rela;
|
||||
struct rela *text_rela, *table_rela;
|
||||
struct instruction *orig_insn = insn;
|
||||
struct section *rodata_sec;
|
||||
struct section *table_sec;
|
||||
unsigned long table_offset;
|
||||
|
||||
/*
|
||||
@ -1031,7 +1035,7 @@ static struct rela *find_switch_table(struct objtool_file *file,
|
||||
continue;
|
||||
|
||||
table_offset = text_rela->addend;
|
||||
rodata_sec = text_rela->sym->sec;
|
||||
table_sec = text_rela->sym->sec;
|
||||
|
||||
if (text_rela->type == R_X86_64_PC32)
|
||||
table_offset += 4;
|
||||
@ -1045,29 +1049,31 @@ static struct rela *find_switch_table(struct objtool_file *file,
|
||||
* need to be placed in the C_JUMP_TABLE_SECTION section. They
|
||||
* have symbols associated with them.
|
||||
*/
|
||||
if (find_symbol_containing(rodata_sec, table_offset) &&
|
||||
strcmp(rodata_sec->name, C_JUMP_TABLE_SECTION))
|
||||
if (find_symbol_containing(table_sec, table_offset) &&
|
||||
strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
|
||||
continue;
|
||||
|
||||
/* Each table entry has a rela associated with it. */
|
||||
table_rela = find_rela_by_dest(table_sec, table_offset);
|
||||
if (!table_rela)
|
||||
continue;
|
||||
|
||||
rodata_rela = find_rela_by_dest(rodata_sec, table_offset);
|
||||
if (rodata_rela) {
|
||||
/*
|
||||
* Use of RIP-relative switch jumps is quite rare, and
|
||||
* indicates a rare GCC quirk/bug which can leave dead
|
||||
* code behind.
|
||||
* indicates a rare GCC quirk/bug which can leave dead code
|
||||
* behind.
|
||||
*/
|
||||
if (text_rela->type == R_X86_64_PC32)
|
||||
file->ignore_unreachables = true;
|
||||
|
||||
return rodata_rela;
|
||||
}
|
||||
return table_rela;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int add_func_switch_tables(struct objtool_file *file,
|
||||
static int add_func_jump_tables(struct objtool_file *file,
|
||||
struct symbol *func)
|
||||
{
|
||||
struct instruction *insn, *last = NULL, *prev_jump = NULL;
|
||||
@ -1080,7 +1086,7 @@ static int add_func_switch_tables(struct objtool_file *file,
|
||||
|
||||
/*
|
||||
* Store back-pointers for unconditional forward jumps such
|
||||
* that find_switch_table() can back-track using those and
|
||||
* that find_jump_table() can back-track using those and
|
||||
* avoid some potentially confusing code.
|
||||
*/
|
||||
if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
|
||||
@ -1095,17 +1101,17 @@ static int add_func_switch_tables(struct objtool_file *file,
|
||||
if (insn->type != INSN_JUMP_DYNAMIC)
|
||||
continue;
|
||||
|
||||
rela = find_switch_table(file, func, insn);
|
||||
rela = find_jump_table(file, func, insn);
|
||||
if (!rela)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* We found a switch table, but we don't know yet how big it
|
||||
* We found a jump 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.
|
||||
* the beginning of another jump table in the same function.
|
||||
*/
|
||||
if (prev_jump) {
|
||||
ret = add_switch_table(file, prev_jump, prev_rela, rela);
|
||||
ret = add_jump_table(file, prev_jump, prev_rela, rela);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -1115,7 +1121,7 @@ static int add_func_switch_tables(struct objtool_file *file,
|
||||
}
|
||||
|
||||
if (prev_jump) {
|
||||
ret = add_switch_table(file, prev_jump, prev_rela, NULL);
|
||||
ret = add_jump_table(file, prev_jump, prev_rela, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -1128,7 +1134,7 @@ static int add_func_switch_tables(struct objtool_file *file,
|
||||
* section which contains a list of addresses within the function to jump to.
|
||||
* This finds these jump tables and adds them to the insn->alts lists.
|
||||
*/
|
||||
static int add_switch_table_alts(struct objtool_file *file)
|
||||
static int add_jump_table_alts(struct objtool_file *file)
|
||||
{
|
||||
struct section *sec;
|
||||
struct symbol *func;
|
||||
@ -1142,7 +1148,7 @@ static int add_switch_table_alts(struct objtool_file *file)
|
||||
if (func->type != STT_FUNC)
|
||||
continue;
|
||||
|
||||
ret = add_func_switch_tables(file, func);
|
||||
ret = add_func_jump_tables(file, func);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -1339,7 +1345,7 @@ static int decode_sections(struct objtool_file *file)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = add_switch_table_alts(file);
|
||||
ret = add_jump_table_alts(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -385,7 +385,7 @@ static int read_relas(struct elf *elf)
|
||||
rela->offset = rela->rela.r_offset;
|
||||
symndx = GELF_R_SYM(rela->rela.r_info);
|
||||
rela->sym = find_symbol_by_index(elf, symndx);
|
||||
rela->rela_sec = sec;
|
||||
rela->sec = sec;
|
||||
if (!rela->sym) {
|
||||
WARN("can't find rela entry symbol %d for %s",
|
||||
symndx, sec->name);
|
||||
|
@ -57,7 +57,7 @@ struct rela {
|
||||
struct list_head list;
|
||||
struct hlist_node hash;
|
||||
GElf_Rela rela;
|
||||
struct section *rela_sec;
|
||||
struct section *sec;
|
||||
struct symbol *sym;
|
||||
unsigned int type;
|
||||
unsigned long offset;
|
||||
|
Loading…
Reference in New Issue
Block a user