>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