objtool: Add support for relocations without addends
authorMatt Helsley <mhelsley@vmware.com>
Fri, 29 May 2020 21:01:14 +0000 (14:01 -0700)
committerJosh Poimboeuf <jpoimboe@redhat.com>
Tue, 2 Jun 2020 20:37:04 +0000 (15:37 -0500)
Currently objtool only collects information about relocations with
addends. In recordmcount, which we are about to merge into objtool,
some supported architectures do not use rela relocations.

Signed-off-by: Matt Helsley <mhelsley@vmware.com>
Reviewed-by: Julien Thierry <jthierry@redhat.com>
Reviewed-by: Kamalesh Babulal <kamalesh@linux.vnet.ibm.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
tools/objtool/elf.c
tools/objtool/elf.h
tools/objtool/orc_gen.c

index 3160931..95d86bc 100644 (file)
@@ -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;
index 6ad759f..78a2db2 100644 (file)
@@ -61,7 +61,10 @@ struct symbol {
 struct reloc {
        struct list_head list;
        struct hlist_node hash;
-       GElf_Rela rela;
+       union {
+               GElf_Rela rela;
+               GElf_Rel  rel;
+       };
        struct section *sec;
        struct symbol *sym;
        unsigned int type;
@@ -116,7 +119,7 @@ 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, size_t entsize, int nr);
-struct section *elf_create_reloc_section(struct elf *elf, struct section *base);
+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_write(const struct elf *elf);
 void elf_close(struct elf *elf);
index 93c720b..75e08cf 100644 (file)
@@ -181,7 +181,7 @@ int create_orc_sections(struct objtool_file *file)
        if (!sec)
                return -1;
 
-       ip_relocsec = elf_create_reloc_section(file->elf, sec);
+       ip_relocsec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
        if (!ip_relocsec)
                return -1;