--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -158,24 +158,30 @@ $(TARGET).efi: VIRT_BASE = 0x$(shell $(N
 $(TARGET).efi: ALT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A ALT_START$$,,p')
 # Don't use $(wildcard ...) here - at least make 3.80 expands this too early!
 $(TARGET).efi: guard = $(if $(shell echo efi/dis* | grep disabled),:)
+
 ifneq ($(build_id_linker),)
-$(TARGET).efi: note.o
+ifeq ($(call ld-ver-build-id,$(LD) $(filter -m%,$(EFI_LDFLAGS))),y)
+CFLAGS += -DBUILD_ID_EFI
+EFI_LDFLAGS += $(build_id_linker)
+note_file := efi/buildid.o
+else
 note_file := note.o
+endif
 else
 note_file :=
 endif
 
-$(TARGET).efi: prelink-efi.o efi.lds efi/relocs-dummy.o $(BASEDIR)/common/symbols-dummy.o efi/mkreloc
+$(TARGET).efi: prelink-efi.o $(note_file) efi.lds efi/relocs-dummy.o $(BASEDIR)/common/symbols-dummy.o efi/mkreloc
 	$(foreach base, $(VIRT_BASE) $(ALT_BASE), \
 	          $(guard) $(LD) $(call EFI_LDFLAGS,$(base)) -T efi.lds -N $< efi/relocs-dummy.o \
-	                $(BASEDIR)/common/symbols-dummy.o -o $(@D)/.$(@F).$(base).0 &&) :
+	                $(BASEDIR)/common/symbols-dummy.o $(note_file) -o $(@D)/.$(@F).$(base).0 &&) :
 	$(guard) efi/mkreloc $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).0) >$(@D)/.$(@F).0r.S
 	$(guard) $(NM) -pa --format=sysv $(@D)/.$(@F).$(VIRT_BASE).0 \
 		| $(guard) $(BASEDIR)/tools/symbols $(all_symbols) --sysv --sort >$(@D)/.$(@F).0s.S
 	$(guard) $(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).0r.o $(@D)/.$(@F).0s.o
 	$(foreach base, $(VIRT_BASE) $(ALT_BASE), \
 	          $(guard) $(LD) $(call EFI_LDFLAGS,$(base)) -T efi.lds -N $< \
-	                $(@D)/.$(@F).0r.o $(@D)/.$(@F).0s.o -o $(@D)/.$(@F).$(base).1 &&) :
+	                $(@D)/.$(@F).0r.o $(@D)/.$(@F).0s.o $(note_file) -o $(@D)/.$(@F).$(base).1 &&) :
 	$(guard) efi/mkreloc $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).1) >$(@D)/.$(@F).1r.S
 	$(guard) $(NM) -pa --format=sysv $(@D)/.$(@F).$(VIRT_BASE).1 \
 		| $(guard) $(BASEDIR)/tools/symbols $(all_symbols) --sysv --sort >$(@D)/.$(@F).1s.S
@@ -185,8 +191,8 @@ $(TARGET).efi: prelink-efi.o efi.lds efi
 	if $(guard) false; then rm -f $@; echo 'EFI support disabled'; fi
 	rm -f $(@D)/.$(@F).[0-9]*
 
-efi/boot.init.o efi/runtime.o efi/compat.o: $(BASEDIR)/arch/x86/efi/built_in.o
-efi/boot.init.o efi/runtime.o efi/compat.o: ;
+efi/boot.init.o efi/runtime.o efi/compat.o efi/buildid.o: $(BASEDIR)/arch/x86/efi/built_in.o
+efi/boot.init.o efi/runtime.o efi/compat.o efi/buildid.o: ;
 
 asm-offsets.s: $(TARGET_SUBARCH)/asm-offsets.c
 	$(CC) $(filter-out -flto,$(CFLAGS)) -S -o $@ $<
--- a/xen/arch/x86/efi/Makefile
+++ b/xen/arch/x86/efi/Makefile
@@ -9,6 +9,9 @@ efi := $(if $(efi),$(shell $(CC) $(filte
 efi := $(if $(efi),$(shell $(LD) -mi386pep --subsystem=10 -o check.efi check.o 2>disabled && echo y))
 efi := $(if $(efi),$(shell rm disabled)y,$(shell $(call create,boot.init.o); $(call create,runtime.o)))
 
-extra-$(efi) += boot.init.o relocs-dummy.o runtime.o compat.o
+extra-$(efi) += boot.init.o relocs-dummy.o runtime.o compat.o buildid.o
+
+%.o: %.ihex
+	$(OBJCOPY) -I ihex -O binary $< $@
 
 stub.o: $(extra-y)
--- /dev/null
+++ b/xen/arch/x86/efi/buildid.ihex
@@ -0,0 +1,3 @@
+:10000000648600004D8DAD57140000000000000014
+:0400100000000000EC
+:00000001FF
--- a/xen/arch/x86/efi/mkreloc.c
+++ b/xen/arch/x86/efi/mkreloc.c
@@ -342,6 +342,7 @@ int main(int argc, char *argv[])
          */
         if ( memcmp(sec1[i].name, ".initcal", sizeof(sec1[i].name)) == 0 ||
              memcmp(sec1[i].name, ".init.se", sizeof(sec1[i].name)) == 0 ||
+             memcmp(sec1[i].name, ".buildid", sizeof(sec1[i].name)) == 0 ||
              memcmp(sec1[i].name, ".lockpro", sizeof(sec1[i].name)) == 0 )
             continue;
 
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -93,7 +93,7 @@ SECTIONS
        *(.rodata)
        *(.rodata.*)
 
-#if defined(BUILD_ID) && defined(EFI)
+#if defined(BUILD_ID) && defined(EFI) && !defined(BUILD_ID_EFI)
 /*
  * No mechanism to put an PT_NOTE in the EFI file - so put
  * it in .rodata section. (notes.o supplies us with .note.gnu.build-id).
@@ -122,19 +122,26 @@ SECTIONS
 #endif
   } :text
 
-#if defined(BUILD_ID) && !defined(EFI)
+#if defined(BUILD_ID)
+#if !defined(EFI)
 /*
  * What a strange section name. The reason is that on ELF builds this section
  * is extracted to notes.o (which then is ingested in the EFI file). But the
  * compiler may want to inject other things in the .note which we don't care
  * about - hence this unique name.
  */
-  . = ALIGN(4);
   .note.gnu.build-id : {
        __note_gnu_build_id_start = .;
        *(.note.gnu.build-id)
        __note_gnu_build_id_end = .;
   } :note :text
+#elif defined(BUILD_ID_EFI)
+  .buildid : {
+       __note_gnu_build_id_start = .;
+       *(.buildid)
+       __note_gnu_build_id_end = .;
+  } :text
+#endif
 #endif
   _erodata = .;
 
--- a/xen/common/version.c
+++ b/xen/common/version.c
@@ -4,6 +4,7 @@
 #include <xen/lib.h>
 #include <xen/string.h>
 #include <xen/types.h>
+#include <xen/efi.h>
 #include <xen/elf.h>
 #include <xen/version.h>
 
@@ -117,10 +118,33 @@ int xen_build_id_check(const Elf_Note *n
     return 0;
 }
 
+struct pe_external_debug_directory
+{
+    uint32_t characteristics;
+    uint32_t time_stamp;
+    uint16_t major_version;
+    uint16_t minor_version;
+#define PE_IMAGE_DEBUG_TYPE_CODEVIEW 2
+    uint32_t type;
+    uint32_t size;
+    uint32_t rva_of_data;
+    uint32_t filepos_of_data;
+};
+
+struct cv_info_pdb70
+{
+#define CVINFO_PDB70_CVSIGNATURE 0x53445352 /* "RSDS" */
+    uint32_t cv_signature;
+    unsigned char signature[16];
+    uint32_t age;
+    char pdb_filename[];
+};
+
 static int __init xen_build_init(void)
 {
     const Elf_Note *n = __note_gnu_build_id_start;
     unsigned int sz;
+    int rc;
 
     /* --build-id invoked with wrong parameters. */
     if ( __note_gnu_build_id_end <= &n[0] )
@@ -132,7 +156,38 @@ static int __init xen_build_init(void)
 
     sz = (void *)__note_gnu_build_id_end - (void *)n;
 
-    return xen_build_id_check(n, sz, &build_id_p, &build_id_len);
+    rc = xen_build_id_check(n, sz, &build_id_p, &build_id_len);
+
+#ifdef CONFIG_X86
+    /* Alternatively we may have a CodeView record from an EFI build. */
+    if ( rc && efi_enabled )
+    {
+        const struct pe_external_debug_directory *dir = (const void *)n;
+
+        /*
+         * Validate that the full-note-header check above won't prevent
+         * fall-through to the CodeView case here.
+         */
+        BUILD_BUG_ON(sizeof(*n) > sizeof(*dir));
+
+        if ( sz > sizeof(*dir) + sizeof(struct cv_info_pdb70) &&
+             dir->type == PE_IMAGE_DEBUG_TYPE_CODEVIEW &&
+             dir->size > sizeof(struct cv_info_pdb70) &&
+             XEN_VIRT_START + dir->rva_of_data == (unsigned long)(dir + 1) )
+        {
+            const struct cv_info_pdb70 *info = (const void *)(dir + 1);
+
+            if ( info->cv_signature == CVINFO_PDB70_CVSIGNATURE )
+            {
+                build_id_p = info->signature;
+                build_id_len = sizeof(info->signature);
+                rc = 0;
+            }
+        }
+    }
+#endif
+
+    return rc;
 }
 __initcall(xen_build_init);
 #endif
