| //===- 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 |