Skip to content

Commit 8aa8eb2

Browse files
achartrePeter Zijlstra
authored andcommitted
objtool: Add support for intra-function calls
Change objtool to support intra-function calls. On x86, an intra-function call is represented in objtool as a push onto the stack (of the return address), and a jump to the destination address. That way the stack information is correctly updated and the call flow is still accurate. Signed-off-by: Alexandre Chartre <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Reviewed-by: Miroslav Benes <[email protected]> Acked-by: Josh Poimboeuf <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent b490f45 commit 8aa8eb2

File tree

4 files changed

+102
-4
lines changed

4 files changed

+102
-4
lines changed

include/linux/frame.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,20 @@
1515
static void __used __section(.discard.func_stack_frame_non_standard) \
1616
*__func_stack_frame_non_standard_##func = func
1717

18+
/*
19+
* This macro indicates that the following intra-function call is valid.
20+
* Any non-annotated intra-function call will cause objtool to issue a warning.
21+
*/
22+
#define ANNOTATE_INTRA_FUNCTION_CALL \
23+
999: \
24+
.pushsection .discard.intra_function_calls; \
25+
.long 999b; \
26+
.popsection;
27+
1828
#else /* !CONFIG_STACK_VALIDATION */
1929

2030
#define STACK_FRAME_NON_STANDARD(func)
31+
#define ANNOTATE_INTRA_FUNCTION_CALL
2132

2233
#endif /* CONFIG_STACK_VALIDATION */
2334

tools/objtool/Documentation/stack-validation.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,14 @@ they mean, and suggestions for how to fix them.
323323
The easiest way to enforce this is to ensure alternatives do not contain
324324
any ORC entries, which in turn implies the above constraint.
325325

326+
11. file.o: warning: unannotated intra-function call
327+
328+
This warning means that a direct call is done to a destination which
329+
is not at the beginning of a function. If this is a legit call, you
330+
can remove this warning by putting the ANNOTATE_INTRA_FUNCTION_CALL
331+
directive right before the call.
332+
333+
326334
If the error doesn't seem to make sense, it could be a bug in objtool.
327335
Feel free to ask the objtool maintainer for help.
328336

tools/objtool/arch/x86/decode.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,14 @@ int arch_decode_instruction(const struct elf *elf, const struct section *sec,
496496

497497
case 0xe8:
498498
*type = INSN_CALL;
499+
/*
500+
* For the impact on the stack, a CALL behaves like
501+
* a PUSH of an immediate value (the return address).
502+
*/
503+
ADD_OP(op) {
504+
op->src.type = OP_SRC_CONST;
505+
op->dest.type = OP_DEST_PUSH;
506+
}
499507
break;
500508

501509
case 0xfc:

tools/objtool/check.c

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,16 @@ static int add_jump_destinations(struct objtool_file *file)
674674
return 0;
675675
}
676676

677+
static void remove_insn_ops(struct instruction *insn)
678+
{
679+
struct stack_op *op, *tmp;
680+
681+
list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
682+
list_del(&op->list);
683+
free(op);
684+
}
685+
}
686+
677687
/*
678688
* Find the destination instructions for all calls.
679689
*/
@@ -699,10 +709,7 @@ static int add_call_destinations(struct objtool_file *file)
699709
continue;
700710

701711
if (!insn->call_dest) {
702-
WARN_FUNC("unsupported intra-function call",
703-
insn->sec, insn->offset);
704-
if (retpoline)
705-
WARN("If this is a retpoline, please patch it in with alternatives and annotate it with ANNOTATE_NOSPEC_ALTERNATIVE.");
712+
WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset);
706713
return -1;
707714
}
708715

@@ -725,6 +732,15 @@ static int add_call_destinations(struct objtool_file *file)
725732
}
726733
} else
727734
insn->call_dest = rela->sym;
735+
736+
/*
737+
* Whatever stack impact regular CALLs have, should be undone
738+
* by the RETURN of the called function.
739+
*
740+
* Annotated intra-function calls retain the stack_ops but
741+
* are converted to JUMP, see read_intra_function_calls().
742+
*/
743+
remove_insn_ops(insn);
728744
}
729745

730746
return 0;
@@ -1404,6 +1420,57 @@ static int read_instr_hints(struct objtool_file *file)
14041420
return 0;
14051421
}
14061422

1423+
static int read_intra_function_calls(struct objtool_file *file)
1424+
{
1425+
struct instruction *insn;
1426+
struct section *sec;
1427+
struct rela *rela;
1428+
1429+
sec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls");
1430+
if (!sec)
1431+
return 0;
1432+
1433+
list_for_each_entry(rela, &sec->rela_list, list) {
1434+
unsigned long dest_off;
1435+
1436+
if (rela->sym->type != STT_SECTION) {
1437+
WARN("unexpected relocation symbol type in %s",
1438+
sec->name);
1439+
return -1;
1440+
}
1441+
1442+
insn = find_insn(file, rela->sym->sec, rela->addend);
1443+
if (!insn) {
1444+
WARN("bad .discard.intra_function_call entry");
1445+
return -1;
1446+
}
1447+
1448+
if (insn->type != INSN_CALL) {
1449+
WARN_FUNC("intra_function_call not a direct call",
1450+
insn->sec, insn->offset);
1451+
return -1;
1452+
}
1453+
1454+
/*
1455+
* Treat intra-function CALLs as JMPs, but with a stack_op.
1456+
* See add_call_destinations(), which strips stack_ops from
1457+
* normal CALLs.
1458+
*/
1459+
insn->type = INSN_JUMP_UNCONDITIONAL;
1460+
1461+
dest_off = insn->offset + insn->len + insn->immediate;
1462+
insn->jump_dest = find_insn(file, insn->sec, dest_off);
1463+
if (!insn->jump_dest) {
1464+
WARN_FUNC("can't find call dest at %s+0x%lx",
1465+
insn->sec, insn->offset,
1466+
insn->sec->name, dest_off);
1467+
return -1;
1468+
}
1469+
}
1470+
1471+
return 0;
1472+
}
1473+
14071474
static void mark_rodata(struct objtool_file *file)
14081475
{
14091476
struct section *sec;
@@ -1459,6 +1526,10 @@ static int decode_sections(struct objtool_file *file)
14591526
if (ret)
14601527
return ret;
14611528

1529+
ret = read_intra_function_calls(file);
1530+
if (ret)
1531+
return ret;
1532+
14621533
ret = add_call_destinations(file);
14631534
if (ret)
14641535
return ret;

0 commit comments

Comments
 (0)