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

