//===- bolt/Passes/VeneerElimination.cpp ----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This class implements a pass that removes linker-inserted veneers from the // code and redirects veneer callers to call to veneers destinations // //===----------------------------------------------------------------------===// #include "bolt/Passes/VeneerElimination.h" #define DEBUG_TYPE "veneer-elim" using namespace llvm; namespace opts { extern cl::OptionCategory BoltOptCategory; static llvm::cl::opt EliminateVeneers("elim-link-veneers", cl::desc("run veneer elimination pass"), cl::init(true), cl::Hidden, cl::cat(BoltOptCategory)); static llvm::cl::opt DropCortexA53843419Veneers( "drop-cortex-a53-843419-veneers", cl::desc("inline and drop Cortex-A53 erratum 843419 linker veneers; only " "use if the BOLTed binary will not run on Cortex-A53"), cl::init(false), cl::cat(BoltOptCategory)); } // namespace opts namespace llvm { namespace bolt { Error VeneerElimination::runOnFunctions(BinaryContext &BC) { if (!opts::EliminateVeneers || !BC.isAArch64()) return Error::success(); std::unordered_map VeneerDestinations; uint64_t NumEliminatedVeneers = 0; uint64_t NumE843419Inlined = 0; for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) { if (!BF.isPossibleVeneer()) continue; if (BF.isIgnored()) continue; // Cortex-A53 erratum 843419 veneers: inline the veneer body at each // branch site instead of redirecting, so LongJmp do not introduce // code that clobbers registers (e.g. x16) used by the caller. if (BC.MIB->matchE843419Veneer(BF)) { if (!opts::DropCortexA53843419Veneers) { BC.errs() << "BOLT-ERROR: binary contains Cortex-A53 erratum 843419 " "workaround veneers; pass " "--drop-cortex-a53-843419-veneers only if the BOLTed " "binary will not run on Cortex-A53, or relink without " "--fix-cortex-a53-843419\n"; exit(1); } const MCInst &VeneerFirstInstr = BF.front().getInstructionAtIndex(0); const MCSymbol *ReturnTargetSym = BC.MIB->getTargetSymbol(BF.front().getInstructionAtIndex(1)); // Check if this branch targets our e843419 veneer. auto BranchTargetsVeneer = [&BF, &BC](const MCSymbol *Target) { if (!Target) return false; if (BC.getFunctionForSymbol(Target) == &BF) return true; if (ErrorOr Addr = BC.getSymbolValue(*Target)) return BC.getBinaryFunctionContainingAddress(*Addr) == &BF; return false; }; uint64_t CallSites = 0; // Find caller from veneer's branch-back target so we can limit the scan. BinaryFunction *CallerBF = nullptr; if (ReturnTargetSym) { if (ErrorOr Addr = BC.getSymbolValue(*ReturnTargetSym)) CallerBF = BC.getBinaryFunctionContainingAddress(*Addr); } auto ScanForBranchesToVeneer = [&](BinaryFunction &F) { for (BinaryBasicBlock &BB : F) { for (auto II = BB.begin(); II != BB.end();) { MCInst &Instr = *II; if (!BC.MIB->isBranch(Instr)) { ++II; continue; } const MCSymbol *Target = BC.MIB->getTargetSymbol(Instr); if (!BranchTargetsVeneer(Target)) { ++II; continue; } InstructionListType Repl; Repl.emplace_back(VeneerFirstInstr); II = BB.replaceInstruction(II, Repl); ++CallSites; ++NumE843419Inlined; } } }; if (CallerBF && !CallerBF->isIgnored()) ScanForBranchesToVeneer(*CallerBF); else LLVM_DEBUG(dbgs() << "BOLT: skipping e843419 veneer inline for " << BF.getOneName() << " (caller not resolved or ignored)\n"); // Only mark pseudo when we actually inlined at least one branch site. if (CallSites > 0) { ++NumEliminatedVeneers; BF.setPseudo(true); LLVM_DEBUG(dbgs() << "BOLT-INFO: inlined e843419 veneer " << BF.getOneName() << " at " << CallSites << " branch sites\n"); } else { LLVM_DEBUG(dbgs() << "BOLT: e843419 veneer " << BF.getOneName() << " left unchanged (no branch sites inlined)\n"); } continue; } MCInst &FirstInstruction = *(BF.begin()->begin()); const MCSymbol *VeneerTargetSymbol = 0; uint64_t TargetAddress; if (BC.MIB->isTailCall(FirstInstruction)) { VeneerTargetSymbol = BC.MIB->getTargetSymbol(FirstInstruction); } else if (BC.MIB->matchAbsLongVeneer(BF, TargetAddress)) { if (BinaryFunction *TargetBF = BC.getBinaryFunctionAtAddress(TargetAddress)) VeneerTargetSymbol = TargetBF->getSymbol(); } else if (BC.MIB->hasAnnotation(FirstInstruction, "AArch64Veneer")) { VeneerTargetSymbol = BC.MIB->getTargetSymbol(FirstInstruction, 1); } if (!VeneerTargetSymbol) continue; for (const MCSymbol *Symbol : BF.getSymbols()) VeneerDestinations[Symbol] = VeneerTargetSymbol; NumEliminatedVeneers++; BF.setPseudo(true); } BC.outs() << "BOLT-INFO: number of removed linker-inserted veneers: " << NumEliminatedVeneers << '\n'; if (NumE843419Inlined) BC.outs() << "BOLT-INFO: e843419 veneer call sites inlined: " << NumE843419Inlined << '\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; } uint64_t VeneerCallers = 0; for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) { for (BinaryBasicBlock &BB : BF) { for (MCInst &Instr : BB) { if (!BC.MIB->isCall(Instr) || BC.MIB->isIndirectCall(Instr)) continue; const MCSymbol *TargetSymbol = BC.MIB->getTargetSymbol(Instr, 0); auto It = VeneerDestinations.find(TargetSymbol); if (It == VeneerDestinations.end()) continue; VeneerCallers++; BC.MIB->replaceBranchTarget(Instr, It->second, BC.Ctx.get()); } } } LLVM_DEBUG( dbgs() << "BOLT-INFO: number of linker-inserted veneers call sites: " << VeneerCallers << "\n"); (void)VeneerCallers; return Error::success(); } } // namespace bolt } // namespace llvm