Commit f56dae88 authored by Peter Zijlstra's avatar Peter Zijlstra
Browse files

objtool: Handle __sanitize_cov*() tail calls



Turns out the compilers also generate tail calls to __sanitize_cov*(),
make sure to also patch those out in noinstr code.

Fixes: 0f1441b4 ("objtool: Fix noinstr vs KCOV")
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: default avatarMarco Elver <elver@google.com>
Link: https://lore.kernel.org/r/20210624095147.818783799@infradead.org
parent 8b946cc3
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -659,6 +659,26 @@ const char *arch_nop_insn(int len)
	return nops[len-1];
}

#define BYTE_RET	0xC3

const char *arch_ret_insn(int len)
{
	static const char ret[5][5] = {
		{ BYTE_RET },
		{ BYTE_RET, BYTES_NOP1 },
		{ BYTE_RET, BYTES_NOP2 },
		{ BYTE_RET, BYTES_NOP3 },
		{ BYTE_RET, BYTES_NOP4 },
	};

	if (len < 1 || len > 5) {
		WARN("invalid RET size: %d\n", len);
		return NULL;
	}

	return ret[len-1];
}

/* asm/alternative.h ? */

#define ALTINSTR_FLAG_INV	(1 << 15)
+84 −74
Original line number Diff line number Diff line
@@ -904,6 +904,79 @@ static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *i
	return insn->reloc;
}

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);
	}
}

static void add_call_dest(struct objtool_file *file, struct instruction *insn,
			  struct symbol *dest, bool sibling)
{
	struct reloc *reloc = insn_reloc(file, insn);

	insn->call_dest = dest;
	if (!dest)
		return;

	if (insn->call_dest->static_call_tramp) {
		list_add_tail(&insn->call_node,
			      &file->static_call_list);
	}

	/*
	 * Many compilers cannot disable KCOV with a function attribute
	 * so they need a little help, NOP out any KCOV calls from noinstr
	 * text.
	 */
	if (insn->sec->noinstr &&
	    !strncmp(insn->call_dest->name, "__sanitizer_cov_", 16)) {
		if (reloc) {
			reloc->type = R_NONE;
			elf_write_reloc(file->elf, reloc);
		}

		elf_write_insn(file->elf, insn->sec,
			       insn->offset, insn->len,
			       sibling ? arch_ret_insn(insn->len)
			               : arch_nop_insn(insn->len));

		insn->type = sibling ? INSN_RETURN : INSN_NOP;
	}

	if (mcount && !strcmp(insn->call_dest->name, "__fentry__")) {
		if (sibling)
			WARN_FUNC("Tail call to __fentry__ !?!?", insn->sec, insn->offset);

		if (reloc) {
			reloc->type = R_NONE;
			elf_write_reloc(file->elf, reloc);
		}

		elf_write_insn(file->elf, insn->sec,
			       insn->offset, insn->len,
			       arch_nop_insn(insn->len));

		insn->type = INSN_NOP;

		list_add_tail(&insn->mcount_loc_node,
			      &file->mcount_loc_list);
	}

	/*
	 * Whatever stack impact regular CALLs have, should be undone
	 * by the RETURN of the called function.
	 *
	 * Annotated intra-function calls retain the stack_ops but
	 * are converted to JUMP, see read_intra_function_calls().
	 */
	remove_insn_ops(insn);
}

/*
 * Find the destination instructions for all jumps.
 */
@@ -942,11 +1015,7 @@ static int add_jump_destinations(struct objtool_file *file)
			continue;
		} else if (insn->func) {
			/* internal or external sibling call (with reloc) */
			insn->call_dest = reloc->sym;
			if (insn->call_dest->static_call_tramp) {
				list_add_tail(&insn->call_node,
					      &file->static_call_list);
			}
			add_call_dest(file, insn, reloc->sym, true);
			continue;
		} else if (reloc->sym->sec->idx) {
			dest_sec = reloc->sym->sec;
@@ -1002,13 +1071,8 @@ static int add_jump_destinations(struct objtool_file *file)

			} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
				   insn->jump_dest->offset == insn->jump_dest->func->offset) {

				/* internal sibling call (without reloc) */
				insn->call_dest = insn->jump_dest->func;
				if (insn->call_dest->static_call_tramp) {
					list_add_tail(&insn->call_node,
						      &file->static_call_list);
				}
				add_call_dest(file, insn, insn->jump_dest->func, true);
			}
		}
	}
@@ -1016,16 +1080,6 @@ 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);
	}
}

static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
{
	struct symbol *call_dest;
@@ -1044,6 +1098,7 @@ static int add_call_destinations(struct objtool_file *file)
{
	struct instruction *insn;
	unsigned long dest_off;
	struct symbol *dest;
	struct reloc *reloc;

	for_each_insn(file, insn) {
@@ -1053,7 +1108,9 @@ static int add_call_destinations(struct objtool_file *file)
		reloc = insn_reloc(file, insn);
		if (!reloc) {
			dest_off = arch_jump_destination(insn);
			insn->call_dest = find_call_destination(insn->sec, dest_off);
			dest = find_call_destination(insn->sec, dest_off);

			add_call_dest(file, insn, dest, false);

			if (insn->ignore)
				continue;
@@ -1071,9 +1128,8 @@ static int add_call_destinations(struct objtool_file *file)

		} else if (reloc->sym->type == STT_SECTION) {
			dest_off = arch_dest_reloc_offset(reloc->addend);
			insn->call_dest = find_call_destination(reloc->sym->sec,
								dest_off);
			if (!insn->call_dest) {
			dest = find_call_destination(reloc->sym->sec, dest_off);
			if (!dest) {
				WARN_FUNC("can't find call dest symbol at %s+0x%lx",
					  insn->sec, insn->offset,
					  reloc->sym->sec->name,
@@ -1081,6 +1137,8 @@ static int add_call_destinations(struct objtool_file *file)
				return -1;
			}

			add_call_dest(file, insn, dest, false);

		} else if (arch_is_retpoline(reloc->sym)) {
			/*
			 * Retpoline calls are really dynamic calls in
@@ -1096,55 +1154,7 @@ static int add_call_destinations(struct objtool_file *file)
			continue;

		} else
			insn->call_dest = reloc->sym;

		if (insn->call_dest && insn->call_dest->static_call_tramp) {
			list_add_tail(&insn->call_node,
				      &file->static_call_list);
		}

		/*
		 * Many compilers cannot disable KCOV with a function attribute
		 * so they need a little help, NOP out any KCOV calls from noinstr
		 * text.
		 */
		if (insn->sec->noinstr &&
		    !strncmp(insn->call_dest->name, "__sanitizer_cov_", 16)) {
			if (reloc) {
				reloc->type = R_NONE;
				elf_write_reloc(file->elf, reloc);
			}

			elf_write_insn(file->elf, insn->sec,
				       insn->offset, insn->len,
				       arch_nop_insn(insn->len));
			insn->type = INSN_NOP;
		}

		if (mcount && !strcmp(insn->call_dest->name, "__fentry__")) {
			if (reloc) {
				reloc->type = R_NONE;
				elf_write_reloc(file->elf, reloc);
			}

			elf_write_insn(file->elf, insn->sec,
				       insn->offset, insn->len,
				       arch_nop_insn(insn->len));

			insn->type = INSN_NOP;

			list_add_tail(&insn->mcount_loc_node,
				      &file->mcount_loc_list);
		}

		/*
		 * Whatever stack impact regular CALLs have, should be undone
		 * by the RETURN of the called function.
		 *
		 * Annotated intra-function calls retain the stack_ops but
		 * are converted to JUMP, see read_intra_function_calls().
		 */
		remove_insn_ops(insn);
			add_call_dest(file, insn, reloc->sym, false);
	}

	return 0;
+1 −0
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ unsigned long arch_jump_destination(struct instruction *insn);
unsigned long arch_dest_reloc_offset(int addend);

const char *arch_nop_insn(int len);
const char *arch_ret_insn(int len);

int arch_decode_hint_reg(u8 sp_reg, int *base);