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: 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
This commit is contained in:
Родитель
8b946cc38e
Коммит
f56dae88a8
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче