Commit a05de0a7 authored by Josh Poimboeuf's avatar Josh Poimboeuf
Browse files

objtool: Refactor add_jump_destinations()



The add_jump_destinations() logic is a bit weird and convoluted after
being incrementally tweaked over the years.  Refactor it to hopefully be
more logical and straightforward.

Acked-by: default avatarPetr Mladek <pmladek@suse.com>
Tested-by: default avatarJoe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@kernel.org>
parent 935c0b6a
Loading
Loading
Loading
Loading
+99 −113
Original line number Diff line number Diff line
@@ -1423,9 +1423,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 */
@@ -1433,51 +1438,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_sym(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 symbol *func = insn_func(insn);
		struct instruction *dest_insn;
		struct section *dest_sec;
		struct symbol *dest_sym;
		unsigned long dest_off;

		if (!is_static_jump(insn))
			continue;

		if (insn->jump_dest) {
			/*
@@ -1486,51 +1470,53 @@ 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_sec_sym(reloc->sym)) {
			dest_sec = reloc->sym->sec;
			dest_off = arch_insn_adjusted_addend(insn, reloc);
		} else if (reloc->sym->retpoline_thunk) {
			dest_sym = dest_sec->sym;
		} else {
			dest_sym = reloc->sym;
			if (is_undef_sym(dest_sym)) {
				if (dest_sym->retpoline_thunk) {
					if (add_retpoline_call(file, insn))
						return -1;
					continue;
		} else if (reloc->sym->return_thunk) {
				}

				if (dest_sym->return_thunk) {
					add_return_call(file, insn, true);
					continue;
		} else if (func) {
			/*
			 * External sibling call or internal sibling call with
			 * STT_FUNC reloc.
			 */
			if (add_call_dest(file, insn, reloc->sym, true))
				}

				/* External symbol */
				if (func) {
					/* External sibling call */
					if (add_call_dest(file, insn, dest_sym, true))
						return -1;
					continue;
		} else if (reloc->sym->sec->idx) {
			dest_sec = reloc->sym->sec;
			dest_off = reloc->sym->sym.st_value +
				   arch_insn_adjusted_addend(insn, reloc);
		} else {
			/* non-func asm code jumping to another file */
				}

				/* Non-func asm code jumping to external symbol */
				continue;
			}

		jump_dest = find_insn(file, dest_sec, dest_off);
		if (!jump_dest) {
			dest_sec = dest_sym->sec;
			dest_off = dest_sym->offset + arch_insn_adjusted_addend(insn, reloc);
		}

		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.
			 * retbleed_untrain_ret() 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);
@@ -1538,43 +1524,43 @@ static int add_jump_destinations(struct objtool_file *file)
			}

			/*
			 * GCOV/KCOV dead code can jump to the end of the
			 * function/section.
			 * GCOV/KCOV dead code can jump to the end of
			 * the function/section.
			 */
			if (file->ignore_unreachables && func &&
			    dest_sec == insn->sec &&
			    dest_off == func->offset + func->len)
				continue;

			ERROR_INSN(insn, "can't find jump dest instruction at %s+0x%lx",
				   dest_sec->name, dest_off);
			ERROR_INSN(insn, "can't find jump dest instruction at %s",
				   offstr(dest_sec, 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) {
		if (!dest_sym || is_sec_sym(dest_sym)) {
			dest_sym = dest_insn->sym;
			if (!dest_sym)
				goto set_jump_dest;
		}

		if (dest_sym->retpoline_thunk && dest_insn->offset == dest_sym->offset) {
			if (add_retpoline_call(file, insn))
				return -1;
			continue;
		}
			if (jump_dest->sym->return_thunk) {

		if (dest_sym->return_thunk && dest_insn->offset == dest_sym->offset) {
			add_return_call(file, insn, true);
			continue;
		}
		}

		if (!insn->sym || insn->sym == dest_insn->sym)
			goto set_jump_dest;

		/*
		 * Cross-function jump.
		 * Internal cross-function jump.
		 */

		if (func && insn_func(jump_dest) && !func->cold &&
		    insn_func(jump_dest)->cold) {

		/*
		 * For GCC 8+, create parent/child links for any cold
		 * subfunctions.  This is _mostly_ redundant with a
@@ -1590,21 +1576,21 @@ static int add_jump_destinations(struct objtool_file *file)
		 * case where the parent function's only reference to a
		 * subfunction is through a jump table.
		 */
			func->cfunc = insn_func(jump_dest);
			insn_func(jump_dest)->pfunc = func;
		if (func && dest_sym->cold) {
			func->cfunc = dest_sym;
			dest_sym->pfunc = func;
			goto set_jump_dest;
		}

		if (jump_is_sibling_call(file, insn, jump_dest)) {
			/*
			 * Internal sibling call without reloc or with
			 * STT_SECTION reloc.
			 */
			if (add_call_dest(file, insn, insn_func(jump_dest), true))
		if (is_first_func_insn(file, dest_insn)) {
			/* Internal sibling call */
			if (add_call_dest(file, insn, dest_sym, true))
				return -1;
			continue;
		}

		insn->jump_dest = jump_dest;
set_jump_dest:
		insn->jump_dest = dest_insn;
	}

	return 0;
+2 −2
Original line number Diff line number Diff line
@@ -181,9 +181,9 @@ static inline unsigned int elf_text_rela_type(struct elf *elf)
	return elf_addr_size(elf) == 4 ? R_TEXT32 : R_TEXT64;
}

static inline bool sym_has_sec(struct symbol *sym)
static inline bool is_undef_sym(struct symbol *sym)
{
	return sym->sec->idx;
	return !sym->sec->idx;
}

static inline bool is_null_sym(struct symbol *sym)