diff options
author | Aaron Durbin <adurbin@chromium.org> | 2015-10-28 11:26:40 -0500 |
---|---|---|
committer | Aaron Durbin <adurbin@chromium.org> | 2015-10-29 17:00:47 +0100 |
commit | cedcb882c1f0a2b9b61ffed9a646744d3e481e60 (patch) | |
tree | 4e5ed1ba81f210a66a26e7240f4f3d8bf3391fc7 /util/cbfstool | |
parent | 8e982eabfacf9cce5bcc011ee2c23ca7ced4b05c (diff) | |
download | coreboot-cedcb882c1f0a2b9b61ffed9a646744d3e481e60.tar.xz |
cbfstool: add ELF symbol, relocation, and string table support
In order to convert rmodules back into ELF files one needs
to add in the relocations so they can be converted back to
rmodules. Because of that requirement symbol tables need
to be present because the relocations reference the symbols.
Additionally, symbol tables reference a string table for the
symbol names. Provide the necessary support for adding all
of those things to an ELF writer.
TEST=Extracted rmodule from a cbfs and compared with the
source ELF file. Confirmed relocations and code sizes
are correct.
Change-Id: I07e87a30b3371ddedabcfc682046e3db8c956ff2
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/12221
Tested-by: build bot (Jenkins)
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
Diffstat (limited to 'util/cbfstool')
-rw-r--r-- | util/cbfstool/elfheaders.c | 431 | ||||
-rw-r--r-- | util/cbfstool/elfparsing.h | 11 |
2 files changed, 430 insertions, 12 deletions
diff --git a/util/cbfstool/elfheaders.c b/util/cbfstool/elfheaders.c index 262137d580..45e96fd850 100644 --- a/util/cbfstool/elfheaders.c +++ b/util/cbfstool/elfheaders.c @@ -667,17 +667,101 @@ struct elf_writer_section { const char *name; }; +struct elf_writer_string_table { + size_t next_offset; + size_t max_size; + char *buffer; +}; + +struct elf_writer_sym_table { + size_t max_entries; + size_t num_entries; + Elf64_Sym *syms; +}; + +#define MAX_REL_NAME 32 +struct elf_writer_rel { + size_t num_entries; + size_t max_entries; + Elf64_Rel *rels; + struct elf_writer_section *sec; + char name[MAX_REL_NAME]; +}; + struct elf_writer { Elf64_Ehdr ehdr; struct xdr *xdr; size_t num_secs; struct elf_writer_section sections[MAX_SECTIONS]; + struct elf_writer_rel rel_sections[MAX_SECTIONS]; Elf64_Phdr *phdrs; - struct elf_writer_section *shstrtab; + struct elf_writer_section *shstrtab_sec; + struct elf_writer_section *strtab_sec; + struct elf_writer_section *symtab_sec; + struct elf_writer_string_table strtab; + struct elf_writer_sym_table symtab; int bit64; }; +static size_t section_index(struct elf_writer *ew, + struct elf_writer_section *sec) +{ + return sec - &ew->sections[0]; +} + +static struct elf_writer_section *last_section(struct elf_writer *ew) +{ + return &ew->sections[ew->num_secs - 1]; +} + +static void strtab_init(struct elf_writer *ew, size_t size) +{ + struct buffer b; + Elf64_Shdr shdr; + + /* Start adding strings after the initial NUL entry. */ + ew->strtab.next_offset = 1; + ew->strtab.max_size = size; + ew->strtab.buffer = calloc(1, ew->strtab.max_size); + + buffer_init(&b, NULL, ew->strtab.buffer, ew->strtab.max_size); + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_STRTAB; + shdr.sh_addralign = 1; + shdr.sh_size = ew->strtab.max_size; + elf_writer_add_section(ew, &shdr, &b, ".strtab"); + ew->strtab_sec = last_section(ew); +} + +static void symtab_init(struct elf_writer *ew, size_t max_entries) +{ + struct buffer b; + Elf64_Shdr shdr; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_SYMTAB; + + if (ew->bit64) { + shdr.sh_entsize = sizeof(Elf64_Sym); + shdr.sh_addralign = sizeof(Elf64_Addr); + } else { + shdr.sh_entsize = sizeof(Elf32_Sym); + shdr.sh_addralign = sizeof(Elf32_Addr); + } + + shdr.sh_size = shdr.sh_entsize * max_entries; + + ew->symtab.syms = calloc(max_entries, sizeof(Elf64_Sym)); + ew->symtab.num_entries = 1; + ew->symtab.max_entries = max_entries; + + buffer_init(&b, NULL, ew->symtab.syms, shdr.sh_size); + + elf_writer_add_section(ew, &shdr, &b, ".symtab"); + ew->symtab_sec = last_section(ew); +} + struct elf_writer *elf_writer_init(const Elf64_Ehdr *ehdr) { struct elf_writer *ew; @@ -715,8 +799,12 @@ struct elf_writer *elf_writer_init(const Elf64_Ehdr *ehdr) /* Add section header string table and maintain reference to it. */ shdr.sh_type = SHT_STRTAB; elf_writer_add_section(ew, &shdr, &empty_buffer, ".shstrtab"); - ew->ehdr.e_shstrndx = ew->num_secs - 1; - ew->shstrtab = &ew->sections[ew->ehdr.e_shstrndx]; + ew->shstrtab_sec = last_section(ew); + ew->ehdr.e_shstrndx = section_index(ew, ew->shstrtab_sec); + + /* Add a small string table and symbol table. */ + strtab_init(ew, 4096); + symtab_init(ew, 100); return ew; } @@ -727,8 +815,13 @@ struct elf_writer *elf_writer_init(const Elf64_Ehdr *ehdr) */ void elf_writer_destroy(struct elf_writer *ew) { + int i; if (ew->phdrs != NULL) free(ew->phdrs); + free(ew->strtab.buffer); + free(ew->symtab.syms); + for (i = 0; i < MAX_SECTIONS; i++) + free(ew->rel_sections[i].rels); free(ew); } @@ -913,6 +1006,122 @@ static void write_phdrs(struct elf_writer *ew, struct buffer *phdrs) phdr_write(ew, phdrs, &phdr); } +static void fixup_symbol_table(struct elf_writer *ew) +{ + struct elf_writer_section *sec = ew->symtab_sec; + + /* If there is only the NULL section, mark section as inactive. */ + if (ew->symtab.num_entries == 1) { + sec->shdr.sh_type = SHT_NULL; + sec->shdr.sh_size = 0; + } else { + size_t i; + struct buffer wr; + + buffer_clone(&wr, &sec->content); + /* To appease xdr. */ + buffer_set_size(&wr, 0); + for (i = 0; i < ew->symtab.num_entries; i++) { + /* Create local copy as were over-writing backing + * store of the symbol. */ + Elf64_Sym sym = ew->symtab.syms[i]; + if (ew->bit64) { + ew->xdr->put32(&wr, sym.st_name); + ew->xdr->put8(&wr, sym.st_info); + ew->xdr->put8(&wr, sym.st_other); + ew->xdr->put16(&wr, sym.st_shndx); + ew->xdr->put64(&wr, sym.st_value); + ew->xdr->put64(&wr, sym.st_size); + } else { + ew->xdr->put32(&wr, sym.st_name); + ew->xdr->put32(&wr, sym.st_value); + ew->xdr->put32(&wr, sym.st_size); + ew->xdr->put8(&wr, sym.st_info); + ew->xdr->put8(&wr, sym.st_other); + ew->xdr->put16(&wr, sym.st_shndx); + } + } + + /* Update section size. */ + sec->shdr.sh_size = sec->shdr.sh_entsize; + sec->shdr.sh_size *= ew->symtab.num_entries; + + /* Fix up sh_link to point to string table. */ + sec->shdr.sh_link = section_index(ew, ew->strtab_sec); + /* sh_info is supposed to be 1 greater than symbol table + * index of last local binding. Just use max symbols. */ + sec->shdr.sh_info = ew->symtab.num_entries; + } + + buffer_set_size(&sec->content, sec->shdr.sh_size); +} + +static void fixup_relocations(struct elf_writer *ew) +{ + int i; + Elf64_Xword type; + + switch (ew->ehdr.e_machine) { + case EM_386: + type = R_386_32; + break; + case EM_ARM: + type = R_ARM_ABS32; + break; + case EM_AARCH64: + type = R_AARCH64_ABS64; + break; + case EM_MIPS: + type = R_MIPS_32; + break; + case EM_RISCV: + type = R_RISCV_32; + break; + default: + ERROR("Unable to handle relocations for e_machine %x\n", + ew->ehdr.e_machine); + return; + } + + for (i = 0; i < MAX_SECTIONS; i++) { + struct elf_writer_rel *rel_sec = &ew->rel_sections[i]; + struct elf_writer_section *sec = rel_sec->sec; + struct buffer writer; + size_t j; + + if (sec == NULL) + continue; + + /* Update section header size as well as content size. */ + buffer_init(&sec->content, sec->content.name, rel_sec->rels, + rel_sec->num_entries * sec->shdr.sh_entsize); + sec->shdr.sh_size = buffer_size(&sec->content); + buffer_clone(&writer, &sec->content); + /* To make xdr happy. */ + buffer_set_size(&writer, 0); + + for (j = 0; j < ew->rel_sections[i].num_entries; j++) { + /* Make copy as we're overwriting backing store. */ + Elf64_Rel rel = rel_sec->rels[j]; + rel.r_info = ELF64_R_INFO(ELF64_R_SYM(rel.r_info), + ELF64_R_TYPE(type)); + + if (ew->bit64) { + ew->xdr->put64(&writer, rel.r_offset); + ew->xdr->put64(&writer, rel.r_info); + } else { + Elf32_Rel rel32; + rel32.r_offset = rel.r_offset; + rel32.r_info = + ELF32_R_INFO(ELF64_R_SYM(rel.r_info), + ELF64_R_TYPE(rel.r_info)); + ew->xdr->put32(&writer, rel32.r_offset); + ew->xdr->put32(&writer, rel32.r_info); + } + } + } +} + /* * Serialize the ELF file to the output buffer. Return < 0 on error, * 0 on success. @@ -931,6 +1140,10 @@ int elf_writer_serialize(struct elf_writer *ew, struct buffer *out) INFO("Writing %zu sections.\n", ew->num_secs); + /* Perform any necessary work for special sections. */ + fixup_symbol_table(ew); + fixup_relocations(ew); + /* Determine size of sections to be written. */ program_size = 0; /* Start with 1 byte for first byte of section header string table. */ @@ -981,9 +1194,9 @@ int elf_writer_serialize(struct elf_writer *ew, struct buffer *out) ew->ehdr.e_phnum * ew->ehdr.e_phentsize); buffer_splice(&data, out, metadata_size, program_size); /* Set up the section header string table contents. */ - strtab = &ew->shstrtab->content; + strtab = &ew->shstrtab_sec->content; buffer_splice(strtab, out, shstroffset, shstrlen); - ew->shstrtab->shdr.sh_size = shstrlen; + ew->shstrtab_sec->shdr.sh_size = shstrlen; /* Reset current locations. */ buffer_set_size(&metadata, 0); @@ -1000,26 +1213,220 @@ int elf_writer_serialize(struct elf_writer *ew, struct buffer *out) for (i = 0; i < ew->num_secs; i++) { struct elf_writer_section *sec = &ew->sections[i]; - /* Update section offsets. Be sure to not update SHT_NULL. */ - if (sec == ew->shstrtab) + /* Update section offsets. Be sure to not update SHN_UNDEF. */ + if (sec == ew->shstrtab_sec) sec->shdr.sh_offset = shstroffset; - else if (i != 0) + else if (i != SHN_UNDEF) sec->shdr.sh_offset = buffer_size(&data) + metadata_size; + shdr_write(ew, i, &metadata); /* Add section name to string table. */ if (sec->name != NULL) bputs(strtab, sec->name, strlen(sec->name) + 1); - if (!(sec->shdr.sh_flags & SHF_ALLOC)) + /* Output section data for all sections but SHN_UNDEF and + * section header string table. */ + if (i != SHN_UNDEF && sec != ew->shstrtab_sec) + bputs(&data, buffer_get(&sec->content), + buffer_size(&sec->content)); + } + + write_phdrs(ew, &phdrs); + + return 0; +} + +/* Add a string to the string table returning index on success, < 0 on error. */ +static int elf_writer_add_string(struct elf_writer *ew, const char *new) +{ + size_t current_offset; + size_t new_len; + + for (current_offset = 0; current_offset < ew->strtab.next_offset; ) { + const char *str = ew->strtab.buffer + current_offset; + size_t len = strlen(str) + 1; + + if (!strcmp(str, new)) + return current_offset; + current_offset += len; + } + + new_len = strlen(new) + 1; + + if (current_offset + new_len > ew->strtab.max_size) { + ERROR("No space for string in .strtab.\n"); + return -1; + } + + memcpy(ew->strtab.buffer + current_offset, new, new_len); + ew->strtab.next_offset = current_offset + new_len; + + return current_offset; +} + +static int elf_writer_section_index(struct elf_writer *ew, const char *name) +{ + size_t i; + + for (i = 0; i < ew->num_secs; i++) { + if (ew->sections[i].name == NULL) continue; + if (!strcmp(ew->sections[i].name, name)) + return i; + } + + ERROR("ELF Section not found: %s\n", name); - bputs(&data, buffer_get(&sec->content), - buffer_size(&sec->content)); + return -1; +} + +int elf_writer_add_symbol(struct elf_writer *ew, const char *name, + const char *section_name, + Elf64_Addr value, Elf64_Word size, + int binding, int type) +{ + int index; + Elf64_Sym sym = { + .st_value = value, + .st_size = size, + .st_info = ELF64_ST_INFO(binding, type), + }; + + if (ew->symtab.max_entries == ew->symtab.num_entries) { + ERROR("No more symbol entries left.\n"); + return -1; } - write_phdrs(ew, &phdrs); + index = elf_writer_add_string(ew, name); + if (index < 0) + return -1; + sym.st_name = index; + + index = elf_writer_section_index(ew, section_name); + if (index < 0) + return -1; + sym.st_shndx = index; + + ew->symtab.syms[ew->symtab.num_entries++] = sym; + + return 0; +} + +static int elf_sym_index(struct elf_writer *ew, const char *sym) +{ + int index; + size_t i; + Elf64_Word st_name; + + /* Determine index of symbol in the string table. */ + index = elf_writer_add_string(ew, sym); + if (index < 0) + return -1; + + st_name = index; + + for (i = 0; i < ew->symtab.num_entries; i++) + if (ew->symtab.syms[i].st_name == st_name) + return i; + + return -1; +} + +static struct elf_writer_rel *rel_section(struct elf_writer *ew, + const Elf64_Rel *r) +{ + Elf64_Sym *sym; + struct elf_writer_rel *rel; + Elf64_Shdr shdr; + struct buffer b; + + sym = &ew->symtab.syms[ELF64_R_SYM(r->r_info)]; + + /* Determine if section has been initialized yet. */ + rel = &ew->rel_sections[sym->st_shndx]; + if (rel->sec != NULL) + return rel; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_REL; + shdr.sh_link = section_index(ew, ew->symtab_sec); + shdr.sh_info = sym->st_shndx; + + if (ew->bit64) { + shdr.sh_addralign = sizeof(Elf64_Addr); + shdr.sh_entsize = sizeof(Elf64_Rel); + } else { + shdr.sh_addralign = sizeof(Elf32_Addr); + shdr.sh_entsize = sizeof(Elf32_Rel); + } + + if ((strlen(".rel") + strlen(ew->sections[sym->st_shndx].name) + 1) > + MAX_REL_NAME) { + ERROR("Rel Section name won't fit\n"); + return NULL; + } + + strcat(rel->name, ".rel"); + strcat(rel->name, ew->sections[sym->st_shndx].name); + buffer_init(&b, rel->name, NULL, 0); + + elf_writer_add_section(ew, &shdr, &b, rel->name); + rel->sec = last_section(ew); + + return rel; +} + +static int add_rel(struct elf_writer_rel *rel_sec, const Elf64_Rel *rel) +{ + if (rel_sec->num_entries == rel_sec->max_entries) { + size_t num = rel_sec->max_entries * 2; + Elf64_Rel *old_rels; + + if (num == 0) + num = 128; + + old_rels = rel_sec->rels; + rel_sec->rels = calloc(num, sizeof(Elf64_Rel)); + + memcpy(rel_sec->rels, old_rels, + rel_sec->num_entries * sizeof(Elf64_Rel)); + free(old_rels); + + rel_sec->max_entries = num; + } + + rel_sec->rels[rel_sec->num_entries] = *rel; + rel_sec->num_entries++; return 0; } + +int elf_writer_add_rel(struct elf_writer *ew, const char *sym, Elf64_Addr addr) +{ + Elf64_Rel rel; + Elf64_Xword sym_info; + int sym_index; + struct elf_writer_rel *rel_sec; + + sym_index = elf_sym_index(ew, sym); + + if (sym_index < 0) { + ERROR("Unable to locate symbol: %s\n", sym); + return -1; + } + + sym_info = sym_index; + + /* The relocation type will get fixed prior to serialization. */ + rel.r_offset = addr; + rel.r_info = ELF64_R_INFO(sym_info, 0); + + rel_sec = rel_section(ew, &rel); + + if (rel_sec == NULL) + return -1; + + return add_rel(rel_sec, &rel); +} diff --git a/util/cbfstool/elfparsing.h b/util/cbfstool/elfparsing.h index fa1c277acc..9024b6d60e 100644 --- a/util/cbfstool/elfparsing.h +++ b/util/cbfstool/elfparsing.h @@ -109,6 +109,17 @@ void elf_writer_destroy(struct elf_writer *ew); int elf_writer_add_section(struct elf_writer *ew, const Elf64_Shdr *shdr, struct buffer *contents, const char *name); +/* Add an absolute symbol to the ELF file returning < 0 on error, index of + * symbol otherwise. */ +int elf_writer_add_symbol(struct elf_writer *ew, const char *name, + const char *section_name, + Elf64_Addr value, Elf64_Word size, + int binding, int type); + +/* Add an absolute relocation referencing the provided symbol name. Returns < 0 + * on error, 0 on success. */ +int elf_writer_add_rel(struct elf_writer *ew, const char *sym, Elf64_Addr addr); + /* * Serialize the ELF file to the output buffer. Return < 0 on error, * 0 on success. |