@@ -1583,13 +1583,18 @@ bool BinaryFunction::scanExternalRefs() {
15831583 assert (FunctionData.size () == getMaxSize () &&
15841584 " function size does not match raw data size" );
15851585
1586- if (BC.isX86 ())
1587- BC.SymbolicDisAsm ->setSymbolizer (
1588- BC.MIB ->createTargetSymbolizer (*this , /* CreateSymbols*/ false ));
1586+ BC.SymbolicDisAsm ->setSymbolizer (
1587+ BC.MIB ->createTargetSymbolizer (*this , /* CreateSymbols*/ false ));
1588+
1589+ // A list of patches for this function.
1590+ using PatchTy = std::pair<uint64_t , MCInst>;
1591+ std::vector<PatchTy> InstructionPatches;
15891592
15901593 // Disassemble contents of the function. Detect code entry points and create
15911594 // relocations for references to code that will be moved.
15921595 uint64_t Size = 0 ; // instruction size
1596+ MCInst Instruction;
1597+ MCInst PrevInstruction;
15931598 for (uint64_t Offset = 0 ; Offset < getSize (); Offset += Size) {
15941599 // Check for data inside code and ignore it
15951600 if (const size_t DataInCodeSize = getSizeOfDataInCodeAt (Offset)) {
@@ -1598,7 +1603,7 @@ bool BinaryFunction::scanExternalRefs() {
15981603 }
15991604
16001605 const uint64_t AbsoluteInstrAddr = getAddress () + Offset;
1601- MCInst Instruction;
1606+ PrevInstruction = Instruction;
16021607 if (!BC.SymbolicDisAsm ->getInstruction (Instruction, Size,
16031608 FunctionData.slice (Offset),
16041609 AbsoluteInstrAddr, nulls ())) {
@@ -1673,12 +1678,108 @@ bool BinaryFunction::scanExternalRefs() {
16731678 if (BranchTargetSymbol) {
16741679 BC.MIB ->replaceBranchTarget (Instruction, BranchTargetSymbol,
16751680 Emitter.LocalCtx .get ());
1676- } else if (!llvm::any_of (Instruction,
1677- [](const MCOperand &Op) { return Op.isExpr (); })) {
1678- // Skip assembly if the instruction may not have any symbolic operands.
1679- continue ;
16801681 } else {
16811682 analyzeInstructionForFuncReference (Instruction);
1683+ const bool NeedsPatch = llvm::any_of (
1684+ MCPlus::primeOperands (Instruction), [&](const MCOperand &Op) {
1685+ return Op.isExpr () &&
1686+ !ignoreReference (BC.MIB ->getTargetSymbol (Op.getExpr ()));
1687+ });
1688+ if (!NeedsPatch)
1689+ continue ;
1690+ }
1691+
1692+ // For AArch64, we need to undo relaxation done by the linker if the target
1693+ // of the instruction is a function that we plan to move.
1694+ //
1695+ // Linker relaxation is documented at:
1696+ // https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
1697+ // under #relocation-optimization.
1698+ if (const Relocation *Rel;
1699+ BC.isAArch64 () && (Rel = getRelocationAt (Offset))) {
1700+ // NOP+ADR sequence can originate from either ADRP+ADD or ADRP+LDR.
1701+ // In either case, we convert it into ADRP+ADD.
1702+ if (BC.MIB ->isADR (Instruction) &&
1703+ (Rel->Type == ELF::R_AARCH64_ADD_ABS_LO12_NC ||
1704+ Rel->Type == ELF::R_AARCH64_LD64_GOT_LO12_NC)) {
1705+ if (!BC.MIB ->isNoop (PrevInstruction)) {
1706+ // In case of unexpected conversion from the linker, skip target
1707+ // optimization.
1708+ const MCSymbol *Symbol = BC.MIB ->getTargetSymbol (Instruction);
1709+ BC.errs () << " BOLT-WARNING: cannot undo linker relaxation for "
1710+ " instruction at 0x"
1711+ << Twine::utohexstr (AbsoluteInstrAddr) << " referencing "
1712+ << Symbol->getName () << ' \n ' ;
1713+ if (BinaryFunction *TargetBF = BC.getFunctionForSymbol (Symbol))
1714+ TargetBF->setIgnored ();
1715+ continue ;
1716+ }
1717+
1718+ InstructionListType AdrpAdd =
1719+ BC.MIB ->undoAdrpAddRelaxation (Instruction, BC.Ctx .get ());
1720+ assert (AdrpAdd.size () == 2 && " Two instructions expected" );
1721+ LLVM_DEBUG ({
1722+ dbgs () << " BOLT-DEBUG: linker relaxation undone for instruction "
1723+ " at 0x"
1724+ << Twine::utohexstr (AbsoluteInstrAddr) << ' \n ' ;
1725+ });
1726+ InstructionPatches.push_back ({AbsoluteInstrAddr - 4 , AdrpAdd[0 ]});
1727+ InstructionPatches.push_back ({AbsoluteInstrAddr, AdrpAdd[1 ]});
1728+ continue ;
1729+ }
1730+
1731+ // If ADR was emitted by the compiler/assembler to reference a nearby
1732+ // local function, we cannot move away that function due to ADR address
1733+ // span limitation. Hence, we skip the optimization.
1734+ if (BC.MIB ->isADR (Instruction) &&
1735+ Rel->Type == ELF::R_AARCH64_ADR_PREL_LO21) {
1736+ BC.errs () << " BOLT-WARNING: unable to convert ADR that references "
1737+ << Rel->Symbol ->getName ()
1738+ << " . Will not optimize the target\n " ;
1739+ if (BinaryFunction *TargetBF = BC.getFunctionForSymbol (Rel->Symbol ))
1740+ TargetBF->setIgnored ();
1741+ continue ;
1742+ }
1743+
1744+ // In the case of GOT load, ADRP+LDR can also be converted into ADRP+ADD.
1745+ // When this happens, it's not always possible to properly symbolize ADRP
1746+ // operand and we might have to adjust the operand based on the next
1747+ // instruction.
1748+ if (BC.MIB ->isAddXri (Instruction) &&
1749+ Rel->Type == ELF::R_AARCH64_LD64_GOT_LO12_NC) {
1750+ if (!BC.MIB ->matchAdrpAddPair (PrevInstruction, Instruction)) {
1751+ BC.errs () << " BOLT-ERROR: cannot find matching ADRP for relaxed LDR "
1752+ " instruction at 0x"
1753+ << Twine::utohexstr (AbsoluteInstrAddr) << ' \n ' ;
1754+ exit (1 );
1755+ }
1756+
1757+ // Check if ADRP was already patched. If not, add a new patch for it.
1758+ if (InstructionPatches.empty () ||
1759+ InstructionPatches.back ().first != AbsoluteInstrAddr - 4 )
1760+ InstructionPatches.push_back (
1761+ {AbsoluteInstrAddr - 4 , PrevInstruction});
1762+
1763+ // Adjust the operand for ADRP from the patch.
1764+ MCInst &ADRPInst = InstructionPatches.back ().second ;
1765+ const MCSymbol *ADRPSymbol = BC.MIB ->getTargetSymbol (ADRPInst);
1766+ const MCSymbol *ADDSymbol = BC.MIB ->getTargetSymbol (Instruction);
1767+ if (ADRPSymbol != ADDSymbol) {
1768+ const int64_t Addend = BC.MIB ->getTargetAddend (Instruction);
1769+ BC.MIB ->setOperandToSymbolRef (ADRPInst, /* OpNum*/ 1 , ADDSymbol,
1770+ Addend, BC.Ctx .get (),
1771+ ELF::R_AARCH64_NONE);
1772+ }
1773+ }
1774+ }
1775+
1776+ // On AArch64, we use instruction patches for fixing references. We make an
1777+ // exception for branch instructions since they require optional
1778+ // relocations.
1779+ if (BC.isAArch64 () && !BranchTargetSymbol) {
1780+ LLVM_DEBUG (BC.printInstruction (dbgs (), Instruction, AbsoluteInstrAddr));
1781+ InstructionPatches.push_back ({AbsoluteInstrAddr, Instruction});
1782+ continue ;
16821783 }
16831784
16841785 // Emit the instruction using temp emitter and generate relocations.
@@ -1720,6 +1821,23 @@ bool BinaryFunction::scanExternalRefs() {
17201821 for (Relocation &Rel : FunctionRelocations)
17211822 getOriginSection ()->addPendingRelocation (Rel);
17221823
1824+ // Add patches grouping them together.
1825+ if (!InstructionPatches.empty ()) {
1826+ uint64_t PatchGroupAddress;
1827+ InstructionListType PatchGroup;
1828+ for (auto PI = InstructionPatches.begin (), PE = InstructionPatches.end ();
1829+ PI != PE; ++PI) {
1830+ auto &Patch = *PI;
1831+ if (PatchGroup.empty ())
1832+ PatchGroupAddress = Patch.first ;
1833+ PatchGroup.push_back (Patch.second );
1834+ if (std::next (PI) == PE || std::next (PI)->first != Patch.first + 4 ) {
1835+ BC.createInstructionPatch (PatchGroupAddress, PatchGroup);
1836+ PatchGroup.clear ();
1837+ }
1838+ }
1839+ }
1840+
17231841 // Inform BinaryContext that this function symbols will not be defined and
17241842 // relocations should not be created against them.
17251843 if (BC.HasRelocations ) {
0 commit comments