diff --git a/xen/arch/x86/Rules.mk b/xen/arch/x86/Rules.mk
index 568657e..b501c88 100644
--- a/xen/arch/x86/Rules.mk
+++ b/xen/arch/x86/Rules.mk
@@ -7,6 +7,8 @@ CFLAGS += -I$(BASEDIR)/include
 CFLAGS += -I$(BASEDIR)/include/asm-x86/mach-generic
 CFLAGS += -I$(BASEDIR)/include/asm-x86/mach-default
 CFLAGS += -DXEN_IMG_OFFSET=$(XEN_IMG_OFFSET)
+CFLAGS += -DXEN_LOAD_ALIGN=XEN_IMG_OFFSET
+CFLAGS += -DXEN_FILE_ALIGN=PAGE_SIZE
 CFLAGS += '-D__OBJECT_LABEL__=$(subst /,$$,$(subst -,_,$(subst $(BASEDIR)/,,$(CURDIR))/$@))'
 
 # Prevent floating-point variables from creeping into Xen.
diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
index fd6fc33..28bbc04 100644
--- a/xen/arch/x86/boot/head.S
+++ b/xen/arch/x86/boot/head.S
@@ -1,3 +1,4 @@
+#include <xen/compile.h>
 #include <xen/multiboot.h>
 #include <xen/multiboot2.h>
 #include <public/xen.h>
@@ -44,6 +45,150 @@
 .Lmb2ht_init_end\@:
         .endm
 
+        .section .efi.pe.header, "a", @progbits
+
+ENTRY(efi_pe_head)
+        /*
+         * Legacy EXE header.
+         *
+         * Most of it is copied from binutils package, version 2.28,
+         * include/coff/pe.h:struct external_PEI_filehdr and
+         * bfd/peXXigen.c:_bfd_XXi_only_swap_filehdr_out().
+         *
+         * Page is equal 512 bytes here.
+         * Paragraph is equal 16 bytes here.
+         */
+        .short  0x5a4d                             /* EXE magic number. */
+        .short  0x90                               /* Bytes on last page of file. */
+        .short  0x3                                /* Pages in file. */
+        .short  0                                  /* Relocations. */
+        .short  0x4                                /* Size of header in paragraphs. */
+        .short  0                                  /* Minimum extra paragraphs needed. */
+        .short  0xffff                             /* Maximum extra paragraphs needed. */
+        .short  0                                  /* Initial (relative) SS value. */
+        .short  0xb8                               /* Initial SP value. */
+        .short  0                                  /* Checksum. */
+        .short  0                                  /* Initial IP value. */
+        .short  0                                  /* Initial (relative) CS value. */
+        .short  0x40                               /* File address of relocation table. */
+        .short  0                                  /* Overlay number. */
+        .fill   4, 2, 0                            /* Reserved words. */
+        .short  0                                  /* OEM identifier. */
+        .short  0                                  /* OEM information. */
+        .fill   10, 2, 0                           /* Reserved words. */
+        .long   pe_header - efi_pe_head            /* File address of the PE header. */
+
+        /*
+         * DOS message.
+         *
+         * It is copied from binutils package, version 2.28,
+         * include/coff/pe.h:struct external_PEI_filehdr and
+         * bfd/peXXigen.c:_bfd_XXi_only_swap_filehdr_out().
+         */
+        .long   0x0eba1f0e
+        .long   0xcd09b400
+        .long   0x4c01b821
+        .long   0x685421cd
+        .long   0x70207369
+        .long   0x72676f72
+        .long   0x63206d61
+        .long   0x6f6e6e61
+        .long   0x65622074
+        .long   0x6e757220
+        .long   0x206e6920
+        .long   0x20534f44
+        .long   0x65646f6d
+        .long   0x0a0d0d2e
+        .long   0x24
+        .long   0
+
+        /*
+         * PE/COFF header.
+         *
+         * The PE/COFF format is defined by Microsoft, and is available from
+         * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
+         * 
+         * Some ideas are taken from Linux kernel and Xen ARM64.
+         */
+
+pe_header:
+        .ascii  "PE\0\0"                           /* PE signature. */
+        .short  0x8664                             /* Machine: IMAGE_FILE_MACHINE_AMD64. */
+        .short  1                                  /* NumberOfSections. */
+        .long   XEN_COMPILE_POSIX_TIME             /* TimeDateStamp. */
+        .long   0                                  /* PointerToSymbolTable. */
+        .long   0                                  /* NumberOfSymbols. */
+        .short  section_table - optional_header    /* SizeOfOptionalHeader. */
+        .short  0x226                              /* Characteristics:
+                                                    *   IMAGE_FILE_EXECUTABLE_IMAGE |
+                                                    *   IMAGE_FILE_LARGE_ADDRESS_AWARE |
+                                                    *   IMAGE_FILE_DEBUG_STRIPPED |
+                                                    *   IMAGE_FILE_LINE_NUMS_STRIPPED
+                                                    */
+
+optional_header:
+        .short  0x20b                              /* PE format: PE32+ */
+        .byte   0x02                               /* MajorLinkerVersion. */
+        .byte   0x14                               /* MinorLinkerVersion. */
+        .long   __2M_rwdata_end - efi_pe_head_end  /* SizeOfCode. */
+        .long   0                                  /* SizeOfInitializedData. */
+        .long   0                                  /* SizeOfUninitializedData. */
+        .long   sym_offs(efi_start)                /* AddressOfEntryPoint. */
+        .long   sym_offs(start)                    /* BaseOfCode. */
+        .quad   sym_offs(__image_base__)           /* ImageBase. */
+        .long   XEN_LOAD_ALIGN                     /* SectionAlignment. */
+        .long   XEN_FILE_ALIGN                     /* FileAlignment. */
+        .short  2                                  /* MajorOperatingSystemVersion. */
+        .short  0                                  /* MinorOperatingSystemVersion. */
+        .short  XEN_VERSION                        /* MajorImageVersion. */
+        .short  XEN_SUBVERSION                     /* MinorImageVersion. */
+        .short  2                                  /* MajorSubsystemVersion. */
+        .short  0                                  /* MinorSubsystemVersion. */
+        .long   0                                  /* Win32VersionValue. */
+        .long   __pe_SizeOfImage                   /* SizeOfImage. */
+        .long   efi_pe_head_end - efi_pe_head      /* SizeOfHeaders. */
+        .long   0                                  /* CheckSum. */
+        .short  0xa                                /* Subsystem: EFI application. */
+        .short  0                                  /* DllCharacteristics. */
+        .quad   0                                  /* SizeOfStackReserve. */
+        .quad   0                                  /* SizeOfStackCommit. */
+        .quad   0                                  /* SizeOfHeapReserve. */
+        .quad   0                                  /* SizeOfHeapCommit. */
+        .long   0                                  /* LoaderFlags. */
+        .long   0x6                                /* NumberOfRvaAndSizes. */
+
+        /* Data Directories. */
+        .quad   0                                  /* Export Table. */
+        .quad   0                                  /* Import Table. */
+        .quad   0                                  /* Resource Table. */
+        .quad   0                                  /* Exception Table. */
+        .quad   0                                  /* Certificate Table. */
+        .quad   0                                  /* Base Relocation Table. */
+
+section_table:
+        .ascii  ".text\0\0\0"                      /* Name. */
+        .long   __2M_rwdata_end - efi_pe_head_end  /* VirtualSize. */
+        .long   sym_offs(start)                    /* VirtualAddress. */
+        .long   __bss_start - efi_pe_head_end      /* SizeOfRawData. */
+        .long   efi_pe_head_end - efi_pe_head      /* PointerToRawData. */
+        .long   0                                  /* PointerToRelocations. */
+        .long   0                                  /* PointerToLinenumbers. */
+        .short  0                                  /* NumberOfRelocations. */
+        .short  0                                  /* NumberOfLinenumbers. */
+        .long   0xe0500020                         /* Characteristics:
+                                                    *   IMAGE_SCN_CNT_CODE |
+                                                    *   IMAGE_SCN_ALIGN_16BYTES |
+                                                    *   IMAGE_SCN_MEM_EXECUTE |
+                                                    *   IMAGE_SCN_MEM_READ |
+                                                    *   IMAGE_SCN_MEM_WRITE
+                                                    */
+
+        .align XEN_FILE_ALIGN
+ENTRY(efi_pe_head_end)
+
+        .text
+        .code32
+
 ENTRY(start)
         jmp     __start
 
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 8289a1b..3c115b9 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -54,7 +54,12 @@ SECTIONS
   __2M_text_start = .;         /* Start of 2M superpages, mapped RX. */
 #endif
 
-  . = __XEN_VIRT_START + XEN_IMG_OFFSET;
+  . = __XEN_VIRT_START + XEN_IMG_OFFSET - efi_pe_head_end + efi_pe_head;
+
+  .efi.pe.header : {
+       *(.efi.pe.header)
+  } :NONE
+
   _start = .;
   .text : {
         _stext = .;            /* Text and read-only data */
@@ -234,6 +239,8 @@ SECTIONS
        *(.data.rel)
        *(.data.rel.*)
        CONSTRUCTORS
+       /* PE file must end at XEN_FILE_ALIGN boundary. */
+       . = ALIGN(XEN_FILE_ALIGN);
   } :text
 
   .bss : {                     /* BSS */
@@ -259,6 +266,8 @@ SECTIONS
 #endif
   __2M_rwdata_end = .;
 
+  __pe_SizeOfImage = ALIGN(. - __image_base__, XEN_LOAD_ALIGN);
+
 #ifdef EFI
   . = ALIGN(4);
   .reloc : {
@@ -337,3 +346,8 @@ ASSERT((trampoline_end - trampoline_start) < TRAMPOLINE_SPACE - MBI_SPACE_MIN,
     "not enough room for trampoline and mbi data")
 ASSERT((wakeup_stack - wakeup_stack_start) >= WAKEUP_STACK_MIN,
     "wakeup stack too small")
+
+ASSERT(efi_pe_head_end == _start, "PE header does not end at the beginning of .text section")
+ASSERT(_start == __XEN_VIRT_START + XEN_IMG_OFFSET, ".text section begins at wrong address")
+ASSERT(IS_ALIGNED(_start,      XEN_FILE_ALIGN), "_start misaligned")
+ASSERT(IS_ALIGNED(__bss_start, XEN_FILE_ALIGN), "__bss_start misaligned")
