diff options
-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. |