diff options
author | Aaron Durbin <adurbin@chromium.org> | 2014-03-11 11:48:56 -0500 |
---|---|---|
committer | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2014-03-18 18:59:25 +0100 |
commit | 36be8135d74964fa2eb03af44079d845c199486b (patch) | |
tree | 1b1e482a259111d321c1cf07c4e8c300342e37c4 /util/cbfstool/elfheaders.c | |
parent | 8089f1780635daae24ded0ac7fb9dc2d2f2a01bb (diff) |
cbfstool: add ELF writing support
In order to generate rmodules in the format of ELF files
there needs to be support for writing out ELF files. The
ELF writer is fairly simple. It accpets sections that can
be associated with an optional buffer (file data). For each
section flagged with SHF_ALLOC a PT_LOAD segment is generated.
There isn't smart merging of the sections into a single PT_LOAD
segment.
Change-Id: I4d1a11f2e65be2369fb3f8bff350cbb28e14c89d
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/5377
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Diffstat (limited to 'util/cbfstool/elfheaders.c')
-rw-r--r-- | util/cbfstool/elfheaders.c | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/util/cbfstool/elfheaders.c b/util/cbfstool/elfheaders.c index f3ed05c105..d217aac0cd 100644 --- a/util/cbfstool/elfheaders.c +++ b/util/cbfstool/elfheaders.c @@ -625,3 +625,322 @@ elf_headers(const struct buffer *pinput, return 0; } + +/* ELF Writing Support + * + * The ELF file is written according to the following layout: + * +------------------+ + * | ELF Header | + * +------------------+ + * | Section Headers | + * +------------------+ + * | Program Headers | + * +------------------+ + * | String table | + * +------------------+ <- 4KiB Aligned + * | Code/Data | + * +------------------+ + */ + +/* Arbitray maximum number of sections. */ +#define MAX_SECTIONS 16 +struct elf_writer_section { + Elf64_Shdr shdr; + struct buffer content; + const char *name; +}; + +struct elf_writer +{ + Elf64_Ehdr ehdr; + struct xdr *xdr; + size_t num_secs; + struct elf_writer_section sections[MAX_SECTIONS]; + Elf64_Phdr *phdrs; + struct elf_writer_section *shstrtab; + int bit64; +}; + +struct elf_writer *elf_writer_init(const Elf64_Ehdr *ehdr) +{ + struct elf_writer *ew; + Elf64_Shdr shdr; + struct buffer empty_buffer; + + if (!iself(ehdr)) + return NULL; + + ew = calloc(1, sizeof(*ew)); + + memcpy(&ew->ehdr, ehdr, sizeof(ew->ehdr)); + + ew->bit64 = ew->ehdr.e_ident[EI_CLASS] == ELFCLASS64; + + /* Set the endinan ops. */ + if (ew->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) + ew->xdr = &xdr_be; + else + ew->xdr = &xdr_le; + + /* Reset count and offsets */ + ew->ehdr.e_phoff = 0; + ew->ehdr.e_shoff = 0; + ew->ehdr.e_shnum = 0; + ew->ehdr.e_phnum = 0; + + memset(&empty_buffer, 0, sizeof(empty_buffer)); + memset(&shdr, 0, sizeof(shdr)); + + /* Add SHT_NULL section header. */ + shdr.sh_type = SHT_NULL; + elf_writer_add_section(ew, &shdr, &empty_buffer, NULL); + + /* 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]; + + return ew; +} + +/* + * Clean up any internal state represented by ew. Aftewards the elf_writer + * is invalid. + */ +void elf_writer_destroy(struct elf_writer *ew) +{ + if (ew->phdrs != NULL) + free(ew->phdrs); + free(ew); +} + +/* + * Add a section to the ELF file. Section type, flags, and memsize are + * maintained from the passed in Elf64_Shdr. The buffer represents the + * content of the section while the name is the name of section itself. + * Returns < 0 on error, 0 on success. + */ +int elf_writer_add_section(struct elf_writer *ew, const Elf64_Shdr *shdr, + struct buffer *contents, const char *name) +{ + struct elf_writer_section *newsh; + + if (ew->num_secs == MAX_SECTIONS) + return -1; + + newsh = &ew->sections[ew->num_secs]; + ew->num_secs++; + + memcpy(&newsh->shdr, shdr, sizeof(newsh->shdr)); + newsh->shdr.sh_offset = 0; + + newsh->name = name; + if (contents != NULL) + buffer_clone(&newsh->content, contents); + + return 0; +} + +static void ehdr_write(struct elf_writer *ew, struct buffer *m) +{ + int i; + + for (i = 0; i < EI_NIDENT; i++) + ew->xdr->put8(m, ew->ehdr.e_ident[i]); + ew->xdr->put16(m, ew->ehdr.e_type); + ew->xdr->put16(m, ew->ehdr.e_machine); + ew->xdr->put32(m, ew->ehdr.e_version); + if (ew->bit64) { + ew->xdr->put64(m, ew->ehdr.e_entry); + ew->xdr->put64(m, ew->ehdr.e_phoff); + ew->xdr->put64(m, ew->ehdr.e_shoff); + } else { + ew->xdr->put32(m, ew->ehdr.e_entry); + ew->xdr->put32(m, ew->ehdr.e_phoff); + ew->xdr->put32(m, ew->ehdr.e_shoff); + } + ew->xdr->put32(m, ew->ehdr.e_flags); + ew->xdr->put16(m, ew->ehdr.e_ehsize); + ew->xdr->put16(m, ew->ehdr.e_phentsize); + ew->xdr->put16(m, ew->ehdr.e_phnum); + ew->xdr->put16(m, ew->ehdr.e_shentsize); + ew->xdr->put16(m, ew->ehdr.e_shnum); + ew->xdr->put16(m, ew->ehdr.e_shstrndx); +} + +static void shdr_write(struct elf_writer *ew, size_t n, struct buffer *m) +{ + struct xdr *xdr = ew->xdr; + int bit64 = ew->bit64; + struct elf_writer_section *sec = &ew->sections[n]; + Elf64_Shdr *shdr = &sec->shdr; + + xdr->put32(m, shdr->sh_name); + xdr->put32(m, shdr->sh_type); + xdr->put32(m, shdr->sh_flags); + if (bit64) { + xdr->put64(m, shdr->sh_addr); + xdr->put64(m, shdr->sh_offset); + xdr->put64(m, shdr->sh_size); + xdr->put32(m, shdr->sh_link); + xdr->put32(m, shdr->sh_info); + xdr->put64(m, shdr->sh_addralign); + xdr->put64(m, shdr->sh_entsize); + } else { + xdr->put32(m, shdr->sh_addr); + xdr->put32(m, shdr->sh_offset); + xdr->put32(m, shdr->sh_size); + xdr->put32(m, shdr->sh_link); + xdr->put32(m, shdr->sh_info); + xdr->put32(m, shdr->sh_addralign); + xdr->put32(m, shdr->sh_entsize); + } +} + +static void +phdr_write(struct elf_writer *ew, struct buffer *m, Elf64_Phdr *phdr) +{ + if (ew->bit64) { + ew->xdr->put32(m, phdr->p_type); + ew->xdr->put32(m, phdr->p_flags); + ew->xdr->put64(m, phdr->p_offset); + ew->xdr->put64(m, phdr->p_vaddr); + ew->xdr->put64(m, phdr->p_paddr); + ew->xdr->put64(m, phdr->p_filesz); + ew->xdr->put64(m, phdr->p_memsz); + ew->xdr->put64(m, phdr->p_align); + } else { + ew->xdr->put32(m, phdr->p_type); + ew->xdr->put32(m, phdr->p_offset); + ew->xdr->put32(m, phdr->p_vaddr); + ew->xdr->put32(m, phdr->p_paddr); + ew->xdr->put32(m, phdr->p_filesz); + ew->xdr->put32(m, phdr->p_memsz); + ew->xdr->put32(m, phdr->p_flags); + ew->xdr->put32(m, phdr->p_align); + } + +} + +/* + * Serialize the ELF file to the output buffer. Return < 0 on error, + * 0 on success. + */ +int elf_writer_serialize(struct elf_writer *ew, struct buffer *out) +{ + Elf64_Half i; + Elf64_Xword metadata_size; + Elf64_Xword program_size; + Elf64_Off shstroffset; + size_t shstrlen; + struct buffer metadata; + struct buffer phdrs; + struct buffer data; + struct buffer *strtab; + + INFO("Writing %zu sections.\n", ew->num_secs); + + /* Determine size of sections to be written. */ + program_size = 0; + /* Start with 1 byte for first byte of section header string table. */ + shstrlen = 1; + for (i = 0; i < ew->num_secs; i++) { + struct elf_writer_section *sec = &ew->sections[i]; + + if (sec->shdr.sh_flags & SHF_ALLOC) + ew->ehdr.e_phnum++; + + program_size += buffer_size(&sec->content); + + /* Keep track of the length sections' names. */ + if (sec->name != NULL) { + sec->shdr.sh_name = shstrlen; + shstrlen += strlen(sec->name) + 1; + } + } + ew->ehdr.e_shnum = ew->num_secs; + metadata_size = 0; + metadata_size += ew->ehdr.e_ehsize; + metadata_size += ew->ehdr.e_shnum * ew->ehdr.e_shentsize; + metadata_size += ew->ehdr.e_phnum * ew->ehdr.e_phentsize; + shstroffset = metadata_size; + /* Align up section header string size and metadata size to 4KiB */ + metadata_size = ALIGN(metadata_size + shstrlen, 4096); + + if (buffer_create(out, metadata_size + program_size, "elfout")) { + ERROR("Could not create output buffer for ELF.\n"); + return -1; + } + + INFO("Created %zu output buffer for ELF file.\n", buffer_size(out)); + + /* + * Write out ELF header. Section headers come right after ELF header + * followed by the program headers. Buffers need to be created first + * to do the writing. + */ + ew->ehdr.e_shoff = ew->ehdr.e_ehsize; + ew->ehdr.e_phoff = ew->ehdr.e_shoff + + ew->ehdr.e_shnum * ew->ehdr.e_shentsize; + + buffer_splice(&metadata, out, 0, metadata_size); + buffer_splice(&phdrs, out, ew->ehdr.e_phoff, + 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; + buffer_splice(strtab, out, shstroffset, shstrlen); + ew->shstrtab->shdr.sh_size = shstrlen; + + /* Reset current locations. */ + buffer_set_size(&metadata, 0); + buffer_set_size(&data, 0); + buffer_set_size(&phdrs, 0); + buffer_set_size(strtab, 0); + + /* ELF Header */ + ehdr_write(ew, &metadata); + + /* Write out section headers, section strings, section content, and + * program headers. */ + ew->xdr->put8(strtab, 0); + for (i = 0; i < ew->num_secs; i++) { + Elf64_Phdr phdr; + struct elf_writer_section *sec = &ew->sections[i]; + + /* Update section offsets. Be sure to not update SHT_NULL. */ + if (sec == ew->shstrtab) + sec->shdr.sh_offset = shstroffset; + else if (i != 0) + 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)) + continue; + + bputs(&data, buffer_get(&sec->content), + buffer_size(&sec->content)); + + phdr.p_type = PT_LOAD; + phdr.p_offset = sec->shdr.sh_offset; + phdr.p_vaddr = sec->shdr.sh_addr; + phdr.p_paddr = sec->shdr.sh_addr; + phdr.p_filesz = buffer_size(&sec->content); + phdr.p_memsz = sec->shdr.sh_size; + phdr.p_flags = 0; + if (sec->shdr.sh_flags & SHF_EXECINSTR) + phdr.p_flags |= PF_X | PF_R; + if (sec->shdr.sh_flags & SHF_WRITE) + phdr.p_flags |= PF_W; + phdr.p_align = sec->shdr.sh_addralign; + phdr_write(ew, &phdrs, &phdr); + } + + return 0; +} |