]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - tools/objtool/check.c
objtool: Free insns when done
[thirdparty/linux.git] / tools / objtool / check.c
index b11c25a715acd7364f5b9d3343d6d34b818bf4fd..8936a05f0e5acbb151e7547b01a23268d711dfcb 100644 (file)
@@ -8,7 +8,6 @@
 #include <inttypes.h>
 #include <sys/mman.h>
 
-#include <arch/elf.h>
 #include <objtool/builtin.h>
 #include <objtool/cfi.h>
 #include <objtool/arch.h>
@@ -494,7 +493,7 @@ static int add_pv_ops(struct objtool_file *file, const char *symname)
 {
        struct symbol *sym, *func;
        unsigned long off, end;
-       struct reloc *rel;
+       struct reloc *reloc;
        int idx;
 
        sym = find_symbol_by_name(file->elf, symname);
@@ -504,19 +503,20 @@ static int add_pv_ops(struct objtool_file *file, const char *symname)
        off = sym->offset;
        end = off + sym->len;
        for (;;) {
-               rel = find_reloc_by_dest_range(file->elf, sym->sec, off, end - off);
-               if (!rel)
+               reloc = find_reloc_by_dest_range(file->elf, sym->sec, off, end - off);
+               if (!reloc)
                        break;
 
-               func = rel->sym;
+               func = reloc->sym;
                if (func->type == STT_SECTION)
-                       func = find_symbol_by_offset(rel->sym->sec, rel->addend);
+                       func = find_symbol_by_offset(reloc->sym->sec,
+                                                    reloc_addend(reloc));
 
-               idx = (rel->offset - sym->offset) / sizeof(unsigned long);
+               idx = (reloc_offset(reloc) - sym->offset) / sizeof(unsigned long);
 
                objtool_pv_add(file, idx, func);
 
-               off = rel->offset + 1;
+               off = reloc_offset(reloc) + 1;
                if (off > end)
                        break;
        }
@@ -581,35 +581,40 @@ static struct instruction *find_last_insn(struct objtool_file *file,
  */
 static int add_dead_ends(struct objtool_file *file)
 {
-       struct section *sec;
+       struct section *rsec;
        struct reloc *reloc;
        struct instruction *insn;
+       s64 addend;
 
        /*
         * Check for manually annotated dead ends.
         */
-       sec = find_section_by_name(file->elf, ".rela.discard.unreachable");
-       if (!sec)
+       rsec = find_section_by_name(file->elf, ".rela.discard.unreachable");
+       if (!rsec)
                goto reachable;
 
-       list_for_each_entry(reloc, &sec->reloc_list, list) {
+       for_each_reloc(rsec, reloc) {
+
                if (reloc->sym->type != STT_SECTION) {
-                       WARN("unexpected relocation symbol type in %s", sec->name);
+                       WARN("unexpected relocation symbol type in %s", rsec->name);
                        return -1;
                }
-               insn = find_insn(file, reloc->sym->sec, reloc->addend);
+
+               addend = reloc_addend(reloc);
+
+               insn = find_insn(file, reloc->sym->sec, addend);
                if (insn)
                        insn = prev_insn_same_sec(file, insn);
-               else if (reloc->addend == reloc->sym->sec->sh.sh_size) {
+               else if (addend == reloc->sym->sec->sh.sh_size) {
                        insn = find_last_insn(file, reloc->sym->sec);
                        if (!insn) {
                                WARN("can't find unreachable insn at %s+0x%" PRIx64,
-                                    reloc->sym->sec->name, reloc->addend);
+                                    reloc->sym->sec->name, addend);
                                return -1;
                        }
                } else {
                        WARN("can't find unreachable insn at %s+0x%" PRIx64,
-                            reloc->sym->sec->name, reloc->addend);
+                            reloc->sym->sec->name, addend);
                        return -1;
                }
 
@@ -623,28 +628,32 @@ reachable:
         * GCC doesn't know the "ud2" is fatal, so it generates code as if it's
         * not a dead end.
         */
-       sec = find_section_by_name(file->elf, ".rela.discard.reachable");
-       if (!sec)
+       rsec = find_section_by_name(file->elf, ".rela.discard.reachable");
+       if (!rsec)
                return 0;
 
-       list_for_each_entry(reloc, &sec->reloc_list, list) {
+       for_each_reloc(rsec, reloc) {
+
                if (reloc->sym->type != STT_SECTION) {
-                       WARN("unexpected relocation symbol type in %s", sec->name);
+                       WARN("unexpected relocation symbol type in %s", rsec->name);
                        return -1;
                }
-               insn = find_insn(file, reloc->sym->sec, reloc->addend);
+
+               addend = reloc_addend(reloc);
+
+               insn = find_insn(file, reloc->sym->sec, addend);
                if (insn)
                        insn = prev_insn_same_sec(file, insn);
-               else if (reloc->addend == reloc->sym->sec->sh.sh_size) {
+               else if (addend == reloc->sym->sec->sh.sh_size) {
                        insn = find_last_insn(file, reloc->sym->sec);
                        if (!insn) {
                                WARN("can't find reachable insn at %s+0x%" PRIx64,
-                                    reloc->sym->sec->name, reloc->addend);
+                                    reloc->sym->sec->name, addend);
                                return -1;
                        }
                } else {
                        WARN("can't find reachable insn at %s+0x%" PRIx64,
-                            reloc->sym->sec->name, reloc->addend);
+                            reloc->sym->sec->name, addend);
                        return -1;
                }
 
@@ -656,8 +665,8 @@ reachable:
 
 static int create_static_call_sections(struct objtool_file *file)
 {
-       struct section *sec;
        struct static_call_site *site;
+       struct section *sec;
        struct instruction *insn;
        struct symbol *key_sym;
        char *key_name, *tmp;
@@ -677,22 +686,21 @@ static int create_static_call_sections(struct objtool_file *file)
        list_for_each_entry(insn, &file->static_call_list, call_node)
                idx++;
 
-       sec = elf_create_section(file->elf, ".static_call_sites", SHF_WRITE,
-                                sizeof(struct static_call_site), idx);
+       sec = elf_create_section_pair(file->elf, ".static_call_sites",
+                                     sizeof(*site), idx, idx * 2);
        if (!sec)
                return -1;
 
+       /* Allow modules to modify the low bits of static_call_site::key */
+       sec->sh.sh_flags |= SHF_WRITE;
+
        idx = 0;
        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' */
-               if (elf_add_reloc_to_insn(file->elf, sec,
-                                         idx * sizeof(struct static_call_site),
-                                         R_X86_64_PC32,
-                                         insn->sec, insn->offset))
+               if (!elf_init_reloc_text_sym(file->elf, sec,
+                                            idx * sizeof(*site), idx * 2,
+                                            insn->sec, insn->offset))
                        return -1;
 
                /* find key symbol */
@@ -732,10 +740,10 @@ static int create_static_call_sections(struct objtool_file *file)
                free(key_name);
 
                /* populate reloc for 'key' */
-               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))
+               if (!elf_init_reloc_data_sym(file->elf, sec,
+                                            idx * sizeof(*site) + 4,
+                                            (idx * 2) + 1, key_sym,
+                                            is_sibling_call(insn) * STATIC_CALL_SITE_TAIL))
                        return -1;
 
                idx++;
@@ -763,26 +771,18 @@ static int create_retpoline_sites_sections(struct objtool_file *file)
        if (!idx)
                return 0;
 
-       sec = elf_create_section(file->elf, ".retpoline_sites", 0,
-                                sizeof(int), idx);
-       if (!sec) {
-               WARN("elf_create_section: .retpoline_sites");
+       sec = elf_create_section_pair(file->elf, ".retpoline_sites",
+                                     sizeof(int), idx, idx);
+       if (!sec)
                return -1;
-       }
 
        idx = 0;
        list_for_each_entry(insn, &file->retpoline_call_list, call_node) {
 
-               int *site = (int *)sec->data->d_buf + idx;
-               *site = 0;
-
-               if (elf_add_reloc_to_insn(file->elf, sec,
-                                         idx * sizeof(int),
-                                         R_X86_64_PC32,
-                                         insn->sec, insn->offset)) {
-                       WARN("elf_add_reloc_to_insn: .retpoline_sites");
+               if (!elf_init_reloc_text_sym(file->elf, sec,
+                                            idx * sizeof(int), idx,
+                                            insn->sec, insn->offset))
                        return -1;
-               }
 
                idx++;
        }
@@ -809,26 +809,18 @@ static int create_return_sites_sections(struct objtool_file *file)
        if (!idx)
                return 0;
 
-       sec = elf_create_section(file->elf, ".return_sites", 0,
-                                sizeof(int), idx);
-       if (!sec) {
-               WARN("elf_create_section: .return_sites");
+       sec = elf_create_section_pair(file->elf, ".return_sites",
+                                     sizeof(int), idx, idx);
+       if (!sec)
                return -1;
-       }
 
        idx = 0;
        list_for_each_entry(insn, &file->return_thunk_list, call_node) {
 
-               int *site = (int *)sec->data->d_buf + idx;
-               *site = 0;
-
-               if (elf_add_reloc_to_insn(file->elf, sec,
-                                         idx * sizeof(int),
-                                         R_X86_64_PC32,
-                                         insn->sec, insn->offset)) {
-                       WARN("elf_add_reloc_to_insn: .return_sites");
+               if (!elf_init_reloc_text_sym(file->elf, sec,
+                                            idx * sizeof(int), idx,
+                                            insn->sec, insn->offset))
                        return -1;
-               }
 
                idx++;
        }
@@ -861,12 +853,10 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file)
        if (!idx)
                return 0;
 
-       sec = elf_create_section(file->elf, ".ibt_endbr_seal", 0,
-                                sizeof(int), idx);
-       if (!sec) {
-               WARN("elf_create_section: .ibt_endbr_seal");
+       sec = elf_create_section_pair(file->elf, ".ibt_endbr_seal",
+                                     sizeof(int), idx, idx);
+       if (!sec)
                return -1;
-       }
 
        idx = 0;
        list_for_each_entry(insn, &file->endbr_list, call_node) {
@@ -881,13 +871,10 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file)
                     !strcmp(sym->name, "cleanup_module")))
                        WARN("%s(): not an indirect call target", sym->name);
 
-               if (elf_add_reloc_to_insn(file->elf, sec,
-                                         idx * sizeof(int),
-                                         R_X86_64_PC32,
-                                         insn->sec, insn->offset)) {
-                       WARN("elf_add_reloc_to_insn: .ibt_endbr_seal");
+               if (!elf_init_reloc_text_sym(file->elf, sec,
+                                            idx * sizeof(int), idx,
+                                            insn->sec, insn->offset))
                        return -1;
-               }
 
                idx++;
        }
@@ -899,7 +886,6 @@ static int create_cfi_sections(struct objtool_file *file)
 {
        struct section *sec;
        struct symbol *sym;
-       unsigned int *loc;
        int idx;
 
        sec = find_section_by_name(file->elf, ".cfi_sites");
@@ -920,7 +906,8 @@ static int create_cfi_sections(struct objtool_file *file)
                idx++;
        }
 
-       sec = elf_create_section(file->elf, ".cfi_sites", 0, sizeof(unsigned int), idx);
+       sec = elf_create_section_pair(file->elf, ".cfi_sites",
+                                     sizeof(unsigned int), idx, idx);
        if (!sec)
                return -1;
 
@@ -932,13 +919,9 @@ static int create_cfi_sections(struct objtool_file *file)
                if (strncmp(sym->name, "__cfi_", 6))
                        continue;
 
-               loc = (unsigned int *)sec->data->d_buf + idx;
-               memset(loc, 0, sizeof(unsigned int));
-
-               if (elf_add_reloc_to_insn(file->elf, sec,
-                                         idx * sizeof(unsigned int),
-                                         R_X86_64_PC32,
-                                         sym->sec, sym->offset))
+               if (!elf_init_reloc_text_sym(file->elf, sec,
+                                            idx * sizeof(unsigned int), idx,
+                                            sym->sec, sym->offset))
                        return -1;
 
                idx++;
@@ -949,7 +932,7 @@ static int create_cfi_sections(struct objtool_file *file)
 
 static int create_mcount_loc_sections(struct objtool_file *file)
 {
-       int addrsize = elf_class_addrsize(file->elf);
+       size_t addr_size = elf_addr_size(file->elf);
        struct instruction *insn;
        struct section *sec;
        int idx;
@@ -968,25 +951,26 @@ static int create_mcount_loc_sections(struct objtool_file *file)
        list_for_each_entry(insn, &file->mcount_loc_list, call_node)
                idx++;
 
-       sec = elf_create_section(file->elf, "__mcount_loc", 0, addrsize, idx);
+       sec = elf_create_section_pair(file->elf, "__mcount_loc", addr_size,
+                                     idx, idx);
        if (!sec)
                return -1;
 
-       sec->sh.sh_addralign = addrsize;
+       sec->sh.sh_addralign = addr_size;
 
        idx = 0;
        list_for_each_entry(insn, &file->mcount_loc_list, call_node) {
-               void *loc;
 
-               loc = sec->data->d_buf + idx;
-               memset(loc, 0, addrsize);
+               struct reloc *reloc;
 
-               if (elf_add_reloc_to_insn(file->elf, sec, idx,
-                                         addrsize == sizeof(u64) ? R_ABS64 : R_ABS32,
-                                         insn->sec, insn->offset))
+               reloc = elf_init_reloc_text_sym(file->elf, sec, idx * addr_size, idx,
+                                              insn->sec, insn->offset);
+               if (!reloc)
                        return -1;
 
-               idx += addrsize;
+               set_reloc_type(file->elf, reloc, addr_size == 8 ? R_ABS64 : R_ABS32);
+
+               idx++;
        }
 
        return 0;
@@ -996,7 +980,6 @@ static int create_direct_call_sections(struct objtool_file *file)
 {
        struct instruction *insn;
        struct section *sec;
-       unsigned int *loc;
        int idx;
 
        sec = find_section_by_name(file->elf, ".call_sites");
@@ -1013,20 +996,17 @@ static int create_direct_call_sections(struct objtool_file *file)
        list_for_each_entry(insn, &file->call_list, call_node)
                idx++;
 
-       sec = elf_create_section(file->elf, ".call_sites", 0, sizeof(unsigned int), idx);
+       sec = elf_create_section_pair(file->elf, ".call_sites",
+                                     sizeof(unsigned int), idx, idx);
        if (!sec)
                return -1;
 
        idx = 0;
        list_for_each_entry(insn, &file->call_list, call_node) {
 
-               loc = (unsigned int *)sec->data->d_buf + idx;
-               memset(loc, 0, sizeof(unsigned int));
-
-               if (elf_add_reloc_to_insn(file->elf, sec,
-                                         idx * sizeof(unsigned int),
-                                         R_X86_64_PC32,
-                                         insn->sec, insn->offset))
+               if (!elf_init_reloc_text_sym(file->elf, sec,
+                                            idx * sizeof(unsigned int), idx,
+                                            insn->sec, insn->offset))
                        return -1;
 
                idx++;
@@ -1041,28 +1021,29 @@ static int create_direct_call_sections(struct objtool_file *file)
 static void add_ignores(struct objtool_file *file)
 {
        struct instruction *insn;
-       struct section *sec;
+       struct section *rsec;
        struct symbol *func;
        struct reloc *reloc;
 
-       sec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard");
-       if (!sec)
+       rsec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard");
+       if (!rsec)
                return;
 
-       list_for_each_entry(reloc, &sec->reloc_list, list) {
+       for_each_reloc(rsec, reloc) {
                switch (reloc->sym->type) {
                case STT_FUNC:
                        func = reloc->sym;
                        break;
 
                case STT_SECTION:
-                       func = find_func_by_offset(reloc->sym->sec, reloc->addend);
+                       func = find_func_by_offset(reloc->sym->sec, reloc_addend(reloc));
                        if (!func)
                                continue;
                        break;
 
                default:
-                       WARN("unexpected relocation symbol type in %s: %d", sec->name, reloc->sym->type);
+                       WARN("unexpected relocation symbol type in %s: %d",
+                            rsec->name, reloc->sym->type);
                        continue;
                }
 
@@ -1281,21 +1262,21 @@ static void add_uaccess_safe(struct objtool_file *file)
  */
 static int add_ignore_alternatives(struct objtool_file *file)
 {
-       struct section *sec;
+       struct section *rsec;
        struct reloc *reloc;
        struct instruction *insn;
 
-       sec = find_section_by_name(file->elf, ".rela.discard.ignore_alts");
-       if (!sec)
+       rsec = find_section_by_name(file->elf, ".rela.discard.ignore_alts");
+       if (!rsec)
                return 0;
 
-       list_for_each_entry(reloc, &sec->reloc_list, list) {
+       for_each_reloc(rsec, reloc) {
                if (reloc->sym->type != STT_SECTION) {
-                       WARN("unexpected relocation symbol type in %s", sec->name);
+                       WARN("unexpected relocation symbol type in %s", rsec->name);
                        return -1;
                }
 
-               insn = find_insn(file, reloc->sym->sec, reloc->addend);
+               insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
                if (!insn) {
                        WARN("bad .discard.ignore_alts entry");
                        return -1;
@@ -1382,10 +1363,8 @@ static void annotate_call_site(struct objtool_file *file,
         * noinstr text.
         */
        if (opts.hack_noinstr && insn->sec->noinstr && sym->profiling_func) {
-               if (reloc) {
-                       reloc->type = R_NONE;
-                       elf_write_reloc(file->elf, reloc);
-               }
+               if (reloc)
+                       set_reloc_type(file->elf, reloc, R_NONE);
 
                elf_write_insn(file->elf, insn->sec,
                               insn->offset, insn->len,
@@ -1411,10 +1390,8 @@ static void annotate_call_site(struct objtool_file *file,
                if (sibling)
                        WARN_INSN(insn, "tail call to __fentry__ !?!?");
                if (opts.mnop) {
-                       if (reloc) {
-                               reloc->type = R_NONE;
-                               elf_write_reloc(file->elf, reloc);
-                       }
+                       if (reloc)
+                               set_reloc_type(file->elf, reloc, R_NONE);
 
                        elf_write_insn(file->elf, insn->sec,
                                       insn->offset, insn->len,
@@ -1571,7 +1548,7 @@ static int add_jump_destinations(struct objtool_file *file)
                        dest_off = arch_jump_destination(insn);
                } else if (reloc->sym->type == STT_SECTION) {
                        dest_sec = reloc->sym->sec;
-                       dest_off = arch_dest_reloc_offset(reloc->addend);
+                       dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
                } else if (reloc->sym->retpoline_thunk) {
                        add_retpoline_call(file, insn);
                        continue;
@@ -1588,7 +1565,7 @@ static int add_jump_destinations(struct objtool_file *file)
                } else if (reloc->sym->sec->idx) {
                        dest_sec = reloc->sym->sec;
                        dest_off = reloc->sym->sym.st_value +
-                                  arch_dest_reloc_offset(reloc->addend);
+                                  arch_dest_reloc_offset(reloc_addend(reloc));
                } else {
                        /* non-func asm code jumping to another file */
                        continue;
@@ -1705,7 +1682,7 @@ static int add_call_destinations(struct objtool_file *file)
                        }
 
                } else if (reloc->sym->type == STT_SECTION) {
-                       dest_off = arch_dest_reloc_offset(reloc->addend);
+                       dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
                        dest = find_call_destination(reloc->sym->sec, dest_off);
                        if (!dest) {
                                WARN_INSN(insn, "can't find call dest symbol at %s+0x%lx",
@@ -1893,10 +1870,8 @@ static int handle_jump_alt(struct objtool_file *file,
        if (opts.hack_jump_label && special_alt->key_addend & 2) {
                struct reloc *reloc = insn_reloc(file, orig_insn);
 
-               if (reloc) {
-                       reloc->type = R_NONE;
-                       elf_write_reloc(file->elf, reloc);
-               }
+               if (reloc)
+                       set_reloc_type(file->elf, reloc, R_NONE);
                elf_write_insn(file->elf, orig_insn->sec,
                               orig_insn->offset, orig_insn->len,
                               arch_nop_insn(orig_insn->len));
@@ -2008,34 +1983,35 @@ out:
 }
 
 static int add_jump_table(struct objtool_file *file, struct instruction *insn,
-                           struct reloc *table)
+                         struct reloc *next_table)
 {
-       struct reloc *reloc = table;
-       struct instruction *dest_insn;
-       struct alternative *alt;
        struct symbol *pfunc = insn_func(insn)->pfunc;
+       struct reloc *table = insn_jump_table(insn);
+       struct instruction *dest_insn;
        unsigned int prev_offset = 0;
+       struct reloc *reloc = table;
+       struct alternative *alt;
 
        /*
         * Each @reloc is a switch table relocation which points to the target
         * instruction.
         */
-       list_for_each_entry_from(reloc, &table->sec->reloc_list, list) {
+       for_each_reloc_from(table->sec, reloc) {
 
                /* Check for the end of the table: */
-               if (reloc != table && reloc->jump_table_start)
+               if (reloc != table && reloc == next_table)
                        break;
 
                /* Make sure the table entries are consecutive: */
-               if (prev_offset && reloc->offset != prev_offset + 8)
+               if (prev_offset && reloc_offset(reloc) != prev_offset + 8)
                        break;
 
                /* Detect function pointers from contiguous objects: */
                if (reloc->sym->sec == pfunc->sec &&
-                   reloc->addend == pfunc->offset)
+                   reloc_addend(reloc) == pfunc->offset)
                        break;
 
-               dest_insn = find_insn(file, reloc->sym->sec, reloc->addend);
+               dest_insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
                if (!dest_insn)
                        break;
 
@@ -2052,7 +2028,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
                alt->insn = dest_insn;
                alt->next = insn->alts;
                insn->alts = alt;
-               prev_offset = reloc->offset;
+               prev_offset = reloc_offset(reloc);
        }
 
        if (!prev_offset) {
@@ -2096,7 +2072,7 @@ static struct reloc *find_jump_table(struct objtool_file *file,
                table_reloc = arch_find_switch_table(file, insn);
                if (!table_reloc)
                        continue;
-               dest_insn = find_insn(file, table_reloc->sym->sec, table_reloc->addend);
+               dest_insn = find_insn(file, table_reloc->sym->sec, reloc_addend(table_reloc));
                if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func)
                        continue;
 
@@ -2138,29 +2114,39 @@ static void mark_func_jump_tables(struct objtool_file *file,
                        continue;
 
                reloc = find_jump_table(file, func, insn);
-               if (reloc) {
-                       reloc->jump_table_start = true;
+               if (reloc)
                        insn->_jump_table = reloc;
-               }
        }
 }
 
 static int add_func_jump_tables(struct objtool_file *file,
                                  struct symbol *func)
 {
-       struct instruction *insn;
-       int ret;
+       struct instruction *insn, *insn_t1 = NULL, *insn_t2;
+       int ret = 0;
 
        func_for_each_insn(file, func, insn) {
                if (!insn_jump_table(insn))
                        continue;
 
-               ret = add_jump_table(file, insn, insn_jump_table(insn));
+               if (!insn_t1) {
+                       insn_t1 = insn;
+                       continue;
+               }
+
+               insn_t2 = insn;
+
+               ret = add_jump_table(file, insn_t1, insn_jump_table(insn_t2));
                if (ret)
                        return ret;
+
+               insn_t1 = insn_t2;
        }
 
-       return 0;
+       if (insn_t1)
+               ret = add_jump_table(file, insn_t1, NULL);
+
+       return ret;
 }
 
 /*
@@ -2201,7 +2187,7 @@ static void set_func_state(struct cfi_state *state)
 static int read_unwind_hints(struct objtool_file *file)
 {
        struct cfi_state cfi = init_cfi;
-       struct section *sec, *relocsec;
+       struct section *sec;
        struct unwind_hint *hint;
        struct instruction *insn;
        struct reloc *reloc;
@@ -2211,8 +2197,7 @@ static int read_unwind_hints(struct objtool_file *file)
        if (!sec)
                return 0;
 
-       relocsec = sec->reloc;
-       if (!relocsec) {
+       if (!sec->rsec) {
                WARN("missing .rela.discard.unwind_hints section");
                return -1;
        }
@@ -2233,7 +2218,7 @@ static int read_unwind_hints(struct objtool_file *file)
                        return -1;
                }
 
-               insn = find_insn(file, reloc->sym->sec, reloc->addend);
+               insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
                if (!insn) {
                        WARN("can't find insn for unwind_hints[%d]", i);
                        return -1;
@@ -2292,16 +2277,17 @@ static int read_unwind_hints(struct objtool_file *file)
 
 static int read_noendbr_hints(struct objtool_file *file)
 {
-       struct section *sec;
        struct instruction *insn;
+       struct section *rsec;
        struct reloc *reloc;
 
-       sec = find_section_by_name(file->elf, ".rela.discard.noendbr");
-       if (!sec)
+       rsec = find_section_by_name(file->elf, ".rela.discard.noendbr");
+       if (!rsec)
                return 0;
 
-       list_for_each_entry(reloc, &sec->reloc_list, list) {
-               insn = find_insn(file, reloc->sym->sec, reloc->sym->offset + reloc->addend);
+       for_each_reloc(rsec, reloc) {
+               insn = find_insn(file, reloc->sym->sec,
+                                reloc->sym->offset + reloc_addend(reloc));
                if (!insn) {
                        WARN("bad .discard.noendbr entry");
                        return -1;
@@ -2315,21 +2301,21 @@ static int read_noendbr_hints(struct objtool_file *file)
 
 static int read_retpoline_hints(struct objtool_file *file)
 {
-       struct section *sec;
+       struct section *rsec;
        struct instruction *insn;
        struct reloc *reloc;
 
-       sec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe");
-       if (!sec)
+       rsec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe");
+       if (!rsec)
                return 0;
 
-       list_for_each_entry(reloc, &sec->reloc_list, list) {
+       for_each_reloc(rsec, reloc) {
                if (reloc->sym->type != STT_SECTION) {
-                       WARN("unexpected relocation symbol type in %s", sec->name);
+                       WARN("unexpected relocation symbol type in %s", rsec->name);
                        return -1;
                }
 
-               insn = find_insn(file, reloc->sym->sec, reloc->addend);
+               insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
                if (!insn) {
                        WARN("bad .discard.retpoline_safe entry");
                        return -1;
@@ -2351,21 +2337,21 @@ static int read_retpoline_hints(struct objtool_file *file)
 
 static int read_instr_hints(struct objtool_file *file)
 {
-       struct section *sec;
+       struct section *rsec;
        struct instruction *insn;
        struct reloc *reloc;
 
-       sec = find_section_by_name(file->elf, ".rela.discard.instr_end");
-       if (!sec)
+       rsec = find_section_by_name(file->elf, ".rela.discard.instr_end");
+       if (!rsec)
                return 0;
 
-       list_for_each_entry(reloc, &sec->reloc_list, list) {
+       for_each_reloc(rsec, reloc) {
                if (reloc->sym->type != STT_SECTION) {
-                       WARN("unexpected relocation symbol type in %s", sec->name);
+                       WARN("unexpected relocation symbol type in %s", rsec->name);
                        return -1;
                }
 
-               insn = find_insn(file, reloc->sym->sec, reloc->addend);
+               insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
                if (!insn) {
                        WARN("bad .discard.instr_end entry");
                        return -1;
@@ -2374,17 +2360,17 @@ static int read_instr_hints(struct objtool_file *file)
                insn->instr--;
        }
 
-       sec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
-       if (!sec)
+       rsec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
+       if (!rsec)
                return 0;
 
-       list_for_each_entry(reloc, &sec->reloc_list, list) {
+       for_each_reloc(rsec, reloc) {
                if (reloc->sym->type != STT_SECTION) {
-                       WARN("unexpected relocation symbol type in %s", sec->name);
+                       WARN("unexpected relocation symbol type in %s", rsec->name);
                        return -1;
                }
 
-               insn = find_insn(file, reloc->sym->sec, reloc->addend);
+               insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
                if (!insn) {
                        WARN("bad .discard.instr_begin entry");
                        return -1;
@@ -2398,21 +2384,21 @@ static int read_instr_hints(struct objtool_file *file)
 
 static int read_validate_unret_hints(struct objtool_file *file)
 {
-       struct section *sec;
+       struct section *rsec;
        struct instruction *insn;
        struct reloc *reloc;
 
-       sec = find_section_by_name(file->elf, ".rela.discard.validate_unret");
-       if (!sec)
+       rsec = find_section_by_name(file->elf, ".rela.discard.validate_unret");
+       if (!rsec)
                return 0;
 
-       list_for_each_entry(reloc, &sec->reloc_list, list) {
+       for_each_reloc(rsec, reloc) {
                if (reloc->sym->type != STT_SECTION) {
-                       WARN("unexpected relocation symbol type in %s", sec->name);
+                       WARN("unexpected relocation symbol type in %s", rsec->name);
                        return -1;
                }
 
-               insn = find_insn(file, reloc->sym->sec, reloc->addend);
+               insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
                if (!insn) {
                        WARN("bad .discard.instr_end entry");
                        return -1;
@@ -2427,23 +2413,23 @@ static int read_validate_unret_hints(struct objtool_file *file)
 static int read_intra_function_calls(struct objtool_file *file)
 {
        struct instruction *insn;
-       struct section *sec;
+       struct section *rsec;
        struct reloc *reloc;
 
-       sec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls");
-       if (!sec)
+       rsec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls");
+       if (!rsec)
                return 0;
 
-       list_for_each_entry(reloc, &sec->reloc_list, list) {
+       for_each_reloc(rsec, reloc) {
                unsigned long dest_off;
 
                if (reloc->sym->type != STT_SECTION) {
                        WARN("unexpected relocation symbol type in %s",
-                            sec->name);
+                            rsec->name);
                        return -1;
                }
 
-               insn = find_insn(file, reloc->sym->sec, reloc->addend);
+               insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
                if (!insn) {
                        WARN("bad .discard.intra_function_call entry");
                        return -1;
@@ -3339,15 +3325,15 @@ static inline bool func_uaccess_safe(struct symbol *func)
 static inline const char *call_dest_name(struct instruction *insn)
 {
        static char pvname[19];
-       struct reloc *rel;
+       struct reloc *reloc;
        int idx;
 
        if (insn_call_dest(insn))
                return insn_call_dest(insn)->name;
 
-       rel = insn_reloc(NULL, insn);
-       if (rel && !strcmp(rel->sym->name, "pv_ops")) {
-               idx = (rel->addend / sizeof(void *));
+       reloc = insn_reloc(NULL, insn);
+       if (reloc && !strcmp(reloc->sym->name, "pv_ops")) {
+               idx = (reloc_addend(reloc) / sizeof(void *));
                snprintf(pvname, sizeof(pvname), "pv_ops[%d]", idx);
                return pvname;
        }
@@ -3358,14 +3344,14 @@ static inline const char *call_dest_name(struct instruction *insn)
 static bool pv_call_dest(struct objtool_file *file, struct instruction *insn)
 {
        struct symbol *target;
-       struct reloc *rel;
+       struct reloc *reloc;
        int idx;
 
-       rel = insn_reloc(file, insn);
-       if (!rel || strcmp(rel->sym->name, "pv_ops"))
+       reloc = insn_reloc(file, insn);
+       if (!reloc || strcmp(reloc->sym->name, "pv_ops"))
                return false;
 
-       idx = (arch_dest_reloc_offset(rel->addend) / sizeof(void *));
+       idx = (arch_dest_reloc_offset(reloc_addend(reloc)) / sizeof(void *));
 
        if (file->pv_ops[idx].clean)
                return true;
@@ -4296,8 +4282,8 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn
        for (reloc = insn_reloc(file, insn);
             reloc;
             reloc = find_reloc_by_dest_range(file->elf, insn->sec,
-                                             reloc->offset + 1,
-                                             (insn->offset + insn->len) - (reloc->offset + 1))) {
+                                             reloc_offset(reloc) + 1,
+                                             (insn->offset + insn->len) - (reloc_offset(reloc) + 1))) {
 
                /*
                 * static_call_update() references the trampoline, which
@@ -4307,10 +4293,11 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn
                        continue;
 
                off = reloc->sym->offset;
-               if (reloc->type == R_X86_64_PC32 || reloc->type == R_X86_64_PLT32)
-                       off += arch_dest_reloc_offset(reloc->addend);
+               if (reloc_type(reloc) == R_X86_64_PC32 ||
+                   reloc_type(reloc) == R_X86_64_PLT32)
+                       off += arch_dest_reloc_offset(reloc_addend(reloc));
                else
-                       off += reloc->addend;
+                       off += reloc_addend(reloc);
 
                dest = find_insn(file, reloc->sym->sec, off);
                if (!dest)
@@ -4367,7 +4354,7 @@ static int validate_ibt_data_reloc(struct objtool_file *file,
        struct instruction *dest;
 
        dest = find_insn(file, reloc->sym->sec,
-                        reloc->sym->offset + reloc->addend);
+                        reloc->sym->offset + reloc_addend(reloc));
        if (!dest)
                return 0;
 
@@ -4380,7 +4367,7 @@ static int validate_ibt_data_reloc(struct objtool_file *file,
                return 0;
 
        WARN_FUNC("data relocation to !ENDBR: %s",
-                 reloc->sec->base, reloc->offset,
+                 reloc->sec->base, reloc_offset(reloc),
                  offstr(dest->sec, dest->offset));
 
        return 1;
@@ -4407,7 +4394,7 @@ static int validate_ibt(struct objtool_file *file)
                if (sec->sh.sh_flags & SHF_EXECINSTR)
                        continue;
 
-               if (!sec->reloc)
+               if (!sec->rsec)
                        continue;
 
                /*
@@ -4434,7 +4421,7 @@ static int validate_ibt(struct objtool_file *file)
                    strstr(sec->name, "__patchable_function_entries"))
                        continue;
 
-               list_for_each_entry(reloc, &sec->reloc->reloc_list, list)
+               for_each_reloc(sec->rsec, reloc)
                        warnings += validate_ibt_data_reloc(file, reloc);
        }
 
@@ -4609,6 +4596,34 @@ static int disas_warned_funcs(struct objtool_file *file)
        return 0;
 }
 
+struct insn_chunk {
+       void *addr;
+       struct insn_chunk *next;
+};
+
+/*
+ * Reduce peak RSS usage by freeing insns memory before writing the ELF file,
+ * which can trigger more allocations for .debug_* sections whose data hasn't
+ * been read yet.
+ */
+static void free_insns(struct objtool_file *file)
+{
+       struct instruction *insn;
+       struct insn_chunk *chunks = NULL, *chunk;
+
+       for_each_insn(file, insn) {
+               if (!insn->idx) {
+                       chunk = malloc(sizeof(*chunk));
+                       chunk->addr = insn;
+                       chunk->next = chunks;
+                       chunks = chunk;
+               }
+       }
+
+       for (chunk = chunks; chunk; chunk = chunk->next)
+               free(chunk->addr);
+}
+
 int check(struct objtool_file *file)
 {
        int ret, warnings = 0;
@@ -4755,6 +4770,8 @@ int check(struct objtool_file *file)
                warnings += ret;
        }
 
+       free_insns(file);
+
        if (opts.verbose)
                disas_warned_funcs(file);