summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--util/cbfstool/elfheaders.c431
-rw-r--r--util/cbfstool/elfparsing.h11
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.