* Since this function examines addresses much more numerically,
  * it takes the input and output pointers as 'unsigned long'.
  */
-unsigned char *choose_random_location(unsigned long input,
-                                     unsigned long input_size,
-                                     unsigned long output,
-                                     unsigned long output_size)
+void choose_random_location(unsigned long input,
+                           unsigned long input_size,
+                           unsigned long *output,
+                           unsigned long output_size,
+                           unsigned long *virt_addr)
 {
-       unsigned long choice = output;
        unsigned long random_addr;
 
+       /* By default, keep output position unchanged. */
+       *virt_addr = *output;
+
        if (cmdline_find_option_bool("nokaslr")) {
                warn("KASLR disabled: 'nokaslr' on cmdline.");
-               goto out;
+               return;
        }
 
        boot_params->hdr.loadflags |= KASLR_FLAG;
        initialize_identity_maps();
 
        /* Record the various known unsafe memory ranges. */
-       mem_avoid_init(input, input_size, output);
+       mem_avoid_init(input, input_size, *output);
 
        /* Walk e820 and find a random address. */
-       random_addr = find_random_phys_addr(output, output_size);
+       random_addr = find_random_phys_addr(*output, output_size);
        if (!random_addr) {
                warn("KASLR disabled: could not find suitable E820 region!");
-               goto out;
+       } else {
+               /* Update the new physical address location. */
+               if (*output != random_addr) {
+                       add_identity_map(random_addr, output_size);
+                       *output = random_addr;
+               }
        }
 
-       /* Always enforce the minimum. */
-       if (random_addr < choice)
-               goto out;
-
-       choice = random_addr;
-
-       add_identity_map(choice, output_size);
-
        /* This actually loads the identity pagetable on x86_64. */
        finalize_identity_maps();
-out:
-       return (unsigned char *)choice;
+
+       /* Pick random virtual address starting from LOAD_PHYSICAL_ADDR. */
+       if (IS_ENABLED(CONFIG_X86_64))
+               random_addr = find_random_virt_addr(LOAD_PHYSICAL_ADDR, output_size);
+       *virt_addr = random_addr;
 }
 
 }
 
 #if CONFIG_X86_NEED_RELOCS
-static void handle_relocations(void *output, unsigned long output_len)
+static void handle_relocations(void *output, unsigned long output_len,
+                              unsigned long virt_addr)
 {
        int *reloc;
        unsigned long delta, map, ptr;
         * and where it was actually loaded.
         */
        delta = min_addr - LOAD_PHYSICAL_ADDR;
-       if (!delta) {
-               debug_putstr("No relocation needed... ");
-               return;
-       }
-       debug_putstr("Performing relocations... ");
 
        /*
         * The kernel contains a table of relocation addresses. Those
         */
        map = delta - __START_KERNEL_map;
 
+       /*
+        * 32-bit always performs relocations. 64-bit relocations are only
+        * needed if KASLR has chosen a different starting address offset
+        * from __START_KERNEL_map.
+        */
+       if (IS_ENABLED(CONFIG_X86_64))
+               delta = virt_addr - LOAD_PHYSICAL_ADDR;
+
+       if (!delta) {
+               debug_putstr("No relocation needed... ");
+               return;
+       }
+       debug_putstr("Performing relocations... ");
+
        /*
         * Process relocations: 32 bit relocations first then 64 bit after.
         * Three sets of binary relocations are added to the end of the kernel
 #endif
 }
 #else
-static inline void handle_relocations(void *output, unsigned long output_len)
+static inline void handle_relocations(void *output, unsigned long output_len,
+                                     unsigned long virt_addr)
 { }
 #endif
 
                                  unsigned long output_len)
 {
        const unsigned long kernel_total_size = VO__end - VO__text;
-       unsigned char *output_orig = output;
+       unsigned long virt_addr = (unsigned long)output;
 
        /* Retain x86 boot parameters pointer passed from startup_32/64. */
        boot_params = rmode;
         * the entire decompressed kernel plus relocation table, or the
         * entire decompressed kernel plus .bss and .brk sections.
         */
-       output = choose_random_location((unsigned long)input_data, input_len,
-                                       (unsigned long)output,
-                                       max(output_len, kernel_total_size));
+       choose_random_location((unsigned long)input_data, input_len,
+                               (unsigned long *)&output,
+                               max(output_len, kernel_total_size),
+                               &virt_addr);
 
        /* Validate memory location choices. */
        if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
-               error("Destination address inappropriately aligned");
+               error("Destination physical address inappropriately aligned");
+       if (virt_addr & (MIN_KERNEL_ALIGN - 1))
+               error("Destination virtual address inappropriately aligned");
 #ifdef CONFIG_X86_64
        if (heap > 0x3fffffffffffUL)
                error("Destination address too large");
 #endif
 #ifndef CONFIG_RELOCATABLE
        if ((unsigned long)output != LOAD_PHYSICAL_ADDR)
-               error("Wrong destination address");
+               error("Destination address does not match LOAD_PHYSICAL_ADDR");
+       if ((unsigned long)output != virt_addr)
+               error("Destination virtual address changed when not relocatable");
 #endif
 
        debug_putstr("\nDecompressing Linux... ");
        __decompress(input_data, input_len, NULL, NULL, output, output_len,
                        NULL, error);
        parse_elf(output);
-       /*
-        * 32-bit always performs relocations. 64-bit relocations are only
-        * needed if kASLR has chosen a different load address.
-        */
-       if (!IS_ENABLED(CONFIG_X86_64) || output != output_orig)
-               handle_relocations(output, output_len);
+       handle_relocations(output, output_len, virt_addr);
        debug_putstr("done.\nBooting the kernel.\n");
        return output;
 }
 
 
 #if CONFIG_RANDOMIZE_BASE
 /* kaslr.c */
-unsigned char *choose_random_location(unsigned long input_ptr,
-                                     unsigned long input_size,
-                                     unsigned long output_ptr,
-                                     unsigned long output_size);
+void choose_random_location(unsigned long input,
+                           unsigned long input_size,
+                           unsigned long *output,
+                           unsigned long output_size,
+                           unsigned long *virt_addr);
 /* cpuflags.c */
 bool has_cpuflag(int flag);
 #else
-static inline
-unsigned char *choose_random_location(unsigned long input_ptr,
-                                     unsigned long input_size,
-                                     unsigned long output_ptr,
-                                     unsigned long output_size)
+static inline void choose_random_location(unsigned long input,
+                                         unsigned long input_size,
+                                         unsigned long *output,
+                                         unsigned long output_size,
+                                         unsigned long *virt_addr)
 {
-       return (unsigned char *)output_ptr;
+       /* No change from existing output location. */
+       *virt_addr = *output;
 }
 #endif