[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] xen/x86: fix xen.efi boot crash from some bootloaders



xen.efi PE does not boot when loaded from shim or some patched
downstream grub2.

What happens is the bootloader would honour the MEM_DISCARDABLE
flag of the .reloc section meaning it would not load its content
into memory.

But Xen is parsing the .reloc section content twice at boot:
* https://elixir.bootlin.com/xen/v4.20.1/source/xen/common/efi/boot.c#L1362
* https://elixir.bootlin.com/xen/v4.20.1/source/xen/arch/x86/efi/efi-boot.h#L237

Therefore it would crash with the following message:
"Unsupported relocation type" as reported there:

* https://github.com/QubesOS/qubes-issues/issues/8206#issuecomment-2619048838
* 
https://lore.kernel.org/xen-devel/7e039262-1f54-46e1-8f70-ac3f03607d5a@xxxxxxxx/T/#me122b9e6c27cd98db917da2c9f67e74a2c6ad7a5

This commit adds a small C host tool named keeprelocs
that is called after xen.efi is produced by the build system
in order to remove this bit from its .reloc section header.

Signed-off-by: Yann Sionneau <yann.sionneau@xxxxxxxxxx>
---
 xen/Makefile           |   5 +-
 xen/arch/x86/Makefile  |   1 +
 xen/tools/Makefile     |   3 ++
 xen/tools/keeprelocs.c | 119 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 127 insertions(+), 1 deletion(-)
 create mode 100644 xen/tools/keeprelocs.c

diff --git a/xen/Makefile b/xen/Makefile
index 8fc4e042ff..7dc9cd7e05 100644
--- a/xen/Makefile
+++ b/xen/Makefile
@@ -299,10 +299,13 @@ export XEN_HAS_CHECKPOLICY := $(call 
success,$(CHECKPOLICY) -h 2>&1 | grep -q xe
 # ===========================================================================
 # Rules shared between *config targets and build targets
 
-PHONY += tools_fixdep
+PHONY += tools_fixdep tools_keeprelocs
 tools_fixdep:
        $(Q)$(MAKE) $(build)=tools tools/fixdep
 
+tools_keeprelocs:
+       $(Q)$(MAKE) $(build)=tools tools/keeprelocs
+
 PHONY += outputmakefile
 # Before starting out-of-tree build, make sure the source tree is clean.
 # outputmakefile generates a Makefile in the output directory, if using a
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index ce724a9daa..9a47002fae 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -236,6 +236,7 @@ endif
        $(NM) -pa --format=sysv $@ \
                | $(objtree)/tools/symbols --all-symbols --xensyms --sysv 
--sort \
                > $@.map
+       $(objtree)/tools/keeprelocs -q -i $@
 ifeq ($(CONFIG_DEBUG_INFO),y)
        $(if $(filter --strip-debug,$(EFI_LDFLAGS)),:$(space))$(OBJCOPY) -O 
elf64-x86-64 $@ $@.elf
 endif
diff --git a/xen/tools/Makefile b/xen/tools/Makefile
index a5078b7cb8..4fd917b398 100644
--- a/xen/tools/Makefile
+++ b/xen/tools/Makefile
@@ -1,2 +1,5 @@
 hostprogs-always-y += symbols
 hostprogs-always-y += fixdep
+hostprogs-always-$(XEN_BUILD_PE) += keeprelocs
+# next line is to allow including include/efi/pe.h
+HOSTCFLAGS_keeprelocs.o := -I ../include
\ No newline at end of file
diff --git a/xen/tools/keeprelocs.c b/xen/tools/keeprelocs.c
new file mode 100644
index 0000000000..c169ddba1a
--- /dev/null
+++ b/xen/tools/keeprelocs.c
@@ -0,0 +1,119 @@
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <efi/pe.h>
+
+#undef DEBUG
+
+static void print_usage(const char *name) {
+       printf("%s: [-q] [-h] -i xen.efi\n", name);
+}
+
+int main(int argc, char **argv)
+{
+       char *filename = NULL;
+       int fd;
+       char *mem;
+       struct stat st;
+       off_t len;
+       int ret;
+       struct mz_hdr *mz;
+       struct pe_hdr *pe;
+       int opt;
+       const char *prog_name = argv[0];
+       int quiet = 0;
+
+       while ((opt = getopt(argc, argv, ":i:qh")) != -1)
+       {
+               switch (opt) {
+               case 'i':
+                       filename = optarg;
+                       break;
+               case 'q':
+                       quiet = 1;
+                       break;
+               case 'h':
+                       print_usage(prog_name);
+                       return 0;
+                       break;
+               case '?':
+               default:
+                       print_usage(prog_name);
+                       return -1;
+               }
+       }
+
+
+       if (!filename) {
+               printf("Error: you must provide a `-i xen.efi` argument\n");
+               return -1;
+       }
+
+       fd = open(filename, O_RDWR);
+       if (fd < 0) {
+               printf("Could not open file %s: %s\n", filename, 
strerror(errno));
+               return -1;
+       }
+
+       ret = fstat(fd, &st);
+       if (ret < 0) {
+               perror("Error while getting PE file length");
+               return -1;
+       }
+
+       len = st.st_size;
+       mem = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+       if (mem == MAP_FAILED) {
+               perror("Failed to mmap PE file");
+               return -1;
+       }
+
+       mz = (struct mz_hdr *)mem;
+       if (mz->magic != MZ_MAGIC) { // "MZ"
+               printf("file has incorrect MZ header 0x%02x instead of 
0x5a4d\n", mz->magic);
+               return -1;
+       }
+
+       pe = (struct pe_hdr *)(mem + mz->peaddr);
+       if (strncmp((char *)&pe->magic, "PE\0\0", 4)) {
+               printf("file has incorrect PE header magic %08x instead of 
0x00004550\n", pe->magic);
+               return -1;
+       }
+
+       if (pe->opt_hdr_size == 0) {
+               printf("file has empty OptionalHeader\n");
+               return -1;
+       }
+
+       struct section_header *section = (struct section_header *)((uint8_t 
*)pe + sizeof(*pe) + pe->opt_hdr_size);
+       for (unsigned int section_id = 0; section_id < pe->sections; 
section_id++, section++)
+       {
+#ifdef DEBUG
+               printf("section %s\n", section->Name);
+#endif
+               if (strncmp(section->name, ".reloc", strlen(".reloc")))
+                       continue;
+
+               if (!quiet)
+                       printf(".reloc section characteristics: %08x\n", 
section->flags);
+               if (section->flags & IMAGE_SCN_MEM_DISCARDABLE) {
+                       if (!quiet)
+                               printf("MEM_DISCARDABLE flag found! Dropping 
it.\n");
+                       section->flags &= ~(IMAGE_SCN_MEM_DISCARDABLE);
+               }
+       }
+
+       munmap(mem, len);
+       close(fd);
+
+       if (!quiet)
+               printf("Ok!\n");
+       return 0;
+}
-- 
2.43.0



Yann Sionneau | Vates XCP-ng Developer

XCP-ng & Xen Orchestra - Vates solutions

web: https://vates.tech




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.