For duplicate strings, elf_add_string() just blindly adds duplicates. That can be a problem for arm64 which often uses two consecutive instructions (and corresponding relocations) to put an address into a register, like:
d8: 90000001 adrp x1, 0 <meminfo_proc_show> d8: R_AARCH64_ADR_PREL_PG_HI21 .rodata.meminfo_proc_show.str1.8 dc: 91000021 add x1, x1, #0x0 dc: R_AARCH64_ADD_ABS_LO12_NC .rodata.meminfo_proc_show.str1.8 Referencing two different addresses in the ADRP+ADD pair would corrupt the memory access. Avoid that by detecting and reusing duplicates when cloning string relocs. Signed-off-by: Josh Poimboeuf <[email protected]> --- tools/objtool/elf.c | 29 +++++++++++++++++++++++------ tools/objtool/include/objtool/elf.h | 3 ++- tools/objtool/klp-diff.c | 4 +++- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index e09bb0a63be35..065ccfeb98288 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -1366,9 +1366,27 @@ struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name) return elf; } -unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char *str) +int elf_find_string(struct elf *elf, struct section *strtab, const char *str) { - unsigned int offset; + char *d_buf; + int i; + + if (!strtab->data) + return -1; + + d_buf = strtab->data->d_buf; + + for (i = 0; i < strtab->data->d_size; i += strlen(d_buf + i) + 1) { + if (!strcmp(d_buf + i, str)) + return i; + } + + return -1; +} + +int elf_add_string(struct elf *elf, struct section *strtab, const char *str) +{ + void *data; if (!strtab) strtab = find_section_by_name(elf, ".strtab"); @@ -1382,12 +1400,11 @@ unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char return -1; } - offset = ALIGN(sec_size(strtab), strtab->sh.sh_addralign); - - if (!elf_add_data(elf, strtab, str, strlen(str) + 1)) + data = elf_add_data(elf, strtab, str, strlen(str) + 1); + if (!data) return -1; - return offset; + return data - strtab->data->d_buf; } void *elf_add_data(struct elf *elf, struct section *sec, const void *data, size_t size) diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index 0801fcad516bb..d895023674673 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -187,7 +187,8 @@ struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec); void *elf_add_data(struct elf *elf, struct section *sec, const void *data, size_t size); -unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char *str); +int elf_find_string(struct elf *elf, struct section *strtab, const char *str); +int elf_add_string(struct elf *elf, struct section *strtab, const char *str); struct reloc *elf_create_reloc(struct elf *elf, struct section *sec, unsigned long offset, struct symbol *sym, diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 6a1cec57dc6a3..6957292e455e4 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -1509,7 +1509,9 @@ static int clone_reloc(struct elfs *e, struct reloc *patched_reloc, __dbg_clone("\"%s\"", escape_str(str)); - addend = elf_add_string(e->out, out_sym->sec, str); + addend = elf_find_string(e->out, out_sym->sec, str); + if (addend == -1) + addend = elf_add_string(e->out, out_sym->sec, str); if (addend == -1) return -1; } -- 2.53.0

