forked from luck/tmp_suning_uos_patched
objtool: Fix false positive warnings for functions with multiple switch statements
Ingo reported [1] some false positive objtool warnings: drivers/net/wireless/realtek/rtlwifi/base.o: warning: objtool: rtlwifi_rate_mapping()+0x2e7: frame pointer state mismatch drivers/net/wireless/realtek/rtlwifi/base.o: warning: objtool: rtlwifi_rate_mapping()+0x2f3: frame pointer state mismatch ... And so did the 0-day bot [2]: drivers/gpu/drm/radeon/cik.o: warning: objtool: cik_tiling_mode_table_init()+0x6ce: call without frame pointer save/setup drivers/gpu/drm/radeon/cik.o: warning: objtool: cik_tiling_mode_table_init()+0x72b: call without frame pointer save/setup ... Both sets of warnings involve functions which have multiple switch statements. When there's more than one switch statement in a function, objtool interprets all the switch jump tables as a single table. If the targets of one jump table assume a stack frame and the targets of another one don't, it prints false positive warnings. Fix the bug by detecting the size of each switch jump table. For multiple tables, each one ends where the next one begins. [1] https://lkml.kernel.org/r/20160308103716.GA9618@gmail.com [2] https://lists.01.org/pipermail/kbuild-all/2016-March/018124.html Reported-by: Ingo Molnar <mingo@kernel.org> Reported-by: kbuild test robot <fengguang.wu@intel.com> Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andy Lutomirski <luto@kernel.org> Cc: Arnaldo Carvalho de Melo <acme@infradead.org> Cc: Arnaldo Carvalho de Melo <acme@kernel.org> Cc: Bernd Petrovitsch <bernd@petrovitsch.priv.at> Cc: Borislav Petkov <bp@alien8.de> Cc: Chris J Arges <chris.j.arges@canonical.com> Cc: Jiri Slaby <jslaby@suse.cz> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Michal Marek <mmarek@suse.cz> Cc: Namhyung Kim <namhyung@gmail.com> Cc: Pedro Alves <palves@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: live-patching@vger.kernel.org Link: http://lkml.kernel.org/r/2d7eecc6bc52d301f494b80f5fd62c2b6c895658.1457502970.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
a196e17198
commit
8133fbb424
|
@ -61,6 +61,7 @@ struct alternative {
|
|||
struct objtool_file {
|
||||
struct elf *elf;
|
||||
struct list_head insn_list;
|
||||
struct section *rodata;
|
||||
};
|
||||
|
||||
const char *objname;
|
||||
|
@ -599,6 +600,103 @@ static int add_special_section_alts(struct objtool_file *file)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int add_switch_table(struct objtool_file *file, struct symbol *func,
|
||||
struct instruction *insn, struct rela *table,
|
||||
struct rela *next_table)
|
||||
{
|
||||
struct rela *rela = table;
|
||||
struct instruction *alt_insn;
|
||||
struct alternative *alt;
|
||||
|
||||
list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) {
|
||||
if (rela == next_table)
|
||||
break;
|
||||
|
||||
if (rela->sym->sec != insn->sec ||
|
||||
rela->addend <= func->offset ||
|
||||
rela->addend >= func->offset + func->len)
|
||||
break;
|
||||
|
||||
alt_insn = find_insn(file, insn->sec, rela->addend);
|
||||
if (!alt_insn) {
|
||||
WARN("%s: can't find instruction at %s+0x%x",
|
||||
file->rodata->rela->name, insn->sec->name,
|
||||
rela->addend);
|
||||
return -1;
|
||||
}
|
||||
|
||||
alt = malloc(sizeof(*alt));
|
||||
if (!alt) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
alt->insn = alt_insn;
|
||||
list_add_tail(&alt->list, &insn->alts);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_func_switch_tables(struct objtool_file *file,
|
||||
struct symbol *func)
|
||||
{
|
||||
struct instruction *insn, *prev_jump;
|
||||
struct rela *text_rela, *rodata_rela, *prev_rela;
|
||||
int ret;
|
||||
|
||||
prev_jump = NULL;
|
||||
|
||||
func_for_each_insn(file, func, insn) {
|
||||
if (insn->type != INSN_JUMP_DYNAMIC)
|
||||
continue;
|
||||
|
||||
text_rela = find_rela_by_dest_range(insn->sec, insn->offset,
|
||||
insn->len);
|
||||
if (!text_rela || text_rela->sym != file->rodata->sym)
|
||||
continue;
|
||||
|
||||
/* common case: jmpq *[addr](,%rax,8) */
|
||||
rodata_rela = find_rela_by_dest(file->rodata,
|
||||
text_rela->addend);
|
||||
|
||||
/*
|
||||
* TODO: Document where this is needed, or get rid of it.
|
||||
*
|
||||
* rare case: jmpq *[addr](%rip)
|
||||
*/
|
||||
if (!rodata_rela)
|
||||
rodata_rela = find_rela_by_dest(file->rodata,
|
||||
text_rela->addend + 4);
|
||||
|
||||
if (!rodata_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, func, prev_jump, prev_rela,
|
||||
rodata_rela);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
prev_jump = insn;
|
||||
prev_rela = rodata_rela;
|
||||
}
|
||||
|
||||
if (prev_jump) {
|
||||
ret = add_switch_table(file, func, prev_jump, prev_rela, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For some switch statements, gcc generates a jump table in the .rodata
|
||||
* section which contains a list of addresses within the function to jump to.
|
||||
|
@ -606,66 +704,21 @@ static int add_special_section_alts(struct objtool_file *file)
|
|||
*/
|
||||
static int add_switch_table_alts(struct objtool_file *file)
|
||||
{
|
||||
struct instruction *insn, *alt_insn;
|
||||
struct rela *rodata_rela, *text_rela;
|
||||
struct section *rodata;
|
||||
struct section *sec;
|
||||
struct symbol *func;
|
||||
struct alternative *alt;
|
||||
int ret;
|
||||
|
||||
for_each_insn(file, insn) {
|
||||
if (insn->type != INSN_JUMP_DYNAMIC)
|
||||
continue;
|
||||
if (!file->rodata || !file->rodata->rela)
|
||||
return 0;
|
||||
|
||||
text_rela = find_rela_by_dest_range(insn->sec, insn->offset,
|
||||
insn->len);
|
||||
if (!text_rela || strcmp(text_rela->sym->name, ".rodata"))
|
||||
continue;
|
||||
list_for_each_entry(sec, &file->elf->sections, list) {
|
||||
list_for_each_entry(func, &sec->symbol_list, list) {
|
||||
if (func->type != STT_FUNC)
|
||||
continue;
|
||||
|
||||
rodata = find_section_by_name(file->elf, ".rodata");
|
||||
if (!rodata || !rodata->rela)
|
||||
continue;
|
||||
|
||||
/* common case: jmpq *[addr](,%rax,8) */
|
||||
rodata_rela = find_rela_by_dest(rodata, text_rela->addend);
|
||||
|
||||
/* rare case: jmpq *[addr](%rip) */
|
||||
if (!rodata_rela)
|
||||
rodata_rela = find_rela_by_dest(rodata,
|
||||
text_rela->addend + 4);
|
||||
if (!rodata_rela)
|
||||
continue;
|
||||
|
||||
func = find_containing_func(insn->sec, insn->offset);
|
||||
if (!func) {
|
||||
WARN_FUNC("can't find containing func",
|
||||
insn->sec, insn->offset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
list_for_each_entry_from(rodata_rela, &rodata->rela->rela_list,
|
||||
list) {
|
||||
if (rodata_rela->sym->sec != insn->sec ||
|
||||
rodata_rela->addend <= func->offset ||
|
||||
rodata_rela->addend >= func->offset + func->len)
|
||||
break;
|
||||
|
||||
alt_insn = find_insn(file, insn->sec,
|
||||
rodata_rela->addend);
|
||||
if (!alt_insn) {
|
||||
WARN("%s: can't find instruction at %s+0x%x",
|
||||
rodata->rela->name, insn->sec->name,
|
||||
rodata_rela->addend);
|
||||
return -1;
|
||||
}
|
||||
|
||||
alt = malloc(sizeof(*alt));
|
||||
if (!alt) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
alt->insn = alt_insn;
|
||||
list_add_tail(&alt->list, &insn->alts);
|
||||
ret = add_func_switch_tables(file, func);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -676,6 +729,8 @@ static int decode_sections(struct objtool_file *file)
|
|||
{
|
||||
int ret;
|
||||
|
||||
file->rodata = find_section_by_name(file->elf, ".rodata");
|
||||
|
||||
ret = decode_instructions(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
Loading…
Reference in New Issue
Block a user