Commit f56dae88 authored by Peter Zijlstra's avatar Peter Zijlstra

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
...@@ -659,6 +659,26 @@ const char *arch_nop_insn(int len) ...@@ -659,6 +659,26 @@ const char *arch_nop_insn(int len)
return nops[len-1]; 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 ? */ /* asm/alternative.h ? */
#define ALTINSTR_FLAG_INV (1 << 15) #define ALTINSTR_FLAG_INV (1 << 15)
......
...@@ -904,6 +904,79 @@ static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *i ...@@ -904,6 +904,79 @@ static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *i
return insn->reloc; 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. * Find the destination instructions for all jumps.
*/ */
...@@ -942,11 +1015,7 @@ static int add_jump_destinations(struct objtool_file *file) ...@@ -942,11 +1015,7 @@ static int add_jump_destinations(struct objtool_file *file)
continue; continue;
} else if (insn->func) { } else if (insn->func) {
/* internal or external sibling call (with reloc) */ /* internal or external sibling call (with reloc) */
insn->call_dest = reloc->sym; add_call_dest(file, insn, reloc->sym, true);
if (insn->call_dest->static_call_tramp) {
list_add_tail(&insn->call_node,
&file->static_call_list);
}
continue; continue;
} else if (reloc->sym->sec->idx) { } else if (reloc->sym->sec->idx) {
dest_sec = reloc->sym->sec; dest_sec = reloc->sym->sec;
...@@ -1002,13 +1071,8 @@ static int add_jump_destinations(struct objtool_file *file) ...@@ -1002,13 +1071,8 @@ static int add_jump_destinations(struct objtool_file *file)
} else if (insn->jump_dest->func->pfunc != insn->func->pfunc && } else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
insn->jump_dest->offset == insn->jump_dest->func->offset) { insn->jump_dest->offset == insn->jump_dest->func->offset) {
/* internal sibling call (without reloc) */ /* internal sibling call (without reloc) */
insn->call_dest = insn->jump_dest->func; add_call_dest(file, insn, insn->jump_dest->func, true);
if (insn->call_dest->static_call_tramp) {
list_add_tail(&insn->call_node,
&file->static_call_list);
}
} }
} }
} }
...@@ -1016,16 +1080,6 @@ static int add_jump_destinations(struct objtool_file *file) ...@@ -1016,16 +1080,6 @@ static int add_jump_destinations(struct objtool_file *file)
return 0; 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) static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
{ {
struct symbol *call_dest; struct symbol *call_dest;
...@@ -1044,6 +1098,7 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -1044,6 +1098,7 @@ static int add_call_destinations(struct objtool_file *file)
{ {
struct instruction *insn; struct instruction *insn;
unsigned long dest_off; unsigned long dest_off;
struct symbol *dest;
struct reloc *reloc; struct reloc *reloc;
for_each_insn(file, insn) { for_each_insn(file, insn) {
...@@ -1053,7 +1108,9 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -1053,7 +1108,9 @@ static int add_call_destinations(struct objtool_file *file)
reloc = insn_reloc(file, insn); reloc = insn_reloc(file, insn);
if (!reloc) { if (!reloc) {
dest_off = arch_jump_destination(insn); 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) if (insn->ignore)
continue; continue;
...@@ -1071,9 +1128,8 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -1071,9 +1128,8 @@ static int add_call_destinations(struct objtool_file *file)
} else if (reloc->sym->type == STT_SECTION) { } else if (reloc->sym->type == STT_SECTION) {
dest_off = arch_dest_reloc_offset(reloc->addend); dest_off = arch_dest_reloc_offset(reloc->addend);
insn->call_dest = find_call_destination(reloc->sym->sec, dest = find_call_destination(reloc->sym->sec, dest_off);
dest_off); if (!dest) {
if (!insn->call_dest) {
WARN_FUNC("can't find call dest symbol at %s+0x%lx", WARN_FUNC("can't find call dest symbol at %s+0x%lx",
insn->sec, insn->offset, insn->sec, insn->offset,
reloc->sym->sec->name, reloc->sym->sec->name,
...@@ -1081,6 +1137,8 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -1081,6 +1137,8 @@ static int add_call_destinations(struct objtool_file *file)
return -1; return -1;
} }
add_call_dest(file, insn, dest, false);
} else if (arch_is_retpoline(reloc->sym)) { } else if (arch_is_retpoline(reloc->sym)) {
/* /*
* Retpoline calls are really dynamic calls in * Retpoline calls are really dynamic calls in
...@@ -1096,55 +1154,7 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -1096,55 +1154,7 @@ static int add_call_destinations(struct objtool_file *file)
continue; continue;
} else } else
insn->call_dest = reloc->sym; add_call_dest(file, insn, reloc->sym, false);
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);
} }
return 0; return 0;
......
...@@ -82,6 +82,7 @@ unsigned long arch_jump_destination(struct instruction *insn); ...@@ -82,6 +82,7 @@ unsigned long arch_jump_destination(struct instruction *insn);
unsigned long arch_dest_reloc_offset(int addend); unsigned long arch_dest_reloc_offset(int addend);
const char *arch_nop_insn(int len); const char *arch_nop_insn(int len);
const char *arch_ret_insn(int len);
int arch_decode_hint_reg(u8 sp_reg, int *base); int arch_decode_hint_reg(u8 sp_reg, int *base);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment