Skip to content

Commit bd98c81

Browse files
thejhKAGA-KOKO
authored andcommitted
objtool: Support repeated uses of the same C jump table
This fixes objtool for both a GCC issue and a Clang issue: 1) GCC issue: kernel/bpf/core.o: warning: objtool: ___bpf_prog_run()+0x8d5: sibling call from callable instruction with modified stack frame With CONFIG_RETPOLINE=n, GCC is doing the following optimization in ___bpf_prog_run(). Before: select_insn: jmp *jumptable(,%rax,8) ... ALU64_ADD_X: ... jmp select_insn ALU_ADD_X: ... jmp select_insn After: select_insn: jmp *jumptable(, %rax, 8) ... ALU64_ADD_X: ... jmp *jumptable(, %rax, 8) ALU_ADD_X: ... jmp *jumptable(, %rax, 8) This confuses objtool. It has never seen multiple indirect jump sites which use the same jump table. For GCC switch tables, the only way of detecting the size of a table is by continuing to scan for more tables. The size of the previous table can only be determined after another switch table is found, or when the scan reaches the end of the function. That logic was reused for C jump tables, and was based on the assumption that each jump table only has a single jump site. The above optimization breaks that assumption. 2) Clang issue: drivers/usb/misc/sisusbvga/sisusb.o: warning: objtool: sisusb_write_mem_bulk()+0x588: can't find switch jump table With clang 9, code can be generated where a function contains two indirect jump instructions which use the same switch table. The fix is the same for both issues: split the jump table parsing into two passes. In the first pass, locate the heads of all switch tables for the function and mark their locations. In the second pass, parse the switch tables and add them. Fixes: e55a732 ("bpf: Fix ORC unwinding in non-JIT BPF code") Reported-by: Randy Dunlap <[email protected]> Reported-by: Arnd Bergmann <[email protected]> Signed-off-by: Jann Horn <[email protected]> Signed-off-by: Josh Poimboeuf <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Nick Desaulniers <[email protected]> Acked-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lkml.kernel.org/r/e995befaada9d4d8b2cf788ff3f566ba900d2b4d.1563413318.git.jpoimboe@redhat.com Co-developed-by: Josh Poimboeuf <[email protected]>
1 parent e7c2bc3 commit bd98c81

File tree

3 files changed

+30
-25
lines changed

3 files changed

+30
-25
lines changed

tools/objtool/check.c

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,7 @@ static int add_special_section_alts(struct objtool_file *file)
900900
}
901901

902902
static int add_jump_table(struct objtool_file *file, struct instruction *insn,
903-
struct rela *table, struct rela *next_table)
903+
struct rela *table)
904904
{
905905
struct rela *rela = table;
906906
struct instruction *dest_insn;
@@ -913,7 +913,9 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
913913
* instruction.
914914
*/
915915
list_for_each_entry_from(rela, &table->sec->rela_list, list) {
916-
if (rela == next_table)
916+
917+
/* Check for the end of the table: */
918+
if (rela != table && rela->jump_table_start)
917919
break;
918920

919921
/* Make sure the table entries are consecutive: */
@@ -1072,13 +1074,15 @@ static struct rela *find_jump_table(struct objtool_file *file,
10721074
return NULL;
10731075
}
10741076

1075-
1076-
static int add_func_jump_tables(struct objtool_file *file,
1077-
struct symbol *func)
1077+
/*
1078+
* First pass: Mark the head of each jump table so that in the next pass,
1079+
* we know when a given jump table ends and the next one starts.
1080+
*/
1081+
static void mark_func_jump_tables(struct objtool_file *file,
1082+
struct symbol *func)
10781083
{
1079-
struct instruction *insn, *last = NULL, *prev_jump = NULL;
1080-
struct rela *rela, *prev_rela = NULL;
1081-
int ret;
1084+
struct instruction *insn, *last = NULL;
1085+
struct rela *rela;
10821086

10831087
func_for_each_insn_all(file, func, insn) {
10841088
if (!last)
@@ -1102,26 +1106,24 @@ static int add_func_jump_tables(struct objtool_file *file,
11021106
continue;
11031107

11041108
rela = find_jump_table(file, func, insn);
1105-
if (!rela)
1106-
continue;
1107-
1108-
/*
1109-
* We found a jump table, but we don't know yet how big it
1110-
* is. Don't add it until we reach the end of the function or
1111-
* the beginning of another jump table in the same function.
1112-
*/
1113-
if (prev_jump) {
1114-
ret = add_jump_table(file, prev_jump, prev_rela, rela);
1115-
if (ret)
1116-
return ret;
1109+
if (rela) {
1110+
rela->jump_table_start = true;
1111+
insn->jump_table = rela;
11171112
}
1118-
1119-
prev_jump = insn;
1120-
prev_rela = rela;
11211113
}
1114+
}
1115+
1116+
static int add_func_jump_tables(struct objtool_file *file,
1117+
struct symbol *func)
1118+
{
1119+
struct instruction *insn;
1120+
int ret;
1121+
1122+
func_for_each_insn_all(file, func, insn) {
1123+
if (!insn->jump_table)
1124+
continue;
11221125

1123-
if (prev_jump) {
1124-
ret = add_jump_table(file, prev_jump, prev_rela, NULL);
1126+
ret = add_jump_table(file, insn, insn->jump_table);
11251127
if (ret)
11261128
return ret;
11271129
}
@@ -1148,6 +1150,7 @@ static int add_jump_table_alts(struct objtool_file *file)
11481150
if (func->type != STT_FUNC)
11491151
continue;
11501152

1153+
mark_func_jump_tables(file, func);
11511154
ret = add_func_jump_tables(file, func);
11521155
if (ret)
11531156
return ret;

tools/objtool/check.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct instruction {
3838
struct symbol *call_dest;
3939
struct instruction *jump_dest;
4040
struct instruction *first_jump_src;
41+
struct rela *jump_table;
4142
struct list_head alts;
4243
struct symbol *func;
4344
struct stack_op stack_op;

tools/objtool/elf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ struct rela {
6262
unsigned int type;
6363
unsigned long offset;
6464
int addend;
65+
bool jump_table_start;
6566
};
6667

6768
struct elf {

0 commit comments

Comments
 (0)