objtool: Add elf_create_reloc() helper

commit ef47cc01cb upstream.

We have 4 instances of adding a relocation. Create a common helper
to avoid growing even more.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Borislav Petkov <bp@suse.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Link: https://lkml.kernel.org/r/20210326151259.817438847@infradead.org
[bwh: Backported to 5.10: drop changes in create_mcount_loc_sections()]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Peter Zijlstra 2021-03-26 16:12:07 +01:00 committed by Greg Kroah-Hartman
parent c9049cf480
commit fcdb7926d3
4 changed files with 79 additions and 90 deletions

View file

@ -433,8 +433,7 @@ static int add_dead_ends(struct objtool_file *file)
static int create_static_call_sections(struct objtool_file *file)
{
struct section *sec, *reloc_sec;
struct reloc *reloc;
struct section *sec;
struct static_call_site *site;
struct instruction *insn;
struct symbol *key_sym;
@ -460,8 +459,7 @@ static int create_static_call_sections(struct objtool_file *file)
if (!sec)
return -1;
reloc_sec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
if (!reloc_sec)
if (!elf_create_reloc_section(file->elf, sec, SHT_RELA))
return -1;
idx = 0;
@ -471,25 +469,11 @@ static int create_static_call_sections(struct objtool_file *file)
memset(site, 0, sizeof(struct static_call_site));
/* populate reloc for 'addr' */
reloc = malloc(sizeof(*reloc));
if (!reloc) {
perror("malloc");
if (elf_add_reloc_to_insn(file->elf, sec,
idx * sizeof(struct static_call_site),
R_X86_64_PC32,
insn->sec, insn->offset))
return -1;
}
memset(reloc, 0, sizeof(*reloc));
insn_to_reloc_sym_addend(insn->sec, insn->offset, reloc);
if (!reloc->sym) {
WARN_FUNC("static call tramp: missing containing symbol",
insn->sec, insn->offset);
return -1;
}
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);
@ -526,18 +510,11 @@ static int create_static_call_sections(struct objtool_file *file)
free(key_name);
/* populate reloc for 'key' */
reloc = malloc(sizeof(*reloc));
if (!reloc) {
perror("malloc");
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))
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++;
}

View file

@ -262,32 +262,6 @@ struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, uns
return find_reloc_by_dest_range(elf, sec, offset, 1);
}
void insn_to_reloc_sym_addend(struct section *sec, unsigned long offset,
struct reloc *reloc)
{
if (sec->sym) {
reloc->sym = sec->sym;
reloc->addend = offset;
return;
}
/*
* The Clang assembler strips section symbols, so we have to reference
* the function symbol instead:
*/
reloc->sym = find_symbol_containing(sec, offset);
if (!reloc->sym) {
/*
* Hack alert. This happens when we need to reference the NOP
* pad insn immediately after the function.
*/
reloc->sym = find_symbol_containing(sec, offset - 1);
}
if (reloc->sym)
reloc->addend = offset - reloc->sym->offset;
}
static int read_sections(struct elf *elf)
{
Elf_Scn *s = NULL;
@ -524,14 +498,66 @@ static int read_symbols(struct elf *elf)
return -1;
}
void elf_add_reloc(struct elf *elf, struct reloc *reloc)
int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset,
unsigned int type, struct symbol *sym, int addend)
{
struct section *sec = reloc->sec;
struct reloc *reloc;
list_add_tail(&reloc->list, &sec->reloc_list);
reloc = malloc(sizeof(*reloc));
if (!reloc) {
perror("malloc");
return -1;
}
memset(reloc, 0, sizeof(*reloc));
reloc->sec = sec->reloc;
reloc->offset = offset;
reloc->type = type;
reloc->sym = sym;
reloc->addend = addend;
list_add_tail(&reloc->list, &sec->reloc->reloc_list);
elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc));
sec->changed = true;
sec->reloc->changed = true;
return 0;
}
int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int type,
struct section *insn_sec, unsigned long insn_off)
{
struct symbol *sym;
int addend;
if (insn_sec->sym) {
sym = insn_sec->sym;
addend = insn_off;
} else {
/*
* The Clang assembler strips section symbols, so we have to
* reference the function symbol instead:
*/
sym = find_symbol_containing(insn_sec, insn_off);
if (!sym) {
/*
* Hack alert. This happens when we need to reference
* the NOP pad insn immediately after the function.
*/
sym = find_symbol_containing(insn_sec, insn_off - 1);
}
if (!sym) {
WARN("can't find symbol containing %s+0x%lx", insn_sec->name, insn_off);
return -1;
}
addend = insn_off - sym->offset;
}
return elf_add_reloc(elf, sec, offset, type, sym, addend);
}
static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx)

View file

@ -123,7 +123,13 @@ static inline u32 reloc_hash(struct reloc *reloc)
struct elf *elf_open_read(const char *name, int flags);
struct section *elf_create_section(struct elf *elf, const char *name, unsigned int sh_flags, size_t entsize, int nr);
struct section *elf_create_reloc_section(struct elf *elf, struct section *base, int reltype);
void elf_add_reloc(struct elf *elf, struct reloc *reloc);
int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset,
unsigned int type, struct symbol *sym, int addend);
int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int type,
struct section *insn_sec, unsigned long insn_off);
int elf_write_insn(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int len,
const char *insn);
@ -140,8 +146,6 @@ struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, uns
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
unsigned long offset, unsigned int len);
struct symbol *find_func_containing(struct section *sec, unsigned long offset);
void insn_to_reloc_sym_addend(struct section *sec, unsigned long offset,
struct reloc *reloc);
#define for_each_sec(file, sec) \
list_for_each_entry(sec, &file->elf->sections, list)

View file

@ -81,37 +81,20 @@ static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi)
}
static int write_orc_entry(struct elf *elf, struct section *orc_sec,
struct section *ip_rsec, unsigned int idx,
struct section *ip_sec, unsigned int idx,
struct section *insn_sec, unsigned long insn_off,
struct orc_entry *o)
{
struct orc_entry *orc;
struct reloc *reloc;
/* populate ORC data */
orc = (struct orc_entry *)orc_sec->data->d_buf + idx;
memcpy(orc, o, sizeof(*orc));
/* populate reloc for ip */
reloc = malloc(sizeof(*reloc));
if (!reloc) {
perror("malloc");
if (elf_add_reloc_to_insn(elf, ip_sec, idx * sizeof(int), R_X86_64_PC32,
insn_sec, insn_off))
return -1;
}
memset(reloc, 0, sizeof(*reloc));
insn_to_reloc_sym_addend(insn_sec, insn_off, reloc);
if (!reloc->sym) {
WARN("missing symbol for insn at offset 0x%lx",
insn_off);
return -1;
}
reloc->type = R_X86_64_PC32;
reloc->offset = idx * sizeof(int);
reloc->sec = ip_rsec;
elf_add_reloc(elf, reloc);
return 0;
}
@ -150,7 +133,7 @@ static unsigned long alt_group_len(struct alt_group *alt_group)
int orc_create(struct objtool_file *file)
{
struct section *sec, *ip_rsec, *orc_sec;
struct section *sec, *orc_sec;
unsigned int nr = 0, idx = 0;
struct orc_list_entry *entry;
struct list_head orc_list;
@ -239,13 +222,12 @@ int orc_create(struct objtool_file *file)
sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr);
if (!sec)
return -1;
ip_rsec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
if (!ip_rsec)
if (!elf_create_reloc_section(file->elf, sec, SHT_RELA))
return -1;
/* Write ORC entries to sections: */
list_for_each_entry(entry, &orc_list, list) {
if (write_orc_entry(file->elf, orc_sec, ip_rsec, idx++,
if (write_orc_entry(file->elf, orc_sec, sec, idx++,
entry->insn_sec, entry->insn_off,
&entry->orc))
return -1;