Skip to content

Instantly share code, notes, and snippets.

@eblot
Last active January 22, 2019 10:08
Show Gist options
  • Save eblot/43552f8c01cc7d2ee4faef42454c2c83 to your computer and use it in GitHub Desktop.
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+
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