static int add_section(struct elf_writer *ew, struct buffer *data, const char *name, Elf64_Addr addr, Elf64_Word size) { Elf64_Shdr shdr; int ret; memset(&shdr, 0, sizeof(shdr)); if (data != NULL) { shdr.sh_type = SHT_PROGBITS; shdr.sh_flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR; } else { shdr.sh_type = SHT_NOBITS; shdr.sh_flags = SHF_ALLOC; } shdr.sh_addr = addr; shdr.sh_offset = addr; shdr.sh_size = size; ret = elf_writer_add_section(ew, &shdr, data, name); if (ret) ERROR("Could not add '%s' section.\n", name); return ret; }
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; }
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; }