llvm-project/lld/lib/ReaderWriter/MachO/TLVPass.cpp

142 lines
4.0 KiB
C++

//===- lib/ReaderWriter/MachO/TLVPass.cpp -----------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This linker pass transforms all TLV references to real references.
///
//===----------------------------------------------------------------------===//
#include "ArchHandler.h"
#include "File.h"
#include "MachOPasses.h"
#include "lld/Core/Simple.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
namespace lld {
namespace mach_o {
//
// TLVP Entry Atom created by the TLV pass.
//
class TLVPEntryAtom : public SimpleDefinedAtom {
public:
TLVPEntryAtom(const File &file, bool is64, StringRef name)
: SimpleDefinedAtom(file), _is64(is64), _name(name) {}
~TLVPEntryAtom() override = default;
ContentType contentType() const override {
return DefinedAtom::typeTLVInitializerPtr;
}
Alignment alignment() const override {
return _is64 ? 8 : 4;
}
uint64_t size() const override {
return _is64 ? 8 : 4;
}
ContentPermissions permissions() const override {
return DefinedAtom::permRW_;
}
ArrayRef<uint8_t> rawContent() const override {
static const uint8_t zeros[] =
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
return llvm::makeArrayRef(zeros, size());
}
StringRef slotName() const {
return _name;
}
private:
const bool _is64;
StringRef _name;
};
class TLVPass : public Pass {
public:
TLVPass(const MachOLinkingContext &context)
: _ctx(context), _archHandler(_ctx.archHandler()),
_file(*_ctx.make_file<MachOFile>("<mach-o TLV pass>")) {
_file.setOrdinal(_ctx.getNextOrdinalAndIncrement());
}
private:
llvm::Error perform(SimpleFile &mergedFile) override {
bool allowTLV = _ctx.minOS("10.7", "1.0");
for (const DefinedAtom *atom : mergedFile.defined()) {
for (const Reference *ref : *atom) {
if (!_archHandler.isTLVAccess(*ref))
continue;
if (!allowTLV)
return llvm::make_error<GenericError>(
"targeted OS version does not support use of thread local "
"variables in " + atom->name() + " for architecture " +
_ctx.archName());
const Atom *target = ref->target();
assert(target != nullptr);
const DefinedAtom *tlvpEntry = makeTLVPEntry(target);
const_cast<Reference*>(ref)->setTarget(tlvpEntry);
_archHandler.updateReferenceToTLV(ref);
}
}
std::vector<const TLVPEntryAtom*> entries;
entries.reserve(_targetToTLVP.size());
for (auto &it : _targetToTLVP)
entries.push_back(it.second);
std::sort(entries.begin(), entries.end(),
[](const TLVPEntryAtom *lhs, const TLVPEntryAtom *rhs) {
return (lhs->slotName().compare(rhs->slotName()) < 0);
});
for (const TLVPEntryAtom *slot : entries)
mergedFile.addAtom(*slot);
return llvm::Error::success();
}
const DefinedAtom *makeTLVPEntry(const Atom *target) {
auto pos = _targetToTLVP.find(target);
if (pos != _targetToTLVP.end())
return pos->second;
auto *tlvpEntry = new (_file.allocator())
TLVPEntryAtom(_file, _ctx.is64Bit(), target->name());
_targetToTLVP[target] = tlvpEntry;
const ArchHandler::ReferenceInfo &nlInfo =
_archHandler.stubInfo().nonLazyPointerReferenceToBinder;
tlvpEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch,
nlInfo.kind, 0, target, 0);
return tlvpEntry;
}
const MachOLinkingContext &_ctx;
mach_o::ArchHandler &_archHandler;
MachOFile &_file;
llvm::DenseMap<const Atom*, const TLVPEntryAtom*> _targetToTLVP;
};
void addTLVPass(PassManager &pm, const MachOLinkingContext &ctx) {
assert(ctx.needsTLVPass());
pm.add(llvm::make_unique<TLVPass>(ctx));
}
} // end namesapce mach_o
} // end namesapce lld