mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-11-01 17:08:10 +00:00
objtool: Handle __sanitize_cov*() tail calls
[ Upstream commitf56dae88a8
] Turns out the compilers also generate tail calls to __sanitize_cov*(), make sure to also patch those out in noinstr code. Fixes:0f1441b44e
("objtool: Fix noinstr vs KCOV") Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Marco Elver <elver@google.com> Link: https://lore.kernel.org/r/20210624095147.818783799@infradead.org Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
c8a2b96d75
commit
d9b17a030a
3 changed files with 105 additions and 74 deletions
|
@ -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)
|
||||
|
|
|
@ -829,6 +829,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.
|
||||
*/
|
||||
|
@ -867,11 +940,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;
|
||||
|
@ -927,13 +996,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -941,16 +1005,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;
|
||||
|
@ -969,6 +1023,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) {
|
||||
|
@ -978,7 +1033,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;
|
||||
|
@ -996,9 +1053,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,
|
||||
|
@ -1006,6 +1062,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
|
||||
|
@ -1021,55 +1079,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;
|
||||
|
|
|
@ -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(struct instruction *insn, u8 sp_reg);
|
||||
|
||||
|
|
Loading…
Reference in a new issue