bool partitioned_file_read_region(struct buffer *dest, const partitioned_file_t *file, const char *region) { assert(dest); assert(file); assert(file->buffer.data); assert(region); if (file->fmap) { const struct fmap_area *area = fmap_find_area(file->fmap, region); if (!area) { ERROR("Image is missing '%s' region\n", region); return false; } if (area->offset + area->size > file->buffer.size) { ERROR("Region '%s' runs off the end of the image file\n", region); return false; } buffer_splice(dest, &file->buffer, area->offset, area->size); } else { if (strcmp(region, SECTION_NAME_PRIMARY_CBFS) != 0) { ERROR("This is a legacy image that contains only a CBFS\n"); return false; } buffer_clone(dest, &file->buffer); } return true; }
static int strtab_read(const struct buffer *in, struct parsed_elf *pelf) { Elf64_Ehdr *ehdr; Elf64_Word i; ehdr = &pelf->ehdr; if (ehdr->e_shstrndx >= ehdr->e_shnum) { ERROR("Section header string table index out of range: %d\n", ehdr->e_shstrndx); return -1; } /* For each section of type SHT_STRTAB create a symtab buffer. */ pelf->strtabs = calloc(ehdr->e_shnum, sizeof(struct buffer *)); for (i = 0; i < ehdr->e_shnum; i++) { struct buffer *b; Elf64_Shdr *shdr = &pelf->shdr[i]; if (shdr->sh_type != SHT_STRTAB) continue; b = calloc(1, sizeof(*b)); buffer_splice(b, in, shdr->sh_offset, shdr->sh_size); if (check_size(in, shdr->sh_offset, buffer_size(b), "strtab")) { ERROR("STRTAB section not within bounds: %d\n", i); free(b); return -1; } pelf->strtabs[i] = b; } return 0; }
static int shdr_read(const struct buffer *in, struct parsed_elf *pelf, struct xdr *xdr, int bit64) { struct buffer b; Elf64_Shdr *shdr; Elf64_Ehdr *ehdr; int i; ehdr = &pelf->ehdr; /* cons up an input buffer for the section headers. * Note that the section headers can be anywhere, * per the ELF spec, You'd be surprised how many ELF * readers miss this little detail. */ buffer_splice(&b, in, ehdr->e_shoff, ehdr->e_shentsize * ehdr->e_shnum); if (check_size(in, ehdr->e_shoff, buffer_size(&b), "section headers")) return -1; /* gather up all the shdrs. */ shdr = calloc(ehdr->e_shnum, sizeof(*shdr)); for (i = 0; i < ehdr->e_shnum; i++) { DEBUG("Parsing section %d\n", i); elf_shdr(&b, &shdr[i], ehdr->e_shentsize, xdr, bit64); } pelf->shdr = shdr; return 0; }
partitioned_file_t *partitioned_file_create(const char *filename, struct buffer *flashmap) { assert(filename); assert(flashmap); assert(flashmap->data); if (fmap_find((const uint8_t *)flashmap->data, flashmap->size) != 0) { ERROR("Attempted to create a partitioned image out of something that isn't an FMAP\n"); return NULL; } struct fmap *bootstrap_fmap = (struct fmap *)flashmap->data; const struct fmap_area *fmap_area = fmap_find_area(bootstrap_fmap, SECTION_NAME_FMAP); if (!fmap_area) { ERROR("Provided FMAP missing '%s' region\n", SECTION_NAME_FMAP); return NULL; } if (count_selected_fmap_entries(bootstrap_fmap, partitioned_file_fmap_select_children_of, fmap_area)) { ERROR("Provided FMAP's '%s' region contains other regions\n", SECTION_NAME_FMAP); return NULL; } int fmap_len = fmap_size(bootstrap_fmap); if (fmap_len < 0) { ERROR("Unable to determine size of provided FMAP\n"); return NULL; } assert((size_t)fmap_len <= flashmap->size); if ((uint32_t)fmap_len > fmap_area->size) { ERROR("Provided FMAP's '%s' region needs to be at least %d bytes\n", SECTION_NAME_FMAP, fmap_len); return NULL; } partitioned_file_t *file = partitioned_file_create_flat(filename, bootstrap_fmap->size); if (!file) return NULL; struct buffer fmap_region; buffer_splice(&fmap_region, &file->buffer, fmap_area->offset, fmap_area->size); memcpy(fmap_region.data, bootstrap_fmap, fmap_len); if (!partitioned_file_write_region(file, &fmap_region)) { partitioned_file_close(file); return NULL; } file->fmap = (struct fmap *)(file->buffer.data + fmap_area->offset); return file; }
static int phdr_read(const struct buffer *in, struct parsed_elf *pelf, struct xdr *xdr, int bit64) { struct buffer b; Elf64_Phdr *phdr; Elf64_Ehdr *ehdr; int i; ehdr = &pelf->ehdr; /* cons up an input buffer for the headers. * Note that the program headers can be anywhere, * per the ELF spec, You'd be surprised how many ELF * readers miss this little detail. */ buffer_splice(&b, in, ehdr->e_phoff, ehdr->e_phentsize * ehdr->e_phnum); if (check_size(in, ehdr->e_phoff, buffer_size(&b), "program headers")) return -1; /* gather up all the phdrs. * We do them all at once because there is more * than one loop over all the phdrs. */ phdr = calloc(ehdr->e_phnum, sizeof(*phdr)); for (i = 0; i < ehdr->e_phnum; i++) { DEBUG("Parsing segment %d\n", i); elf_phdr(&b, &phdr[i], ehdr->e_phentsize, xdr, bit64); /* Ensure the contents are valid within the elf file. */ if (check_size(in, phdr[i].p_offset, phdr[i].p_filesz, "segment contents")) { free(phdr); return -1; } } pelf->phdr = phdr; return 0; }
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; }
static int write_elf(const struct rmod_context *ctx, const struct buffer *in, struct buffer *out) { int ret; int bit64; size_t loc; size_t rmod_data_size; struct elf_writer *ew; struct buffer rmod_data; struct buffer rmod_header; struct buffer program; struct buffer relocs; Elf64_Xword total_size; Elf64_Addr addr; Elf64_Ehdr ehdr; bit64 = ctx->pelf.ehdr.e_ident[EI_CLASS] == ELFCLASS64; /* * 3 sections will be added to the ELF file. * +------------------+ * | rmodule header | * +------------------+ * | program | * +------------------+ * | relocations | * +------------------+ */ /* Create buffer for header and relocations. */ rmod_data_size = sizeof(struct rmodule_header); if (bit64) rmod_data_size += ctx->nrelocs * sizeof(Elf64_Addr); else rmod_data_size += ctx->nrelocs * sizeof(Elf32_Addr); if (buffer_create(&rmod_data, rmod_data_size, "rmod")) return -1; buffer_splice(&rmod_header, &rmod_data, 0, sizeof(struct rmodule_header)); buffer_clone(&relocs, &rmod_data); buffer_seek(&relocs, sizeof(struct rmodule_header)); /* Reset current location. */ buffer_set_size(&rmod_header, 0); buffer_set_size(&relocs, 0); /* Program contents. */ buffer_splice(&program, in, ctx->phdr->p_offset, ctx->phdr->p_filesz); /* Create ELF writer with modified entry point. */ memcpy(&ehdr, &ctx->pelf.ehdr, sizeof(ehdr)); ew = elf_writer_init(&ehdr); if (ew == NULL) { ERROR("Failed to create ELF writer.\n"); buffer_delete(&rmod_data); return -1; } /* Write out rmodule_header. */ ctx->xdr->put16(&rmod_header, RMODULE_MAGIC); ctx->xdr->put8(&rmod_header, RMODULE_VERSION_1); ctx->xdr->put8(&rmod_header, 0); /* payload_begin_offset */ loc = sizeof(struct rmodule_header); ctx->xdr->put32(&rmod_header, loc); /* payload_end_offset */ loc += ctx->phdr->p_filesz; ctx->xdr->put32(&rmod_header, loc); /* relocations_begin_offset */ ctx->xdr->put32(&rmod_header, loc); /* relocations_end_offset */ if (bit64) loc += ctx->nrelocs * sizeof(Elf64_Addr); else loc += ctx->nrelocs * sizeof(Elf32_Addr); ctx->xdr->put32(&rmod_header, loc); /* module_link_start_address */ ctx->xdr->put32(&rmod_header, ctx->phdr->p_vaddr); /* module_program_size */ ctx->xdr->put32(&rmod_header, ctx->phdr->p_memsz); /* module_entry_point */ ctx->xdr->put32(&rmod_header, ctx->pelf.ehdr.e_entry); /* parameters_begin */ ctx->xdr->put32(&rmod_header, ctx->parameters_begin); /* parameters_end */ ctx->xdr->put32(&rmod_header, ctx->parameters_end); /* bss_begin */ ctx->xdr->put32(&rmod_header, ctx->bss_begin); /* bss_end */ ctx->xdr->put32(&rmod_header, ctx->bss_end); /* padding[4] */ ctx->xdr->put32(&rmod_header, 0); ctx->xdr->put32(&rmod_header, 0); ctx->xdr->put32(&rmod_header, 0); ctx->xdr->put32(&rmod_header, 0); /* Write the relocations. */ for (unsigned i = 0; i < ctx->nrelocs; i++) { if (bit64) ctx->xdr->put64(&relocs, ctx->emitted_relocs[i]); else ctx->xdr->put32(&relocs, ctx->emitted_relocs[i]); } total_size = 0; addr = 0; /* * There are 2 cases to deal with. The program has a large NOBITS * section and the relocations can fit entirely within occupied memory * region for the program. The other is that the relocations increase * the memory footprint of the program if it was loaded directly into * the region it would run. The rmdoule header is a fixed cost that * is considered a part of the program. */ total_size += buffer_size(&rmod_header); if (buffer_size(&relocs) + ctx->phdr->p_filesz > ctx->phdr->p_memsz) { total_size += buffer_size(&relocs); total_size += ctx->phdr->p_filesz; } else { total_size += ctx->phdr->p_memsz; } ret = add_section(ew, &rmod_header, ".header", addr, buffer_size(&rmod_header)); if (ret < 0) goto out; addr += buffer_size(&rmod_header); ret = add_section(ew, &program, ".program", addr, ctx->phdr->p_filesz); if (ret < 0) goto out; addr += ctx->phdr->p_filesz; if (ctx->nrelocs) { ret = add_section(ew, &relocs, ".relocs", addr, buffer_size(&relocs)); if (ret < 0) goto out; addr += buffer_size(&relocs); } if (total_size != addr) { ret = add_section(ew, NULL, ".empty", addr, total_size - addr); if (ret < 0) goto out; } /* * Ensure last section has a memory usage that meets the required * total size of the program in memory. */ ret = elf_writer_serialize(ew, out); if (ret < 0) ERROR("Failed to serialize ELF to buffer.\n"); out: buffer_delete(&rmod_data); elf_writer_destroy(ew); return ret; }
/* * 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; }
static int symtab_read(const struct buffer *in, struct parsed_elf *pelf, struct xdr *xdr, int bit64) { Elf64_Ehdr *ehdr; Elf64_Shdr *shdr; Elf64_Half i; Elf64_Xword nsyms; Elf64_Sym *sym; struct buffer b; ehdr = &pelf->ehdr; shdr = NULL; for (i = 0; i < ehdr->e_shnum; i++) { if (pelf->shdr[i].sh_type != SHT_SYMTAB) continue; if (shdr != NULL) { ERROR("Multiple symbol sections found. %u and %u\n", (unsigned int)(shdr - pelf->shdr), i); return -1; } shdr = &pelf->shdr[i]; } if (shdr == NULL) { ERROR("No symbol table found.\n"); return -1; } buffer_splice(&b, in, shdr->sh_offset, shdr->sh_size); if (check_size(in, shdr->sh_offset, buffer_size(&b), "symtab")) return -1; nsyms = shdr->sh_size / shdr->sh_entsize; pelf->syms = calloc(nsyms, sizeof(Elf64_Sym)); for (i = 0; i < nsyms; i++) { sym = &pelf->syms[i]; if (bit64) { sym->st_name = xdr->get32(&b); sym->st_info = xdr->get8(&b); sym->st_other = xdr->get8(&b); sym->st_shndx = xdr->get16(&b); sym->st_value = xdr->get64(&b); sym->st_size = xdr->get64(&b); } else { sym->st_name = xdr->get32(&b); sym->st_value = xdr->get32(&b); sym->st_size = xdr->get32(&b); sym->st_info = xdr->get8(&b); sym->st_other = xdr->get8(&b); sym->st_shndx = xdr->get16(&b); } } return 0; }
static int reloc_read(const struct buffer *in, struct parsed_elf *pelf, struct xdr *xdr, int bit64) { struct buffer b; Elf64_Word i; Elf64_Ehdr *ehdr; ehdr = &pelf->ehdr; pelf->relocs = calloc(ehdr->e_shnum, sizeof(Elf64_Rela *)); /* Allocate array for each section that contains relocation entries. */ for (i = 0; i < ehdr->e_shnum; i++) { Elf64_Shdr *shdr; Elf64_Rela *rela; Elf64_Xword j; Elf64_Xword nrelocs; int is_rela; shdr = &pelf->shdr[i]; /* Only process REL and RELA sections. */ if (shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA) continue; DEBUG("Checking relocation section %u\n", i); /* Ensure the section that relocations apply is a valid. */ if (shdr->sh_info >= ehdr->e_shnum || shdr->sh_info == SHN_UNDEF) { ERROR("Relocations apply to an invalid section: %u\n", shdr[i].sh_info); return -1; } is_rela = shdr->sh_type == SHT_RELA; /* Determine the number relocations in this section. */ nrelocs = shdr->sh_size / shdr->sh_entsize; pelf->relocs[i] = calloc(nrelocs, sizeof(Elf64_Rela)); buffer_splice(&b, in, shdr->sh_offset, shdr->sh_size); if (check_size(in, shdr->sh_offset, buffer_size(&b), "relocation section")) { ERROR("Relocation section %u failed.\n", i); return -1; } rela = pelf->relocs[i]; for (j = 0; j < nrelocs; j++) { if (bit64) { rela->r_offset = xdr->get64(&b); rela->r_info = xdr->get64(&b); if (is_rela) rela->r_addend = xdr->get64(&b); } else { uint32_t r_info; rela->r_offset = xdr->get32(&b); r_info = xdr->get32(&b); rela->r_info = ELF64_R_INFO(ELF32_R_SYM(r_info), ELF32_R_TYPE(r_info)); if (is_rela) rela->r_addend = xdr->get32(&b); } rela++; } } return 0; }