objtool: Handle __sanitize_cov*() tail calls
authorPeter Zijlstra <peterz@infradead.org>
Thu, 24 Jun 2021 09:41:02 +0000 (11:41 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 18 Nov 2021 18:16:23 +0000 (19:16 +0100)
[ Upstream commit f56dae88a81fded66adf2bea9922d1d98d1da14f ]

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

Fixes: 0f1441b44e82 ("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>
tools/objtool/arch/x86/decode.c
tools/objtool/check.c
tools/objtool/include/objtool/arch.h

index 0893436..77b5160 100644 (file)
@@ -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)
index 8bffc00..8198294 100644 (file)
@@ -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;
index 062bb6e..478e054 100644 (file)
@@ -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);