aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Wennborg <hans@hanshq.net>2019-02-05 11:19:42 +0000
committerHans Wennborg <hans@hanshq.net>2019-02-05 11:19:42 +0000
commit9c110d55f93a7367ab1d949df5aee1239be340e0 (patch)
treef2cd08b3dbbf2797ad4ff9b561ebe44d9d686e85
parentca26c44df0122e65a56661bef56c9a63901eb699 (diff)
downloadllvm-9c110d55f93a7367ab1d949df5aee1239be340e0.zip
llvm-9c110d55f93a7367ab1d949df5aee1239be340e0.tar.gz
llvm-9c110d55f93a7367ab1d949df5aee1239be340e0.tar.bz2
Merging r352929:
------------------------------------------------------------------------ r352929 | mstorsjo | 2019-02-01 23:08:09 +0100 (Fri, 01 Feb 2019) | 11 lines [COFF] Create range extension thunks for ARM64 On ARM64, this is normally necessary only after a module exceeds 128 MB in size (while the limit for thumb is 16 MB). For conditional branches, the range limit is only 1 MB though (the same as for thumb), and for the tbz instruction, the range is only 32 KB, which allows for a test much smaller than the full 128 MB. This fixes PR40467. Differential Revision: https://reviews.llvm.org/D57575 ------------------------------------------------------------------------ llvm-svn: 353158
-rw-r--r--lld/COFF/Chunks.cpp24
-rw-r--r--lld/COFF/Chunks.h13
-rw-r--r--lld/COFF/Writer.cpp49
-rw-r--r--lld/test/COFF/arm64-branch-range.test16
-rw-r--r--lld/test/COFF/arm64-thunks.s27
5 files changed, 97 insertions, 32 deletions
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index 29131d7..2bb9aa0 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -669,18 +669,38 @@ const uint8_t ArmThunk[] = {
0xe7, 0x44, // L1: add pc, ip
};
-size_t RangeExtensionThunk::getSize() const {
+size_t RangeExtensionThunkARM::getSize() const {
assert(Config->Machine == ARMNT);
return sizeof(ArmThunk);
}
-void RangeExtensionThunk::writeTo(uint8_t *Buf) const {
+void RangeExtensionThunkARM::writeTo(uint8_t *Buf) const {
assert(Config->Machine == ARMNT);
uint64_t Offset = Target->getRVA() - RVA - 12;
memcpy(Buf + OutputSectionOff, ArmThunk, sizeof(ArmThunk));
applyMOV32T(Buf + OutputSectionOff, uint32_t(Offset));
}
+// A position independent ARM64 adrp+add thunk, with a maximum range of
+// +/- 4 GB, which is enough for any PE-COFF.
+const uint8_t Arm64Thunk[] = {
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Dest
+ 0x10, 0x02, 0x00, 0x91, // add x16, x16, :lo12:Dest
+ 0x00, 0x02, 0x1f, 0xd6, // br x16
+};
+
+size_t RangeExtensionThunkARM64::getSize() const {
+ assert(Config->Machine == ARM64);
+ return sizeof(Arm64Thunk);
+}
+
+void RangeExtensionThunkARM64::writeTo(uint8_t *Buf) const {
+ assert(Config->Machine == ARM64);
+ memcpy(Buf + OutputSectionOff, Arm64Thunk, sizeof(Arm64Thunk));
+ applyArm64Addr(Buf + OutputSectionOff + 0, Target->getRVA(), RVA, 12);
+ applyArm64Imm(Buf + OutputSectionOff + 4, Target->getRVA() & 0xfff, 0);
+}
+
void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
Res->emplace_back(getRVA());
}
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index f8a0ddd..e132fdf 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -355,9 +355,18 @@ private:
Defined *ImpSymbol;
};
-class RangeExtensionThunk : public Chunk {
+class RangeExtensionThunkARM : public Chunk {
public:
- explicit RangeExtensionThunk(Defined *T) : Target(T) {}
+ explicit RangeExtensionThunkARM(Defined *T) : Target(T) {}
+ size_t getSize() const override;
+ void writeTo(uint8_t *Buf) const override;
+
+ Defined *Target;
+};
+
+class RangeExtensionThunkARM64 : public Chunk {
+public:
+ explicit RangeExtensionThunkARM64(Defined *T) : Target(T) {}
size_t getSize() const override;
void writeTo(uint8_t *Buf) const override;
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 71c24f6..6acfaf9 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -306,16 +306,31 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
// Check whether the target address S is in range from a relocation
// of type RelType at address P.
static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) {
- assert(Config->Machine == ARMNT);
- int64_t Diff = AbsoluteDifference(S, P + 4) + Margin;
- switch (RelType) {
- case IMAGE_REL_ARM_BRANCH20T:
- return isInt<21>(Diff);
- case IMAGE_REL_ARM_BRANCH24T:
- case IMAGE_REL_ARM_BLX23T:
- return isInt<25>(Diff);
- default:
- return true;
+ if (Config->Machine == ARMNT) {
+ int64_t Diff = AbsoluteDifference(S, P + 4) + Margin;
+ switch (RelType) {
+ case IMAGE_REL_ARM_BRANCH20T:
+ return isInt<21>(Diff);
+ case IMAGE_REL_ARM_BRANCH24T:
+ case IMAGE_REL_ARM_BLX23T:
+ return isInt<25>(Diff);
+ default:
+ return true;
+ }
+ } else if (Config->Machine == ARM64) {
+ int64_t Diff = AbsoluteDifference(S, P) + Margin;
+ switch (RelType) {
+ case IMAGE_REL_ARM64_BRANCH26:
+ return isInt<28>(Diff);
+ case IMAGE_REL_ARM64_BRANCH19:
+ return isInt<21>(Diff);
+ case IMAGE_REL_ARM64_BRANCH14:
+ return isInt<16>(Diff);
+ default:
+ return true;
+ }
+ } else {
+ llvm_unreachable("Unexpected architecture");
}
}
@@ -327,7 +342,17 @@ getThunk(DenseMap<uint64_t, Defined *> &LastThunks, Defined *Target, uint64_t P,
Defined *&LastThunk = LastThunks[Target->getRVA()];
if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin))
return {LastThunk, false};
- RangeExtensionThunk *C = make<RangeExtensionThunk>(Target);
+ Chunk *C;
+ switch (Config->Machine) {
+ case ARMNT:
+ C = make<RangeExtensionThunkARM>(Target);
+ break;
+ case ARM64:
+ C = make<RangeExtensionThunkARM64>(Target);
+ break;
+ default:
+ llvm_unreachable("Unexpected architecture");
+ }
Defined *D = make<DefinedSynthetic>("", C);
LastThunk = D;
return {D, true};
@@ -429,7 +454,7 @@ static bool verifyRanges(const std::vector<Chunk *> Chunks) {
// Assign addresses and add thunks if necessary.
void Writer::finalizeAddresses() {
assignAddresses();
- if (Config->Machine != ARMNT)
+ if (Config->Machine != ARMNT && Config->Machine != ARM64)
return;
size_t OrigNumChunks = 0;
diff --git a/lld/test/COFF/arm64-branch-range.test b/lld/test/COFF/arm64-branch-range.test
deleted file mode 100644
index 0b581e9..0000000
--- a/lld/test/COFF/arm64-branch-range.test
+++ /dev/null
@@ -1,16 +0,0 @@
-// REQUIRES: aarch64
-
-// RUN: echo -e '.globl _start\n _start:\n bl too_far26\n' > %t.main26.s
-// RUN: echo -e '.globl _start\n _start:\n b.ne too_far19\n' > %t.main19.s
-// RUN: echo -e '.globl _start\n _start:\n tbz x0, #0, too_far14\n' > %t.main14.s
-
-// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %t.main26.s -o %t.main26.obj
-// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %t.main19.s -o %t.main19.obj
-// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %t.main14.s -o %t.main14.obj
-// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/far-arm64-abs.s -o %t.far.obj
-
-// RUN: not lld-link -base:0x10000 -entry:_start -subsystem:console %t.main26.obj %t.far.obj -out:%t.exe 2>&1 | FileCheck %s
-// RUN: not lld-link -base:0x10000 -entry:_start -subsystem:console %t.main19.obj %t.far.obj -out:%t.exe 2>&1 | FileCheck %s
-// RUN: not lld-link -base:0x10000 -entry:_start -subsystem:console %t.main14.obj %t.far.obj -out:%t.exe 2>&1 | FileCheck %s
-
-// CHECK: relocation out of range
diff --git a/lld/test/COFF/arm64-thunks.s b/lld/test/COFF/arm64-thunks.s
new file mode 100644
index 0000000..4900454
--- /dev/null
+++ b/lld/test/COFF/arm64-thunks.s
@@ -0,0 +1,27 @@
+// REQUIRES: aarch64
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %s -o %t.obj
+// RUN: lld-link -entry:main -subsystem:console %t.obj -out:%t.exe -verbose 2>&1 | FileCheck -check-prefix=VERBOSE %s
+// RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=DISASM %s
+
+// VERBOSE: Added 1 thunks with margin {{.*}} in 1 passes
+
+ .globl main
+ .globl func1
+ .text
+main:
+ tbz w0, #0, func1
+ ret
+ .section .text$a, "xr"
+ .space 0x8000
+ .section .text$b, "xr"
+func1:
+ ret
+
+// DISASM: 0000000140001000 .text:
+// DISASM: 140001000: 40 00 00 36 tbz w0, #0, #8 <.text+0x8>
+// DISASM: 140001004: c0 03 5f d6 ret
+// DISASM: 140001008: 50 00 00 90 adrp x16, #32768
+// DISASM: 14000100c: 10 52 00 91 add x16, x16, #20
+// DISASM: 140001010: 00 02 1f d6 br x16
+
+// DISASM: 140009014: c0 03 5f d6 ret