From: Ard Biesheuvel <ardb@xxxxxxxxxx> The purgatory ELF object is typically a partially linked object, which puts the burden on the kexec loader to lay out the executable in memory, and this involves (among other things) deciding the placement of the sections in memory, and fixing up all relocations (relative and absolute ones) All of this can be greatly simplified by using a fully linked PIE ELF executable instead, constructed in a way that removes the need for any relocation processing or layout and allocation of individual sections. By gathering all allocatable sections into a single PT_LOAD segment, and relying on RIP-relative references, all relocations will be applied by the linker, and the segment simply needs to be copied into memory. So add a linker script and some minimal handling in generic code, which can be used by architectures to opt into this approach. This will be wired up for x86 in a subsequent patch. Signed-off-by: Ard Biesheuvel <ardb@xxxxxxxxxx> --- include/asm-generic/purgatory.lds | 34 ++++++++++ kernel/kexec_file.c | 68 +++++++++++++++++++- 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/include/asm-generic/purgatory.lds b/include/asm-generic/purgatory.lds new file mode 100644 index 000000000000..260c457f7608 --- /dev/null +++ b/include/asm-generic/purgatory.lds @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +PHDRS +{ + text PT_LOAD FLAGS(7) FILEHDR PHDRS; +} + +SECTIONS +{ + . = SIZEOF_HEADERS; + + .text : { + *(.text .rodata* .kexec-purgatory .data*) + } :text + + .bss : { + *(.bss .dynbss) + } :text + + .rela.dyn : { + *(.rela.*) + } + + .symtab 0 : { *(.symtab) } + .strtab 0 : { *(.strtab) } + .shstrtab 0 : { *(.shstrtab) } + + /DISCARD/ : { + *(.interp .modinfo .dynsym .dynstr .hash .gnu.* .dynamic .comment) + *(.got .plt .got.plt .plt.got .note.* .eh_frame .sframe) + } +} + +ASSERT(SIZEOF(.rela.dyn) == 0, "Absolute relocations detected"); diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index bef2f6f2571b..6379f8dfc29f 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -1010,6 +1010,62 @@ static int kexec_apply_relocations(struct kimage *image) return 0; } +/* + * kexec_load_purgatory_pie - Load the position independent purgatory object. + * @pi: Purgatory info struct. + * @kbuf: Memory parameters to use. + * + * Load a purgatory PIE executable. This is a fully linked executable + * consisting of a single PT_LOAD segment that does not require any relocation + * processing. + * + * Return: 0 on success, negative errno on error. + */ +static int kexec_load_purgatory_pie(struct purgatory_info *pi, + struct kexec_buf *kbuf) +{ + const Elf_Phdr *phdr = (void *)pi->ehdr + pi->ehdr->e_phoff; + int ret; + + if (pi->ehdr->e_phnum != 1) + return -EINVAL; + + kbuf->bufsz = phdr->p_filesz; + kbuf->memsz = phdr->p_memsz; + kbuf->buf_align = phdr->p_align; + + kbuf->buffer = vzalloc(kbuf->bufsz); + if (!kbuf->buffer) + return -ENOMEM; + + ret = kexec_add_buffer(kbuf); + if (ret) + goto out_free_kbuf; + + kbuf->image->start = kbuf->mem + pi->ehdr->e_entry; + + pi->sechdrs = vcalloc(pi->ehdr->e_shnum, pi->ehdr->e_shentsize); + if (!pi->sechdrs) + goto out_free_kbuf; + + pi->purgatory_buf = memcpy(kbuf->buffer, + (void *)pi->ehdr + phdr->p_offset, + kbuf->bufsz); + + memcpy(pi->sechdrs, (void *)pi->ehdr + pi->ehdr->e_shoff, + pi->ehdr->e_shnum * pi->ehdr->e_shentsize); + + for (int i = 0; i < pi->ehdr->e_shnum; i++) + if (pi->sechdrs[i].sh_flags & SHF_ALLOC) + pi->sechdrs[i].sh_addr += kbuf->mem; + + return 0; + +out_free_kbuf: + vfree(kbuf->buffer); + return ret; +} + /* * kexec_load_purgatory - Load and relocate the purgatory object. * @image: Image to add the purgatory to. @@ -1031,6 +1087,9 @@ int kexec_load_purgatory(struct kimage *image, struct kexec_buf *kbuf) pi->ehdr = (const Elf_Ehdr *)kexec_purgatory; + if (pi->ehdr->e_type != ET_REL) + return kexec_load_purgatory_pie(pi, kbuf); + ret = kexec_purgatory_setup_kbuf(pi, kbuf); if (ret) return ret; @@ -1087,7 +1146,8 @@ static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi, /* Go through symbols for a match */ for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) { - if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL) + if (pi->ehdr->e_type == ET_REL && + ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL) continue; if (strcmp(strtab + syms[k].st_name, name) != 0) @@ -1159,6 +1219,12 @@ int kexec_purgatory_get_set_symbol(struct kimage *image, const char *name, sym_buf = (char *)pi->purgatory_buf + sec->sh_offset + sym->st_value; + if (pi->ehdr->e_type != ET_REL) { + const Elf_Shdr *shdr = (void *)pi->ehdr + pi->ehdr->e_shoff; + + sym_buf -= shdr[sym->st_shndx].sh_addr; + } + if (get_value) memcpy((void *)buf, sym_buf, size); else -- 2.44.0.769.g3c40516874-goog _______________________________________________ kexec mailing list kexec@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/kexec