summaryrefslogtreecommitdiff
path: root/rts/linker/elf_plt_arm.c
diff options
context:
space:
mode:
Diffstat (limited to 'rts/linker/elf_plt_arm.c')
-rw-r--r--rts/linker/elf_plt_arm.c183
1 files changed, 183 insertions, 0 deletions
diff --git a/rts/linker/elf_plt_arm.c b/rts/linker/elf_plt_arm.c
new file mode 100644
index 0000000000..4ef50c6ceb
--- /dev/null
+++ b/rts/linker/elf_plt_arm.c
@@ -0,0 +1,183 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "elf_compat.h"
+#include "ghcplatform.h"
+#if defined(arm_HOST_ARCH)
+
+#include "Elf.h"
+#include "elf_plt.h"
+
+#if defined(OBJFORMAT_ELF)
+
+/* three 4 byte instructions */
+const size_t stubSizeArm = 12;
+
+/*
+ * Compute the number of stub (PLT entries) for a given section by iterating
+ * over the relocations and relocations with explicit addend and counting those
+ * relocations that might require a PLT relocation.
+ *
+ * This will be an upper bound, and we might not use all stubs. However by
+ * calculating the number of potential stubs beforehand, we can allocate enough
+ * space adjacent to the section, such that the PLT is rather close to the
+ * section, and the risk of the stubs being out of reach for the instruction to
+ * be relocated is minimal.
+ */
+bool needStubForRelArm(Elf_Rel * rel) {
+ switch(ELF32_R_TYPE(rel->r_info)) {
+ case COMPAT_R_ARM_PC24:
+ case COMPAT_R_ARM_CALL:
+ case COMPAT_R_ARM_JUMP24:
+ case COMPAT_R_ARM_THM_CALL:
+ case COMPAT_R_ARM_THM_JUMP24:
+ case COMPAT_R_ARM_THM_JUMP19:
+ return true;
+ default:
+ return false;
+ }
+}
+bool needStubForRelaArm(Elf_Rela * rela) {
+ switch(ELF32_R_TYPE(rela->r_info)) {
+ case COMPAT_R_ARM_PC24:
+ case COMPAT_R_ARM_CALL:
+ case COMPAT_R_ARM_JUMP24:
+ case COMPAT_R_ARM_THM_CALL:
+ case COMPAT_R_ARM_THM_JUMP24:
+ case COMPAT_R_ARM_THM_JUMP19:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool makeStubArmArm(Stub * s);
+bool makeStubArmThm(Stub * s);
+/*
+ Note [The ARM/Thumb Story]
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Support for the ARM architecture is complicated by the fact that ARM has not
+ one but several instruction encodings. The two relevant ones here are the
+ original ARM encoding and Thumb, a more dense variant of ARM supporting only
+ a subset of the instruction set.
+
+ How the CPU decodes a particular instruction is determined by a mode bit. This
+ mode bit is set on jump instructions, the value being determined by the low
+ bit of the target address: An odd address means the target is a procedure
+ encoded in the Thumb encoding whereas an even address means it's a traditional
+ ARM procedure (the actual address jumped to is even regardless of the encoding
+ bit).
+
+ Interoperation between Thumb- and ARM-encoded object code (known as
+ "interworking") is tricky. If the linker needs to link a call by an ARM object
+ into Thumb code (or vice-versa) it will produce a jump island using stubs.
+ This, however, is incompatible with GHC's tables-next-to-code since pointers
+ fixed-up in this way will point to a bit of generated code, not a info
+ table/Haskell closure like TNTC expects. For this reason, it is critical that
+ GHC emit exclusively ARM or Thumb objects for all Haskell code.
+
+ We still do, however, need to worry about calls to foreign code, hence the
+ need for makeArmSymbolExtra.
+*/
+
+bool
+makeStubArmArm(Stub * s) {
+
+ // We (the linker) may corrupt r12 (ip) according to the "ELF for the ARM
+ // Architecture" reference.
+
+ // movw<c> <Rd>, #<imm16> (Encoding A2) looks like:
+ // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
+ // [ cond ] 0 0 1 1 0 0 0 0 [ imm4 ]
+ //
+ // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ // [ Rd ] [ imm12 ]
+ //
+ // movt<c> <Rd>, #<imm16> (Encoding A1) looks like:
+ // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
+ // [ cond ] 0 0 1 1 0 1 0 0 [ imm4 ]
+ //
+ // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ // [ Rd ] [ imm12 ]
+ //
+ // bx<c> <Rd> (Encoding A1) looks like:
+ // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
+ // [ cond ] 0 0 0 1 0 0 1 0 1 1 1 1
+ //
+ // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ // 1 1 1 1 1 1 1 1 0 0 0 1 [ Rd ]
+ //
+ // The difference for the movw and movt is only bit 22.
+ // We'll use 0b1110 for the condition.
+
+ uint32_t movw_r12 = 0xe300c000;
+ uint32_t movt_r12 = 0xe340c000;
+ uint32_t bx_r12 = 0xe12fff1c;
+
+ *((uint32_t*)s->addr+0) = movw_r12
+ | (((uint32_t )s->target & 0xf000) << 4)
+ | ((uint32_t )s->target & 0x0fff);
+ *((uint32_t*)s->addr+1) = movt_r12
+ | ((((uint32_t )s->target >> 16) & 0xf000) << 4)
+ | (((uint32_t )s->target >> 16) & 0x0fff);
+ *((uint32_t*)s->addr+2) = bx_r12;
+
+ return EXIT_SUCCESS;
+}
+
+bool
+makeStubArmThm(Stub * s) {
+
+ // movw<c> <Rd>, #<imm16> (Encoding T3) looks like:
+ // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
+ // 1 1 1 1 0 i 1 0 0 1 0 0 [ imm4 ]
+ //
+ // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ // 0 [ imm3 ] [ Rd ] [ imm8 ]
+ //
+ // imm32 = zero_extend(imm4:i:imm3:imm8,32)
+ //
+ // movt<c> <Rd>, #<imm16> (Encoding T1) looks like:
+ // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
+ // 1 1 1 1 0 i 1 0 1 1 0 0 [ imm4 ]
+ //
+ // imm16 = imm4:i:imm3:imm8
+ //
+ // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ // 0 [ imm3 ] [ Rd ] [ imm8 ]
+ //
+ // bx<c> <Rd> (Encoding T1) looks like:
+ // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ // 0 1 0 0 0 1 1 1 0 [ Rd ] 0 0 0
+
+ uint32_t movw_r12 = 0xf2400c00;
+ uint32_t movt_r12 = 0xf2c00c00;
+ uint32_t bx_r12 = 0x47600000;
+
+ *((uint32_t*)s->addr+0) = movw_r12
+ | (((uint32_t )s->target & 0xf000) << 4)
+ | (((uint32_t )s->target & 0x0800) << 16)
+ | (((uint32_t )s->target & 0x0700) << 4)
+ | ((uint32_t )s->target & 0x00ff);
+ *((uint32_t*)s->addr+1) = movt_r12
+ | ((((uint32_t )s->target >> 16) & 0xf000) << 4)
+ | ((((uint32_t )s->target >> 16) & 0x0800) << 16)
+ | ((((uint32_t )s->target >> 16) & 0x0700) << 4)
+ | (((uint32_t )s->target >> 16) & 0x00ff);
+ *((uint32_t*)s->addr+2) = bx_r12;
+
+ return EXIT_SUCCESS;
+}
+
+bool
+makeStubArm(Stub * s) {
+ if((s->flags & 1) == 0)
+ return makeStubArmArm(s);
+ else
+ return makeStubArmThm(s);
+}
+
+#endif // OBJECTFORMAT_ELF
+
+#endif // arm_HOST_ARCH