Merge tag 'gpio-v5.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[linux-2.6-microblaze.git] / tools / objtool / check.c
index 90a6689..c6ab445 100644 (file)
 #include "warn.h"
 #include "arch_elf.h"
 
+#include <linux/objtool.h>
 #include <linux/hashtable.h>
 #include <linux/kernel.h>
+#include <linux/static_call_types.h>
 
 #define FAKE_JUMP_OFFSET -1
 
-#define C_JUMP_TABLE_SECTION ".rodata..c_jump_table"
-
 struct alternative {
        struct list_head list;
        struct instruction *insn;
        bool skip_orig;
 };
 
-const char *objname;
 struct cfi_init_state initial_func_cfi;
 
 struct instruction *find_insn(struct objtool_file *file,
@@ -110,12 +109,6 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
        for (insn = next_insn_same_sec(file, insn); insn;               \
             insn = next_insn_same_sec(file, insn))
 
-static bool is_static_jump(struct instruction *insn)
-{
-       return insn->type == INSN_JUMP_CONDITIONAL ||
-              insn->type == INSN_JUMP_UNCONDITIONAL;
-}
-
 static bool is_sibling_call(struct instruction *insn)
 {
        /* An indirect jump is either a sibling call or a jump to a table. */
@@ -433,6 +426,103 @@ reachable:
        return 0;
 }
 
+static int create_static_call_sections(struct objtool_file *file)
+{
+       struct section *sec, *reloc_sec;
+       struct reloc *reloc;
+       struct static_call_site *site;
+       struct instruction *insn;
+       struct symbol *key_sym;
+       char *key_name, *tmp;
+       int idx;
+
+       sec = find_section_by_name(file->elf, ".static_call_sites");
+       if (sec) {
+               INIT_LIST_HEAD(&file->static_call_list);
+               WARN("file already has .static_call_sites section, skipping");
+               return 0;
+       }
+
+       if (list_empty(&file->static_call_list))
+               return 0;
+
+       idx = 0;
+       list_for_each_entry(insn, &file->static_call_list, static_call_node)
+               idx++;
+
+       sec = elf_create_section(file->elf, ".static_call_sites", SHF_WRITE,
+                                sizeof(struct static_call_site), idx);
+       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) {
+
+               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");
+                       return -1;
+               }
+               memset(reloc, 0, sizeof(*reloc));
+               reloc->sym = insn->sec->sym;
+               reloc->addend = insn->offset;
+               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);
+               if (!key_name) {
+                       perror("strdup");
+                       return -1;
+               }
+               if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR,
+                           STATIC_CALL_TRAMP_PREFIX_LEN)) {
+                       WARN("static_call: trampoline name malformed: %s", key_name);
+                       return -1;
+               }
+               tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN;
+               memcpy(tmp, STATIC_CALL_KEY_PREFIX_STR, STATIC_CALL_KEY_PREFIX_LEN);
+
+               key_sym = find_symbol_by_name(file->elf, tmp);
+               if (!key_sym) {
+                       WARN("static_call: can't find static_call_key symbol: %s", tmp);
+                       return -1;
+               }
+               free(key_name);
+
+               /* populate reloc for 'key' */
+               reloc = malloc(sizeof(*reloc));
+               if (!reloc) {
+                       perror("malloc");
+                       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;
+}
+
 /*
  * Warnings shouldn't be reported for ignored functions.
  */
@@ -493,6 +583,8 @@ static const char *uaccess_safe_builtin[] = {
        "__asan_store4_noabort",
        "__asan_store8_noabort",
        "__asan_store16_noabort",
+       "__kasan_check_read",
+       "__kasan_check_write",
        /* KASAN in-line */
        "__asan_report_load_n_noabort",
        "__asan_report_load1_noabort",
@@ -528,6 +620,61 @@ static const char *uaccess_safe_builtin[] = {
        "__tsan_write4",
        "__tsan_write8",
        "__tsan_write16",
+       "__tsan_read_write1",
+       "__tsan_read_write2",
+       "__tsan_read_write4",
+       "__tsan_read_write8",
+       "__tsan_read_write16",
+       "__tsan_atomic8_load",
+       "__tsan_atomic16_load",
+       "__tsan_atomic32_load",
+       "__tsan_atomic64_load",
+       "__tsan_atomic8_store",
+       "__tsan_atomic16_store",
+       "__tsan_atomic32_store",
+       "__tsan_atomic64_store",
+       "__tsan_atomic8_exchange",
+       "__tsan_atomic16_exchange",
+       "__tsan_atomic32_exchange",
+       "__tsan_atomic64_exchange",
+       "__tsan_atomic8_fetch_add",
+       "__tsan_atomic16_fetch_add",
+       "__tsan_atomic32_fetch_add",
+       "__tsan_atomic64_fetch_add",
+       "__tsan_atomic8_fetch_sub",
+       "__tsan_atomic16_fetch_sub",
+       "__tsan_atomic32_fetch_sub",
+       "__tsan_atomic64_fetch_sub",
+       "__tsan_atomic8_fetch_and",
+       "__tsan_atomic16_fetch_and",
+       "__tsan_atomic32_fetch_and",
+       "__tsan_atomic64_fetch_and",
+       "__tsan_atomic8_fetch_or",
+       "__tsan_atomic16_fetch_or",
+       "__tsan_atomic32_fetch_or",
+       "__tsan_atomic64_fetch_or",
+       "__tsan_atomic8_fetch_xor",
+       "__tsan_atomic16_fetch_xor",
+       "__tsan_atomic32_fetch_xor",
+       "__tsan_atomic64_fetch_xor",
+       "__tsan_atomic8_fetch_nand",
+       "__tsan_atomic16_fetch_nand",
+       "__tsan_atomic32_fetch_nand",
+       "__tsan_atomic64_fetch_nand",
+       "__tsan_atomic8_compare_exchange_strong",
+       "__tsan_atomic16_compare_exchange_strong",
+       "__tsan_atomic32_compare_exchange_strong",
+       "__tsan_atomic64_compare_exchange_strong",
+       "__tsan_atomic8_compare_exchange_weak",
+       "__tsan_atomic16_compare_exchange_weak",
+       "__tsan_atomic32_compare_exchange_weak",
+       "__tsan_atomic64_compare_exchange_weak",
+       "__tsan_atomic8_compare_exchange_val",
+       "__tsan_atomic16_compare_exchange_val",
+       "__tsan_atomic32_compare_exchange_val",
+       "__tsan_atomic64_compare_exchange_val",
+       "__tsan_atomic_thread_fence",
+       "__tsan_atomic_signal_fence",
        /* KCOV */
        "write_comp_data",
        "check_kcov_mode",
@@ -548,8 +695,9 @@ static const char *uaccess_safe_builtin[] = {
        "__ubsan_handle_shift_out_of_bounds",
        /* misc */
        "csum_partial_copy_generic",
-       "__memcpy_mcsafe",
-       "mcsafe_handle_tail",
+       "copy_mc_fragile",
+       "copy_mc_fragile_handle_tail",
+       "copy_mc_enhanced_fast_string",
        "ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */
        NULL
 };
@@ -649,6 +797,10 @@ static int add_jump_destinations(struct objtool_file *file)
                } else {
                        /* external sibling call */
                        insn->call_dest = reloc->sym;
+                       if (insn->call_dest->static_call_tramp) {
+                               list_add_tail(&insn->static_call_node,
+                                             &file->static_call_list);
+                       }
                        continue;
                }
 
@@ -700,6 +852,10 @@ static int add_jump_destinations(struct objtool_file *file)
 
                                /* internal sibling call */
                                insn->call_dest = insn->jump_dest->func;
+                               if (insn->call_dest->static_call_tramp) {
+                                       list_add_tail(&insn->static_call_node,
+                                                     &file->static_call_list);
+                               }
                        }
                }
        }
@@ -717,6 +873,17 @@ static void remove_insn_ops(struct instruction *insn)
        }
 }
 
+static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
+{
+       struct symbol *call_dest;
+
+       call_dest = find_func_by_offset(sec, offset);
+       if (!call_dest)
+               call_dest = find_symbol_by_offset(sec, offset);
+
+       return call_dest;
+}
+
 /*
  * Find the destination instructions for all calls.
  */
@@ -734,9 +901,7 @@ static int add_call_destinations(struct objtool_file *file)
                                               insn->offset, insn->len);
                if (!reloc) {
                        dest_off = arch_jump_destination(insn);
-                       insn->call_dest = find_func_by_offset(insn->sec, dest_off);
-                       if (!insn->call_dest)
-                               insn->call_dest = find_symbol_by_offset(insn->sec, dest_off);
+                       insn->call_dest = find_call_destination(insn->sec, dest_off);
 
                        if (insn->ignore)
                                continue;
@@ -754,8 +919,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_func_by_offset(reloc->sym->sec,
-                                                             dest_off);
+                       insn->call_dest = find_call_destination(reloc->sym->sec,
+                                                               dest_off);
                        if (!insn->call_dest) {
                                WARN_FUNC("can't find call dest symbol at %s+0x%lx",
                                          insn->sec, insn->offset,
@@ -867,6 +1032,8 @@ static int handle_group_alt(struct objtool_file *file,
        alt_group = alt_group_next_index++;
        insn = *new_insn;
        sec_for_each_insn_from(file, insn) {
+               struct reloc *alt_reloc;
+
                if (insn->offset >= special_alt->new_off + special_alt->new_len)
                        break;
 
@@ -883,14 +1050,11 @@ static int handle_group_alt(struct objtool_file *file,
                 * .altinstr_replacement section, unless the arch's
                 * alternatives code can adjust the relative offsets
                 * accordingly.
-                *
-                * The x86 alternatives code adjusts the offsets only when it
-                * encounters a branch instruction at the very beginning of the
-                * replacement group.
                 */
-               if ((insn->offset != special_alt->new_off ||
-                   (insn->type != INSN_CALL && !is_static_jump(insn))) &&
-                   find_reloc_by_dest_range(file->elf, insn->sec, insn->offset, insn->len)) {
+               alt_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
+                                                  insn->offset, insn->len);
+               if (alt_reloc &&
+                   !arch_support_alt_relocation(special_alt, insn, alt_reloc)) {
 
                        WARN_FUNC("unsupported relocation in alternatives section",
                                  insn->sec, insn->offset);
@@ -1092,56 +1256,15 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
 }
 
 /*
- * find_jump_table() - Given a dynamic jump, find the switch jump table in
- * .rodata associated with it.
- *
- * There are 3 basic patterns:
- *
- * 1. jmpq *[rodata addr](,%reg,8)
- *
- *    This is the most common case by far.  It jumps to an address in a simple
- *    jump table which is stored in .rodata.
- *
- * 2. jmpq *[rodata addr](%rip)
- *
- *    This is caused by a rare GCC quirk, currently only seen in three driver
- *    functions in the kernel, only with certain obscure non-distro configs.
- *
- *    As part of an optimization, GCC makes a copy of an existing switch jump
- *    table, modifies it, and then hard-codes the jump (albeit with an indirect
- *    jump) to use a single entry in the table.  The rest of the jump table and
- *    some of its jump targets remain as dead code.
- *
- *    In such a case we can just crudely ignore all unreachable instruction
- *    warnings for the entire object file.  Ideally we would just ignore them
- *    for the function, but that would require redesigning the code quite a
- *    bit.  And honestly that's just not worth doing: unreachable instruction
- *    warnings are of questionable value anyway, and this is such a rare issue.
- *
- * 3. mov [rodata addr],%reg1
- *    ... some instructions ...
- *    jmpq *(%reg1,%reg2,8)
- *
- *    This is a fairly uncommon pattern which is new for GCC 6.  As of this
- *    writing, there are 11 occurrences of it in the allmodconfig kernel.
- *
- *    As of GCC 7 there are quite a few more of these and the 'in between' code
- *    is significant. Esp. with KASAN enabled some of the code between the mov
- *    and jmpq uses .rodata itself, which can confuse things.
- *
- *    TODO: Once we have DWARF CFI and smarter instruction decoding logic,
- *    ensure the same register is used in the mov and jump instructions.
- *
- *    NOTE: RETPOLINE made it harder still to decode dynamic jumps.
+ * find_jump_table() - Given a dynamic jump, find the switch jump table
+ * associated with it.
  */
 static struct reloc *find_jump_table(struct objtool_file *file,
                                      struct symbol *func,
                                      struct instruction *insn)
 {
-       struct reloc *text_reloc, *table_reloc;
+       struct reloc *table_reloc;
        struct instruction *dest_insn, *orig_insn = insn;
-       struct section *table_sec;
-       unsigned long table_offset;
 
        /*
         * Backward search using the @first_jump_src links, these help avoid
@@ -1162,52 +1285,13 @@ static struct reloc *find_jump_table(struct objtool_file *file,
                     insn->jump_dest->offset > orig_insn->offset))
                    break;
 
-               /* look for a relocation which references .rodata */
-               text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
-                                                   insn->offset, insn->len);
-               if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
-                   !text_reloc->sym->sec->rodata)
-                       continue;
-
-               table_offset = text_reloc->addend;
-               table_sec = text_reloc->sym->sec;
-
-               if (text_reloc->type == R_X86_64_PC32)
-                       table_offset += 4;
-
-               /*
-                * Make sure the .rodata address isn't associated with a
-                * symbol.  GCC jump tables are anonymous data.
-                *
-                * Also support C jump tables which are in the same format as
-                * switch jump tables.  For objtool to recognize them, they
-                * need to be placed in the C_JUMP_TABLE_SECTION section.  They
-                * have symbols associated with them.
-                */
-               if (find_symbol_containing(table_sec, table_offset) &&
-                   strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
-                       continue;
-
-               /*
-                * Each table entry has a reloc associated with it.  The reloc
-                * should reference text in the same function as the original
-                * instruction.
-                */
-               table_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset);
+               table_reloc = arch_find_switch_table(file, insn);
                if (!table_reloc)
                        continue;
                dest_insn = find_insn(file, table_reloc->sym->sec, table_reloc->addend);
                if (!dest_insn || !dest_insn->func || dest_insn->func->pfunc != func)
                        continue;
 
-               /*
-                * Use of RIP-relative switch jumps is quite rare, and
-                * indicates a rare GCC quirk/bug which can leave dead code
-                * behind.
-                */
-               if (text_reloc->type == R_X86_64_PC32)
-                       file->ignore_unreachables = true;
-
                return table_reloc;
        }
 
@@ -1350,32 +1434,7 @@ static int read_unwind_hints(struct objtool_file *file)
 
                insn->hint = true;
 
-               switch (hint->sp_reg) {
-               case ORC_REG_UNDEFINED:
-                       cfa->base = CFI_UNDEFINED;
-                       break;
-               case ORC_REG_SP:
-                       cfa->base = CFI_SP;
-                       break;
-               case ORC_REG_BP:
-                       cfa->base = CFI_BP;
-                       break;
-               case ORC_REG_SP_INDIRECT:
-                       cfa->base = CFI_SP_INDIRECT;
-                       break;
-               case ORC_REG_R10:
-                       cfa->base = CFI_R10;
-                       break;
-               case ORC_REG_R13:
-                       cfa->base = CFI_R13;
-                       break;
-               case ORC_REG_DI:
-                       cfa->base = CFI_DI;
-                       break;
-               case ORC_REG_DX:
-                       cfa->base = CFI_DX;
-                       break;
-               default:
+               if (arch_decode_hint_reg(insn, hint->sp_reg)) {
                        WARN_FUNC("unsupported unwind_hint sp base reg %d",
                                  insn->sec, insn->offset, hint->sp_reg);
                        return -1;
@@ -1522,6 +1581,23 @@ static int read_intra_function_calls(struct objtool_file *file)
        return 0;
 }
 
+static int read_static_call_tramps(struct objtool_file *file)
+{
+       struct section *sec;
+       struct symbol *func;
+
+       for_each_sec(file, sec) {
+               list_for_each_entry(func, &sec->symbol_list, list) {
+                       if (func->bind == STB_GLOBAL &&
+                           !strncmp(func->name, STATIC_CALL_TRAMP_PREFIX_STR,
+                                    strlen(STATIC_CALL_TRAMP_PREFIX_STR)))
+                               func->static_call_tramp = true;
+               }
+       }
+
+       return 0;
+}
+
 static void mark_rodata(struct objtool_file *file)
 {
        struct section *sec;
@@ -1569,6 +1645,10 @@ static int decode_sections(struct objtool_file *file)
        if (ret)
                return ret;
 
+       ret = read_static_call_tramps(file);
+       if (ret)
+               return ret;
+
        ret = add_jump_destinations(file);
        if (ret)
                return ret;
@@ -1768,7 +1848,8 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
                return 0;
        }
 
-       if (cfi->type == ORC_TYPE_REGS || cfi->type == ORC_TYPE_REGS_IRET)
+       if (cfi->type == UNWIND_HINT_TYPE_REGS ||
+           cfi->type == UNWIND_HINT_TYPE_REGS_PARTIAL)
                return update_cfi_state_regs(insn, cfi, op);
 
        switch (op->dest.type) {
@@ -2016,7 +2097,7 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
                                /* drap: push %rbp */
                                cfi->stack_size = 0;
 
-                       } else if (regs[op->src.reg].base == CFI_UNDEFINED) {
+                       } else {
 
                                /* drap: push %reg */
                                save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
@@ -2045,9 +2126,7 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
 
                                /* save drap offset so we know when to restore it */
                                cfi->drap_offset = op->dest.offset;
-                       }
-
-                       else if (regs[op->src.reg].base == CFI_UNDEFINED) {
+                       } else {
 
                                /* drap: mov reg, disp(%rbp) */
                                save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
@@ -2432,6 +2511,11 @@ 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:
@@ -2612,9 +2696,10 @@ static bool is_ubsan_insn(struct instruction *insn)
                        "__ubsan_handle_builtin_unreachable"));
 }
 
-static bool ignore_unreachable_insn(struct instruction *insn)
+static bool ignore_unreachable_insn(struct objtool_file *file, struct instruction *insn)
 {
        int i;
+       struct instruction *prev_insn;
 
        if (insn->ignore || insn->type == INSN_NOP)
                return true;
@@ -2631,6 +2716,9 @@ static bool ignore_unreachable_insn(struct instruction *insn)
            !strcmp(insn->sec->name, ".altinstr_aux"))
                return true;
 
+       if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->offset == FAKE_JUMP_OFFSET)
+               return true;
+
        if (!insn->func)
                return false;
 
@@ -2639,8 +2727,11 @@ static bool ignore_unreachable_insn(struct instruction *insn)
         * __builtin_unreachable().  The BUG() macro has an unreachable() after
         * the UD2, which causes GCC's undefined trap logic to emit another UD2
         * (or occasionally a JMP to UD2).
+        *
+        * It may also insert a UD2 after calling a __noreturn function.
         */
-       if (list_prev_entry(insn, list)->dead_end &&
+       prev_insn = list_prev_entry(insn, list);
+       if ((prev_insn->dead_end || dead_end_function(file, prev_insn->call_dest)) &&
            (insn->type == INSN_BUG ||
             (insn->type == INSN_JUMP_UNCONDITIONAL &&
              insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
@@ -2767,7 +2858,7 @@ static int validate_reachable_instructions(struct objtool_file *file)
                return 0;
 
        for_each_insn(file, insn) {
-               if (insn->visited || ignore_unreachable_insn(insn))
+               if (insn->visited || ignore_unreachable_insn(file, insn))
                        continue;
 
                WARN_FUNC("unreachable instruction", insn->sec, insn->offset);
@@ -2777,36 +2868,22 @@ static int validate_reachable_instructions(struct objtool_file *file)
        return 0;
 }
 
-static struct objtool_file file;
-
-int check(const char *_objname, bool orc)
+int check(struct objtool_file *file)
 {
        int ret, warnings = 0;
 
-       objname = _objname;
-
-       file.elf = elf_open_read(objname, O_RDWR);
-       if (!file.elf)
-               return 1;
-
-       INIT_LIST_HEAD(&file.insn_list);
-       hash_init(file.insn_hash);
-       file.c_file = !vmlinux && find_section_by_name(file.elf, ".comment");
-       file.ignore_unreachables = no_unreachable;
-       file.hints = false;
-
        arch_initial_func_cfi_state(&initial_func_cfi);
 
-       ret = decode_sections(&file);
+       ret = decode_sections(file);
        if (ret < 0)
                goto out;
        warnings += ret;
 
-       if (list_empty(&file.insn_list))
+       if (list_empty(&file->insn_list))
                goto out;
 
        if (vmlinux && !validate_dup) {
-               ret = validate_vmlinux_functions(&file);
+               ret = validate_vmlinux_functions(file);
                if (ret < 0)
                        goto out;
 
@@ -2815,44 +2892,33 @@ int check(const char *_objname, bool orc)
        }
 
        if (retpoline) {
-               ret = validate_retpoline(&file);
+               ret = validate_retpoline(file);
                if (ret < 0)
                        return ret;
                warnings += ret;
        }
 
-       ret = validate_functions(&file);
+       ret = validate_functions(file);
        if (ret < 0)
                goto out;
        warnings += ret;
 
-       ret = validate_unwind_hints(&file, NULL);
+       ret = validate_unwind_hints(file, NULL);
        if (ret < 0)
                goto out;
        warnings += ret;
 
        if (!warnings) {
-               ret = validate_reachable_instructions(&file);
+               ret = validate_reachable_instructions(file);
                if (ret < 0)
                        goto out;
                warnings += ret;
        }
 
-       if (orc) {
-               ret = create_orc(&file);
-               if (ret < 0)
-                       goto out;
-
-               ret = create_orc_sections(&file);
-               if (ret < 0)
-                       goto out;
-       }
-
-       if (file.elf->changed) {
-               ret = elf_write(file.elf);
-               if (ret < 0)
-                       goto out;
-       }
+       ret = create_static_call_sections(file);
+       if (ret < 0)
+               goto out;
+       warnings += ret;
 
 out:
        if (ret < 0) {