summaryrefslogtreecommitdiffstats
path: root/tools/objtool/elf.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/objtool/elf.c')
-rw-r--r--tools/objtool/elf.c145
1 files changed, 128 insertions, 17 deletions
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 3160931e858c..95d86bcb9512 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -496,6 +496,32 @@ void elf_add_reloc(struct elf *elf, struct reloc *reloc)
elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc));
}
+static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx)
+{
+ if (!gelf_getrel(sec->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;
+}
+
+static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx)
+{
+ if (!gelf_getrela(sec->data, i, &reloc->rela)) {
+ 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);
+ return 0;
+}
+
static int read_relocs(struct elf *elf)
{
struct section *sec;
@@ -505,7 +531,8 @@ static int read_relocs(struct elf *elf)
unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0;
list_for_each_entry(sec, &elf->sections, list) {
- if (sec->sh.sh_type != SHT_RELA)
+ if ((sec->sh.sh_type != SHT_RELA) &&
+ (sec->sh.sh_type != SHT_REL))
continue;
sec->base = find_section_by_index(elf, sec->sh.sh_info);
@@ -525,16 +552,17 @@ static int read_relocs(struct elf *elf)
return -1;
}
memset(reloc, 0, sizeof(*reloc));
-
- if (!gelf_getrela(sec->data, i, &reloc->rela)) {
- WARN_ELF("gelf_getrela");
- return -1;
+ switch (sec->sh.sh_type) {
+ case SHT_REL:
+ if (read_rel_reloc(sec, i, reloc, &symndx))
+ return -1;
+ break;
+ case SHT_RELA:
+ if (read_rela_reloc(sec, i, reloc, &symndx))
+ return -1;
+ break;
+ default: 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->sym = find_symbol_by_index(elf, symndx);
reloc->sec = sec;
if (!reloc->sym) {
@@ -722,7 +750,37 @@ struct section *elf_create_section(struct elf *elf, const char *name,
return sec;
}
-struct section *elf_create_reloc_section(struct elf *elf, struct section *base)
+static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base)
+{
+ char *relocname;
+ struct section *sec;
+
+ relocname = malloc(strlen(base->name) + strlen(".rel") + 1);
+ if (!relocname) {
+ perror("malloc");
+ return NULL;
+ }
+ strcpy(relocname, ".rel");
+ strcat(relocname, base->name);
+
+ sec = elf_create_section(elf, relocname, sizeof(GElf_Rel), 0);
+ free(relocname);
+ if (!sec)
+ return NULL;
+
+ base->reloc = sec;
+ sec->base = base;
+
+ sec->sh.sh_type = SHT_REL;
+ sec->sh.sh_addralign = 8;
+ sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
+ sec->sh.sh_info = base->idx;
+ sec->sh.sh_flags = SHF_INFO_LINK;
+
+ return sec;
+}
+
+static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base)
{
char *relocname;
struct section *sec;
@@ -752,16 +810,53 @@ struct section *elf_create_reloc_section(struct elf *elf, struct section *base)
return sec;
}
-int elf_rebuild_reloc_section(struct section *sec)
+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 *sec, int nr)
{
struct reloc *reloc;
- int nr, idx = 0, size;
- GElf_Rela *relocs;
+ int idx = 0, size;
+ GElf_Rel *relocs;
- nr = 0;
- list_for_each_entry(reloc, &sec->reloc_list, list)
- nr++;
+ /* Allocate a buffer for relocations */
+ size = nr * sizeof(*relocs);
+ relocs = malloc(size);
+ if (!relocs) {
+ perror("malloc");
+ return -1;
+ }
+
+ sec->data->d_buf = relocs;
+ sec->data->d_size = size;
+
+ sec->sh.sh_size = size;
+
+ idx = 0;
+ list_for_each_entry(reloc, &sec->reloc_list, list) {
+ relocs[idx].r_offset = reloc->offset;
+ relocs[idx].r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
+ idx++;
+ }
+
+ return 0;
+}
+static int elf_rebuild_rela_reloc_section(struct section *sec, int nr)
+{
+ struct reloc *reloc;
+ int idx = 0, size;
+ GElf_Rela *relocs;
+
+ /* Allocate a buffer for relocations with addends */
size = nr * sizeof(*relocs);
relocs = malloc(size);
if (!relocs) {
@@ -785,6 +880,22 @@ int elf_rebuild_reloc_section(struct section *sec)
return 0;
}
+int elf_rebuild_reloc_section(struct section *sec)
+{
+ struct reloc *reloc;
+ int nr;
+
+ nr = 0;
+ list_for_each_entry(reloc, &sec->reloc_list, list)
+ nr++;
+
+ switch (sec->sh.sh_type) {
+ case SHT_REL: return elf_rebuild_rel_reloc_section(sec, nr);
+ case SHT_RELA: return elf_rebuild_rela_reloc_section(sec, nr);
+ default: return -1;
+ }
+}
+
int elf_write(const struct elf *elf)
{
struct section *sec;