From c0f529ea679bdf48cbe1c4b3dd92fb552456a1fe Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Tue, 3 Feb 2015 21:16:36 +0000 Subject: [PATCH] arm: implement additional relocations generated by gcc 4.9 at -O3 GCC 4.9 also generates R_ARM_THM_MOVW_ABS_NC and R_ARM_THM_MOVT_ABS, as an alternative to ABS32. Signed-off-by: Leif Lindholm --- grub-core/kern/arm/dl.c | 15 +++++++++++++ grub-core/kern/arm/dl_helper.c | 39 ++++++++++++++++++++++++++++++++++ include/grub/arm/reloc.h | 5 +++++ 3 files changed, 59 insertions(+) diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c index 57cac2e75..5cbd65e46 100644 --- a/grub-core/kern/arm/dl.c +++ b/grub-core/kern/arm/dl.c @@ -205,6 +205,21 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, */ case R_ARM_V4BX: break; + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + { + grub_uint32_t offset; + offset = grub_arm_thm_movw_movt_get_value((grub_uint16_t *) target); + offset += sym_addr; + + if (ELF_R_TYPE (rel->r_info) == R_ARM_THM_MOVT_ABS) + offset >>= 16; + else + offset &= 0xffff; + + grub_arm_thm_movw_movt_set_value((grub_uint16_t *) target, offset); + } + break; case R_ARM_THM_JUMP19: { /* Thumb instructions can be 16-bit aligned */ diff --git a/grub-core/kern/arm/dl_helper.c b/grub-core/kern/arm/dl_helper.c index 5721939c1..8a7263291 100644 --- a/grub-core/kern/arm/dl_helper.c +++ b/grub-core/kern/arm/dl_helper.c @@ -25,6 +25,20 @@ #include #include +static inline grub_uint32_t +thumb_get_instruction_word(grub_uint16_t *target) +{ + /* Extract instruction word in alignment-safe manner */ + return grub_le_to_cpu16 ((*target)) << 16 | grub_le_to_cpu16 (*(target + 1)); +} + +static inline void +thumb_set_instruction_word(grub_uint16_t *target, grub_uint32_t insword) +{ + *target = grub_cpu_to_le16 (insword >> 16); + *(target + 1) = grub_cpu_to_le16 (insword & 0xffff); +} + /* * R_ARM_ABS32 * @@ -214,3 +228,28 @@ grub_arm_jump24_set_offset (grub_uint32_t *target, *target = grub_cpu_to_le32 (insword); } + +grub_uint16_t +grub_arm_thm_movw_movt_get_value (grub_uint16_t *target) +{ + grub_uint32_t insword; + + insword = thumb_get_instruction_word (target); + + return ((insword & 0xf0000) >> 4) | ((insword & 0x04000000) >> 15) | \ + ((insword & 0x7000) >> 4) | (insword & 0xff); +} + +void +grub_arm_thm_movw_movt_set_value (grub_uint16_t *target, grub_uint16_t value) +{ + grub_uint32_t insword; + + insword = thumb_get_instruction_word (target); + insword &= 0xfbf08f00; + + insword |= ((value & 0xf000) << 4) | ((value & 0x0800) << 15) | \ + ((value & 0x0700) << 4) | (value & 0xff); + + thumb_set_instruction_word (target, insword); +} diff --git a/include/grub/arm/reloc.h b/include/grub/arm/reloc.h index b938037a0..ae92e21f1 100644 --- a/include/grub/arm/reloc.h +++ b/include/grub/arm/reloc.h @@ -43,4 +43,9 @@ void grub_arm_jump24_set_offset (grub_uint32_t *target, grub_int32_t offset); +grub_uint16_t +grub_arm_thm_movw_movt_get_value (grub_uint16_t *target); +void +grub_arm_thm_movw_movt_set_value (grub_uint16_t *target, grub_uint16_t value); + #endif