If you want to execute 32-bit userspace applications, say Y.
 
+config RELOCATABLE
+       bool "Build a relocatable kernel"
+       depends on MMU && 64BIT && !XIP_KERNEL
+       help
+          This builds a kernel as a Position Independent Executable (PIE),
+          which retains all relocation metadata required to relocate the
+          kernel binary at runtime to a different virtual address than the
+          address it was linked at.
+          Since RISCV uses the RELA relocation format, this requires a
+          relocation pass at runtime even if the kernel is loaded at the
+          same address it was linked at.
+
+          If unsure, say N.
+
 endmenu # "Kernel features"
 
 menu "Boot options"
 
 #
 
 OBJCOPYFLAGS    := -O binary
-LDFLAGS_vmlinux :=
+ifeq ($(CONFIG_RELOCATABLE),y)
+       LDFLAGS_vmlinux += -shared -Bsymbolic -z notext -z norelro
+       KBUILD_CFLAGS += -fPIE
+endif
 ifeq ($(CONFIG_DYNAMIC_FTRACE),y)
-       LDFLAGS_vmlinux := --no-relax
+       LDFLAGS_vmlinux += --no-relax
        KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
 ifeq ($(CONFIG_RISCV_ISA_C),y)
        CC_FLAGS_FTRACE := -fpatchable-function-entry=4
 
 #include <linux/dma-map-ops.h>
 #include <linux/crash_dump.h>
 #include <linux/hugetlb.h>
+#ifdef CONFIG_RELOCATABLE
+#include <linux/elf.h>
+#endif
 
 #include <asm/fixmap.h>
 #include <asm/tlbflush.h>
                print_ml("kasan", KASAN_SHADOW_START, KASAN_SHADOW_END);
 #endif
 
-               print_ml("kernel", (unsigned long)KERNEL_LINK_ADDR,
+               print_ml("kernel", (unsigned long)kernel_map.virt_addr,
                         (unsigned long)ADDRESS_SPACE_END);
        }
 }
 #error "setup_vm() is called from head.S before relocate so it should not use absolute addressing."
 #endif
 
+#ifdef CONFIG_RELOCATABLE
+extern unsigned long __rela_dyn_start, __rela_dyn_end;
+
+static void __init relocate_kernel(void)
+{
+       Elf64_Rela *rela = (Elf64_Rela *)&__rela_dyn_start;
+       /*
+        * This holds the offset between the linked virtual address and the
+        * relocated virtual address.
+        */
+       uintptr_t reloc_offset = kernel_map.virt_addr - KERNEL_LINK_ADDR;
+       /*
+        * This holds the offset between kernel linked virtual address and
+        * physical address.
+        */
+       uintptr_t va_kernel_link_pa_offset = KERNEL_LINK_ADDR - kernel_map.phys_addr;
+
+       for ( ; rela < (Elf64_Rela *)&__rela_dyn_end; rela++) {
+               Elf64_Addr addr = (rela->r_offset - va_kernel_link_pa_offset);
+               Elf64_Addr relocated_addr = rela->r_addend;
+
+               if (rela->r_info != R_RISCV_RELATIVE)
+                       continue;
+
+               /*
+                * Make sure to not relocate vdso symbols like rt_sigreturn
+                * which are linked from the address 0 in vmlinux since
+                * vdso symbol addresses are actually used as an offset from
+                * mm->context.vdso in VDSO_OFFSET macro.
+                */
+               if (relocated_addr >= KERNEL_LINK_ADDR)
+                       relocated_addr += reloc_offset;
+
+               *(Elf64_Addr *)addr = relocated_addr;
+       }
+}
+#endif /* CONFIG_RELOCATABLE */
+
 #ifdef CONFIG_XIP_KERNEL
 static void __init create_kernel_page_table(pgd_t *pgdir,
                                            __always_unused bool early)
        BUG_ON((kernel_map.virt_addr + kernel_map.size) > ADDRESS_SPACE_END - SZ_4K);
 #endif
 
+#ifdef CONFIG_RELOCATABLE
+       /*
+        * Early page table uses only one PUD, which makes it possible
+        * to map PUD_SIZE aligned on PUD_SIZE: if the relocation offset
+        * makes the kernel cross over a PUD_SIZE boundary, raise a bug
+        * since a part of the kernel would not get mapped.
+        */
+       BUG_ON(PUD_SIZE - (kernel_map.virt_addr & (PUD_SIZE - 1)) < kernel_map.size);
+       relocate_kernel();
+#endif
+
        apply_early_boot_alternatives();
        pt_ops_set_early();