diff --git a/arch/arm64/kernel/idreg-override.c b/arch/arm64/kernel/idreg-override.c
index d7fc813ba5913e27..f8ae7f6d0d9b4fd0 100644
--- a/arch/arm64/kernel/idreg-override.c
+++ b/arch/arm64/kernel/idreg-override.c
@@ -6,6 +6,7 @@
  * Author: Marc Zyngier <maz@kernel.org>
  */
 
+#include <linux/build_bug.h>
 #include <linux/ctype.h>
 #include <linux/kernel.h>
 #include <linux/libfdt.h>
@@ -22,18 +23,29 @@
 static u64 __boot_status __initdata;
 
 struct ftr_set_desc {
-	char 				name[FTR_DESC_NAME_LEN];
-	struct arm64_ftr_override	*override;
+	s32		override_offset; 	// must remain first
+	char 		name[FTR_DESC_NAME_LEN];
 	struct {
-		char			name[FTR_DESC_FIELD_LEN];
-		u8			shift;
-		u8			width;
-		bool			(*filter)(u64 val);
-	} 				fields[];
+		char	name[FTR_DESC_FIELD_LEN];
+		u8	shift;
+		u8	width;
+		bool	(*filter)(u64 val);
+	} 		fields[];
 };
 
+static_assert(offsetof(struct ftr_set_desc, override_offset) == 0);
+
 #define FIELD(n, s, f)	{ .name = n, .shift = s, .width = 4, .filter = f }
 
+#define DEFINE_OVERRIDE(__idx, __id, __name, __ovr, ...)		\
+	asmlinkage const struct ftr_set_desc __initconst __id = {	\
+		.name	= __name,					\
+		.fields = { __VA_ARGS__ },				\
+	};								\
+	asm(".globl " #__ovr "; "					\
+	    ".reloc " #__id ", R_AARCH64_PREL32, " #__ovr "; "		\
+	    ".reloc regs + (4 * " #__idx "), R_AARCH64_PREL32, " #__id)
+
 static bool __init mmfr1_vh_filter(u64 val)
 {
 	/*
@@ -46,14 +58,9 @@ static bool __init mmfr1_vh_filter(u64 val)
 		 val == 0);
 }
 
-static const struct ftr_set_desc mmfr1 __initconst = {
-	.name		= "id_aa64mmfr1",
-	.override	= &id_aa64mmfr1_override,
-	.fields		= {
+DEFINE_OVERRIDE(0, mmfr1, "id_aa64mmfr1", id_aa64mmfr1_override,
 		FIELD("vh", ID_AA64MMFR1_EL1_VH_SHIFT, mmfr1_vh_filter),
-		{}
-	},
-};
+		{});
 
 static bool __init pfr0_sve_filter(u64 val)
 {
@@ -70,14 +77,9 @@ static bool __init pfr0_sve_filter(u64 val)
 	return true;
 }
 
-static const struct ftr_set_desc pfr0 __initconst = {
-	.name		= "id_aa64pfr0",
-	.override	= &id_aa64pfr0_override,
-	.fields		= {
+DEFINE_OVERRIDE(1, pfr0, "id_aa64pfr0", id_aa64pfr0_override,
 	        FIELD("sve", ID_AA64PFR0_EL1_SVE_SHIFT, pfr0_sve_filter),
-		{}
-	},
-};
+		{});
 
 static bool __init pfr1_sme_filter(u64 val)
 {
@@ -94,67 +96,46 @@ static bool __init pfr1_sme_filter(u64 val)
 	return true;
 }
 
-static const struct ftr_set_desc pfr1 __initconst = {
-	.name		= "id_aa64pfr1",
-	.override	= &id_aa64pfr1_override,
-	.fields		= {
+DEFINE_OVERRIDE(2, pfr1, "id_aa64pfr1", id_aa64pfr1_override,
 		FIELD("bt", ID_AA64PFR1_EL1_BT_SHIFT, NULL ),
 		FIELD("mte", ID_AA64PFR1_EL1_MTE_SHIFT, NULL),
 		FIELD("sme", ID_AA64PFR1_EL1_SME_SHIFT, pfr1_sme_filter),
-		{}
-	},
-};
+		{});
 
-static const struct ftr_set_desc isar1 __initconst = {
-	.name		= "id_aa64isar1",
-	.override	= &id_aa64isar1_override,
-	.fields		= {
+DEFINE_OVERRIDE(3, isar1, "id_aa64isar1", id_aa64isar1_override,
 		FIELD("gpi", ID_AA64ISAR1_EL1_GPI_SHIFT, NULL),
 		FIELD("gpa", ID_AA64ISAR1_EL1_GPA_SHIFT, NULL),
 		FIELD("api", ID_AA64ISAR1_EL1_API_SHIFT, NULL),
 		FIELD("apa", ID_AA64ISAR1_EL1_APA_SHIFT, NULL),
-		{}
-	},
-};
+		{});
 
-static const struct ftr_set_desc isar2 __initconst = {
-	.name		= "id_aa64isar2",
-	.override	= &id_aa64isar2_override,
-	.fields		= {
+DEFINE_OVERRIDE(4, isar2, "id_aa64isar2", id_aa64isar2_override,
 		FIELD("gpa3", ID_AA64ISAR2_EL1_GPA3_SHIFT, NULL),
 		FIELD("apa3", ID_AA64ISAR2_EL1_APA3_SHIFT, NULL),
-		{}
-	},
-};
+		{});
 
-static const struct ftr_set_desc smfr0 __initconst = {
-	.name		= "id_aa64smfr0",
-	.override	= &id_aa64smfr0_override,
-	.fields		= {
+DEFINE_OVERRIDE(5, smfr0, "id_aa64smfr0", id_aa64smfr0_override,
 		/* FA64 is a one bit field... :-/ */
 		{ "fa64", ID_AA64SMFR0_EL1_FA64_SHIFT, 1, },
-		{}
-	},
-};
+		{});
 
-static const struct ftr_set_desc sw_features __initconst = {
-	.name		= "arm64_sw",
-	.override	= &arm64_sw_feature_override,
-	.fields		= {
+DEFINE_OVERRIDE(6, sw_features, "arm64_sw", arm64_sw_feature_override,
 		FIELD("nokaslr", ARM64_SW_FEATURE_OVERRIDE_NOKASLR, NULL),
-		{}
-	},
-};
+		{});
 
-static const struct ftr_set_desc * const regs[] __initconst = {
-	&mmfr1,
-	&pfr0,
-	&pfr1,
-	&isar1,
-	&isar2,
-	&smfr0,
-	&sw_features,
-};
+/*
+ * regs[] is populated by R_AARCH64_PREL32 directives invisible to the compiler
+ * so it cannot be static or const, or the compiler might try to use constant
+ * propagation on the values.
+ */
+asmlinkage s32 regs[7] __initdata = { [0 ... ARRAY_SIZE(regs) - 1] = S32_MAX };
+
+static struct arm64_ftr_override * __init reg_override(int i)
+{
+	const struct ftr_set_desc *reg = offset_to_ptr(&regs[i]);
+
+	return offset_to_ptr(&reg->override_offset);
+}
 
 static const struct {
 	char	alias[FTR_ALIAS_NAME_LEN];
@@ -193,15 +174,16 @@ static void __init match_options(const char *cmdline)
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(regs); i++) {
+		const struct ftr_set_desc *reg = offset_to_ptr(&regs[i]);
 		int f;
 
-		for (f = 0; strlen(regs[i]->fields[f].name); f++) {
-			u64 shift = regs[i]->fields[f].shift;
-			u64 width = regs[i]->fields[f].width ?: 4;
+		for (f = 0; strlen(reg->fields[f].name); f++) {
+			u64 shift = reg->fields[f].shift;
+			u64 width = reg->fields[f].width ?: 4;
 			u64 mask = GENMASK_ULL(shift + width - 1, shift);
 			u64 v;
 
-			if (find_field(cmdline, regs[i], f, &v))
+			if (find_field(cmdline, reg, f, &v))
 				continue;
 
 			/*
@@ -209,16 +191,16 @@ static void __init match_options(const char *cmdline)
 			 * it by setting the value to the all-ones while
 			 * clearing the mask... Yes, this is fragile.
 			 */
-			if (regs[i]->fields[f].filter &&
-			    !regs[i]->fields[f].filter(v)) {
-				regs[i]->override->val  |= mask;
-				regs[i]->override->mask &= ~mask;
+			if (reg->fields[f].filter &&
+			    !reg->fields[f].filter(v)) {
+				reg_override(i)->val  |= mask;
+				reg_override(i)->mask &= ~mask;
 				continue;
 			}
 
-			regs[i]->override->val  &= ~mask;
-			regs[i]->override->val  |= (v << shift) & mask;
-			regs[i]->override->mask |= mask;
+			reg_override(i)->val  &= ~mask;
+			reg_override(i)->val  |= (v << shift) & mask;
+			reg_override(i)->mask |= mask;
 
 			return;
 		}
@@ -295,8 +277,8 @@ asmlinkage void __init init_feature_override(u64 boot_status)
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(regs); i++) {
-		regs[i]->override->val  = 0;
-		regs[i]->override->mask = 0;
+		reg_override(i)->val  = 0;
+		reg_override(i)->mask = 0;
 	}
 
 	__boot_status = boot_status;
@@ -304,8 +286,8 @@ asmlinkage void __init init_feature_override(u64 boot_status)
 	parse_cmdline();
 
 	for (i = 0; i < ARRAY_SIZE(regs); i++) {
-		dcache_clean_inval_poc((unsigned long)regs[i]->override,
-				       (unsigned long)regs[i]->override +
-				       sizeof(*regs[i]->override));
+		dcache_clean_inval_poc((unsigned long)reg_override(i),
+				       (unsigned long)reg_override(i) +
+				       sizeof(struct arm64_ftr_override));
 	}
 }
