blob: 041d0d8c2b274fecff51d7f20b4348ffac58422c [file] [log] [blame]
//===- 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