summaryrefslogtreecommitdiff
path: root/tools/objtool/elf.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/objtool/elf.c')
-rw-r--r--tools/objtool/elf.c151
1 files changed, 91 insertions, 60 deletions
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 67967c231972..5cbc9d578a45 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -533,41 +533,6 @@ err:
return -1;
}
-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_rela_section(elf, sec))
- return -1;
-
- reloc = malloc(sizeof(*reloc));
- if (!reloc) {
- perror("malloc");
- return -1;
- }
- memset(reloc, 0, sizeof(*reloc));
-
- reloc->sec = sec->rsec;
- reloc->offset = offset;
- reloc->type = type;
- reloc->sym = sym;
- reloc->addend = addend;
-
- list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list);
- list_add_tail(&reloc->list, &sec->rsec->reloc_list);
- elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
-
- sec->rsec->sh.sh_size += sec->rsec->sh.sh_entsize;
-
- mark_sec_changed(elf, sec->rsec, true);
-
- return 0;
-}
-
/*
* Ensure that any reloc section containing references to @sym is marked
* changed such that it will get re-generated in elf_rebuild_reloc_sections()
@@ -841,13 +806,57 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size)
return sym;
}
-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)
+static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
+ unsigned int reloc_idx,
+ unsigned long offset, struct symbol *sym,
+ s64 addend, unsigned int type)
+{
+ struct reloc *reloc;
+
+ if (reloc_idx >= rsec->sh.sh_size / elf_rela_size(elf)) {
+ WARN("%s: bad reloc_idx %u for %s with size 0x%lx",
+ __func__, reloc_idx, rsec->name, rsec->sh.sh_size);
+ return NULL;
+ }
+
+ reloc = malloc(sizeof(*reloc));
+ if (!reloc) {
+ perror("malloc");
+ return NULL;
+ }
+ memset(reloc, 0, sizeof(*reloc));
+
+ reloc->idx = reloc_idx;
+ reloc->sec = rsec;
+ reloc->offset = offset;
+ reloc->type = type;
+ reloc->sym = sym;
+ reloc->addend = addend;
+
+ list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list);
+ list_add_tail(&reloc->list, &rsec->reloc_list);
+ elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
+
+ mark_sec_changed(elf, rsec, true);
+
+ return reloc;
+}
+
+struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
+ unsigned long offset,
+ unsigned int reloc_idx,
+ struct section *insn_sec,
+ unsigned long insn_off)
{
struct symbol *sym = insn_sec->sym;
int addend = insn_off;
+ if (!(insn_sec->sh.sh_flags & SHF_EXECINSTR)) {
+ WARN("bad call to %s() for data symbol %s",
+ __func__, sym->name);
+ return NULL;
+ }
+
if (!sym) {
/*
* Due to how weak functions work, we must use section based
@@ -857,12 +866,29 @@ int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,
*/
sym = elf_create_section_symbol(elf, insn_sec);
if (!sym)
- return -1;
+ return NULL;
insn_sec->sym = sym;
}
- return elf_add_reloc(elf, sec, offset, type, sym, addend);
+ return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend,
+ elf_text_rela_type(elf));
+}
+
+struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
+ unsigned long offset,
+ unsigned int reloc_idx,
+ struct symbol *sym,
+ s64 addend)
+{
+ if (sym->sec && (sec->sh.sh_flags & SHF_EXECINSTR)) {
+ WARN("bad call to %s() for text symbol %s",
+ __func__, sym->name);
+ return NULL;
+ }
+
+ return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend,
+ elf_data_rela_type(elf));
}
static int read_reloc(struct section *rsec, int i, struct reloc *reloc)
@@ -1048,7 +1074,7 @@ static int elf_add_string(struct elf *elf, struct section *strtab, char *str)
}
struct section *elf_create_section(struct elf *elf, const char *name,
- size_t entsize, int nr)
+ size_t entsize, unsigned int nr)
{
struct section *sec, *shstrtab;
size_t size = entsize * nr;
@@ -1129,7 +1155,8 @@ struct section *elf_create_section(struct elf *elf, const char *name,
}
static struct section *elf_create_rela_section(struct elf *elf,
- struct section *sec)
+ struct section *sec,
+ unsigned int reloc_nr)
{
struct section *rsec;
char *rsec_name;
@@ -1142,46 +1169,50 @@ static struct section *elf_create_rela_section(struct elf *elf,
strcpy(rsec_name, ".rela");
strcat(rsec_name, sec->name);
- rsec = elf_create_section(elf, rsec_name, elf_rela_size(elf), 0);
+ rsec = elf_create_section(elf, rsec_name, elf_rela_size(elf), reloc_nr);
free(rsec_name);
if (!rsec)
return NULL;
- sec->rsec = rsec;
- rsec->base = sec;
-
+ rsec->data->d_type = ELF_T_RELA;
rsec->sh.sh_type = SHT_RELA;
rsec->sh.sh_addralign = elf_addr_size(elf);
rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
rsec->sh.sh_info = sec->idx;
rsec->sh.sh_flags = SHF_INFO_LINK;
+ sec->rsec = rsec;
+ rsec->base = sec;
+
return rsec;
}
+struct section *elf_create_section_pair(struct elf *elf, const char *name,
+ size_t entsize, unsigned int nr,
+ unsigned int reloc_nr)
+{
+ struct section *sec;
+
+ sec = elf_create_section(elf, name, entsize, nr);
+ if (!sec)
+ return NULL;
+
+ if (!elf_create_rela_section(elf, sec, reloc_nr))
+ return NULL;
+
+ return sec;
+}
+
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, ret;
- void *buf;
-
- /* Allocate a buffer for relocations */
- 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 = 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 (rela) {
+ if (rsec->sh.sh_type == SHT_RELA) {
reloc->rela.r_addend = reloc->addend;
ret = gelf_update_rela(rsec->data, idx, &reloc->rela);
} else {