Instead of calling elf_newdata() for each new piece of data with its own
separate buffer, keep it all in the same growable buffer so the
section's entire data can be accessed if needed.

Signed-off-by: Josh Poimboeuf <[email protected]>
---
 tools/objtool/elf.c                 | 123 ++++++++++++++--------------
 tools/objtool/include/objtool/elf.h |  13 ++-
 2 files changed, 71 insertions(+), 65 deletions(-)

diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 33c95a74a51bd..e09bb0a63be35 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -1134,9 +1134,6 @@ static int read_relocs(struct elf *elf)
 
                rsec->base->rsec = rsec;
 
-               /* nr_alloc_relocs=0: libelf owns d_buf */
-               rsec->nr_alloc_relocs = 0;
-
                rsec->relocs = calloc(sec_num_entries(rsec), sizeof(*reloc));
                if (!rsec->relocs) {
                        ERROR_GLIBC("calloc");
@@ -1395,7 +1392,7 @@ unsigned int elf_add_string(struct elf *elf, struct 
section *strtab, const char
 
 void *elf_add_data(struct elf *elf, struct section *sec, const void *data, 
size_t size)
 {
-       unsigned long offset;
+       unsigned long offset, size_old, size_new, alloc_size_old, 
alloc_size_new;
        Elf_Scn *s;
 
        if (!sec->sh.sh_addralign) {
@@ -1409,30 +1406,55 @@ void *elf_add_data(struct elf *elf, struct section 
*sec, const void *data, size_
                return NULL;
        }
 
-       sec->data = elf_newdata(s);
        if (!sec->data) {
-               ERROR_ELF("elf_newdata");
-               return NULL;
+               sec->data = elf_newdata(s);
+               if (!sec->data) {
+                       ERROR_ELF("elf_newdata");
+                       return NULL;
+               }
+
+               sec->data->d_align = sec->sh.sh_addralign;
        }
 
-       sec->data->d_buf = calloc(1, size);
-       if (!sec->data->d_buf) {
-               ERROR_GLIBC("calloc");
-               return NULL;
+       size_old = sec->data->d_size;
+       offset = ALIGN(size_old, sec->sh.sh_addralign);
+       size_new = offset + size;
+
+       if (!sec->data_overallocated)
+               alloc_size_old = size_old;
+       else
+               alloc_size_old = max(64UL, roundup_pow_of_two(size_old ? : 1));
+
+       alloc_size_new = max(64UL, roundup_pow_of_two(size_new ? : 1));
+
+       if (alloc_size_new > alloc_size_old) {
+               void *orig_buf = sec->data->d_buf;
+
+               sec->data->d_buf = calloc(1, alloc_size_new);
+               if (!sec->data->d_buf) {
+                       ERROR_GLIBC("calloc");
+                       return NULL;
+               }
+
+               if (size_old)
+                       memcpy(sec->data->d_buf, orig_buf, size_old);
+
+               if (orig_buf && sec->data_owned)
+                       free(orig_buf);
+
+               sec->data_owned = 1;
+               sec->data_overallocated = 1;
        }
 
        if (data)
-               memcpy(sec->data->d_buf, data, size);
-
-       sec->data->d_size = size;
-       sec->data->d_align = sec->sh.sh_addralign;
-
-       offset = ALIGN(sec_size(sec), sec->sh.sh_addralign);
-       sec->sh.sh_size = offset + size;
+               memcpy(sec->data->d_buf + offset, data, size);
+       else
+               memset(sec->data->d_buf + offset, 0, size);
 
+       sec->data->d_size = size_new;
+       sec->sh.sh_size = size_new;
        mark_sec_changed(elf, sec, true);
-
-       return sec->data->d_buf;
+       return sec->data->d_buf + offset;
 }
 
 struct section *elf_create_section(struct elf *elf, const char *name,
@@ -1483,6 +1505,8 @@ struct section *elf_create_section(struct elf *elf, const 
char *name,
                        ERROR_GLIBC("calloc");
                        return NULL;
                }
+
+               sec->data_owned = 1;
        }
 
        if (!gelf_getshdr(s, &sec->sh)) {
@@ -1533,60 +1557,33 @@ static int elf_alloc_reloc(struct elf *elf, struct 
section *rsec)
        struct reloc *old_relocs, *old_relocs_end, *new_relocs;
        unsigned int nr_relocs_old = sec_num_entries(rsec);
        unsigned int nr_relocs_new = nr_relocs_old + 1;
-       unsigned long nr_alloc;
+       unsigned long nr_alloc_old = 0, nr_alloc_new;
        struct symbol *sym;
 
-       if (!rsec->data) {
-               rsec->data = elf_newdata(elf_getscn(elf->elf, rsec->idx));
-               if (!rsec->data) {
-                       ERROR_ELF("elf_newdata");
-                       return -1;
-               }
+       if (!elf_add_data(elf, rsec, NULL, elf_rela_size(elf)))
+               return -1;
 
-               rsec->data->d_align = 1;
-               rsec->data->d_type = ELF_T_RELA;
-               rsec->data->d_buf = NULL;
-       }
+       rsec->data->d_type = ELF_T_RELA;
 
-       rsec->data->d_size = nr_relocs_new * elf_rela_size(elf);
-       rsec->sh.sh_size   = rsec->data->d_size;
+       if (rsec->relocs_overallocated)
+               nr_alloc_old = max(64UL, roundup_pow_of_two(nr_relocs_old ? : 
1));
+       else
+               nr_alloc_old = nr_relocs_old;
 
-       nr_alloc = max(64UL, roundup_pow_of_two(nr_relocs_new));
-       if (nr_alloc <= rsec->nr_alloc_relocs)
+       nr_alloc_new = max(64UL, roundup_pow_of_two(nr_relocs_new ? : 1));
+
+       if (nr_alloc_old == nr_alloc_new)
                return 0;
 
-       if (rsec->data->d_buf && !rsec->nr_alloc_relocs) {
-               void *orig_buf = rsec->data->d_buf;
-
-               /*
-                * The original d_buf is owned by libelf so it can't be
-                * realloced.
-                */
-               rsec->data->d_buf = malloc(nr_alloc * elf_rela_size(elf));
-               if (!rsec->data->d_buf) {
-                       ERROR_GLIBC("malloc");
-                       return -1;
-               }
-               memcpy(rsec->data->d_buf, orig_buf,
-                      nr_relocs_old * elf_rela_size(elf));
-       } else {
-               rsec->data->d_buf = realloc(rsec->data->d_buf,
-                                           nr_alloc * elf_rela_size(elf));
-               if (!rsec->data->d_buf) {
-                       ERROR_GLIBC("realloc");
-                       return -1;
-               }
-       }
-
-       rsec->nr_alloc_relocs = nr_alloc;
-
-       old_relocs = rsec->relocs;
-       new_relocs = calloc(nr_alloc, sizeof(struct reloc));
+       new_relocs = calloc(nr_alloc_new, sizeof(struct reloc));
        if (!new_relocs) {
                ERROR_GLIBC("calloc");
                return -1;
        }
 
+       rsec->relocs_overallocated = 1;
+
+       old_relocs = rsec->relocs;
        if (!old_relocs)
                goto done;
 
@@ -1631,6 +1628,7 @@ static int elf_alloc_reloc(struct elf *elf, struct 
section *rsec)
        }
 
        free(old_relocs);
+
 done:
        rsec->relocs = new_relocs;
        return 0;
@@ -1660,7 +1658,6 @@ struct section *elf_create_rela_section(struct elf *elf, 
struct section *sec,
        if (nr_relocs) {
                rsec->data->d_type = ELF_T_RELA;
 
-               rsec->nr_alloc_relocs = nr_relocs;
                rsec->relocs = calloc(nr_relocs, sizeof(struct reloc));
                if (!rsec->relocs) {
                        ERROR_GLIBC("calloc");
diff --git a/tools/objtool/include/objtool/elf.h 
b/tools/objtool/include/objtool/elf.h
index d9c44df9cc76a..0801fcad516bb 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -58,9 +58,18 @@ struct section {
        Elf_Data *data;
        const char *name;
        int idx;
-       bool _changed, text, rodata, noinstr, init, truncate;
+       u32 _changed                    : 1,
+           text                        : 1,
+           rodata                      : 1,
+           noinstr                     : 1,
+           init                        : 1,
+           truncate                    : 1,
+           data_owned                  : 1,
+           data_overallocated          : 1,
+           relocs_overallocated        : 1;
+           /* 23 bit hole */
+
        struct reloc *relocs;
-       unsigned long nr_alloc_relocs;
        struct section *twin;
 };
 
-- 
2.53.0


Reply via email to