Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f8f25fde authored by Alexandre Chartre's avatar Alexandre Chartre Committed by Greg Kroah-Hartman
Browse files

objtool: Add support for intra-function calls



commit 8aa8eb2a8f5b3305a95f39957dd2b715fa668e21 upstream.

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: default avatarAlexandre Chartre <alexandre.chartre@oracle.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarMiroslav Benes <mbenes@suse.cz>
Acked-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20200414103618.12657-4-alexandre.chartre@oracle.com


Signed-off-by: default avatarRui Qi <qirui.001@bytedance.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent eafb2936
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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 */

+8 −0
Original line number Diff line number Diff line
@@ -290,6 +290,14 @@ they mean, and suggestions for how to fix them.
      https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646


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.

+6 −0
Original line number Diff line number Diff line
@@ -437,6 +437,12 @@ int arch_decode_instruction(struct elf *elf, 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).
		 */
			op->src.type = OP_SRC_CONST;
			op->dest.type = OP_DEST_PUSH;
		break;

	case 0xfc:
+58 −4
Original line number Diff line number Diff line
@@ -645,6 +645,7 @@ static int add_jump_destinations(struct objtool_file *file)
	return 0;
}


/*
 * Find the destination instructions for all calls.
 */
@@ -666,10 +667,7 @@ static int add_call_destinations(struct objtool_file *file)
								dest_off);

			if (!insn->call_dest && !insn->ignore) {
				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;
			}

@@ -1291,6 +1289,58 @@ static int read_retpoline_hints(struct objtool_file *file)
	return 0;
}


static int read_intra_function_calls(struct objtool_file *file)
{
	struct instruction *insn;
	struct section *sec;
	struct rela *rela;

	sec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls");
	if (!sec)
		return 0;

	list_for_each_entry(rela, &sec->rela_list, list) {
		unsigned long dest_off;

		if (rela->sym->type != STT_SECTION) {
			WARN("unexpected relocation symbol type in %s",
			     sec->name);
			return -1;
		}

		insn = find_insn(file, rela->sym->sec, rela->addend);
		if (!insn) {
			WARN("bad .discard.intra_function_call entry");
			return -1;
		}

		if (insn->type != INSN_CALL) {
			WARN_FUNC("intra_function_call not a direct call",
				  insn->sec, insn->offset);
			return -1;
		}

		/*
		 * Treat intra-function CALLs as JMPs, but with a stack_op.
		 * See add_call_destinations(), which strips stack_ops from
		 * normal CALLs.
		 */
		insn->type = INSN_JUMP_UNCONDITIONAL;

		dest_off = insn->offset + insn->len + insn->immediate;
		insn->jump_dest = find_insn(file, insn->sec, dest_off);
		if (!insn->jump_dest) {
			WARN_FUNC("can't find call dest at %s+0x%lx",
				  insn->sec, insn->offset,
				  insn->sec->name, dest_off);
			return -1;
		}
	}

	return 0;
}

static void mark_rodata(struct objtool_file *file)
{
	struct section *sec;
@@ -1346,6 +1396,10 @@ static int decode_sections(struct objtool_file *file)
	if (ret)
		return ret;

	ret = read_intra_function_calls(file);
	if (ret)
		return ret;

	ret = add_call_destinations(file);
	if (ret)
		return ret;