Skip to content

Commit 1381043

Browse files
jpoimboeIngo Molnar
authored andcommitted
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: damian <[email protected]> Reported-by: Arnd Bergmann <[email protected]> Signed-off-by: Josh Poimboeuf <[email protected]> Acked-by: Peter Zijlstra (Intel) <[email protected]> Cc: David Laight <[email protected]> Cc: Greg KH <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Randy Dunlap <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: http://lkml.kernel.org/r/0965e7fcfc5f31a276f0c7f298ff770c19b68706.1525923412.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <[email protected]>
1 parent 0afd0d9 commit 1381043

File tree

3 files changed

+93
-44
lines changed

3 files changed

+93
-44
lines changed

tools/objtool/check.c

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,31 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file,
5959
return next;
6060
}
6161

62+
static struct instruction *next_insn_same_func(struct objtool_file *file,
63+
struct instruction *insn)
64+
{
65+
struct instruction *next = list_next_entry(insn, list);
66+
struct symbol *func = insn->func;
67+
68+
if (!func)
69+
return NULL;
70+
71+
if (&next->list != &file->insn_list && next->func == func)
72+
return next;
73+
74+
/* Check if we're already in the subfunction: */
75+
if (func == func->cfunc)
76+
return NULL;
77+
78+
/* Move to the subfunction: */
79+
return find_insn(file, func->cfunc->sec, func->cfunc->offset);
80+
}
81+
82+
#define func_for_each_insn_all(file, func, insn) \
83+
for (insn = find_insn(file, func->sec, func->offset); \
84+
insn; \
85+
insn = next_insn_same_func(file, insn))
86+
6287
#define func_for_each_insn(file, func, insn) \
6388
for (insn = find_insn(file, func->sec, func->offset); \
6489
insn && &insn->list != &file->insn_list && \
@@ -149,10 +174,14 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
149174
if (!strcmp(func->name, global_noreturns[i]))
150175
return 1;
151176

152-
if (!func->sec)
177+
if (!func->len)
153178
return 0;
154179

155-
func_for_each_insn(file, func, insn) {
180+
insn = find_insn(file, func->sec, func->offset);
181+
if (!insn->func)
182+
return 0;
183+
184+
func_for_each_insn_all(file, func, insn) {
156185
empty = false;
157186

158187
if (insn->type == INSN_RETURN)
@@ -167,28 +196,17 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
167196
* case, the function's dead-end status depends on whether the target
168197
* of the sibling call returns.
169198
*/
170-
func_for_each_insn(file, func, insn) {
171-
if (insn->sec != func->sec ||
172-
insn->offset >= func->offset + func->len)
173-
break;
174-
199+
func_for_each_insn_all(file, func, insn) {
175200
if (insn->type == INSN_JUMP_UNCONDITIONAL) {
176201
struct instruction *dest = insn->jump_dest;
177-
struct symbol *dest_func;
178202

179203
if (!dest)
180204
/* sibling call to another file */
181205
return 0;
182206

183-
if (dest->sec != func->sec ||
184-
dest->offset < func->offset ||
185-
dest->offset >= func->offset + func->len) {
186-
/* local sibling call */
187-
dest_func = find_symbol_by_offset(dest->sec,
188-
dest->offset);
189-
if (!dest_func)
190-
continue;
207+
if (dest->func && dest->func->pfunc != insn->func->pfunc) {
191208

209+
/* local sibling call */
192210
if (recursion == 5) {
193211
/*
194212
* Infinite recursion: two functions
@@ -199,7 +217,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
199217
return 0;
200218
}
201219

202-
return __dead_end_function(file, dest_func,
220+
return __dead_end_function(file, dest->func,
203221
recursion + 1);
204222
}
205223
}
@@ -426,7 +444,7 @@ static void add_ignores(struct objtool_file *file)
426444
if (!ignore_func(file, func))
427445
continue;
428446

429-
func_for_each_insn(file, func, insn)
447+
func_for_each_insn_all(file, func, insn)
430448
insn->ignore = true;
431449
}
432450
}
@@ -786,9 +804,8 @@ static int add_special_section_alts(struct objtool_file *file)
786804
return ret;
787805
}
788806

789-
static int add_switch_table(struct objtool_file *file, struct symbol *func,
790-
struct instruction *insn, struct rela *table,
791-
struct rela *next_table)
807+
static int add_switch_table(struct objtool_file *file, struct instruction *insn,
808+
struct rela *table, struct rela *next_table)
792809
{
793810
struct rela *rela = table;
794811
struct instruction *alt_insn;
@@ -798,18 +815,13 @@ static int add_switch_table(struct objtool_file *file, struct symbol *func,
798815
if (rela == next_table)
799816
break;
800817

801-
if (rela->sym->sec != insn->sec ||
802-
rela->addend <= func->offset ||
803-
rela->addend >= func->offset + func->len)
818+
alt_insn = find_insn(file, rela->sym->sec, rela->addend);
819+
if (!alt_insn)
804820
break;
805821

806-
alt_insn = find_insn(file, insn->sec, rela->addend);
807-
if (!alt_insn) {
808-
WARN("%s: can't find instruction at %s+0x%x",
809-
file->rodata->rela->name, insn->sec->name,
810-
rela->addend);
811-
return -1;
812-
}
822+
/* Make sure the jmp dest is in the function or subfunction: */
823+
if (alt_insn->func->pfunc != insn->func->pfunc)
824+
break;
813825

814826
alt = malloc(sizeof(*alt));
815827
if (!alt) {
@@ -947,7 +959,7 @@ static int add_func_switch_tables(struct objtool_file *file,
947959
struct rela *rela, *prev_rela = NULL;
948960
int ret;
949961

950-
func_for_each_insn(file, func, insn) {
962+
func_for_each_insn_all(file, func, insn) {
951963
if (!last)
952964
last = insn;
953965

@@ -978,8 +990,7 @@ static int add_func_switch_tables(struct objtool_file *file,
978990
* the beginning of another switch table in the same function.
979991
*/
980992
if (prev_jump) {
981-
ret = add_switch_table(file, func, prev_jump, prev_rela,
982-
rela);
993+
ret = add_switch_table(file, prev_jump, prev_rela, rela);
983994
if (ret)
984995
return ret;
985996
}
@@ -989,7 +1000,7 @@ static int add_func_switch_tables(struct objtool_file *file,
9891000
}
9901001

9911002
if (prev_jump) {
992-
ret = add_switch_table(file, func, prev_jump, prev_rela, NULL);
1003+
ret = add_switch_table(file, prev_jump, prev_rela, NULL);
9931004
if (ret)
9941005
return ret;
9951006
}
@@ -1753,15 +1764,13 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
17531764
while (1) {
17541765
next_insn = next_insn_same_sec(file, insn);
17551766

1756-
1757-
if (file->c_file && func && insn->func && func != insn->func) {
1767+
if (file->c_file && func && insn->func && func != insn->func->pfunc) {
17581768
WARN("%s() falls through to next function %s()",
17591769
func->name, insn->func->name);
17601770
return 1;
17611771
}
17621772

1763-
if (insn->func)
1764-
func = insn->func;
1773+
func = insn->func ? insn->func->pfunc : NULL;
17651774

17661775
if (func && insn->ignore) {
17671776
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,
17821791

17831792
i = insn;
17841793
save_insn = NULL;
1785-
func_for_each_insn_continue_reverse(file, func, i) {
1794+
func_for_each_insn_continue_reverse(file, insn->func, i) {
17861795
if (i->save) {
17871796
save_insn = i;
17881797
break;
@@ -1869,7 +1878,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
18691878
case INSN_JUMP_UNCONDITIONAL:
18701879
if (insn->jump_dest &&
18711880
(!func || !insn->jump_dest->func ||
1872-
func == insn->jump_dest->func)) {
1881+
insn->jump_dest->func->pfunc == func)) {
18731882
ret = validate_branch(file, insn->jump_dest,
18741883
state);
18751884
if (ret)
@@ -2064,7 +2073,7 @@ static int validate_functions(struct objtool_file *file)
20642073

20652074
for_each_sec(file, sec) {
20662075
list_for_each_entry(func, &sec->symbol_list, list) {
2067-
if (func->type != STT_FUNC)
2076+
if (func->type != STT_FUNC || func->pfunc != func)
20682077
continue;
20692078

20702079
insn = find_insn(file, sec, func->offset);

tools/objtool/elf.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,19 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
7979
return NULL;
8080
}
8181

82+
struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
83+
{
84+
struct section *sec;
85+
struct symbol *sym;
86+
87+
list_for_each_entry(sec, &elf->sections, list)
88+
list_for_each_entry(sym, &sec->symbol_list, list)
89+
if (!strcmp(sym->name, name))
90+
return sym;
91+
92+
return NULL;
93+
}
94+
8295
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
8396
{
8497
struct symbol *sym;
@@ -203,10 +216,11 @@ static int read_sections(struct elf *elf)
203216

204217
static int read_symbols(struct elf *elf)
205218
{
206-
struct section *symtab;
207-
struct symbol *sym;
219+
struct section *symtab, *sec;
220+
struct symbol *sym, *pfunc;
208221
struct list_head *entry, *tmp;
209222
int symbols_nr, i;
223+
char *coldstr;
210224

211225
symtab = find_section_by_name(elf, ".symtab");
212226
if (!symtab) {
@@ -281,6 +295,30 @@ static int read_symbols(struct elf *elf)
281295
hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
282296
}
283297

298+
/* Create parent/child links for any cold subfunctions */
299+
list_for_each_entry(sec, &elf->sections, list) {
300+
list_for_each_entry(sym, &sec->symbol_list, list) {
301+
if (sym->type != STT_FUNC)
302+
continue;
303+
sym->pfunc = sym->cfunc = sym;
304+
coldstr = strstr(sym->name, ".cold.");
305+
if (coldstr) {
306+
coldstr[0] = '\0';
307+
pfunc = find_symbol_by_name(elf, sym->name);
308+
coldstr[0] = '.';
309+
310+
if (!pfunc) {
311+
WARN("%s(): can't find parent function",
312+
sym->name);
313+
goto err;
314+
}
315+
316+
sym->pfunc = pfunc;
317+
pfunc->cfunc = sym;
318+
}
319+
}
320+
}
321+
284322
return 0;
285323

286324
err:

tools/objtool/elf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ struct symbol {
6161
unsigned char bind, type;
6262
unsigned long offset;
6363
unsigned int len;
64+
struct symbol *pfunc, *cfunc;
6465
};
6566

6667
struct rela {
@@ -86,6 +87,7 @@ struct elf {
8687
struct elf *elf_open(const char *name, int flags);
8788
struct section *find_section_by_name(struct elf *elf, const char *name);
8889
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
90+
struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
8991
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
9092
struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
9193
struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,

0 commit comments

Comments
 (0)