diff --git a/util/cbfstool/cbfs_image.c b/util/cbfstool/cbfs_image.c index bd432d43de..c23a6e806c 100644 --- a/util/cbfstool/cbfs_image.c +++ b/util/cbfstool/cbfs_image.c @@ -28,6 +28,7 @@ #include "common.h" #include "cbfs_image.h" #include "elfparsing.h" +#include "rmodule.h" /* Even though the file-adding functions---cbfs_add_entry() and * cbfs_add_entry_at()---perform their sizing checks against the beginning of @@ -770,6 +771,7 @@ static int cbfs_stage_make_elf(struct buffer *buff, uint32_t arch) struct elf_writer *ew; struct buffer elf_out; size_t empty_sz; + int rmod_ret; if (cbfs_stage_decompress(&stage, buff)) { ERROR("Failed to decompress stage.\n"); @@ -781,6 +783,17 @@ static int cbfs_stage_make_elf(struct buffer *buff, uint32_t arch) ehdr.e_entry = stage.entry; + /* Attempt rmodule translation first. */ + rmod_ret = rmodule_stage_to_elf(&ehdr, buff); + + if (rmod_ret < 0) { + ERROR("rmodule parsing failed\n"); + return -1; + } else if (rmod_ret == 0) + return 0; + + /* Rmodule couldn't do anything with the data. Continue on with SELF. */ + ew = elf_writer_init(&ehdr); if (ew == NULL) { ERROR("Unable to init ELF writer.\n"); diff --git a/util/cbfstool/rmodule.c b/util/cbfstool/rmodule.c index 986ba623de..96c834f2c1 100644 --- a/util/cbfstool/rmodule.c +++ b/util/cbfstool/rmodule.c @@ -677,3 +677,173 @@ out: rmodule_cleanup(&ctx); return ret; } + +static void rmod_deserialize(struct rmodule_header *rmod, struct buffer *buff, + struct xdr *xdr) +{ + rmod->magic = xdr->get16(buff); + rmod->version = xdr->get8(buff); + rmod->type = xdr->get8(buff); + rmod->payload_begin_offset = xdr->get32(buff); + rmod->payload_end_offset = xdr->get32(buff); + rmod->relocations_begin_offset = xdr->get32(buff); + rmod->relocations_end_offset = xdr->get32(buff); + rmod->module_link_start_address = xdr->get32(buff); + rmod->module_program_size = xdr->get32(buff); + rmod->module_entry_point = xdr->get32(buff); + rmod->parameters_begin = xdr->get32(buff); + rmod->parameters_end = xdr->get32(buff); + rmod->bss_begin = xdr->get32(buff); + rmod->bss_end = xdr->get32(buff); + rmod->padding[0] = xdr->get32(buff); + rmod->padding[1] = xdr->get32(buff); + rmod->padding[2] = xdr->get32(buff); + rmod->padding[3] = xdr->get32(buff); +} + +int rmodule_stage_to_elf(Elf64_Ehdr *ehdr, struct buffer *buff) +{ + struct buffer reader; + struct buffer elf_out; + struct rmodule_header rmod; + struct xdr *xdr; + struct elf_writer *ew; + Elf64_Shdr shdr; + int bit64; + size_t payload_sz; + const char *section_name = ".program"; + const size_t input_sz = buffer_size(buff); + + buffer_clone(&reader, buff); + + xdr = (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) ? &xdr_be : &xdr_le; + bit64 = ehdr->e_ident[EI_CLASS] == ELFCLASS64; + + rmod_deserialize(&rmod, &reader, xdr); + + /* Indicate that file is not an rmodule if initial checks fail. */ + if (rmod.magic != RMODULE_MAGIC) + return 1; + if (rmod.version != RMODULE_VERSION_1) + return 1; + + if (rmod.payload_begin_offset > input_sz || + rmod.payload_end_offset > input_sz || + rmod.relocations_begin_offset > input_sz || + rmod.relocations_end_offset > input_sz) { + ERROR("Rmodule fields out of bounds.\n"); + return -1; + } + + ehdr->e_entry = rmod.module_entry_point; + ew = elf_writer_init(ehdr); + + if (ew == NULL) + return -1; + + payload_sz = rmod.payload_end_offset - rmod.payload_begin_offset; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_flags = SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR; + shdr.sh_addr = rmod.module_link_start_address; + shdr.sh_size = payload_sz; + buffer_splice(&reader, buff, rmod.payload_begin_offset, payload_sz); + + if (elf_writer_add_section(ew, &shdr, &reader, section_name)) { + ERROR("Unable to add ELF section: %s\n", section_name); + elf_writer_destroy(ew); + return -1; + } + + if (payload_sz != rmod.module_program_size) { + struct buffer b; + + buffer_init(&b, NULL, NULL, 0); + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_NOBITS; + shdr.sh_flags = SHF_WRITE | SHF_ALLOC; + shdr.sh_addr = rmod.module_link_start_address + payload_sz; + shdr.sh_size = rmod.module_program_size - payload_sz; + if (elf_writer_add_section(ew, &shdr, &b, ".empty")) { + ERROR("Unable to add ELF section: .empty\n"); + elf_writer_destroy(ew); + return -1; + } + } + + /* Provide a section symbol so the relcoations can reference that. */ + if (elf_writer_add_symbol(ew, section_name, section_name, shdr.sh_addr, + 0, STB_LOCAL, STT_SECTION)) { + ERROR("Unable to add section symbol to ELF.\n"); + elf_writer_destroy(ew); + return -1; + } + + /* Add symbols for the parameters if they are non-zero. */ + if (rmod.parameters_begin != rmod.parameters_end) { + int ret = 0; + + ret |= elf_writer_add_symbol(ew, "_rmodule_params", + section_name, + rmod.parameters_begin, 0, + STB_GLOBAL, STT_NOTYPE); + ret |= elf_writer_add_symbol(ew, "_ermodule_params", + section_name, + rmod.parameters_end, 0, + STB_GLOBAL, STT_NOTYPE); + + if (ret != 0) { + ERROR("Unable to add module params symbols to ELF\n"); + elf_writer_destroy(ew); + return -1; + } + } + + if (elf_writer_add_symbol(ew, "_bss", section_name, rmod.bss_begin, 0, + STB_GLOBAL, STT_NOTYPE) || + elf_writer_add_symbol(ew, "_ebss", section_name, rmod.bss_end, 0, + STB_GLOBAL, STT_NOTYPE)) { + ERROR("Unable to add bss symbols to ELF\n"); + elf_writer_destroy(ew); + return -1; + } + + ssize_t relocs_sz = rmod.relocations_end_offset; + relocs_sz -= rmod.relocations_begin_offset; + buffer_splice(&reader, buff, rmod.relocations_begin_offset, relocs_sz); + while (relocs_sz > 0) { + Elf64_Addr addr; + + if (bit64) { + relocs_sz -= sizeof(Elf64_Addr); + addr = xdr->get64(&reader); + } else { + relocs_sz -= sizeof(Elf32_Addr); + addr = xdr->get32(&reader); + } + + /* Skip any relocations that are below the link address. */ + if (addr < rmod.module_link_start_address) + continue; + + if (elf_writer_add_rel(ew, section_name, addr)) { + ERROR("Relocation addition failure.\n"); + elf_writer_destroy(ew); + return -1; + } + } + + if (elf_writer_serialize(ew, &elf_out)) { + ERROR("ELF writer serialize failure.\n"); + elf_writer_destroy(ew); + return -1; + } + + elf_writer_destroy(ew); + + /* Flip buffer with the created ELF one. */ + buffer_delete(buff); + *buff = elf_out; + + return 0; +} diff --git a/util/cbfstool/rmodule.h b/util/cbfstool/rmodule.h index 9a65677418..fa717659a1 100644 --- a/util/cbfstool/rmodule.h +++ b/util/cbfstool/rmodule.h @@ -88,4 +88,11 @@ int rmodule_collect_relocations(struct rmod_context *c, struct reloc_filter *f); /* Clean up the memory consumed by the rmdoule context. */ void rmodule_cleanup(struct rmod_context *ctx); +/* + * Create an ELF file from the passed in rmodule in the buffer. The buffer + * contents will be replaced with an ELF file. Returns 1 if buff doesn't + * contain an rmodule and < 0 on failure, 0 on success. + */ +int rmodule_stage_to_elf(Elf64_Ehdr *ehdr, struct buffer *buff); + #endif /* TOOL_RMODULE_H */