diff mbox series

[6/8] x86/EFI: avoid use of GNU ld's --disable-reloc-section when possible

Message ID ff15338a-ca10-ff38-3c2a-459303ce9d68@suse.com (mailing list archive)
State Superseded
Headers show
Series x86/EFI: build adjustments | expand

Commit Message

Jan Beulich April 1, 2021, 9:46 a.m. UTC
As of commit 6fa7408d72b3 ("ld: don't generate base relocations in PE
output for absolute symbols") I'm feeling sufficiently confident in GNU
ld to use its logic for generating base relocations, which was enabled
for executables at some point last year (prior to that this would have
got done only for DLLs).

GNU ld, seeing the original relocations coming from the ELF object files,
generates different relocation types for our page tables (64-bit ones,
while mkreloc produces 32-bit ones). This requires also permitting and
handling that type in efi_arch_relocate_image().

Signed-off-by: Jan Beulich <jbeulich@suse.com>

Comments

Roger Pau Monne April 21, 2021, 10:21 a.m. UTC | #1
On Thu, Apr 01, 2021 at 11:46:44AM +0200, Jan Beulich wrote:
> As of commit 6fa7408d72b3 ("ld: don't generate base relocations in PE
> output for absolute symbols") I'm feeling sufficiently confident in GNU
> ld to use its logic for generating base relocations, which was enabled
> for executables at some point last year (prior to that this would have
> got done only for DLLs).
> 
> GNU ld, seeing the original relocations coming from the ELF object files,
> generates different relocation types for our page tables (64-bit ones,
> while mkreloc produces 32-bit ones). This requires also permitting and
> handling that type in efi_arch_relocate_image().
> 
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
> 
> --- a/xen/arch/x86/Makefile
> +++ b/xen/arch/x86/Makefile
> @@ -120,18 +120,37 @@ $(TARGET): $(TARGET)-syms $(efi-y) boot/
>  	mv $(TMP) $(TARGET)
>  
>  ifneq ($(efi-y),)
> +
>  # Check if the compiler supports the MS ABI.
>  export XEN_BUILD_EFI := $(shell $(CC) $(XEN_CFLAGS) -c efi/check.c -o efi/check.o 2>/dev/null && echo y)
> +CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI
> +
>  # Check if the linker supports PE.
>  EFI_LDFLAGS = $(patsubst -m%,-mi386pep,$(XEN_LDFLAGS)) --subsystem=10 --strip-debug
>  XEN_BUILD_PE := $(if $(XEN_BUILD_EFI),$(shell $(LD) $(EFI_LDFLAGS) -o efi/check.efi efi/check.o 2>/dev/null && echo y))
> -CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI
> -# Check if the linker produces fixups in PE by default (we need to disable it doing so for now).
> -XEN_NO_PE_FIXUPS := $(if $(XEN_BUILD_EFI), \
> -                         $(shell $(LD) $(EFI_LDFLAGS) --disable-reloc-section -o efi/check.efi efi/check.o 2>/dev/null && \
> -                                 echo --disable-reloc-section))
> +
> +ifeq ($(XEN_BUILD_PE),y)
> +
> +# Check if the linker produces fixups in PE by default
> +nr-fixups := $(shell $(OBJDUMP) -p efi/check.efi | grep '^[[:blank:]]*reloc[[:blank:]]*[0-9][[:blank:]].*DIR64$$' | wc -l)
> +ifeq ($(nr-fixups),2)
> +MKRELOC := :
> +relocs-dummy :=
> +else
> +MKRELOC := efi/mkreloc
> +relocs-dummy := efi/relocs-dummy.o
> +# If the linker produced fixups but not precisely two of them, we need to
> +# disable it doing so.  But if it didn't produce any fixups, it also wouldn't
> +# recognize the option.
> +ifneq ($(nr-fixups),0)
> +EFI_LDFLAGS += --disable-reloc-section
> +endif
>  endif
>  
> +endif # $(XEN_BUILD_PE)
> +
> +endif # $(efi-y)
> +
>  ALL_OBJS := $(BASEDIR)/arch/x86/boot/built_in.o $(BASEDIR)/arch/x86/efi/built_in.o $(ALL_OBJS)
>  
>  ifeq ($(CONFIG_LTO),y)
> @@ -175,7 +194,7 @@ note.o: $(TARGET)-syms
>  		--rename-section=.data=.note.gnu.build-id -S $@.bin $@
>  	rm -f $@.bin
>  
> -EFI_LDFLAGS += --image-base=$(1) --stack=0,0 --heap=0,0 $(XEN_NO_PE_FIXUPS)
> +EFI_LDFLAGS += --image-base=$(1) --stack=0,0 --heap=0,0
>  EFI_LDFLAGS += --section-alignment=0x200000 --file-alignment=0x20
>  EFI_LDFLAGS += --major-image-version=$(XEN_VERSION)
>  EFI_LDFLAGS += --minor-image-version=$(XEN_SUBVERSION)
> @@ -189,7 +208,11 @@ EFI_LDFLAGS += --no-insert-timestamp
>  endif
>  
>  $(TARGET).efi: VIRT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A VIRT_START$$,,p')
> +ifeq ($(MKRELOC),:)
> +$(TARGET).efi: ALT_BASE :=
> +else
>  $(TARGET).efi: ALT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A ALT_START$$,,p')

Could you maybe check whether $(relocs-dummy) is set as the condition
here and use it here instead of efi/relocs-dummy.o?

> +endif
>  
>  ifneq ($(build_id_linker),)
>  ifeq ($(call ld-ver-build-id,$(LD) $(filter -m%,$(EFI_LDFLAGS))),y)
> @@ -210,16 +233,16 @@ note_file_option ?= $(note_file)
>  ifeq ($(XEN_BUILD_PE),y)
>  $(TARGET).efi: prelink.o $(note_file) efi.lds efi/relocs-dummy.o efi/mkreloc

Do you need to also replace the target prerequisite to use $(relocs-dummy)?

>  	$(foreach base, $(VIRT_BASE) $(ALT_BASE), \
> -	          $(LD) $(call EFI_LDFLAGS,$(base)) -T efi.lds -N $< efi/relocs-dummy.o \
> +	          $(LD) $(call EFI_LDFLAGS,$(base)) -T efi.lds -N $< $(relocs-dummy) \
>  	                $(BASEDIR)/common/symbols-dummy.o $(note_file_option) -o $(@D)/.$(@F).$(base).0 &&) :
> -	efi/mkreloc $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).0) >$(@D)/.$(@F).0r.S
> +	$(MKRELOC) $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).0) >$(@D)/.$(@F).0r.S
>  	$(NM) -pa --format=sysv $(@D)/.$(@F).$(VIRT_BASE).0 \
>  		| $(BASEDIR)/tools/symbols $(all_symbols) --sysv --sort >$(@D)/.$(@F).0s.S
>  	$(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).0r.o $(@D)/.$(@F).0s.o
>  	$(foreach base, $(VIRT_BASE) $(ALT_BASE), \
>  	          $(LD) $(call EFI_LDFLAGS,$(base)) -T efi.lds -N $< \
>  	                $(@D)/.$(@F).0r.o $(@D)/.$(@F).0s.o $(note_file_option) -o $(@D)/.$(@F).$(base).1 &&) :
> -	efi/mkreloc $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).1) >$(@D)/.$(@F).1r.S
> +	$(MKRELOC) $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).1) >$(@D)/.$(@F).1r.S
>  	$(NM) -pa --format=sysv $(@D)/.$(@F).$(VIRT_BASE).1 \
>  		| $(BASEDIR)/tools/symbols $(all_symbols) --sysv --sort >$(@D)/.$(@F).1s.S
>  	$(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).1r.o $(@D)/.$(@F).1s.o
> --- a/xen/arch/x86/efi/check.c
> +++ b/xen/arch/x86/efi/check.c
> @@ -2,3 +2,17 @@ int __attribute__((__ms_abi__)) test(int
>  {
>      return i;
>  }
> +
> +/*
> + * Populate an array with "addresses" of relocatable and absolute values.
> + * This is to probe ld for (a) emitting base relocations at all and (b) not
> + * emitting base relocations for absolute symbols.
> + */
> +extern const unsigned char __image_base__[], __file_alignment__[],
> +                           __section_alignment__[];
> +const void *const data[] = {
> +    __image_base__,
> +    __file_alignment__,
> +    __section_alignment__,
> +    data,
> +};
> --- a/xen/arch/x86/efi/efi-boot.h
> +++ b/xen/arch/x86/efi/efi-boot.h
> @@ -86,10 +86,12 @@ static void __init efi_arch_relocate_ima
>                  }
>                  break;
>              case PE_BASE_RELOC_DIR64:
> -                if ( in_page_tables(addr) )
> -                    blexit(L"Unexpected relocation type");
>                  if ( delta )
> +                {
>                      *(u64 *)addr += delta;
> +                    if ( in_page_tables(addr) )
> +                        *(u64 *)addr += xen_phys_start;

Doesn't the in_page_tables check and modification also apply when
delta == 0?

Maybe you could just break on !delta to reduce indentation if none of
this applies then?

Thanks, Roger.
Jan Beulich April 21, 2021, 12:03 p.m. UTC | #2
On 21.04.2021 12:21, Roger Pau Monné wrote:
> On Thu, Apr 01, 2021 at 11:46:44AM +0200, Jan Beulich wrote:
>> @@ -189,7 +208,11 @@ EFI_LDFLAGS += --no-insert-timestamp
>>  endif
>>  
>>  $(TARGET).efi: VIRT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A VIRT_START$$,,p')
>> +ifeq ($(MKRELOC),:)
>> +$(TARGET).efi: ALT_BASE :=
>> +else
>>  $(TARGET).efi: ALT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A ALT_START$$,,p')
> 
> Could you maybe check whether $(relocs-dummy) is set as the condition
> here and use it here instead of efi/relocs-dummy.o?

I can use it in the ifeq() if you think that's neater (the current way
is minimally shorter), but using it in the ALT_BASE assignment would
make this differ more from the VIRT_BASE one, which I'd like to avoid.

>> @@ -210,16 +233,16 @@ note_file_option ?= $(note_file)
>>  ifeq ($(XEN_BUILD_PE),y)
>>  $(TARGET).efi: prelink.o $(note_file) efi.lds efi/relocs-dummy.o efi/mkreloc
> 
> Do you need to also replace the target prerequisite to use $(relocs-dummy)?

No - without the dependency the file might not be generated (if this ends
up being the only real dependency on $(BASEDIR)/arch/x86/efi/built_in.o).
We can't rely on $(note_file) resolving to efi/buildid.o, and hence
recursing into $(BASEDIR)/arch/x86/efi/ may not otherwise get triggered.
Yet to calculate VIRT_BASE we need efi/relocs-dummy.o.

>> --- a/xen/arch/x86/efi/efi-boot.h
>> +++ b/xen/arch/x86/efi/efi-boot.h
>> @@ -86,10 +86,12 @@ static void __init efi_arch_relocate_ima
>>                  }
>>                  break;
>>              case PE_BASE_RELOC_DIR64:
>> -                if ( in_page_tables(addr) )
>> -                    blexit(L"Unexpected relocation type");
>>                  if ( delta )
>> +                {
>>                      *(u64 *)addr += delta;
>> +                    if ( in_page_tables(addr) )
>> +                        *(u64 *)addr += xen_phys_start;
> 
> Doesn't the in_page_tables check and modification also apply when
> delta == 0?

No, it would be wrong to do so: efi_arch_load_addr_check() sets
xen_phys_start, and subsequently (to still be able to produce human
visible output) we invoke efi_arch_relocate_image() with an argument
of 0. Later we'll invoke efi_arch_relocate_image() a 2nd time (when
having exited boot services already, and hence when we can't produce
output via EFI anymore, and we can't produce output yet via Xen's
normal mechanisms), with a non-zero argument. Thus we'd add in
xen_phys_start twice.

> Maybe you could just break on !delta to reduce indentation if none of
> this applies then?

Could be done, sure, and if you think this makes sufficiently much
of a difference I can add a patch. The purpose here though it to
have this and the preceding case block look as similar as possible,
yet also not re-format that earlier one (which would be an unrelated
change).

Jan
Roger Pau Monne April 21, 2021, 3:20 p.m. UTC | #3
On Wed, Apr 21, 2021 at 02:03:49PM +0200, Jan Beulich wrote:
> On 21.04.2021 12:21, Roger Pau Monné wrote:
> > On Thu, Apr 01, 2021 at 11:46:44AM +0200, Jan Beulich wrote:
> >> @@ -189,7 +208,11 @@ EFI_LDFLAGS += --no-insert-timestamp
> >>  endif
> >>  
> >>  $(TARGET).efi: VIRT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A VIRT_START$$,,p')
> >> +ifeq ($(MKRELOC),:)
> >> +$(TARGET).efi: ALT_BASE :=
> >> +else
> >>  $(TARGET).efi: ALT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A ALT_START$$,,p')
> > 
> > Could you maybe check whether $(relocs-dummy) is set as the condition
> > here and use it here instead of efi/relocs-dummy.o?
> 
> I can use it in the ifeq() if you think that's neater (the current way
> is minimally shorter), but using it in the ALT_BASE assignment would
> make this differ more from the VIRT_BASE one, which I'd like to avoid.

Sorry, I think I'm slightly confused because when the linker can
produce the required relocations relocs-dummy variable is left empty,
so it won't be added to $(TARGET).efi.

But we still need to generate the efi/relocs-dummy.o file for the ELF
build I assume?

Thanks, Roger.
Jan Beulich April 21, 2021, 3:34 p.m. UTC | #4
On 21.04.2021 17:20, Roger Pau Monné wrote:
> On Wed, Apr 21, 2021 at 02:03:49PM +0200, Jan Beulich wrote:
>> On 21.04.2021 12:21, Roger Pau Monné wrote:
>>> On Thu, Apr 01, 2021 at 11:46:44AM +0200, Jan Beulich wrote:
>>>> @@ -189,7 +208,11 @@ EFI_LDFLAGS += --no-insert-timestamp
>>>>  endif
>>>>  
>>>>  $(TARGET).efi: VIRT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A VIRT_START$$,,p')
>>>> +ifeq ($(MKRELOC),:)
>>>> +$(TARGET).efi: ALT_BASE :=
>>>> +else
>>>>  $(TARGET).efi: ALT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A ALT_START$$,,p')
>>>
>>> Could you maybe check whether $(relocs-dummy) is set as the condition
>>> here and use it here instead of efi/relocs-dummy.o?
>>
>> I can use it in the ifeq() if you think that's neater (the current way
>> is minimally shorter), but using it in the ALT_BASE assignment would
>> make this differ more from the VIRT_BASE one, which I'd like to avoid.
> 
> Sorry, I think I'm slightly confused because when the linker can
> produce the required relocations relocs-dummy variable is left empty,
> so it won't be added to $(TARGET).efi.
> 
> But we still need to generate the efi/relocs-dummy.o file for the ELF
> build I assume?

Not anymore (after "x86/EFI: redo .reloc section bounds determination").
We need it, as said, to fish VIRT_BASE out of it. It indeed wouldn't get
linked into any of the final binaries anymore.

Jan
Roger Pau Monne April 22, 2021, 7:22 a.m. UTC | #5
On Wed, Apr 21, 2021 at 05:34:29PM +0200, Jan Beulich wrote:
> On 21.04.2021 17:20, Roger Pau Monné wrote:
> > On Wed, Apr 21, 2021 at 02:03:49PM +0200, Jan Beulich wrote:
> >> On 21.04.2021 12:21, Roger Pau Monné wrote:
> >>> On Thu, Apr 01, 2021 at 11:46:44AM +0200, Jan Beulich wrote:
> >>>> @@ -189,7 +208,11 @@ EFI_LDFLAGS += --no-insert-timestamp
> >>>>  endif
> >>>>  
> >>>>  $(TARGET).efi: VIRT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A VIRT_START$$,,p')
> >>>> +ifeq ($(MKRELOC),:)
> >>>> +$(TARGET).efi: ALT_BASE :=
> >>>> +else
> >>>>  $(TARGET).efi: ALT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A ALT_START$$,,p')
> >>>
> >>> Could you maybe check whether $(relocs-dummy) is set as the condition
> >>> here and use it here instead of efi/relocs-dummy.o?
> >>
> >> I can use it in the ifeq() if you think that's neater (the current way
> >> is minimally shorter), but using it in the ALT_BASE assignment would
> >> make this differ more from the VIRT_BASE one, which I'd like to avoid.
> > 
> > Sorry, I think I'm slightly confused because when the linker can
> > produce the required relocations relocs-dummy variable is left empty,
> > so it won't be added to $(TARGET).efi.
> > 
> > But we still need to generate the efi/relocs-dummy.o file for the ELF
> > build I assume?
> 
> Not anymore (after "x86/EFI: redo .reloc section bounds determination").
> We need it, as said, to fish VIRT_BASE out of it. It indeed wouldn't get
> linked into any of the final binaries anymore.

Could you please add some text to the commit message to note that
while relocs-dummy is not linked into the final binary it's still
needed to get VIRT_BASE?

With that:

Acked-by: Roger Pau Monné <roger.pau@citrix.com>

FWIW, it would also be nice to add some comments to the $(TARGET).efi
target commands, as it's quite impenetrable right now.

Thanks, Roger.
Jan Beulich April 22, 2021, 10:42 a.m. UTC | #6
On 22.04.2021 09:22, Roger Pau Monné wrote:
> On Wed, Apr 21, 2021 at 05:34:29PM +0200, Jan Beulich wrote:
>> On 21.04.2021 17:20, Roger Pau Monné wrote:
>>> On Wed, Apr 21, 2021 at 02:03:49PM +0200, Jan Beulich wrote:
>>>> On 21.04.2021 12:21, Roger Pau Monné wrote:
>>>>> On Thu, Apr 01, 2021 at 11:46:44AM +0200, Jan Beulich wrote:
>>>>>> @@ -189,7 +208,11 @@ EFI_LDFLAGS += --no-insert-timestamp
>>>>>>  endif
>>>>>>  
>>>>>>  $(TARGET).efi: VIRT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A VIRT_START$$,,p')
>>>>>> +ifeq ($(MKRELOC),:)
>>>>>> +$(TARGET).efi: ALT_BASE :=
>>>>>> +else
>>>>>>  $(TARGET).efi: ALT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A ALT_START$$,,p')
>>>>>
>>>>> Could you maybe check whether $(relocs-dummy) is set as the condition
>>>>> here and use it here instead of efi/relocs-dummy.o?
>>>>
>>>> I can use it in the ifeq() if you think that's neater (the current way
>>>> is minimally shorter), but using it in the ALT_BASE assignment would
>>>> make this differ more from the VIRT_BASE one, which I'd like to avoid.
>>>
>>> Sorry, I think I'm slightly confused because when the linker can
>>> produce the required relocations relocs-dummy variable is left empty,
>>> so it won't be added to $(TARGET).efi.
>>>
>>> But we still need to generate the efi/relocs-dummy.o file for the ELF
>>> build I assume?
>>
>> Not anymore (after "x86/EFI: redo .reloc section bounds determination").
>> We need it, as said, to fish VIRT_BASE out of it. It indeed wouldn't get
>> linked into any of the final binaries anymore.
> 
> Could you please add some text to the commit message to note that
> while relocs-dummy is not linked into the final binary it's still
> needed to get VIRT_BASE?

I've added

"Note that in the case that we leave base relocation generation to ld,
 while efi/relocs-dummy.o then won't be linked into any executable
 anymore, it still needs generating (and hence dependencies need to be
 kept as they are) in order to have VIRT_BASE pulled out of it."

> With that:
> 
> Acked-by: Roger Pau Monné <roger.pau@citrix.com>

Thanks.

> FWIW, it would also be nice to add some comments to the $(TARGET).efi
> target commands, as it's quite impenetrable right now.

Well, my preferred plan was to pull it (and $(TARGET)-sym's) apart
into separate steps, suitably linked together via dependencies. This
may not eliminate altogether the need for some comments, but we then
may get away with brief ones.

Jan
diff mbox series

Patch

--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -120,18 +120,37 @@  $(TARGET): $(TARGET)-syms $(efi-y) boot/
 	mv $(TMP) $(TARGET)
 
 ifneq ($(efi-y),)
+
 # Check if the compiler supports the MS ABI.
 export XEN_BUILD_EFI := $(shell $(CC) $(XEN_CFLAGS) -c efi/check.c -o efi/check.o 2>/dev/null && echo y)
+CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI
+
 # Check if the linker supports PE.
 EFI_LDFLAGS = $(patsubst -m%,-mi386pep,$(XEN_LDFLAGS)) --subsystem=10 --strip-debug
 XEN_BUILD_PE := $(if $(XEN_BUILD_EFI),$(shell $(LD) $(EFI_LDFLAGS) -o efi/check.efi efi/check.o 2>/dev/null && echo y))
-CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI
-# Check if the linker produces fixups in PE by default (we need to disable it doing so for now).
-XEN_NO_PE_FIXUPS := $(if $(XEN_BUILD_EFI), \
-                         $(shell $(LD) $(EFI_LDFLAGS) --disable-reloc-section -o efi/check.efi efi/check.o 2>/dev/null && \
-                                 echo --disable-reloc-section))
+
+ifeq ($(XEN_BUILD_PE),y)
+
+# Check if the linker produces fixups in PE by default
+nr-fixups := $(shell $(OBJDUMP) -p efi/check.efi | grep '^[[:blank:]]*reloc[[:blank:]]*[0-9][[:blank:]].*DIR64$$' | wc -l)
+ifeq ($(nr-fixups),2)
+MKRELOC := :
+relocs-dummy :=
+else
+MKRELOC := efi/mkreloc
+relocs-dummy := efi/relocs-dummy.o
+# If the linker produced fixups but not precisely two of them, we need to
+# disable it doing so.  But if it didn't produce any fixups, it also wouldn't
+# recognize the option.
+ifneq ($(nr-fixups),0)
+EFI_LDFLAGS += --disable-reloc-section
+endif
 endif
 
+endif # $(XEN_BUILD_PE)
+
+endif # $(efi-y)
+
 ALL_OBJS := $(BASEDIR)/arch/x86/boot/built_in.o $(BASEDIR)/arch/x86/efi/built_in.o $(ALL_OBJS)
 
 ifeq ($(CONFIG_LTO),y)
@@ -175,7 +194,7 @@  note.o: $(TARGET)-syms
 		--rename-section=.data=.note.gnu.build-id -S $@.bin $@
 	rm -f $@.bin
 
-EFI_LDFLAGS += --image-base=$(1) --stack=0,0 --heap=0,0 $(XEN_NO_PE_FIXUPS)
+EFI_LDFLAGS += --image-base=$(1) --stack=0,0 --heap=0,0
 EFI_LDFLAGS += --section-alignment=0x200000 --file-alignment=0x20
 EFI_LDFLAGS += --major-image-version=$(XEN_VERSION)
 EFI_LDFLAGS += --minor-image-version=$(XEN_SUBVERSION)
@@ -189,7 +208,11 @@  EFI_LDFLAGS += --no-insert-timestamp
 endif
 
 $(TARGET).efi: VIRT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A VIRT_START$$,,p')
+ifeq ($(MKRELOC),:)
+$(TARGET).efi: ALT_BASE :=
+else
 $(TARGET).efi: ALT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A ALT_START$$,,p')
+endif
 
 ifneq ($(build_id_linker),)
 ifeq ($(call ld-ver-build-id,$(LD) $(filter -m%,$(EFI_LDFLAGS))),y)
@@ -210,16 +233,16 @@  note_file_option ?= $(note_file)
 ifeq ($(XEN_BUILD_PE),y)
 $(TARGET).efi: prelink.o $(note_file) efi.lds efi/relocs-dummy.o efi/mkreloc
 	$(foreach base, $(VIRT_BASE) $(ALT_BASE), \
-	          $(LD) $(call EFI_LDFLAGS,$(base)) -T efi.lds -N $< efi/relocs-dummy.o \
+	          $(LD) $(call EFI_LDFLAGS,$(base)) -T efi.lds -N $< $(relocs-dummy) \
 	                $(BASEDIR)/common/symbols-dummy.o $(note_file_option) -o $(@D)/.$(@F).$(base).0 &&) :
-	efi/mkreloc $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).0) >$(@D)/.$(@F).0r.S
+	$(MKRELOC) $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).0) >$(@D)/.$(@F).0r.S
 	$(NM) -pa --format=sysv $(@D)/.$(@F).$(VIRT_BASE).0 \
 		| $(BASEDIR)/tools/symbols $(all_symbols) --sysv --sort >$(@D)/.$(@F).0s.S
 	$(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).0r.o $(@D)/.$(@F).0s.o
 	$(foreach base, $(VIRT_BASE) $(ALT_BASE), \
 	          $(LD) $(call EFI_LDFLAGS,$(base)) -T efi.lds -N $< \
 	                $(@D)/.$(@F).0r.o $(@D)/.$(@F).0s.o $(note_file_option) -o $(@D)/.$(@F).$(base).1 &&) :
-	efi/mkreloc $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).1) >$(@D)/.$(@F).1r.S
+	$(MKRELOC) $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).1) >$(@D)/.$(@F).1r.S
 	$(NM) -pa --format=sysv $(@D)/.$(@F).$(VIRT_BASE).1 \
 		| $(BASEDIR)/tools/symbols $(all_symbols) --sysv --sort >$(@D)/.$(@F).1s.S
 	$(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).1r.o $(@D)/.$(@F).1s.o
--- a/xen/arch/x86/efi/check.c
+++ b/xen/arch/x86/efi/check.c
@@ -2,3 +2,17 @@  int __attribute__((__ms_abi__)) test(int
 {
     return i;
 }
+
+/*
+ * Populate an array with "addresses" of relocatable and absolute values.
+ * This is to probe ld for (a) emitting base relocations at all and (b) not
+ * emitting base relocations for absolute symbols.
+ */
+extern const unsigned char __image_base__[], __file_alignment__[],
+                           __section_alignment__[];
+const void *const data[] = {
+    __image_base__,
+    __file_alignment__,
+    __section_alignment__,
+    data,
+};
--- a/xen/arch/x86/efi/efi-boot.h
+++ b/xen/arch/x86/efi/efi-boot.h
@@ -86,10 +86,12 @@  static void __init efi_arch_relocate_ima
                 }
                 break;
             case PE_BASE_RELOC_DIR64:
-                if ( in_page_tables(addr) )
-                    blexit(L"Unexpected relocation type");
                 if ( delta )
+                {
                     *(u64 *)addr += delta;
+                    if ( in_page_tables(addr) )
+                        *(u64 *)addr += xen_phys_start;
+                }
                 break;
             default:
                 blexit(L"Unsupported relocation type");