//===- 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 orderedSections(jitlink::LinkGraph &G) { SmallVector 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 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 Alloc; public: BOLTInFlightAlloc(std::unique_ptr 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 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 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 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(); for (auto *Section : orderedSections(G)) { if (Section->empty()) continue; auto SectionAlloc = Alloc->allocateSection(*Section); updateSection(*Section, static_cast(SectionAlloc.Address), SectionAlloc.Size, SectionAlloc.Alignment); size_t CurrentOffset = 0; auto *Buf = static_cast(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(std::move(Alloc))); } void ExecutableFileMemoryManager::deallocate( std::vector Allocs, OnDeallocatedFunction OnDeallocated) { for (auto &Alloc : Allocs) delete Alloc.release().toPtr(); OnDeallocated(Error::success()); } } // namespace bolt } // namespace llvm