Last active
January 22, 2019 10:08
-
-
Save eblot/43552f8c01cc7d2ee4faef42454c2c83 to your computer and use it in GitHub Desktop.
Add support for long thunk on ARMv6m, i.e. Cortex-M0 & Cortex-M0+
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp | |
index 693dba6..bd0eb77 100644 | |
--- a/ELF/Driver.cpp | |
+++ b/ELF/Driver.cpp | |
@@ -1485,9 +1485,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) { | |
if (Config->ARMJ1J2BranchEncoding == false) | |
warn("lld uses extended branch encoding, no object with architecture " | |
"supporting feature detected."); | |
- if (Config->ARMHasMovtMovw == false) | |
- warn("lld may use movt/movw, no object with architecture supporting " | |
- "feature detected."); | |
} | |
// This adds a .comment section containing a version string. We have to add it | |
diff --git a/ELF/Thunks.cpp b/ELF/Thunks.cpp | |
index 2cd7e51..a3af01f 100644 | |
--- a/ELF/Thunks.cpp | |
+++ b/ELF/Thunks.cpp | |
@@ -159,6 +159,25 @@ public: | |
void addSymbols(ThunkSection &IS) override; | |
}; | |
+// Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted | |
+class ThumbV6MABSLongThunk final : public ThumbThunk { | |
+public: | |
+ ThumbV6MABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {} | |
+ | |
+ uint32_t sizeLong() override { return 12; } | |
+ void writeLong(uint8_t *Buf) override; | |
+ void addSymbols(ThunkSection &IS) override; | |
+}; | |
+ | |
+class ThumbV6MPILongThunk final : public ThumbThunk { | |
+public: | |
+ ThumbV6MPILongThunk(Symbol &Dest) : ThumbThunk(Dest) {} | |
+ | |
+ uint32_t sizeLong() override { return 16; } | |
+ void writeLong(uint8_t *Buf) override; | |
+ void addSymbols(ThunkSection &IS) override; | |
+}; | |
+ | |
// MIPS LA25 thunk | |
class MipsThunk final : public Thunk { | |
public: | |
@@ -433,6 +452,56 @@ void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) { | |
addSymbol("$t", STT_NOTYPE, 0, IS); | |
} | |
+void ThumbV6MABSLongThunk::writeLong(uint8_t *Buf) { | |
+ // Most Thumb instructions cannot access the high registers r8 - r15. As the | |
+ // only register we can corrupt is r12 we must instead spill a low register | |
+ // to the stack to use as a scratch register. We push r1 even though we | |
+ // don't need to get some space to use for the return address. | |
+ const uint8_t Data[] = { | |
+ 0x03, 0xb4, // push {r0, r1} ; Obtain scratch registers | |
+ 0x01, 0x48, // ldr r0, [pc, #4] ; L1 | |
+ 0x01, 0x90, // str r0, [sp, #4] ; SP + 4 = S | |
+ 0x01, 0xbd, // pop {r0, pc} ; restore r0 and branch to dest | |
+ 0x00, 0x00, 0x00, 0x00 // L1: .word S | |
+ }; | |
+ uint64_t S = getARMThunkDestVA(Destination); | |
+ memcpy(Buf, Data, sizeof(Data)); | |
+ Target->relocateOne(Buf + 8, R_ARM_ABS32, S); | |
+} | |
+ | |
+void ThumbV6MABSLongThunk::addSymbols(ThunkSection &IS) { | |
+ addSymbol(Saver.save("__Thumbv6MABSLongThunk_" + Destination.getName()), | |
+ STT_FUNC, 1, IS); | |
+ addSymbol("$t", STT_NOTYPE, 0, IS); | |
+ addSymbol("$d", STT_NOTYPE, 8, IS); | |
+} | |
+ | |
+void ThumbV6MPILongThunk::writeLong(uint8_t *Buf) { | |
+ // Most Thumb instructions cannot access the high registers r8 - r15. As the | |
+ // only register we can corrupt is ip (r12) we must instead spill a low | |
+ // register to the stack to use as a scratch register. | |
+ const uint8_t Data[] = { | |
+ 0x01, 0xb4, // P: push {r0} ; Obtain scratch register | |
+ 0x02, 0x48, // ldr r0, [pc, #8] ; L2 | |
+ 0x84, 0x46, // mov ip, r0 ; high to low register | |
+ 0x01, 0xbc, // pop {r0} ; restore scratch register | |
+ 0xe7, 0x44, // L1: add pc, ip ; transfer control | |
+ 0xc0, 0x46, // nop ; pad to 4-byte boundary | |
+ 0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 4) | |
+ }; | |
+ uint64_t S = getARMThunkDestVA(Destination); | |
+ uint64_t P = getThunkTargetSym()->getVA() & ~0x1; | |
+ memcpy(Buf, Data, sizeof(Data)); | |
+ Target->relocateOne(Buf + 12, R_ARM_REL32, S - P - 12); | |
+} | |
+ | |
+void ThumbV6MPILongThunk::addSymbols(ThunkSection &IS) { | |
+ addSymbol(Saver.save("__Thumbv6MPILongThunk_" + Destination.getName()), | |
+ STT_FUNC, 1, IS); | |
+ addSymbol("$t", STT_NOTYPE, 0, IS); | |
+ addSymbol("$d", STT_NOTYPE, 12, IS); | |
+} | |
+ | |
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one. | |
void MipsThunk::writeTo(uint8_t *Buf) { | |
uint64_t S = Destination.getVA(); | |
@@ -533,8 +602,30 @@ static Thunk *addThunkAArch64(RelType Type, Symbol &S) { | |
return make<AArch64ABSLongThunk>(S); | |
} | |
+// Create a thunk for Thumb long branch on V6-M. | |
+// Arm Architecture v6-M only supports Thumb instructions. This means | |
+// - MOVT and MOVW instructions cannot be used. | |
+// - Only a limited number of instructions can access registers r8 and above | |
+// - No interworking support is needed (all Thumb). | |
+static Thunk *addThunkV6M(RelType Reloc, Symbol &S) { | |
+ switch (Reloc) { | |
+ case R_ARM_THM_JUMP19: | |
+ case R_ARM_THM_JUMP24: | |
+ case R_ARM_THM_CALL: | |
+ if (Config->Pic) | |
+ return make<ThumbV6MPILongThunk>(S); | |
+ return make<ThumbV6MABSLongThunk>(S); | |
+ } | |
+ fatal("relocation " + toString(Reloc) + " to " + toString(S) + | |
+ " not supported for Armv6-M targets"); | |
+} | |
+ | |
// Creates a thunk for Thumb-ARM interworking. | |
static Thunk *addThunkArm(RelType Reloc, Symbol &S) { | |
+ if (!Config->ARMHasMovtMovw) { | |
+ return addThunkV6M(Reloc, S); | |
+ } | |
+ | |
// ARM relocations need ARM to Thumb interworking Thunks. | |
// Thumb relocations need Thumb to ARM relocations. | |
// Use position independent Thunks if we require position independent code. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment