diff options
Diffstat (limited to 'tools/objtool/elf.c')
-rw-r--r-- | tools/objtool/elf.c | 202 |
1 files changed, 54 insertions, 148 deletions
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 86ae62dfdba2..4bbdd8e6df2c 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -533,16 +533,15 @@ err: 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)); @@ -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; } @@ -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", @@ -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 */ @@ -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++; @@ -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) @@ -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; |