objtool: Keep track of retpoline call sites
[linux-2.6-microblaze.git] / tools / objtool / check.c
index d45f018..77074db 100644 (file)
@@ -433,8 +433,7 @@ reachable:
 
 static int create_static_call_sections(struct objtool_file *file)
 {
-       struct section *sec, *reloc_sec;
-       struct reloc *reloc;
+       struct section *sec;
        struct static_call_site *site;
        struct instruction *insn;
        struct symbol *key_sym;
@@ -452,7 +451,7 @@ static int create_static_call_sections(struct objtool_file *file)
                return 0;
 
        idx = 0;
-       list_for_each_entry(insn, &file->static_call_list, static_call_node)
+       list_for_each_entry(insn, &file->static_call_list, call_node)
                idx++;
 
        sec = elf_create_section(file->elf, ".static_call_sites", SHF_WRITE,
@@ -460,36 +459,18 @@ static int create_static_call_sections(struct objtool_file *file)
        if (!sec)
                return -1;
 
-       reloc_sec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
-       if (!reloc_sec)
-               return -1;
-
        idx = 0;
-       list_for_each_entry(insn, &file->static_call_list, static_call_node) {
+       list_for_each_entry(insn, &file->static_call_list, call_node) {
 
                site = (struct static_call_site *)sec->data->d_buf + idx;
                memset(site, 0, sizeof(struct static_call_site));
 
                /* populate reloc for 'addr' */
-               reloc = malloc(sizeof(*reloc));
-
-               if (!reloc) {
-                       perror("malloc");
+               if (elf_add_reloc_to_insn(file->elf, sec,
+                                         idx * sizeof(struct static_call_site),
+                                         R_X86_64_PC32,
+                                         insn->sec, insn->offset))
                        return -1;
-               }
-               memset(reloc, 0, sizeof(*reloc));
-
-               insn_to_reloc_sym_addend(insn->sec, insn->offset, reloc);
-               if (!reloc->sym) {
-                       WARN_FUNC("static call tramp: missing containing symbol",
-                                 insn->sec, insn->offset);
-                       return -1;
-               }
-
-               reloc->type = R_X86_64_PC32;
-               reloc->offset = idx * sizeof(struct static_call_site);
-               reloc->sec = reloc_sec;
-               elf_add_reloc(file->elf, reloc);
 
                /* find key symbol */
                key_name = strdup(insn->call_dest->name);
@@ -526,32 +507,21 @@ static int create_static_call_sections(struct objtool_file *file)
                free(key_name);
 
                /* populate reloc for 'key' */
-               reloc = malloc(sizeof(*reloc));
-               if (!reloc) {
-                       perror("malloc");
+               if (elf_add_reloc(file->elf, sec,
+                                 idx * sizeof(struct static_call_site) + 4,
+                                 R_X86_64_PC32, key_sym,
+                                 is_sibling_call(insn) * STATIC_CALL_SITE_TAIL))
                        return -1;
-               }
-               memset(reloc, 0, sizeof(*reloc));
-               reloc->sym = key_sym;
-               reloc->addend = is_sibling_call(insn) ? STATIC_CALL_SITE_TAIL : 0;
-               reloc->type = R_X86_64_PC32;
-               reloc->offset = idx * sizeof(struct static_call_site) + 4;
-               reloc->sec = reloc_sec;
-               elf_add_reloc(file->elf, reloc);
 
                idx++;
        }
 
-       if (elf_rebuild_reloc_section(file->elf, reloc_sec))
-               return -1;
-
        return 0;
 }
 
 static int create_mcount_loc_sections(struct objtool_file *file)
 {
-       struct section *sec, *reloc_sec;
-       struct reloc *reloc;
+       struct section *sec;
        unsigned long *loc;
        struct instruction *insn;
        int idx;
@@ -574,49 +544,21 @@ static int create_mcount_loc_sections(struct objtool_file *file)
        if (!sec)
                return -1;
 
-       reloc_sec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
-       if (!reloc_sec)
-               return -1;
-
        idx = 0;
        list_for_each_entry(insn, &file->mcount_loc_list, mcount_loc_node) {
 
                loc = (unsigned long *)sec->data->d_buf + idx;
                memset(loc, 0, sizeof(unsigned long));
 
-               reloc = malloc(sizeof(*reloc));
-               if (!reloc) {
-                       perror("malloc");
+               if (elf_add_reloc_to_insn(file->elf, sec,
+                                         idx * sizeof(unsigned long),
+                                         R_X86_64_64,
+                                         insn->sec, insn->offset))
                        return -1;
-               }
-               memset(reloc, 0, sizeof(*reloc));
-
-               if (insn->sec->sym) {
-                       reloc->sym = insn->sec->sym;
-                       reloc->addend = insn->offset;
-               } else {
-                       reloc->sym = find_symbol_containing(insn->sec, insn->offset);
-
-                       if (!reloc->sym) {
-                               WARN("missing symbol for insn at offset 0x%lx\n",
-                                    insn->offset);
-                               return -1;
-                       }
-
-                       reloc->addend = insn->offset - reloc->sym->offset;
-               }
-
-               reloc->type = R_X86_64_64;
-               reloc->offset = idx * sizeof(unsigned long);
-               reloc->sec = reloc_sec;
-               elf_add_reloc(file->elf, reloc);
 
                idx++;
        }
 
-       if (elf_rebuild_reloc_section(file->elf, reloc_sec))
-               return -1;
-
        return 0;
 }
 
@@ -850,6 +792,11 @@ static int add_ignore_alternatives(struct objtool_file *file)
        return 0;
 }
 
+__weak bool arch_is_retpoline(struct symbol *sym)
+{
+       return false;
+}
+
 /*
  * Find the destination instructions for all jumps.
  */
@@ -872,7 +819,7 @@ static int add_jump_destinations(struct objtool_file *file)
                } else if (reloc->sym->type == STT_SECTION) {
                        dest_sec = reloc->sym->sec;
                        dest_off = arch_dest_reloc_offset(reloc->addend);
-               } else if (!strncmp(reloc->sym->name, "__x86_indirect_thunk_", 21)) {
+               } else if (arch_is_retpoline(reloc->sym)) {
                        /*
                         * Retpoline jumps are really dynamic jumps in
                         * disguise, so convert them accordingly.
@@ -882,13 +829,16 @@ static int add_jump_destinations(struct objtool_file *file)
                        else
                                insn->type = INSN_JUMP_DYNAMIC_CONDITIONAL;
 
+                       list_add_tail(&insn->call_node,
+                                     &file->retpoline_call_list);
+
                        insn->retpoline_safe = true;
                        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->static_call_node,
+                               list_add_tail(&insn->call_node,
                                              &file->static_call_list);
                        }
                        continue;
@@ -950,7 +900,7 @@ static int add_jump_destinations(struct objtool_file *file)
                                /* internal sibling call (without reloc) */
                                insn->call_dest = insn->jump_dest->func;
                                if (insn->call_dest->static_call_tramp) {
-                                       list_add_tail(&insn->static_call_node,
+                                       list_add_tail(&insn->call_node,
                                                      &file->static_call_list);
                                }
                        }
@@ -1025,9 +975,29 @@ static int add_call_destinations(struct objtool_file *file)
                                          dest_off);
                                return -1;
                        }
+
+               } else if (arch_is_retpoline(reloc->sym)) {
+                       /*
+                        * Retpoline calls are really dynamic calls in
+                        * disguise, so convert them accordingly.
+                        */
+                       insn->type = INSN_CALL_DYNAMIC;
+                       insn->retpoline_safe = true;
+
+                       list_add_tail(&insn->call_node,
+                                     &file->retpoline_call_list);
+
+                       remove_insn_ops(insn);
+                       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
@@ -1750,6 +1720,11 @@ static void mark_rodata(struct objtool_file *file)
        file->rodata = found;
 }
 
+__weak int arch_rewrite_retpolines(struct objtool_file *file)
+{
+       return 0;
+}
+
 static int decode_sections(struct objtool_file *file)
 {
        int ret;
@@ -1771,10 +1746,17 @@ static int decode_sections(struct objtool_file *file)
        if (ret)
                return ret;
 
+       /*
+        * Must be before add_{jump_call}_destination.
+        */
        ret = read_static_call_tramps(file);
        if (ret)
                return ret;
 
+       /*
+        * Must be before add_special_section_alts() as that depends on
+        * jump_dest being set.
+        */
        ret = add_jump_destinations(file);
        if (ret)
                return ret;
@@ -1783,6 +1765,10 @@ static int decode_sections(struct objtool_file *file)
        if (ret)
                return ret;
 
+       /*
+        * Must be before add_call_destination(); it changes INSN_CALL to
+        * INSN_JUMP.
+        */
        ret = read_intra_function_calls(file);
        if (ret)
                return ret;
@@ -1807,6 +1793,15 @@ static int decode_sections(struct objtool_file *file)
        if (ret)
                return ret;
 
+       /*
+        * Must be after add_special_section_alts(), since this will emit
+        * alternatives. Must be after add_{jump,call}_destination(), since
+        * those create the call insn lists.
+        */
+       ret = arch_rewrite_retpolines(file);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
@@ -2745,11 +2740,6 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
                        if (dead_end_function(file, insn->call_dest))
                                return 0;
 
-                       if (insn->type == INSN_CALL && insn->call_dest->static_call_tramp) {
-                               list_add_tail(&insn->static_call_node,
-                                             &file->static_call_list);
-                       }
-
                        break;
 
                case INSN_JUMP_CONDITIONAL: