[tip: objtool/core] objtool: Add support for intra-function calls
The following commit has been merged into the objtool/core branch of tip: Commit-ID: 8aa8eb2a8f5b3305a95f39957dd2b715fa668e21 Gitweb: https://git.kernel.org/tip/8aa8eb2a8f5b3305a95f39957dd2b715fa668e21 Author:Alexandre Chartre AuthorDate:Tue, 14 Apr 2020 12:36:12 +02:00 Committer: Peter Zijlstra CommitterDate: Thu, 30 Apr 2020 20:14:33 +02:00 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 Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200414103618.12657-4-alexandre.char...@oracle.com --- include/linux/frame.h| 11 ++- tools/objtool/Documentation/stack-validation.txt | 8 ++- tools/objtool/arch/x86/decode.c | 8 ++- tools/objtool/check.c| 79 ++- 4 files changed, 102 insertions(+), 4 deletions(-) diff --git a/include/linux/frame.h b/include/linux/frame.h index 02d3ca2..303cda6 100644 --- a/include/linux/frame.h +++ b/include/linux/frame.h @@ -15,9 +15,20 @@ static void __used __section(.discard.func_stack_frame_non_standard) \ *__func_stack_frame_non_standard_##func = func +/* + * This macro indicates that the following intra-function call is valid. + * Any non-annotated intra-function call will cause objtool to issue a warning. + */ +#define ANNOTATE_INTRA_FUNCTION_CALL \ + 999:\ + .pushsection .discard.intra_function_calls; \ + .long 999b; \ + .popsection; + #else /* !CONFIG_STACK_VALIDATION */ #define STACK_FRAME_NON_STANDARD(func) +#define ANNOTATE_INTRA_FUNCTION_CALL #endif /* CONFIG_STACK_VALIDATION */ diff --git a/tools/objtool/Documentation/stack-validation.txt b/tools/objtool/Documentation/stack-validation.txt index 0189039..0542e46 100644 --- a/tools/objtool/Documentation/stack-validation.txt +++ b/tools/objtool/Documentation/stack-validation.txt @@ -323,6 +323,14 @@ they mean, and suggestions for how to fix them. The easiest way to enforce this is to ensure alternatives do not contain any ORC entries, which in turn implies the above constraint. +11. file.o: warning: unannotated intra-function call + + This warning means that a direct call is done to a destination which + is not at the beginning of a function. If this is a legit call, you + can remove this warning by putting the ANNOTATE_INTRA_FUNCTION_CALL + directive right before the call. + + If the error doesn't seem to make sense, it could be a bug in objtool. Feel free to ask the objtool maintainer for help. diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index d7b5d10..4b504fc 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -496,6 +496,14 @@ int arch_decode_instruction(const struct elf *elf, const struct section *sec, case 0xe8: *type = INSN_CALL; + /* +* For the impact on the stack, a CALL behaves like +* a PUSH of an immediate value (the return address). +*/ + ADD_OP(op) { + op->src.type = OP_SRC_CONST; + op->dest.type = OP_DEST_PUSH; + } break; case 0xfc: diff --git a/tools/objtool/check.c b/tools/objtool/check.c index d822858..32dea5f 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -674,6 +674,16 @@ static int add_jump_destinations(struct objtool_file *file) return 0; } +static void remove_insn_ops(struct instruction *insn) +{ + struct stack_op *op, *tmp; + + list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) { + list_del(&op->list); + free(op); + } +} + /* * Find the destination instructions for all calls. */ @@ -699,10 +709,7 @@ static int add_call_destinations(struct objtool_file *file) continue; if (!insn->call_dest) { - WARN_FUNC("unsupported intra-function call", - insn->sec, insn->offset); - if (retpoline) - WARN("If this is a retpoline, please patch it in with alternatives and annotate it with ANNOTATE_NOSPEC_ALTERNATIVE."); + WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset); return -1;
[tip: objtool/core] objtool: Uniquely identify alternative instruction groups
The following commit has been merged into the objtool/core branch of tip: Commit-ID: 13fab06d9a3ad3afdfd51c7f8f87f2ae28444648 Gitweb: https://git.kernel.org/tip/13fab06d9a3ad3afdfd51c7f8f87f2ae28444648 Author:Alexandre Chartre AuthorDate:Tue, 14 Apr 2020 12:36:11 +02:00 Committer: Peter Zijlstra CommitterDate: Thu, 30 Apr 2020 20:14:31 +02:00 objtool: Uniquely identify alternative instruction groups Assign a unique identifier to every alternative instruction group in order to be able to tell which instructions belong to what alternative. [peterz: extracted from a larger patch] Signed-off-by: Alexandre Chartre Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes --- tools/objtool/check.c | 6 +- tools/objtool/check.h | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index cc52da6..4da6bfb 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -752,7 +752,9 @@ static int handle_group_alt(struct objtool_file *file, struct instruction *orig_insn, struct instruction **new_insn) { + static unsigned int alt_group_next_index = 1; struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump = NULL; + unsigned int alt_group = alt_group_next_index++; unsigned long dest_off; last_orig_insn = NULL; @@ -761,7 +763,7 @@ static int handle_group_alt(struct objtool_file *file, if (insn->offset >= special_alt->orig_off + special_alt->orig_len) break; - insn->alt_group = true; + insn->alt_group = alt_group; last_orig_insn = insn; } @@ -795,6 +797,7 @@ static int handle_group_alt(struct objtool_file *file, } last_new_insn = NULL; + alt_group = alt_group_next_index++; insn = *new_insn; sec_for_each_insn_from(file, insn) { if (insn->offset >= special_alt->new_off + special_alt->new_len) @@ -804,6 +807,7 @@ static int handle_group_alt(struct objtool_file *file, insn->ignore = orig_insn->ignore_alts; insn->func = orig_insn->func; + insn->alt_group = alt_group; /* * Since alternative replacement code is copy/pasted by the diff --git a/tools/objtool/check.h b/tools/objtool/check.h index 12a9660..2428022 100644 --- a/tools/objtool/check.h +++ b/tools/objtool/check.h @@ -30,12 +30,13 @@ struct instruction { unsigned int len; enum insn_type type; unsigned long immediate; - bool alt_group, dead_end, ignore, ignore_alts; + bool dead_end, ignore, ignore_alts; bool hint; bool retpoline_safe; s8 instr; u8 visited; u8 ret_offset; + int alt_group; struct symbol *call_dest; struct instruction *jump_dest; struct instruction *first_jump_src;
[tip: objtool/core] objtool: is_fentry_call() crashes if call has no destination
The following commit has been merged into the objtool/core branch of tip: Commit-ID: 87cf61fe848ca8ddf091548671e168f52e8a718e Gitweb: https://git.kernel.org/tip/87cf61fe848ca8ddf091548671e168f52e8a718e Author:Alexandre Chartre AuthorDate:Tue, 14 Apr 2020 12:36:10 +02:00 Committer: Peter Zijlstra CommitterDate: Thu, 30 Apr 2020 20:14:32 +02:00 objtool: is_fentry_call() crashes if call has no destination Fix is_fentry_call() so that it works if a call has no destination set (call_dest). This needs to be done in order to support intra- function calls. Signed-off-by: Alexandre Chartre Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200414103618.12657-2-alexandre.char...@oracle.com --- tools/objtool/check.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index fa9bf36..8af8de2 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1484,7 +1484,7 @@ static int decode_sections(struct objtool_file *file) static bool is_fentry_call(struct instruction *insn) { - if (insn->type == INSN_CALL && + if (insn->type == INSN_CALL && insn->call_dest && insn->call_dest->type == STT_NOTYPE && !strcmp(insn->call_dest->name, "__fentry__")) return true;
[tip: objtool/core] objtool: UNWIND_HINT_RET_OFFSET should not check registers
The following commit has been merged into the objtool/core branch of tip: Commit-ID: c721b3f80faebc7891211fa82de303eebadfed15 Gitweb: https://git.kernel.org/tip/c721b3f80faebc7891211fa82de303eebadfed15 Author:Alexandre Chartre AuthorDate:Tue, 07 Apr 2020 09:31:35 +02:00 Committer: Peter Zijlstra CommitterDate: Thu, 30 Apr 2020 20:14:32 +02:00 objtool: UNWIND_HINT_RET_OFFSET should not check registers UNWIND_HINT_RET_OFFSET will adjust a modified stack. However if a callee-saved register was pushed on the stack then the stack frame will still appear modified. So stop checking registers when UNWIND_HINT_RET_OFFSET is used. Signed-off-by: Alexandre Chartre Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Miroslav Benes Acked-by: Josh Poimboeuf Link: https://lkml.kernel.org/r/20200407073142.20659-3-alexandre.char...@oracle.com --- tools/objtool/check.c | 8 1 file changed, 8 insertions(+) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 8af8de2..068897d 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1507,6 +1507,14 @@ static bool has_modified_stack_frame(struct instruction *insn, struct insn_state if (cfi->stack_size != initial_func_cfi.cfa.offset + ret_offset) return true; + /* +* If there is a ret offset hint then don't check registers +* because a callee-saved register might have been pushed on +* the stack. +*/ + if (ret_offset) + return false; + for (i = 0; i < CFI_NUM_REGS; i++) { if (cfi->regs[i].base != initial_func_cfi.regs[i].base || cfi->regs[i].offset != initial_func_cfi.regs[i].offset)