[RFC 14/31] objtool: Refactor add_jump_destinations()

Josh Poimboeuf
2024-09-03T04:00:36Z

The logic is a bit weird - simplify it.

Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
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