The logic is a bit weird - simplify it. Signed-off-by: Josh Poimboeuf <jpoimboe@xxxxxxxxxx> --- tools/objtool/check.c | 239 ++++++++++++++++++++---------------------- 1 file changed, 114 insertions(+), 125 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 9212b5edffc8..6490bc939892 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1479,9 +1479,14 @@ static void add_return_call(struct objtool_file *file, struct instruction *insn, } static bool is_first_func_insn(struct objtool_file *file, - struct instruction *insn, struct symbol *sym) + struct instruction *insn) { - if (insn->offset == sym->offset) + struct symbol *func = insn_func(insn); + + if (!func) + return false; + + if (insn->offset == func->offset) return true; /* Allow direct CALL/JMP past ENDBR */ @@ -1489,50 +1494,30 @@ static bool is_first_func_insn(struct objtool_file *file, struct instruction *prev = prev_insn_same_sym(file, insn); if (prev && prev->type == INSN_ENDBR && - insn->offset == sym->offset + prev->len) + insn->offset == func->offset + prev->len) return true; } return false; } -/* - * A sibling call is a tail-call to another symbol -- to differentiate from a - * recursive tail-call which is to the same symbol. - */ -static bool jump_is_sibling_call(struct objtool_file *file, - struct instruction *from, struct instruction *to) -{ - struct symbol *fs = from->sym; - struct symbol *ts = to->sym; - - /* Not a sibling call if from/to a symbol hole */ - if (!fs || !ts) - return false; - - /* Not a sibling call if not targeting the start of a symbol. */ - if (!is_first_func_insn(file, to, ts)) - return false; - - /* Disallow sibling calls into STT_NOTYPE */ - if (is_notype_symbol(ts)) - return false; - - /* Must not be self to be a sibling */ - return fs->pfunc != ts->pfunc; -} - /* * Find the destination instructions for all jumps. */ static int add_jump_destinations(struct objtool_file *file) { - struct instruction *insn, *jump_dest; + struct instruction *insn; struct reloc *reloc; - struct section *dest_sec; - unsigned long dest_off; for_each_insn(file, insn) { + struct instruction *dest_insn; + struct section *dest_sec = NULL; + struct symbol *dest_sym = NULL; + unsigned long dest_off; + + if (!is_static_jump(insn)) + continue; + if (insn->jump_dest) { /* * handle_group_alt() may have previously set @@ -1540,114 +1525,118 @@ static int add_jump_destinations(struct objtool_file *file) */ continue; } - if (!is_static_jump(insn)) - continue; reloc = insn_reloc(file, insn); if (!reloc) { dest_sec = insn->sec; dest_off = arch_jump_destination(insn); - } else if (is_section_symbol(reloc->sym)) { - dest_sec = reloc->sym->sec; - dest_off = arch_dest_reloc_offset(reloc_addend(reloc)); - } else if (reloc->sym->retpoline_thunk) { - add_retpoline_call(file, insn); - continue; - } else if (reloc->sym->return_thunk) { - add_return_call(file, insn, true); - continue; - } else if (insn_func(insn)) { - /* - * External sibling call or internal sibling call with - * STT_FUNC reloc. - */ - add_call_dest(file, insn, reloc->sym, true); - continue; - } else if (reloc->sym->sec->idx) { + } else if (sym_has_section(reloc->sym)) { dest_sec = reloc->sym->sec; dest_off = reloc->sym->sym.st_value + arch_dest_reloc_offset(reloc_addend(reloc)); } else { - /* non-func asm code jumping to another file */ + /* External symbol (UNDEF) */ + dest_sec = NULL; + dest_sym = reloc->sym; + } + + if (!dest_sym) { + dest_insn = find_insn(file, dest_sec, dest_off); + + if (!dest_insn) { + struct symbol *sym = find_symbol_by_offset(dest_sec, dest_off); + + /* + * This is a special case for retbleed_untrain_ret(). + * It jumps to __x86_return_thunk(), but objtool + * can't find the thunk's starting RET + * instruction, because the RET is also in the + * middle of another instruction. Objtool only + * knows about the outer instruction. + */ + if (sym && sym->embedded_insn) { + add_return_call(file, insn, false); + continue; + } + + WARN_INSN(insn, "can't find jump dest instruction at %s+0x%lx", + dest_sec->name, dest_off); + return -1; + } + + dest_sym = dest_insn->sym; + if (!dest_sym) + goto set_jump_dest; + } + + if (dest_sym->retpoline_thunk) { + add_retpoline_call(file, insn); continue; } - jump_dest = find_insn(file, dest_sec, dest_off); - if (!jump_dest) { - struct symbol *sym = find_symbol_by_offset(dest_sec, dest_off); - - /* - * This is a special case for retbleed_untrain_ret(). - * It jumps to __x86_return_thunk(), but objtool - * can't find the thunk's starting RET - * instruction, because the RET is also in the - * middle of another instruction. Objtool only - * knows about the outer instruction. - */ - if (sym && sym->embedded_insn) { - add_return_call(file, insn, false); - continue; - } - - WARN_INSN(insn, "can't find jump dest instruction at %s+0x%lx", - dest_sec->name, dest_off); - return -1; - } - - /* - * An intra-TU jump in retpoline.o might not have a relocation - * for its jump dest, in which case the above - * add_{retpoline,return}_call() didn't happen. - */ - if (jump_dest->sym && jump_dest->offset == jump_dest->sym->offset) { - if (jump_dest->sym->retpoline_thunk) { - add_retpoline_call(file, insn); - continue; - } - if (jump_dest->sym->return_thunk) { - add_return_call(file, insn, true); - continue; - } - } - - /* - * Cross-function jump. - */ - if (insn_func(insn) && insn_func(jump_dest) && - insn_func(insn) != insn_func(jump_dest)) { - - /* - * For GCC 8+, create parent/child links for any cold - * subfunctions. This is _mostly_ redundant with a - * similar initialization in read_symbols(). - * - * If a function has aliases, we want the *first* such - * function in the symbol table to be the subfunction's - * parent. In that case we overwrite the - * initialization done in read_symbols(). - * - * However this code can't completely replace the - * read_symbols() code because this doesn't detect the - * case where the parent function's only reference to a - * subfunction is through a jump table. - */ - if (!strstr(insn_func(insn)->name, ".cold") && - strstr(insn_func(jump_dest)->name, ".cold")) { - insn_func(insn)->cfunc = insn_func(jump_dest); - insn_func(jump_dest)->pfunc = insn_func(insn); - } - } - - if (jump_is_sibling_call(file, insn, jump_dest)) { - /* - * Internal sibling call without reloc or with - * STT_SECTION reloc. - */ - add_call_dest(file, insn, insn_func(jump_dest), true); + if (dest_sym->return_thunk) { + add_return_call(file, insn, true); continue; } - insn->jump_dest = jump_dest; + if (!dest_sec) { + /* External symbol */ + if (insn_func(insn)) { + /* External sibling call */ + add_call_dest(file, insn, dest_sym, true); + continue; + } + + /* Non-func asm code jumping to external symbol */ + continue; + } + + if (!insn_func(insn) && insn_func(dest_insn)) { + /* + * Switching from non-func asm to func - force + * validate_branch() to stop. + */ + continue; + } + + if (!insn_func(insn) || !insn_func(dest_insn) || + insn_func(insn) == insn_func(dest_insn)) + goto set_jump_dest; + + /* + * Internal cross-function jump. + */ + + /* + * For GCC 8+, create parent/child links for any cold + * subfunctions. This is _mostly_ redundant with a + * similar initialization in read_symbols(). + * + * If a function has aliases, we want the *first* such + * function in the symbol table to be the subfunction's + * parent. In that case we overwrite the + * initialization done in read_symbols(). + * + * However this code can't completely replace the + * read_symbols() code because this doesn't detect the + * case where the parent function's only reference to a + * subfunction is through a jump table. + */ + if (!strstr(insn_func(insn)->name, ".cold") && + strstr(insn_func(dest_insn)->name, ".cold")) { + insn_func(insn)->cfunc = insn_func(dest_insn); + insn_func(dest_insn)->pfunc = insn_func(insn); + goto set_jump_dest; + } + + if (is_first_func_insn(file, dest_insn)) { + /* Internal sibling call */ + add_call_dest(file, insn, insn_func(dest_insn), true); + continue; + } + +set_jump_dest: + insn->jump_dest = dest_insn; } return 0; -- 2.45.2