forked from OSchip/llvm-project
412 lines
14 KiB
C++
412 lines
14 KiB
C++
//=--------- MachOAtomGraphBuilder.cpp - MachO AtomGraph builder ----------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Generic MachO AtomGraph buliding code.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MachOAtomGraphBuilder.h"
|
|
|
|
#define DEBUG_TYPE "jitlink"
|
|
|
|
namespace llvm {
|
|
namespace jitlink {
|
|
|
|
MachOAtomGraphBuilder::~MachOAtomGraphBuilder() {}
|
|
|
|
Expected<std::unique_ptr<AtomGraph>> MachOAtomGraphBuilder::buildGraph() {
|
|
if (auto Err = parseSections())
|
|
return std::move(Err);
|
|
|
|
if (auto Err = addAtoms())
|
|
return std::move(Err);
|
|
|
|
if (auto Err = addRelocations())
|
|
return std::move(Err);
|
|
|
|
return std::move(G);
|
|
}
|
|
|
|
MachOAtomGraphBuilder::MachOAtomGraphBuilder(const object::MachOObjectFile &Obj)
|
|
: Obj(Obj),
|
|
G(llvm::make_unique<AtomGraph>(Obj.getFileName(), getPointerSize(Obj),
|
|
getEndianness(Obj))) {}
|
|
|
|
void MachOAtomGraphBuilder::addCustomAtomizer(StringRef SectionName,
|
|
CustomAtomizeFunction Atomizer) {
|
|
assert(!CustomAtomizeFunctions.count(SectionName) &&
|
|
"Custom atomizer for this section already exists");
|
|
CustomAtomizeFunctions[SectionName] = std::move(Atomizer);
|
|
}
|
|
|
|
bool MachOAtomGraphBuilder::areLayoutLocked(const Atom &A, const Atom &B) {
|
|
// If these atoms are the same then they're trivially "locked".
|
|
if (&A == &B)
|
|
return true;
|
|
|
|
// If A and B are different, check whether either is undefined. (in which
|
|
// case they are not locked).
|
|
if (!A.isDefined() || !B.isDefined())
|
|
return false;
|
|
|
|
// A and B are different, but they're both defined atoms. We need to check
|
|
// whether they're part of the same alt_entry chain.
|
|
auto &DA = static_cast<const DefinedAtom &>(A);
|
|
auto &DB = static_cast<const DefinedAtom &>(B);
|
|
|
|
auto AStartItr = AltEntryStarts.find(&DA);
|
|
if (AStartItr == AltEntryStarts.end()) // If A is not in a chain bail out.
|
|
return false;
|
|
|
|
auto BStartItr = AltEntryStarts.find(&DB);
|
|
if (BStartItr == AltEntryStarts.end()) // If B is not in a chain bail out.
|
|
return false;
|
|
|
|
// A and B are layout locked if they're in the same chain.
|
|
return AStartItr->second == BStartItr->second;
|
|
}
|
|
|
|
unsigned
|
|
MachOAtomGraphBuilder::getPointerSize(const object::MachOObjectFile &Obj) {
|
|
return Obj.is64Bit() ? 8 : 4;
|
|
}
|
|
|
|
support::endianness
|
|
MachOAtomGraphBuilder::getEndianness(const object::MachOObjectFile &Obj) {
|
|
return Obj.isLittleEndian() ? support::little : support::big;
|
|
}
|
|
|
|
MachOAtomGraphBuilder::MachOSection &MachOAtomGraphBuilder::getCommonSection() {
|
|
if (!CommonSymbolsSection) {
|
|
auto Prot = static_cast<sys::Memory::ProtectionFlags>(
|
|
sys::Memory::MF_READ | sys::Memory::MF_WRITE);
|
|
auto &GenericSection = G->createSection("<common>", 1, Prot, true);
|
|
CommonSymbolsSection = MachOSection(GenericSection);
|
|
}
|
|
return *CommonSymbolsSection;
|
|
}
|
|
|
|
Error MachOAtomGraphBuilder::parseSections() {
|
|
for (auto &SecRef : Obj.sections()) {
|
|
assert((SecRef.getAlignment() <= std::numeric_limits<uint32_t>::max()) &&
|
|
"Section alignment does not fit in 32 bits");
|
|
|
|
StringRef Name;
|
|
if (auto EC = SecRef.getName(Name))
|
|
return errorCodeToError(EC);
|
|
|
|
unsigned SectionIndex = SecRef.getIndex() + 1;
|
|
|
|
uint32_t Align = SecRef.getAlignment();
|
|
if (!isPowerOf2_32(Align))
|
|
return make_error<JITLinkError>("Section " + Name +
|
|
" has non-power-of-2 "
|
|
"alignment");
|
|
|
|
// FIXME: Get real section permissions
|
|
// How, exactly, on MachO?
|
|
sys::Memory::ProtectionFlags Prot;
|
|
if (SecRef.isText())
|
|
Prot = static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
|
|
sys::Memory::MF_EXEC);
|
|
else
|
|
Prot = static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
|
|
sys::Memory::MF_WRITE);
|
|
|
|
auto &GenericSection = G->createSection(Name, Align, Prot, SecRef.isBSS());
|
|
|
|
LLVM_DEBUG({
|
|
dbgs() << "Adding section " << Name << ": "
|
|
<< format("0x%016" PRIx64, SecRef.getAddress())
|
|
<< ", align: " << SecRef.getAlignment() << "\n";
|
|
});
|
|
|
|
assert(!Sections.count(SectionIndex) && "Section index already in use");
|
|
|
|
auto &MachOSec =
|
|
Sections
|
|
.try_emplace(SectionIndex, GenericSection, SecRef.getAddress(),
|
|
SecRef.getAlignment())
|
|
.first->second;
|
|
|
|
if (!SecRef.isVirtual()) {
|
|
// If this section has content then record it.
|
|
Expected<StringRef> Content = SecRef.getContents();
|
|
if (!Content)
|
|
return Content.takeError();
|
|
if (Content->size() != SecRef.getSize())
|
|
return make_error<JITLinkError>("Section content size does not match "
|
|
"declared size for " +
|
|
Name);
|
|
MachOSec.setContent(*Content);
|
|
} else {
|
|
// If this is a zero-fill section then just record the size.
|
|
MachOSec.setZeroFill(SecRef.getSize());
|
|
}
|
|
|
|
uint32_t SectionFlags =
|
|
Obj.is64Bit() ? Obj.getSection64(SecRef.getRawDataRefImpl()).flags
|
|
: Obj.getSection(SecRef.getRawDataRefImpl()).flags;
|
|
|
|
MachOSec.setNoDeadStrip(SectionFlags & MachO::S_ATTR_NO_DEAD_STRIP);
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
// Adds atoms with identified start addresses (but not lengths) for all named
|
|
// atoms.
|
|
// Also, for every section that contains named atoms, but does not have an
|
|
// atom at offset zero of that section, constructs an anonymous atom covering
|
|
// that range.
|
|
Error MachOAtomGraphBuilder::addNonCustomAtoms() {
|
|
using AddrToAtomMap = std::map<JITTargetAddress, DefinedAtom *>;
|
|
DenseMap<MachOSection *, AddrToAtomMap> SecToAtoms;
|
|
|
|
DenseMap<MachOSection *, unsigned> FirstOrdinal;
|
|
std::vector<DefinedAtom *> AltEntryAtoms;
|
|
|
|
DenseSet<StringRef> ProcessedSymbols; // Used to check for duplicate defs.
|
|
|
|
for (auto SymI = Obj.symbol_begin(), SymE = Obj.symbol_end(); SymI != SymE;
|
|
++SymI) {
|
|
object::SymbolRef Sym(SymI->getRawDataRefImpl(), &Obj);
|
|
|
|
auto Name = Sym.getName();
|
|
if (!Name)
|
|
return Name.takeError();
|
|
|
|
// Bail out on duplicate definitions: There should never be more than one
|
|
// definition for a symbol in a given object file.
|
|
if (ProcessedSymbols.count(*Name))
|
|
return make_error<JITLinkError>("Duplicate definition within object: " +
|
|
*Name);
|
|
else
|
|
ProcessedSymbols.insert(*Name);
|
|
|
|
auto Addr = Sym.getAddress();
|
|
if (!Addr)
|
|
return Addr.takeError();
|
|
|
|
auto SymType = Sym.getType();
|
|
if (!SymType)
|
|
return SymType.takeError();
|
|
|
|
auto Flags = Sym.getFlags();
|
|
|
|
if (Flags & object::SymbolRef::SF_Undefined) {
|
|
LLVM_DEBUG(dbgs() << "Adding undef atom \"" << *Name << "\"\n");
|
|
G->addExternalAtom(*Name);
|
|
continue;
|
|
} else if (Flags & object::SymbolRef::SF_Absolute) {
|
|
LLVM_DEBUG(dbgs() << "Adding absolute \"" << *Name << "\" addr: "
|
|
<< format("0x%016" PRIx64, *Addr) << "\n");
|
|
auto &A = G->addAbsoluteAtom(*Name, *Addr);
|
|
A.setGlobal(Flags & object::SymbolRef::SF_Global);
|
|
A.setExported(Flags & object::SymbolRef::SF_Exported);
|
|
A.setWeak(Flags & object::SymbolRef::SF_Weak);
|
|
continue;
|
|
} else if (Flags & object::SymbolRef::SF_Common) {
|
|
LLVM_DEBUG({
|
|
dbgs() << "Adding common \"" << *Name
|
|
<< "\" addr: " << format("0x%016" PRIx64, *Addr) << "\n";
|
|
});
|
|
auto &A =
|
|
G->addCommonAtom(getCommonSection().getGenericSection(), *Name, *Addr,
|
|
std::max(Sym.getAlignment(), 1U),
|
|
Obj.getCommonSymbolSize(Sym.getRawDataRefImpl()));
|
|
A.setGlobal(Flags & object::SymbolRef::SF_Global);
|
|
A.setExported(Flags & object::SymbolRef::SF_Exported);
|
|
continue;
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << "Adding defined atom \"" << *Name << "\"\n");
|
|
|
|
// This atom is neither undefined nor absolute, so it must be defined in
|
|
// this object. Get its section index.
|
|
auto SecItr = Sym.getSection();
|
|
if (!SecItr)
|
|
return SecItr.takeError();
|
|
|
|
uint64_t SectionIndex = (*SecItr)->getIndex() + 1;
|
|
|
|
LLVM_DEBUG(dbgs() << " to section index " << SectionIndex << "\n");
|
|
|
|
auto SecByIndexItr = Sections.find(SectionIndex);
|
|
if (SecByIndexItr == Sections.end())
|
|
return make_error<JITLinkError>("Unrecognized section index in macho");
|
|
|
|
auto &Sec = SecByIndexItr->second;
|
|
|
|
auto &DA = G->addDefinedAtom(Sec.getGenericSection(), *Name, *Addr,
|
|
std::max(Sym.getAlignment(), 1U));
|
|
|
|
DA.setGlobal(Flags & object::SymbolRef::SF_Global);
|
|
DA.setExported(Flags & object::SymbolRef::SF_Exported);
|
|
DA.setWeak(Flags & object::SymbolRef::SF_Weak);
|
|
|
|
DA.setCallable(*SymType & object::SymbolRef::ST_Function);
|
|
|
|
// Check NDesc flags.
|
|
{
|
|
uint16_t NDesc = 0;
|
|
if (Obj.is64Bit())
|
|
NDesc = Obj.getSymbol64TableEntry(SymI->getRawDataRefImpl()).n_desc;
|
|
else
|
|
NDesc = Obj.getSymbolTableEntry(SymI->getRawDataRefImpl()).n_desc;
|
|
|
|
// Record atom for alt-entry post-processing (where the layout-next
|
|
// constraints will be added).
|
|
if (NDesc & MachO::N_ALT_ENTRY)
|
|
AltEntryAtoms.push_back(&DA);
|
|
|
|
// If this atom has a no-dead-strip attr attached then mark it live.
|
|
if (NDesc & MachO::N_NO_DEAD_STRIP)
|
|
DA.setLive(true);
|
|
}
|
|
|
|
LLVM_DEBUG({
|
|
dbgs() << " Added " << *Name
|
|
<< " addr: " << format("0x%016" PRIx64, *Addr)
|
|
<< ", align: " << DA.getAlignment()
|
|
<< ", section: " << Sec.getGenericSection().getName() << "\n";
|
|
});
|
|
|
|
auto &SecAtoms = SecToAtoms[&Sec];
|
|
SecAtoms[DA.getAddress() - Sec.getAddress()] = &DA;
|
|
}
|
|
|
|
// Add anonymous atoms.
|
|
for (auto &KV : Sections) {
|
|
auto &S = KV.second;
|
|
|
|
// Skip empty sections.
|
|
if (S.empty())
|
|
continue;
|
|
|
|
// Skip sections with custom handling.
|
|
if (CustomAtomizeFunctions.count(S.getName()))
|
|
continue;
|
|
|
|
auto SAI = SecToAtoms.find(&S);
|
|
|
|
// If S is not in the SecToAtoms map then it contained no named atom. Add
|
|
// one anonymous atom to cover the whole section.
|
|
if (SAI == SecToAtoms.end()) {
|
|
SecToAtoms[&S][0] = &G->addAnonymousAtom(
|
|
S.getGenericSection(), S.getAddress(), S.getAlignment());
|
|
continue;
|
|
}
|
|
|
|
// Otherwise, check whether this section had an atom covering offset zero.
|
|
// If not, add one.
|
|
auto &SecAtoms = SAI->second;
|
|
if (!SecAtoms.count(0))
|
|
SecAtoms[0] = &G->addAnonymousAtom(S.getGenericSection(), S.getAddress(),
|
|
S.getAlignment());
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << "MachOGraphBuilder setting atom content\n");
|
|
|
|
// Set atom contents and any section-based flags.
|
|
for (auto &KV : SecToAtoms) {
|
|
auto &S = *KV.first;
|
|
auto &SecAtoms = KV.second;
|
|
|
|
// Iterate the atoms in reverse order and set up their contents.
|
|
JITTargetAddress LastAtomAddr = S.getSize();
|
|
for (auto I = SecAtoms.rbegin(), E = SecAtoms.rend(); I != E; ++I) {
|
|
auto Offset = I->first;
|
|
auto &A = *I->second;
|
|
LLVM_DEBUG({
|
|
dbgs() << " " << A << " to [ " << S.getAddress() + Offset << " .. "
|
|
<< S.getAddress() + LastAtomAddr << " ]\n";
|
|
});
|
|
|
|
if (S.isZeroFill())
|
|
A.setZeroFill(LastAtomAddr - Offset);
|
|
else
|
|
A.setContent(S.getContent().substr(Offset, LastAtomAddr - Offset));
|
|
|
|
// If the section has no-dead-strip set then mark the atom as live.
|
|
if (S.isNoDeadStrip())
|
|
A.setLive(true);
|
|
|
|
LastAtomAddr = Offset;
|
|
}
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << "Adding alt-entry starts\n");
|
|
|
|
// Sort alt-entry atoms by address in ascending order.
|
|
llvm::sort(AltEntryAtoms.begin(), AltEntryAtoms.end(),
|
|
[](const DefinedAtom *LHS, const DefinedAtom *RHS) {
|
|
return LHS->getAddress() < RHS->getAddress();
|
|
});
|
|
|
|
// Process alt-entry atoms in address order to build the table of alt-entry
|
|
// atoms to alt-entry chain starts.
|
|
for (auto *DA : AltEntryAtoms) {
|
|
assert(!AltEntryStarts.count(DA) && "Duplicate entry in AltEntryStarts");
|
|
|
|
// DA is an alt-entry atom. Look for the predecessor atom that it is locked
|
|
// to, bailing out if we do not find one.
|
|
auto AltEntryPred = G->findAtomByAddress(DA->getAddress() - 1);
|
|
if (!AltEntryPred)
|
|
return AltEntryPred.takeError();
|
|
|
|
// Add a LayoutNext edge from the predecessor to this atom.
|
|
AltEntryPred->setLayoutNext(*DA);
|
|
|
|
// Check to see whether the predecessor itself is an alt-entry atom.
|
|
auto AltEntryStartItr = AltEntryStarts.find(&*AltEntryPred);
|
|
if (AltEntryStartItr != AltEntryStarts.end()) {
|
|
// If the predecessor was an alt-entry atom then re-use its value.
|
|
LLVM_DEBUG({
|
|
dbgs() << " " << *DA << " -> " << *AltEntryStartItr->second
|
|
<< " (based on existing entry for " << *AltEntryPred << ")\n";
|
|
});
|
|
AltEntryStarts[DA] = AltEntryStartItr->second;
|
|
} else {
|
|
// If the predecessor does not have an entry then add an entry for this
|
|
// atom (i.e. the alt_entry atom) and a self-reference entry for the
|
|
/// predecessory atom that is the start of this chain.
|
|
LLVM_DEBUG({
|
|
dbgs() << " " << *AltEntryPred << " -> " << *AltEntryPred << "\n"
|
|
<< " " << *DA << " -> " << *AltEntryPred << "\n";
|
|
});
|
|
AltEntryStarts[&*AltEntryPred] = &*AltEntryPred;
|
|
AltEntryStarts[DA] = &*AltEntryPred;
|
|
}
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
Error MachOAtomGraphBuilder::addAtoms() {
|
|
// Add all named atoms.
|
|
if (auto Err = addNonCustomAtoms())
|
|
return Err;
|
|
|
|
// Process special sections.
|
|
for (auto &KV : Sections) {
|
|
auto &S = KV.second;
|
|
auto HI = CustomAtomizeFunctions.find(S.getGenericSection().getName());
|
|
if (HI != CustomAtomizeFunctions.end()) {
|
|
auto &Atomize = HI->second;
|
|
if (auto Err = Atomize(S))
|
|
return Err;
|
|
}
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
} // end namespace jitlink
|
|
} // end namespace llvm
|