[BOLT][AArch64] Handle gold linker veneers

The gold linker veneers are written between functions without symbols,
so we to handle it specially in BOLT.

Vladislav Khmelevsky,
Advanced Software Technology Lab, Huawei

Differential Revision: https://reviews.llvm.org/D129260
This commit is contained in:
Vladislav Khmelevsky 2022-07-07 00:01:33 +03:00
parent f827b4b657
commit 35efe1d806
12 changed files with 324 additions and 101 deletions

View File

@ -39,6 +39,7 @@
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/raw_ostream.h"
#include <functional>
#include <list>
#include <map>
#include <set>
#include <shared_mutex>
@ -641,6 +642,10 @@ public:
/// special linux kernel sections
std::unordered_map<uint64_t, std::vector<LKInstructionMarkerInfo>> LKMarkers;
/// List of external addresses in the code that are not a function start
/// and are referenced from BinaryFunction.
std::list<std::pair<BinaryFunction *, uint64_t>> InterproceduralReferences;
/// PseudoProbe decoder
MCPseudoProbeDecoder ProbeDecoder;
@ -884,8 +889,23 @@ public:
bool registerFragment(BinaryFunction &TargetFunction,
BinaryFunction &Function) const;
/// Resolve inter-procedural dependencies from \p Function.
void processInterproceduralReferences(BinaryFunction &Function);
/// Add unterprocedural reference for \p Function to \p Address
void addInterproceduralReference(BinaryFunction *Function, uint64_t Address) {
InterproceduralReferences.push_back({Function, Address});
}
/// Used to fix the target of linker-generated AArch64 adrp + add
/// sequence with no relocation info.
void addAdrpAddRelocAArch64(BinaryFunction &BF, MCInst &LoadLowBits,
MCInst &LoadHiBits, uint64_t Target);
/// Return true if AARch64 veneer was successfully matched at a given
/// \p Address and register veneer binary function if \p MatchOnly
/// argument is false.
bool handleAArch64Veneer(uint64_t Address, bool MatchOnly = false);
/// Resolve inter-procedural dependencies from
void processInterproceduralReferences();
/// Skip functions with all parent and child fragments transitively.
void skipMarkedFragments();

View File

@ -253,10 +253,6 @@ private:
std::unique_ptr<BinaryLoopInfo> BLI;
/// Set of external addresses in the code that are not a function start
/// and are referenced from this function.
std::set<uint64_t> InterproceduralReferences;
/// All labels in the function that are referenced via relocations from
/// data objects. Typically these are jump table destinations and computed
/// goto labels.

View File

@ -1384,7 +1384,7 @@ public:
llvm_unreachable("not implemented");
}
/// Return true if the instruction CurInst, in combination with the recent
/// Return not 0 if the instruction CurInst, in combination with the recent
/// history of disassembled instructions supplied by [Begin, End), is a linker
/// generated veneer/stub that needs patching. This happens in AArch64 when
/// the code is large and the linker needs to generate stubs, but it does
@ -1394,11 +1394,14 @@ public:
/// is put in TgtLowBits, and its pair in TgtHiBits. If the instruction in
/// TgtHiBits does not have an immediate operand, but an expression, then
/// this expression is put in TgtHiSym and Tgt only contains the lower bits.
virtual bool matchLinkerVeneer(InstructionIterator Begin,
InstructionIterator End, uint64_t Address,
const MCInst &CurInst, MCInst *&TargetHiBits,
MCInst *&TargetLowBits,
uint64_t &Target) const {
/// Return value is a total number of instructions that were used to create
/// a veneer.
virtual uint64_t matchLinkerVeneer(InstructionIterator Begin,
InstructionIterator End, uint64_t Address,
const MCInst &CurInst,
MCInst *&TargetHiBits,
MCInst *&TargetLowBits,
uint64_t &Target) const {
llvm_unreachable("not implemented");
}

View File

@ -415,7 +415,7 @@ BinaryContext::handleAddressRef(uint64_t Address, BinaryFunction &BF,
BF.addEntryPointAtOffset(Address - BF.getAddress()), Addend);
}
} else {
BF.InterproceduralReferences.insert(Address);
addInterproceduralReference(&BF, Address);
}
}
@ -1120,9 +1120,106 @@ bool BinaryContext::registerFragment(BinaryFunction &TargetFunction,
return true;
}
void BinaryContext::processInterproceduralReferences(BinaryFunction &Function) {
for (uint64_t Address : Function.InterproceduralReferences) {
if (!Address)
void BinaryContext::addAdrpAddRelocAArch64(BinaryFunction &BF,
MCInst &LoadLowBits,
MCInst &LoadHiBits,
uint64_t Target) {
const MCSymbol *TargetSymbol;
uint64_t Addend = 0;
std::tie(TargetSymbol, Addend) = handleAddressRef(Target, BF,
/*IsPCRel*/ true);
int64_t Val;
MIB->replaceImmWithSymbolRef(LoadHiBits, TargetSymbol, Addend, Ctx.get(), Val,
ELF::R_AARCH64_ADR_PREL_PG_HI21);
MIB->replaceImmWithSymbolRef(LoadLowBits, TargetSymbol, Addend, Ctx.get(),
Val, ELF::R_AARCH64_ADD_ABS_LO12_NC);
}
bool BinaryContext::handleAArch64Veneer(uint64_t Address, bool MatchOnly) {
BinaryFunction *TargetFunction = getBinaryFunctionContainingAddress(Address);
if (TargetFunction)
return false;
ErrorOr<BinarySection &> Section = getSectionForAddress(Address);
assert(Section && "cannot get section for referenced address");
if (!Section->isText())
return false;
bool Ret = false;
StringRef SectionContents = Section->getContents();
uint64_t Offset = Address - Section->getAddress();
const uint64_t MaxSize = SectionContents.size() - Offset;
const uint8_t *Bytes =
reinterpret_cast<const uint8_t *>(SectionContents.data());
ArrayRef<uint8_t> Data(Bytes + Offset, MaxSize);
auto matchVeneer = [&](BinaryFunction::InstrMapType &Instructions,
MCInst &Instruction, uint64_t Offset,
uint64_t AbsoluteInstrAddr,
uint64_t TotalSize) -> bool {
MCInst *TargetHiBits, *TargetLowBits;
uint64_t TargetAddress, Count;
Count = MIB->matchLinkerVeneer(Instructions.begin(), Instructions.end(),
AbsoluteInstrAddr, Instruction, TargetHiBits,
TargetLowBits, TargetAddress);
if (!Count)
return false;
if (MatchOnly)
return true;
// NOTE The target symbol was created during disassemble's
// handleExternalReference
const MCSymbol *VeneerSymbol = getOrCreateGlobalSymbol(Address, "FUNCat");
BinaryFunction *Veneer = createBinaryFunction(VeneerSymbol->getName().str(),
*Section, Address, TotalSize);
addAdrpAddRelocAArch64(*Veneer, *TargetLowBits, *TargetHiBits,
TargetAddress);
MIB->addAnnotation(Instruction, "AArch64Veneer", true);
Veneer->addInstruction(Offset, std::move(Instruction));
--Count;
for (auto It = std::prev(Instructions.end()); Count != 0;
It = std::prev(It), --Count) {
MIB->addAnnotation(It->second, "AArch64Veneer", true);
Veneer->addInstruction(It->first, std::move(It->second));
}
Veneer->getOrCreateLocalLabel(Address);
Veneer->setMaxSize(TotalSize);
Veneer->updateState(BinaryFunction::State::Disassembled);
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: handling veneer function at 0x" << Address
<< "\n");
return true;
};
uint64_t Size = 0, TotalSize = 0;
BinaryFunction::InstrMapType VeneerInstructions;
for (Offset = 0; Offset < MaxSize; Offset += Size) {
MCInst Instruction;
const uint64_t AbsoluteInstrAddr = Address + Offset;
if (!SymbolicDisAsm->getInstruction(Instruction, Size, Data.slice(Offset),
AbsoluteInstrAddr, nulls()))
break;
TotalSize += Size;
if (MIB->isBranch(Instruction)) {
Ret = matchVeneer(VeneerInstructions, Instruction, Offset,
AbsoluteInstrAddr, TotalSize);
break;
}
VeneerInstructions.emplace(Offset, std::move(Instruction));
}
return Ret;
}
void BinaryContext::processInterproceduralReferences() {
for (const std::pair<BinaryFunction *, uint64_t> &It :
InterproceduralReferences) {
BinaryFunction &Function = *It.first;
uint64_t Address = It.second;
if (!Address || Function.isIgnored())
continue;
BinaryFunction *TargetFunction =
@ -1131,7 +1228,7 @@ void BinaryContext::processInterproceduralReferences(BinaryFunction &Function) {
continue;
if (TargetFunction) {
if (TargetFunction->IsFragment &&
if (TargetFunction->isFragment() &&
!registerFragment(*TargetFunction, Function)) {
errs() << "BOLT-WARNING: interprocedural reference between unrelated "
"fragments: "
@ -1157,6 +1254,10 @@ void BinaryContext::processInterproceduralReferences(BinaryFunction &Function) {
if (SectionName == ".plt" || SectionName == ".plt.got")
continue;
// Check if it is aarch64 veneer written at Address
if (isAArch64() && handleAArch64Veneer(Address))
continue;
if (opts::processAllFunctions()) {
errs() << "BOLT-ERROR: cannot process binaries with unmarked "
<< "object in code at address 0x" << Twine::utohexstr(Address)
@ -1177,7 +1278,7 @@ void BinaryContext::processInterproceduralReferences(BinaryFunction &Function) {
}
}
clearList(Function.InterproceduralReferences);
InterproceduralReferences.clear();
}
void BinaryContext::postProcessSymbolTable() {

View File

@ -1061,27 +1061,12 @@ bool BinaryFunction::disassemble() {
Instruction, Expr, *BC.Ctx, 0)));
};
// Used to fix the target of linker-generated AArch64 stubs with no relocation
// info
auto fixStubTarget = [&](MCInst &LoadLowBits, MCInst &LoadHiBits,
uint64_t Target) {
const MCSymbol *TargetSymbol;
uint64_t Addend = 0;
std::tie(TargetSymbol, Addend) = BC.handleAddressRef(Target, *this, true);
int64_t Val;
MIB->replaceImmWithSymbolRef(LoadHiBits, TargetSymbol, Addend, Ctx.get(),
Val, ELF::R_AARCH64_ADR_PREL_PG_HI21);
MIB->replaceImmWithSymbolRef(LoadLowBits, TargetSymbol, Addend, Ctx.get(),
Val, ELF::R_AARCH64_ADD_ABS_LO12_NC);
};
auto handleExternalReference = [&](MCInst &Instruction, uint64_t Size,
uint64_t Offset, uint64_t TargetAddress,
bool &IsCall) -> MCSymbol * {
const uint64_t AbsoluteInstrAddr = getAddress() + Offset;
MCSymbol *TargetSymbol = nullptr;
InterproceduralReferences.insert(TargetAddress);
BC.addInterproceduralReference(this, TargetAddress);
if (opts::Verbosity >= 2 && !IsCall && Size == 2 && !BC.HasRelocations) {
errs() << "BOLT-WARNING: relaxed tail call detected at 0x"
<< Twine::utohexstr(AbsoluteInstrAddr) << " in function " << *this
@ -1147,7 +1132,7 @@ bool BinaryFunction::disassemble() {
HasFixedIndirectBranch = true;
} else {
MIB->convertJmpToTailCall(Instruction);
InterproceduralReferences.insert(IndirectTarget);
BC.addInterproceduralReference(this, IndirectTarget);
}
break;
}
@ -1164,19 +1149,20 @@ bool BinaryFunction::disassemble() {
auto handleAArch64IndirectCall = [&](MCInst &Instruction, uint64_t Offset) {
const uint64_t AbsoluteInstrAddr = getAddress() + Offset;
MCInst *TargetHiBits, *TargetLowBits;
uint64_t TargetAddress;
if (MIB->matchLinkerVeneer(Instructions.begin(), Instructions.end(),
AbsoluteInstrAddr, Instruction, TargetHiBits,
TargetLowBits, TargetAddress)) {
uint64_t TargetAddress, Count;
Count = MIB->matchLinkerVeneer(Instructions.begin(), Instructions.end(),
AbsoluteInstrAddr, Instruction, TargetHiBits,
TargetLowBits, TargetAddress);
if (Count) {
MIB->addAnnotation(Instruction, "AArch64Veneer", true);
uint8_t Counter = 0;
for (auto It = std::prev(Instructions.end()); Counter != 2;
--It, ++Counter) {
--Count;
for (auto It = std::prev(Instructions.end()); Count != 0;
It = std::prev(It), --Count) {
MIB->addAnnotation(It->second, "AArch64Veneer", true);
}
fixStubTarget(*TargetLowBits, *TargetHiBits, TargetAddress);
BC.addAdrpAddRelocAArch64(*this, *TargetLowBits, *TargetHiBits,
TargetAddress);
}
};
@ -1295,7 +1281,9 @@ bool BinaryFunction::disassemble() {
TargetSymbol = getOrCreateLocalLabel(TargetAddress);
} else {
if (TargetAddress == getAddress() + getSize() &&
TargetAddress < getAddress() + getMaxSize()) {
TargetAddress < getAddress() + getMaxSize() &&
!(BC.isAArch64() &&
BC.handleAArch64Veneer(TargetAddress, /*MatchOnly*/ true))) {
// Result of __builtin_unreachable().
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: jump past end detected at 0x"
<< Twine::utohexstr(AbsoluteInstrAddr)
@ -2842,7 +2830,6 @@ void BinaryFunction::clearDisasmState() {
clearList(Instructions);
clearList(IgnoredBranches);
clearList(TakenBranches);
clearList(InterproceduralReferences);
if (BC.HasRelocations) {
for (std::pair<const uint32_t, MCSymbol *> &LI : Labels)
@ -4423,17 +4410,20 @@ void BinaryFunction::printLoopInfo(raw_ostream &OS) const {
}
bool BinaryFunction::isAArch64Veneer() const {
if (BasicBlocks.size() != 1)
if (empty())
return false;
BinaryBasicBlock &BB = **BasicBlocks.begin();
if (BB.size() != 3)
return false;
for (MCInst &Inst : BB)
if (!BC.MIB->hasAnnotation(Inst, "AArch64Veneer"))
return false;
for (auto I = BasicBlocks.begin() + 1, E = BasicBlocks.end(); I != E; ++I) {
for (MCInst &Inst : **I)
if (!BC.MIB->isNoop(Inst))
return false;
}
return true;
}

View File

@ -1595,6 +1595,9 @@ void InstructionLowering::runOnFunctions(BinaryContext &BC) {
}
void StripRepRet::runOnFunctions(BinaryContext &BC) {
if (!BC.isX86())
return;
uint64_t NumPrefixesRemoved = 0;
uint64_t NumBytesSaved = 0;
for (auto &BFI : BC.getBinaryFunctions()) {

View File

@ -33,44 +33,35 @@ void VeneerElimination::runOnFunctions(BinaryContext &BC) {
if (!opts::EliminateVeneers || !BC.isAArch64())
return;
auto &BFs = BC.getBinaryFunctions();
std::map<uint64_t, BinaryFunction> &BFs = BC.getBinaryFunctions();
std::unordered_map<const MCSymbol *, const MCSymbol *> VeneerDestinations;
uint64_t VeneersCount = 0;
for (auto It = BFs.begin(); It != BFs.end();) {
auto CurrentIt = It;
++It;
for (auto &It : BFs) {
BinaryFunction &VeneerFunction = It.second;
if (!VeneerFunction.isAArch64Veneer())
continue;
if (CurrentIt->second.isAArch64Veneer()) {
VeneersCount++;
BinaryFunction &VeneerFunction = CurrentIt->second;
MCInst &FirstInstruction = *(VeneerFunction.begin()->begin());
const MCSymbol *VeneerTargetSymbol =
BC.MIB->getTargetSymbol(FirstInstruction, 1);
// Functions can have multiple symbols
for (StringRef Name : VeneerFunction.getNames()) {
MCSymbol *Symbol = BC.Ctx->lookupSymbol(Name);
VeneerDestinations[Symbol] = VeneerTargetSymbol;
BC.SymbolToFunctionMap.erase(Symbol);
}
BC.BinaryDataMap.erase(VeneerFunction.getAddress());
BFs.erase(CurrentIt);
}
VeneersCount++;
VeneerFunction.setPseudo(true);
MCInst &FirstInstruction = *(VeneerFunction.begin()->begin());
const MCSymbol *VeneerTargetSymbol =
BC.MIB->getTargetSymbol(FirstInstruction, 1);
assert(VeneerTargetSymbol && "Expecting target symbol for instruction");
for (const MCSymbol *Symbol : VeneerFunction.getSymbols())
VeneerDestinations[Symbol] = VeneerTargetSymbol;
}
LLVM_DEBUG(dbgs() << "BOLT-INFO: number of removed linker-inserted veneers :"
<< VeneersCount << "\n");
outs() << "BOLT-INFO: number of removed linker-inserted veneers: "
<< VeneersCount << "\n";
// Handle veneers to veneers in case they occur
for (auto entry : VeneerDestinations) {
const MCSymbol *src = entry.first;
const MCSymbol *dest = entry.second;
while (VeneerDestinations.find(dest) != VeneerDestinations.end()) {
dest = VeneerDestinations[dest];
}
VeneerDestinations[src] = dest;
for (auto &Entry : VeneerDestinations) {
const MCSymbol *Src = Entry.first;
const MCSymbol *Dest = Entry.second;
while (VeneerDestinations.find(Dest) != VeneerDestinations.end())
Dest = VeneerDestinations[Dest];
VeneerDestinations[Src] = Dest;
}
uint64_t VeneerCallers = 0;
@ -87,14 +78,16 @@ void VeneerElimination::runOnFunctions(BinaryContext &BC) {
VeneerCallers++;
if (!BC.MIB->replaceBranchTarget(
Instr, VeneerDestinations[TargetSymbol], BC.Ctx.get()))
assert(false && "updating veneer call destination failed");
Instr, VeneerDestinations[TargetSymbol], BC.Ctx.get())) {
errs() << "BOLT-ERROR: updating veneer call destination failed\n";
exit(1);
}
}
}
}
LLVM_DEBUG(
dbgs() << "BOLT-INFO: number of linker-inserted veneers call sites :"
dbgs() << "BOLT-INFO: number of linker-inserted veneers call sites: "
<< VeneerCallers << "\n");
}

View File

@ -313,6 +313,10 @@ void BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
Manager.registerPass(std::make_unique<AsmDumpPass>(),
opts::AsmDump.getNumOccurrences());
if (BC.isAArch64())
Manager.registerPass(
std::make_unique<VeneerElimination>(PrintVeneerElimination));
if (opts::Instrument)
Manager.registerPass(std::make_unique<Instrumentation>(NeverPrint));
@ -339,10 +343,6 @@ void BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
Manager.registerPass(std::make_unique<IdenticalCodeFolding>(PrintICF),
opts::ICF);
if (BC.isAArch64())
Manager.registerPass(
std::make_unique<VeneerElimination>(PrintVeneerElimination));
Manager.registerPass(
std::make_unique<SpecializeMemcpy1>(NeverPrint, opts::SpecializeMemcpy1),
!opts::SpecializeMemcpy1.empty());

View File

@ -2891,10 +2891,9 @@ void RewriteInstance::disassembleFunctions() {
if (opts::PrintAll || opts::PrintDisasm)
Function.print(outs(), "after disassembly", true);
BC->processInterproceduralReferences(Function);
}
BC->processInterproceduralReferences();
BC->clearJumpTableOffsets();
BC->populateJumpTables();
BC->skipMarkedFragments();

View File

@ -1032,17 +1032,17 @@ public:
/// ADD x16, x16, imm
/// BR x16
///
bool matchLinkerVeneer(InstructionIterator Begin, InstructionIterator End,
uint64_t Address, const MCInst &CurInst,
MCInst *&TargetHiBits, MCInst *&TargetLowBits,
uint64_t &Target) const override {
uint64_t matchLinkerVeneer(InstructionIterator Begin, InstructionIterator End,
uint64_t Address, const MCInst &CurInst,
MCInst *&TargetHiBits, MCInst *&TargetLowBits,
uint64_t &Target) const override {
if (CurInst.getOpcode() != AArch64::BR || !CurInst.getOperand(0).isReg() ||
CurInst.getOperand(0).getReg() != AArch64::X16)
return false;
return 0;
auto I = End;
if (I == Begin)
return false;
return 0;
--I;
Address -= 4;
@ -1051,7 +1051,7 @@ public:
!I->getOperand(1).isReg() ||
I->getOperand(0).getReg() != AArch64::X16 ||
I->getOperand(1).getReg() != AArch64::X16 || !I->getOperand(2).isImm())
return false;
return 0;
TargetLowBits = &*I;
uint64_t Addr = I->getOperand(2).getImm() & 0xFFF;
@ -1060,12 +1060,12 @@ public:
if (I->getOpcode() != AArch64::ADRP ||
MCPlus::getNumPrimeOperands(*I) < 2 || !I->getOperand(0).isReg() ||
!I->getOperand(1).isImm() || I->getOperand(0).getReg() != AArch64::X16)
return false;
return 0;
TargetHiBits = &*I;
Addr |= (Address + ((int64_t)I->getOperand(1).getImm() << 12)) &
0xFFFFFFFFFFFFF000ULL;
Target = Addr;
return true;
return 3;
}
bool replaceImmWithSymbolRef(MCInst &Inst, const MCSymbol *Symbol,
@ -1107,8 +1107,6 @@ public:
bool isPrefix(const MCInst &Inst) const override { return false; }
bool deleteREPPrefix(MCInst &Inst) const override { return false; }
bool createReturn(MCInst &Inst) const override {
Inst.setOpcode(AArch64::RET);
Inst.clear();

View File

@ -0,0 +1,74 @@
// This test checks that the gold linker style veneer are properly handled
// by BOLT.
// Strip .rela.mytext section to simulate inserted by a linker veneers
// that does not contain relocations.
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \
# RUN: %s -o %t.o
# RUN: %clang %cflags -fPIC -pie %t.o -o %t.exe -nostdlib \
# RUN: -fuse-ld=lld -Wl,--no-relax -Wl,-q
# RUN: llvm-objcopy --remove-section .rela.mytext %t.exe
# RUN: llvm-objdump -d -j .mytext %t.exe | \
# RUN: FileCheck --check-prefix=CHECKVENEER %s
# RUN: llvm-bolt %t.exe -o %t.bolt --elim-link-veneers=true \
# RUN: --lite=0 --use-old-text=0
# RUN: llvm-objdump -d -j .text %t.bolt | FileCheck %s
.text
.balign 4
.global dummy
.type dummy, %function
dummy:
adrp x1, dummy
ret
.size dummy, .-dummy
.section ".mytext", "ax"
.balign 4
.global foo
.type foo, %function
foo:
# CHECK: <foo>:
# CHECK-NEXT : {{.*}} bl {{.*}} <foo2>
bl .L2
ret
.size foo, .-foo
.global foo2
.type foo2, %function
foo2:
# CHECK: <foo2>:
# CHECK-NEXT : {{.*}} bl {{.*}} <foo2>
bl .L2
ret
.size foo2, .-foo2
.global _start
.type _start, %function
_start:
# CHECK: <_start>:
# CHECK-NEXT: {{.*}} bl {{.*}} <foo>
bl .L1
adr x0, .L0
ret
.L0:
.xword 0
.size _start, .-_start
.L1:
# CHECKVENEER: adrp
# CHECKVENEER-NEXT: add
# CHECKVENEER-NEXT: br
# CHECKVENEER-NEXT: nop
adrp x16, foo
add x16, x16, #:lo12:foo
br x16
nop
.L2:
# CHECKVENEER-NEXT: adrp
# CHECKVENEER-NEXT: add
# CHECKVENEER-NEXT: br
# CHECKVENEER-NEXT: nop
adrp x16, foo2
add x16, x16, #:lo12:foo2
br x16
nop

View File

@ -0,0 +1,46 @@
// This test checks that the veneer are properly handled by BOLT.
// Strip .rela.mytext section to simulate inserted by a linker veneers
// that does not contain relocations.
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \
# RUN: %s -o %t.o
# RUN: %clang %cflags -fPIC -pie %t.o -o %t.exe -nostdlib \
# RUN: -fuse-ld=lld -Wl,--no-relax -Wl,-q
# RUN: llvm-objdump -d --disassemble-symbols='myveneer' %t.exe | \
# RUN: FileCheck --check-prefix=CHECKVENEER %s
# RUN: llvm-objcopy --remove-section .rela.mytext %t.exe
# RUN: llvm-bolt %t.exe -o %t.bolt --elim-link-veneers=true --lite=0
# RUN: llvm-objdump -d -j .text --disassemble-symbols='myveneer' %t.bolt | \
# RUN: FileCheck --check-prefix=CHECKOUTVENEER %s
# RUN: llvm-objdump -d --disassemble-symbols='_start' %t.bolt | FileCheck %s
.text
.balign 4
.global foo
.type foo, %function
foo:
adrp x1, foo
ret
.size foo, .-foo
.section ".mytext", "ax"
.balign 4
# CHECKOUTVENEER-NOT: {{.*}} <myveneer>:
.global myveneer
.type myveneer, %function
myveneer:
# CHECKVENEER: adrp
# CHECKVENEER-NEXT: add
adrp x16, foo
add x16, x16, #:lo12:foo
br x16
nop
.size myveneer, .-myveneer
.global _start
.type _start, %function
_start:
# CHECK: {{.*}} bl {{.*}} <foo>
bl myveneer
ret
.size _start, .-_start