summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--util/cbfstool/cbfs_image.c13
-rw-r--r--util/cbfstool/rmodule.c170
-rw-r--r--util/cbfstool/rmodule.h7
3 files changed, 190 insertions, 0 deletions
diff --git a/util/cbfstool/cbfs_image.c b/util/cbfstool/cbfs_image.c
index bd432d43de7d..c23a6e806cac 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 986ba623dea1..96c834f2c129 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 9a6567741871..fa717659a11b 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 */