[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: 352929
This commit is contained in:
parent
b2b0cab0c3
commit
c9f4d25f26
|
|
@ -670,18 +670,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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -357,9 +357,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;
|
||||
|
||||
|
|
|
|||
|
|
@ -335,16 +335,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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -356,7 +371,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};
|
||||
|
|
@ -458,7 +483,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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue