Commit 13810435 authored by Josh Poimboeuf's avatar Josh Poimboeuf Committed by Ingo Molnar

objtool: Support GCC 8's cold subfunctions

GCC 8 moves a lot of unlikely code out of line to "cold" subfunctions in
.text.unlikely.  Properly detect the new subfunctions and treat them as
extensions of the original functions.

This fixes a bunch of warnings like:

  kernel/cgroup/cgroup.o: warning: objtool: parse_cgroup_root_flags()+0x33: sibling call from callable instruction with modified stack frame
  kernel/cgroup/cgroup.o: warning: objtool: cgroup_addrm_files()+0x290: sibling call from callable instruction with modified stack frame
  kernel/cgroup/cgroup.o: warning: objtool: cgroup_apply_control_enable()+0x25b: sibling call from callable instruction with modified stack frame
  kernel/cgroup/cgroup.o: warning: objtool: rebind_subsystems()+0x325: sibling call from callable instruction with modified stack frame
Reported-and-tested-by: default avatardamian <damian.tometzki@icloud.com>
Reported-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Acked-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Cc: David Laight <David.Laight@ACULAB.COM>
Cc: Greg KH <gregkh@linuxfoundation.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/0965e7fcfc5f31a276f0c7f298ff770c19b68706.1525923412.git.jpoimboe@redhat.comSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 0afd0d9e
...@@ -59,6 +59,31 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file, ...@@ -59,6 +59,31 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file,
return next; return next;
} }
static struct instruction *next_insn_same_func(struct objtool_file *file,
struct instruction *insn)
{
struct instruction *next = list_next_entry(insn, list);
struct symbol *func = insn->func;
if (!func)
return NULL;
if (&next->list != &file->insn_list && next->func == func)
return next;
/* Check if we're already in the subfunction: */
if (func == func->cfunc)
return NULL;
/* Move to the subfunction: */
return find_insn(file, func->cfunc->sec, func->cfunc->offset);
}
#define func_for_each_insn_all(file, func, insn) \
for (insn = find_insn(file, func->sec, func->offset); \
insn; \
insn = next_insn_same_func(file, insn))
#define func_for_each_insn(file, func, insn) \ #define func_for_each_insn(file, func, insn) \
for (insn = find_insn(file, func->sec, func->offset); \ for (insn = find_insn(file, func->sec, func->offset); \
insn && &insn->list != &file->insn_list && \ insn && &insn->list != &file->insn_list && \
...@@ -149,10 +174,14 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func, ...@@ -149,10 +174,14 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
if (!strcmp(func->name, global_noreturns[i])) if (!strcmp(func->name, global_noreturns[i]))
return 1; return 1;
if (!func->sec) if (!func->len)
return 0; return 0;
func_for_each_insn(file, func, insn) { insn = find_insn(file, func->sec, func->offset);
if (!insn->func)
return 0;
func_for_each_insn_all(file, func, insn) {
empty = false; empty = false;
if (insn->type == INSN_RETURN) if (insn->type == INSN_RETURN)
...@@ -167,28 +196,17 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func, ...@@ -167,28 +196,17 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
* case, the function's dead-end status depends on whether the target * case, the function's dead-end status depends on whether the target
* of the sibling call returns. * of the sibling call returns.
*/ */
func_for_each_insn(file, func, insn) { func_for_each_insn_all(file, func, insn) {
if (insn->sec != func->sec ||
insn->offset >= func->offset + func->len)
break;
if (insn->type == INSN_JUMP_UNCONDITIONAL) { if (insn->type == INSN_JUMP_UNCONDITIONAL) {
struct instruction *dest = insn->jump_dest; struct instruction *dest = insn->jump_dest;
struct symbol *dest_func;
if (!dest) if (!dest)
/* sibling call to another file */ /* sibling call to another file */
return 0; return 0;
if (dest->sec != func->sec || if (dest->func && dest->func->pfunc != insn->func->pfunc) {
dest->offset < func->offset ||
dest->offset >= func->offset + func->len) {
/* local sibling call */
dest_func = find_symbol_by_offset(dest->sec,
dest->offset);
if (!dest_func)
continue;
/* local sibling call */
if (recursion == 5) { if (recursion == 5) {
/* /*
* Infinite recursion: two functions * Infinite recursion: two functions
...@@ -199,7 +217,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func, ...@@ -199,7 +217,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
return 0; return 0;
} }
return __dead_end_function(file, dest_func, return __dead_end_function(file, dest->func,
recursion + 1); recursion + 1);
} }
} }
...@@ -426,7 +444,7 @@ static void add_ignores(struct objtool_file *file) ...@@ -426,7 +444,7 @@ static void add_ignores(struct objtool_file *file)
if (!ignore_func(file, func)) if (!ignore_func(file, func))
continue; continue;
func_for_each_insn(file, func, insn) func_for_each_insn_all(file, func, insn)
insn->ignore = true; insn->ignore = true;
} }
} }
...@@ -786,9 +804,8 @@ static int add_special_section_alts(struct objtool_file *file) ...@@ -786,9 +804,8 @@ static int add_special_section_alts(struct objtool_file *file)
return ret; return ret;
} }
static int add_switch_table(struct objtool_file *file, struct symbol *func, static int add_switch_table(struct objtool_file *file, struct instruction *insn,
struct instruction *insn, struct rela *table, struct rela *table, struct rela *next_table)
struct rela *next_table)
{ {
struct rela *rela = table; struct rela *rela = table;
struct instruction *alt_insn; struct instruction *alt_insn;
...@@ -798,18 +815,13 @@ static int add_switch_table(struct objtool_file *file, struct symbol *func, ...@@ -798,18 +815,13 @@ static int add_switch_table(struct objtool_file *file, struct symbol *func,
if (rela == next_table) if (rela == next_table)
break; break;
if (rela->sym->sec != insn->sec || alt_insn = find_insn(file, rela->sym->sec, rela->addend);
rela->addend <= func->offset || if (!alt_insn)
rela->addend >= func->offset + func->len)
break; break;
alt_insn = find_insn(file, insn->sec, rela->addend); /* Make sure the jmp dest is in the function or subfunction: */
if (!alt_insn) { if (alt_insn->func->pfunc != insn->func->pfunc)
WARN("%s: can't find instruction at %s+0x%x", break;
file->rodata->rela->name, insn->sec->name,
rela->addend);
return -1;
}
alt = malloc(sizeof(*alt)); alt = malloc(sizeof(*alt));
if (!alt) { if (!alt) {
...@@ -947,7 +959,7 @@ static int add_func_switch_tables(struct objtool_file *file, ...@@ -947,7 +959,7 @@ static int add_func_switch_tables(struct objtool_file *file,
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_all(file, func, insn) {
if (!last) if (!last)
last = insn; last = insn;
...@@ -978,8 +990,7 @@ static int add_func_switch_tables(struct objtool_file *file, ...@@ -978,8 +990,7 @@ static int add_func_switch_tables(struct objtool_file *file,
* the beginning of another switch table in the same function. * the beginning of another switch table in the same function.
*/ */
if (prev_jump) { if (prev_jump) {
ret = add_switch_table(file, func, prev_jump, prev_rela, ret = add_switch_table(file, prev_jump, prev_rela, rela);
rela);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -989,7 +1000,7 @@ static int add_func_switch_tables(struct objtool_file *file, ...@@ -989,7 +1000,7 @@ static int add_func_switch_tables(struct objtool_file *file,
} }
if (prev_jump) { if (prev_jump) {
ret = add_switch_table(file, func, prev_jump, prev_rela, NULL); ret = add_switch_table(file, prev_jump, prev_rela, NULL);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -1753,15 +1764,13 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1753,15 +1764,13 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
while (1) { while (1) {
next_insn = next_insn_same_sec(file, insn); next_insn = next_insn_same_sec(file, insn);
if (file->c_file && func && insn->func && func != insn->func->pfunc) {
if (file->c_file && func && insn->func && func != insn->func) {
WARN("%s() falls through to next function %s()", WARN("%s() falls through to next function %s()",
func->name, insn->func->name); func->name, insn->func->name);
return 1; return 1;
} }
if (insn->func) func = insn->func ? insn->func->pfunc : NULL;
func = insn->func;
if (func && insn->ignore) { if (func && insn->ignore) {
WARN_FUNC("BUG: why am I validating an ignored function?", WARN_FUNC("BUG: why am I validating an ignored function?",
...@@ -1782,7 +1791,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1782,7 +1791,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
i = insn; i = insn;
save_insn = NULL; save_insn = NULL;
func_for_each_insn_continue_reverse(file, func, i) { func_for_each_insn_continue_reverse(file, insn->func, i) {
if (i->save) { if (i->save) {
save_insn = i; save_insn = i;
break; break;
...@@ -1869,7 +1878,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1869,7 +1878,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
case INSN_JUMP_UNCONDITIONAL: case INSN_JUMP_UNCONDITIONAL:
if (insn->jump_dest && if (insn->jump_dest &&
(!func || !insn->jump_dest->func || (!func || !insn->jump_dest->func ||
func == insn->jump_dest->func)) { insn->jump_dest->func->pfunc == func)) {
ret = validate_branch(file, insn->jump_dest, ret = validate_branch(file, insn->jump_dest,
state); state);
if (ret) if (ret)
...@@ -2064,7 +2073,7 @@ static int validate_functions(struct objtool_file *file) ...@@ -2064,7 +2073,7 @@ static int validate_functions(struct objtool_file *file)
for_each_sec(file, sec) { for_each_sec(file, sec) {
list_for_each_entry(func, &sec->symbol_list, list) { list_for_each_entry(func, &sec->symbol_list, list) {
if (func->type != STT_FUNC) if (func->type != STT_FUNC || func->pfunc != func)
continue; continue;
insn = find_insn(file, sec, func->offset); insn = find_insn(file, sec, func->offset);
......
...@@ -79,6 +79,19 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) ...@@ -79,6 +79,19 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
return NULL; return NULL;
} }
struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
{
struct section *sec;
struct symbol *sym;
list_for_each_entry(sec, &elf->sections, list)
list_for_each_entry(sym, &sec->symbol_list, list)
if (!strcmp(sym->name, name))
return sym;
return NULL;
}
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset) struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
{ {
struct symbol *sym; struct symbol *sym;
...@@ -203,10 +216,11 @@ static int read_sections(struct elf *elf) ...@@ -203,10 +216,11 @@ static int read_sections(struct elf *elf)
static int read_symbols(struct elf *elf) static int read_symbols(struct elf *elf)
{ {
struct section *symtab; struct section *symtab, *sec;
struct symbol *sym; struct symbol *sym, *pfunc;
struct list_head *entry, *tmp; struct list_head *entry, *tmp;
int symbols_nr, i; int symbols_nr, i;
char *coldstr;
symtab = find_section_by_name(elf, ".symtab"); symtab = find_section_by_name(elf, ".symtab");
if (!symtab) { if (!symtab) {
...@@ -281,6 +295,30 @@ static int read_symbols(struct elf *elf) ...@@ -281,6 +295,30 @@ static int read_symbols(struct elf *elf)
hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx); hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
} }
/* Create parent/child links for any cold subfunctions */
list_for_each_entry(sec, &elf->sections, list) {
list_for_each_entry(sym, &sec->symbol_list, list) {
if (sym->type != STT_FUNC)
continue;
sym->pfunc = sym->cfunc = sym;
coldstr = strstr(sym->name, ".cold.");
if (coldstr) {
coldstr[0] = '\0';
pfunc = find_symbol_by_name(elf, sym->name);
coldstr[0] = '.';
if (!pfunc) {
WARN("%s(): can't find parent function",
sym->name);
goto err;
}
sym->pfunc = pfunc;
pfunc->cfunc = sym;
}
}
}
return 0; return 0;
err: err:
......
...@@ -61,6 +61,7 @@ struct symbol { ...@@ -61,6 +61,7 @@ struct symbol {
unsigned char bind, type; unsigned char bind, type;
unsigned long offset; unsigned long offset;
unsigned int len; unsigned int len;
struct symbol *pfunc, *cfunc;
}; };
struct rela { struct rela {
...@@ -86,6 +87,7 @@ struct elf { ...@@ -86,6 +87,7 @@ struct elf {
struct elf *elf_open(const char *name, int flags); struct elf *elf_open(const char *name, int flags);
struct section *find_section_by_name(struct elf *elf, const char *name); struct section *find_section_by_name(struct elf *elf, const char *name);
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
struct rela *find_rela_by_dest(struct section *sec, unsigned long offset); struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment