int rmodule_create(const struct buffer *elfin, struct buffer *elfout) { struct rmod_context ctx; int ret = -1; if (rmodule_init(&ctx, elfin)) goto out; if (rmodule_collect_relocations(&ctx, NULL)) goto out; if (populate_rmodule_info(&ctx)) goto out; if (write_elf(&ctx, elfin, elfout)) goto out; ret = 0; out: rmodule_cleanup(&ctx); return ret; }
int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output, uint32_t *location, const char *ignore_section) { struct xip_context xipctx; struct rmod_context *rmodctx; struct reloc_filter filter; struct parsed_elf *pelf; size_t output_sz; uint32_t adjustment; struct buffer binput; struct buffer boutput; Elf64_Xword i; int ret = -1; xipctx.ignored_section_idx = 0; rmodctx = &xipctx.rmodctx; pelf = &rmodctx->pelf; if (rmodule_init(rmodctx, input)) return -1; /* Only support x86 XIP currently. */ if (rmodctx->pelf.ehdr.e_machine != EM_386) { ERROR("Only support XIP stages for x86\n"); goto out; } xipctx.ignored_section = find_ignored_section_header(pelf, ignore_section); if (xipctx.ignored_section != NULL) xipctx.ignored_section_idx = xipctx.ignored_section - pelf->shdr; filter.filter = rmod_filter; filter.context = &xipctx; if (rmodule_collect_relocations(rmodctx, &filter)) goto out; output_sz = sizeof(struct cbfs_stage) + pelf->phdr->p_filesz; if (buffer_create(output, output_sz, input->name) != 0) { ERROR("Unable to allocate memory: %m\n"); goto out; } buffer_clone(&boutput, output); memset(buffer_get(&boutput), 0, output_sz); buffer_set_size(&boutput, 0); /* Single loadable segment. The entire segment moves to final * location from based on virtual address of loadable segment. */ adjustment = *location - pelf->phdr->p_vaddr; DEBUG("Relocation adjustment: %08x\n", adjustment); fill_cbfs_stage(&boutput, CBFS_COMPRESS_NONE, (uint32_t)pelf->ehdr.e_entry + adjustment, (uint32_t)pelf->phdr->p_vaddr + adjustment, pelf->phdr->p_filesz, pelf->phdr->p_memsz); /* Need an adjustable buffer. */ buffer_clone(&binput, input); buffer_seek(&binput, pelf->phdr->p_offset); bputs(&boutput, buffer_get(&binput), pelf->phdr->p_filesz); buffer_clone(&boutput, output); buffer_seek(&boutput, sizeof(struct cbfs_stage)); /* Make adjustments to all the relocations within the program. */ for (i = 0; i < rmodctx->nrelocs; i++) { size_t reloc_offset; uint32_t val; struct buffer in, out; /* The relocations represent in-program addresses of the * linked program. Obtain the offset into the program to do * the adjustment. */ reloc_offset = rmodctx->emitted_relocs[i] - pelf->phdr->p_vaddr; buffer_clone(&out, &boutput); buffer_seek(&out, reloc_offset); buffer_clone(&in, &out); /* Appease around xdr semantics: xdr decrements buffer * size when get()ing and appends to size when put()ing. */ buffer_set_size(&out, 0); val = xdr_le.get32(&in); DEBUG("reloc %zx %08x -> %08x\n", reloc_offset, val, val + adjustment); xdr_le.put32(&out, val + adjustment); } /* Need to back up the location to include cbfs stage metadata. */ *location -= sizeof(struct cbfs_stage); ret = 0; out: rmodule_cleanup(rmodctx); return ret; }