[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
|
0xe7, 0x44, // L1: add pc, ip
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t RangeExtensionThunk::getSize() const {
|
size_t RangeExtensionThunkARM::getSize() const {
|
||||||
assert(Config->Machine == ARMNT);
|
assert(Config->Machine == ARMNT);
|
||||||
return sizeof(ArmThunk);
|
return sizeof(ArmThunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RangeExtensionThunk::writeTo(uint8_t *Buf) const {
|
void RangeExtensionThunkARM::writeTo(uint8_t *Buf) const {
|
||||||
assert(Config->Machine == ARMNT);
|
assert(Config->Machine == ARMNT);
|
||||||
uint64_t Offset = Target->getRVA() - RVA - 12;
|
uint64_t Offset = Target->getRVA() - RVA - 12;
|
||||||
memcpy(Buf + OutputSectionOff, ArmThunk, sizeof(ArmThunk));
|
memcpy(Buf + OutputSectionOff, ArmThunk, sizeof(ArmThunk));
|
||||||
applyMOV32T(Buf + OutputSectionOff, uint32_t(Offset));
|
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) {
|
void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
|
||||||
Res->emplace_back(getRVA());
|
Res->emplace_back(getRVA());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -357,9 +357,18 @@ private:
|
||||||
Defined *ImpSymbol;
|
Defined *ImpSymbol;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RangeExtensionThunk : public Chunk {
|
class RangeExtensionThunkARM : public Chunk {
|
||||||
public:
|
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;
|
size_t getSize() const override;
|
||||||
void writeTo(uint8_t *Buf) const override;
|
void writeTo(uint8_t *Buf) const override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,7 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
|
||||||
// Check whether the target address S is in range from a relocation
|
// Check whether the target address S is in range from a relocation
|
||||||
// of type RelType at address P.
|
// of type RelType at address P.
|
||||||
static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) {
|
static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) {
|
||||||
assert(Config->Machine == ARMNT);
|
if (Config->Machine == ARMNT) {
|
||||||
int64_t Diff = AbsoluteDifference(S, P + 4) + Margin;
|
int64_t Diff = AbsoluteDifference(S, P + 4) + Margin;
|
||||||
switch (RelType) {
|
switch (RelType) {
|
||||||
case IMAGE_REL_ARM_BRANCH20T:
|
case IMAGE_REL_ARM_BRANCH20T:
|
||||||
|
|
@ -346,6 +346,21 @@ static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) {
|
||||||
default:
|
default:
|
||||||
return true;
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the last thunk for the given target if it is in range,
|
// Return the last thunk for the given target if it is in range,
|
||||||
|
|
@ -356,7 +371,17 @@ getThunk(DenseMap<uint64_t, Defined *> &LastThunks, Defined *Target, uint64_t P,
|
||||||
Defined *&LastThunk = LastThunks[Target->getRVA()];
|
Defined *&LastThunk = LastThunks[Target->getRVA()];
|
||||||
if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin))
|
if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin))
|
||||||
return {LastThunk, false};
|
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);
|
Defined *D = make<DefinedSynthetic>("", C);
|
||||||
LastThunk = D;
|
LastThunk = D;
|
||||||
return {D, true};
|
return {D, true};
|
||||||
|
|
@ -458,7 +483,7 @@ static bool verifyRanges(const std::vector<Chunk *> Chunks) {
|
||||||
// Assign addresses and add thunks if necessary.
|
// Assign addresses and add thunks if necessary.
|
||||||
void Writer::finalizeAddresses() {
|
void Writer::finalizeAddresses() {
|
||||||
assignAddresses();
|
assignAddresses();
|
||||||
if (Config->Machine != ARMNT)
|
if (Config->Machine != ARMNT && Config->Machine != ARM64)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
size_t OrigNumChunks = 0;
|
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