>From 8d7948641b864168acc67cf0a4834585b3242748 Mon Sep 17 00:00:00 2001
From: Leif Lindholm
Date: Sat, 30 Nov 2013 13:05:34 +0000
Subject: [PATCH] arm64: dl: generate veneers for out-of-range relocations
Signed-off-by: Leif Lindholm
---
grub-core/kern/arm64/dl_helper.c | 45 ++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/grub-core/kern/arm64/dl_helper.c b/grub-core/kern/arm64/dl_helper.c
index ae4bce8..a473661 100644
--- a/grub-core/kern/arm64/dl_helper.c
+++ b/grub-core/kern/arm64/dl_helper.c
@@ -17,6 +17,7 @@
* along with GRUB. If not, see .
*/
+#include
#include
#include
#include
@@ -32,6 +33,46 @@ sign_compress_offset (grub_ssize_t offset, int bitpos)
}
/*
+ * AArch64 relative branch offset range is +-128MB.
+ * Modules are loaded onto the heap, which may be further away from the
+ * kernel than this. The workaround for this is in ARM-terminology called
+ * a "veneer" - a short sequence of a literal load, a branch to register
+ * and immediately following: the literal (64-bit aligned) 64-bit value.
+ */
+#ifndef GRUB_UTIL
+#define VENEER_LOAD 0x58000050 /* LDR x16, [PC + 8] */
+#define VENEER_JUMP 0xd61f0200 /* BR x16 */
+struct veneer {
+ grub_uint32_t load;
+ grub_uint32_t jump;
+ Elf64_Addr target;
+};
+
+static grub_err_t
+add_veneer (grub_uint32_t *place, Elf64_Addr adjust)
+{
+ struct veneer *current;
+ grub_err_t retval;
+
+ current = grub_malloc (sizeof(struct veneer));
+ if (!current)
+ return GRUB_ERR_OUT_OF_MEMORY;
+
+ current->load = VENEER_LOAD;
+ current->jump = VENEER_JUMP;
+ current->target = adjust;
+
+ retval = grub_arm64_reloc_xxxx26 (place, (Elf64_Addr) current);
+ if (retval == GRUB_ERR_NONE)
+ grub_arch_sync_caches (current, sizeof(struct veneer));
+ else
+ grub_free (current);
+
+ return retval;
+}
+#endif
+
+/*
* grub_arm64_reloc_xxxx26():
*
* JUMP26/CALL26 relocations for B and BL instructions.
@@ -54,8 +95,12 @@ grub_arm64_reloc_xxxx26 (grub_uint32_t *place, Elf64_Addr adjust)
if ((offset < offset_low) || (offset > offset_high))
{
+#ifndef GRUB_UTIL
+ return add_veneer (place, adjust);
+#else
return grub_error (GRUB_ERR_BAD_MODULE,
N_("CALL26 Relocation out of range"));
+#endif
}
grub_dprintf ("dl", " reloc_xxxx64 %p %c= 0x%llx\n",
--
1.7.10.4