| //===- bolt/Rewrite/ExecutableFileMemoryManager.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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "bolt/Rewrite/ExecutableFileMemoryManager.h" | |
| #include "bolt/Rewrite/JITLinkLinker.h" | |
| #include "bolt/Rewrite/RewriteInstance.h" | |
| #include "llvm/ExecutionEngine/JITLink/JITLink.h" | |
| #include "llvm/Support/MemAlloc.h" | |
| #undef DEBUG_TYPE | |
| #define DEBUG_TYPE "efmm" | |
| using namespace llvm; | |
| using namespace object; | |
| using namespace bolt; | |
| namespace llvm { | |
| namespace bolt { | |
| namespace { | |
| SmallVector<jitlink::Section *> orderedSections(jitlink::LinkGraph &G) { | |
| SmallVector<jitlink::Section *> Sections( | |
| llvm::map_range(G.sections(), [](auto &S) { return &S; })); | |
| llvm::sort(Sections, [](const auto *LHS, const auto *RHS) { | |
| return LHS->getOrdinal() < RHS->getOrdinal(); | |
| }); | |
| return Sections; | |
| } | |
| size_t sectionAlignment(const jitlink::Section &Section) { | |
| assert(!Section.empty() && "Cannot get alignment for empty section"); | |
| return JITLinkLinker::orderedBlocks(Section).front()->getAlignment(); | |
| } | |
| StringRef sectionName(const jitlink::Section &Section, | |
| const BinaryContext &BC) { | |
| auto Name = Section.getName(); | |
| if (BC.isMachO()) { | |
| // JITLink "normalizes" section names as "SegmentName,SectionName" on | |
| // Mach-O. BOLT internally refers to sections just by the section name so | |
| // strip-off the segment name. | |
| auto SegmentEnd = Name.find(','); | |
| assert(SegmentEnd != StringRef::npos && "Mach-O segment not found"); | |
| Name = Name.substr(SegmentEnd + 1); | |
| } | |
| return Name; | |
| } | |
| struct SectionAllocInfo { | |
| void *Address; | |
| size_t Size; | |
| size_t Alignment; | |
| }; | |
| struct AllocInfo { | |
| SmallVector<SectionAllocInfo, 8> AllocatedSections; | |
| ~AllocInfo() { | |
| for (auto &Section : AllocatedSections) | |
| deallocate_buffer(Section.Address, Section.Size, Section.Alignment); | |
| } | |
| SectionAllocInfo allocateSection(const jitlink::Section &Section) { | |
| auto Size = JITLinkLinker::sectionSize(Section); | |
| auto Alignment = sectionAlignment(Section); | |
| auto *Buf = allocate_buffer(Size, Alignment); | |
| SectionAllocInfo Alloc{Buf, Size, Alignment}; | |
| AllocatedSections.push_back(Alloc); | |
| return Alloc; | |
| } | |
| }; | |
| struct BOLTInFlightAlloc : ExecutableFileMemoryManager::InFlightAlloc { | |
| // Even though this is passed using a raw pointer in FinalizedAlloc, we keep | |
| // it in a unique_ptr as long as possible to enjoy automatic cleanup when | |
| // something goes wrong. | |
| std::unique_ptr<AllocInfo> Alloc; | |
| public: | |
| BOLTInFlightAlloc(std::unique_ptr<AllocInfo> Alloc) | |
| : Alloc(std::move(Alloc)) {} | |
| virtual void abandon(OnAbandonedFunction OnAbandoned) override { | |
| OnAbandoned(Error::success()); | |
| } | |
| virtual void finalize(OnFinalizedFunction OnFinalized) override { | |
| OnFinalized(ExecutableFileMemoryManager::FinalizedAlloc( | |
| orc::ExecutorAddr::fromPtr(Alloc.release()))); | |
| } | |
| }; | |
| } // anonymous namespace | |
| void ExecutableFileMemoryManager::updateSection( | |
| const jitlink::Section &JLSection, uint8_t *Contents, size_t Size, | |
| size_t Alignment) { | |
| auto SectionID = JLSection.getName(); | |
| auto SectionName = sectionName(JLSection, BC); | |
| auto Prot = JLSection.getMemProt(); | |
| auto IsCode = (Prot & orc::MemProt::Exec) != orc::MemProt::None; | |
| auto IsReadOnly = (Prot & orc::MemProt::Write) == orc::MemProt::None; | |
| // Register a debug section as a note section. | |
| if (!ObjectsLoaded && RewriteInstance::isDebugSection(SectionName)) { | |
| BinarySection &Section = | |
| BC.registerOrUpdateNoteSection(SectionName, Contents, Size, Alignment); | |
| Section.setSectionID(SectionID); | |
| assert(!Section.isAllocatable() && "note sections cannot be allocatable"); | |
| return; | |
| } | |
| if (!IsCode && (SectionName == ".strtab" || SectionName == ".symtab" || | |
| SectionName == "" || SectionName.starts_with(".rela."))) | |
| return; | |
| SmallVector<char, 256> Buf; | |
| if (ObjectsLoaded > 0) { | |
| if (BC.isELF()) { | |
| SectionName = (Twine(SectionName) + ".bolt.extra." + Twine(ObjectsLoaded)) | |
| .toStringRef(Buf); | |
| } else if (BC.isMachO()) { | |
| assert((SectionName == "__text" || SectionName == "__data" || | |
| SectionName == "__fini" || SectionName == "__setup" || | |
| SectionName == "__cstring" || SectionName == "__literal16") && | |
| "Unexpected section in the instrumentation library"); | |
| // Sections coming from the instrumentation runtime are prefixed with "I". | |
| SectionName = ("I" + Twine(SectionName)).toStringRef(Buf); | |
| } | |
| } | |
| BinarySection *Section = nullptr; | |
| if (!OrgSecPrefix.empty() && SectionName.starts_with(OrgSecPrefix)) { | |
| // Update the original section contents. | |
| ErrorOr<BinarySection &> OrgSection = | |
| BC.getUniqueSectionByName(SectionName.substr(OrgSecPrefix.length())); | |
| assert(OrgSection && OrgSection->isAllocatable() && | |
| "Original section must exist and be allocatable."); | |
| Section = &OrgSection.get(); | |
| Section->updateContents(Contents, Size); | |
| } else { | |
| // If the input contains a section with the section name, rename it in the | |
| // output file to avoid the section name conflict and emit the new section | |
| // under a unique internal name. | |
| ErrorOr<BinarySection &> OrgSection = | |
| BC.getUniqueSectionByName(SectionName); | |
| bool UsePrefix = false; | |
| if (OrgSection && OrgSection->hasSectionRef()) { | |
| OrgSection->setOutputName(OrgSecPrefix + SectionName); | |
| UsePrefix = true; | |
| } | |
| // Register the new section under a unique name to avoid name collision with | |
| // sections in the input file. | |
| BinarySection &NewSection = BC.registerOrUpdateSection( | |
| UsePrefix ? NewSecPrefix + SectionName : SectionName, ELF::SHT_PROGBITS, | |
| BinarySection::getFlags(IsReadOnly, IsCode, true), Contents, Size, | |
| Alignment); | |
| if (UsePrefix) | |
| NewSection.setOutputName(SectionName); | |
| Section = &NewSection; | |
| } | |
| LLVM_DEBUG({ | |
| dbgs() << "BOLT: allocating " | |
| << (IsCode ? "code" : (IsReadOnly ? "read-only data" : "data")) | |
| << " section : " << Section->getOutputName() << " (" | |
| << Section->getName() << ")" | |
| << " with size " << Size << ", alignment " << Alignment << " at " | |
| << Contents << ", ID = " << SectionID << "\n"; | |
| }); | |
| Section->setSectionID(SectionID); | |
| } | |
| void ExecutableFileMemoryManager::allocate(const jitlink::JITLinkDylib *JD, | |
| jitlink::LinkGraph &G, | |
| OnAllocatedFunction OnAllocated) { | |
| auto Alloc = std::make_unique<AllocInfo>(); | |
| for (auto *Section : orderedSections(G)) { | |
| if (Section->empty()) | |
| continue; | |
| auto SectionAlloc = Alloc->allocateSection(*Section); | |
| updateSection(*Section, static_cast<uint8_t *>(SectionAlloc.Address), | |
| SectionAlloc.Size, SectionAlloc.Alignment); | |
| size_t CurrentOffset = 0; | |
| auto *Buf = static_cast<char *>(SectionAlloc.Address); | |
| for (auto *Block : JITLinkLinker::orderedBlocks(*Section)) { | |
| CurrentOffset = jitlink::alignToBlock(CurrentOffset, *Block); | |
| auto BlockSize = Block->getSize(); | |
| auto *BlockBuf = Buf + CurrentOffset; | |
| if (Block->isZeroFill()) | |
| std::memset(BlockBuf, 0, BlockSize); | |
| else | |
| std::memcpy(BlockBuf, Block->getContent().data(), BlockSize); | |
| Block->setMutableContent({BlockBuf, Block->getSize()}); | |
| CurrentOffset += BlockSize; | |
| } | |
| } | |
| OnAllocated(std::make_unique<BOLTInFlightAlloc>(std::move(Alloc))); | |
| } | |
| void ExecutableFileMemoryManager::deallocate( | |
| std::vector<FinalizedAlloc> Allocs, OnDeallocatedFunction OnDeallocated) { | |
| for (auto &Alloc : Allocs) | |
| delete Alloc.release().toPtr<AllocInfo *>(); | |
| OnDeallocated(Error::success()); | |
| } | |
| } // namespace bolt | |
| } // namespace llvm |