[RFC PATCH 6/9] kexec: Add support for fully linked purgatory executables
Ard Biesheuvel
ardb+git at google.com
Wed Apr 24 08:53:16 PDT 2024
From: Ard Biesheuvel <ardb at kernel.org>
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 at kernel.org>
---
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
More information about the kexec
mailing list