Skip to content

Commit 5a9c361

Browse files
author
Peter Zijlstra
committed
objtool: Allow STT_NOTYPE -> STT_FUNC+0 sibling-calls
Teach objtool about STT_NOTYPE -> STT_FUNC+0 sibling calls. Doing do allows slightly simpler .S files. There is a slight complication in that we specifically do not want to allow sibling calls from symbol holes (previously covered by STT_WEAK symbols) -- such things exist where a weak function has a .cold subfunction for example. Additionally, STT_NOTYPE tail-calls are allowed to happen with a modified stack frame, they don't need to obey the normal rules after all. Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
1 parent dbcdbdf commit 5a9c361

File tree

1 file changed

+47
-27
lines changed

1 file changed

+47
-27
lines changed

tools/objtool/check.c

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,13 @@ static bool is_jump_table_jump(struct instruction *insn)
129129
static bool is_sibling_call(struct instruction *insn)
130130
{
131131
/*
132-
* Assume only ELF functions can make sibling calls. This ensures
133-
* sibling call detection consistency between vmlinux.o and individual
134-
* objects.
132+
* Assume only STT_FUNC calls have jump-tables.
135133
*/
136-
if (!insn_func(insn))
137-
return false;
138-
139-
/* An indirect jump is either a sibling call or a jump to a table. */
140-
if (insn->type == INSN_JUMP_DYNAMIC)
141-
return !is_jump_table_jump(insn);
134+
if (insn_func(insn)) {
135+
/* An indirect jump is either a sibling call or a jump to a table. */
136+
if (insn->type == INSN_JUMP_DYNAMIC)
137+
return !is_jump_table_jump(insn);
138+
}
142139

143140
/* add_jump_destinations() sets insn->call_dest for sibling calls. */
144141
return (is_static_jump(insn) && insn->call_dest);
@@ -1400,27 +1397,50 @@ static void add_return_call(struct objtool_file *file, struct instruction *insn,
14001397
list_add_tail(&insn->call_node, &file->return_thunk_list);
14011398
}
14021399

1403-
static bool same_function(struct instruction *insn1, struct instruction *insn2)
1404-
{
1405-
return insn_func(insn1)->pfunc == insn_func(insn2)->pfunc;
1406-
}
1407-
1408-
static bool is_first_func_insn(struct objtool_file *file, struct instruction *insn)
1400+
static bool is_first_func_insn(struct objtool_file *file,
1401+
struct instruction *insn, struct symbol *sym)
14091402
{
1410-
if (insn->offset == insn_func(insn)->offset)
1403+
if (insn->offset == sym->offset)
14111404
return true;
14121405

1406+
/* Allow direct CALL/JMP past ENDBR */
14131407
if (opts.ibt) {
14141408
struct instruction *prev = prev_insn_same_sym(file, insn);
14151409

14161410
if (prev && prev->type == INSN_ENDBR &&
1417-
insn->offset == insn_func(insn)->offset + prev->len)
1411+
insn->offset == sym->offset + prev->len)
14181412
return true;
14191413
}
14201414

14211415
return false;
14221416
}
14231417

1418+
/*
1419+
* A sibling call is a tail-call to another symbol -- to differentiate from a
1420+
* recursive tail-call which is to the same symbol.
1421+
*/
1422+
static bool jump_is_sibling_call(struct objtool_file *file,
1423+
struct instruction *from, struct instruction *to)
1424+
{
1425+
struct symbol *fs = from->sym;
1426+
struct symbol *ts = to->sym;
1427+
1428+
/* Not a sibling call if from/to a symbol hole */
1429+
if (!fs || !ts)
1430+
return false;
1431+
1432+
/* Not a sibling call if not targeting the start of a symbol. */
1433+
if (!is_first_func_insn(file, to, ts))
1434+
return false;
1435+
1436+
/* Disallow sibling calls into STT_NOTYPE */
1437+
if (ts->type == STT_NOTYPE)
1438+
return false;
1439+
1440+
/* Must not be self to be a sibling */
1441+
return fs->pfunc != ts->pfunc;
1442+
}
1443+
14241444
/*
14251445
* Find the destination instructions for all jumps.
14261446
*/
@@ -1519,18 +1539,18 @@ static int add_jump_destinations(struct objtool_file *file)
15191539
strstr(insn_func(jump_dest)->name, ".cold")) {
15201540
insn_func(insn)->cfunc = insn_func(jump_dest);
15211541
insn_func(jump_dest)->pfunc = insn_func(insn);
1522-
1523-
} else if (!same_function(insn, jump_dest) &&
1524-
is_first_func_insn(file, jump_dest)) {
1525-
/*
1526-
* Internal sibling call without reloc or with
1527-
* STT_SECTION reloc.
1528-
*/
1529-
add_call_dest(file, insn, insn_func(jump_dest), true);
1530-
continue;
15311542
}
15321543
}
15331544

1545+
if (jump_is_sibling_call(file, insn, jump_dest)) {
1546+
/*
1547+
* Internal sibling call without reloc or with
1548+
* STT_SECTION reloc.
1549+
*/
1550+
add_call_dest(file, insn, insn_func(jump_dest), true);
1551+
continue;
1552+
}
1553+
15341554
insn->jump_dest = jump_dest;
15351555
}
15361556

@@ -3309,7 +3329,7 @@ static int validate_sibling_call(struct objtool_file *file,
33093329
struct instruction *insn,
33103330
struct insn_state *state)
33113331
{
3312-
if (has_modified_stack_frame(insn, state)) {
3332+
if (insn_func(insn) && has_modified_stack_frame(insn, state)) {
33133333
WARN_FUNC("sibling call from callable instruction with modified stack frame",
33143334
insn->sec, insn->offset);
33153335
return 1;

0 commit comments

Comments
 (0)