diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -1328,6 +1328,16 @@ return IndirectBranchType::UNKNOWN; } + /// Analyze branch \p Instruction in PLT section and try to determine the + /// address of used got entry. + virtual uint64_t analyzePltEntry(MCInst &Instruction, + InstructionIterator Begin, + InstructionIterator End, + uint64_t BeginPC) const { + llvm_unreachable("not implemented"); + return 0; + } + virtual bool analyzeVirtualMethodCall(InstructionIterator Begin, InstructionIterator End, std::vector &MethodFetchInsns, diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h --- a/bolt/include/bolt/Rewrite/RewriteInstance.h +++ b/bolt/include/bolt/Rewrite/RewriteInstance.h @@ -445,7 +445,7 @@ /// multiple variants generated by different linkers. struct PLTSectionInfo { const char *Name; - uint64_t EntrySize; + uint64_t EntrySize{0}; }; /// Different types of X86-64 PLT sections. @@ -458,8 +458,8 @@ /// AArch64 PLT sections. const PLTSectionInfo AArch64_PLTSections[2] = { - { ".plt", 16 }, - { nullptr, 0 } + { ".plt" }, + { nullptr } }; /// Return PLT information for a section with \p SectionName or nullptr diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -1153,8 +1153,7 @@ processDynamicRelocations(); // Process PLT section. - if (BC->TheTriple->getArch() == Triple::x86_64) - disassemblePLT(); + disassemblePLT(); // See if we missed any functions marked by FDE. for (const auto &FDEI : CFIRdWrt->getFDEs()) { @@ -1227,64 +1226,121 @@ void RewriteInstance::disassemblePLT() { auto analyzeOnePLTSection = [&](BinarySection &Section, uint64_t EntrySize) { const uint64_t PLTAddress = Section.getAddress(); + const uint64_t SectionSize = Section.getSize(); StringRef PLTContents = Section.getContents(); ArrayRef PLTData( - reinterpret_cast(PLTContents.data()), - Section.getSize()); + reinterpret_cast(PLTContents.data()), SectionSize); const unsigned PtrSize = BC->AsmInfo->getCodePointerSize(); - - for (uint64_t EntryOffset = 0; EntryOffset + EntrySize <= Section.getSize(); - EntryOffset += EntrySize) { - uint64_t InstrOffset = EntryOffset; - uint64_t InstrSize; - MCInst Instruction; - while (InstrOffset < EntryOffset + EntrySize) { - uint64_t InstrAddr = PLTAddress + InstrOffset; - if (!BC->DisAsm->getInstruction(Instruction, InstrSize, - PLTData.slice(InstrOffset), InstrAddr, - nulls())) { - errs() << "BOLT-ERROR: unable to disassemble instruction in PLT " - "section " - << Section.getName() << " at offset 0x" - << Twine::utohexstr(InstrOffset) << '\n'; - exit(1); - } - - // Check if the entry size needs adjustment. - if (EntryOffset == 0 && BC->MIB->isTerminateBranch(Instruction) && - EntrySize == 8) - EntrySize = 16; - - if (BC->MIB->isIndirectBranch(Instruction)) - break; - - InstrOffset += InstrSize; - } - - if (InstrOffset + InstrSize > EntryOffset + EntrySize) - continue; - - uint64_t TargetAddress; - if (!BC->MIB->evaluateMemOperandTarget(Instruction, TargetAddress, - PLTAddress + InstrOffset, - InstrSize)) { - errs() << "BOLT-ERROR: error evaluating PLT instruction at offset 0x" - << Twine::utohexstr(PLTAddress + InstrOffset) << '\n'; + MCInst Instruction; + uint64_t InstrSize; + + auto disassembleInstruction = [&](uint64_t InstrOffset) { + uint64_t InstrAddr = PLTAddress + InstrOffset; + if (!BC->DisAsm->getInstruction(Instruction, InstrSize, + PLTData.slice(InstrOffset), InstrAddr, + nulls())) { + errs() << "BOLT-ERROR: unable to disassemble instruction in PLT " + "section " + << Section.getName() << " at offset 0x" + << Twine::utohexstr(InstrOffset) << '\n'; exit(1); } + }; + + auto createPltBF = [&](uint64_t TargetAddress, uint64_t EntryAddress, + uint64_t EntrySize) { + if (!TargetAddress) + return; const Relocation *Rel = BC->getDynamicRelocationAt(TargetAddress); if (!Rel || !Rel->Symbol) - continue; + return; BinaryFunction *BF = BC->createBinaryFunction( - Rel->Symbol->getName().str() + "@PLT", Section, - PLTAddress + EntryOffset, 0, EntrySize, Section.getAlignment()); + Rel->Symbol->getName().str() + "@PLT", Section, EntryAddress, 0, + EntrySize, Section.getAlignment()); MCSymbol *TargetSymbol = BC->registerNameAtAddress(Rel->Symbol->getName().str() + "@GOT", TargetAddress, PtrSize, PtrSize); BF->setPLTSymbol(TargetSymbol); - } + }; + + auto analyzeSectionAArch64 = [&]() { + uint64_t EntryOffset, InstrOffset = 0; + // Locate new plt entry + while (InstrOffset < SectionSize) { + InstructionListType Instructions; + EntryOffset = InstrOffset; + EntrySize = 0; + // Loop through entry instructions + while (InstrOffset < SectionSize) { + disassembleInstruction(InstrOffset); + EntrySize += InstrSize; + if (!BC->MIB->isIndirectBranch(Instruction)) { + Instructions.emplace_back(Instruction); + InstrOffset += InstrSize; + continue; + } + + const uint64_t EntryAddress = PLTAddress + EntryOffset; + uint64_t TargetAddress = + BC->MIB->analyzePltEntry(Instruction, Instructions.begin(), + Instructions.end(), EntryAddress); + + createPltBF(TargetAddress, EntryAddress, EntrySize); + break; + } + + // Branch instruction + InstrOffset += InstrSize; + + // Skip nops if any + while (InstrOffset < SectionSize) { + disassembleInstruction(InstrOffset); + if (!BC->MIB->isNoop(Instruction)) + break; + + InstrOffset += InstrSize; + } + } + }; + + auto analyzeSectionX86 = [&]() { + for (uint64_t EntryOffset = 0; EntryOffset + EntrySize <= SectionSize; + EntryOffset += EntrySize) { + uint64_t InstrOffset = EntryOffset; + while (InstrOffset < EntryOffset + EntrySize) { + disassembleInstruction(InstrOffset); + // Check if the entry size needs adjustment. + if (EntryOffset == 0 && BC->MIB->isTerminateBranch(Instruction) && + EntrySize == 8) + EntrySize = 16; + + if (BC->MIB->isIndirectBranch(Instruction)) + break; + + InstrOffset += InstrSize; + } + + if (InstrOffset + InstrSize > EntryOffset + EntrySize) + continue; + + uint64_t TargetAddress; + const uint64_t EntryAddress = PLTAddress + EntryOffset; + if (!BC->MIB->evaluateMemOperandTarget(Instruction, TargetAddress, + EntryAddress, InstrSize)) { + errs() << "BOLT-ERROR: error evaluating PLT instruction at offset 0x" + << Twine::utohexstr(PLTAddress + InstrOffset) << '\n'; + exit(1); + } + + createPltBF(TargetAddress, EntryAddress, EntrySize); + } + }; + + if (BC->isAArch64()) + return analyzeSectionAArch64(); + return analyzeSectionX86(); }; for (BinarySection &Section : BC->allocatableSections()) { @@ -1731,6 +1787,11 @@ SkipVerification = (cantFail(Symbol.getType()) == SymbolRef::ST_Other); // Section symbols are marked as ST_Debug. IsSectionRelocation = (cantFail(Symbol.getType()) == SymbolRef::ST_Debug); + // Check for PLT entry registered with symbol name + if (!SymbolAddress && IsAArch64) { + BinaryData *BD = BC->getBinaryDataByName(SymbolName + "@PLT"); + SymbolAddress = BD ? BD->getAddress() : 0; + } } // For PIE or dynamic libs, the linker may choose not to put the relocation // result at the address if it is a X86_64_64 one because it will emit a diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp --- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp +++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp @@ -709,6 +709,67 @@ return IndirectBranchType::POSSIBLE_PIC_JUMP_TABLE; } + /// Matches PLT entry pattern and returns the GOTs entry address. + /// Typical PLT entry looks like the following: + /// + /// adrp x16, 230000 + /// ldr x17, [x16, #3040] + /// add x16, x16, #0xbe0 + /// br x17 + /// + uint64_t analyzePltEntry(MCInst &Instruction, InstructionIterator Begin, + InstructionIterator End, + uint64_t BeginPC) const override { + // Check branch instruction + MCInst *Branch = &Instruction; + assert(Branch->getOpcode() == AArch64::BR && "Unexpected opcode"); + + DenseMap> UDChain = + computeLocalUDChain(Branch, Begin, End); + + // Match ldr instruction + SmallVector &UsesBranch = UDChain[Branch]; + if (UsesBranch.size() < 1 || UsesBranch[0] == nullptr) + return 0; + + // Check ldr instruction + const MCInst *Ldr = UsesBranch[0]; + if (Ldr->getOpcode() != AArch64::LDRXui) + return 0; + + // Get ldr value + const unsigned ScaleLdr = 8; // LDRX operates on 8 bytes segments + assert(Ldr->getOperand(2).isImm() && "Unexpected ldr operand"); + const uint64_t Offset = Ldr->getOperand(2).getImm() * ScaleLdr; + + // March adrp instruction + SmallVector &UsesLdr = UDChain[Ldr]; + if (UsesLdr.size() == 0 || UsesLdr[1] == nullptr) + return 0; + + // Check adrp instruction + MCInst *Adrp = UsesLdr[1]; + if (Adrp->getOpcode() != AArch64::ADRP) + return 0; + + // Get adrp PC + const unsigned InstSize = 4; + uint64_t AdrpPC = BeginPC; + for (InstructionIterator It = Begin; It != End; ++It) { + if (&(*It) == Adrp) + break; + AdrpPC += InstSize; + } + + // Get adrp value + uint64_t Base; + assert(Adrp->getOperand(1).isImm() && "Unexpected adrp operand"); + bool Ret = evaluateMemOperandTarget(*Adrp, Base, AdrpPC, InstSize); + assert(Ret && "Failed to evaluate adrp"); + + return Base + Offset; + } + unsigned getInvertedBranchOpcode(unsigned Opcode) const { switch (Opcode) { default: diff --git a/bolt/test/runtime/AArch64/plt.c b/bolt/test/runtime/AArch64/plt.c new file mode 100644 --- /dev/null +++ b/bolt/test/runtime/AArch64/plt.c @@ -0,0 +1,25 @@ +// This test checks that the pointers to PLT are properly updated. + +// RUN: %clang %cflags %s -fuse-ld=lld \ +// RUN: -o %t.exe -Wl,-q +// RUN: llvm-bolt %t.exe -o %t.bolt.exe -use-old-text=0 -lite=0 +// RUN: %t.bolt.exe + +#include + +void *(*memcpy_p)(void *dest, const void *src, size_t n); +void *(*memset_p)(void *dest, int c, size_t n); + +int main() { + int a = 0xdeadbeef, b = 0; + + memcpy_p = memcpy; + memcpy_p(&b, &a, sizeof(b)); + if (b != 0xdeadbeef) + return 1; + + memset_p = memset; + memset_p(&a, 0, sizeof(a)); + if (a != 0) + return 1; +}