Skip to content

Commit

Permalink
objtool: Consolidate rel/rela handling
Browse files Browse the repository at this point in the history
The GElf_Rel[a] structs have more similarities than differences.  It's
safe to hard-code the assumptions about their shared fields as they will
never change.  Consolidate their handling where possible, getting rid of
duplicated code.

Also, at least for now we only ever create rela sections, so simplify
the relocation creation code to be rela-only.

Link: https://lore.kernel.org/r/dcabf6df400ca500ea929f1e4284f5e5ec0b27c8.1685464332.git.jpoimboe@kernel.org
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
  • Loading branch information
Josh Poimboeuf committed Jun 7, 2023
1 parent a5bd623 commit 53257a9
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 159 deletions.
12 changes: 6 additions & 6 deletions tools/objtool/check.c
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,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;
Expand All @@ -971,25 +971,25 @@ 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", addrsize, idx);
sec = elf_create_section(file->elf, "__mcount_loc", addr_size, 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);
memset(loc, 0, addr_size);

if (elf_add_reloc_to_insn(file->elf, sec, idx,
addrsize == sizeof(u64) ? R_ABS64 : R_ABS32,
addr_size == sizeof(u64) ? R_ABS64 : R_ABS32,
insn->sec, insn->offset))
return -1;

idx += addrsize;
idx += addr_size;
}

return 0;
Expand Down
202 changes: 54 additions & 148 deletions tools/objtool/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -533,16 +533,15 @@ static int read_symbols(struct elf *elf)
return -1;
}

static struct section *elf_create_reloc_section(struct elf *elf,
struct section *sec,
int reltype);
static struct section *elf_create_rela_section(struct elf *elf,
struct section *sec);

int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset,
unsigned int type, struct symbol *sym, s64 addend)
{
struct reloc *reloc;

if (!sec->rsec && !elf_create_reloc_section(elf, sec, SHT_RELA))
if (!sec->rsec && !elf_create_rela_section(elf, sec))
return -1;

reloc = malloc(sizeof(*reloc));
Expand Down Expand Up @@ -865,29 +864,25 @@ int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,
return elf_add_reloc(elf, sec, offset, type, sym, addend);
}

static int read_rel_reloc(struct section *rsec, int i, struct reloc *reloc, unsigned int *symndx)
static int read_reloc(struct section *rsec, int i, struct reloc *reloc)
{
if (!gelf_getrel(rsec->data, i, &reloc->rel)) {
WARN_ELF("gelf_getrel");
return -1;
}
reloc->type = GELF_R_TYPE(reloc->rel.r_info);
reloc->addend = 0;
reloc->offset = reloc->rel.r_offset;
*symndx = GELF_R_SYM(reloc->rel.r_info);
return 0;
}
bool rela = rsec->sh.sh_type == SHT_RELA;
void *retp;

static int read_rela_reloc(struct section *rsec, int i, struct reloc *reloc, unsigned int *symndx)
{
if (!gelf_getrela(rsec->data, i, &reloc->rela)) {
if (rela)
retp = gelf_getrela(rsec->data, i, &reloc->rela);
else
retp = gelf_getrel(rsec->data, i, &reloc->rel);

if (!retp) {
WARN_ELF("gelf_getrela");
return -1;
}
reloc->type = GELF_R_TYPE(reloc->rela.r_info);
reloc->addend = reloc->rela.r_addend;
reloc->offset = reloc->rela.r_offset;
*symndx = GELF_R_SYM(reloc->rela.r_info);

reloc->offset = reloc->rel.r_offset;
reloc->type = GELF_R_TYPE(reloc->rel.r_info);
reloc->addend = rela ? reloc->rela.r_addend : 0;

return 0;
}

Expand Down Expand Up @@ -926,20 +921,13 @@ static int read_relocs(struct elf *elf)
}
for (i = 0; i < rsec->sh.sh_size / rsec->sh.sh_entsize; i++) {
reloc = &rsec->reloc_data[i];
switch (rsec->sh.sh_type) {
case SHT_REL:
if (read_rel_reloc(rsec, i, reloc, &symndx))
return -1;
break;
case SHT_RELA:
if (read_rela_reloc(rsec, i, reloc, &symndx))
return -1;
break;
default: return -1;
}

if (read_reloc(rsec, i, reloc))
return -1;

reloc->sec = rsec;
reloc->idx = i;
symndx = GELF_R_SYM(reloc->rel.r_info);
reloc->sym = sym = find_symbol_by_index(elf, symndx);
if (!reloc->sym) {
WARN("can't find reloc entry symbol %d for %s",
Expand Down Expand Up @@ -1141,86 +1129,42 @@ struct section *elf_create_section(struct elf *elf, const char *name,
return sec;
}

static struct section *elf_create_rel_reloc_section(struct elf *elf,
struct section *sec)
static struct section *elf_create_rela_section(struct elf *elf,
struct section *sec)
{
char *relocname;
struct section *rsec;
char *rsec_name;

relocname = malloc(strlen(sec->name) + strlen(".rel") + 1);
if (!relocname) {
rsec_name = malloc(strlen(sec->name) + strlen(".rela") + 1);
if (!rsec_name) {
perror("malloc");
return NULL;
}
strcpy(relocname, ".rel");
strcat(relocname, sec->name);
strcpy(rsec_name, ".rela");
strcat(rsec_name, sec->name);

rsec = elf_create_section(elf, relocname, sizeof(GElf_Rel), 0);
free(relocname);
rsec = elf_create_section(elf, rsec_name, elf_rela_size(elf), 0);
free(rsec_name);
if (!rsec)
return NULL;

sec->rsec = rsec;
rsec->base = sec;

rsec->sh.sh_type = SHT_REL;
rsec->sh.sh_addralign = 8;
rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
rsec->sh.sh_info = sec->idx;
rsec->sh.sh_flags = SHF_INFO_LINK;

return rsec;
}

static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base)
{
char *relocname;
struct section *rsec;
int addrsize = elf_class_addrsize(elf);

relocname = malloc(strlen(base->name) + strlen(".rela") + 1);
if (!relocname) {
perror("malloc");
return NULL;
}
strcpy(relocname, ".rela");
strcat(relocname, base->name);

if (addrsize == sizeof(u32))
rsec = elf_create_section(elf, relocname, sizeof(Elf32_Rela), 0);
else
rsec = elf_create_section(elf, relocname, sizeof(GElf_Rela), 0);
free(relocname);
if (!rsec)
return NULL;

base->rsec = rsec;
rsec->base = base;

rsec->sh.sh_type = SHT_RELA;
rsec->sh.sh_addralign = addrsize;
rsec->sh.sh_addralign = elf_addr_size(elf);
rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
rsec->sh.sh_info = base->idx;
rsec->sh.sh_info = sec->idx;
rsec->sh.sh_flags = SHF_INFO_LINK;

return rsec;
}

static struct section *elf_create_reloc_section(struct elf *elf,
struct section *base,
int reltype)
{
switch (reltype) {
case SHT_REL: return elf_create_rel_reloc_section(elf, base);
case SHT_RELA: return elf_create_rela_reloc_section(elf, base);
default: return NULL;
}
}

static int elf_rebuild_rel_reloc_section(struct section *rsec)
static int elf_rebuild_reloc_section(struct elf *elf, struct section *rsec)
{
bool rela = rsec->sh.sh_type == SHT_RELA;
struct reloc *reloc;
int idx = 0;
int idx = 0, ret;
void *buf;

/* Allocate a buffer for relocations */
Expand All @@ -1232,46 +1176,20 @@ static int elf_rebuild_rel_reloc_section(struct section *rsec)

rsec->data->d_buf = buf;
rsec->data->d_size = rsec->sh.sh_size;
rsec->data->d_type = ELF_T_REL;
rsec->data->d_type = rela ? ELF_T_RELA : ELF_T_REL;

idx = 0;
list_for_each_entry(reloc, &rsec->reloc_list, list) {
reloc->rel.r_offset = reloc->offset;
reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
if (!gelf_update_rel(rsec->data, idx, &reloc->rel)) {
WARN_ELF("gelf_update_rel");
return -1;
if (rela) {
reloc->rela.r_addend = reloc->addend;
ret = gelf_update_rela(rsec->data, idx, &reloc->rela);
} else {
ret = gelf_update_rel(rsec->data, idx, &reloc->rel);
}
idx++;
}

return 0;
}

static int elf_rebuild_rela_reloc_section(struct section *rsec)
{
struct reloc *reloc;
int idx = 0;
void *buf;

/* Allocate a buffer for relocations with addends */
buf = malloc(rsec->sh.sh_size);
if (!buf) {
perror("malloc");
return -1;
}

rsec->data->d_buf = buf;
rsec->data->d_size = rsec->sh.sh_size;
rsec->data->d_type = ELF_T_RELA;

idx = 0;
list_for_each_entry(reloc, &rsec->reloc_list, list) {
reloc->rela.r_offset = reloc->offset;
reloc->rela.r_addend = reloc->addend;
reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
if (!gelf_update_rela(rsec->data, idx, &reloc->rela)) {
WARN_ELF("gelf_update_rela");
if (!ret) {
WARN_ELF("gelf_update_rel");
return -1;
}
idx++;
Expand All @@ -1280,15 +1198,6 @@ static int elf_rebuild_rela_reloc_section(struct section *rsec)
return 0;
}

static int elf_rebuild_reloc_section(struct elf *elf, struct section *rsec)
{
switch (rsec->sh.sh_type) {
case SHT_REL: return elf_rebuild_rel_reloc_section(rsec);
case SHT_RELA: return elf_rebuild_rela_reloc_section(rsec);
default: return -1;
}
}

int elf_write_insn(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int len,
const char *insn)
Expand All @@ -1311,24 +1220,21 @@ int elf_write_insn(struct elf *elf, struct section *sec,
int elf_write_reloc(struct elf *elf, struct reloc *reloc)
{
struct section *rsec = reloc->sec;
int ret;

if (rsec->sh.sh_type == SHT_REL) {
reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
reloc->rel.r_offset = reloc->offset;
reloc->rel.r_offset = reloc->offset;
reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);

if (!gelf_update_rel(rsec->data, reloc->idx, &reloc->rel)) {
WARN_ELF("gelf_update_rel");
return -1;
}
} else {
reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
if (rsec->sh.sh_type == SHT_RELA) {
reloc->rela.r_addend = reloc->addend;
reloc->rela.r_offset = reloc->offset;
ret = gelf_update_rela(rsec->data, reloc->idx, &reloc->rela);
} else {
ret = gelf_update_rel(rsec->data, reloc->idx, &reloc->rel);
}

if (!gelf_update_rela(rsec->data, reloc->idx, &reloc->rela)) {
WARN_ELF("gelf_update_rela");
return -1;
}
if (!ret) {
WARN_ELF("gelf_update_rela");
return -1;
}

elf->changed = true;
Expand Down
13 changes: 8 additions & 5 deletions tools/objtool/include/objtool/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <linux/hashtable.h>
#include <linux/rbtree.h>
#include <linux/jhash.h>
#include <arch/elf.h>

#ifdef LIBELF_USE_DEPRECATED
# define elf_getshdrnum elf_getshnum
Expand Down Expand Up @@ -147,12 +148,14 @@ static inline bool has_multiple_files(struct elf *elf)
return elf->num_files > 1;
}

static inline int elf_class_addrsize(struct elf *elf)
static inline size_t elf_addr_size(struct elf *elf)
{
if (elf->ehdr.e_ident[EI_CLASS] == ELFCLASS32)
return sizeof(u32);
else
return sizeof(u64);
return elf->ehdr.e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
}

static inline size_t elf_rela_size(struct elf *elf)
{
return elf_addr_size(elf) == 4 ? sizeof(Elf32_Rela) : sizeof(Elf64_Rela);
}

#define for_each_sec(file, sec) \
Expand Down

0 comments on commit 53257a9

Please sign in to comment.